diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf index 8944deee..97ef031a 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf @@ -49,4 +49,7 @@ CONFIG_DEBUG_COREDUMP_BACKEND_OTHER=y CONFIG_RTC=y CONFIG_RTC_ALARM=y -CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD=y \ No newline at end of file +CONFIG_RTC_RV8263_ALARM_OWN_THREAD=y +CONFIG_RTC_UPDATE=y +CONFIG_RTC_RV8263_UPDATE_OWN_THREAD=y +CONFIG_RTC_CALIBRATION=y \ No newline at end of file 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 a239dc66..ad548e8f 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay @@ -194,6 +194,7 @@ reg = <0xA2>; status = "okay"; clkout = <0>; + int-gpios = <&gpio1 13 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; }; bmi270: bmi270@68 { diff --git a/app/drivers/rtc/rv8263c8/Kconfig b/app/drivers/rtc/rv8263c8/Kconfig index 8e68577c..53cfe77f 100644 --- a/app/drivers/rtc/rv8263c8/Kconfig +++ b/app/drivers/rtc/rv8263c8/Kconfig @@ -11,48 +11,73 @@ config RTC_RV8263 Micro Crystal RV-8263-C8 RTC driver. if RTC_RV8263 - config RTC_RV8263_UPDATE_THREAD_STACK_SIZE - int "Stack size for the RV-8263-C8 update thread" + menu "RV8263 Update" depends on RTC_UPDATE - default 512 - help - Size of the stack used for the thread handling RTC updates. - - config RTC_RV8263_UPDATE_THREAD_PRIORITY - int "RV-8263-C8 update thread priority" - depends on RTC_UPDATE - default 0 - help - Priority level for the thread handling RTC updates. - - 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 interrupts 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 interrupts and dispatching callbacks. - - choice - prompt "RV-8263-C8 alarm handling mode" - default RTC_RV8263_ALARM_OWN_THREAD - - config RTC_RV8263_ALARM_GLOBAL_THREAD - depends on GPIO - bool "Use workqueue" + + 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 - Use the global workqueue to process the interrupts from the RV-8263-C8. + Size of the stack used for the thread handling alarms and dispatching callbacks. - config RTC_RV8263_ALARM_OWN_THREAD - depends on GPIO - bool "Use driver thread" + config RTC_RV8263_ALARM_THREAD_PRIORITY + int "RV-8263-C8 alarm thread priority" + depends on RTC_RV8263_ALARM_OWN_THREAD + default 0 help - Use a separate thread to process the interrupts from the RV-8263-C8. - endchoice + 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 index 633f9dd8..476422a7 100644 --- a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c +++ b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include @@ -33,18 +31,21 @@ #define RV8263C8_REGISTER_TIMER_VALUE 0x10 #define RV8263C8_REGISTER_TIMER_MODE 0x11 -#define RV8263C8_BM_24H_MODE_ENABLE (0x00 << 0x01) -#define RV8263C8_BM_24H_MODE_DISABLE (0x00 << 0x01) -#define RV8263C8_BM_CLOCK_ENABLE (0x00 << 0x05) -#define RV8263C8_BM_CLOCK_DISABLE (0x01 << 0x05) -#define RV8263C8_BM_ALARM_INT_ENABLE (0x01 << 0x07) -#define RV8263C8_BM_ALARM_INT_DISABLE (0x00 << 0x07) -#define RV8263C8_BM_MINUTE_INT_ENABLE (0x01 << 0x05) -#define RV8263C8_BM_MINUTE_INT_DISABLE (0x00 << 0x05) -#define RV8263C8_BM_HALF_MINUTE_INT_ENABLE (0x01 << 0x04) -#define RV8263C8_BM_HALF_MINUTE_INT_DISABLE (0x00 << 0x04) -#define RV8263C8_BM_ALARM_ENABLE (0x00 << 0x07) -#define RV8263C8_BM_ALARM_DISABLE (0x01 << 0x07) +#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) @@ -73,6 +74,34 @@ #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 { @@ -80,30 +109,44 @@ struct rv8263c8_config { uint8_t clkout; bool fast_mode; int8_t offset; -#ifdef CONFIG_RTC_ALARM + +#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) struct gpio_dt_spec int_gpio; #endif }; struct rv8263c8_data { struct k_spinlock lock; -#ifdef CONFIG_RTC_ALARM + +#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 - struct k_sem update_sem; 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 }; @@ -112,17 +155,24 @@ static K_KERNEL_STACK_MEMBER(rv8263c8_alarm_stack, CONFIG_RTC_RV8263_ALARM_THREA static struct k_thread rv8263c8_alarm_thread; #endif -#ifdef CONFIG_RTC_UPDATE +#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 -#ifdef CONFIG_RTC_ALARM +#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) + gpio_port_pins_t pins) { - ARG_UNUSED(p_port); ARG_UNUSED(pins); + ARG_UNUSED(p_port); struct rv8263c8_data *data = CONTAINER_OF(p_cb, struct rv8263c8_data, gpio_cb); @@ -131,55 +181,93 @@ static void rv8263c8_gpio_callback_handler(const struct device *p_port, struct g #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; - // TODO: Check for pending alarm + 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); } -#ifdef CONFIG_RTC_RV8263_ALARM_OWN_THREAD +#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) { - struct rv8263c8_data *data = p_arg1; + 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(data->dev); + rv8263_process_alarm(dev); } } -#else +#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 interrupt from worker"); + 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)) { + (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)) { + (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)) { + (p_timeptr->tm_hour < MIN_HOUR || p_timeptr->tm_hour > MAX_HOUR)) { return false; } @@ -188,29 +276,111 @@ static bool rv8263c8_validate_alarm(const struct rtc_time *p_timeptr, uint32_t m #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) { - struct rv8263c8_data *data = p_arg1; + const struct device *dev = p_arg1; + struct rv8263c8_data *data = dev->data; while (1) { - k_sem_take(&data->sem, K_FOREVER); - if (data->update_cb != NULL) { - LOG_DBG("Calling update callback"); - data->update_cb(p_dev, 0, data->update_cb_data); - } + 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; - k_spinlock_key_t key = k_spin_lock(&data->lock); + 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, @@ -224,26 +394,36 @@ static int rv8263c8_time_set(const struct device *p_dev, const struct rtc_time * 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; - k_spinlock_key_t key = k_spin_lock(&data->lock); + 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)); - if (err != 0) { - goto rv8263c8_time_get_exit; + k_spin_unlock(&data->lock, key); + if (err < 0) { + return err; } p_timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS); @@ -262,26 +442,33 @@ static int rv8263c8_time_get(const struct device *p_dev, struct rtc_time *p_time // Validate the chip in 24hr mode if (regs[2] & VALIDATE_24HR) { - err = -ENODATA; - goto rv8263c8_time_get_exit; + return -ENODATA; } - LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, min = %d, sec = %d", + 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); -rv8263c8_time_get_exit: - k_spin_unlock(&data->lock, key); - - return err; + 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; @@ -298,62 +485,72 @@ static int rv8263c8_init(const struct device *p_dev) 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 CONTROL1! Error: %i", err); + 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 - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, - RV8263C8_BM_ALARM_INT_ENABLE | RV8263C8_BM_MINUTE_INT_DISABLE | RV8263C8_BM_HALF_MINUTE_INT_DISABLE | - (config->clkout << 0x00)); + 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 CONTROL2! Error: %i", err); + LOG_ERR("Error while writing CONTROL_2! Error: %i", err); goto rv8263c8_init_exit; } - // Configure the offset register - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_OFFSET, - config->fast_mode << 0x07 | (config->offset & 0x7F)); +#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 -rv8263c8_init_exit: - k_spin_unlock(&data->lock, key); - -#ifdef CONFIG_RTC_ALARM - data->dev = p_dev; - +#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE) if (!gpio_is_ready_dt(&config->int_gpio)) { LOG_ERR("GPIO not ready!"); - return -ENODEV; + goto rv8263c8_init_exit; } err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); - if (err != 0) { + if (err < 0) { LOG_ERR("Failed to configure GPIO! Error: %u", err); - return -ENODEV; + goto rv8263c8_init_exit; } err = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_INACTIVE); - if (err != 0) { + if (err < 0) { LOG_ERR("Failed to configure interrupt! Error: %u", err); - return -ENODEV; + 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) { + if (err < 0) { LOG_ERR("Failed to add GPIO callback! Error: %u", err); - return -ENODEV; + 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, data, NULL, + 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; @@ -361,19 +558,37 @@ static int rv8263c8_init(const struct device *p_dev) #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, data, NULL, + 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) + uint16_t *p_mask) { ARG_UNUSED(p_dev); @@ -387,6 +602,15 @@ static int rv8263c8_alarm_get_supported_fields(const struct device *p_dev, uint1 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) { @@ -402,8 +626,7 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint // Check time valid if (!rv8263c8_validate_alarm(p_timeptr, mask)) { - err = -EINVAL; - goto rv8263c8_alarm_set_time_exit; + return -EINVAL; } if (mask & RTC_ALARM_TIME_MASK_SECOND) { @@ -450,7 +673,7 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint } if (err < 0) { - LOG_ERR("Error while writing DATE alarm! Error: %i", err); + LOG_ERR("Error while writing MONTHDAY alarm! Error: %i", err); goto rv8263c8_alarm_set_time_exit; } @@ -462,14 +685,7 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint } if (err < 0) { - LOG_ERR("Error while writing DATE alarm! Error: %i", err); - goto rv8263c8_alarm_set_time_exit; - } - - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, RV8263C8_BM_ALARM_INT_ENABLE, - RV8263C8_BM_ALARM_INT_ENABLE); - if (err < 0) { - LOG_ERR("Error while writing CONTROL2! Error: %i", err); + LOG_ERR("Error while writing WEEKDAY alarm! Error: %i", err); goto rv8263c8_alarm_set_time_exit; } @@ -479,83 +695,74 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint 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) { - int err; struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - uint8_t value; + uint8_t value[5]; if ((p_dev == NULL) || (p_timeptr == NULL) || (id != 0)) { return -EINVAL; } - k_spinlock_key_t key = k_spin_lock(&data->lock); - (*p_mask) = 0; - err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, &value); + 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 SECONDS! Error: %i", err); - goto rv8263c8_alarm_get_time_exit; + LOG_ERR("Error while reading alarm! Error: %i", err); + return err; } - if (value <= MAX_SEC) { - p_timeptr->tm_sec = value; + if (value[0] <= MAX_SEC) { + p_timeptr->tm_sec = value[0]; (*p_mask) |= RTC_ALARM_TIME_MASK_SECOND; } - err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, &value); - if (err < 0) { - LOG_ERR("Error while reading MINUTES! Error: %i", err); - goto rv8263c8_alarm_get_time_exit; - } - - if (value <= MAX_MIN) { - p_timeptr->tm_min = value; + if (value[1] <= MAX_MIN) { + p_timeptr->tm_min = value[1]; (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE; } - err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, &value); - if (err < 0) { - LOG_ERR("Error while reading SECONDS! Error: %i", err); - goto rv8263c8_alarm_get_time_exit; - } - - if (value <= MAX_HOUR) { - p_timeptr->tm_hour = value; + if (value[2] <= MAX_HOUR) { + p_timeptr->tm_hour = value[2]; (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; } - err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_DATE_ALARM, &value); - if (err < 0) { - LOG_ERR("Error while reading SECONDS! Error: %i", err); - goto rv8263c8_alarm_get_time_exit; - } - - if (value <= MAX_MDAY) { - p_timeptr->tm_mday = value; + if (value[3] <= MAX_MDAY) { + p_timeptr->tm_mday = value[3]; (*p_mask) |= RTC_ALARM_TIME_MASK_MONTHDAY; } - err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_WEEKDAY_ALARM, &value); - if (err < 0) { - LOG_ERR("Error while reading SECONDS! Error: %i", err); - goto rv8263c8_alarm_get_time_exit; - } - - if (value <= MAX_WDAY) { - p_timeptr->tm_hour = value; + if (value[4] <= MAX_WDAY) { + p_timeptr->tm_hour = value[4]; (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY; } -rv8263c8_alarm_get_time_exit: - k_spin_unlock(&data->lock, key); - - return err; + 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) { @@ -590,6 +797,13 @@ static int rv8263c8_alarm_set_callback(const struct device *p_dev, uint16_t id, 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; @@ -600,10 +814,8 @@ static int rv8263c8_alarm_is_pending(const struct device *p_dev, uint16_t id) } 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; @@ -611,6 +823,14 @@ static int rv8263c8_alarm_is_pending(const struct device *p_dev, uint16_t id) #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; @@ -620,39 +840,71 @@ int rv8263_update_callback(const struct device *p_dev, rtc_update_callback callb } k_spinlock_key_t key = k_spin_lock(&data->lock); - - data->update_callback = callback; - data->update_user_data = user_data; - - // TODO: Enable 1 Hz output and interrupts - // This can be done with the timer - + data->update_cb = callback; + data->update_cb_data = p_user_data; k_spin_unlock(&data->lock, key); - return 0; + 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; } - return 0; + 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 @@ -676,18 +928,18 @@ static const struct rtc_driver_api rv8263c8_driver_api = { #endif }; -#define RV_8263_C8_DEFINE(inst) \ +#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(CONFIG_BMI270_PLUS_TRIGGER, \ + 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(RV_8263_C8_DEFINE) \ No newline at end of file +DT_INST_FOREACH_STATUS_OKAY(RV8263_DEFINE) \ No newline at end of file diff --git a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml index fe871510..1c998e3e 100644 --- a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml +++ b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml @@ -13,19 +13,21 @@ properties: fast_mode: type: boolean + required: false description: | Enable fast offset mode (Offset every 4 minutes). offset: type: int + required: false default: 0 description: Offset for timer compensation. clkout: type: int - default: 0 required: false + default: 0 enum: - 32768 - 8192