Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drivers: RTC: Initial implementation of RTC for IFX cyw920829m2evk_02 platform #79343

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards/infineon/cyw920829m2evk_02/cyw920829m2evk_02.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ supported:
- watchdog
- spi
- i2c
- rtc

vendor: infineon
1 change: 1 addition & 0 deletions drivers/rtc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ zephyr_library_sources_ifdef(CONFIG_RTC_AMBIQ rtc_ambiq.c)
zephyr_library_sources_ifdef(CONFIG_RTC_DS1307 rtc_ds1307.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c)
zephyr_library_sources_ifdef(CONFIG_RTC_EMUL rtc_emul.c)
zephyr_library_sources_ifdef(CONFIG_RTC_INFINEON_CAT1 rtc_ifx_cat1.c)
zephyr_library_sources_ifdef(CONFIG_RTC_PCF8523 rtc_pcf8523.c)
zephyr_library_sources_ifdef(CONFIG_RTC_PCF8563 rtc_pcf8563.c)
zephyr_library_sources_ifdef(CONFIG_RTC_MOTOROLA_MC146818 rtc_mc146818.c)
Expand Down
1 change: 1 addition & 0 deletions drivers/rtc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ source "drivers/rtc/Kconfig.ambiq"
source "drivers/rtc/Kconfig.ds1307"
source "drivers/rtc/Kconfig.emul"
source "drivers/rtc/Kconfig.fake"
source "drivers/rtc/Kconfig.ifx_cat1"
source "drivers/rtc/Kconfig.mc146818"
source "drivers/rtc/Kconfig.pcf8523"
source "drivers/rtc/Kconfig.pcf8563"
Expand Down
14 changes: 14 additions & 0 deletions drivers/rtc/Kconfig.ifx_cat1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Infineon CAT1 RTC configuration options

# Copyright (c) 2024 Cypress Semiconductor Corporation (an Infineon company) or
# an affiliate of Cypress Semiconductor Corporation
#
# SPDX-License-Identifier: Apache-2.0

config RTC_INFINEON_CAT1
bool "Infineon CAT1 RTC driver"
default y
depends on DT_HAS_INFINEON_CAT1_RTC_ENABLED
select USE_INFINEON_RTC
help
This option enables the RTC driver for Infineon CAT1 family.
343 changes: 343 additions & 0 deletions drivers/rtc/rtc_ifx_cat1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
/*
* Copyright (c) 2024 Cypress Semiconductor Corporation (an Infineon company) or
* an affiliate of Cypress Semiconductor Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @brief RTC driver for Infineon CAT1 MCU family.
*/

#include <zephyr/drivers/rtc.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <cy_pdl.h>

LOG_MODULE_REGISTER(ifx_cat1_rtc, CONFIG_RTC_LOG_LEVEL);

#define DT_DRV_COMPAT infineon_cat1_rtc

#define _IFX_CAT1_RTC_STATE_UNINITIALIZED 0
#define _IFX_CAT1_RTC_STATE_ENABLED 1
#define _IFX_CAT1_RTC_STATE_TIME_SET 2

#define _IFX_CAT1_RTC_INIT_CENTURY 2000
#define _IFX_CAT1_RTC_TM_YEAR_BASE 1900

#if defined(CONFIG_SOC_FAMILY_INFINEON_CAT1B)
#if defined(SRSS_BACKUP_NUM_BREG3) && (SRSS_BACKUP_NUM_BREG3 > 0)
npal-cy marked this conversation as resolved.
Show resolved Hide resolved
#define _IFX_CAT1_RTC_BREG (BACKUP->BREG_SET3[SRSS_BACKUP_NUM_BREG3 - 1])
#elif defined(SRSS_BACKUP_NUM_BREG2) && (SRSS_BACKUP_NUM_BREG2 > 0)
#define _IFX_CAT1_RTC_BREG (BACKUP->BREG_SET2[SRSS_BACKUP_NUM_BREG2 - 1])
#elif defined(SRSS_BACKUP_NUM_BREG1) && (SRSS_BACKUP_NUM_BREG1 > 0)
#define _IFX_CAT1_RTC_BREG (BACKUP->BREG_SET1[SRSS_BACKUP_NUM_BREG1 - 1])
#elif defined(SRSS_BACKUP_NUM_BREG0) && (SRSS_BACKUP_NUM_BREG0 > 0)
#define _IFX_CAT1_RTC_BREG (BACKUP->BREG_SET0[SRSS_BACKUP_NUM_BREG0 - 1])
#endif
#endif

#define _IFX_CAT1_RTC_BREG_CENTURY_Pos 0UL
#define _IFX_CAT1_RTC_BREG_CENTURY_Msk 0x0000FFFFUL
#define _IFX_CAT1_RTC_BREG_STATE_Pos 16UL
#define _IFX_CAT1_RTC_BREG_STATE_Msk 0xFFFF0000UL

static const uint32_t _IFX_CAT1_RTC_MAX_RETRY = 10;
static const uint32_t _IFX_CAT1_RTC_RETRY_DELAY_MS = 1;

static cy_stc_rtc_dst_t *_ifx_cat1_rtc_dst;

#ifdef CONFIG_PM
static cy_en_syspm_status_t _ifx_cat1_rtc_syspm_callback(cy_stc_syspm_callback_params_t *params,
npal-cy marked this conversation as resolved.
Show resolved Hide resolved
cy_en_syspm_callback_mode_t mode)
{
return Cy_RTC_DeepSleepCallback(params, mode);
}

static cy_stc_syspm_callback_params_t _ifx_cat1_rtc_pm_cb_params = {NULL, NULL};
static cy_stc_syspm_callback_t _ifx_cat1_rtc_pm_cb = {
.callback = &_ifx_cat1_rtc_syspm_callback,
.type = CY_SYSPM_DEEPSLEEP,
.callbackParams = &_ifx_cat1_rtc_pm_cb_params,
};
#endif /* CONFIG_PM */

#define _IFX_CAT1_RTC_WAIT_ONE_MS() Cy_SysLib_Delay(_IFX_CAT1_RTC_RETRY_DELAY_MS);

/* Internal macro to validate RTC year parameter falls within 21st century */
#define IFX_CAT1_RTC_VALID_CENTURY(year) ((year) >= _IFX_CAT1_RTC_INIT_CENTURY)

#define MAX_IFX_CAT1_CAL (60)

/* Convert parts per billion to groupings of 128 ticks added or removed from one hour of clock
* cycles at 32768 Hz.
*
* ROUND_DOWN(ppb * 32768Hz * 60min * 60sec / 1000000000, 128) / 128
* ROUND_DOWN(ppb * 117964800 / 1000000000, 128) / 128
* ROUND_DOWN(ppb * 9216 / 78125, 128) / 128
*/
#define PPB_TO_WCO_PULSE_SETS(ppb) ((ROUND_DOWN((ppb * 9216 / 78125), 128)) / 128)

/* Convert groupings of 128 ticks added or removed from one hour of clock cycles at
* 32768 Hz to parts per billion
*
* wps * 128 * 1000000000 / 32768Hz * 60min * 60sec
* wps * 128000000000 / 117964800
* wps * 78125 / 72
*/
#define WCO_PULSE_SETS_TO_PPB(wps) (wps * 78125 / 72)

struct ifx_cat1_rtc_data {
struct k_spinlock lock;
};

static inline uint16_t _ifx_cat1_rtc_get_state(void)
{
return _FLD2VAL(_IFX_CAT1_RTC_BREG_STATE, _IFX_CAT1_RTC_BREG);
}

static inline void _ifx_cat1_rtc_set_state(uint16_t init)
{
_IFX_CAT1_RTC_BREG &= _IFX_CAT1_RTC_BREG_CENTURY_Msk;
_IFX_CAT1_RTC_BREG |= _VAL2FLD(_IFX_CAT1_RTC_BREG_STATE, init);
}

