diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt
index 65d822dbe..247d0fe20 100644
--- a/drivers/rtc/CMakeLists.txt
+++ b/drivers/rtc/CMakeLists.txt
@@ -5,6 +5,8 @@ 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)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 10b228cee..f7bb981e4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -50,6 +50,7 @@ source "drivers/rtc/Kconfig.pcf8563"
 source "drivers/rtc/Kconfig.mc146818"
 source "drivers/rtc/Kconfig.sam"
 source "drivers/rtc/Kconfig.stm32"
+source "drivers/rtc/Kconfig.rv8263"
 source "drivers/rtc/Kconfig.smartbond"
 
 endif # RTC
diff --git a/drivers/rtc/Kconfig.rv8263 b/drivers/rtc/Kconfig.rv8263
new file mode 100644
index 000000000..035034da5
--- /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 <DanielKampert@Kampis-Elektroecke.de>
+
+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_emul.c b/drivers/rtc/rtc_emul.c
index bf95fa878..84d061fd3 100644
--- a/drivers/rtc/rtc_emul.c
+++ b/drivers/rtc/rtc_emul.c
@@ -10,6 +10,8 @@
 #include <zephyr/device.h>
 #include <zephyr/drivers/rtc.h>
 
+#include "rtc_utils.h"
+
 struct rtc_emul_data;
 
 struct rtc_emul_work_delayable {
@@ -67,43 +69,6 @@ static bool rtc_emul_is_leap_year(struct rtc_time *datetime)
 	return false;
 }
 
