From fe9105a92b49b77b157c28e9d89e38605f518f26 Mon Sep 17 00:00:00 2001 From: Daniel Kampert Date: Thu, 20 Jun 2024 10:29:39 +0200 Subject: [PATCH] - Add patch to port RV-8263-C8 driver from upstream Zephyr to ZSWatch Zephyr - Move patches from "zephyr_patches" directory into "patches" directory - Remove "zephyr_patches" directory --- .../zswatch_nrf5340_cpuapp_5.overlay | 3 +- app/drivers/CMakeLists.txt | 3 +- app/drivers/Kconfig | 3 +- app/drivers/rtc/CMakeLists.txt | 6 - app/drivers/rtc/Kconfig | 3 - app/drivers/rtc/rv8263c8/CMakeLists.txt | 5 - app/drivers/rtc/rv8263c8/Kconfig | 83 -- .../rtc/rv8263c8/microcrystal_rv8263c8.c | 945 ------------------ app/{zephyr_patches => patches}/ntc_fix.patch | 0 app/patches/rv8263_rtc.patch | 866 ++++++++++++++++ 10 files changed, 869 insertions(+), 1048 deletions(-) delete mode 100644 app/drivers/rtc/CMakeLists.txt delete mode 100644 app/drivers/rtc/Kconfig delete mode 100644 app/drivers/rtc/rv8263c8/CMakeLists.txt delete mode 100644 app/drivers/rtc/rv8263c8/Kconfig delete mode 100644 app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c rename app/{zephyr_patches => patches}/ntc_fix.patch (100%) create mode 100644 app/patches/rv8263_rtc.patch diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay index ad548e8f..1ff8f2a0 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay @@ -193,7 +193,6 @@ compatible = "microcrystal,rv-8263-c8"; reg = <0xA2>; status = "okay"; - clkout = <0>; int-gpios = <&gpio1 13 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; }; @@ -367,4 +366,4 @@ reg = <0x400000 0x100000 >; }; }; -}; \ No newline at end of file +}; diff --git a/app/drivers/CMakeLists.txt b/app/drivers/CMakeLists.txt index 5c438f54..cd6d4278 100644 --- a/app/drivers/CMakeLists.txt +++ b/app/drivers/CMakeLists.txt @@ -1,5 +1,4 @@ add_subdirectory(display) add_subdirectory(input) -add_subdirectory(rtc) -add_subdirectory_ifdef(CONFIG_SENSOR sensor) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_SENSOR sensor) diff --git a/app/drivers/Kconfig b/app/drivers/Kconfig index 774a916e..1c492e03 100644 --- a/app/drivers/Kconfig +++ b/app/drivers/Kconfig @@ -1,4 +1,3 @@ rsource "display/Kconfig" rsource "input/Kconfig" -rsource "rtc/Kconfig" -rsource "sensor/Kconfig" \ No newline at end of file +rsource "sensor/Kconfig" diff --git a/app/drivers/rtc/CMakeLists.txt b/app/drivers/rtc/CMakeLists.txt deleted file mode 100644 index 720d23ca..00000000 --- a/app/drivers/rtc/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright (c) 2024, Daniel Kampert -# -# SPDX-License-Identifier: Apache-2.0 -# - -add_subdirectory_ifdef(CONFIG_RTC_RV8263 rv8263c8) \ No newline at end of file diff --git a/app/drivers/rtc/Kconfig b/app/drivers/rtc/Kconfig deleted file mode 100644 index 903dd8f1..00000000 --- a/app/drivers/rtc/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -if RTC - rsource "rv8263c8/Kconfig" -endif \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/CMakeLists.txt b/app/drivers/rtc/rv8263c8/CMakeLists.txt deleted file mode 100644 index 6c34f57d..00000000 --- a/app/drivers/rtc/rv8263c8/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright (c) 2024 Daniel Kampert -# Author: Daniel Kampert - -zephyr_sources(microcrystal_rv8263c8.c) \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/Kconfig b/app/drivers/rtc/rv8263c8/Kconfig deleted file mode 100644 index 53cfe77f..00000000 --- a/app/drivers/rtc/rv8263c8/Kconfig +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright (c) 2024 Daniel Kampert -# Author: Daniel Kampert - -config RTC_RV8263 - bool "Micro Crystal RV-8263-C8 RTC driver" - default y - depends on DT_HAS_MICROCRYSTAL_RV_8263_C8_ENABLED - select I2C - help - Micro Crystal RV-8263-C8 RTC driver. - -if RTC_RV8263 - menu "RV8263 Update" - depends on RTC_UPDATE - - choice - prompt "RV-8263-C8 update handling mode" - default RTC_RV8263_UPDATE_GLOBAL_THREAD - - config RTC_RV8263_UPDATE_GLOBAL_THREAD - depends on GPIO - bool "Use workqueue (Default)" - help - Use the global workqueue to process the updates from the RV-8263-C8. - - config RTC_RV8263_UPDATE_OWN_THREAD - depends on GPIO - bool "Use driver thread" - help - Use a separate thread to process the updates from the RV-8263-C8. - endchoice - - config RTC_RV8263_UPDATE_THREAD_STACK_SIZE - int "Stack size for the RV-8263-C8 update thread" - depends on RTC_RV8263_UPDATE_OWN_THREAD - default 512 - help - Size of the stack used for the thread handling RTC updates and dispatching callbacks. - - config RTC_RV8263_UPDATE_THREAD_PRIORITY - int "RV-8263-C8 update thread priority" - depends on RTC_RV8263_UPDATE_OWN_THREAD - default 0 - help - Priority level for the thread handling RTC updates and dispatching callbacks. - endmenu - - menu "RV8263 Alarm" - depends on RTC_ALARM - - choice - prompt "RV-8263-C8 alarm handling mode" - default RTC_RV8263_ALARM_GLOBAL_THREAD - - config RTC_RV8263_ALARM_GLOBAL_THREAD - depends on GPIO - bool "Use workqueue (Default)" - help - Use the global workqueue to process the interrupts from the RV-8263-C8. - - config RTC_RV8263_ALARM_OWN_THREAD - depends on GPIO - bool "Use driver thread" - help - Use a separate thread to process the alarms from the RV-8263-C8. - endchoice - - config RTC_RV8263_ALARM_THREAD_STACK_SIZE - int "Stack size for the RV-8263-C8 interrupt thread" - depends on RTC_RV8263_ALARM_OWN_THREAD - default 512 - help - Size of the stack used for the thread handling alarms and dispatching callbacks. - - config RTC_RV8263_ALARM_THREAD_PRIORITY - int "RV-8263-C8 alarm thread priority" - depends on RTC_RV8263_ALARM_OWN_THREAD - default 0 - help - Priority level for the thread handling alarms and dispatching callbacks. - endmenu -endif \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c deleted file mode 100644 index 476422a7..00000000 --- a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c +++ /dev/null @@ -1,945 +0,0 @@ -/* microcrystal_rv8263c8.c - Driver for Micro Crystal RV-8263-C8 RTC. - * - * Copyright (c) 2024 Daniel Kampert - * Author: Daniel Kampert - */ - -#include -#include -#include -#include -#include -#include -#include - -#define RV8263C8_REGISTER_CONTROL_1 0x00 -#define RV8263C8_REGISTER_CONTROL_2 0x01 -#define RV8263C8_REGISTER_OFFSET 0x02 -#define RV8263C8_REGISTER_RAM 0x03 -#define RV8263C8_REGISTER_SECONDS 0x04 -#define RV8263C8_REGISTER_MINUTES 0x05 -#define RV8263C8_REGISTER_HOURS 0x06 -#define RV8263C8_REGISTER_DATE 0x07 -#define RV8263C8_REGISTER_WEEKDAY 0x08 -#define RV8263C8_REGISTER_MONTH 0x09 -#define RV8263C8_REGISTER_YEAR 0x0A -#define RV8263C8_REGISTER_SECONDS_ALARM 0x0B -#define RV8263C8_REGISTER_MINUTES_ALARM 0x0C -#define RV8263C8_REGISTER_HOURS_ALARM 0x0D -#define RV8263C8_REGISTER_DATE_ALARM 0x0E -#define RV8263C8_REGISTER_WEEKDAY_ALARM 0x0F -#define RV8263C8_REGISTER_TIMER_VALUE 0x10 -#define RV8263C8_REGISTER_TIMER_MODE 0x11 - -#define RV8263C8_BM_24H_MODE_ENABLE (0x00 << 1) -#define RV8263C8_BM_24H_MODE_DISABLE (0x00 << 1) -#define RV8263C8_BM_CLOCK_ENABLE (0x00 << 5) -#define RV8263C8_BM_CLOCK_DISABLE (0x01 << 5) -#define RV8263C8_BM_ALARM_INT_ENABLE (0x01 << 7) -#define RV8263C8_BM_ALARM_INT_DISABLE (0x00 << 7) -#define RV8263C8_BM_MINUTE_INT_ENABLE (0x01 << 5) -#define RV8263C8_BM_MINUTE_INT_DISABLE (0x00 << 5) -#define RV8263C8_BM_HALF_MINUTE_INT_ENABLE (0x01 << 4) -#define RV8263C8_BM_HALF_MINUTE_INT_DISABLE (0x00 << 4) -#define RV8263C8_BM_ALARM_ENABLE (0x00 << 7) -#define RV8263C8_BM_ALARM_DISABLE (0x01 << 7) -#define RV8263C8_BM_AF (0x01 << 6) -#define RV8263C8_BM_TF (0x01 << 3) -#define RV8263_BM_MODE (0x01 << 7) -#define RV8263C8_BM_SOFTWARE_RESET (0x58) - -#define SECONDS_BITS GENMASK(6, 0) -#define MINUTES_BITS GENMASK(7, 0) -#define HOURS_BITS GENMASK(5, 0) -#define DATE_BITS GENMASK(5, 0) -#define MONTHS_BITS GENMASK(4, 0) -#define WEEKDAY_BITS GENMASK(2, 0) -#define YEAR_BITS GENMASK(7, 0) -#define VALIDATE_24HR BIT(6) - -#define MIN_SEC 0 -#define MAX_SEC 59 -#define MIN_MIN 0 -#define MAX_MIN 59 -#define MIN_HOUR 0 -#define MAX_HOUR 23 -#define MAX_WDAY 7 -#define MIN_WDAY 1 -#define MAX_MDAY 31 -#define MIN_MDAY 1 -#define MAX_MON 12 -#define MIN_MON 1 -#define MIN_YEAR_DIFF 0 // YEAR - 1900 */ -#define MAX_YEAR_DIFF 99 // YEAR - 1999 */ - -#define DT_DRV_COMPAT microcrystal_rv_8263_c8 - -#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) -#define RV8263_USE_INT 1 -/** - * @brief RV-8263-C8 "int_gpios" property must be in the devicetree in order to use the RTC_ALARM or RTC_UPDATE feature. - */ -#if !DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) -#error int-gpios property not available in devicetree! -#endif -#endif - -#ifdef CONFIG_RTC_CALIBRATION -/** - * @brief RV-8263-C8 "offset" property must be in the devicetree in order to use the RTC_CALIBRATION feature. - */ -#if !DT_ANY_INST_HAS_PROP_STATUS_OKAY(offset) -#error offset-property not available in devicetree! -#endif -#endif - -#ifdef CONFIG_RTC_CALIBRATION -/** - * @brief RV-8263-C8 "fast_mode" property must be in the devicetree in order to use the RTC_CALIBRATION feature. - */ -#if !DT_ANY_INST_HAS_PROP_STATUS_OKAY(fast_mode) -#error fast-mode-property not available in devicetree! -#endif -#endif - -LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_RTC_LOG_LEVEL); - -struct rv8263c8_config { - struct i2c_dt_spec i2c_bus; - uint8_t clkout; - bool fast_mode; - int8_t offset; - -#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) - struct gpio_dt_spec int_gpio; -#endif -}; - -struct rv8263c8_data { - struct k_spinlock lock; - -#if defined(CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD) || defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) - const struct device *dev; -#endif - -#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) - struct gpio_callback gpio_cb; -#endif - -#ifdef CONFIG_RTC_ALARM - rtc_alarm_callback alarm_cb; - void *alarm_cb_data; - bool is_alarm_pending; - -#if defined(CONFIG_RTC_RV8263_ALARM_OWN_THREAD) - struct k_sem alarm_sem; -#elif defined(CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD) - struct k_work alarm_work; -#endif -#endif - -#ifdef CONFIG_RTC_UPDATE - rtc_update_callback update_cb; - void *update_cb_data; - -#if defined(CONFIG_RTC_RV8263_UPDATE_OWN_THREAD) - struct k_sem update_sem; -#elif defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) - struct k_work update_work; -#endif -#endif -}; - -#ifdef CONFIG_RTC_RV8263_ALARM_OWN_THREAD -static K_KERNEL_STACK_MEMBER(rv8263c8_alarm_stack, CONFIG_RTC_RV8263_ALARM_THREAD_STACK_SIZE); -static struct k_thread rv8263c8_alarm_thread; -#endif - -#ifdef CONFIG_RTC_RV8263_UPDATE_OWN_THREAD -static K_KERNEL_STACK_MEMBER(rv8263c8_update_stack, CONFIG_RTC_RV8263_UPDATE_THREAD_STACK_SIZE); -static struct k_thread rv8263c8_update_thread; -#endif - -#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) -/** - * @brief - * - * @param p_port - * @param p_cb - * @param pins - */ -static void rv8263c8_gpio_callback_handler(const struct device *p_port, struct gpio_callback *p_cb, - gpio_port_pins_t pins) -{ - ARG_UNUSED(pins); - ARG_UNUSED(p_port); - - struct rv8263c8_data *data = CONTAINER_OF(p_cb, struct rv8263c8_data, gpio_cb); - -#if defined(CONFIG_RTC_RV8263_ALARM_OWN_THREAD) - k_sem_give(&data->alarm_sem); -#elif defined(CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD) - k_work_submit(&data->alarm_work); -#endif - -#if defined(CONFIG_RTC_RV8263_UPDATE_OWN_THREAD) - k_sem_give(&data->update_sem); -#elif defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) - k_work_submit(&data->update_work); -#endif -} -#endif - -#ifdef CONFIG_RTC_ALARM -/** - * @brief - * - * @param p_dev - */ -static void rv8263_process_alarm(const struct device *p_dev) -{ - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - data->is_alarm_pending = true; - - if (data->alarm_cb != NULL) { - LOG_DBG("Calling alarm callback"); - data->alarm_cb(p_dev, 0, data->alarm_cb_data); - } - - i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_AF, RV8263C8_BM_AF); -} - -#if defined(CONFIG_RTC_RV8263_ALARM_OWN_THREAD) -/** - * @brief - * - * @param p_arg1 - * @param p_arg2 - * @param p_arg3 - */ -static void rv8263c8_alarm_thread_func(void *p_arg1, void *p_arg2, void *p_arg3) -{ - const struct device *dev = p_arg1; - struct rv8263c8_data *data = dev->data; - - while (1) { - k_sem_take(&data->alarm_sem, K_FOREVER); - rv8263_process_alarm(dev); - } -} -#elif defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) -/** - * @brief - * - * @param p_work - */ -static void rv8263c8_alarm_worker(struct k_work *p_work) -{ - struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, alarm_work); - - LOG_DBG("Process alarm interrupt from worker"); - - rv8263_process_alarm(data->dev); -} -#endif -#endif - -/** - * @brief - * - * @param p_timeptr - * @param mask - * @return true - * @return false - */ -static bool rv8263c8_validate_alarm(const struct rtc_time *p_timeptr, uint32_t mask) -{ - if ((mask & RTC_ALARM_TIME_MASK_SECOND) && - (p_timeptr->tm_sec < MIN_SEC || p_timeptr->tm_sec > MAX_SEC)) { - return false; - } - - if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && - (p_timeptr->tm_min < MIN_MIN || p_timeptr->tm_min > MAX_MIN)) { - return false; - } - - if ((mask & RTC_ALARM_TIME_MASK_HOUR) && - (p_timeptr->tm_hour < MIN_HOUR || p_timeptr->tm_hour > MAX_HOUR)) { - return false; - } - - return true; -} -#endif - -#ifdef CONFIG_RTC_UPDATE -/** - * @brief This function configures and enables the countdown timer. - * - * @param p_dev Pointer to device strucure - * @return int 0 when successful - */ -static int rv8263c8_update_enable_timer(const struct device *p_dev) -{ - int err; - uint8_t regs; - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if (p_dev == NULL) { - return -EINVAL; - } - - regs = 0; - k_spinlock_key_t key = k_spin_lock(&data->lock); - err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_VALUE, ®s, sizeof(regs)); - if (err < 0) { - goto rv8263c8_update_enable_timer_exit; - } - - // Configure the countdown timer for 1 Hz and enable pulse interrupts. - regs = (0x02 << 3) | (0x01 << 2) | (0x01 << 1) | (0x01 << 0); - err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_MODE, ®s, sizeof(regs)); - -rv8263c8_update_enable_timer_exit: - k_spin_unlock(&data->lock, key); - - return err; -} - -/** - * @brief - * - * @param p_dev - */ -static void rv8263_process_update(const struct device *p_dev) -{ - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if (data->update_cb != NULL) { - LOG_DBG("Calling update callback"); - data->update_cb(p_dev, data->update_cb_data); - } - - rv8263c8_update_enable_timer(p_dev); - i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_TF, RV8263C8_BM_TF); -} - -#if defined(CONFIG_RTC_RV8263_UPDATE_OWN_THREAD) -/** - * @brief - * - * @param p_arg1 - * @param p_arg2 - * @param p_arg3 - */ -static void rv8263c8_update_thread_func(void *p_arg1, void *p_arg2, void *p_arg3) -{ - const struct device *dev = p_arg1; - struct rv8263c8_data *data = dev->data; - - while (1) { - k_sem_take(&data->update_sem, K_FOREVER); - rv8263_process_update(dev); - } -} -#elif defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) -/** - * @brief - * - * @param p_work - */ -static void rv8263c8_update_worker(struct k_work *p_work) -{ - struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, alarm_work); - - LOG_DBG("Process update interrupt from worker"); - - rv8263_process_update(data->dev); -} -#endif -#endif - -/** - * @brief Set time function for RTC API. - * - * @param p_dev Pointer to device strucure - * @param p_timeptr - * @return int 0 when successful - */ -static int rv8263c8_time_set(const struct device *p_dev, const struct rtc_time *p_timeptr) -{ - int err; - uint8_t regs[7]; - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if ((p_dev == NULL) || (p_timeptr == NULL)) { - return -EINVAL; - } - - LOG_DBG("Set time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = %u", - p_timeptr->tm_year, p_timeptr->tm_mon, p_timeptr->tm_mday, p_timeptr->tm_wday, p_timeptr->tm_hour, p_timeptr->tm_min, - p_timeptr->tm_sec); - - regs[0] = bin2bcd(p_timeptr->tm_sec) & SECONDS_BITS; - regs[1] = bin2bcd(p_timeptr->tm_min); - regs[2] = bin2bcd(p_timeptr->tm_hour); - regs[3] = bin2bcd(p_timeptr->tm_wday); - regs[4] = bin2bcd(p_timeptr->tm_mday); - regs[5] = bin2bcd(p_timeptr->tm_mon); - regs[6] = bin2bcd((p_timeptr->tm_year % 100)); - - k_spinlock_key_t key = k_spin_lock(&data->lock); - err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); - k_spin_unlock(&data->lock, key); - - return err; -} - -/** - * @brief Get time function for RTC API. - * - * @param p_dev Pointer to device strucure - * @param p_timeptr - * @return int 0 when successful - */ -static int rv8263c8_time_get(const struct device *p_dev, struct rtc_time *p_timeptr) -{ - int err; - uint8_t regs[7]; - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if ((p_dev == NULL) || (p_timeptr == NULL)) { - return -EINVAL; - } - - k_spinlock_key_t key = k_spin_lock(&data->lock); - err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); - k_spin_unlock(&data->lock, key); - if (err < 0) { - return err; - } - - p_timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS); - p_timeptr->tm_min = bcd2bin(regs[1] & MINUTES_BITS); - p_timeptr->tm_hour = bcd2bin(regs[2] & HOURS_BITS); - p_timeptr->tm_wday = bcd2bin(regs[3] & WEEKDAY_BITS); - p_timeptr->tm_mday = bcd2bin(regs[4] & DATE_BITS); - p_timeptr->tm_mon = bcd2bin(regs[5] & MONTHS_BITS); - p_timeptr->tm_year = bcd2bin(regs[6] & YEAR_BITS); - p_timeptr->tm_year = p_timeptr->tm_year + 100; - - // Unused - p_timeptr->tm_nsec = 0; - p_timeptr->tm_isdst = -1; - p_timeptr->tm_yday = -1; - - // Validate the chip in 24hr mode - if (regs[2] & VALIDATE_24HR) { - return -ENODATA; - } - - LOG_DBG("get time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = %u", - p_timeptr->tm_year, p_timeptr->tm_mon, p_timeptr->tm_mday, p_timeptr->tm_wday, p_timeptr->tm_hour, p_timeptr->tm_min, - p_timeptr->tm_sec); - - return 0; -} - -/** - * @brief Init function for RTC API. - * - * @param p_dev Pointer to device strucure - * @return int 0 when successful - */ -static int rv8263c8_init(const struct device *p_dev) -{ - int err; - int temp; - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if (p_dev == NULL) { - return -EINVAL; - } - - if (!i2c_is_ready_dt(&config->i2c_bus)) { - LOG_ERR("I2C bus not ready!"); - return -ENODEV; - } - - LOG_DBG("Configure RV-8263-C8:"); - LOG_DBG(" ClkOut: %u", config->clkout); - LOG_DBG(" Fast Mode: %u", config->fast_mode); - LOG_DBG(" Offset: %i", config->offset); - - k_spinlock_key_t key = k_spin_lock(&data->lock); - - // Configure the first config register - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_1, - RV8263C8_BM_24H_MODE_DISABLE | RV8263C8_BM_CLOCK_ENABLE); - if (err < 0) { - LOG_ERR("Error while writing CONTROL_1! Error: %i", err); - goto rv8263c8_init_exit; - } - -#ifdef CONFIG_RTC_CALIBRATION - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, RV8263_BM_MODE, config->fast_mode << 7); - if (err < 0) { - LOG_ERR("Error while setting OFFSET mode! Error: %i", err); - goto rv8263c8_init_exit; - } -#endif - - // Configure the second config register - temp = RV8263C8_BM_MINUTE_INT_DISABLE | RV8263C8_BM_HALF_MINUTE_INT_DISABLE | (config->clkout << 0x00); - -#ifdef CONFIG_RTC_ALARM - temp |= RV8263C8_BM_ALARM_INT_ENABLE; -#endif - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, temp); - if (err < 0) { - LOG_ERR("Error while writing CONTROL_2! Error: %i", err); - goto rv8263c8_init_exit; - } - -#ifdef CONFIG_RTC_UPDATE - uint8_t regs = 0; - err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_MODE, ®s, sizeof(regs)); - if (err < 0) { - LOG_ERR("Error while writing CONTROL2! Error: %i", err); - goto rv8263c8_init_exit; - } -#endif - -#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) - if (!gpio_is_ready_dt(&config->int_gpio)) { - LOG_ERR("GPIO not ready!"); - goto rv8263c8_init_exit; - } - - err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); - if (err < 0) { - LOG_ERR("Failed to configure GPIO! Error: %u", err); - goto rv8263c8_init_exit; - } - - err = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_INACTIVE); - if (err < 0) { - LOG_ERR("Failed to configure interrupt! Error: %u", err); - goto rv8263c8_init_exit; - } - - gpio_init_callback(&data->gpio_cb, rv8263c8_gpio_callback_handler, BIT(config->int_gpio.pin)); - - err = gpio_add_callback_dt(&config->int_gpio, &data->gpio_cb); - if (err < 0) { - LOG_ERR("Failed to add GPIO callback! Error: %u", err); - goto rv8263c8_init_exit; - } -#endif - -#ifdef CONFIG_RTC_ALARM -#if defined(CONFIG_RTC_RV8263_ALARM_OWN_THREAD) - k_sem_init(&data->alarm_sem, 0, K_SEM_MAX_LIMIT); - k_thread_create(&rv8263c8_alarm_thread, rv8263c8_alarm_stack, - K_THREAD_STACK_SIZEOF(rv8263c8_alarm_stack), - rv8263c8_alarm_thread_func, (struct device *)p_dev, NULL, - NULL, CONFIG_RTC_RV8263_ALARM_THREAD_PRIORITY, 0, K_NO_WAIT); -#elif defined(CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD) - data->alarm_work.handler = rv8263c8_alarm_worker; -#endif -#endif - -#ifdef CONFIG_RTC_UPDATE -#if defined(CONFIG_RTC_RV8263_UPDATE_OWN_THREAD) - k_sem_init(&data->update_sem, 0, K_SEM_MAX_LIMIT); - k_thread_create(&rv8263c8_update_thread, rv8263c8_update_stack, - K_THREAD_STACK_SIZEOF(rv8263c8_update_stack), - rv8263c8_update_thread_func, (struct device *)p_dev, NULL, - NULL, CONFIG_RTC_RV8263_UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT); -#elif defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) - data->update_work.handler = rv8263c8_update_worker; -#endif -#endif - -#if defined(CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD) || defined(CONFIG_RTC_RV8263_UPDATE_GLOBAL_THREAD) - data->dev = p_dev; -#endif - -rv8263c8_init_exit: - k_spin_unlock(&data->lock, key); - return err; -} - -#ifdef CONFIG_RTC_ALARM -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param id - * @param p_mask - * @return int 0 when successful - */ -static int rv8263c8_alarm_get_supported_fields(const struct device *p_dev, uint16_t id, - uint16_t *p_mask) -{ - ARG_UNUSED(p_dev); - - if ((p_dev == NULL) || (id != 0)) { - return -EINVAL; - } - - (*p_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_WEEKDAY); - - return 0; -} - -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param id - * @param mask - * @param p_timeptr - * @return int 0 when successful - */ -static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint16_t mask, - const struct rtc_time *p_timeptr) -{ - int err; - struct rv8263c8_data *const data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if ((p_dev == NULL) || (p_timeptr == NULL) || (id != 0) || ((mask > 0) && (p_timeptr == NULL))) { - return -EINVAL; - } - - k_spinlock_key_t key = k_spin_lock(&data->lock); - - // Check time valid - if (!rv8263c8_validate_alarm(p_timeptr, mask)) { - return -EINVAL; - } - - if (mask & RTC_ALARM_TIME_MASK_SECOND) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_sec); - } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_BM_ALARM_DISABLE); - } - - if (err < 0) { - LOG_ERR("Error while writing SECONDS alarm! Error: %i", err); - goto rv8263c8_alarm_set_time_exit; - } - - if (mask & RTC_ALARM_TIME_MASK_MINUTE) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, - RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_min); - } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, RV8263C8_BM_ALARM_DISABLE); - } - - if (err < 0) { - LOG_ERR("Error while writing MINUTE alarm! Error: %i", err); - goto rv8263c8_alarm_set_time_exit; - } - - if (mask & RTC_ALARM_TIME_MASK_HOUR) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, - RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_hour); - } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, RV8263C8_BM_ALARM_DISABLE); - } - - if (err < 0) { - LOG_ERR("Error while writing HOUR alarm! Error: %i", err); - goto rv8263c8_alarm_set_time_exit; - } - - if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, - RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_mday); - } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, RV8263C8_BM_ALARM_DISABLE); - } - - if (err < 0) { - LOG_ERR("Error while writing MONTHDAY alarm! Error: %i", err); - goto rv8263c8_alarm_set_time_exit; - } - - if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, - RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_wday); - } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, RV8263C8_BM_ALARM_DISABLE); - } - - if (err < 0) { - LOG_ERR("Error while writing WEEKDAY alarm! Error: %i", err); - goto rv8263c8_alarm_set_time_exit; - } - -rv8263c8_alarm_set_time_exit: - k_spin_unlock(&data->lock, key); - - return err; -} - -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param id - * @param p_mask - * @param p_timeptr - * @return int 0 when successful - */ -static int rv8263c8_alarm_get_time(const struct device *p_dev, uint16_t id, uint16_t *p_mask, - struct rtc_time *p_timeptr) -{ - struct rv8263c8_data *const data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - uint8_t value[5]; - - if ((p_dev == NULL) || (p_timeptr == NULL) || (id != 0)) { - return -EINVAL; - } - - (*p_mask) = 0; - - k_spinlock_key_t key = k_spin_lock(&data->lock); - err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, value, sizeof(value)); - k_spin_unlock(&data->lock, key); - - if (err < 0) { - LOG_ERR("Error while reading alarm! Error: %i", err); - return err; - } - - if (value[0] <= MAX_SEC) { - p_timeptr->tm_sec = value[0]; - (*p_mask) |= RTC_ALARM_TIME_MASK_SECOND; - } - - if (value[1] <= MAX_MIN) { - p_timeptr->tm_min = value[1]; - (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE; - } - - if (value[2] <= MAX_HOUR) { - p_timeptr->tm_hour = value[2]; - (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; - } - - if (value[3] <= MAX_MDAY) { - p_timeptr->tm_mday = value[3]; - (*p_mask) |= RTC_ALARM_TIME_MASK_MONTHDAY; - } - - if (value[4] <= MAX_WDAY) { - p_timeptr->tm_hour = value[4]; - (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY; - } - - return 0; -} - -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param id - * @param callback RTC Alarm callback - * @param p_user_data Pointer to user data for alarm callback - * @return int 0 when successful - */ -static int rv8263c8_alarm_set_callback(const struct device *p_dev, uint16_t id, - rtc_alarm_callback callback, void *p_user_data) -{ - int err; - struct rv8263c8_data *const data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if ((p_dev == NULL) || (id != 0)) { - return -EINVAL; - } - - k_spinlock_key_t key = k_spin_lock(&data->lock); - - data->alarm_cb = callback; - data->alarm_cb_data = p_user_data; - - if (callback != NULL) { - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_ALARM_INT_ENABLE, - RV8263C8_BM_ALARM_INT_ENABLE); - } else { - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_ALARM_INT_ENABLE, - RV8263C8_BM_ALARM_INT_DISABLE); - } - - k_spin_unlock(&data->lock, key); - - if (err < 0) { - LOG_ERR("Error while writing CONTROL2! Error: %i", err); - return err; - } - - return 0; -} - -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param id - * @return int 0 when successful - */ -static int rv8263c8_alarm_is_pending(const struct device *p_dev, uint16_t id) -{ - int ret; - struct rv8263c8_data *const data = p_dev->data; - - if ((p_dev == NULL) || (id != 0)) { - return -EINVAL; - } - - k_spinlock_key_t key = k_spin_lock(&data->lock); - ret = data->is_alarm_pending ? 1 : 0; - data->is_alarm_pending = false; - k_spin_unlock(&data->lock, key); - - return ret; -} -#endif - -#ifdef CONFIG_RTC_UPDATE -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param callback RTC update callback - * @param p_user_data Pointer to user data for update callback - * @return int 0 when successful - */ -int rv8263_update_callback(const struct device *p_dev, rtc_update_callback callback, void *p_user_data) -{ - struct rv8263c8_data *const data = p_dev->data; - - if (p_dev == NULL) { - return -EINVAL; - } - - k_spinlock_key_t key = k_spin_lock(&data->lock); - data->update_cb = callback; - data->update_cb_data = p_user_data; - k_spin_unlock(&data->lock, key); - - return rv8263c8_update_enable_timer(p_dev); -} -#endif - -#ifdef CONFIG_RTC_CALIBRATION -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param calibration - * @return int 0 when successful - */ -int rv8263c8_calibration_set(const struct device *p_dev, int32_t calibration) -{ - int err; - int8_t reg; - struct rv8263c8_data *const data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if (p_dev == NULL) { - return -EINVAL; - } - - reg = calibration & 0x7F; - reg = bin2bcd(reg); - - k_spinlock_key_t key = k_spin_lock(&data->lock); - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, 0x7F, reg); - k_spin_unlock(&data->lock, key); - - return err; -} - -/** - * @brief - * - * @param p_dev Pointer to device strucure - * @param p_calibration - * @return int 0 when successful - */ -int rv8263c8_calibration_get(const struct device *p_dev, int32_t *p_calibration) -{ - int err; - uint8_t value; - struct rv8263c8_data *const data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - if ((p_dev == NULL) || (p_calibration == NULL)) { - return -EINVAL; - } - - k_spinlock_key_t key = k_spin_lock(&data->lock); - err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, &value); - k_spin_unlock(&data->lock, key); - if (err < 0) { - LOG_ERR("Error while reading OFFSET! Error: %i", err); - return err; - } - - *p_calibration = bcd2bin(value & 0x7F); - - return 0; -} -#endif - -static const struct rtc_driver_api rv8263c8_driver_api = { - .set_time = rv8263c8_time_set, - .get_time = rv8263c8_time_get, -#ifdef CONFIG_RTC_ALARM - .alarm_get_supported_fields = rv8263c8_alarm_get_supported_fields, - .alarm_set_time = rv8263c8_alarm_set_time, - .alarm_get_time = rv8263c8_alarm_get_time, - .alarm_is_pending = rv8263c8_alarm_is_pending, - .alarm_set_callback = rv8263c8_alarm_set_callback, -#endif -#ifdef CONFIG_RTC_UPDATE - .update_set_callback = rv8263_update_callback, -#endif -#ifdef CONFIG_RTC_CALIBRATION - .set_calibration = rv8263c8_calibration_set, - .get_calibration = rv8263c8_calibration_get, -#endif -}; - -#define RV8263_DEFINE(inst) \ - static struct rv8263c8_data rv8263c8_data_##inst; \ - static const struct rv8263c8_config rv8263c8_config_##inst = { \ - .i2c_bus = I2C_DT_SPEC_INST_GET(inst), \ - .clkout = DT_INST_PROP(inst, clkout), \ - .fast_mode = DT_INST_PROP(inst, fast_mode), \ - .offset = DT_INST_PROP(inst, offset), \ - IF_ENABLED(RV8263_USE_INT, \ - (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),)) \ - }; \ - DEVICE_DT_INST_DEFINE(inst, &rv8263c8_init, NULL, &rv8263c8_data_##inst, \ - &rv8263c8_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, \ - &rv8263c8_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(RV8263_DEFINE) \ No newline at end of file diff --git a/app/zephyr_patches/ntc_fix.patch b/app/patches/ntc_fix.patch similarity index 100% rename from app/zephyr_patches/ntc_fix.patch rename to app/patches/ntc_fix.patch diff --git a/app/patches/rv8263_rtc.patch b/app/patches/rv8263_rtc.patch new file mode 100644 index 00000000..ce840c73 --- /dev/null +++ b/app/patches/rv8263_rtc.patch @@ -0,0 +1,866 @@ +diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt +index 85ff98320dd..4236ad596cc 100644 +--- a/drivers/rtc/CMakeLists.txt ++++ b/drivers/rtc/CMakeLists.txt +@@ -7,6 +7,7 @@ zephyr_library() + + zephyr_library_sources(rtc_utils.c) + ++zephyr_library_sources_ifdef(CONFIG_RTC_RV8263 rtc_rv8263.c) + zephyr_library_sources_ifdef(CONFIG_RTC_AM1805 rtc_am1805.c) + zephyr_library_sources_ifdef(CONFIG_RTC_DS1307 rtc_ds1307.c) + zephyr_library_sources_ifdef(CONFIG_USERSPACE rtc_handlers.c) +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 46bd2562dd9..2a1d5fa05bd 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -53,5 +53,6 @@ source "drivers/rtc/Kconfig.sam" + source "drivers/rtc/Kconfig.smartbond" + source "drivers/rtc/Kconfig.stm32" + source "drivers/rtc/Kconfig.numaker" ++source "drivers/rtc/Kconfig.rv8263" + + endif # RTC +diff --git a/drivers/rtc/Kconfig.rv8263 b/drivers/rtc/Kconfig.rv8263 +new file mode 100644 +index 00000000000..035034da56f +--- /dev/null ++++ b/drivers/rtc/Kconfig.rv8263 +@@ -0,0 +1,11 @@ ++# SPDX-License-Identifier: Apache-2.0 ++# Copyright (c) 2024 Daniel Kampert ++# Author: Daniel Kampert ++ ++config RTC_RV8263 ++ bool "Micro Crystal RV-8263-C8 RTC driver" ++ default y ++ depends on DT_HAS_MICROCRYSTAL_RV_8263_C8_ENABLED ++ select I2C ++ help ++ Micro Crystal RV-8263-C8 RTC driver. +diff --git a/drivers/rtc/rtc_rv8263.c b/drivers/rtc/rtc_rv8263.c +new file mode 100644 +index 00000000000..0a4bc23e7cf +--- /dev/null ++++ b/drivers/rtc/rtc_rv8263.c +@@ -0,0 +1,784 @@ ++/* Copyright (c) 2024 Daniel Kampert ++ * Author: Daniel Kampert ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rtc_utils.h" ++ ++#define RV8263C8_REGISTER_CONTROL_1 0x00 ++#define RV8263C8_REGISTER_CONTROL_2 0x01 ++#define RV8263C8_REGISTER_OFFSET 0x02 ++#define RV8263C8_REGISTER_RAM 0x03 ++#define RV8263C8_REGISTER_SECONDS 0x04 ++#define RV8263C8_REGISTER_MINUTES 0x05 ++#define RV8263C8_REGISTER_HOURS 0x06 ++#define RV8263C8_REGISTER_DATE 0x07 ++#define RV8263C8_REGISTER_WEEKDAY 0x08 ++#define RV8263C8_REGISTER_MONTH 0x09 ++#define RV8263C8_REGISTER_YEAR 0x0A ++#define RV8263C8_REGISTER_SECONDS_ALARM 0x0B ++#define RV8263C8_REGISTER_MINUTES_ALARM 0x0C ++#define RV8263C8_REGISTER_HOURS_ALARM 0x0D ++#define RV8263C8_REGISTER_DATE_ALARM 0x0E ++#define RV8263C8_REGISTER_WEEKDAY_ALARM 0x0F ++#define RV8263C8_REGISTER_TIMER_VALUE 0x10 ++#define RV8263C8_REGISTER_TIMER_MODE 0x11 ++ ++#define RV8263_BM_FAST_MODE (0x01 << 7) ++#define RV8263_BM_NORMAL_MODE (0x00 << 7) ++#define RV8263C8_BM_24H_MODE_ENABLE (0x00 << 1) ++#define RV8263C8_BM_24H_MODE_DISABLE (0x00 << 1) ++#define RV8263C8_BM_CLOCK_ENABLE (0x00 << 5) ++#define RV8263C8_BM_CLOCK_DISABLE (0x01 << 5) ++#define RV8263C8_BM_ALARM_INT_ENABLE (0x01 << 7) ++#define RV8263C8_BM_ALARM_INT_DISABLE (0x00 << 7) ++#define RV8263C8_BM_MINUTE_INT_ENABLE (0x01 << 5) ++#define RV8263C8_BM_MINUTE_INT_DISABLE (0x00 << 5) ++#define RV8263C8_BM_HALF_MINUTE_INT_ENABLE (0x01 << 4) ++#define RV8263C8_BM_HALF_MINUTE_INT_DISABLE (0x00 << 4) ++#define RV8263C8_BM_ALARM_ENABLE (0x00 << 7) ++#define RV8263C8_BM_ALARM_DISABLE (0x01 << 7) ++#define RV8263C8_BM_AF (0x01 << 6) ++#define RV8263C8_BM_TF (0x01 << 3) ++#define RV8263_BM_MODE (0x01 << 7) ++#define RV8263_BM_TD_1HZ (0x02 << 3) ++#define RV8263_BM_TE_ENABLE (0x01 << 2) ++#define RV8263_BM_TIE_ENABLE (0x01 << 1) ++#define RV8263_BM_TI_TP_PULSE (0x01 << 0) ++#define RV8263_BM_OS (0x01 << 7) ++#define RV8263C8_BM_SOFTWARE_RESET (0x58) ++#define RV8263C8_BM_REGISTER_OFFSET 0x7F ++#define RV8263_YEAR_OFFSET (2000 - 1900) ++ ++#define SECONDS_BITS GENMASK(6, 0) ++#define MINUTES_BITS GENMASK(7, 0) ++#define HOURS_BITS GENMASK(5, 0) ++#define DATE_BITS GENMASK(5, 0) ++#define MONTHS_BITS GENMASK(4, 0) ++#define WEEKDAY_BITS GENMASK(2, 0) ++#define YEAR_BITS GENMASK(7, 0) ++#define VALIDATE_24HR BIT(6) ++ ++#define MIN_SEC 0 ++#define MAX_SEC 59 ++#define MIN_MIN 0 ++#define MAX_MIN 59 ++#define MIN_HOUR 0 ++#define MAX_HOUR 23 ++#define MAX_WDAY 7 ++#define MIN_WDAY 1 ++#define MAX_MDAY 31 ++#define MIN_MDAY 1 ++#define MAX_MON 12 ++#define MIN_MON 1 ++#define MIN_YEAR_DIFF 0 ++#define MAX_YEAR_DIFF 99 ++ ++#define DT_DRV_COMPAT microcrystal_rv_8263_c8 ++ ++LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_RTC_LOG_LEVEL); ++ ++struct rv8263c8_config { ++ struct i2c_dt_spec i2c_bus; ++ uint32_t clkout; ++ ++#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ struct gpio_dt_spec int_gpio; ++#endif ++}; ++ ++struct rv8263c8_data { ++ struct k_sem lock; ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ const struct device *dev; ++ struct gpio_callback gpio_cb; ++#endif ++ ++#if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ rtc_alarm_callback alarm_cb; ++ void *alarm_cb_data; ++ struct k_work alarm_work; ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ rtc_update_callback update_cb; ++ void *update_cb_data; ++ struct k_work update_work; ++#endif ++}; ++ ++static int rv8263c8_update_disable_timer(const struct device *dev) ++{ ++ int err; ++ uint8_t reg; ++ const struct rv8263c8_config *config = dev->config; ++ ++ /* Value 0 disables the timer. */ ++ reg = 0; ++ err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_VALUE, ®, ++ sizeof(reg)); ++ if (err < 0) { ++ return err; ++ } ++ ++ return i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_MODE, ®, ++ sizeof(reg)); ++} ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++static void rv8263c8_gpio_callback_handler(const struct device *p_port, struct gpio_callback *p_cb, ++ gpio_port_pins_t pins) ++{ ++ ARG_UNUSED(pins); ++ ARG_UNUSED(p_port); ++ ++ struct rv8263c8_data *data = CONTAINER_OF(p_cb, struct rv8263c8_data, gpio_cb); ++ ++#if CONFIG_RTC_ALARM ++ k_work_submit(&data->alarm_work); ++#endif ++ ++#if CONFIG_RTC_UPDATE ++ k_work_submit(&data->update_work); ++#endif ++} ++#endif ++ ++#if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++static void rv8263c8_alarm_worker(struct k_work *p_work) ++{ ++ struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, alarm_work); ++ const struct rv8263c8_config *config = data->dev->config; ++ ++ LOG_DBG("Process alarm worker from interrupt"); ++ ++ if (data->alarm_cb != NULL) { ++ uint8_t reg; ++ ++ i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®); ++ ++ if (reg & RV8263C8_BM_AF) { ++ reg &= ~RV8263C8_BM_AF; ++ ++ LOG_DBG("Calling alarm callback"); ++ data->alarm_cb(data->dev, 0, data->alarm_cb_data); ++ ++ i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, reg); ++ } ++ } ++} ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++static int rv8263c8_update_enable_timer(const struct device *dev) ++{ ++ int err; ++ uint8_t reg; ++ const struct rv8263c8_config *config = dev->config; ++ ++ /* Set the timer preload value for 1 second. */ ++ reg = 1; ++ err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_VALUE, ®, ++ sizeof(reg)); ++ if (err < 0) { ++ return err; ++ } ++ ++ reg = RV8263_BM_TD_1HZ | RV8263_BM_TE_ENABLE | RV8263_BM_TIE_ENABLE | RV8263_BM_TI_TP_PULSE; ++ ++ return i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_MODE, ®, ++ sizeof(reg)); ++} ++ ++static void rv8263c8_update_worker(struct k_work *p_work) ++{ ++ uint8_t reg; ++ struct rv8263c8_data *data = CONTAINER_OF(p_work, struct rv8263c8_data, update_work); ++ const struct rv8263c8_config *config = data->dev->config; ++ ++ LOG_DBG("Process update worker from interrupt"); ++ ++ if (data->update_cb != NULL) { ++ i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®); ++ ++ if (reg & RV8263C8_BM_TF) { ++ LOG_DBG("Calling update callback"); ++ data->update_cb(data->dev, data->update_cb_data); ++ } ++ } ++ ++ rv8263c8_update_enable_timer(data->dev); ++ i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_TF, ++ RV8263C8_BM_TF); ++} ++#endif ++ ++static int rv8263c8_time_set(const struct device *dev, const struct rtc_time *timeptr) ++{ ++ uint8_t regs[7]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ if (timeptr == NULL || (timeptr->tm_year < RV8263_YEAR_OFFSET)) { ++ LOG_ERR("invalid time"); ++ return -EINVAL; ++ } ++ ++ LOG_DBG("Set time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = " ++ "%u", ++ timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday, ++ timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); ++ ++ regs[0] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS; ++ regs[1] = bin2bcd(timeptr->tm_min) & MINUTES_BITS; ++ regs[2] = bin2bcd(timeptr->tm_hour) & HOURS_BITS; ++ regs[3] = bin2bcd(timeptr->tm_mday) & DATE_BITS; ++ regs[4] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS; ++ regs[5] = bin2bcd(timeptr->tm_mon) & MONTHS_BITS; ++ regs[6] = bin2bcd(timeptr->tm_year - RV8263_YEAR_OFFSET) & YEAR_BITS; ++ ++ return i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); ++} ++ ++static int rv8263c8_time_get(const struct device *dev, struct rtc_time *timeptr) ++{ ++ int err; ++ uint8_t regs[7]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ if (timeptr == NULL) { ++ return -EINVAL; ++ } ++ ++ err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); ++ if (err < 0) { ++ return err; ++ } ++ ++ /* Return an error when the oscillator is stopped. */ ++ if (regs[0] & RV8263_BM_OS) { ++ return -ECANCELED; ++ } ++ ++ timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS); ++ timeptr->tm_min = bcd2bin(regs[1] & MINUTES_BITS); ++ timeptr->tm_hour = bcd2bin(regs[2] & HOURS_BITS); ++ timeptr->tm_mday = bcd2bin(regs[3] & DATE_BITS); ++ timeptr->tm_wday = bcd2bin(regs[4] & WEEKDAY_BITS); ++ timeptr->tm_mon = bcd2bin(regs[5] & MONTHS_BITS); ++ timeptr->tm_year = bcd2bin(regs[6] & YEAR_BITS) + RV8263_YEAR_OFFSET; ++ ++ /* Unused. */ ++ timeptr->tm_nsec = 0; ++ timeptr->tm_isdst = -1; ++ timeptr->tm_yday = -1; ++ ++ /* Validate the chip in 24hr mode. */ ++ if (regs[2] & VALIDATE_24HR) { ++ return -ENODATA; ++ } ++ ++ LOG_DBG("Get time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = " ++ "%u", ++ timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday, ++ timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); ++ ++ return 0; ++} ++ ++static int rv8263c8_init(const struct device *dev) ++{ ++ int err; ++ int temp; ++ struct rv8263c8_data *data = dev->data; ++ const struct rv8263c8_config *config = dev->config; ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ if (config->int_gpio.port == NULL) { ++ return -EINVAL; ++ } ++#endif ++ ++ if (!i2c_is_ready_dt(&config->i2c_bus)) { ++ LOG_ERR("I2C bus not ready!"); ++ return -ENODEV; ++ } ++ ++ k_sem_init(&data->lock, 1, 1); ++ ++ err = rv8263c8_update_disable_timer(dev); ++ if (err < 0) { ++ LOG_ERR("Error while disabling the timer! Error: %i", err); ++ return err; ++ } ++ ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_1, ++ RV8263C8_BM_24H_MODE_DISABLE | RV8263C8_BM_CLOCK_ENABLE); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL_1! Error: %i", err); ++ return err; ++ } ++ ++ temp = 0x00; ++ if (config->clkout == 0) { ++ temp = 0x07; ++ } else if (config->clkout == 1) { ++ temp = 0x06; ++ } else if (config->clkout == 1024) { ++ temp = 0x05; ++ } else if (config->clkout == 2048) { ++ temp = 0x04; ++ } else if (config->clkout == 4096) { ++ temp = 0x03; ++ } else if (config->clkout == 8192) { ++ temp = 0x02; ++ } else if (config->clkout == 16384) { ++ temp = 0x01; ++ } ++ ++ LOG_DBG("Configure ClkOut: %u", temp); ++ ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_MINUTE_INT_DISABLE | ++ RV8263C8_BM_HALF_MINUTE_INT_DISABLE | temp); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL_2! Error: %i", err); ++ return err; ++ } ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ uint8_t regs = 0; ++ ++ err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_TIMER_MODE, ®s, ++ sizeof(regs)); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL2! Error: %i", err); ++ return err; ++ } ++#endif ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ if (!gpio_is_ready_dt(&config->int_gpio)) { ++ LOG_ERR("GPIO not ready!"); ++ return err; ++ } ++ ++ err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); ++ if (err < 0) { ++ LOG_ERR("Failed to configure GPIO! Error: %u", err); ++ return err; ++ } ++ ++ err = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_FALLING); ++ if (err < 0) { ++ LOG_ERR("Failed to configure interrupt! Error: %u", err); ++ return err; ++ } ++ ++ gpio_init_callback(&data->gpio_cb, rv8263c8_gpio_callback_handler, ++ BIT(config->int_gpio.pin)); ++ ++ err = gpio_add_callback_dt(&config->int_gpio, &data->gpio_cb); ++ if (err < 0) { ++ LOG_ERR("Failed to add GPIO callback! Error: %u", err); ++ return err; ++ } ++#endif ++ ++ (void)k_sem_take(&data->lock, K_FOREVER); ++#if CONFIG_RTC_ALARM && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ data->alarm_work.handler = rv8263c8_alarm_worker; ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ data->update_work.handler = rv8263c8_update_worker; ++#endif ++ ++#if (CONFIG_RTC_ALARM || CONFIG_RTC_UPDATE) && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ data->dev = dev; ++#endif ++ k_sem_give(&data->lock); ++ ++ return 0; ++} ++ ++#if CONFIG_RTC_ALARM ++static int rv8263c8_alarm_get_supported_fields(const struct device *dev, uint16_t id, ++ uint16_t *p_mask) ++{ ++ ARG_UNUSED(dev); ++ ARG_UNUSED(id); ++ ++ (*p_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_WEEKDAY); ++ ++ return 0; ++} ++ ++static int rv8263c8_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, ++ const struct rtc_time *timeptr) ++{ ++ int err; ++ uint8_t regs[5]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ ARG_UNUSED(id); ++ ++ if ((mask > 0) && (timeptr == NULL)) { ++ return -EINVAL; ++ } ++ ++ /* Disable the alarm when mask is zero. */ ++ if (mask == 0) { ++ return i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_ALARM_INT_ENABLE, ++ RV8263C8_BM_ALARM_INT_DISABLE); ++ } ++ ++ if (!rtc_utils_validate_rtc_time(timeptr, mask)) { ++ LOG_ERR("Invalid mask!"); ++ return -EINVAL; ++ } ++ ++ regs[0] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS; ++ regs[1] = bin2bcd(timeptr->tm_min) & MINUTES_BITS; ++ regs[2] = bin2bcd(timeptr->tm_hour) & HOURS_BITS; ++ regs[3] = bin2bcd(timeptr->tm_mday) & DATE_BITS; ++ regs[4] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS; ++ ++ if (mask & RTC_ALARM_TIME_MASK_SECOND) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[0]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing SECONDS alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_MINUTE) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[1]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing MINUTE alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_HOUR) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[2]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing HOUR alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[3]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing MONTHDAY alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, ++ RV8263C8_BM_ALARM_ENABLE | regs[4]); ++ } else { ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, ++ RV8263C8_BM_ALARM_DISABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing WEEKDAY alarm! Error: %i", err); ++ } ++ ++ return err; ++} ++ ++static int rv8263c8_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *p_mask, ++ struct rtc_time *timeptr) ++{ ++ int err; ++ const struct rv8263c8_config *config = dev->config; ++ uint8_t value[5]; ++ ++ ARG_UNUSED(id); ++ ++ if (timeptr == NULL) { ++ return -EINVAL; ++ } ++ ++ (*p_mask) = 0; ++ ++ err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, value, ++ sizeof(value)); ++ if (err < 0) { ++ LOG_ERR("Error while reading alarm! Error: %i", err); ++ return err; ++ } ++ ++ if (value[0] <= MAX_SEC) { ++ timeptr->tm_sec = bcd2bin(value[0]) & SECONDS_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_SECOND; ++ } ++ ++ if (value[1] <= MAX_MIN) { ++ timeptr->tm_min = bcd2bin(value[1]) & MINUTES_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE; ++ } ++ ++ if (value[2] <= MAX_HOUR) { ++ timeptr->tm_hour = bcd2bin(value[2]) & HOURS_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; ++ } ++ ++ if (value[3] <= MAX_MDAY) { ++ timeptr->tm_mday = bcd2bin(value[3]) & DATE_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_MONTHDAY; ++ } ++ ++ if (value[4] <= MAX_WDAY) { ++ timeptr->tm_wday = bcd2bin(value[4]) & WEEKDAY_BITS; ++ (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY; ++ } ++ ++ return 0; ++} ++ ++static int rv8263c8_alarm_set_callback(const struct device *dev, uint16_t id, ++ rtc_alarm_callback callback, void *user_data) ++{ ++ int err; ++ const struct rv8263c8_config *config = dev->config; ++ ++#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ struct rv8263c8_data *data = dev->data; ++#endif ++ ++ ARG_UNUSED(id); ++ ++#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ if (config->int_gpio.port == NULL) { ++ return -ENOTSUP; ++ } ++ ++ (void)k_sem_take(&data->lock, K_FOREVER); ++ data->alarm_cb = callback; ++ data->alarm_cb_data = user_data; ++ k_sem_give(&data->lock); ++#else ++ return -ENOTSUP; ++#endif ++ ++ if ((callback == NULL) && (user_data == NULL)) { ++ LOG_DBG("Disable alarm function"); ++ ++ err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_ALARM_INT_ENABLE, ++ RV8263C8_BM_ALARM_INT_DISABLE); ++ } else { ++ LOG_DBG("Enable alarm function"); ++ ++ err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_ALARM_INT_ENABLE | RV8263C8_BM_AF, ++ RV8263C8_BM_ALARM_INT_ENABLE); ++ } ++ ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL2! Error: %i", err); ++ } ++ ++ return err; ++} ++ ++static int rv8263c8_alarm_is_pending(const struct device *dev, uint16_t id) ++{ ++ int err; ++ uint8_t reg; ++ const struct rv8263c8_config *config = dev->config; ++ ++ ARG_UNUSED(id); ++ ++ err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ®); ++ if (err) { ++ return err; ++ } ++ ++ if (reg & RV8263C8_BM_AF) { ++ reg &= ~RV8263C8_BM_AF; ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, reg); ++ if (err) { ++ return err; ++ } ++ ++ return 1; ++ } ++ ++ return 0; ++} ++#endif ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++int rv8263_update_callback(const struct device *dev, rtc_update_callback callback, void *user_data) ++{ ++ struct rv8263c8_data *const data = dev->data; ++ ++ (void)k_sem_take(&data->lock, K_FOREVER); ++ data->update_cb = callback; ++ data->update_cb_data = user_data; ++ k_sem_give(&data->lock); ++ ++ /* Disable the update callback. */ ++ if ((callback == NULL) && (user_data == NULL)) { ++ return rv8263c8_update_disable_timer(dev); ++ } ++ ++ return rv8263c8_update_enable_timer(dev); ++} ++#endif ++ ++#ifdef CONFIG_RTC_CALIBRATION ++int rv8263c8_calibration_set(const struct device *dev, int32_t calibration) ++{ ++ int8_t offset; ++ int32_t test_mode0; ++ int32_t test_mode1; ++ int32_t offset_ppm_mode0; ++ int32_t offset_ppm_mode1; ++ const struct rv8263c8_config *config = dev->config; ++ ++ /* NOTE: The RTC API is using a PPB (Parts Per Billion) value. The RTC is using PPM. ++ * Here we calculate the offset when using MODE = 0. ++ * Formula from the application manual: ++ * Offset [ppm] = (calibration [ppb] / (4.34 [ppm] x 1000)) ++ */ ++ offset_ppm_mode0 = calibration / 4340; ++ ++ /* Here we calculate the offset when using MODE = 1. ++ * Formula from the application manual: ++ * Offset [ppm] = (calibration [ppb] / (4.069 [ppm] x 1000)) ++ */ ++ offset_ppm_mode1 = calibration / 4069; ++ ++ LOG_DBG("Offset Mode = 0: %i", offset_ppm_mode0); ++ LOG_DBG("Offset Mode = 1: %i", offset_ppm_mode1); ++ ++ test_mode0 = offset_ppm_mode0 * 4340; ++ test_mode0 = calibration - test_mode0; ++ test_mode1 = offset_ppm_mode1 * 4069; ++ test_mode1 = calibration - test_mode1; ++ ++ /* Compare the values and select the value with the smallest error. */ ++ test_mode0 = test_mode0 < 0 ? -test_mode0 : test_mode0; ++ test_mode1 = test_mode1 < 0 ? -test_mode1 : test_mode1; ++ if (test_mode0 > test_mode1) { ++ LOG_DBG("Use fast mode (Mode = 1)"); ++ ++ /* Error with MODE = 1 is smaller -> Use MODE = 1. */ ++ offset = RV8263_BM_FAST_MODE | (offset_ppm_mode1 & GENMASK(7, 0)); ++ } else { ++ LOG_DBG("Use normal mode (Mode = 0)"); ++ ++ /* Error with MODE = 0 is smaller -> Use MODE = 0. */ ++ offset = RV8263_BM_NORMAL_MODE | (offset_ppm_mode0 & GENMASK(7, 0)); ++ } ++ ++ LOG_DBG("Set offset value: %i", (offset & RV8263C8_BM_REGISTER_OFFSET)); ++ ++ return i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, offset); ++} ++ ++int rv8263c8_calibration_get(const struct device *dev, int32_t *calibration) ++{ ++ int err; ++ int32_t temp; ++ int8_t offset; ++ const struct rv8263c8_config *config = dev->config; ++ ++ if (calibration == NULL) { ++ return -EINVAL; ++ } ++ ++ err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, &offset); ++ if (err) { ++ return err; ++ } ++ ++ /* Convert the signed 7 bit into a signed 8 bit value. */ ++ if (offset & (0x01 << 6)) { ++ temp = offset | (0x01 << 7); ++ } else { ++ temp = offset & (0x3F); ++ temp &= ~(0x01 << 7); ++ } ++ ++ LOG_DBG("Read offset: %i", temp); ++ ++ if (offset & RV8263_BM_FAST_MODE) { ++ temp = temp * 4340L; ++ } else { ++ temp = temp * 4069L; ++ } ++ ++ *calibration = temp; ++ ++ return 0; ++} ++#endif ++ ++static const struct rtc_driver_api rv8263c8_driver_api = { ++ .set_time = rv8263c8_time_set, ++ .get_time = rv8263c8_time_get, ++#if CONFIG_RTC_ALARM ++ .alarm_get_supported_fields = rv8263c8_alarm_get_supported_fields, ++ .alarm_set_time = rv8263c8_alarm_set_time, ++ .alarm_get_time = rv8263c8_alarm_get_time, ++ .alarm_is_pending = rv8263c8_alarm_is_pending, ++ .alarm_set_callback = rv8263c8_alarm_set_callback, ++#endif ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ .update_set_callback = rv8263_update_callback, ++#endif ++#ifdef CONFIG_RTC_CALIBRATION ++ .set_calibration = rv8263c8_calibration_set, ++ .get_calibration = rv8263c8_calibration_get, ++#endif ++}; ++ ++#define RV8263_DEFINE(inst) \ ++ static struct rv8263c8_data rv8263c8_data_##inst; \ ++ static const struct rv8263c8_config rv8263c8_config_##inst = { \ ++ .i2c_bus = I2C_DT_SPEC_INST_GET(inst), \ ++ .clkout = DT_INST_PROP_OR(inst, clkout, 0), \ ++ IF_ENABLED(DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios), \ ++ (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0})))}; \ ++ DEVICE_DT_INST_DEFINE(inst, &rv8263c8_init, NULL, &rv8263c8_data_##inst, \ ++ &rv8263c8_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY, \ ++ &rv8263c8_driver_api); ++ ++DT_INST_FOREACH_STATUS_OKAY(RV8263_DEFINE) +diff --git a/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml +new file mode 100644 +index 00000000000..6c9205f4881 +--- /dev/null ++++ b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml +@@ -0,0 +1,30 @@ ++# SPDX-License-Identifier: Apache-2.0 ++# Copyright (c) 2024 Daniel Kampert ++# Author: Daniel Kampert ++ ++description: Micro Crystal RV-8263-C8 RTC ++ ++compatible: "microcrystal,rv-8263-c8" ++ ++include: ++ - name: rtc-device.yaml ++ - name: i2c-device.yaml ++ ++properties: ++ int-gpios: ++ type: phandle-array ++ ++ clkout: ++ type: int ++ default: 32768 ++ enum: ++ - 32768 ++ - 16384 ++ - 8192 ++ - 4096 ++ - 1024 ++ - 1 ++ - 0 ++ description: ++ Enable CLKOUT at given frequency. When disabled, CLKOUT pin is LOW. Set to 0 to ++ disable the CLKOUT signal.