static inline uint16_t _ifx_cat1_rtc_get_century(void)
{
return _FLD2VAL(_IFX_CAT1_RTC_BREG_CENTURY, _IFX_CAT1_RTC_BREG);
}

static inline void _ifx_cat1_rtc_set_century(uint16_t century)
{
_IFX_CAT1_RTC_BREG &= _IFX_CAT1_RTC_BREG_STATE_Msk;
_IFX_CAT1_RTC_BREG |= _VAL2FLD(_IFX_CAT1_RTC_BREG_CENTURY, century);
}

static void _ifx_cat1_rtc_from_pdl_time(cy_stc_rtc_config_t *pdlTime, const int year,
struct rtc_time *z_time)
{
CY_ASSERT(pdlTime != NULL);
CY_ASSERT(z_time != NULL);

z_time->tm_sec = (int)pdlTime->sec;
z_time->tm_min = (int)pdlTime->min;
z_time->tm_hour = (int)pdlTime->hour;
z_time->tm_mday = (int)pdlTime->date;
z_time->tm_year = (int)(year - _IFX_CAT1_RTC_TM_YEAR_BASE);

/* The subtraction of 1 here is to translate between internal ifx_cat1 code and the Zephyr
* driver.
*/
z_time->tm_mon = (int)(pdlTime->month - 1u);
z_time->tm_wday = (int)(pdlTime->dayOfWeek - 1u);

/* year day not known in pdl RTC structure without conversion */
z_time->tm_yday = -1;

/* daylight savings currently marked as unknown */
z_time->tm_isdst = -1;

/* nanoseconds not tracked by ifx code. Set to value indicating unknown */
z_time->tm_nsec = 0;
}

static void _ifx_cat1_rtc_isr_handler(void)
{
Cy_RTC_Interrupt(_ifx_cat1_rtc_dst, NULL != _ifx_cat1_rtc_dst);
}

void _ifx_cat1_rtc_century_interrupt(void)
{
/* The century is stored in its own register so when a "century interrupt"
* occurs at a rollover. The current century is retrieved and 100 is added
* to it and the register is reset to reflect the new century.
* i.e. 1999->2000
*/
_ifx_cat1_rtc_set_century(_ifx_cat1_rtc_get_century() + 100);
ifyall marked this conversation as resolved.
Show resolved Hide resolved
}

static int ifx_cat1_rtc_init(const struct device *dev)
{
cy_rslt_t rslt = CY_RSLT_SUCCESS;

Cy_SysClk_ClkBakSetSource(CY_SYSCLK_BAK_IN_CLKLF);

if (_ifx_cat1_rtc_get_state() == _IFX_CAT1_RTC_STATE_UNINITIALIZED) {
if (Cy_RTC_IsExternalResetOccurred()) {
_ifx_cat1_rtc_set_century(_IFX_CAT1_RTC_INIT_CENTURY);
}

#ifdef CONFIG_PM
rslt = Cy_SysPm_RegisterCallback(&_ifx_cat1_rtc_pm_cb)
#endif /* CONFIG_PM */

if (rslt == CY_RSLT_SUCCESS) {
_ifx_cat1_rtc_set_state(_IFX_CAT1_RTC_STATE_ENABLED);
} else {
rslt = -EINVAL;

Check notice on line 178 in drivers/rtc/rtc_ifx_cat1.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/rtc/rtc_ifx_cat1.c:178 - if (rslt == CY_RSLT_SUCCESS) { + if (rslt == CY_RSLT_SUCCESS) + { _ifx_cat1_rtc_set_state(_IFX_CAT1_RTC_STATE_ENABLED); - } else { + } + else + {
}

} else if (_ifx_cat1_rtc_get_state() == _IFX_CAT1_RTC_STATE_ENABLED ||
_ifx_cat1_rtc_get_state() == _IFX_CAT1_RTC_STATE_TIME_SET) {

if (Cy_RTC_GetInterruptStatus() & CY_RTC_INTR_CENTURY) {
_ifx_cat1_rtc_century_interrupt();
}
}

Cy_RTC_ClearInterrupt(CY_RTC_INTR_CENTURY);
Cy_RTC_SetInterruptMask(CY_RTC_INTR_CENTURY);

_ifx_cat1_rtc_dst = NULL;
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), _ifx_cat1_rtc_isr_handler,
DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQN(0));

return rslt;
}

static int ifx_cat1_rtc_set_time(const struct device *dev, const struct rtc_time *timeptr)
{
struct ifx_cat1_rtc_data *data = dev->data;

uint32_t sec = timeptr->tm_sec;
uint32_t min = timeptr->tm_min;
uint32_t hour = timeptr->tm_hour;
uint32_t day = timeptr->tm_mday;
/* The addition of 1 here is to translate between internal ifx_cat1 code and the Zephyr
* driver.
*/
uint32_t mon = timeptr->tm_mon + 1;
ifyall marked this conversation as resolved.
Show resolved Hide resolved
uint32_t year = timeptr->tm_year + _IFX_CAT1_RTC_TM_YEAR_BASE;
uint32_t year2digit = year % 100;

cy_rslt_t rslt;
uint32_t retry = 0;

if (!CY_RTC_IS_SEC_VALID(sec) || !CY_RTC_IS_MIN_VALID(min) || !CY_RTC_IS_HOUR_VALID(hour) ||
!CY_RTC_IS_MONTH_VALID(mon) || !CY_RTC_IS_YEAR_SHORT_VALID(year2digit) ||
!IFX_CAT1_RTC_VALID_CENTURY(year)) {

return -EINVAL;
}
do {
if (retry != 0) {
_IFX_CAT1_RTC_WAIT_ONE_MS();
}

k_spinlock_key_t key = k_spin_lock(&data->lock);

rslt = Cy_RTC_SetDateAndTimeDirect(sec, min, hour, day, mon, year2digit);
if (rslt == CY_RSLT_SUCCESS) {
_ifx_cat1_rtc_set_century((uint16_t)(year) - (uint16_t)(year2digit));
}

k_spin_unlock(&data->lock, key);
++retry;
} while (rslt == CY_RTC_INVALID_STATE && retry < _IFX_CAT1_RTC_MAX_RETRY);

retry = 0;
while (CY_RTC_BUSY == Cy_RTC_GetSyncStatus() && retry < _IFX_CAT1_RTC_MAX_RETRY) {
_IFX_CAT1_RTC_WAIT_ONE_MS();
++retry;
}

if (rslt == CY_RSLT_SUCCESS) {
_ifx_cat1_rtc_set_state(_IFX_CAT1_RTC_STATE_TIME_SET);
return 0;
} else {
return -EINVAL;
}
}

static int ifx_cat1_rtc_get_time(const struct device *dev, struct rtc_time *timeptr)
{
struct ifx_cat1_rtc_data *data = dev->data;

cy_stc_rtc_config_t dateTime = {.hrFormat = CY_RTC_24_HOURS};

if (_ifx_cat1_rtc_get_state() != _IFX_CAT1_RTC_STATE_TIME_SET) {
LOG_ERR("Valid time has not been set with rtc_set_time yet");
return -ENODATA;
}

k_spinlock_key_t key = k_spin_lock(&data->lock);

Cy_RTC_GetDateAndTime(&dateTime);
const int year = (int)(dateTime.year + _ifx_cat1_rtc_get_century());
bjarki-andreasen marked this conversation as resolved.
Show resolved Hide resolved

k_spin_unlock(&data->lock, key);

_ifx_cat1_rtc_from_pdl_time(&dateTime, year, timeptr);

return CY_RSLT_SUCCESS;
}

