From 69801cb06097ba19f803931ccd4d6a38ebe79446 Mon Sep 17 00:00:00 2001 From: Daniel Kampert Date: Mon, 27 May 2024 09:06:11 +0200 Subject: [PATCH 1/4] - Add skeleton for RV-8263-C8 RTC - Rename display driver to match it with the vendor_type scheme of the other drivers - Remove Funkytown song from Buzzer driver - Disable selecting of the step counter and battery arc in digital watchface - Reorganize Kconfig and move the application configuration into the "Applications" menu --- app/Kconfig | 48 +- .../zswatch_nrf5340_cpuapp_5.conf | 4 +- .../zswatch_nrf5340_cpuapp_5.overlay | 59 ++- app/drivers/CMakeLists.txt | 1 + app/drivers/Kconfig | 6 +- app/drivers/display/gc9a01/CMakeLists.txt | 2 +- app/drivers/display/gc9a01/Kconfig | 2 +- .../gc9a01/{gc9a01.c => buydisplay_gc9a01.c} | 0 app/drivers/input/cst816s/Kconfig | 4 +- app/drivers/rtc/CMakeLists.txt | 6 + app/drivers/rtc/Kconfig | 3 + app/drivers/rtc/rv8263c8/CMakeLists.txt | 7 + app/drivers/rtc/rv8263c8/Kconfig | 58 ++ .../microcrystal_rv8263c8_interrupt.c | 70 +++ .../microcrystal_rv8263c8_interrupt.h | 15 + .../rtc/rv8263c8/microcrystal_rv8263c8.c | 494 ++++++++++++++++++ .../rtc/rv8263c8/microcrystal_rv8263c8.h | 22 + .../private/microcrystal_rv8263c8_types.h | 36 ++ app/drivers/sensor/bmi270/Kconfig | 37 +- app/drivers/sensor/bmi270/bosch_bmi270.c | 2 +- .../bindings/rtc/microcrystal,rv-8263-c8.yml | 38 ++ app/dts/bindings/sensor/bosch,bmi270-plus.yml | 1 + app/prj.conf | 2 +- app/src/applications/compass/compass_app.c | 6 +- .../sensors_summary/sensors_summary_app.c | 2 +- app/src/drivers/zsw_buzzer.c | 16 - .../0_digital/zsw_watchface_digital_ui.c | 17 +- 27 files changed, 857 insertions(+), 101 deletions(-) rename app/drivers/display/gc9a01/{gc9a01.c => buydisplay_gc9a01.c} (100%) create mode 100644 app/drivers/rtc/CMakeLists.txt create mode 100644 app/drivers/rtc/Kconfig create mode 100644 app/drivers/rtc/rv8263c8/CMakeLists.txt create mode 100644 app/drivers/rtc/rv8263c8/Kconfig create mode 100644 app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c create mode 100644 app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h create mode 100644 app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c create mode 100644 app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h create mode 100644 app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h create mode 100644 app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml diff --git a/app/Kconfig b/app/Kconfig index b870a3b3..bd17c409 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -70,6 +70,31 @@ menu "ZSWatch" bool prompt "Activate the application 'PPT remote'" default n + + menu "Configuration" + menu "Compass" + depends on APPLICATIONS_USE_COMPASS + + config APPLICATIONS_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS + int + prompt "Refresh interval in milliseconds" + default 50 + + config APPLICATIONS_CONFIGURATION_COMPASS_CALIBRATION_TIME_S + int + prompt "Calibration time in seconds" + default 30 + endmenu + + menu "Sensors Summary" + depends on APPLICATIONS_USE_SENSORS_SUMMARY + + config APPLICATIONS_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS + int + prompt "Refresh interval in milliseconds" + default 100 + endmenu + endmenu endmenu menu "Watchface" @@ -111,29 +136,6 @@ menu "ZSWatch" default 85 endmenu - menu "Sensors Summary" - depends on APPLICATIONS_USE_SENSORS_SUMMARY - - config DEFAULT_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS - int - prompt "Refresh interval in milliseconds" - default 100 - endmenu - - menu "Compass" - depends on APPLICATIONS_USE_COMPASS - - config DEFAULT_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS - int - prompt "Refresh interval in milliseconds" - default 50 - - config DEFAULT_CONFIGURATION_COMPASS_CALIBRATION_TIME_S - int - prompt "Calibration time in seconds" - default 30 - endmenu - menu "Power Management" config POWER_MANAGEMENT_IDLE_TIMEOUT_SECONDS int 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 c2368b48..20ce8c79 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf @@ -1,9 +1,7 @@ CONFIG_ZSWATCH_PCB_REV=5 CONFIG_BT_DIS_HW_REV_STR="5" -#--------------- # FLASH -#----------------- CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_STREAM_FLASH=y @@ -49,3 +47,5 @@ CONFIG_NVS=y CONFIG_DEBUG_COREDUMP_BACKEND_OTHER=y +CONFIG_RTC=y +CONFIG_RTC_ALARM=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 ed16b9ff..935d6b40 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay @@ -77,6 +77,7 @@ ; low-power-enable; }; + group2 { psels = ; low-power-enable; @@ -125,21 +126,25 @@ buttons: buttons { compatible = "gpio-keys"; + button1: button_1 { label = "top-right"; gpios = <&gpio0 27 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; + button2: button_2 { label = "bottom-left"; gpios = <&gpio1 10 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; + button3: button_3 { label = "bottom-right"; gpios = <&gpio0 31 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; zephyr,code = ; }; + button4: button_4 { label = "top-left"; gpios = <&gpio0 26 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; @@ -167,12 +172,15 @@ pwmleds { compatible = "pwm-leds"; + display_blk: pwm_led_0 { pwms = <&pwm0 0 PWM_USEC(15) PWM_POLARITY_INVERTED>; }; + vibrator_pwm: pwm_led_1 { pwms = <&pwm1 0 PWM_MSEC(20) 0>; }; + buzzer_pwm: pwm_led_2 { pwms = <&pwm2 0 PWM_HZ(880) PWM_POLARITY_NORMAL>; }; @@ -181,25 +189,33 @@ }; &i2c1 { - bmi270: bmi270@68 { - compatible = "bosch,bmi270-plus"; - reg = <0x68>; - status = "okay"; - int-gpios = <&gpio1 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; - // NOTE: The swap is performed AFTER the axis inversion. So we have to invert the y axis of the sensor - // to get the x axis of the watch coordinate system. - swap-xy; - invert-y; - }; + rv_8263_c8: rv-8263-c8@A2 { + compatible = "microcrystal,rv-8263-c8"; + reg = <0xA2>; + status = "okay"; + clkout = <7>; + }; + + bmi270: bmi270@68 { + compatible = "bosch,bmi270-plus"; + reg = <0x68>; + status = "okay"; + int-gpios = <&gpio1 4 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + // NOTE: The swap is performed AFTER the axis inversion. So we have to invert the y axis of the sensor + // to get the x axis of the watch coordinate system. + swap-xy; + invert-y; + }; + + apds9306: apds9306@52 { + compatible = "avago,apds9306"; + reg = <0x52>; + status = "okay"; + gain = <0>; + resolution = <0>; + frequency = <0>; + }; - apds9306: apds9306@52 { - compatible = "avago,apds9306"; - reg = <0x52>; - status = "okay"; - gain = <0>; - resolution = <0>; - frequency = <0>; - }; npm1300_ek_pmic: pmic@6b { compatible = "nordic,npm1300"; reg = <0x6b>; @@ -253,6 +269,7 @@ thermistor-beta = <3380>; charging-enable; }; + npm1300_ek_buttons: buttons { compatible = "gpio-keys"; status = "disabled"; @@ -286,6 +303,7 @@ pinctrl-0 = <&qspi_default>; pinctrl-1 = <&qspi_sleep>; pinctrl-names = "default", "sleep"; + gd25lq128d: gd25lq128d@0 { compatible = "nordic,qspi-nor"; reg = <0>; @@ -309,6 +327,7 @@ / { fstab { compatible = "zephyr,fstab"; + lvgl_lfs: lvgl_lfs { compatible = "zephyr,fstab,littlefs"; mount-point = "/lvgl_lfs"; @@ -336,13 +355,15 @@ label = "littlefs_storage"; reg = <0x00000000 0x00200000>; }; + lvgl_raw_partition: partition@200000 { label = "lvgl_raw_partition"; reg = <0x00200000 0x00200000>; }; + storage_partition: partition@400000 { label = "storage_partition"; reg = <0x400000 0x100000 >; }; }; -}; +}; \ No newline at end of file diff --git a/app/drivers/CMakeLists.txt b/app/drivers/CMakeLists.txt index 31f019e0..5c438f54 100644 --- a/app/drivers/CMakeLists.txt +++ b/app/drivers/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(display) add_subdirectory(input) +add_subdirectory(rtc) add_subdirectory_ifdef(CONFIG_SENSOR sensor) \ No newline at end of file diff --git a/app/drivers/Kconfig b/app/drivers/Kconfig index aa785211..774a916e 100644 --- a/app/drivers/Kconfig +++ b/app/drivers/Kconfig @@ -1,6 +1,4 @@ rsource "display/Kconfig" rsource "input/Kconfig" - -menu "Drivers" - rsource "sensor/Kconfig" -endmenu \ No newline at end of file +rsource "rtc/Kconfig" +rsource "sensor/Kconfig" \ No newline at end of file diff --git a/app/drivers/display/gc9a01/CMakeLists.txt b/app/drivers/display/gc9a01/CMakeLists.txt index a58a904e..860978b4 100644 --- a/app/drivers/display/gc9a01/CMakeLists.txt +++ b/app/drivers/display/gc9a01/CMakeLists.txt @@ -1 +1 @@ -zephyr_sources(gc9a01.c) \ No newline at end of file +zephyr_sources(buydisplay_gc9a01.c) \ No newline at end of file diff --git a/app/drivers/display/gc9a01/Kconfig b/app/drivers/display/gc9a01/Kconfig index 8bc74830..a4562ec0 100644 --- a/app/drivers/display/gc9a01/Kconfig +++ b/app/drivers/display/gc9a01/Kconfig @@ -1,5 +1,5 @@ menuconfig GC9A01 - bool "GC9A01 compatible display controller driver" + bool "GC9A01 Display" default n select SPI help diff --git a/app/drivers/display/gc9a01/gc9a01.c b/app/drivers/display/gc9a01/buydisplay_gc9a01.c similarity index 100% rename from app/drivers/display/gc9a01/gc9a01.c rename to app/drivers/display/gc9a01/buydisplay_gc9a01.c diff --git a/app/drivers/input/cst816s/Kconfig b/app/drivers/input/cst816s/Kconfig index 43adc895..5f4224f9 100644 --- a/app/drivers/input/cst816s/Kconfig +++ b/app/drivers/input/cst816s/Kconfig @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig INPUT_MODIFIED_CST816S - bool "Use modified out of tree CST816S capacitive touch panel driver" + bool "CST816S Capacitive touch panel driver" default y depends on DT_HAS_HYNITRON_CST816S_ENABLED select I2C @@ -26,4 +26,4 @@ config INPUT_CST816S_INTERRUPT help Enable interrupt support (requires GPIO). -endif # INPUT_CST816S +endif diff --git a/app/drivers/rtc/CMakeLists.txt b/app/drivers/rtc/CMakeLists.txt new file mode 100644 index 00000000..a095c974 --- /dev/null +++ b/app/drivers/rtc/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2024, Daniel Kampert +# +# SPDX-License-Identifier: Apache-2.0 +# + +add_subdirectory_ifdef(CONFIG_RV_8263_C8 rv8263c8) \ No newline at end of file diff --git a/app/drivers/rtc/Kconfig b/app/drivers/rtc/Kconfig new file mode 100644 index 00000000..903dd8f1 --- /dev/null +++ b/app/drivers/rtc/Kconfig @@ -0,0 +1,3 @@ +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 new file mode 100644 index 00000000..900aa7a5 --- /dev/null +++ b/app/drivers/rtc/rv8263c8/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2024, Daniel Kampert +# +# SPDX-License-Identifier: Apache-2.0 +# + +zephyr_sources(microcrystal_rv8263c8.c) +zephyr_sources(interrupt/microcrystal_rv8263c8_interrupt.c) \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/Kconfig b/app/drivers/rtc/rv8263c8/Kconfig new file mode 100644 index 00000000..6befd40b --- /dev/null +++ b/app/drivers/rtc/rv8263c8/Kconfig @@ -0,0 +1,58 @@ +# RV-8263-C8 RTC configuration options. + +# Copyright (c) 240, Daniel Kampert +# +# SPDX-License-Identifier: Apache-2.0 + +menuconfig RV_8263_C8 + bool "RV-8263-C8 RTC" + depends on DT_HAS_MICROCRYSTAL_RV_8263_C8_ENABLED + default y + select I2C + help + Enable the driver for the RV-8263-C8 RTC. + +if RV_8263_C8 + +module = MICROCRYSTAL_RV_8263_C8 +module-str = MICROCRYSTAL_RV_8263_C8 +source "subsys/logging/Kconfig.template.log_config" + + config RV_8263_C8_THREAD_STACK_SIZE + int "RTC delayed work thread stack size" + depends on RV_8263_C8_INTERRUPT_OWN_THREAD + default 1024 + + config RV_8263_C8_THREAD_PRIORITY + int "RV-8263-C8 thread priority" + depends on RV_8263_C8_INTERRUPT_OWN_THREAD + default 10 + + choice + prompt "RV-8263-C8 interrupt mode" + default RV_8263_C8_INTERRUPT_NONE + + config RV_8263_C8_INTERRUPT_NONE + bool "No interrupt" + help + Disable the INTERRUPT interface. + + config RV_8263_C8_INTERRUPT_GLOBAL_THREAD + depends on GPIO + select RV_8263_C8_INTERRUPT + bool "Use workqueue" + help + Use the global workqueue to process the interrupts from the RV-8263-C8. + + config RV_8263_C8_INTERRUPT_OWN_THREAD + depends on GPIO + select RV_8263_C8_INTERRUPT + bool "Use driver thread" + help + Use a separate thread to process the interrupts from the RV-8263-C8. + endchoice + + config RV_8263_C8_INTERRUPT + bool + +endif \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c new file mode 100644 index 00000000..4e4c20f2 --- /dev/null +++ b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c @@ -0,0 +1,70 @@ +/* microcrystal_rv8263c8_interrupt.c - Driver for Micro Crystal RV-8263-C8 RTC. */ + +/* + * Copyright (c) 2024, Daniel Kampert + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "../microcrystal_rv8263c8.h" +#include "../private/microcrystal_rv8263c8_types.h" + +LOG_MODULE_REGISTER(microcrystal_rv8263c8_interrupt, CONFIG_MICROCRYSTAL_RV_8263_C8_LOG_LEVEL); + +#ifdef CONFIG_RV_8263_C8_TRIGGER_OWN_THREAD +static K_KERNEL_STACK_DEFINE(rv8263c8_thread_stack, CONFIG_RV_8263_C8_THREAD_STACK_SIZE); +static struct k_thread rv8263c8_thread; +#endif + +/** @brief + * @param p_dev + * @param p_cb + * @param pins +*/ +static void rv8263c8_gpio_on_interrupt_callback(const struct device *p_dev, struct gpio_callback *p_cb, uint32_t pins) +{ + ARG_UNUSED(pins); + ARG_UNUSED(p_dev); +} + +int rv8263c8_init_interrupt(const struct device *p_dev) +{ + struct rv8263c8_data *data = p_dev->data; + const struct rv8263c8_config *config = p_dev->config; + + LOG_DBG("Initialize interrupts..."); + + if (!gpio_is_ready_dt(&config->int_gpio)) { + LOG_ERR("INT GPIO device not ready!"); + return -ENODEV; + } + + if (gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT)) { + return -EFAULT; + } + /* + data->dev = p_dev; + + #ifdef CONFIG_RV_8263_C8_TRIGGER_OWN_THREAD + k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT); + + k_thread_create(&bmi2_thread, bmi2_thread_stack, CONFIG_RV_8263_C8_THREAD_STACK_SIZE, + bmi2_worker_thread, data, NULL, NULL, K_PRIO_COOP(CONFIG_RV_8263_C8_THREAD_PRIORITY), + 0, K_NO_WAIT); + #else + data->work.handler = bmi2_worker; + #endif + + gpio_init_callback(&data->gpio_handler, rv8263c8_gpio_on_interrupt_callback, BIT(config->int_gpio.pin)); + + if (gpio_add_callback(config->int_gpio.port, &data->gpio_handler)) { + return -EFAULT; + } + + LOG_DBG("Interrupts ready!"); + */ + return 0; +} \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h new file mode 100644 index 00000000..37287b7a --- /dev/null +++ b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h @@ -0,0 +1,15 @@ +/* microcrystal_rv8263c8_interrupt.c - Driver for Micro Crystal RV-8263-C8 RTC. */ + +/* + * Copyright (c) 2024, Daniel Kampert + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/** @brief + * @param p_dev + * @return 0 when successful +*/ +int rv8263c8_init_interrupt(const struct device *p_dev); \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c new file mode 100644 index 00000000..78acd90a --- /dev/null +++ b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c @@ -0,0 +1,494 @@ +/* microcrystal_rv8263c8.c - Driver for Micro Crystal RV-8263-C8 RTC. */ + +/* + * Copyright (c) 2024, Daniel Kampert + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "microcrystal_rv8263c8.h" +#include "private/microcrystal_rv8263c8_types.h" +#include "interrupt/microcrystal_rv8263c8_interrupt.h" + +#define RV8263C8_REGISTER_CONTROL1 0x00 +#define RV8263C8_REGISTER_CONTROL2 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_24H_MODE_ENABLE (0x00 << 0x01) +#define RV8263C8_24H_MODE_DISABLE (0x00 << 0x01) +#define RV8263C8_CLOCK_ENABLE (0x00 << 0x05) +#define RV8263C8_CLOCK_DISABLE (0x01 << 0x05) +#define RV8263C8_ALARM_INT_ENABLE (0x01 << 0x07) +#define RV8263C8_ALARM_INT_DISABLE (0x00 << 0x05) +#define RV8263C8_MINUTE_INT_ENABLE (0x01 << 0x05) +#define RV8263C8_MINUTE_INT_DISABLE (0x00 << 0x05) +#define RV8263C8_HALF_MINUTE_INT_ENABLE (0x01 << 0x04) +#define RV8263C8_HALF_MINUTE_INT_DISABLE (0x00 << 0x04) +#define RV8263C8_ALARM_ENABLE (0x00 << 0x07) +#define RV8263C8_ALARM_DISABLE (0x01 << 0x07) +#define RV8263C8_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 + +LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_MICROCRYSTAL_RV_8263_C8_LOG_LEVEL); + +static void rv8263c8_isr(const struct device *p_dev) +{ + struct rv8263c8_data *const dev_data = p_dev->data; + const struct rv8263c8_config *config = p_dev->config; +} + +static int rv8263c8_set_time(const struct device *p_dev, const struct rtc_time *p_tm) +{ + 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); + + LOG_DBG("Set time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = %u", + p_tm->tm_year, p_tm->tm_mon, p_tm->tm_mday, p_tm->tm_wday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec); + + regs[0] = bin2bcd(p_tm->tm_sec) & SECONDS_BITS; + regs[1] = bin2bcd(p_tm->tm_min); + regs[2] = bin2bcd(p_tm->tm_hour); + regs[3] = bin2bcd(p_tm->tm_wday); + regs[4] = bin2bcd(p_tm->tm_mday); + regs[5] = bin2bcd(p_tm->tm_mon); + regs[6] = bin2bcd((p_tm->tm_year % 100)); + + err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); + + k_spin_unlock(&data->lock, key); + + return err; +} + +static int rv8263c8_get_time(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); + + err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); + if (err != 0) { + goto rv8263c8_get_time_exit; + } + + 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) { + err = -ENODATA; + goto rv8263c8_get_time_exit; + } + + LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, min = %d, sec = %d", + 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_get_time_exit: + k_spin_unlock(&data->lock, key); + + return err; +} + +static int rv8263c8_init(const struct device *p_dev) +{ + int err; + struct rv8263c8_data *data = p_dev->data; + const struct rv8263c8_config *config = p_dev->config; + + 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_CONTROL1, + RV8263C8_24H_MODE_DISABLE | RV8263C8_CLOCK_ENABLE); + if (err < 0) { + LOG_ERR("Error while writing CONTROL1! Error: %i", err); + goto rv8263c8_init_exit; + } + + // Configure the second config register + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, + RV8263C8_ALARM_INT_ENABLE | RV8263C8_MINUTE_INT_DISABLE | RV8263C8_HALF_MINUTE_INT_DISABLE | (config->clkout << 0x00)); + if (err < 0) { + LOG_ERR("Error while writing CONTROL2! 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)); + if (err < 0) { + LOG_ERR("Error while writing CONTROL2! Error: %i", err); + } + + if (config->int_gpio.port) { + if (rv8263c8_init_interrupt(p_dev)) { + LOG_ERR("Could not initialize interrupts!"); + return -EFAULT; + } + } + +rv8263c8_init_exit: + k_spin_unlock(&data->lock, key); + + return err; +} + +#ifdef CONFIG_RTC_ALARM +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; +} + +static int rv8263c8_alarm_get_supported_fields(const struct device *p_dev, uint16_t id, + uint16_t *p_mask) +{ + ARG_UNUSED(p_dev); + + if (id != 0) { + return -EINVAL; + } + + (*p_mask) = (RTC_ALARM_TIME_MASK_SECOND + | RTC_ALARM_TIME_MASK_MINUTE + | RTC_ALARM_TIME_MASK_HOUR); + + return 0; +} + +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 dev_data = p_dev->data; + const struct rv8263c8_config *config = p_dev->config; + + k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + + if (id != 0) { + err = -EINVAL; + goto rv8263c8_alarm_set_time_exit; + } + + if ((mask > 0) && (p_timeptr == NULL)) { + err = -EINVAL; + goto rv8263c8_alarm_set_time_exit; + } + + // Check time valid + if (!rv8263c8_validate_alarm(p_timeptr, mask)) { + err = -EINVAL; + goto rv8263c8_alarm_set_time_exit; + } + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, + RV8263C8_ALARM_ENABLE | p_timeptr->tm_sec); + } else { + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_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_SECONDS_ALARM, + RV8263C8_ALARM_ENABLE | p_timeptr->tm_min); + } else { + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_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_SECONDS_ALARM, + RV8263C8_ALARM_ENABLE | p_timeptr->tm_hour); + } else { + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + } + + if (err < 0) { + LOG_ERR("Error while writing HOUR alarm! Error: %i", err); + goto rv8263c8_alarm_set_time_exit; + } + + err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_ENABLE); + if (err < 0) { + LOG_ERR("Error while writing CONTROL2! Error: %i", err); + goto rv8263c8_alarm_set_time_exit; + } + + err = 0; + +rv8263c8_alarm_set_time_exit: + k_spin_unlock(&dev_data->lock, key); + + return err; +} + +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 dev_data = p_dev->data; + const struct rv8263c8_config *config = p_dev->config; + + rtc_alarm_callback cb; + void *cb_data; + rtc_update_callback update_cb; + void *update_cb_data; + uint8_t value; + + k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + + if (id != 0) { + err = -EINVAL; + goto rv8263c8_alarm_get_time_exit; + } + + if (p_timeptr == NULL) { + err = -EINVAL; + goto rv8263c8_alarm_get_time_exit; + } + + (*p_mask) = 0; + + err = i2c_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, &value); + if (err < 0) { + LOG_ERR("Error while reading SECONDS! Error: %i", err); + goto rv8263c8_alarm_get_time_exit; + } + + if (value <= MAX_SEC) { + p_timeptr->tm_sec = value; + (*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_hour = value; + (*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; + (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; + } + + err = 0; + +rv8263c8_alarm_get_time_exit: + k_spin_unlock(&dev_data->lock, key); + + return err; +} + +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 dev_data = p_dev->data; + const struct rv8263c8_config *config = p_dev->config; + + if (id != 0) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + + dev_data->cb = callback; + dev_data->cb_data = p_user_data; + + if (callback != NULL) { + err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_ENABLE); + } else { + err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_DISABLE); + } + + k_spin_unlock(&dev_data->lock, key); + + if (err < 0) { + LOG_ERR("Error while writing CONTROL2! Error: %i", err); + return err; + } + + return 0; +} + +static int rv8263c8_alarm_is_pending(const struct device *p_dev, uint16_t id) +{ + int ret; + struct rv8263c8_data *const dev_data = p_dev->data; + + if (id != 0) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + + ret = dev_data->alarm_pending ? 1 : 0; + dev_data->alarm_pending = false; + + k_spin_unlock(&dev_data->lock, key); + + return ret; +} +#endif + +#ifdef CONFIG_RTC_UPDATE +static int rv8263c8_update_set_callback(const struct device *p_dev, + rtc_update_callback callback, void *p_user_data) +{ + struct rv8263c8_data *const dev_data = p_dev->data; + +#error Not supported + + k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + + dev_data->update_cb = callback; + dev_data->update_cb_data = p_user_data; + + if (callback != NULL) { + // TODO: Enable + } else { + // TODO: Disable + } + + k_spin_unlock(&dev_data->lock, key); + + return 0; +} +#endif + +static const struct rtc_driver_api rv8263c8_driver_api = { + .set_time = rv8263c8_set_time, + .get_time = rv8263c8_get_time, +#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 = rv8263c8_update_set_callback, +#endif +}; + +#define RV_8263_C8_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, \ + (.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 diff --git a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h new file mode 100644 index 00000000..23fc1068 --- /dev/null +++ b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h @@ -0,0 +1,22 @@ +/* microcrystal_rv8263c8.h - Driver for Micro Crystal RV-8263-C8 RTC. */ + +/* + * Copyright (c 2024, Daniel Kampert + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/** @brief +*/ +typedef enum { + RV8263C8_OUT_32KHZ = 0, + RV8263C8_OUT_16KHZ, + RV8263C8_OUT_8KHZ, + RV8263C8_OUT_4KHZ, + RV8263C8_OUT_2KHZ, + RV8263C8_OUT_1KHZ, + RV8263C8_OUT_1HZ, + RV8263C8_OUT_LOW, +} rv_8263_c8_clkout_t; \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h b/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h new file mode 100644 index 00000000..660c5919 --- /dev/null +++ b/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h @@ -0,0 +1,36 @@ +/* microcrystal_rv8263c8_types.h - Driver for Micro Crystal RV-8263-C8 RTC. */ + +/* + * Copyright (c 2024, Daniel Kampert + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rv8263c8_config { + struct i2c_dt_spec i2c_bus; + rv_8263_c8_clkout_t clkout; + bool fast_mode; + int8_t offset; + struct gpio_dt_spec int_gpio; +}; + +struct rv8263c8_data { + struct k_spinlock lock; + bool alarm_pending; + rtc_alarm_callback cb; + void *cb_data; + rtc_update_callback update_cb; + void *update_cb_data; +}; \ No newline at end of file diff --git a/app/drivers/sensor/bmi270/Kconfig b/app/drivers/sensor/bmi270/Kconfig index ef546f0d..7c3fa5e4 100644 --- a/app/drivers/sensor/bmi270/Kconfig +++ b/app/drivers/sensor/bmi270/Kconfig @@ -27,25 +27,24 @@ if BMI270_PLUS prompt "BMI270 trigger mode" default BMI270_PLUS_TRIGGER_NONE - config BMI270_PLUS_TRIGGER_NONE - bool "No trigger" - help - Disable the trigger interface. - - config BMI270_PLUS_TRIGGER_GLOBAL_THREAD - depends on GPIO - select BMI270_PLUS_TRIGGER - bool "Use workqueue" - help - Use the global workqueue to process the interrupts from the BMI270. - - config BMI270_PLUS_TRIGGER_OWN_THREAD - depends on GPIO - select BMI270_PLUS_TRIGGER - bool "Use driver thread" - help - Use a separate thread to process the interrupts from the BMI270. - + config BMI270_PLUS_TRIGGER_NONE + bool "No trigger" + help + Disable the trigger interface. + + config BMI270_PLUS_TRIGGER_GLOBAL_THREAD + depends on GPIO + select BMI270_PLUS_TRIGGER + bool "Use workqueue" + help + Use the global workqueue to process the interrupts from the BMI270. + + config BMI270_PLUS_TRIGGER_OWN_THREAD + depends on GPIO + select BMI270_PLUS_TRIGGER + bool "Use driver thread" + help + Use a separate thread to process the interrupts from the BMI270. endchoice config BMI270_PLUS_TRIGGER diff --git a/app/drivers/sensor/bmi270/bosch_bmi270.c b/app/drivers/sensor/bmi270/bosch_bmi270.c index f72a3cdb..90d0707f 100644 --- a/app/drivers/sensor/bmi270/bosch_bmi270.c +++ b/app/drivers/sensor/bmi270/bosch_bmi270.c @@ -542,7 +542,7 @@ static int bmi270_pm_action(const struct device *p_dev, enum pm_device_action ac .swap_xy = DT_INST_PROP(inst, swap_xy), \ .invert_x = DT_INST_PROP(inst, invert_x), \ .invert_y = DT_INST_PROP(inst, invert_y), \ - .rotation = DT_INST_PROP(inst, rotation), \ + .rotation = DT_INST_PROP(inst, rotation), \ IF_ENABLED(CONFIG_BMI270_PLUS_TRIGGER, \ (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),)) \ }; \ diff --git a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml new file mode 100644 index 00000000..9383be82 --- /dev/null +++ b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml @@ -0,0 +1,38 @@ +description: | + The RV-8263-C8 is a CMOS real-time clock/calendar module optimized for low power consumption. An Offset + register makes it possible to compensate for the frequency deviation of the clock of 32.768 kHz + +compatible: "microcrystal,rv-8263-c8" + +include: [rtc-device.yaml, i2c-device.yaml] + +properties: + int-gpios: + type: phandle-array + required: false + + fast_mode: + type: boolean + description: | + Enable fast offset mode (Offset every 4 minutes). + + offset: + type: int + default: 0 + description: + Offset for timer compensation. + + clkout: + type: int + default: 7 + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + description: + CLKOUT frequency selection. Set to 7 to disable it. \ No newline at end of file diff --git a/app/dts/bindings/sensor/bosch,bmi270-plus.yml b/app/dts/bindings/sensor/bosch,bmi270-plus.yml index bf48212b..f4a49e82 100644 --- a/app/dts/bindings/sensor/bosch,bmi270-plus.yml +++ b/app/dts/bindings/sensor/bosch,bmi270-plus.yml @@ -25,6 +25,7 @@ properties: type: boolean description: | Invert the y axis. + rotation: type: int default: 0 diff --git a/app/prj.conf b/app/prj.conf index cecaecea..5d5b8cb3 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -180,7 +180,7 @@ CONFIG_WATCHFACE_BACKGROUND_NONE=n # Default watch CONFIG_POWER_MANAGEMENT_IDLE_TIMEOUT_SECONDS=20 -CONFIG_DEFAULT_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS=100 +CONFIG_APPLICATIONS_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS=100 # CST816S CONFIG_INPUT_CST816S_INTERRUPT=y diff --git a/app/src/applications/compass/compass_app.c b/app/src/applications/compass/compass_app.c index ffec4d1c..de12cfaf 100644 --- a/app/src/applications/compass/compass_app.c +++ b/app/src/applications/compass/compass_app.c @@ -35,7 +35,7 @@ static uint32_t cal_start_ms; static void compass_app_start(lv_obj_t *root, lv_group_t *group) { compass_ui_show(root, on_start_calibration); - refresh_timer = lv_timer_create(timer_callback, CONFIG_DEFAULT_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS, NULL); + refresh_timer = lv_timer_create(timer_callback, CONFIG_APPLICATIONS_CONFIGURATION_COMPASS_REFRESH_INTERVAL_MS, NULL); zsw_sensor_fusion_init(); } @@ -59,14 +59,14 @@ static void on_start_calibration(void) cal_start_ms = lv_tick_get(); zsw_popup_show("Calibration", "Rotate the watch 360 degrees\naround each x,y,z.\n a few times.", NULL, - CONFIG_DEFAULT_CONFIGURATION_COMPASS_CALIBRATION_TIME_S, false); + CONFIG_APPLICATIONS_CONFIGURATION_COMPASS_CALIBRATION_TIME_S, false); } static void timer_callback(lv_timer_t *timer) { float heading; if (is_calibrating && - (lv_tick_elaps(cal_start_ms) >= (CONFIG_DEFAULT_CONFIGURATION_COMPASS_CALIBRATION_TIME_S * 1000UL))) { + (lv_tick_elaps(cal_start_ms) >= (CONFIG_APPLICATIONS_CONFIGURATION_COMPASS_CALIBRATION_TIME_S * 1000UL))) { zsw_magnetometer_stop_calibration(); is_calibrating = false; zsw_popup_remove(); diff --git a/app/src/applications/sensors_summary/sensors_summary_app.c b/app/src/applications/sensors_summary/sensors_summary_app.c index 00c6c114..7b13ed2a 100644 --- a/app/src/applications/sensors_summary/sensors_summary_app.c +++ b/app/src/applications/sensors_summary/sensors_summary_app.c @@ -39,7 +39,7 @@ static void sensors_summary_app_start(lv_obj_t *root, lv_group_t *group) // Increase ODR since we want high accuracy here. zsw_pressure_sensor_set_odr(BOSCH_BMP581_ODR_160_HZ); - refresh_timer = lv_timer_create(timer_callback, CONFIG_DEFAULT_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS, + refresh_timer = lv_timer_create(timer_callback, CONFIG_APPLICATIONS_CONFIGURATION_SENSORS_SUMMARY_REFRESH_INTERVAL_MS, NULL); } diff --git a/app/src/drivers/zsw_buzzer.c b/app/src/drivers/zsw_buzzer.c index f5fcc1d5..ed95e744 100644 --- a/app/src/drivers/zsw_buzzer.c +++ b/app/src/drivers/zsw_buzzer.c @@ -71,22 +71,6 @@ static void buzzer_off_work_handler(struct k_work *work); static void run_next_tone_state(note_duration_t *state); static void pattern_timer_timeout(struct k_timer *timer_id); -static struct note_duration_t funkytown_song[] = { - {.note = C5, .duration = quarter}, - {.note = REST, .duration = eigth}, - {.note = C5, .duration = quarter}, - {.note = Bb4, .duration = quarter}, - {.note = C5, .duration = quarter}, - {.note = REST, .duration = quarter}, - {.note = G4, .duration = quarter}, - {.note = REST, .duration = quarter}, - {.note = G4, .duration = quarter}, - {.note = C5, .duration = quarter}, - {.note = F5, .duration = quarter}, - {.note = E5, .duration = quarter}, - {.note = C5, .duration = quarter} -}; - static struct note_duration_t mario_song[] = { {.note = E6, .duration = quarter}, {.note = REST, .duration = eigth}, diff --git a/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c b/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c index ce084374..98f852bb 100644 --- a/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c +++ b/app/src/ui/watchfaces/0_digital/zsw_watchface_digital_ui.c @@ -309,7 +309,7 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_add_flag(ui_battery_arc, LV_OBJ_FLAG_EVENT_BUBBLE); lv_obj_clear_flag(ui_battery_arc, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SNAPPABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | - LV_OBJ_FLAG_SCROLL_CHAIN); + LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_CLICKABLE); lv_obj_set_style_arc_width(ui_battery_arc, 3, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(ui_battery_arc, lv_color_hex(0xFFB140), LV_PART_INDICATOR | LV_STATE_DEFAULT); @@ -324,8 +324,9 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_set_width(ui_battery_arc_icon, LV_SIZE_CONTENT); lv_obj_set_height(ui_battery_arc_icon, LV_SIZE_CONTENT); lv_obj_set_align(ui_battery_arc_icon, LV_ALIGN_CENTER); + lv_obj_add_flag(ui_battery_arc_icon, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_clear_flag(ui_battery_arc_icon, - LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | + LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | LV_OBJ_FLAG_SCROLL_CHAIN); lv_obj_set_style_img_recolor(ui_battery_arc_icon, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_img_recolor_opa(ui_battery_arc_icon, 255, LV_PART_MAIN | LV_STATE_DEFAULT); @@ -348,10 +349,9 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_set_x(ui_step_arc, -52); lv_obj_set_y(ui_step_arc, 67); lv_obj_set_align(ui_step_arc, LV_ALIGN_CENTER); - lv_obj_add_flag(ui_step_arc, LV_OBJ_FLAG_EVENT_BUBBLE); lv_obj_clear_flag(ui_step_arc, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SNAPPABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | - LV_OBJ_FLAG_SCROLL_CHAIN); + LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_CLICKABLE); lv_obj_set_style_arc_width(ui_step_arc, 3, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_arc_color(ui_step_arc, lv_color_hex(0x9D3BE0), LV_PART_INDICATOR | LV_STATE_DEFAULT); @@ -367,6 +367,7 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, lv_obj_set_width(ui_step_arc_icon, LV_SIZE_CONTENT); lv_obj_set_height(ui_step_arc_icon, LV_SIZE_CONTENT); lv_obj_set_align(ui_step_arc_icon, LV_ALIGN_CENTER); + lv_obj_add_flag(ui_step_arc_icon, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE); lv_obj_clear_flag(ui_step_arc_icon, LV_OBJ_FLAG_PRESS_LOCK | LV_OBJ_FLAG_CLICK_FOCUSABLE | LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_ELASTIC | LV_OBJ_FLAG_SCROLL_MOMENTUM | LV_OBJ_FLAG_SCROLL_CHAIN); lv_obj_set_style_img_recolor(ui_step_arc_icon, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT); @@ -465,8 +466,8 @@ static void watchface_show(lv_obj_t *parent, watchface_app_evt_listener evt_cb, #endif // Listeners - lv_obj_add_event_cb(ui_battery_arc, arc_event_pressed, LV_EVENT_CLICKED, NULL); - lv_obj_add_event_cb(ui_step_arc, arc_event_pressed, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_battery_arc_icon, arc_event_pressed, LV_EVENT_CLICKED, NULL); + lv_obj_add_event_cb(ui_step_arc_icon, arc_event_pressed, LV_EVENT_CLICKED, NULL); } static void watchface_remove(void) @@ -645,10 +646,10 @@ static const void *watchface_get_preview_img(void) static void arc_event_pressed(lv_event_t *e) { watchface_app_evt_t evt = {.type = WATCHFACE_APP_EVENT_OPEN_APP}; - if (lv_event_get_target(e) == ui_battery_arc) { + if (lv_event_get_target(e) == ui_battery_arc_icon) { evt.data.app = WATCHFACE_APP_EVT_CLICK_BATT; ui_evt_cb(evt); - } else if (lv_event_get_target(e) == ui_step_arc) { + } else if (lv_event_get_target(e) == ui_step_arc_icon) { evt.data.app = WATCHFACE_APP_EVT_CLICK_STEP; ui_evt_cb(evt); } From b45bdaf359a0afe0a7f66078794c0940a8e1e152 Mon Sep 17 00:00:00 2001 From: Daniel Kampert Date: Tue, 28 May 2024 14:08:25 +0200 Subject: [PATCH 2/4] - Update bosch_bmi270_types.h - Add RV-8263-C8 driver - Couple smaller changes --- .../zswatch_nrf5340_cpuapp_5.conf | 3 +- .../zswatch_nrf5340_cpuapp_5.overlay | 2 +- app/drivers/rtc/CMakeLists.txt | 2 +- app/drivers/rtc/rv8263c8/CMakeLists.txt | 8 +- app/drivers/rtc/rv8263c8/Kconfig | 86 ++-- .../microcrystal_rv8263c8_interrupt.c | 70 --- .../microcrystal_rv8263c8_interrupt.h | 15 - .../rtc/rv8263c8/microcrystal_rv8263c8.c | 483 +++++++++++++----- .../rtc/rv8263c8/microcrystal_rv8263c8.h | 22 - .../private/microcrystal_rv8263c8_types.h | 36 -- app/drivers/sensor/apds9306/Kconfig | 2 +- app/drivers/sensor/bmi270/Kconfig | 2 +- .../bmi270/private/bosch_bmi270_types.h | 28 +- app/drivers/sensor/bmp581/Kconfig | 2 +- .../bindings/rtc/microcrystal,rv-8263-c8.yml | 18 +- 15 files changed, 416 insertions(+), 363 deletions(-) delete mode 100644 app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c delete mode 100644 app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h delete mode 100644 app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h delete mode 100644 app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h 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 20ce8c79..8944deee 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf @@ -48,4 +48,5 @@ CONFIG_NVS=y CONFIG_DEBUG_COREDUMP_BACKEND_OTHER=y CONFIG_RTC=y -CONFIG_RTC_ALARM=y \ No newline at end of file +CONFIG_RTC_ALARM=y +CONFIG_RTC_RV8263_ALARM_GLOBAL_THREAD=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 935d6b40..a239dc66 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,7 @@ compatible = "microcrystal,rv-8263-c8"; reg = <0xA2>; status = "okay"; - clkout = <7>; + clkout = <0>; }; bmi270: bmi270@68 { diff --git a/app/drivers/rtc/CMakeLists.txt b/app/drivers/rtc/CMakeLists.txt index a095c974..720d23ca 100644 --- a/app/drivers/rtc/CMakeLists.txt +++ b/app/drivers/rtc/CMakeLists.txt @@ -3,4 +3,4 @@ # SPDX-License-Identifier: Apache-2.0 # -add_subdirectory_ifdef(CONFIG_RV_8263_C8 rv8263c8) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_RTC_RV8263 rv8263c8) \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/CMakeLists.txt b/app/drivers/rtc/rv8263c8/CMakeLists.txt index 900aa7a5..6c34f57d 100644 --- a/app/drivers/rtc/rv8263c8/CMakeLists.txt +++ b/app/drivers/rtc/rv8263c8/CMakeLists.txt @@ -1,7 +1,5 @@ -# Copyright (c) 2024, Daniel Kampert -# # SPDX-License-Identifier: Apache-2.0 -# +# Copyright (c) 2024 Daniel Kampert +# Author: Daniel Kampert -zephyr_sources(microcrystal_rv8263c8.c) -zephyr_sources(interrupt/microcrystal_rv8263c8_interrupt.c) \ No newline at end of file +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 index 6befd40b..8e68577c 100644 --- a/app/drivers/rtc/rv8263c8/Kconfig +++ b/app/drivers/rtc/rv8263c8/Kconfig @@ -1,58 +1,58 @@ -# RV-8263-C8 RTC configuration options. - -# Copyright (c) 240, Daniel Kampert -# # SPDX-License-Identifier: Apache-2.0 - -menuconfig RV_8263_C8 - bool "RV-8263-C8 RTC" - depends on DT_HAS_MICROCRYSTAL_RV_8263_C8_ENABLED - default y - select I2C - help - Enable the driver for the RV-8263-C8 RTC. - -if RV_8263_C8 - -module = MICROCRYSTAL_RV_8263_C8 -module-str = MICROCRYSTAL_RV_8263_C8 -source "subsys/logging/Kconfig.template.log_config" - - config RV_8263_C8_THREAD_STACK_SIZE - int "RTC delayed work thread stack size" - depends on RV_8263_C8_INTERRUPT_OWN_THREAD - default 1024 - - config RV_8263_C8_THREAD_PRIORITY - int "RV-8263-C8 thread priority" - depends on RV_8263_C8_INTERRUPT_OWN_THREAD - default 10 +# 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 + config RTC_RV8263_UPDATE_THREAD_STACK_SIZE + int "Stack size for the RV-8263-C8 update thread" + 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 interrupt mode" - default RV_8263_C8_INTERRUPT_NONE - - config RV_8263_C8_INTERRUPT_NONE - bool "No interrupt" - help - Disable the INTERRUPT interface. + prompt "RV-8263-C8 alarm handling mode" + default RTC_RV8263_ALARM_OWN_THREAD - config RV_8263_C8_INTERRUPT_GLOBAL_THREAD + config RTC_RV8263_ALARM_GLOBAL_THREAD depends on GPIO - select RV_8263_C8_INTERRUPT bool "Use workqueue" help Use the global workqueue to process the interrupts from the RV-8263-C8. - config RV_8263_C8_INTERRUPT_OWN_THREAD + config RTC_RV8263_ALARM_OWN_THREAD depends on GPIO - select RV_8263_C8_INTERRUPT bool "Use driver thread" help Use a separate thread to process the interrupts from the RV-8263-C8. endchoice - - config RV_8263_C8_INTERRUPT - bool - endif \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c deleted file mode 100644 index 4e4c20f2..00000000 --- a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c +++ /dev/null @@ -1,70 +0,0 @@ -/* microcrystal_rv8263c8_interrupt.c - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c) 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include "../microcrystal_rv8263c8.h" -#include "../private/microcrystal_rv8263c8_types.h" - -LOG_MODULE_REGISTER(microcrystal_rv8263c8_interrupt, CONFIG_MICROCRYSTAL_RV_8263_C8_LOG_LEVEL); - -#ifdef CONFIG_RV_8263_C8_TRIGGER_OWN_THREAD -static K_KERNEL_STACK_DEFINE(rv8263c8_thread_stack, CONFIG_RV_8263_C8_THREAD_STACK_SIZE); -static struct k_thread rv8263c8_thread; -#endif - -/** @brief - * @param p_dev - * @param p_cb - * @param pins -*/ -static void rv8263c8_gpio_on_interrupt_callback(const struct device *p_dev, struct gpio_callback *p_cb, uint32_t pins) -{ - ARG_UNUSED(pins); - ARG_UNUSED(p_dev); -} - -int rv8263c8_init_interrupt(const struct device *p_dev) -{ - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - LOG_DBG("Initialize interrupts..."); - - if (!gpio_is_ready_dt(&config->int_gpio)) { - LOG_ERR("INT GPIO device not ready!"); - return -ENODEV; - } - - if (gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT)) { - return -EFAULT; - } - /* - data->dev = p_dev; - - #ifdef CONFIG_RV_8263_C8_TRIGGER_OWN_THREAD - k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT); - - k_thread_create(&bmi2_thread, bmi2_thread_stack, CONFIG_RV_8263_C8_THREAD_STACK_SIZE, - bmi2_worker_thread, data, NULL, NULL, K_PRIO_COOP(CONFIG_RV_8263_C8_THREAD_PRIORITY), - 0, K_NO_WAIT); - #else - data->work.handler = bmi2_worker; - #endif - - gpio_init_callback(&data->gpio_handler, rv8263c8_gpio_on_interrupt_callback, BIT(config->int_gpio.pin)); - - if (gpio_add_callback(config->int_gpio.port, &data->gpio_handler)) { - return -EFAULT; - } - - LOG_DBG("Interrupts ready!"); - */ - return 0; -} \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h deleted file mode 100644 index 37287b7a..00000000 --- a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h +++ /dev/null @@ -1,15 +0,0 @@ -/* microcrystal_rv8263c8_interrupt.c - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c) 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -/** @brief - * @param p_dev - * @return 0 when successful -*/ -int rv8263c8_init_interrupt(const struct device *p_dev); \ 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 78acd90a..633f9dd8 100644 --- a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c +++ b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c @@ -1,17 +1,21 @@ -/* microcrystal_rv8263c8.c - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c) 2024, Daniel Kampert +/* microcrystal_rv8263c8.c - Driver for Micro Crystal RV-8263-C8 RTC. * - * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Daniel Kampert + * Author: Daniel Kampert */ -#include "microcrystal_rv8263c8.h" -#include "private/microcrystal_rv8263c8_types.h" -#include "interrupt/microcrystal_rv8263c8_interrupt.h" - -#define RV8263C8_REGISTER_CONTROL1 0x00 -#define RV8263C8_REGISTER_CONTROL2 0x01 +#include +#include +#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 @@ -29,19 +33,19 @@ #define RV8263C8_REGISTER_TIMER_VALUE 0x10 #define RV8263C8_REGISTER_TIMER_MODE 0x11 -#define RV8263C8_24H_MODE_ENABLE (0x00 << 0x01) -#define RV8263C8_24H_MODE_DISABLE (0x00 << 0x01) -#define RV8263C8_CLOCK_ENABLE (0x00 << 0x05) -#define RV8263C8_CLOCK_DISABLE (0x01 << 0x05) -#define RV8263C8_ALARM_INT_ENABLE (0x01 << 0x07) -#define RV8263C8_ALARM_INT_DISABLE (0x00 << 0x05) -#define RV8263C8_MINUTE_INT_ENABLE (0x01 << 0x05) -#define RV8263C8_MINUTE_INT_DISABLE (0x00 << 0x05) -#define RV8263C8_HALF_MINUTE_INT_ENABLE (0x01 << 0x04) -#define RV8263C8_HALF_MINUTE_INT_DISABLE (0x00 << 0x04) -#define RV8263C8_ALARM_ENABLE (0x00 << 0x07) -#define RV8263C8_ALARM_DISABLE (0x01 << 0x07) -#define RV8263C8_SOFTWARE_RESET (0x58) +#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_SOFTWARE_RESET (0x58) #define SECONDS_BITS GENMASK(6, 0) #define MINUTES_BITS GENMASK(7, 0) @@ -69,15 +73,136 @@ #define DT_DRV_COMPAT microcrystal_rv_8263_c8 -LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_MICROCRYSTAL_RV_8263_C8_LOG_LEVEL); +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; +#ifdef CONFIG_RTC_ALARM + struct gpio_dt_spec int_gpio; +#endif +}; + +struct rv8263c8_data { + struct k_spinlock lock; +#ifdef CONFIG_RTC_ALARM + const struct device *dev; + struct gpio_callback gpio_cb; + 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; +#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_UPDATE +static K_KERNEL_STACK_MEMBER(rv8263c8_update_stack, CONFIG_RTC_RV8263_UPDATE_THREAD_STACK_SIZE); +static struct k_thread rv8263c8_update_thread; +#endif -static void rv8263c8_isr(const struct device *p_dev) +#ifdef CONFIG_RTC_ALARM +static void rv8263c8_gpio_callback_handler(const struct device *p_port, struct gpio_callback *p_cb, + gpio_port_pins_t pins) { - struct rv8263c8_data *const dev_data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; + ARG_UNUSED(p_port); + ARG_UNUSED(pins); + + 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 +} + +static void rv8263_process_alarm(const struct device *p_dev) +{ + struct rv8263c8_data *data = p_dev->data; + + // TODO: Check for pending alarm + + if (data->alarm_cb != NULL) { + LOG_DBG("Calling alarm callback"); + data->alarm_cb(p_dev, 0, data->alarm_cb_data); + } +} + +#ifdef CONFIG_RTC_RV8263_ALARM_OWN_THREAD +static void rv8263c8_alarm_thread_func(void *p_arg1, void *p_arg2, void *p_arg3) +{ + struct rv8263c8_data *data = p_arg1; + + while (1) { + k_sem_take(&data->alarm_sem, K_FOREVER); + rv8263_process_alarm(data->dev); + } +} +#else +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"); + + rv8263_process_alarm(data->dev); +} +#endif + +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 +static void rv8263c8_update_thread_func(void *p_arg1, void *p_arg2, void *p_arg3) +{ + struct rv8263c8_data *data = p_arg1; + + 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); + } + } } +#endif -static int rv8263c8_set_time(const struct device *p_dev, const struct rtc_time *p_tm) +static int rv8263c8_time_set(const struct device *p_dev, const struct rtc_time *p_timeptr) { int err; uint8_t regs[7]; @@ -88,15 +213,16 @@ static int rv8263c8_set_time(const struct device *p_dev, const struct rtc_time * k_spinlock_key_t key = k_spin_lock(&data->lock); LOG_DBG("Set time: year = %u, mon = %u, mday = %u, wday = %u, hour = %u, min = %u, sec = %u", - p_tm->tm_year, p_tm->tm_mon, p_tm->tm_mday, p_tm->tm_wday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec); + 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_tm->tm_sec) & SECONDS_BITS; - regs[1] = bin2bcd(p_tm->tm_min); - regs[2] = bin2bcd(p_tm->tm_hour); - regs[3] = bin2bcd(p_tm->tm_wday); - regs[4] = bin2bcd(p_tm->tm_mday); - regs[5] = bin2bcd(p_tm->tm_mon); - regs[6] = bin2bcd((p_tm->tm_year % 100)); + 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)); err = i2c_burst_write_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); @@ -105,7 +231,7 @@ static int rv8263c8_set_time(const struct device *p_dev, const struct rtc_time * return err; } -static int rv8263c8_get_time(const struct device *p_dev, struct rtc_time *p_timeptr) +static int rv8263c8_time_get(const struct device *p_dev, struct rtc_time *p_timeptr) { int err; uint8_t regs[7]; @@ -117,7 +243,7 @@ static int rv8263c8_get_time(const struct device *p_dev, struct rtc_time *p_time err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs)); if (err != 0) { - goto rv8263c8_get_time_exit; + goto rv8263c8_time_get_exit; } p_timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS); @@ -137,14 +263,14 @@ static int rv8263c8_get_time(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_get_time_exit; + goto rv8263c8_time_get_exit; } LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, min = %d, sec = %d", 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_get_time_exit: +rv8263c8_time_get_exit: k_spin_unlock(&data->lock, key); return err; @@ -157,7 +283,7 @@ static int rv8263c8_init(const struct device *p_dev) const struct rv8263c8_config *config = p_dev->config; if (!i2c_is_ready_dt(&config->i2c_bus)) { - LOG_ERR("I2C bus not ready"); + LOG_ERR("I2C bus not ready!"); return -ENODEV; } @@ -169,16 +295,17 @@ static int rv8263c8_init(const struct device *p_dev) 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_CONTROL1, - RV8263C8_24H_MODE_DISABLE | RV8263C8_CLOCK_ENABLE); + 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); goto rv8263c8_init_exit; } // Configure the second config register - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, - RV8263C8_ALARM_INT_ENABLE | RV8263C8_MINUTE_INT_DISABLE | RV8263C8_HALF_MINUTE_INT_DISABLE | (config->clkout << 0x00)); + 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)); if (err < 0) { LOG_ERR("Error while writing CONTROL2! Error: %i", err); goto rv8263c8_init_exit; @@ -191,52 +318,71 @@ static int rv8263c8_init(const struct device *p_dev) LOG_ERR("Error while writing CONTROL2! Error: %i", err); } - if (config->int_gpio.port) { - if (rv8263c8_init_interrupt(p_dev)) { - LOG_ERR("Could not initialize interrupts!"); - return -EFAULT; - } - } - rv8263c8_init_exit: k_spin_unlock(&data->lock, key); - return err; -} - #ifdef CONFIG_RTC_ALARM -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; + data->dev = p_dev; + + if (!gpio_is_ready_dt(&config->int_gpio)) { + LOG_ERR("GPIO not ready!"); + return -ENODEV; } - if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && - (p_timeptr->tm_min < MIN_MIN || p_timeptr->tm_min > MAX_MIN)) { - return false; + err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); + if (err != 0) { + LOG_ERR("Failed to configure GPIO! Error: %u", err); + return -ENODEV; } - if ((mask & RTC_ALARM_TIME_MASK_HOUR) && - (p_timeptr->tm_hour < MIN_HOUR || p_timeptr->tm_hour > MAX_HOUR)) { - return false; + 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); + return -ENODEV; } - return true; + 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 -ENODEV; + } + +#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, + 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 + 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, + NULL, CONFIG_RTC_RV8263_UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT); +#endif + + return err; } +#ifdef CONFIG_RTC_ALARM static int rv8263c8_alarm_get_supported_fields(const struct device *p_dev, uint16_t id, uint16_t *p_mask) { ARG_UNUSED(p_dev); - if (id != 0) { + 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); + (*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; } @@ -245,20 +391,14 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint const struct rtc_time *p_timeptr) { int err; - struct rv8263c8_data *const dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); - - if (id != 0) { - err = -EINVAL; - goto rv8263c8_alarm_set_time_exit; + if ((p_dev == NULL) || (p_timeptr == NULL) || (id != 0) || ((mask > 0) && (p_timeptr == NULL))) { + return -EINVAL; } - if ((mask > 0) && (p_timeptr == NULL)) { - err = -EINVAL; - goto rv8263c8_alarm_set_time_exit; - } + k_spinlock_key_t key = k_spin_lock(&data->lock); // Check time valid if (!rv8263c8_validate_alarm(p_timeptr, mask)) { @@ -268,9 +408,9 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint if (mask & RTC_ALARM_TIME_MASK_SECOND) { err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_ALARM_ENABLE | p_timeptr->tm_sec); + RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_sec); } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_BM_ALARM_DISABLE); } if (err < 0) { @@ -279,10 +419,10 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint } if (mask & RTC_ALARM_TIME_MASK_MINUTE) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_ALARM_ENABLE | p_timeptr->tm_min); + 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_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, RV8263C8_BM_ALARM_DISABLE); } if (err < 0) { @@ -291,10 +431,10 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint } if (mask & RTC_ALARM_TIME_MASK_HOUR) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_ALARM_ENABLE | p_timeptr->tm_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_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, RV8263C8_BM_ALARM_DISABLE); } if (err < 0) { @@ -302,16 +442,39 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint goto rv8263c8_alarm_set_time_exit; } - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_ENABLE); + 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 CONTROL2! Error: %i", err); + LOG_ERR("Error while writing DATE 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 DATE alarm! Error: %i", err); goto rv8263c8_alarm_set_time_exit; } - err = 0; + 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); + goto rv8263c8_alarm_set_time_exit; + } rv8263c8_alarm_set_time_exit: - k_spin_unlock(&dev_data->lock, key); + k_spin_unlock(&data->lock, key); return err; } @@ -320,26 +483,15 @@ static int rv8263c8_alarm_get_time(const struct device *p_dev, uint16_t id, uint struct rtc_time *p_timeptr) { int err; - struct rv8263c8_data *const dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - - rtc_alarm_callback cb; - void *cb_data; - rtc_update_callback update_cb; - void *update_cb_data; uint8_t value; - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); - - if (id != 0) { - err = -EINVAL; - goto rv8263c8_alarm_get_time_exit; + if ((p_dev == NULL) || (p_timeptr == NULL) || (id != 0)) { + return -EINVAL; } - if (p_timeptr == NULL) { - err = -EINVAL; - goto rv8263c8_alarm_get_time_exit; - } + k_spinlock_key_t key = k_spin_lock(&data->lock); (*p_mask) = 0; @@ -361,7 +513,7 @@ static int rv8263c8_alarm_get_time(const struct device *p_dev, uint16_t id, uint } if (value <= MAX_MIN) { - p_timeptr->tm_hour = value; + p_timeptr->tm_min = value; (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE; } @@ -376,10 +528,30 @@ static int rv8263c8_alarm_get_time(const struct device *p_dev, uint16_t id, uint (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; } - err = 0; + 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; + (*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; + (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY; + } rv8263c8_alarm_get_time_exit: - k_spin_unlock(&dev_data->lock, key); + k_spin_unlock(&data->lock, key); return err; } @@ -388,25 +560,27 @@ 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 dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - if (id != 0) { + if ((p_dev == NULL) || (id != 0)) { return -EINVAL; } - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + k_spinlock_key_t key = k_spin_lock(&data->lock); - dev_data->cb = callback; - dev_data->cb_data = p_user_data; + 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_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_ENABLE); + 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_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_DISABLE); + 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(&dev_data->lock, key); + k_spin_unlock(&data->lock, key); if (err < 0) { LOG_ERR("Error while writing CONTROL2! Error: %i", err); @@ -419,51 +593,73 @@ static int rv8263c8_alarm_set_callback(const struct device *p_dev, uint16_t id, static int rv8263c8_alarm_is_pending(const struct device *p_dev, uint16_t id) { int ret; - struct rv8263c8_data *const dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; - if (id != 0) { + if ((p_dev == NULL) || (id != 0)) { return -EINVAL; } - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + k_spinlock_key_t key = k_spin_lock(&data->lock); - ret = dev_data->alarm_pending ? 1 : 0; - dev_data->alarm_pending = false; + ret = data->is_alarm_pending ? 1 : 0; + data->is_alarm_pending = false; - k_spin_unlock(&dev_data->lock, key); + k_spin_unlock(&data->lock, key); return ret; } #endif #ifdef CONFIG_RTC_UPDATE -static int rv8263c8_update_set_callback(const struct device *p_dev, - rtc_update_callback callback, void *p_user_data) +int rv8263_update_callback(const struct device *p_dev, rtc_update_callback callback, void *p_user_data) { - struct rv8263c8_data *const dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; -#error Not supported + if (p_dev == NULL) { + return -EINVAL; + } - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + k_spinlock_key_t key = k_spin_lock(&data->lock); - dev_data->update_cb = callback; - dev_data->update_cb_data = p_user_data; + data->update_callback = callback; + data->update_user_data = user_data; - if (callback != NULL) { - // TODO: Enable - } else { - // TODO: Disable + // TODO: Enable 1 Hz output and interrupts + // This can be done with the timer + + k_spin_unlock(&data->lock, key); + + return 0; +} +#endif + +#ifdef CONFIG_RTC_CALIBRATION +int rv8263c8_calibration_set(const struct device *p_dev, int32_t calibration) +{ + struct rv8263c8_data *const data = p_dev->data; + + if (p_dev == NULL) { + return -EINVAL; } - k_spin_unlock(&dev_data->lock, key); + return 0; +} + +int rv8263c8_calibration_get(const struct device *p_dev, int32_t *p_calibration) +{ + struct rv8263c8_data *const data = p_dev->data; + + if ((p_dev == NULL) || (p_calibration == NULL)) { + return -EINVAL; + } return 0; } #endif static const struct rtc_driver_api rv8263c8_driver_api = { - .set_time = rv8263c8_set_time, - .get_time = rv8263c8_get_time, + .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, @@ -471,9 +667,12 @@ static const struct rtc_driver_api rv8263c8_driver_api = { .alarm_is_pending = rv8263c8_alarm_is_pending, .alarm_set_callback = rv8263c8_alarm_set_callback, #endif - #ifdef CONFIG_RTC_UPDATE - .update_set_callback = rv8263c8_update_set_callback, + .update_set_callback = rv8263_update_callback, +#endif +#ifdef CONFIG_RTC_CALIBRATION + .set_calibration = rv8263c8_calibration_set, + .get_calibration = rv8263c8_calibration_get, #endif }; diff --git a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h deleted file mode 100644 index 23fc1068..00000000 --- a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h +++ /dev/null @@ -1,22 +0,0 @@ -/* microcrystal_rv8263c8.h - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -/** @brief -*/ -typedef enum { - RV8263C8_OUT_32KHZ = 0, - RV8263C8_OUT_16KHZ, - RV8263C8_OUT_8KHZ, - RV8263C8_OUT_4KHZ, - RV8263C8_OUT_2KHZ, - RV8263C8_OUT_1KHZ, - RV8263C8_OUT_1HZ, - RV8263C8_OUT_LOW, -} rv_8263_c8_clkout_t; \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h b/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h deleted file mode 100644 index 660c5919..00000000 --- a/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h +++ /dev/null @@ -1,36 +0,0 @@ -/* microcrystal_rv8263c8_types.h - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct rv8263c8_config { - struct i2c_dt_spec i2c_bus; - rv_8263_c8_clkout_t clkout; - bool fast_mode; - int8_t offset; - struct gpio_dt_spec int_gpio; -}; - -struct rv8263c8_data { - struct k_spinlock lock; - bool alarm_pending; - rtc_alarm_callback cb; - void *cb_data; - rtc_update_callback update_cb; - void *update_cb_data; -}; \ No newline at end of file diff --git a/app/drivers/sensor/apds9306/Kconfig b/app/drivers/sensor/apds9306/Kconfig index 60af26e3..ea704e35 100644 --- a/app/drivers/sensor/apds9306/Kconfig +++ b/app/drivers/sensor/apds9306/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig APDS9306 - bool "APDS9306 Sensor" + bool "Avago APDS9306 Sensor" default y depends on DT_HAS_AVAGO_APDS9306_ENABLED select I2C diff --git a/app/drivers/sensor/bmi270/Kconfig b/app/drivers/sensor/bmi270/Kconfig index 7c3fa5e4..4c7f01a4 100644 --- a/app/drivers/sensor/bmi270/Kconfig +++ b/app/drivers/sensor/bmi270/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig BMI270_PLUS - bool "BMI270 Plus Sensor" + bool "Bosch BMI270 Plus Sensor" depends on DT_HAS_BOSCH_BMI270_PLUS_ENABLED default y select I2C diff --git a/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h b/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h index 22759684..a47ca631 100644 --- a/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h +++ b/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h @@ -19,7 +19,7 @@ /** @brief */ struct bmi270_config { - struct i2c_dt_spec i2c; + struct i2c_dt_spec i2c; #ifdef CONFIG_BMI270_PLUS_TRIGGER struct gpio_dt_spec int_gpio; #endif @@ -48,25 +48,23 @@ struct bmi270_data { sensor_trigger_handler_t motion; #endif -#ifdef CONFIG_BMI270_PLUS_TRIGGER_OWN_THREAD +#if defined(CONFIG_BMI270_PLUS_TRIGGER_OWN_THREAD) struct k_sem sem; -#endif - -#ifdef CONFIG_BMI270_PLUS_TRIGGER_GLOBAL_THREAD +#elif defined(CONFIG_BMI270_PLUS_TRIGGER_GLOBAL_THREAD) struct k_work work; #endif int16_t ax; - int16_t ay; - int16_t az; - int16_t gx; - int16_t gy; - int16_t gz; - uint16_t temp; + int16_t ay; + int16_t az; + int16_t gx; + int16_t gy; + int16_t gz; + uint16_t temp; uint8_t acc_range; - uint8_t acc_odr; + uint8_t acc_odr; uint16_t gyr_range; - uint8_t gyr_odr; - uint8_t gyr_osr; + uint8_t gyr_odr; + uint8_t gyr_osr; struct bmi2_dev bmi2; -}; \ No newline at end of file +}; diff --git a/app/drivers/sensor/bmp581/Kconfig b/app/drivers/sensor/bmp581/Kconfig index 6c8eef94..2ba582c5 100644 --- a/app/drivers/sensor/bmp581/Kconfig +++ b/app/drivers/sensor/bmp581/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig BMP581 - bool "BMP581 Sensor" + bool "Bosch BMP581 Sensor" depends on DT_HAS_BOSCH_BMP581_ENABLED default y select I2C diff --git a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml index 9383be82..fe871510 100644 --- a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml +++ b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml @@ -24,15 +24,15 @@ properties: clkout: type: int - default: 7 + default: 0 + required: false enum: - - 0 + - 32768 + - 8192 + - 1024 + - 64 + - 32 - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 + - 0 description: - CLKOUT frequency selection. Set to 7 to disable it. \ No newline at end of file + CLKOUT frequency selection. Set to 0 to disable the CLKOUT signal (CLKOUT pin is LOW). \ No newline at end of file From 1b31081221c37f9e7079947250d41044dee7476c Mon Sep 17 00:00:00 2001 From: Daniel Kampert Date: Tue, 28 May 2024 14:08:25 +0200 Subject: [PATCH 3/4] - Update bosch_bmi270_types.h - Add RV-8263-C8 driver - Couple smaller changes --- .../zswatch_nrf5340_cpuapp_5.conf | 6 +- .../zswatch_nrf5340_cpuapp_5.overlay | 3 +- app/drivers/rtc/CMakeLists.txt | 2 +- app/drivers/rtc/rv8263c8/CMakeLists.txt | 8 +- app/drivers/rtc/rv8263c8/Kconfig | 113 ++- .../microcrystal_rv8263c8_interrupt.c | 70 -- .../microcrystal_rv8263c8_interrupt.h | 15 - .../rtc/rv8263c8/microcrystal_rv8263c8.c | 809 ++++++++++++++---- .../rtc/rv8263c8/microcrystal_rv8263c8.h | 22 - .../private/microcrystal_rv8263c8_types.h | 36 - app/drivers/sensor/apds9306/Kconfig | 2 +- app/drivers/sensor/bmi270/Kconfig | 2 +- .../bmi270/private/bosch_bmi270_types.h | 28 +- app/drivers/sensor/bmp581/Kconfig | 2 +- .../bindings/rtc/microcrystal,rv-8263-c8.yml | 20 +- 15 files changed, 750 insertions(+), 388 deletions(-) delete mode 100644 app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c delete mode 100644 app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h delete mode 100644 app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h delete mode 100644 app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h 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 20ce8c79..97ef031a 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf @@ -48,4 +48,8 @@ CONFIG_NVS=y CONFIG_DEBUG_COREDUMP_BACKEND_OTHER=y CONFIG_RTC=y -CONFIG_RTC_ALARM=y \ No newline at end of file +CONFIG_RTC_ALARM=y +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 935d6b40..ad548e8f 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,8 @@ compatible = "microcrystal,rv-8263-c8"; reg = <0xA2>; status = "okay"; - clkout = <7>; + clkout = <0>; + int-gpios = <&gpio1 13 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; }; bmi270: bmi270@68 { diff --git a/app/drivers/rtc/CMakeLists.txt b/app/drivers/rtc/CMakeLists.txt index a095c974..720d23ca 100644 --- a/app/drivers/rtc/CMakeLists.txt +++ b/app/drivers/rtc/CMakeLists.txt @@ -3,4 +3,4 @@ # SPDX-License-Identifier: Apache-2.0 # -add_subdirectory_ifdef(CONFIG_RV_8263_C8 rv8263c8) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_RTC_RV8263 rv8263c8) \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/CMakeLists.txt b/app/drivers/rtc/rv8263c8/CMakeLists.txt index 900aa7a5..6c34f57d 100644 --- a/app/drivers/rtc/rv8263c8/CMakeLists.txt +++ b/app/drivers/rtc/rv8263c8/CMakeLists.txt @@ -1,7 +1,5 @@ -# Copyright (c) 2024, Daniel Kampert -# # SPDX-License-Identifier: Apache-2.0 -# +# Copyright (c) 2024 Daniel Kampert +# Author: Daniel Kampert -zephyr_sources(microcrystal_rv8263c8.c) -zephyr_sources(interrupt/microcrystal_rv8263c8_interrupt.c) \ No newline at end of file +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 index 6befd40b..53cfe77f 100644 --- a/app/drivers/rtc/rv8263c8/Kconfig +++ b/app/drivers/rtc/rv8263c8/Kconfig @@ -1,58 +1,83 @@ -# RV-8263-C8 RTC configuration options. - -# Copyright (c) 240, Daniel Kampert -# # SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2024 Daniel Kampert +# Author: Daniel Kampert -menuconfig RV_8263_C8 - bool "RV-8263-C8 RTC" - depends on DT_HAS_MICROCRYSTAL_RV_8263_C8_ENABLED - default y - select I2C - help - Enable the driver for the RV-8263-C8 RTC. - -if RV_8263_C8 +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. -module = MICROCRYSTAL_RV_8263_C8 -module-str = MICROCRYSTAL_RV_8263_C8 -source "subsys/logging/Kconfig.template.log_config" +if RTC_RV8263 + menu "RV8263 Update" + depends on RTC_UPDATE - config RV_8263_C8_THREAD_STACK_SIZE - int "RTC delayed work thread stack size" - depends on RV_8263_C8_INTERRUPT_OWN_THREAD - default 1024 + choice + prompt "RV-8263-C8 update handling mode" + default RTC_RV8263_UPDATE_GLOBAL_THREAD - config RV_8263_C8_THREAD_PRIORITY - int "RV-8263-C8 thread priority" - depends on RV_8263_C8_INTERRUPT_OWN_THREAD - default 10 + 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. - choice - prompt "RV-8263-C8 interrupt mode" - default RV_8263_C8_INTERRUPT_NONE + 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 RV_8263_C8_INTERRUPT_NONE - bool "No interrupt" + 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 - Disable the INTERRUPT interface. + Size of the stack used for the thread handling RTC updates and dispatching callbacks. - config RV_8263_C8_INTERRUPT_GLOBAL_THREAD - depends on GPIO - select RV_8263_C8_INTERRUPT - bool "Use workqueue" + config RTC_RV8263_UPDATE_THREAD_PRIORITY + int "RV-8263-C8 update thread priority" + depends on RTC_RV8263_UPDATE_OWN_THREAD + default 0 help - Use the global workqueue to process the interrupts from the RV-8263-C8. + Priority level for the thread handling RTC updates and dispatching callbacks. + endmenu - config RV_8263_C8_INTERRUPT_OWN_THREAD - depends on GPIO - select RV_8263_C8_INTERRUPT - bool "Use driver thread" - help - Use a separate thread to process the interrupts from the RV-8263-C8. - endchoice + menu "RV8263 Alarm" + depends on RTC_ALARM + + choice + prompt "RV-8263-C8 alarm handling mode" + default RTC_RV8263_ALARM_GLOBAL_THREAD - config RV_8263_C8_INTERRUPT - bool + 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/interrupt/microcrystal_rv8263c8_interrupt.c b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c deleted file mode 100644 index 4e4c20f2..00000000 --- a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.c +++ /dev/null @@ -1,70 +0,0 @@ -/* microcrystal_rv8263c8_interrupt.c - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c) 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include "../microcrystal_rv8263c8.h" -#include "../private/microcrystal_rv8263c8_types.h" - -LOG_MODULE_REGISTER(microcrystal_rv8263c8_interrupt, CONFIG_MICROCRYSTAL_RV_8263_C8_LOG_LEVEL); - -#ifdef CONFIG_RV_8263_C8_TRIGGER_OWN_THREAD -static K_KERNEL_STACK_DEFINE(rv8263c8_thread_stack, CONFIG_RV_8263_C8_THREAD_STACK_SIZE); -static struct k_thread rv8263c8_thread; -#endif - -/** @brief - * @param p_dev - * @param p_cb - * @param pins -*/ -static void rv8263c8_gpio_on_interrupt_callback(const struct device *p_dev, struct gpio_callback *p_cb, uint32_t pins) -{ - ARG_UNUSED(pins); - ARG_UNUSED(p_dev); -} - -int rv8263c8_init_interrupt(const struct device *p_dev) -{ - struct rv8263c8_data *data = p_dev->data; - const struct rv8263c8_config *config = p_dev->config; - - LOG_DBG("Initialize interrupts..."); - - if (!gpio_is_ready_dt(&config->int_gpio)) { - LOG_ERR("INT GPIO device not ready!"); - return -ENODEV; - } - - if (gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT)) { - return -EFAULT; - } - /* - data->dev = p_dev; - - #ifdef CONFIG_RV_8263_C8_TRIGGER_OWN_THREAD - k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT); - - k_thread_create(&bmi2_thread, bmi2_thread_stack, CONFIG_RV_8263_C8_THREAD_STACK_SIZE, - bmi2_worker_thread, data, NULL, NULL, K_PRIO_COOP(CONFIG_RV_8263_C8_THREAD_PRIORITY), - 0, K_NO_WAIT); - #else - data->work.handler = bmi2_worker; - #endif - - gpio_init_callback(&data->gpio_handler, rv8263c8_gpio_on_interrupt_callback, BIT(config->int_gpio.pin)); - - if (gpio_add_callback(config->int_gpio.port, &data->gpio_handler)) { - return -EFAULT; - } - - LOG_DBG("Interrupts ready!"); - */ - return 0; -} \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h b/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h deleted file mode 100644 index 37287b7a..00000000 --- a/app/drivers/rtc/rv8263c8/interrupt/microcrystal_rv8263c8_interrupt.h +++ /dev/null @@ -1,15 +0,0 @@ -/* microcrystal_rv8263c8_interrupt.c - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c) 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -/** @brief - * @param p_dev - * @return 0 when successful -*/ -int rv8263c8_init_interrupt(const struct device *p_dev); \ 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 78acd90a..77560833 100644 --- a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c +++ b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.c @@ -1,17 +1,19 @@ -/* microcrystal_rv8263c8.c - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c) 2024, Daniel Kampert +/* microcrystal_rv8263c8.c - Driver for Micro Crystal RV-8263-C8 RTC. * - * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Daniel Kampert + * Author: Daniel Kampert */ -#include "microcrystal_rv8263c8.h" -#include "private/microcrystal_rv8263c8_types.h" -#include "interrupt/microcrystal_rv8263c8_interrupt.h" +#include +#include +#include +#include +#include +#include +#include -#define RV8263C8_REGISTER_CONTROL1 0x00 -#define RV8263C8_REGISTER_CONTROL2 0x01 +#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 @@ -29,19 +31,22 @@ #define RV8263C8_REGISTER_TIMER_VALUE 0x10 #define RV8263C8_REGISTER_TIMER_MODE 0x11 -#define RV8263C8_24H_MODE_ENABLE (0x00 << 0x01) -#define RV8263C8_24H_MODE_DISABLE (0x00 << 0x01) -#define RV8263C8_CLOCK_ENABLE (0x00 << 0x05) -#define RV8263C8_CLOCK_DISABLE (0x01 << 0x05) -#define RV8263C8_ALARM_INT_ENABLE (0x01 << 0x07) -#define RV8263C8_ALARM_INT_DISABLE (0x00 << 0x05) -#define RV8263C8_MINUTE_INT_ENABLE (0x01 << 0x05) -#define RV8263C8_MINUTE_INT_DISABLE (0x00 << 0x05) -#define RV8263C8_HALF_MINUTE_INT_ENABLE (0x01 << 0x04) -#define RV8263C8_HALF_MINUTE_INT_DISABLE (0x00 << 0x04) -#define RV8263C8_ALARM_ENABLE (0x00 << 0x07) -#define RV8263C8_ALARM_DISABLE (0x01 << 0x07) -#define RV8263C8_SOFTWARE_RESET (0x58) +#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) @@ -69,55 +74,355 @@ #define DT_DRV_COMPAT microcrystal_rv_8263_c8 -LOG_MODULE_REGISTER(microcrystal_rv8263c8, CONFIG_MICROCRYSTAL_RV_8263_C8_LOG_LEVEL); +#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 -static void rv8263c8_isr(const struct device *p_dev) +#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) { - struct rv8263c8_data *const dev_data = p_dev->data; + 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); } -static int rv8263c8_set_time(const struct device *p_dev, const struct rtc_time *p_tm) +#ifdef 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) { - int err; - uint8_t regs[7]; + 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); + } +} +#else +/** + * @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 + +/** + * @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); +} + +#ifdef 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); + } +} +#else +/** + * @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_tm->tm_year, p_tm->tm_mon, p_tm->tm_mday, p_tm->tm_wday, p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec); + 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_tm->tm_sec) & SECONDS_BITS; - regs[1] = bin2bcd(p_tm->tm_min); - regs[2] = bin2bcd(p_tm->tm_hour); - regs[3] = bin2bcd(p_tm->tm_wday); - regs[4] = bin2bcd(p_tm->tm_mday); - regs[5] = bin2bcd(p_tm->tm_mon); - regs[6] = bin2bcd((p_tm->tm_year % 100)); + 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; } -static int rv8263c8_get_time(const struct device *p_dev, struct rtc_time *p_timeptr) +/** + * @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_get_time_exit; + k_spin_unlock(&data->lock, key); + if (err < 0) { + return err; } p_timeptr->tm_sec = bcd2bin(regs[0] & SECONDS_BITS); @@ -136,28 +441,35 @@ static int rv8263c8_get_time(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_get_time_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_get_time_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"); + LOG_ERR("I2C bus not ready!"); return -ENODEV; } @@ -169,108 +481,158 @@ static int rv8263c8_init(const struct device *p_dev) 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_CONTROL1, - RV8263C8_24H_MODE_DISABLE | RV8263C8_CLOCK_ENABLE); + 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_CONTROL2, - RV8263C8_ALARM_INT_ENABLE | RV8263C8_MINUTE_INT_DISABLE | RV8263C8_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 - if (config->int_gpio.port) { - if (rv8263c8_init_interrupt(p_dev)) { - LOG_ERR("Could not initialize interrupts!"); - return -EFAULT; - } +#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; } -rv8263c8_init_exit: - k_spin_unlock(&data->lock, key); - - return err; -} - -#ifdef CONFIG_RTC_ALARM -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; + 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; } - if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && - (p_timeptr->tm_min < MIN_MIN || p_timeptr->tm_min > MAX_MIN)) { - return false; + 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; } - if ((mask & RTC_ALARM_TIME_MASK_HOUR) && - (p_timeptr->tm_hour < MIN_HOUR || p_timeptr->tm_hour > MAX_HOUR)) { - return false; + 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 - return true; +#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 (id != 0) { + 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); + (*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 dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); - - if (id != 0) { - err = -EINVAL; - goto rv8263c8_alarm_set_time_exit; + if ((p_dev == NULL) || (p_timeptr == NULL) || (id != 0) || ((mask > 0) && (p_timeptr == NULL))) { + return -EINVAL; } - if ((mask > 0) && (p_timeptr == NULL)) { - err = -EINVAL; - goto rv8263c8_alarm_set_time_exit; - } + k_spinlock_key_t key = k_spin_lock(&data->lock); // 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) { err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_ALARM_ENABLE | p_timeptr->tm_sec); + RV8263C8_BM_ALARM_ENABLE | p_timeptr->tm_sec); } else { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, RV8263C8_BM_ALARM_DISABLE); } if (err < 0) { @@ -279,10 +641,10 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint } if (mask & RTC_ALARM_TIME_MASK_MINUTE) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_ALARM_ENABLE | p_timeptr->tm_min); + 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_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_MINUTES_ALARM, RV8263C8_BM_ALARM_DISABLE); } if (err < 0) { @@ -291,10 +653,10 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint } if (mask & RTC_ALARM_TIME_MASK_HOUR) { - err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, - RV8263C8_ALARM_ENABLE | p_timeptr->tm_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_SECONDS_ALARM, RV8263C8_ALARM_DISABLE); + err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_HOURS_ALARM, RV8263C8_BM_ALARM_DISABLE); } if (err < 0) { @@ -302,47 +664,61 @@ static int rv8263c8_alarm_set_time(const struct device *p_dev, uint16_t id, uint goto rv8263c8_alarm_set_time_exit; } - err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_ENABLE); + 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 CONTROL2! Error: %i", err); + LOG_ERR("Error while writing MONTHDAY alarm! Error: %i", err); goto rv8263c8_alarm_set_time_exit; } - err = 0; + 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(&dev_data->lock, key); + 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) { int err; - struct rv8263c8_data *const dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - - rtc_alarm_callback cb; - void *cb_data; - rtc_update_callback update_cb; - void *update_cb_data; uint8_t value; - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); - - if (id != 0) { - err = -EINVAL; - goto rv8263c8_alarm_get_time_exit; - } - - if (p_timeptr == NULL) { - err = -EINVAL; - goto rv8263c8_alarm_get_time_exit; + 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_reg_read_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS_ALARM, &value); if (err < 0) { LOG_ERR("Error while reading SECONDS! Error: %i", err); @@ -361,7 +737,7 @@ static int rv8263c8_alarm_get_time(const struct device *p_dev, uint16_t id, uint } if (value <= MAX_MIN) { - p_timeptr->tm_hour = value; + p_timeptr->tm_min = value; (*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE; } @@ -376,37 +752,68 @@ static int rv8263c8_alarm_get_time(const struct device *p_dev, uint16_t id, uint (*p_mask) |= RTC_ALARM_TIME_MASK_HOUR; } - err = 0; + 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; + (*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; + (*p_mask) |= RTC_ALARM_TIME_MASK_WEEKDAY; + } rv8263c8_alarm_get_time_exit: - k_spin_unlock(&dev_data->lock, key); + k_spin_unlock(&data->lock, key); return err; } +/** + * @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 dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; const struct rv8263c8_config *config = p_dev->config; - if (id != 0) { + if ((p_dev == NULL) || (id != 0)) { return -EINVAL; } - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + k_spinlock_key_t key = k_spin_lock(&data->lock); - dev_data->cb = callback; - dev_data->cb_data = p_user_data; + 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_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_ENABLE); + 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_CONTROL2, 0x01 << 0x07, RV8263C8_ALARM_INT_DISABLE); + 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(&dev_data->lock, key); + k_spin_unlock(&data->lock, key); if (err < 0) { LOG_ERR("Error while writing CONTROL2! Error: %i", err); @@ -416,54 +823,121 @@ 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; - struct rv8263c8_data *const dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; - if (id != 0) { + if ((p_dev == NULL) || (id != 0)) { return -EINVAL; } - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); - - ret = dev_data->alarm_pending ? 1 : 0; - dev_data->alarm_pending = false; - - k_spin_unlock(&dev_data->lock, key); + 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 -static int rv8263c8_update_set_callback(const struct device *p_dev, - rtc_update_callback callback, void *p_user_data) +/** + * @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 dev_data = p_dev->data; + struct rv8263c8_data *const data = p_dev->data; -#error Not supported + if (p_dev == NULL) { + return -EINVAL; + } - k_spinlock_key_t key = k_spin_lock(&dev_data->lock); + 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); - dev_data->update_cb = callback; - dev_data->update_cb_data = p_user_data; + return rv8263c8_update_enable_timer(p_dev); +} +#endif - if (callback != NULL) { - // TODO: Enable - } else { - // TODO: Disable +#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; } - k_spin_unlock(&dev_data->lock, key); + 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_set_time, - .get_time = rv8263c8_get_time, + .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, @@ -471,24 +945,27 @@ static const struct rtc_driver_api rv8263c8_driver_api = { .alarm_is_pending = rv8263c8_alarm_is_pending, .alarm_set_callback = rv8263c8_alarm_set_callback, #endif - #ifdef CONFIG_RTC_UPDATE - .update_set_callback = rv8263c8_update_set_callback, + .update_set_callback = rv8263_update_callback, +#endif +#ifdef CONFIG_RTC_CALIBRATION + .set_calibration = rv8263c8_calibration_set, + .get_calibration = rv8263c8_calibration_get, #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/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h b/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h deleted file mode 100644 index 23fc1068..00000000 --- a/app/drivers/rtc/rv8263c8/microcrystal_rv8263c8.h +++ /dev/null @@ -1,22 +0,0 @@ -/* microcrystal_rv8263c8.h - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -/** @brief -*/ -typedef enum { - RV8263C8_OUT_32KHZ = 0, - RV8263C8_OUT_16KHZ, - RV8263C8_OUT_8KHZ, - RV8263C8_OUT_4KHZ, - RV8263C8_OUT_2KHZ, - RV8263C8_OUT_1KHZ, - RV8263C8_OUT_1HZ, - RV8263C8_OUT_LOW, -} rv_8263_c8_clkout_t; \ No newline at end of file diff --git a/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h b/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h deleted file mode 100644 index 660c5919..00000000 --- a/app/drivers/rtc/rv8263c8/private/microcrystal_rv8263c8_types.h +++ /dev/null @@ -1,36 +0,0 @@ -/* microcrystal_rv8263c8_types.h - Driver for Micro Crystal RV-8263-C8 RTC. */ - -/* - * Copyright (c 2024, Daniel Kampert - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct rv8263c8_config { - struct i2c_dt_spec i2c_bus; - rv_8263_c8_clkout_t clkout; - bool fast_mode; - int8_t offset; - struct gpio_dt_spec int_gpio; -}; - -struct rv8263c8_data { - struct k_spinlock lock; - bool alarm_pending; - rtc_alarm_callback cb; - void *cb_data; - rtc_update_callback update_cb; - void *update_cb_data; -}; \ No newline at end of file diff --git a/app/drivers/sensor/apds9306/Kconfig b/app/drivers/sensor/apds9306/Kconfig index 60af26e3..ea704e35 100644 --- a/app/drivers/sensor/apds9306/Kconfig +++ b/app/drivers/sensor/apds9306/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig APDS9306 - bool "APDS9306 Sensor" + bool "Avago APDS9306 Sensor" default y depends on DT_HAS_AVAGO_APDS9306_ENABLED select I2C diff --git a/app/drivers/sensor/bmi270/Kconfig b/app/drivers/sensor/bmi270/Kconfig index 7c3fa5e4..4c7f01a4 100644 --- a/app/drivers/sensor/bmi270/Kconfig +++ b/app/drivers/sensor/bmi270/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig BMI270_PLUS - bool "BMI270 Plus Sensor" + bool "Bosch BMI270 Plus Sensor" depends on DT_HAS_BOSCH_BMI270_PLUS_ENABLED default y select I2C diff --git a/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h b/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h index 22759684..a47ca631 100644 --- a/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h +++ b/app/drivers/sensor/bmi270/private/bosch_bmi270_types.h @@ -19,7 +19,7 @@ /** @brief */ struct bmi270_config { - struct i2c_dt_spec i2c; + struct i2c_dt_spec i2c; #ifdef CONFIG_BMI270_PLUS_TRIGGER struct gpio_dt_spec int_gpio; #endif @@ -48,25 +48,23 @@ struct bmi270_data { sensor_trigger_handler_t motion; #endif -#ifdef CONFIG_BMI270_PLUS_TRIGGER_OWN_THREAD +#if defined(CONFIG_BMI270_PLUS_TRIGGER_OWN_THREAD) struct k_sem sem; -#endif - -#ifdef CONFIG_BMI270_PLUS_TRIGGER_GLOBAL_THREAD +#elif defined(CONFIG_BMI270_PLUS_TRIGGER_GLOBAL_THREAD) struct k_work work; #endif int16_t ax; - int16_t ay; - int16_t az; - int16_t gx; - int16_t gy; - int16_t gz; - uint16_t temp; + int16_t ay; + int16_t az; + int16_t gx; + int16_t gy; + int16_t gz; + uint16_t temp; uint8_t acc_range; - uint8_t acc_odr; + uint8_t acc_odr; uint16_t gyr_range; - uint8_t gyr_odr; - uint8_t gyr_osr; + uint8_t gyr_odr; + uint8_t gyr_osr; struct bmi2_dev bmi2; -}; \ No newline at end of file +}; diff --git a/app/drivers/sensor/bmp581/Kconfig b/app/drivers/sensor/bmp581/Kconfig index 6c8eef94..2ba582c5 100644 --- a/app/drivers/sensor/bmp581/Kconfig +++ b/app/drivers/sensor/bmp581/Kconfig @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 menuconfig BMP581 - bool "BMP581 Sensor" + bool "Bosch BMP581 Sensor" depends on DT_HAS_BOSCH_BMP581_ENABLED default y select I2C diff --git a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml index 9383be82..1c998e3e 100644 --- a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml +++ b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml @@ -13,26 +13,28 @@ 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: 7 + required: false + default: 0 enum: - - 0 + - 32768 + - 8192 + - 1024 + - 64 + - 32 - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 + - 0 description: - CLKOUT frequency selection. Set to 7 to disable it. \ No newline at end of file + CLKOUT frequency selection. Set to 0 to disable the CLKOUT signal (CLKOUT pin is LOW). \ No newline at end of file From 9d781816f964203864cae0586b58d79821a755ad Mon Sep 17 00:00:00 2001 From: Daniel Kampert Date: Thu, 20 Jun 2024 10:29:39 +0200 Subject: [PATCH 4/4] - 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 - Add RTC to firmware --- app/CMakeLists.txt | 12 +- .../zswatch_nrf5340_cpuapp_3.overlay | 4 + .../zswatch_nrf5340_cpuapp_4.overlay | 4 + .../zswatch_nrf5340_cpuapp_5.conf | 4 - .../zswatch_nrf5340_cpuapp_5.overlay | 9 +- app/child_image/hci_ipc.conf | 47 + 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 ----------------- .../bindings/rtc/microcrystal,rv-8263-c8.yml | 40 - app/{zephyr_patches => patches}/ntc_fix.patch | 0 app/patches/rv8263_rtc.patch | 950 ++++++++++++++++++ app/src/events/zsw_periodic_event.c | 1 + app/src/main.c | 1 + app/src/zsw_clock.c | 29 +- app/src/zsw_clock.h | 7 + app/src/zsw_rtc.c | 98 ++ 21 files changed, 1147 insertions(+), 1107 deletions(-) create mode 100644 app/child_image/hci_ipc.conf 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 delete mode 100644 app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml rename app/{zephyr_patches => patches}/ntc_fix.patch (100%) create mode 100644 app/patches/rv8263_rtc.patch create mode 100644 app/src/zsw_rtc.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 96633aaa..58e89bbb 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -47,13 +47,19 @@ include_directories(src/applications) include_directories(src/ui/watchfaces) if (CONFIG_DT_HAS_NORDIC_NPM1300_ENABLED) -add_subdirectory(src/fuel_gauge) + add_subdirectory(src/fuel_gauge) else() -add_subdirectory(src/basic_battery) + add_subdirectory(src/basic_battery) endif() -target_sources(app PRIVATE src/main.c) +# TODO target_sources(app PRIVATE src/zsw_clock.c) +#if (CONFIG_RTC) +# target_sources(app PRIVATE src/zsw_rtc.c) +#else() +#endif() + +target_sources(app PRIVATE src/main.c) target_sources(app PRIVATE src/zsw_cpu_freq.c) target_sources(app PRIVATE src/zsw_retained_ram_storage.c) target_sources(app PRIVATE src/zsw_coredump.c) diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay index 4273d1fb..4266ef71 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_3.overlay @@ -83,6 +83,10 @@ }; }; +&uart0 { + status = "disabled"; +}; + &i2c1 { bmi270: bmi270@68 { compatible = "bosch,bmi270-plus"; diff --git a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay index 4df49748..3402482f 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_4.overlay @@ -127,6 +127,10 @@ }; +&uart0 { + status = "disabled"; +}; + &i2c1 { bmi270: bmi270@68 { compatible = "bosch,bmi270-plus"; 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 97ef031a..b9a724ce 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.conf @@ -48,8 +48,4 @@ CONFIG_NVS=y CONFIG_DEBUG_COREDUMP_BACKEND_OTHER=y CONFIG_RTC=y -CONFIG_RTC_ALARM=y -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 ad548e8f..18ba65e0 100644 --- a/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay +++ b/app/boards/arm/zswatch_nrf5340/zswatch_nrf5340_cpuapp_5.overlay @@ -185,13 +185,16 @@ pwms = <&pwm2 0 PWM_HZ(880) PWM_POLARITY_NORMAL>; }; }; +}; +&uart0 { + status = "disabled"; }; &i2c1 { - rv_8263_c8: rv-8263-c8@A2 { + rv_8263_c8: rv-8263-c8@51 { compatible = "microcrystal,rv-8263-c8"; - reg = <0xA2>; + reg = <0x51>; status = "okay"; clkout = <0>; int-gpios = <&gpio1 13 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; @@ -367,4 +370,4 @@ reg = <0x400000 0x100000 >; }; }; -}; \ No newline at end of file +}; diff --git a/app/child_image/hci_ipc.conf b/app/child_image/hci_ipc.conf new file mode 100644 index 00000000..3eafa0e6 --- /dev/null +++ b/app/child_image/hci_ipc.conf @@ -0,0 +1,47 @@ +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_CTLR=y +CONFIG_BT_LL_SW_SPLIT=y + +CONFIG_BT_CTLR_ADV_EXT=y +CONFIG_BT_CTLR_ADV_PERIODIC=y + +CONFIG_BT_CTLR_DF=y + +CONFIG_BT_CTLR_DF_SCAN_CTE_RX=n +CONFIG_BT_CTLR_DF_ANT_SWITCH_RX=n +CONFIG_BT_CTLR_DF_CTE_RX=n + +# Limit number of possible connection to decrease memory usage +CONFIG_BT_MAX_CONN=1 + +# Enable chaining of multiple CTEs in periodic advertising +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y +CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=16 + +CONFIG_BT_CTLR_TX_PWR_0=y +CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=191 + +# Enable chaining of multiple CTEs in periodic advertising +CONFIG_BT_CTLR_ADVANCED_FEATURES=y +CONFIG_BT_CTLR_ADV_SYNC_PDU_BACK2BACK=y +CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX=16 + +CONFIG_BT_CTLR_DF_ANT_SWITCH_TX=n +CONFIG_BT_BROADCASTER=y + +CONFIG_BT_EXT_ADV_MAX_ADV_SET=2 +CONFIG_BT_CTLR_ADV_AUX_SET=2 +CONFIG_BT_TICKER_UPDATE=y + +CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y +# DFU over BT requires +# increase of ble throughput +CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_TX_SIZE=502 + +# Temporary fix as there is a strange behaviour with some Android +# phones. +CONFIG_BT_DATA_LEN_UPDATE=n +#CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 \ 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/dts/bindings/rtc/microcrystal,rv-8263-c8.yml b/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml deleted file mode 100644 index 1c998e3e..00000000 --- a/app/dts/bindings/rtc/microcrystal,rv-8263-c8.yml +++ /dev/null @@ -1,40 +0,0 @@ -description: | - The RV-8263-C8 is a CMOS real-time clock/calendar module optimized for low power consumption. An Offset - register makes it possible to compensate for the frequency deviation of the clock of 32.768 kHz - -compatible: "microcrystal,rv-8263-c8" - -include: [rtc-device.yaml, i2c-device.yaml] - -properties: - int-gpios: - type: phandle-array - required: false - - 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 - required: false - default: 0 - enum: - - 32768 - - 8192 - - 1024 - - 64 - - 32 - - 1 - - 0 - description: - CLKOUT frequency selection. Set to 0 to disable the CLKOUT signal (CLKOUT pin is LOW). \ 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..35fdada1 --- /dev/null +++ b/app/patches/rv8263_rtc.patch @@ -0,0 +1,950 @@ +diff --git a/drivers/rtc/rtc_utils.h b/drivers/rtc/rtc_utils.c +new file mode 100644 +index 0000000000..035034da56 +--- /dev/null ++++ b/drivers/rtc/rtc_utils.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2023 Bjarki Arge Andreasen ++ * Copyright (c) 2024 Andrew Featherstone ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "rtc_utils.h" ++ ++bool rtc_utils_validate_rtc_time(const struct rtc_time *timeptr, uint16_t mask) ++{ ++ if ((mask & RTC_ALARM_TIME_MASK_SECOND) && (timeptr->tm_sec < 0 || timeptr->tm_sec > 59)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && (timeptr->tm_min < 0 || timeptr->tm_min > 59)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_HOUR) && (timeptr->tm_hour < 0 || timeptr->tm_hour > 23)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_MONTH) && (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_MONTHDAY) && ++ (timeptr->tm_mday < 1 || timeptr->tm_mday > 31)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_YEAR) && (timeptr->tm_year < 0 || timeptr->tm_year > 199)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_WEEKDAY) && ++ (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_YEARDAY) && ++ (timeptr->tm_yday < 0 || timeptr->tm_yday > 365)) { ++ return false; ++ } ++ ++ if ((mask & RTC_ALARM_TIME_MASK_NSEC) && ++ (timeptr->tm_nsec < 0 || timeptr->tm_nsec > 999999999)) { ++ return false; ++ } ++ ++ return true; ++} +diff --git a/drivers/rtc/rtc_utils.h b/drivers/rtc/rtc_utils.h +new file mode 100644 +index 0000000000..035034da56 +--- /dev/null ++++ b/drivers/rtc/rtc_utils.h +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (c) 2023 Bjarki Arge Andreasen ++ * Copyright (c) 2024 Andrew Featherstone ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++#ifndef ZEPHYR_DRIVERS_RTC_RTC_UTILS_H_ ++#define ZEPHYR_DRIVERS_RTC_RTC_UTILS_H_ ++ ++#include ++#include ++ ++#include ++ ++/** ++ * @brief Validate a datetime with a mask ++ * ++ * Ensure that any fields selected by mask contain a valid value. ++ * ++ * @param timeptr The time to set ++ * @param mask Mask of fields to validate ++ * ++ * @return true if the required fields are valid. ++ */ ++bool rtc_utils_validate_rtc_time(const struct rtc_time *timeptr, uint16_t mask); ++ ++#endif /* ZEPHYR_DRIVERS_RTC_RTC_UTILS_H_ */ +diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt +index 65d822d..4236ad5 100644 +--- a/drivers/rtc/CMakeLists.txt ++++ b/drivers/rtc/CMakeLists.txt +@@ -5,6 +5,9 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/rtc.h) + + 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) +index 46bd2562dd..2a1d5fa05b 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -53,1 +53,2 @@ source "drivers/rtc/Kconfig.sam" ++source "drivers/rtc/Kconfig.rv8263" + + endif # RTC +diff --git a/drivers/rtc/Kconfig.rv8263 b/drivers/rtc/Kconfig.rv8263 +new file mode 100644 +index 0000000000..035034da56 +--- /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 0000000000..bb82ed7d9b +--- /dev/null ++++ b/drivers/rtc/rtc_rv8263.c +@@ -0,0 +1,771 @@ ++/* 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 buf[2]; ++ const struct rv8263c8_config *config = dev->config; ++ ++ /* Value 0 disables the timer. */ ++ buf[0] = RV8263C8_REGISTER_TIMER_VALUE; ++ buf[1] = 0; ++ err = i2c_write_dt(&config->i2c_bus, buf, 2); ++ if (err < 0) { ++ return err; ++ } ++ ++ buf[0] = RV8263C8_REGISTER_TIMER_MODE; ++ return i2c_write_dt(&config->i2c_bus, buf, 2); ++} ++ ++#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; ++ const struct rv8263c8_config *config = dev->config; ++ uint8_t buf[2]; ++ ++ /* Set the timer preload value for 1 second. */ ++ buf[0] = RV8263C8_REGISTER_TIMER_VALUE; ++ buf[1] = 1; ++ err = i2c_write_dt(&config->i2c_bus, buf, 2); ++ if (err < 0) { ++ return err; ++ } ++ ++ buf[0] = RV8263C8_REGISTER_TIMER_MODE; ++ buf[1] = RV8263_BM_TD_1HZ | RV8263_BM_TE_ENABLE | RV8263_BM_TIE_ENABLE | ++ RV8263_BM_TI_TP_PULSE; ++ return i2c_write_dt(&config->i2c_bus, buf, 2); ++} ++ ++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[8]; ++ 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] = RV8263C8_REGISTER_SECONDS; ++ regs[1] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS; ++ regs[2] = bin2bcd(timeptr->tm_min) & MINUTES_BITS; ++ regs[3] = bin2bcd(timeptr->tm_hour) & HOURS_BITS; ++ regs[4] = bin2bcd(timeptr->tm_mday) & DATE_BITS; ++ regs[5] = bin2bcd(timeptr->tm_wday) & WEEKDAY_BITS; ++ regs[6] = bin2bcd(timeptr->tm_mon) & MONTHS_BITS; ++ regs[7] = bin2bcd(timeptr->tm_year - RV8263_YEAR_OFFSET) & YEAR_BITS; ++ ++ return i2c_write_dt(&config->i2c_bus, regs, 8); ++} ++ ++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 = config->clkout; ++ LOG_DBG("Configure ClkOut: %u", temp); ++ ++ err = i2c_reg_write_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2, ++ RV8263C8_BM_AF | temp); ++ if (err < 0) { ++ LOG_ERR("Error while writing CONTROL_2! Error: %i", err); ++ return err; ++ } ++ ++ LOG_DBG("Configure ClkOut: %u", temp); ++ ++#if CONFIG_RTC_UPDATE && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) ++ uint8_t buf[2]; ++ ++ buf[0] = RV8263C8_REGISTER_TIMER_MODE; ++ buf[1] = 0; ++ err = i2c_write_dt(&config->i2c_bus, buf, 2); ++ 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_ENUM_IDX(inst, clkout), \ ++ IF_ENABLED(1, \ ++ (.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 0000000000..da6c246dbc +--- /dev/null ++++ b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml +@@ -0,0 +1,31 @@ ++# 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: 0 ++ enum: ++ - 32768 ++ - 16384 ++ - 8192 ++ - 4096 ++ - 2048 ++ - 1024 ++ - 1 ++ - 0 ++ description: ++ Enable CLKOUT at given frequency. When disabled, CLKOUT pin is LOW. ++ The default is 0 and corresponds to the disable the CLKOUT signal. diff --git a/app/src/events/zsw_periodic_event.c b/app/src/events/zsw_periodic_event.c index 27209a96..d3981bb9 100644 --- a/app/src/events/zsw_periodic_event.c +++ b/app/src/events/zsw_periodic_event.c @@ -105,6 +105,7 @@ static int zsw_timer_init(void) k_work_init_delayable(work, handle_slow_timeout); zbus_chan_finish(&periodic_event_10s_chan); + // TODO: Use the RTC instead of the timer in rev 5 to generate this zbus_chan_claim(&periodic_event_1s_chan, K_FOREVER); work = (struct k_work_delayable *)zbus_chan_user_data(&periodic_event_1s_chan); k_work_init_delayable(work, handle_mid_timeout); diff --git a/app/src/main.c b/app/src/main.c index bf9c4513..7b7740b7 100644 --- a/app/src/main.c +++ b/app/src/main.c @@ -574,6 +574,7 @@ static void on_zbus_ble_data_callback(const struct zbus_channel *chan) switch (event->data.type) { case BLE_COMM_DATA_TYPE_SET_TIME: { if (event->data.data.time.seconds > 0) { + // TODO: Replace with set time struct timespec tspec; tspec.tv_sec = event->data.data.time.seconds; tspec.tv_nsec = 0; diff --git a/app/src/zsw_clock.c b/app/src/zsw_clock.c index ce187f74..fc46a423 100644 --- a/app/src/zsw_clock.c +++ b/app/src/zsw_clock.c @@ -15,22 +15,19 @@ * along with this program. If not, see . */ -#include -#include -#include -#include #include #include #include +#include +#include + #include #include -#include #include #include -#include -#include -#include +#include "zsw_clock.h" +#include "zsw_retained_ram_storage.h" #include "events/zsw_periodic_event.h" static void zbus_periodic_slow_callback(const struct zbus_channel *chan); @@ -38,6 +35,16 @@ static void zbus_periodic_slow_callback(const struct zbus_channel *chan); ZBUS_CHAN_DECLARE(periodic_event_1s_chan); ZBUS_LISTENER_DEFINE(zsw_clock_lis, zbus_periodic_slow_callback); +void zsw_clock_set_time(zsw_timeval_t *ztm) +{ + struct timespec tspec; + + tspec.tv_sec = ztm->tm.tm_sec; + tspec.tv_nsec = 0; + + clock_settime(CLOCK_REALTIME, &tspec); +} + void zsw_clock_get_time(zsw_timeval_t *ztm) { struct timeval tv; @@ -49,10 +56,8 @@ void zsw_clock_get_time(zsw_timeval_t *ztm) // Add one to the month because we want to count from December instead of January ztm->tm.tm_mon += 1; - // Add 1900 to the year because we want to count from 0 + // Add 1900 to the year because we want to count from 1900 ztm->tm.tm_year += 1900; - - ztm->tv_usec = tv.tv_usec; } time_t zsw_clock_get_time_unix(void) @@ -72,8 +77,10 @@ static void zbus_periodic_slow_callback(const struct zbus_channel *chan) static int zsw_clock_init(void) { struct timespec tspec; + tspec.tv_sec = retained.current_time_seconds; tspec.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &tspec); if (strlen(retained.timezone) > 0) { setenv("TZ", retained.timezone, 1); diff --git a/app/src/zsw_clock.h b/app/src/zsw_clock.h index 5290ceb9..cbfd188d 100644 --- a/app/src/zsw_clock.h +++ b/app/src/zsw_clock.h @@ -20,10 +20,17 @@ #include #include +typedef void (*zsw_clock_on_update)(void); + typedef struct { struct tm tm; /**< Modified time object with 1900 added to the year and the month increased by one. */ uint32_t tv_usec; } zsw_timeval_t; +void zsw_clock_add_update(zsw_clock_on_update callback); + +void zsw_clock_set_time(zsw_timeval_t *ztm); + void zsw_clock_get_time(zsw_timeval_t *ztm); + time_t zsw_clock_get_time_unix(void); \ No newline at end of file diff --git a/app/src/zsw_rtc.c b/app/src/zsw_rtc.c new file mode 100644 index 00000000..33977beb --- /dev/null +++ b/app/src/zsw_rtc.c @@ -0,0 +1,98 @@ +/* + * This file is part of ZSWatch project . + * Copyright (c) 2023 Jakob Krantz. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "zsw_clock.h" +#include "events/zsw_periodic_event.h" + +const struct device *const rtc = DEVICE_DT_GET(DT_NODELABEL(rv_8263_c8)); + +LOG_MODULE_REGISTER(zsw_clock, LOG_LEVEL_DBG); + +void zsw_clock_add_update(zsw_clock_on_update callback) +{ + +} + +void zsw_clock_set_time(zsw_timeval_t *ztm) +{ + struct rtc_time time; + + time.tm_sec = ztm->tm.tm_sec; + time.tm_min = ztm->tm.tm_min; + time.tm_hour = ztm->tm.tm_hour; + time.tm_wday = ztm->tm.tm_wday; + time.tm_mday = ztm->tm.tm_mday; + time.tm_mon = ztm->tm.tm_mon - 1; + time.tm_year = ztm->tm.tm_year - 1900; + + //rtc_set_time(rtc, &time); +} + +void zsw_clock_get_time(zsw_timeval_t *ztm) +{ + struct rtc_time time; + + //rtc_get_time(rtc, &time); + + ztm->tm.tm_sec = time.tm_sec; + ztm->tm.tm_min = time.tm_min; + ztm->tm.tm_hour = time.tm_hour; + ztm->tm.tm_wday = time.tm_wday; + ztm->tm.tm_mday = time.tm_mday; + + // Add one to the month because we want to count from December instead of January + ztm->tm.tm_mon = time.tm_mon + 1; + + // Add 1900 to the year because we want to count from 0 + ztm->tm.tm_year = time.tm_year + 1900; +} + +time_t zsw_clock_get_time_unix(void) +{ + // TODO: + return 0; +} + +void on_rtc_update(const struct device *dev, void *user_data) +{ +} + +static int zsw_clock_init(void) +{ + if (!device_is_ready(rtc)) { + LOG_ERR("Device not ready!"); + return -1; + } + + rtc_update_set_callback(rtc, on_rtc_update, NULL); + + return 0; +} + +SYS_INIT(zsw_clock_init, APPLICATION, 2); \ No newline at end of file