-#ifdef CONFIG_RTC_ALARM
-static bool rtc_emul_validate_alarm_time(const struct rtc_time *timeptr, uint32_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;
-	}
-
-	return true;
-}
-#endif /* CONFIG_RTC_ALARM */
-
 static int rtc_emul_get_days_in_month(struct rtc_time *datetime)
 {
 	const uint8_t *dim = (rtc_emul_is_leap_year(datetime) == true) ?
@@ -346,7 +311,7 @@ static int rtc_emul_alarm_set_time(const struct device *dev, uint16_t id, uint16
 	}
 
 	if (mask > 0) {
-		if (rtc_emul_validate_alarm_time(timeptr, mask) == false) {
+		if (rtc_utils_validate_rtc_time(timeptr, mask) == false) {
 			return -EINVAL;
 		}
 	}
@@ -461,7 +426,7 @@ static int rtc_emul_get_calibration(const struct device *dev, int32_t *calibrati
 }
 #endif /* CONFIG_RTC_CALIBRATION */
 
-struct rtc_driver_api rtc_emul_driver_api = {
+static const struct rtc_driver_api rtc_emul_driver_api = {
 	.set_time = rtc_emul_set_time,
 	.get_time = rtc_emul_get_time,
 #ifdef CONFIG_RTC_ALARM
@@ -511,4 +476,4 @@ int rtc_emul_init(const struct device *dev)
 	DEVICE_DT_INST_DEFINE(id, rtc_emul_init, NULL, &rtc_emul_data_##id, NULL, POST_KERNEL,	\
 			      CONFIG_RTC_INIT_PRIORITY, &rtc_emul_driver_api);
 
-DT_INST_FOREACH_STATUS_OKAY(RTC_EMUL_DEVICE);
+DT_INST_FOREACH_STATUS_OKAY(RTC_EMUL_DEVICE);
\ No newline at end of file
diff --git a/drivers/rtc/rtc_rv8263.c b/drivers/rtc/rtc_rv8263.c
new file mode 100644
index 000000000..31e8b2ccb
--- /dev/null
+++ b/drivers/rtc/rtc_rv8263.c
@@ -0,0 +1,721 @@
+/* Copyright (c) 2024 Daniel Kampert
+ * Author: Daniel Kampert <DanielKampert@kampis-Elektroecke.de>
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/devicetree.h>
+#include <zephyr/drivers/i2c.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/rtc.h>
+#include <zephyr/logging/log.h>
+#include <zephyr/sys/byteorder.h>
+#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 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, &reg);
+
+		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, &reg);
+
+		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) + 1;
+	regs[7] = bin2bcd(timeptr->tm_year - RV8263_YEAR_OFFSET) & YEAR_BITS;
+
+	return i2c_write_dt(&config->i2c_bus, regs, sizeof(regs));
+}
+
+static int rv8263c8_time_get(const struct device *dev, struct rtc_time *timeptr)
+{
+	int err;
+	uint8_t regs[7];
+	const struct rv8263c8_config *config = dev->config;
+
+	if (timeptr == NULL) {
+		return -EINVAL;
+	}
+
+	err = i2c_burst_read_dt(&config->i2c_bus, RV8263C8_REGISTER_SECONDS, regs, sizeof(regs));
+	if (err < 0) {
+		return err;
+	}
+
+	/* Return an error when the oscillator is stopped. */
+	if (regs[0] & RV8263_BM_OS) {
+		return -ENODATA;
+	}
+
+	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) - 1;
+	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[6];
+	const struct rv8263c8_config *config = dev->config;
+
+	ARG_UNUSED(id);
+
+	if ((mask > 0) && (timeptr == NULL)) {
+		return -EINVAL;
+	}
+
+	if (!rtc_utils_validate_rtc_time(timeptr, mask)) {
+		LOG_ERR("Invalid mask!");
+		return -EINVAL;
+	}
+
+	if (mask == 0) {
+		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_DISABLE);
+	} else {
+		/* Clear the AIE and AF bit to prevent false triggering of the alarm. */
+		err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2,
+					     RV8263C8_BM_ALARM_INT_ENABLE | RV8263C8_BM_AF, 0);
+	}
+
+	if (err < 0) {
+		LOG_ERR("Error while enabling alarm! Error: %i", err);
+		return err;
+	}
+
+	regs[0] = RV8263C8_REGISTER_SECONDS_ALARM;
+
+	if (mask & RTC_ALARM_TIME_MASK_SECOND) {
+		regs[1] = bin2bcd(timeptr->tm_sec) & SECONDS_BITS;
+	} else {
+		regs[1] = RV8263C8_BM_ALARM_DISABLE;
+	}
+
+	if (mask & RTC_ALARM_TIME_MASK_MINUTE) {
+		regs[2] = bin2bcd(timeptr->tm_min) & MINUTES_BITS;
+	} else {
+		regs[2] = RV8263C8_BM_ALARM_DISABLE;
+	}
+
+	if (mask & RTC_ALARM_TIME_MASK_HOUR) {
+		regs[3] = bin2bcd(timeptr->tm_min) & HOURS_BITS;
+	} else {
+		regs[3] = RV8263C8_BM_ALARM_DISABLE;
+	}
+
+	if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
+		regs[4] = bin2bcd(timeptr->tm_min) & DATE_BITS;
+	} else {
+		regs[4] = RV8263C8_BM_ALARM_DISABLE;
+	}
+
+	if (mask & RTC_ALARM_TIME_MASK_WEEKDAY) {
+		regs[5] = bin2bcd(timeptr->tm_min) & WEEKDAY_BITS;
+	} else {
+		regs[5] = RV8263C8_BM_ALARM_DISABLE;
+	}
+
+	err = i2c_write_dt(&config->i2c_bus, regs, sizeof(regs));
+	if (err < 0) {
+		LOG_ERR("Error while setting alarm time! Error: %i", err);
+		return err;
+	}
+
+	if (mask != 0) {
+		/* Enable the alarm interrupt */
+		err = i2c_reg_update_byte_dt(&config->i2c_bus, RV8263C8_REGISTER_CONTROL_2,
+					     RV8263C8_BM_ALARM_INT_ENABLE,
+					     RV8263C8_BM_ALARM_INT_ENABLE);
+	}
+
+	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;
+	}
+
+	/* Check if the highest bit is not set. If so the alarm is enabled. */
+	if ((value[0] & RV8263C8_BM_ALARM_DISABLE) == 0) {
+		timeptr->tm_sec = bcd2bin(value[0]) & SECONDS_BITS;
+		(*p_mask) |= RTC_ALARM_TIME_MASK_SECOND;
+	}
+
+	if ((value[1] & RV8263C8_BM_ALARM_DISABLE) == 0) {
+		timeptr->tm_min = bcd2bin(value[1]) & MINUTES_BITS;
+		(*p_mask) |= RTC_ALARM_TIME_MASK_MINUTE;
+	}
+
+	if ((value[2] & RV8263C8_BM_ALARM_DISABLE) == 0) {
+		timeptr->tm_hour = bcd2bin(value[2]) & HOURS_BITS;
+		(*p_mask) |= RTC_ALARM_TIME_MASK_HOUR;
+	}
+
+	if ((value[3] & RV8263C8_BM_ALARM_DISABLE) == 0) {
+		timeptr->tm_mday = bcd2bin(value[3]) & DATE_BITS;
+		(*p_mask) |= RTC_ALARM_TIME_MASK_MONTHDAY;
+	}
+
+	if ((value[4] & RV8263C8_BM_ALARM_DISABLE) == 0) {
+		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)
+{
+	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
+
+	return 0;
+}
+
+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, &reg);
+	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(DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios),                            \
+			   (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0})))};         \
+	DEVICE_DT_INST_DEFINE(inst, &rv8263c8_init, NULL, &rv8263c8_data_##inst,                   \
+			      &rv8263c8_config_##inst, POST_KERNEL, CONFIG_RTC_INIT_PRIORITY,      \
+			      &rv8263c8_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(RV8263_DEFINE)
\ No newline at end of file
diff --git a/drivers/rtc/rtc_sam.c b/drivers/rtc/rtc_sam.c
index 059a2b1b3..67b911cee 100644
--- a/drivers/rtc/rtc_sam.c
+++ b/drivers/rtc/rtc_sam.c
@@ -26,6 +26,11 @@
 #define RTC_SAM_CALIBRATE_PPB_QUANTA (1500)
 #define RTC_SAM_CALIBRATE_PPB_LOW_SCALE (30500)
 
+#define RTC_SAM_TIME_MASK                                                                          \
+	(RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR |      \
+	 RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_MONTHDAY | RTC_ALARM_TIME_MASK_YEAR |     \
+	 RTC_ALARM_TIME_MASK_WEEKDAY)
+
 typedef void (*rtc_sam_irq_init_fn_ptr)(void);
 
 struct rtc_sam_config {
@@ -58,41 +63,6 @@ static void rtc_sam_enable_wp(void)
 	REG_RTC_WPMR = RTC_SAM_WPMR_ENABLE;
 }
 
-static bool rtc_sam_validate_tm(const struct rtc_time *timeptr, uint32_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;
-	}
-
-	return true;
-}
-
 static uint32_t rtc_sam_timr_from_tm(const struct rtc_time *timeptr)
 {
 	uint32_t timr;
@@ -126,7 +96,7 @@ static int rtc_sam_set_time(const struct device *dev, const struct rtc_time *tim
 	const struct rtc_sam_config *config = dev->config;
 	Rtc *regs = config->regs;
 
-	if (rtc_sam_validate_tm(timeptr, UINT32_MAX) == false) {
+	if (rtc_utils_validate_rtc_time(timeptr, RTC_SAM_TIME_MASK) == false) {
 		return -EINVAL;
 	}
 
@@ -645,7 +615,7 @@ static int rtc_sam_get_calibration(const struct device *dev, int32_t *calibratio
 }
 #endif /* CONFIG_RTC_CALIBRATION */
 
-static struct rtc_driver_api rtc_sam_driver_api = {
+static const struct rtc_driver_api rtc_sam_driver_api = {
 	.set_time = rtc_sam_set_time,
 	.get_time = rtc_sam_get_time,
 #ifdef CONFIG_RTC_ALARM
@@ -709,4 +679,4 @@ static int rtc_sam_init(const struct device *dev)
 			      CONFIG_RTC_INIT_PRIORITY,			\
 			      &rtc_sam_driver_api);
 
-DT_INST_FOREACH_STATUS_OKAY(RTC_SAM_DEVICE);
+DT_INST_FOREACH_STATUS_OKAY(RTC_SAM_DEVICE);
\ No newline at end of file
diff --git a/drivers/rtc/rtc_utils.c b/drivers/rtc/rtc_utils.c
new file mode 100644
index 000000000..20e0e8bb7
--- /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 <stdbool.h>
+#include <stdint.h>
+
+#include <zephyr/drivers/rtc.h>
+
+#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 000000000..1b0953e6c
--- /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 <stdbool.h>
+#include <stdint.h>
+
+#include <zephyr/drivers/rtc.h>
+
+/**
+ * @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/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml
new file mode 100644
index 000000000..6c9205f48
--- /dev/null
+++ b/dts/bindings/rtc/microcrystal,rv-8263-c8.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright (c) 2024 Daniel Kampert
+# Author: Daniel Kampert <DanielKampert@Kampis-Elektroecke.de>
+
+description: Micro Crystal RV-8263-C8 RTC
+
+compatible: "microcrystal,rv-8263-c8"
+
+include:
+  - name: rtc-device.yaml
+  - name: i2c-device.yaml
+
+properties:
+  int-gpios:
+    type: phandle-array
+
+  clkout:
+    type: int
+    default: 32768
+    enum:
+      - 32768
+      - 16384
+      - 8192
+      - 4096
+      - 1024
+      - 1
+      - 0
+    description:
+      Enable CLKOUT at given frequency. When disabled, CLKOUT pin is LOW. Set to 0 to
+      disable the CLKOUT signal.