#ifdef CONFIG_RTC_CALIBRATION
static int ifx_cat1_set_calibration(const struct device *dev, int32_t calibration)
{
cy_rslt_t rslt;

uint8_t uint_calibration;
cy_en_rtc_calib_sign_t calibration_sign;

if (calibration >= 0) {
calibration_sign = CY_RTC_CALIB_SIGN_POSITIVE;
} else {
calibration = abs(calibration);
calibration_sign = CY_RTC_CALIB_SIGN_NEGATIVE;
}

uint_calibration = PPB_TO_WCO_PULSE_SETS(calibration);

/* Maximum calibration value on cat1b of 60 128 tick groupings */
if (MAX_IFX_CAT1_CAL < uint_calibration) {
/* out of supported range */
return -EINVAL;
}

rslt = Cy_RTC_CalibrationControlEnable(uint_calibration, calibration_sign,
CY_RTC_CAL_SEL_CAL1);
if (rslt != CY_RSLT_SUCCESS) {
return -EINVAL;
}

return 0;
}

static int ifx_cat1_get_calibration(const struct device *dev, int32_t *calibration)
{
ARG_UNUSED(dev);

uint32_t hw_calibration = _FLD2VAL(BACKUP_CAL_CTL_CALIB_VAL, BACKUP_CAL_CTL);
cy_en_rtc_calib_sign_t hw_sign =
(cy_en_rtc_calib_sign_t)(_FLD2VAL(BACKUP_CAL_CTL_CALIB_SIGN, BACKUP_CAL_CTL));

if (CY_RTC_CALIB_SIGN_POSITIVE == hw_sign) {
*calibration = WCO_PULSE_SETS_TO_PPB(hw_calibration);
} else {
*calibration = WCO_PULSE_SETS_TO_PPB(hw_calibration) * -1;
}

return 0;
}
#endif /* CONFIG_RTC_CALIBRATION */

static const struct rtc_driver_api ifx_cat1_rtc_driver_api = {
.set_time = ifx_cat1_rtc_set_time,
.get_time = ifx_cat1_rtc_get_time,
#ifdef CONFIG_RTC_CALIBRATION
.set_calibration = ifx_cat1_set_calibration,
.get_calibration = ifx_cat1_get_calibration,
#endif
};

#define INFINEON_CAT1_RTC_INIT(n) \
static struct ifx_cat1_rtc_data ifx_cat1_rtc_data##n; \
\
DEVICE_DT_INST_DEFINE(n, ifx_cat1_rtc_init, NULL, &ifx_cat1_rtc_data##n, \
NULL, PRE_KERNEL_1, CONFIG_RTC_INIT_PRIORITY, \
&ifx_cat1_rtc_driver_api);

Check notice on line 342 in drivers/rtc/rtc_ifx_cat1.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/rtc/rtc_ifx_cat1.c:342 - DEVICE_DT_INST_DEFINE(n, ifx_cat1_rtc_init, NULL, &ifx_cat1_rtc_data##n, \ - NULL, PRE_KERNEL_1, CONFIG_RTC_INIT_PRIORITY, \ - &ifx_cat1_rtc_driver_api); + DEVICE_DT_INST_DEFINE(n, ifx_cat1_rtc_init, NULL, &ifx_cat1_rtc_data##n, NULL, \ + PRE_KERNEL_1, CONFIG_RTC_INIT_PRIORITY, &ifx_cat1_rtc_driver_api);
DT_INST_FOREACH_STATUS_OKAY(INFINEON_CAT1_RTC_INIT)
8 changes: 8 additions & 0 deletions dts/arm/infineon/cat1b/cyw20829/cyw20829.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@
status = "disabled";
};

rtc0: rtc@40220000 {
npal-cy marked this conversation as resolved.
Show resolved Hide resolved
compatible = "infineon,cat1-rtc";
reg = <0x40220000 0x10000>;
interrupts = <10 6>;
alarms-count = <2>;
status = "disabled";
};

counter0_0: counter@404a0000 {
compatible = "infineon,cat1-counter";
reg = <0x404a0000 0x80>;
Expand Down
Loading
Loading