drivers: rtc: Add RTC driver for Infineon XMC4xxx devices
Adds support for settings/getting RTC time and using alarm/update feature. The alarm option needs all fields to be set due to a hardware limitation. RTC shares the same interrupt with the watchdog. Thus shared interrupts must be enabled when WDT and RTC both need to trigger the ISR. Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
This commit is contained in:
parent
a761280318
commit
4ffe418253
8 changed files with 311 additions and 1 deletions
|
|
@ -24,3 +24,4 @@ zephyr_library_sources_ifdef(CONFIG_RTC_ATMEL_SAM rtc_sam.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_RTC_RPI_PICO rtc_rpi_pico.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_RV3028 rtc_rv3028.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_RTC_NUMAKER rtc_numaker.c)
|
zephyr_library_sources_ifdef(CONFIG_RTC_NUMAKER rtc_numaker.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_RTC_XMC4XXX rtc_xmc4xxx.c)
|
||||||
|
|
|
||||||
|
|
@ -56,5 +56,6 @@ source "drivers/rtc/Kconfig.smartbond"
|
||||||
source "drivers/rtc/Kconfig.stm32"
|
source "drivers/rtc/Kconfig.stm32"
|
||||||
source "drivers/rtc/Kconfig.numaker"
|
source "drivers/rtc/Kconfig.numaker"
|
||||||
source "drivers/rtc/Kconfig.rv8263"
|
source "drivers/rtc/Kconfig.rv8263"
|
||||||
|
source "drivers/rtc/Kconfig.xmc4xxx"
|
||||||
|
|
||||||
endif # RTC
|
endif # RTC
|
||||||
|
|
|
||||||
9
drivers/rtc/Kconfig.xmc4xxx
Normal file
9
drivers/rtc/Kconfig.xmc4xxx
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Copyright 2023 Andriy Gelman
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config RTC_XMC4XXX
|
||||||
|
bool "XMC4xxx RTC driver"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_INFINEON_XMC4XXX_RTC_ENABLED
|
||||||
|
help
|
||||||
|
Build RTC driver for XMC4xxx SoCs.
|
||||||
270
drivers/rtc/rtc_xmc4xxx.c
Normal file
270
drivers/rtc/rtc_xmc4xxx.c
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Andriy Gelman
|
||||||
|
* Author: Andriy Gelman <andriy.gelman@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/drivers/rtc.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
#include <soc.h>
|
||||||
|
#include <xmc_rtc.h>
|
||||||
|
#include <xmc_scu.h>
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT infineon_xmc4xxx_rtc
|
||||||
|
|
||||||
|
#define RTC_XMC4XXX_DEFAULT_PRESCALER 0x7fff
|
||||||
|
|
||||||
|
#define RTC_XMC4XXX_SUPPORTED_ALARM_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)
|
||||||
|
|
||||||
|
struct rtc_xmc4xxx_data {
|
||||||
|
#if defined(CONFIG_RTC_ALARM)
|
||||||
|
rtc_alarm_callback alarm_callback;
|
||||||
|
void *alarm_user_data;
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_RTC_UPDATE)
|
||||||
|
rtc_update_callback update_callback;
|
||||||
|
void *update_user_data;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_set_time(const struct device *dev, const struct rtc_time *timeptr)
|
||||||
|
{
|
||||||
|
const struct tm *stdtime;
|
||||||
|
|
||||||
|
if (timeptr == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMC_RTC_Stop();
|
||||||
|
|
||||||
|
stdtime = rtc_time_to_tm((struct rtc_time *)timeptr);
|
||||||
|
XMC_RTC_SetTimeStdFormat(stdtime);
|
||||||
|
|
||||||
|
XMC_RTC_Start();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_get_time(const struct device *dev, struct rtc_time *timeptr)
|
||||||
|
{
|
||||||
|
struct tm *stdtime = rtc_time_to_tm(timeptr);
|
||||||
|
|
||||||
|
if (!XMC_RTC_IsRunning()) {
|
||||||
|
return -ENODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdtime == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMC_RTC_GetTimeStdFormat(stdtime);
|
||||||
|
timeptr->tm_nsec = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
|
||||||
|
static void rtc_xmc4xxx_isr(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct rtc_xmc4xxx_data *dev_data = dev->data;
|
||||||
|
|
||||||
|
uint32_t event = SCU_INTERRUPT->SRRAW;
|
||||||
|
|
||||||
|
#if defined(CONFIG_RTC_ALARM)
|
||||||
|
if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) != 0) {
|
||||||
|
if (dev_data->alarm_callback != NULL) {
|
||||||
|
dev_data->alarm_callback(dev, 0, dev_data->alarm_user_data);
|
||||||
|
}
|
||||||
|
XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_RTC_UPDATE)
|
||||||
|
if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC) != 0) {
|
||||||
|
if (dev_data->update_callback != NULL) {
|
||||||
|
dev_data->update_callback(dev, dev_data->update_user_data);
|
||||||
|
}
|
||||||
|
XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_RTC_ALARM
|
||||||
|
static int rtc_xmc4xxx_alarm_get_supported_fields(const struct device *dev, uint16_t id,
|
||||||
|
uint16_t *mask)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
ARG_UNUSED(id);
|
||||||
|
|
||||||
|
*mask = RTC_XMC4XXX_SUPPORTED_ALARM_MASK;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
|
||||||
|
const struct rtc_time *timeptr)
|
||||||
|
{
|
||||||
|
const struct tm *stdtime = rtc_time_to_tm((struct rtc_time *)timeptr);
|
||||||
|
|
||||||
|
if (id != 0 || (mask > 0 && timeptr == NULL)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask == 0) {
|
||||||
|
XMC_RTC_DisableEvent(XMC_RTC_EVENT_ALARM);
|
||||||
|
XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask != RTC_XMC4XXX_SUPPORTED_ALARM_MASK) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMC_RTC_SetAlarmStdFormat(stdtime);
|
||||||
|
XMC_RTC_EnableEvent(XMC_RTC_EVENT_ALARM);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
|
||||||
|
struct rtc_time *timeptr)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
struct tm *stdtime = rtc_time_to_tm(timeptr);
|
||||||
|
|
||||||
|
if (id != 0 || mask == NULL || timeptr == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*mask = RTC_XMC4XXX_SUPPORTED_ALARM_MASK;
|
||||||
|
|
||||||
|
XMC_RTC_GetAlarmStdFormat(stdtime);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_alarm_is_pending(const struct device *dev, uint16_t id)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
unsigned int key;
|
||||||
|
int alarm = 0;
|
||||||
|
uint32_t event;
|
||||||
|
|
||||||
|
if (id != 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = irq_lock();
|
||||||
|
event = SCU_INTERRUPT->SRRAW;
|
||||||
|
|
||||||
|
if ((event & XMC_SCU_INTERRUPT_EVENT_RTC_ALARM) != 0) {
|
||||||
|
alarm = 1;
|
||||||
|
XMC_SCU_INTERRUPT_ClearEventStatus(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
|
||||||
|
}
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
return alarm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_alarm_set_callback(const struct device *dev, uint16_t id,
|
||||||
|
rtc_alarm_callback callback, void *user_data)
|
||||||
|
{
|
||||||
|
struct rtc_xmc4xxx_data *dev_data = dev->data;
|
||||||
|
unsigned int key;
|
||||||
|
|
||||||
|
if (id != 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = irq_lock();
|
||||||
|
dev_data->alarm_callback = callback;
|
||||||
|
dev_data->alarm_user_data = user_data;
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
if (dev_data->alarm_callback) {
|
||||||
|
XMC_SCU_INTERRUPT_EnableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
|
||||||
|
} else {
|
||||||
|
XMC_SCU_INTERRUPT_DisableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_ALARM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_RTC_ALARM */
|
||||||
|
|
||||||
|
#ifdef CONFIG_RTC_UPDATE
|
||||||
|
static int rtc_xmc4xxx_update_set_callback(const struct device *dev, rtc_update_callback callback,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct rtc_xmc4xxx_data *dev_data = dev->data;
|
||||||
|
unsigned int key;
|
||||||
|
|
||||||
|
key = irq_lock();
|
||||||
|
dev_data->update_callback = callback;
|
||||||
|
dev_data->update_user_data = user_data;
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
if (dev_data->update_callback) {
|
||||||
|
XMC_RTC_EnableEvent(XMC_RTC_EVENT_PERIODIC_SECONDS);
|
||||||
|
XMC_SCU_INTERRUPT_EnableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC);
|
||||||
|
} else {
|
||||||
|
XMC_SCU_INTERRUPT_DisableEvent(XMC_SCU_INTERRUPT_EVENT_RTC_PERIODIC);
|
||||||
|
XMC_RTC_DisableEvent(XMC_RTC_EVENT_PERIODIC_SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_RTC_UPDATE */
|
||||||
|
|
||||||
|
static const struct rtc_driver_api rtc_xmc4xxx_driver_api = {
|
||||||
|
.set_time = rtc_xmc4xxx_set_time,
|
||||||
|
.get_time = rtc_xmc4xxx_get_time,
|
||||||
|
#ifdef CONFIG_RTC_ALARM
|
||||||
|
.alarm_get_supported_fields = rtc_xmc4xxx_alarm_get_supported_fields,
|
||||||
|
.alarm_set_time = rtc_xmc4xxx_alarm_set_time,
|
||||||
|
.alarm_get_time = rtc_xmc4xxx_alarm_get_time,
|
||||||
|
.alarm_is_pending = rtc_xmc4xxx_alarm_is_pending,
|
||||||
|
.alarm_set_callback = rtc_xmc4xxx_alarm_set_callback,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_RTC_UPDATE
|
||||||
|
.update_set_callback = rtc_xmc4xxx_update_set_callback,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
|
||||||
|
static void rtc_xmc4xxx_irq_config(void)
|
||||||
|
{
|
||||||
|
/* RTC and watchdog share the same interrupt. Shared interrupts must */
|
||||||
|
/* be enabled if WDT is enabled and RTC is using alarm or update feature */
|
||||||
|
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), rtc_xmc4xxx_isr,
|
||||||
|
DEVICE_DT_INST_GET(0), 0);
|
||||||
|
irq_enable(DT_INST_IRQN(0));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int rtc_xmc4xxx_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
if (!XMC_RTC_IsRunning()) {
|
||||||
|
if (!XMC_SCU_HIB_IsHibernateDomainEnabled()) {
|
||||||
|
XMC_SCU_HIB_EnableHibernateDomain();
|
||||||
|
}
|
||||||
|
XMC_RTC_SetPrescaler(RTC_XMC4XXX_DEFAULT_PRESCALER);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
|
||||||
|
rtc_xmc4xxx_irq_config();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct rtc_xmc4xxx_data rtc_xmc4xxx_data_0;
|
||||||
|
|
||||||
|
DEVICE_DT_INST_DEFINE(0, rtc_xmc4xxx_init, NULL, &rtc_xmc4xxx_data_0, NULL,
|
||||||
|
POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, &rtc_xmc4xxx_driver_api);
|
||||||
|
|
@ -233,7 +233,15 @@
|
||||||
wdt0: watchdog@50008000 {
|
wdt0: watchdog@50008000 {
|
||||||
compatible = "infineon,xmc4xxx-watchdog";
|
compatible = "infineon,xmc4xxx-watchdog";
|
||||||
reg = <0x50008000 0x4000>;
|
reg = <0x50008000 0x4000>;
|
||||||
interrupts = <0 1>;
|
interrupts = <0 1>; // shared interrupt line with rtc
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
rtc: rtc@50004a00 {
|
||||||
|
compatible = "infineon,xmc4xxx-rtc";
|
||||||
|
reg = <0x50004a00 0x200>;
|
||||||
|
interrupts = <0 1>; // shared interrupt line with wdt0
|
||||||
|
alarms-count = <1>;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
15
dts/bindings/rtc/infineon,xmc4xxx-rtc.yaml
Normal file
15
dts/bindings/rtc/infineon,xmc4xxx-rtc.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2023 Andriy Gelman
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: Infineon XMC4xxx family RTC device
|
||||||
|
|
||||||
|
compatible: "infineon,xmc4xxx-rtc"
|
||||||
|
|
||||||
|
include: rtc-device.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
required: true
|
||||||
|
|
@ -65,4 +65,9 @@ config HAS_XMCLIB_CAN
|
||||||
help
|
help
|
||||||
Enable XMCLIB CAN
|
Enable XMCLIB CAN
|
||||||
|
|
||||||
|
config HAS_XMCLIB_RTC
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Enable XMCLIB RTC
|
||||||
|
|
||||||
endif # HAS_XMCLIB
|
endif # HAS_XMCLIB
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,4 @@ config SOC_SERIES_XMC4XXX
|
||||||
select HAS_XMCLIB_WDT
|
select HAS_XMCLIB_WDT
|
||||||
select HAS_XMCLIB_ETH
|
select HAS_XMCLIB_ETH
|
||||||
select HAS_XMCLIB_CAN
|
select HAS_XMCLIB_CAN
|
||||||
|
select HAS_XMCLIB_RTC
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue