From 207e57abd180ed543a8d7f51d4ae971a2ae0d352 Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Fri, 1 Nov 2024 10:12:43 +0100 Subject: [PATCH 1/4] dts: rtc: sam0: Add clock properties Add properties to differentiate the timer counter operating modes. This properties are necessary to spetialize the driver to be used as a normal 16/32-bit counter or to provide the clock/calendar functions. Signed-off-by: Gerson Fernando Budke --- drivers/timer/Kconfig.sam0_rtc | 4 +- dts/arm/atmel/samc2x.dtsi | 7 +- dts/arm/atmel/samd2x.dtsi | 9 ++- dts/arm/atmel/samd5x.dtsi | 8 +- dts/arm/atmel/saml2x.dtsi | 7 +- dts/bindings/rtc/atmel,sam0-rtc.yaml | 106 ++++++++++++++++++++++++++- 6 files changed, 134 insertions(+), 7 deletions(-) diff --git a/drivers/timer/Kconfig.sam0_rtc b/drivers/timer/Kconfig.sam0_rtc index 14c4ee73939f..6d97d16c564f 100644 --- a/drivers/timer/Kconfig.sam0_rtc +++ b/drivers/timer/Kconfig.sam0_rtc @@ -1,12 +1,14 @@ # Copyright (c) 2014-2015 Wind River Systems, Inc. # Copyright (c) 2016 Cadence Design Systems, Inc. # Copyright (c) 2019 Intel Corp. +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 config SAM0_RTC_TIMER bool "Atmel SAM0 series RTC timer" default y - depends on DT_HAS_ATMEL_SAM0_RTC_ENABLED + depends on DT_HAS_ATMEL_SAM0_RTC_ENABLED \ + && $(dt_nodelabel_bool_prop,rtc,systimer) select PINCTRL select TICKLESS_CAPABLE help diff --git a/dts/arm/atmel/samc2x.dtsi b/dts/arm/atmel/samc2x.dtsi index ba70408eb466..d6ba356b3de8 100644 --- a/dts/arm/atmel/samc2x.dtsi +++ b/dts/arm/atmel/samc2x.dtsi @@ -1,6 +1,6 @@ /* * Copyright (c) 2022 Kamil Serwus - * Copyright (c) 2024 Gerson Fernando Budke + * Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,8 @@ port-b = &portb; port-c = &portc; + rtc = &rtc; + sercom-0 = &sercom0; sercom-1 = &sercom1; sercom-2 = &sercom2; @@ -274,6 +276,9 @@ atmel,assigned-clocks = <&osc32kctrl 0>; atmel,assigned-clock-names = "OSC32KCTRL"; status = "disabled"; + + alarms-count = <1>; + cal-constant = <(4096 * 240)>; }; }; }; diff --git a/dts/arm/atmel/samd2x.dtsi b/dts/arm/atmel/samd2x.dtsi index af2d30f47b90..b889c1a6cdd3 100644 --- a/dts/arm/atmel/samd2x.dtsi +++ b/dts/arm/atmel/samd2x.dtsi @@ -1,6 +1,6 @@ /* * Copyright (c) 2017 Google LLC. - * Copyright (c) 2024 Gerson Fernando Budke + * Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,6 +19,8 @@ port-a = &porta; port-b = &portb; + rtc = &rtc; + sercom-0 = &sercom0; sercom-1 = &sercom1; sercom-2 = &sercom2; @@ -189,7 +191,12 @@ compatible = "atmel,sam0-rtc"; reg = <0x40001400 0x1C>; interrupts = <3 0>; + clocks = <&gclk 4 4>, <&pm 0x18 5>; + clock-names = "GCLK", "PM"; status = "disabled"; + + alarms-count = <1>; + cal-constant = <(1024 * 976)>; }; adc: adc@42004000 { diff --git a/dts/arm/atmel/samd5x.dtsi b/dts/arm/atmel/samd5x.dtsi index 6282b5100938..65123ef0e1da 100644 --- a/dts/arm/atmel/samd5x.dtsi +++ b/dts/arm/atmel/samd5x.dtsi @@ -1,6 +1,6 @@ /* * Copyright (c) 2019 ML!PA Consulting GmbH - * Copyright (c) 2024 Gerson Fernando Budke + * Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -21,6 +21,8 @@ port-c = &portc; port-d = &portd; + rtc = &rtc; + sercom-0 = &sercom0; sercom-1 = &sercom1; sercom-2 = &sercom2; @@ -45,6 +47,7 @@ }; chosen { + zephyr,flash-controller = &nvmctrl; zephyr,entropy = &trng; zephyr,flash-controller = &nvmctrl; }; @@ -331,6 +334,9 @@ atmel,assigned-clocks = <&osc32kctrl 0>; atmel,assigned-clock-names = "OSC32KCTRL"; status = "disabled"; + + alarms-count = <2>; + cal-constant = <(8192 * 128)>; }; adc0: adc@43001c00 { diff --git a/dts/arm/atmel/saml2x.dtsi b/dts/arm/atmel/saml2x.dtsi index efbdd64d869c..f682ac0126aa 100644 --- a/dts/arm/atmel/saml2x.dtsi +++ b/dts/arm/atmel/saml2x.dtsi @@ -1,6 +1,6 @@ /* * Copyright (c) 2021 Argentum Systems Ltd. - * Copyright (c) 2024 Gerson Fernando Budke + * Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,6 +26,8 @@ sercom-4 = &sercom4; sercom-5 = &sercom5; + rtc = &rtc; + tc-4 = &tc4; watchdog0 = &wdog; @@ -203,6 +205,9 @@ atmel,assigned-clocks = <&osc32kctrl 0>; atmel,assigned-clock-names = "OSC32KCTRL"; status = "disabled"; + + alarms-count = <1>; + cal-constant = <(8192 * 128)>; }; adc: adc@43000c00 { diff --git a/dts/bindings/rtc/atmel,sam0-rtc.yaml b/dts/bindings/rtc/atmel,sam0-rtc.yaml index b14f30440506..db3ed230884f 100644 --- a/dts/bindings/rtc/atmel,sam0-rtc.yaml +++ b/dts/bindings/rtc/atmel,sam0-rtc.yaml @@ -1,5 +1,5 @@ # Copyright (c) 2018 omSquare s.r.o. -# Copyright (c) 2024 Gerson Fernando Budke +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 description: Atmel SAM0 RTC @@ -7,7 +7,7 @@ description: Atmel SAM0 RTC compatible: "atmel,sam0-rtc" include: - - name: rtc.yaml + - name: rtc-device.yaml - name: pinctrl-device.yaml - name: atmel,assigned-clocks.yaml @@ -26,3 +26,105 @@ properties: atmel,assigned-clock-names: required: true + + systimer: + type: boolean + description: | + Selects RTC peripheral to be used as a system timer and replace + the ARM systick. When this option is selected the normal RTC + functionality is in exclusive mode and the normal RTC functions + will not be available. + + The systimer exclusive functionality can be enabled using the + following devicetree entry: + + &rtc { + status = "okay"; + systimer; + }; + + cal-constant: + type: int + required: true + description: | + Define the constant used to calculate the calibration. More + information can be found in the datasheet of each SoC series + at RTC Frequency Correction topic. + + Example: + Correction in ppm = (FREQCORR.VALUE * 1e6 ppm) / (8192 * 128) + + &rtc { + cal-constant = <8192 * 128>; + }; + + counter-mode: + type: string + enum: + - "count-32" + - "count-16" + - "clock" + description: | + Configure the RTC counter operating mode. In mode 0, the counter + register is configured as a 32-bit counter. In mode 1, simmilar + to mode 0, the counter register is only 16-bit counter. In mode + 2 the counter register is configured as a clock/calendar. + + &rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; + }; + + prescaler: + type: int + enum: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + - 128 + - 256 + - 512 + - 1024 + description: | + Enable CLKOUT at given frequency. When disabled, CLKOUT pin is LOW. + The default is 0 and corresponds to the disable the CLKOUT signal. + + &rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; + }; + + event-control-msk: + type: int + default: 0 + description: | + Enable peripheral event sources by bitmask. By default all the channels + are always disabled. + + Event Table: + + bit Event Source + 0 Periodic Interval 0 Event Output + 1 Periodic Interval 1 Event Output + 2 Periodic Interval 2 Event Output + 3 Periodic Interval 3 Event Output + 4 Periodic Interval 4 Event Output + 5 Periodic Interval 5 Event Output + 6 Periodic Interval 6 Event Output + 7 Periodic Interval 7 Event Output + 8 Compare/Alarm 0 Event Output + 9 Compare/Alarm 1 Event Output + 14 Tamper Event Output + 15 Overflow Event Output + 16 Tamper Event Input + + Example how to enable Compare/Alarm 0 Event Output: + &rtc { + event-control-msk = <100>; + }; From ad6c68c17c0fcdf11229536966e29c3c7e446d93 Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Fri, 1 Nov 2024 16:49:53 +0100 Subject: [PATCH 2/4] soc: atmel: sam0: Configure GCLK[4] to RTC Configure the Generic Clock Generator to be used as source to RTC. The index 4 is now reserved to RTC. Signed-off-by: Gerson Fernando Budke --- soc/atmel/sam0/common/Kconfig.samd2x | 16 ++++ soc/atmel/sam0/common/Kconfig.samd5x | 29 +++++++ soc/atmel/sam0/common/soc_samc2x.c | 12 ++- soc/atmel/sam0/common/soc_samd2x.c | 108 +++++++++++---------------- soc/atmel/sam0/common/soc_samd5x.c | 49 ++++++++++-- soc/atmel/sam0/common/soc_saml2x.c | 9 ++- 6 files changed, 150 insertions(+), 73 deletions(-) diff --git a/soc/atmel/sam0/common/Kconfig.samd2x b/soc/atmel/sam0/common/Kconfig.samd2x index f23b65579631..11f76f608a14 100644 --- a/soc/atmel/sam0/common/Kconfig.samd2x +++ b/soc/atmel/sam0/common/Kconfig.samd2x @@ -1,3 +1,4 @@ +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 if SOC_SERIES_SAMD20 || SOC_SERIES_SAMD21 || SOC_SERIES_SAMR21 @@ -35,6 +36,21 @@ config SOC_ATMEL_SAMD_XOSC32K_CRYSTAL Enable the crystal oscillator (if disabled, expect a clock signal on XIN32). +DT_ATMEL_RTC := $(dt_nodelabel_path,rtc) +DT_ATMEL_RTC_COUNTER_CLOCK_MODE := $(dt_node_str_prop_equals,$(DT_ATMEL_RTC),counter-mode,clock) + +config SOC_ATMEL_SAMD_XOSC32K_PRESCALER + int "XOSC32 Generic Clock Prescaler" + range 1 512 + default 32 if "$(DT_ATMEL_RTC_COUNTER_CLOCK_MODE)" + default 1 + depends on SOC_ATMEL_SAMD_XOSC32K + help + Configure the prescaler for the generic clock output + connected on the xosc32. When using RTC in calendar mode + the GCLK should be divided by 32 to RTC receive the + 1024 Hz reference clock. + config SOC_ATMEL_SAMD_XOSC bool "External 0.4..32 MHz clock source" help diff --git a/soc/atmel/sam0/common/Kconfig.samd5x b/soc/atmel/sam0/common/Kconfig.samd5x index 969887e99da2..e18616862e1f 100644 --- a/soc/atmel/sam0/common/Kconfig.samd5x +++ b/soc/atmel/sam0/common/Kconfig.samd5x @@ -1,4 +1,5 @@ # Copyright (c) 2019 ML!PA Consulting GmbH +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 if SOC_SERIES_SAMD51 || SOC_SERIES_SAME51 || SOC_SERIES_SAME53 || SOC_SERIES_SAME54 @@ -18,6 +19,34 @@ config SOC_ATMEL_SAMD5X_XOSC32K_STARTUP help Selects the startup time for the external 32 kHz crystal oscillator. +config SOC_ATMEL_SAMD5X_XOSC32K_CRYSTAL + bool "External 32.768 kHz clock is a crystal oscillator" + depends on SOC_ATMEL_SAMD5X_XOSC32K + default y + help + Enable the crystal oscillator (if disabled, expect a clock + signal on XIN32). + +config SOC_ATMEL_SAMD5X_XOSC32K_GAIN_HS + bool "XOSC32 High Speed gain" + depends on SOC_ATMEL_SAMD5X_XOSC32K + help + When this option is selected the gain is set High Speed + instead standard. + +DT_ATMEL_RTC := $(dt_nodelabel_path,rtc) +DT_ATMEL_RTC_COUNTER_CLOCK_MODE := $(dt_node_str_prop_equals,$(DT_ATMEL_RTC),counter-mode,clock) + +config SOC_ATMEL_SAMD5X_OSC32K_PRESCALER + int "XOSC32 Generic Clock Prescaler" + range 1 512 + default 32 if "$(DT_ATMEL_RTC_COUNTER_CLOCK_MODE)" + default 1 + help + Configure the prescaler for the generic clock output connected on the + xosc32 or osculp32k. When using RTC in calendar mode the GCLK should + be divided by 32 to RTC receive the 1024 Hz reference clock. + choice prompt "Main clock source" default SOC_ATMEL_SAMD5X_DEFAULT_AS_MAIN diff --git a/soc/atmel/sam0/common/soc_samc2x.c b/soc/atmel/sam0/common/soc_samc2x.c index 1581b62eef2e..bfcb111b0c2c 100644 --- a/soc/atmel/sam0/common/soc_samc2x.c +++ b/soc/atmel/sam0/common/soc_samc2x.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2022 Kamil Serwus - * Copyright (c) 2023 Gerson Fernando Budke + * Copyright (c) 2023-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,11 +10,19 @@ * @brief Atmel SAMC MCU series initialization code */ +/* GCLK Gen 0 -> GCLK_MAIN @ OSC48M + * GCLK Gen 2 -> WDT @ reserved + * GCLK Gen 0 -> ADC @ OSC48M + * GCLK Gen 4 -> RTC @ reserved + */ + #include #include #include #include +/* clang-format off */ + static void flash_waitstates_init(void) { /* Two wait state at 48 MHz. */ @@ -50,3 +58,5 @@ void soc_reset_hook(void) mclk_init(); gclks_init(); } + +/* clang-format on */ diff --git a/soc/atmel/sam0/common/soc_samd2x.c b/soc/atmel/sam0/common/soc_samd2x.c index f915459a5766..f332100bf644 100644 --- a/soc/atmel/sam0/common/soc_samd2x.c +++ b/soc/atmel/sam0/common/soc_samd2x.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2017 Google LLC. * Copyright (c) 2023 Ionut Catalin Pavel - * Copyright (c) 2023 Gerson Fernando Budke + * Copyright (c) 2023-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,7 @@ * GCLK Gen 1 -> DFLL48M (variable) * GCLK Gen 2 -> WDT @ 32768 Hz * GCLK Gen 3 -> ADC @ 8 MHz + * GCLK Gen 4 -> RTC @ xtal 32768 Hz */ #include @@ -29,6 +30,8 @@ #include #include +/* clang-format off */ + /** * Fix different naming conventions for SAMD20 */ @@ -38,6 +41,23 @@ #define FUSES_OSC32K_CAL_Msk FUSES_OSC32KCAL_Msk #endif +static void gclk_connect(uint8_t gclk, uint32_t src, uint32_t div, uint32_t flags) +{ + GCLK->GENDIV.reg = GCLK_GENDIV_ID(gclk) + | GCLK_GENDIV_DIV(div); + + while (GCLK->STATUS.bit.SYNCBUSY) { + } + + GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk) + | GCLK_GENCTRL_GENEN + | src + | flags; + + while (GCLK->STATUS.bit.SYNCBUSY) { + } +} + static inline void osc8m_init(void) { uint32_t reg; @@ -57,19 +77,7 @@ static inline void osc8m_init(void) /* Use 8Mhz clock as gclk_main to allow switching between clocks * when using bootloaders */ - GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) - | GCLK_GENDIV_DIV(0); - - while (GCLK->STATUS.bit.SYNCBUSY) { - } - - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) - | GCLK_GENCTRL_SRC_OSC8M - | GCLK_GENCTRL_IDC - | GCLK_GENCTRL_GENEN; - - while (GCLK->STATUS.bit.SYNCBUSY) { - } + gclk_connect(0, GCLK_GENCTRL_SRC_OSC8M, 0, 0); } #if !CONFIG_SOC_ATMEL_SAMD_OSC32K || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN @@ -149,28 +157,17 @@ static inline void dfll48m_init(void) { uint32_t fcal, ccal; - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(1) + gclk_connect(1, #if CONFIG_SOC_ATMEL_SAMD_OSC32K_AS_MAIN - | GCLK_GENCTRL_SRC_OSC32K + GCLK_GENCTRL_SRC_OSC32K, #elif CONFIG_SOC_ATMEL_SAMD_XOSC32K_AS_MAIN - | GCLK_GENCTRL_SRC_XOSC32K + GCLK_GENCTRL_SRC_XOSC32K, #elif CONFIG_SOC_ATMEL_SAMD_OSC8M_AS_MAIN - | GCLK_GENCTRL_SRC_OSC8M + GCLK_GENCTRL_SRC_OSC8M, #elif CONFIG_SOC_ATMEL_SAMD_XOSC_AS_MAIN - | GCLK_GENCTRL_SRC_XOSC + GCLK_GENCTRL_SRC_XOSC, #endif - | GCLK_GENCTRL_IDC - | GCLK_GENCTRL_RUNSTDBY - | GCLK_GENCTRL_GENEN; - - while (GCLK->STATUS.bit.SYNCBUSY) { - } - - GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) - | GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK1_DIV); - - while (GCLK->STATUS.bit.SYNCBUSY) { - } + SOC_ATMEL_SAM0_GCLK1_DIV, GCLK_GENCTRL_RUNSTDBY); /* Route multiplexer 0 to DFLL48M */ GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(0) @@ -222,19 +219,7 @@ static inline void flash_waitstates_init(void) #else static inline void gclk_main_configure(void) { - GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) - | GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK0_DIV); - - while (GCLK->STATUS.bit.SYNCBUSY) { - } - - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(0) - | GCLK_GENCTRL_SRC_DFLL48M - | GCLK_GENCTRL_IDC - | GCLK_GENCTRL_GENEN; - - while (GCLK->STATUS.bit.SYNCBUSY) { - } + gclk_connect(0, GCLK_GENCTRL_SRC_DFLL48M, SOC_ATMEL_SAM0_GCLK0_DIV, 0); } #endif @@ -243,19 +228,16 @@ static inline void gclk_main_configure(void) #else static inline void gclk_adc_configure(void) { - GCLK->GENDIV.reg = GCLK_GENDIV_ID(3) - | GCLK_GENDIV_DIV(SOC_ATMEL_SAM0_GCLK3_DIV); - - while (GCLK->STATUS.bit.SYNCBUSY) { - } - - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(3) - | GCLK_GENCTRL_SRC_DFLL48M - | GCLK_GENCTRL_IDC - | GCLK_GENCTRL_GENEN; + gclk_connect(3, GCLK_GENCTRL_SRC_DFLL48M, SOC_ATMEL_SAM0_GCLK3_DIV, 0); +} +#endif - while (GCLK->STATUS.bit.SYNCBUSY) { - } +#if !CONFIG_RTC_ATMEL_SAM0 || CONFIG_SOC_ATMEL_SAMD_DEFAULT_AS_MAIN +#define gclk_rtc_configure() +#else +static inline void gclk_rtc_configure(void) +{ + gclk_connect(4, GCLK_GENCTRL_SRC_XOSC32K, CONFIG_SOC_ATMEL_SAMD_XOSC32K_PRESCALER, 0); } #endif @@ -264,16 +246,7 @@ static inline void gclk_adc_configure(void) #else static inline void gclk_wdt_configure(void) { - GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) - | GCLK_GENDIV_DIV(4); - - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) - | GCLK_GENCTRL_GENEN - | GCLK_GENCTRL_SRC_OSCULP32K - | GCLK_GENCTRL_DIVSEL; - - while (GCLK->STATUS.bit.SYNCBUSY) { - } + gclk_connect(2, GCLK_GENCTRL_SRC_OSCULP32K, 4, GCLK_GENCTRL_DIVSEL); } #endif @@ -296,6 +269,9 @@ void soc_reset_hook(void) flash_waitstates_init(); gclk_main_configure(); gclk_adc_configure(); + gclk_rtc_configure(); gclk_wdt_configure(); osc8m_disable(); } + +/* clang-format on */ diff --git a/soc/atmel/sam0/common/soc_samd5x.c b/soc/atmel/sam0/common/soc_samd5x.c index a12d3891ea83..77ac12e1d132 100644 --- a/soc/atmel/sam0/common/soc_samd5x.c +++ b/soc/atmel/sam0/common/soc_samd5x.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2019 ML!PA Consulting GmbH - * Copyright (c) 2023 Gerson Fernando Budke + * Copyright (c) 2023-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,30 +10,57 @@ * @brief Atmel SAMD MCU series initialization code */ +/* The CPU clock will be configured to the DT requested value, + * and run via DFLL48M. + * + * Reference -> GCLK Gen 1 -> DFLL48M -> GCLK Gen 0 -> GCLK_MAIN + * + * GCLK Gen 0 -> GCLK_MAIN @ DPLL0 + * GCLK Gen 1 -> DFLL48M @ 32768 Hz + * GCLK Gen 2 -> USB @ DFLL48M + * GCLK Gen 3 -> ADC @ reserved + * GCLK Gen 4 -> RTC @ xtal 32768 Hz + */ + #include #include #include #include +/* clang-format off */ + #define SAM0_DFLL_FREQ_HZ (48000000U) #define SAM0_DPLL_FREQ_MIN_HZ (96000000U) #define SAM0_DPLL_FREQ_MAX_HZ (200000000U) #define SAM0_XOSC32K_STARTUP_TIME CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_STARTUP -#if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_AS_MAIN -static void osc32k_init(void) +#if !CONFIG_SOC_ATMEL_SAMD5X_XOSC32K +#define xosc32k_init() +#else +static inline void xosc32k_init(void) { OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE +#if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_CRYSTAL | OSC32KCTRL_XOSC32K_XTALEN +#endif +#if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_GAIN_HS + | OSC32KCTRL_XOSC32K_CGM_HS +#else | OSC32KCTRL_XOSC32K_CGM_XT +#endif | OSC32KCTRL_XOSC32K_EN32K + | OSC32KCTRL_XOSC32K_EN1K | OSC32KCTRL_XOSC32K_RUNSTDBY - | OSC32KCTRL_XOSC32K_STARTUP(SAM0_XOSC32K_STARTUP_TIME) - ; + | OSC32KCTRL_XOSC32K_STARTUP(SAM0_XOSC32K_STARTUP_TIME); while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) { } +} +#endif +#if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K_AS_MAIN +static void osc32k_init(void) +{ GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_SOURCE_XOSC32K) | GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN; @@ -132,6 +159,7 @@ void soc_reset_hook(void) CMCC->CTRL.bit.CEN = 0; gclk_reset(); + xosc32k_init(); osc32k_init(); dfll_init(); dpll_init(0, dfll_div * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC); @@ -141,4 +169,15 @@ void soc_reset_hook(void) /* connect GCLK2 to 48 MHz DFLL for USB */ gclk_connect(2, GCLK_SOURCE_DFLL48M, 0); + + /* connect GCLK4 to xosc32k for RTC. The output is 1024 Hz*/ + gclk_connect(4, +#if CONFIG_SOC_ATMEL_SAMD5X_XOSC32K + GCLK_SOURCE_XOSC32K, +#else + GCLK_SOURCE_OSCULP32K, +#endif + CONFIG_SOC_ATMEL_SAMD5X_OSC32K_PRESCALER); } + +/* clang-format on */ diff --git a/soc/atmel/sam0/common/soc_saml2x.c b/soc/atmel/sam0/common/soc_saml2x.c index 3dd657a52f22..d65831f785a5 100644 --- a/soc/atmel/sam0/common/soc_saml2x.c +++ b/soc/atmel/sam0/common/soc_saml2x.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2021 Argentum Systems Ltd. - * Copyright (c) 2023 Gerson Fernando Budke + * Copyright (c) 2023-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,8 @@ #include #include +/* clang-format off */ + /* the SAML21 currently operates only in Performance Level 2... sleep * and low-power operation are not currently supported by the BSP * @@ -27,6 +29,7 @@ * GCLK Gen 1 -> DFLL48M (variable) * GCLK Gen 2 -> USB @ 48 MHz * GCLK Gen 3 -> ADC @ 24 MHz (further /2 in the ADC peripheral) + * GCLK Gen 4 -> RTC @ reserved */ static inline void gclk_reset(void) @@ -57,6 +60,7 @@ static inline void osc32k_init(void) | !OSC32KCTRL_OSC32K_ONDEMAND | OSC32KCTRL_OSC32K_RUNSTDBY | OSC32KCTRL_OSC32K_EN32K + | OSC32KCTRL_OSC32K_EN1K | OSC32KCTRL_OSC32K_ENABLE; /* wait for ready */ @@ -75,6 +79,7 @@ static inline void xosc32k_init(void) | !OSC32KCTRL_XOSC32K_ONDEMAND | OSC32KCTRL_XOSC32K_RUNSTDBY | OSC32KCTRL_XOSC32K_EN32K + | OSC32KCTRL_XOSC32K_EN1K #if CONFIG_SOC_ATMEL_SAML_XOSC32K_CRYSTAL | OSC32KCTRL_XOSC32K_XTALEN #endif @@ -266,3 +271,5 @@ void soc_reset_hook(void) gclk_usb_configure(); gclk_adc_configure(); } + +/* clang-format on */ From 81fea409a511ec72547e9a1adc50c613a8159011 Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Sun, 17 Nov 2024 14:47:53 +0100 Subject: [PATCH 3/4] drivers: rtc: sam0: Add realtime clock driver Add Atmel sam0 rtc calendar driver. Signed-off-by: Gerson Fernando Budke --- drivers/rtc/CMakeLists.txt | 1 + drivers/rtc/Kconfig | 1 + drivers/rtc/Kconfig.sam0 | 10 + drivers/rtc/rtc_sam0.c | 641 ++++++++++++++++++ .../rtc/rtc_api/boards/samc21n_xpro.conf | 3 + .../rtc/rtc_api/boards/samd20_xpro.conf | 3 + .../rtc/rtc_api/boards/same54_xpro.conf | 3 + .../rtc/rtc_api/boards/saml21_xpro.conf | 3 + .../rtc/rtc_api/boards/samr21_xpro.conf | 3 + 9 files changed, 668 insertions(+) create mode 100644 drivers/rtc/Kconfig.sam0 create mode 100644 drivers/rtc/rtc_sam0.c create mode 100644 tests/drivers/rtc/rtc_api/boards/samc21n_xpro.conf create mode 100644 tests/drivers/rtc/rtc_api/boards/samd20_xpro.conf create mode 100644 tests/drivers/rtc/rtc_api/boards/same54_xpro.conf create mode 100644 tests/drivers/rtc/rtc_api/boards/saml21_xpro.conf create mode 100644 tests/drivers/rtc/rtc_api/boards/samr21_xpro.conf diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt index 4d1de3222636..d2cbe253bc17 100644 --- a/drivers/rtc/CMakeLists.txt +++ b/drivers/rtc/CMakeLists.txt @@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_RTC_SHELL rtc_shell.c) zephyr_library_sources_ifdef(CONFIG_RTC_FAKE rtc_fake.c) zephyr_library_sources_ifdef(CONFIG_RTC_SMARTBOND rtc_smartbond.c) zephyr_library_sources_ifdef(CONFIG_RTC_ATMEL_SAM rtc_sam.c) +zephyr_library_sources_ifdef(CONFIG_RTC_ATMEL_SAM0 rtc_sam0.c) zephyr_library_sources_ifdef(CONFIG_RTC_RPI_PICO rtc_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_RTC_RV3028 rtc_rv3028.c) zephyr_library_sources_ifdef(CONFIG_RTC_NUMAKER rtc_numaker.c) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 03027a236aae..b8779c77201e 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -54,6 +54,7 @@ source "drivers/rtc/Kconfig.pcf8563" source "drivers/rtc/Kconfig.rpi_pico" source "drivers/rtc/Kconfig.rv3028" source "drivers/rtc/Kconfig.sam" +source "drivers/rtc/Kconfig.sam0" source "drivers/rtc/Kconfig.smartbond" source "drivers/rtc/Kconfig.stm32" source "drivers/rtc/Kconfig.numaker" diff --git a/drivers/rtc/Kconfig.sam0 b/drivers/rtc/Kconfig.sam0 new file mode 100644 index 000000000000..5dccc7b357c7 --- /dev/null +++ b/drivers/rtc/Kconfig.sam0 @@ -0,0 +1,10 @@ +# Copyright (c) 2024-2025 Gerson Fernando Budke +# SPDX-License-Identifier: Apache-2.0 + +config RTC_ATMEL_SAM0 + bool "Atmel SAM RTC driver" + default y + depends on DT_HAS_ATMEL_SAM0_RTC_ENABLED \ + && !$(dt_nodelabel_bool_prop,rtc,systimer) + help + Atmel Real-Time Clock (RTC) driver used on SAM0 SoC series. diff --git a/drivers/rtc/rtc_sam0.c b/drivers/rtc/rtc_sam0.c new file mode 100644 index 000000000000..3295a8efd86c --- /dev/null +++ b/drivers/rtc/rtc_sam0.c @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2024-2025 Gerson Fernando Budke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT atmel_sam0_rtc + +/** @file + * @brief RTC driver for Atmel SAM0 MCU family. + */ + +#include + +#include +#include +#include "rtc_utils.h" + +#include +LOG_MODULE_REGISTER(rtc_sam0, CONFIG_RTC_LOG_LEVEL); + +/* clang-format off */ + +#define RTC_SAM0_TIME_MASK \ + (RTC_ALARM_TIME_MASK_SECOND \ + | RTC_ALARM_TIME_MASK_MINUTE \ + | RTC_ALARM_TIME_MASK_HOUR \ + | RTC_ALARM_TIME_MASK_MONTHDAY \ + | RTC_ALARM_TIME_MASK_MONTH \ + | RTC_ALARM_TIME_MASK_YEAR \ + ) + +#define RTC_SAM0_CALIBRATE_PPB_MAX (127) +#define RTC_SAM0_CALIBRATE_PPB_QUANTA (1000) + +enum rtc_sam0_counter_mode { + COUNTER_MODE_0, + COUNTER_MODE_1, + COUNTER_MODE_2, +}; + +struct rtc_sam0_config { + Rtc *regs; + enum rtc_sam0_counter_mode mode; + uint16_t prescaler; + + volatile uint32_t *mclk; + uint32_t mclk_mask; + uint32_t gclk_gen; + uint16_t gclk_id; + bool has_gclk; + bool has_osc32kctrl; + uint8_t osc32_src; + uint32_t evt_ctrl_msk; + +#ifdef CONFIG_RTC_ALARM + uint8_t alarms_count; +#endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_CALIBRATION + int32_t cal_constant; +#endif +}; + +struct rtc_sam0_data_cb { + rtc_alarm_callback cb; + void *cb_data; +}; + +struct rtc_sam0_data { + struct k_spinlock lock; +#ifdef CONFIG_RTC_ALARM + struct rtc_sam0_data_cb *const alarms; +#endif /* CONFIG_RTC_ALARM */ +}; + +static inline void rtc_sam0_sync(Rtc *rtc) +{ + /* Wait for synchronization */ +#ifdef MCLK + while (rtc->MODE0.SYNCBUSY.reg & RTC_MODE0_SYNCBUSY_MASK) { + } +#else + while (rtc->MODE0.STATUS.reg & RTC_STATUS_SYNCBUSY) { + } +#endif +} + +static int rtc_sam0_set_time(const struct device *dev, const struct rtc_time *timeptr) +{ + const struct rtc_sam0_config *cfg = dev->config; + struct rtc_sam0_data *data = dev->data; + RtcMode2 *regs = &cfg->regs->MODE2; + uint32_t datetime = 0; + + if (rtc_utils_validate_rtc_time(timeptr, RTC_SAM0_TIME_MASK) == false) { + return -EINVAL; + } + + datetime |= RTC_MODE2_CLOCK_SECOND(timeptr->tm_sec); + datetime |= RTC_MODE2_CLOCK_MINUTE(timeptr->tm_min); + datetime |= RTC_MODE2_CLOCK_HOUR(timeptr->tm_hour); + datetime |= RTC_MODE2_CLOCK_DAY(timeptr->tm_mday); + datetime |= RTC_MODE2_CLOCK_MONTH(timeptr->tm_mon + 1); + datetime |= RTC_MODE2_CLOCK_YEAR(timeptr->tm_year - 99); + + k_spinlock_key_t key = k_spin_lock(&data->lock); + +#ifdef MCLK + regs->CTRLA.reg &= ~RTC_MODE0_CTRLA_ENABLE; + rtc_sam0_sync(cfg->regs); + regs->CLOCK.reg = datetime; + regs->CTRLA.reg |= RTC_MODE0_CTRLA_ENABLE; +#else + regs->CTRL.reg &= ~RTC_MODE0_CTRL_ENABLE; + rtc_sam0_sync(cfg->regs); + regs->CLOCK.reg = datetime; + regs->CTRL.reg |= RTC_MODE0_CTRL_ENABLE; +#endif + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int rtc_sam0_get_time(const struct device *dev, struct rtc_time *timeptr) +{ + const struct rtc_sam0_config *cfg = dev->config; + RTC_MODE2_CLOCK_Type calendar = cfg->regs->MODE2.CLOCK; + + timeptr->tm_sec = calendar.bit.SECOND; + timeptr->tm_min = calendar.bit.MINUTE; + timeptr->tm_hour = calendar.bit.HOUR; + timeptr->tm_mday = calendar.bit.DAY; + timeptr->tm_mon = calendar.bit.MONTH - 1; + timeptr->tm_year = calendar.bit.YEAR + 99; + timeptr->tm_wday = -1; + timeptr->tm_yday = -1; + timeptr->tm_isdst = -1; + timeptr->tm_nsec = 0; + + LOG_DBG("D/M/Y H:M:S %02d/%02d/%02d %02d:%02d:%02d", + timeptr->tm_mday, timeptr->tm_mon + 1, timeptr->tm_year - 99, + timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); + + return 0; +} + +#ifdef CONFIG_RTC_ALARM +static inline uint32_t rtc_sam0_datetime_from_tm(const struct rtc_time *timeptr, + uint32_t mask) +{ + uint32_t datetime = 0; + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + datetime |= RTC_MODE2_CLOCK_SECOND(timeptr->tm_sec); + } + + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + datetime |= RTC_MODE2_CLOCK_MINUTE(timeptr->tm_min); + } + + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + datetime |= RTC_MODE2_CLOCK_HOUR(timeptr->tm_hour); + } + + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + datetime |= RTC_MODE2_CLOCK_DAY(timeptr->tm_mday); + } + + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + datetime |= RTC_MODE2_CLOCK_MONTH(timeptr->tm_mon + 1); + } + + if (mask & RTC_ALARM_TIME_MASK_YEAR) { + datetime |= RTC_MODE2_CLOCK_YEAR(timeptr->tm_year - 99); + } + + return datetime; +} + +static inline void rtc_sam0_tm_from_datetime(struct rtc_time *timeptr, uint32_t mask, + RTC_MODE2_ALARM_Type calendar) +{ + memset(timeptr, 0x00, sizeof(struct rtc_time)); + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + timeptr->tm_sec = calendar.bit.SECOND; + } + + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + timeptr->tm_min = calendar.bit.MINUTE; + } + + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + timeptr->tm_hour = calendar.bit.HOUR; + } + + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + timeptr->tm_mday = calendar.bit.DAY; + } + + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + timeptr->tm_mon = calendar.bit.MONTH - 1; + } + + if (mask & RTC_ALARM_TIME_MASK_YEAR) { + timeptr->tm_year = calendar.bit.YEAR + 99; + } + + timeptr->tm_wday = -1; + timeptr->tm_yday = -1; + timeptr->tm_isdst = -1; + timeptr->tm_nsec = 0; +} + +static inline uint32_t rtc_sam0_alarm_msk_from_mask(uint32_t mask) +{ + uint32_t alarm_mask = 0; + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + alarm_mask = RTC_MODE2_MASK_SEL_SS_Val; + } + + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + alarm_mask = RTC_MODE2_MASK_SEL_MMSS_Val; + } + + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + alarm_mask = RTC_MODE2_MASK_SEL_HHMMSS_Val; + } + + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + alarm_mask = RTC_MODE2_MASK_SEL_DDHHMMSS_Val; + } + + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + alarm_mask = RTC_MODE2_MASK_SEL_MMDDHHMMSS_Val; + } + + if (mask & RTC_ALARM_TIME_MASK_YEAR) { + alarm_mask = RTC_MODE2_MASK_SEL_YYMMDDHHMMSS_Val; + } + + return alarm_mask; +} + +static inline uint32_t rtc_sam0_mask_from_alarm_msk(uint32_t alarm_mask) +{ + uint32_t mask = 0; + + switch (alarm_mask) { + case RTC_MODE2_MASK_SEL_YYMMDDHHMMSS_Val: + mask |= RTC_ALARM_TIME_MASK_YEAR; + __fallthrough; + case RTC_MODE2_MASK_SEL_MMDDHHMMSS_Val: + mask |= RTC_ALARM_TIME_MASK_MONTH; + __fallthrough; + case RTC_MODE2_MASK_SEL_DDHHMMSS_Val: + mask |= RTC_ALARM_TIME_MASK_MONTHDAY; + __fallthrough; + case RTC_MODE2_MASK_SEL_HHMMSS_Val: + mask |= RTC_ALARM_TIME_MASK_HOUR; + __fallthrough; + case RTC_MODE2_MASK_SEL_MMSS_Val: + mask |= RTC_ALARM_TIME_MASK_MINUTE; + __fallthrough; + case RTC_MODE2_MASK_SEL_SS_Val: + mask |= RTC_ALARM_TIME_MASK_SECOND; + break; + default: + break; + } + + return mask; +} + +static int rtc_sam0_alarm_get_supported_fields(const struct device *dev, uint16_t id, + uint16_t *mask) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + + *mask = RTC_SAM0_TIME_MASK; + + return 0; +} + +static int rtc_sam0_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, + const struct rtc_time *timeptr) +{ + const struct rtc_sam0_config *cfg = dev->config; + struct rtc_sam0_data *data = dev->data; + RtcMode2 *regs = &cfg->regs->MODE2; + uint32_t mask_supported = RTC_SAM0_TIME_MASK; + uint32_t datetime; + uint32_t alarm_msk; + + if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { + return -EINVAL; + } + + if ((mask > 0) && (timeptr == NULL)) { + return -EINVAL; + } + + if (mask & ~mask_supported) { + return -EINVAL; + } + + if (rtc_utils_validate_rtc_time(timeptr, mask) == false) { + return -EINVAL; + } + + datetime = rtc_sam0_datetime_from_tm(timeptr, mask); + alarm_msk = rtc_sam0_alarm_msk_from_mask(mask); + + LOG_DBG("S: datetime: %d, mask: %d", datetime, alarm_msk); + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + irq_disable(DT_INST_IRQN(0)); + + rtc_sam0_sync(cfg->regs); + regs->Mode2Alarm[id].ALARM.reg = datetime; + regs->Mode2Alarm[id].MASK.reg = RTC_MODE2_MASK_SEL(alarm_msk); + regs->INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM(BIT(id)); + + irq_enable(DT_INST_IRQN(0)); + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int rtc_sam0_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, + struct rtc_time *timeptr) +{ + const struct rtc_sam0_config *cfg = dev->config; + struct rtc_sam0_data *data = dev->data; + RtcMode2 *regs = &cfg->regs->MODE2; + RTC_MODE2_ALARM_Type datetime; + uint32_t alarm_msk; + + if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { + return -EINVAL; + } + + if ((mask == NULL) || (timeptr == NULL)) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + rtc_sam0_sync(cfg->regs); + + datetime = regs->Mode2Alarm[id].ALARM; + alarm_msk = regs->Mode2Alarm[id].MASK.reg; + + LOG_DBG("G: datetime: %d, mask: %d", datetime.reg, alarm_msk); + + k_spin_unlock(&data->lock, key); + + *mask = rtc_sam0_mask_from_alarm_msk(alarm_msk); + + rtc_sam0_tm_from_datetime(timeptr, *mask, datetime); + + return 0; +} + +static int rtc_sam0_alarm_is_pending(const struct device *dev, uint16_t id) +{ + const struct rtc_sam0_config *cfg = dev->config; + struct rtc_sam0_data *data = dev->data; + RtcMode2 *regs = &cfg->regs->MODE2; + + if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + if ((regs->INTFLAG.reg & RTC_MODE2_INTFLAG_ALARM(BIT(id))) == 0) { + k_spin_unlock(&data->lock, key); + + return 0; + } + + regs->INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM(BIT(id)); + + k_spin_unlock(&data->lock, key); + + return 1; +} + +static int rtc_sam0_alarm_set_callback(const struct device *dev, uint16_t id, + rtc_alarm_callback callback, void *user_data) +{ + const struct rtc_sam0_config *cfg = dev->config; + struct rtc_sam0_data *data = dev->data; + RtcMode2 *regs = &cfg->regs->MODE2; + + if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + data->alarms[id].cb = callback; + data->alarms[id].cb_data = user_data; + + if (callback) { + regs->INTENSET.reg = RTC_MODE2_INTENSET_ALARM(BIT(id)); + } else { + regs->INTENCLR.reg = RTC_MODE2_INTENCLR_ALARM(BIT(id)); + } + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static void rtc_sam0_isr(const struct device *dev) +{ + const struct rtc_sam0_config *cfg = dev->config; + struct rtc_sam0_data *data = dev->data; + RtcMode2 *regs = &cfg->regs->MODE2; + uint32_t int_flags = regs->INTFLAG.reg; + + for (int i = 0; i < cfg->alarms_count; ++i) { + if (int_flags & RTC_MODE2_INTFLAG_ALARM(BIT(i))) { + if (data->alarms[i].cb != NULL) { + data->alarms[i].cb(dev, i, data->alarms[i].cb_data); + } + } + } + + regs->INTFLAG.reg |= int_flags; +} + +#endif /* CONFIG_RTC_ALARM */ + +#ifdef CONFIG_RTC_CALIBRATION +static int rtc_sam0_set_calibration(const struct device *dev, int32_t calibration) +{ + const struct rtc_sam0_config *cfg = dev->config; + RtcMode2 *regs = &cfg->regs->MODE2; + int32_t correction = calibration / (1000000000 / cfg->cal_constant); + uint32_t abs_correction = abs(correction); + + LOG_DBG("Correction: %d, Absolute: %d, Calibration: %d", + correction, abs_correction, calibration); + + if (abs_correction == 0) { + regs->FREQCORR.reg = 0; + return 0; + } + + if (abs_correction > RTC_SAM0_CALIBRATE_PPB_MAX) { + LOG_ERR("The calibration %d result in an out of range value %d", + calibration, abs_correction); + return -EINVAL; + } + + rtc_sam0_sync(cfg->regs); + regs->FREQCORR.reg = RTC_FREQCORR_VALUE(abs_correction) + | (correction < 0 ? RTC_FREQCORR_SIGN : 0); + + LOG_DBG("W REG: 0x%02x", regs->FREQCORR.reg); + + return 0; +} + +static int rtc_sam0_get_calibration(const struct device *dev, int32_t *calibration) +{ + const struct rtc_sam0_config *cfg = dev->config; + RtcMode2 *regs = &cfg->regs->MODE2; + int32_t correction; + + if (calibration == NULL) { + return -EINVAL; + } + + correction = regs->FREQCORR.bit.VALUE; + + if (correction == 0) { + *calibration = 0; + } else { + *calibration = (correction * 1000000000) / cfg->cal_constant; + } + + if (regs->FREQCORR.bit.SIGN) { + *calibration *= -1; + } + + LOG_DBG("R REG: 0x%02x", regs->FREQCORR.reg); + + return 0; +} +#endif /* CONFIG_RTC_CALIBRATION */ + +static int rtc_sam0_init(const struct device *dev) +{ + const struct rtc_sam0_config *cfg = dev->config; + RtcMode0 *regs = &cfg->regs->MODE0; + + LOG_DBG("Counter Mode %d selected", cfg->mode); + LOG_DBG("gclk_id: %d, gclk_gen: %d, prescaler: %d, osc32k: %d", + cfg->gclk_id, cfg->gclk_gen, cfg->prescaler, cfg->osc32_src); + + *cfg->mclk |= cfg->mclk_mask; + +#ifdef MCLK + if (cfg->has_gclk) { + GCLK->PCHCTRL[cfg->gclk_id].reg = GCLK_PCHCTRL_CHEN + | GCLK_PCHCTRL_GEN(cfg->gclk_gen); + } +#else + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN + | GCLK_CLKCTRL_GEN(cfg->gclk_gen) + | GCLK_CLKCTRL_ID(cfg->gclk_id); +#endif + rtc_sam0_sync(cfg->regs); + +#ifdef MCLK + if (cfg->has_osc32kctrl) { + OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL(cfg->osc32_src); + } +#endif + + rtc_sam0_sync(cfg->regs); + regs->EVCTRL.reg = (cfg->evt_ctrl_msk & RTC_MODE0_EVCTRL_MASK); + +#ifdef MCLK + regs->CTRLA.reg = RTC_MODE0_CTRLA_ENABLE + | RTC_MODE0_CTRLA_COUNTSYNC + | RTC_MODE0_CTRLA_MODE(cfg->mode) + | RTC_MODE0_CTRLA_PRESCALER(cfg->prescaler + 1); +#else + regs->CTRL.reg = RTC_MODE0_CTRL_ENABLE + | RTC_MODE0_CTRL_MODE(cfg->mode) + | RTC_MODE0_CTRL_PRESCALER(cfg->prescaler); +#endif + + regs->INTFLAG.reg = 0; +#ifdef CONFIG_RTC_ALARM + IRQ_CONNECT(DT_INST_IRQN(0), + DT_INST_IRQ(0, priority), + rtc_sam0_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQN(0)); +#endif + return 0; +} + +static const struct rtc_driver_api rtc_sam0_driver_api = { + .set_time = rtc_sam0_set_time, + .get_time = rtc_sam0_get_time, +#ifdef CONFIG_RTC_ALARM + .alarm_get_supported_fields = rtc_sam0_alarm_get_supported_fields, + .alarm_set_time = rtc_sam0_alarm_set_time, + .alarm_get_time = rtc_sam0_alarm_get_time, + .alarm_is_pending = rtc_sam0_alarm_is_pending, + .alarm_set_callback = rtc_sam0_alarm_set_callback, +#endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_CALIBRATION + .set_calibration = rtc_sam0_set_calibration, + .get_calibration = rtc_sam0_get_calibration, +#endif /* CONFIG_RTC_CALIBRATION */ +}; + +#define ASSIGNED_CLOCKS_CELL_BY_NAME \ + ATMEL_SAM0_DT_INST_ASSIGNED_CLOCKS_CELL_BY_NAME + +#define RTC_SAM0_GCLK(n) \ + COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(n, gclk), \ + ( \ + .has_gclk = true, \ + .gclk_gen = ASSIGNED_CLOCKS_CELL_BY_NAME(n, gclk, gen), \ + .gclk_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, id) \ + ), \ + ( \ + .has_gclk = false, \ + .gclk_gen = 0, \ + .gclk_id = 0 \ + )) + +#define RTC_SAM0_OSC32KCTRL(n) \ + COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(n, osc32kctrl), \ + ( \ + .has_osc32kctrl = true, \ + .osc32_src = ASSIGNED_CLOCKS_CELL_BY_NAME(n, osc32kctrl, src) \ + ), \ + ( \ + .has_osc32kctrl = false, \ + .osc32_src = 0 \ + )) + +#define RTC_SAM0_DEVICE(n) \ + BUILD_ASSERT(DT_INST_NODE_HAS_PROP(n, counter_mode), \ + "sam0:rtc: Missing counter-mode devicetree property"); \ + BUILD_ASSERT(DT_INST_NODE_HAS_PROP(n, prescaler), \ + "sam0:rtc: Missing prescaler devicetree property"); \ + \ + static const struct rtc_sam0_config rtc_sam0_config_##n = { \ + .regs = (Rtc *)DT_INST_REG_ADDR(n), \ + .mode = DT_INST_ENUM_IDX(n, counter_mode), \ + .prescaler = DT_INST_ENUM_IDX(n, prescaler), \ + .evt_ctrl_msk = DT_INST_PROP(n, event_control_msk), \ + RTC_SAM0_GCLK(n), \ + RTC_SAM0_OSC32KCTRL(n), \ + .mclk = ATMEL_SAM0_DT_INST_MCLK_PM_REG_ADDR_OFFSET(n), \ + .mclk_mask = ATMEL_SAM0_DT_INST_MCLK_PM_PERIPH_MASK(n, bit), \ + IF_ENABLED(CONFIG_RTC_ALARM, ( \ + .alarms_count = DT_INST_PROP(n, alarms_count), \ + )) \ + IF_ENABLED(CONFIG_RTC_CALIBRATION, ( \ + .cal_constant = DT_INST_PROP(n, cal_constant), \ + )) \ + }; \ + \ + IF_ENABLED(CONFIG_RTC_ALARM, ( \ + static struct rtc_sam0_data_cb \ + rtc_sam0_data_cb_##n[DT_INST_PROP(n, alarms_count)] = {}; \ + )) \ + \ + static struct rtc_sam0_data rtc_sam0_data_##n = { \ + IF_ENABLED(CONFIG_RTC_ALARM, ( \ + .alarms = rtc_sam0_data_cb_##n, \ + )) \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, rtc_sam0_init, \ + NULL, \ + &rtc_sam0_data_##n, \ + &rtc_sam0_config_##n, POST_KERNEL, \ + CONFIG_RTC_INIT_PRIORITY, \ + &rtc_sam0_driver_api); \ + +DT_INST_FOREACH_STATUS_OKAY(RTC_SAM0_DEVICE); + +/* clang-format on */ diff --git a/tests/drivers/rtc/rtc_api/boards/samc21n_xpro.conf b/tests/drivers/rtc/rtc_api/boards/samc21n_xpro.conf new file mode 100644 index 000000000000..80e21536ba35 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/samc21n_xpro.conf @@ -0,0 +1,3 @@ +CONFIG_RTC_ALARM=y +CONFIG_TEST_RTC_ALARM_TIME_MASK=63 +CONFIG_RTC_CALIBRATION=y diff --git a/tests/drivers/rtc/rtc_api/boards/samd20_xpro.conf b/tests/drivers/rtc/rtc_api/boards/samd20_xpro.conf new file mode 100644 index 000000000000..80e21536ba35 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/samd20_xpro.conf @@ -0,0 +1,3 @@ +CONFIG_RTC_ALARM=y +CONFIG_TEST_RTC_ALARM_TIME_MASK=63 +CONFIG_RTC_CALIBRATION=y diff --git a/tests/drivers/rtc/rtc_api/boards/same54_xpro.conf b/tests/drivers/rtc/rtc_api/boards/same54_xpro.conf new file mode 100644 index 000000000000..80e21536ba35 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/same54_xpro.conf @@ -0,0 +1,3 @@ +CONFIG_RTC_ALARM=y +CONFIG_TEST_RTC_ALARM_TIME_MASK=63 +CONFIG_RTC_CALIBRATION=y diff --git a/tests/drivers/rtc/rtc_api/boards/saml21_xpro.conf b/tests/drivers/rtc/rtc_api/boards/saml21_xpro.conf new file mode 100644 index 000000000000..80e21536ba35 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/saml21_xpro.conf @@ -0,0 +1,3 @@ +CONFIG_RTC_ALARM=y +CONFIG_TEST_RTC_ALARM_TIME_MASK=63 +CONFIG_RTC_CALIBRATION=y diff --git a/tests/drivers/rtc/rtc_api/boards/samr21_xpro.conf b/tests/drivers/rtc/rtc_api/boards/samr21_xpro.conf new file mode 100644 index 000000000000..80e21536ba35 --- /dev/null +++ b/tests/drivers/rtc/rtc_api/boards/samr21_xpro.conf @@ -0,0 +1,3 @@ +CONFIG_RTC_ALARM=y +CONFIG_TEST_RTC_ALARM_TIME_MASK=63 +CONFIG_RTC_CALIBRATION=y From 85c2eb05d7a3432abebd1d978e2aa81f60b6fc7e Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Sun, 17 Nov 2024 14:48:32 +0100 Subject: [PATCH 4/4] boards: atmel: sam0: Add rtc configs Add configurations to allow use of RTC driver and tests. Signed-off-by: Gerson Fernando Budke --- boards/atmel/sam0/samc21n_xpro/samc21n_xpro.dts | 9 +++++++++ boards/atmel/sam0/samc21n_xpro/samc21n_xpro.yaml | 3 ++- boards/atmel/sam0/samd20_xpro/samd20_xpro.dts | 7 +++++++ boards/atmel/sam0/samd20_xpro/samd20_xpro.yaml | 3 ++- boards/atmel/sam0/samd21_xpro/samd21_xpro.dts | 8 +++++++- boards/atmel/sam0/samd21_xpro/samd21_xpro.yaml | 3 ++- boards/atmel/sam0/same54_xpro/same54_xpro.dts | 9 +++++++++ boards/atmel/sam0/same54_xpro/same54_xpro.yaml | 1 + boards/atmel/sam0/saml21_xpro/saml21_xpro.dts | 9 +++++++++ boards/atmel/sam0/saml21_xpro/saml21_xpro.yaml | 3 ++- boards/atmel/sam0/samr21_xpro/samr21_xpro.dts | 8 +++++++- boards/atmel/sam0/samr21_xpro/samr21_xpro.yaml | 1 + boards/atmel/sam0/samr34_xpro/samr34_xpro.dts | 9 +++++++++ boards/atmel/sam0/samr34_xpro/samr34_xpro.yaml | 3 ++- 14 files changed, 69 insertions(+), 7 deletions(-) diff --git a/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.dts b/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.dts index 3827dc104d99..dc49e1016c30 100644 --- a/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.dts +++ b/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.dts @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 Kamil Serwus + # Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -82,6 +83,14 @@ pinctrl-names = "default"; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; + + atmel,assigned-clocks = <&osc32kctrl 4>; +}; + &sercom0 { status = "okay"; compatible = "atmel,sam0-uart"; diff --git a/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.yaml b/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.yaml index e825d468e673..3c32cfef3226 100644 --- a/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.yaml +++ b/boards/atmel/sam0/samc21n_xpro/samc21n_xpro.yaml @@ -1,5 +1,5 @@ # Copyright (c) 2022 Kamil Serwus -# Copyright (c) 2024 Gerson Fernando Budke +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 identifier: samc21n_xpro name: SAM C21N Xplained Pro @@ -17,6 +17,7 @@ supported: - gpio - i2c - pwm + - rtc - spi - uart vendor: atmel diff --git a/boards/atmel/sam0/samd20_xpro/samd20_xpro.dts b/boards/atmel/sam0/samd20_xpro/samd20_xpro.dts index 83f5df99c05a..6e465087aefc 100644 --- a/boards/atmel/sam0/samd20_xpro/samd20_xpro.dts +++ b/boards/atmel/sam0/samd20_xpro/samd20_xpro.dts @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Sean Nyekjaer + # Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -51,6 +52,12 @@ clock-frequency = ; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; +}; + &sercom0 { status = "okay"; compatible = "atmel,sam0-spi"; diff --git a/boards/atmel/sam0/samd20_xpro/samd20_xpro.yaml b/boards/atmel/sam0/samd20_xpro/samd20_xpro.yaml index 02e57bc98186..06077f79cd39 100644 --- a/boards/atmel/sam0/samd20_xpro/samd20_xpro.yaml +++ b/boards/atmel/sam0/samd20_xpro/samd20_xpro.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Gerson Fernando Budke +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 identifier: samd20_xpro @@ -14,6 +14,7 @@ supported: - flash - gpio - i2c + - rtc - spi - uart - watchdog diff --git a/boards/atmel/sam0/samd21_xpro/samd21_xpro.dts b/boards/atmel/sam0/samd21_xpro/samd21_xpro.dts index f390bc106de9..72c0b815b8bb 100644 --- a/boards/atmel/sam0/samd21_xpro/samd21_xpro.dts +++ b/boards/atmel/sam0/samd21_xpro/samd21_xpro.dts @@ -1,6 +1,6 @@ /* * Copyright (c) 2018 Bryan O'Donoghue - * Copyright (c) 2024 Gerson Fernando Budke + # Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,6 +71,12 @@ pinctrl-names = "default"; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; +}; + &sercom0 { status = "okay"; compatible = "atmel,sam0-uart"; diff --git a/boards/atmel/sam0/samd21_xpro/samd21_xpro.yaml b/boards/atmel/sam0/samd21_xpro/samd21_xpro.yaml index c9fd20b3ad8f..2ffa9435ca52 100644 --- a/boards/atmel/sam0/samd21_xpro/samd21_xpro.yaml +++ b/boards/atmel/sam0/samd21_xpro/samd21_xpro.yaml @@ -1,5 +1,5 @@ # Copyright (c) 2018 Bryan O'Donoghue -# Copyright (c) 2024 Gerson Fernando Budke +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 identifier: samd21_xpro @@ -17,6 +17,7 @@ supported: - gpio - i2c - pwm + - rtc - spi - uart - usb_device diff --git a/boards/atmel/sam0/same54_xpro/same54_xpro.dts b/boards/atmel/sam0/same54_xpro/same54_xpro.dts index f8d957727cd4..b6b9ff9d78dc 100644 --- a/boards/atmel/sam0/same54_xpro/same54_xpro.dts +++ b/boards/atmel/sam0/same54_xpro/same54_xpro.dts @@ -1,5 +1,6 @@ /* * Copyright (c) 2019 Benjamin Valentin + # Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -68,6 +69,14 @@ pinctrl-names = "default"; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; + + atmel,assigned-clocks = <&osc32kctrl 4>; +}; + &sercom2 { status = "okay"; compatible = "atmel,sam0-uart"; diff --git a/boards/atmel/sam0/same54_xpro/same54_xpro.yaml b/boards/atmel/sam0/same54_xpro/same54_xpro.yaml index 35fae1c81717..bd46418551fd 100644 --- a/boards/atmel/sam0/same54_xpro/same54_xpro.yaml +++ b/boards/atmel/sam0/same54_xpro/same54_xpro.yaml @@ -18,6 +18,7 @@ supported: - i2c - netif:eth - pwm + - rtc - spi - uart - usb_device diff --git a/boards/atmel/sam0/saml21_xpro/saml21_xpro.dts b/boards/atmel/sam0/saml21_xpro/saml21_xpro.dts index c0e0e43d666b..f1a14ab071be 100644 --- a/boards/atmel/sam0/saml21_xpro/saml21_xpro.dts +++ b/boards/atmel/sam0/saml21_xpro/saml21_xpro.dts @@ -1,5 +1,6 @@ /* * Copyright (c) 2021 Argentum Systems Ltd. + # Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,6 +72,14 @@ pinctrl-names = "default"; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; + + atmel,assigned-clocks = <&osc32kctrl 4>; +}; + &sercom0 { status = "okay"; compatible = "atmel,sam0-spi"; diff --git a/boards/atmel/sam0/saml21_xpro/saml21_xpro.yaml b/boards/atmel/sam0/saml21_xpro/saml21_xpro.yaml index 7b2d40bdcc8b..9588bef980df 100644 --- a/boards/atmel/sam0/saml21_xpro/saml21_xpro.yaml +++ b/boards/atmel/sam0/saml21_xpro/saml21_xpro.yaml @@ -1,5 +1,5 @@ # Copyright (c) 2021 Argentum Systems Ltd. -# Copyright (c) 2024 Gerson Fernando Budke +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 identifier: saml21_xpro name: SAM L21 Xplained Pro @@ -17,6 +17,7 @@ supported: - gpio - i2c - pwm + - rtc - spi - uart - usb_device diff --git a/boards/atmel/sam0/samr21_xpro/samr21_xpro.dts b/boards/atmel/sam0/samr21_xpro/samr21_xpro.dts index e53fdc80f554..5b7514c26d87 100644 --- a/boards/atmel/sam0/samr21_xpro/samr21_xpro.dts +++ b/boards/atmel/sam0/samr21_xpro/samr21_xpro.dts @@ -1,6 +1,6 @@ /* * Copyright (c) 2019 Benjamin Valentin - * Copyright (c) 2019-2024 Gerson Fernando Budke + * Copyright (c) 2019-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -122,6 +122,12 @@ pinctrl-names = "default"; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; +}; + &sercom0 { status = "okay"; compatible = "atmel,sam0-uart"; diff --git a/boards/atmel/sam0/samr21_xpro/samr21_xpro.yaml b/boards/atmel/sam0/samr21_xpro/samr21_xpro.yaml index ce752682efdf..77e032878b2e 100644 --- a/boards/atmel/sam0/samr21_xpro/samr21_xpro.yaml +++ b/boards/atmel/sam0/samr21_xpro/samr21_xpro.yaml @@ -19,6 +19,7 @@ supported: - i2c - netif - pwm + - rtc - spi - uart - usb_device diff --git a/boards/atmel/sam0/samr34_xpro/samr34_xpro.dts b/boards/atmel/sam0/samr34_xpro/samr34_xpro.dts index 964cd8af4dfc..cb267d86dba1 100644 --- a/boards/atmel/sam0/samr34_xpro/samr34_xpro.dts +++ b/boards/atmel/sam0/samr34_xpro/samr34_xpro.dts @@ -1,5 +1,6 @@ /* * Copyright (c) 2021 Argentum Systems Ltd. + # Copyright (c) 2024-2025 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,6 +72,14 @@ pinctrl-names = "default"; }; +&rtc { + status = "okay"; + counter-mode = "clock"; + prescaler = <1024>; + + atmel,assigned-clocks = <&osc32kctrl 4>; +}; + &sercom0 { status = "okay"; compatible = "atmel,sam0-uart"; diff --git a/boards/atmel/sam0/samr34_xpro/samr34_xpro.yaml b/boards/atmel/sam0/samr34_xpro/samr34_xpro.yaml index 530499d9cbe2..c43651715260 100644 --- a/boards/atmel/sam0/samr34_xpro/samr34_xpro.yaml +++ b/boards/atmel/sam0/samr34_xpro/samr34_xpro.yaml @@ -1,5 +1,5 @@ # Copyright (c) 2021 Argentum Systems Ltd. -# Copyright (c) 2024 Gerson Fernando Budke +# Copyright (c) 2024-2025 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 identifier: samr34_xpro name: SAM R34 Xplained Pro @@ -17,6 +17,7 @@ supported: - gpio - i2c - pwm + - rtc - spi - uart - usb_device