drivers: sensor: Add driver for MAX32664C

- Add DTS for MAX32664C
- Add driver for MAX32664C
- Add example for MAX32664C Heart rate measurement with Bluetooth
- Add private attributes and channels for health measurement

Closes: #93473

Signed-off-by: Daniel Kampert <DanielKampert@kampis-elektroecke.de>
diff --git a/drivers/sensor/adi/CMakeLists.txt b/drivers/sensor/adi/CMakeLists.txt
index f0bb591..e524f38 100644
--- a/drivers/sensor/adi/CMakeLists.txt
+++ b/drivers/sensor/adi/CMakeLists.txt
@@ -10,4 +10,5 @@
 add_subdirectory_ifdef(CONFIG_ADXL362 adxl362)
 add_subdirectory_ifdef(CONFIG_ADXL367 adxl367)
 add_subdirectory_ifdef(CONFIG_ADXL372 adxl372)
+add_subdirectory_ifdef(CONFIG_SENSOR_MAX32664C max32664c)
 # zephyr-keep-sorted-stop
diff --git a/drivers/sensor/adi/Kconfig b/drivers/sensor/adi/Kconfig
index dea030c..d0812de 100644
--- a/drivers/sensor/adi/Kconfig
+++ b/drivers/sensor/adi/Kconfig
@@ -10,4 +10,5 @@
 source "drivers/sensor/adi/adxl362/Kconfig"
 source "drivers/sensor/adi/adxl367/Kconfig"
 source "drivers/sensor/adi/adxl372/Kconfig"
+source "drivers/sensor/adi/max32664c/Kconfig"
 # zephyr-keep-sorted-stop
diff --git a/drivers/sensor/adi/max32664c/CMakeLists.txt b/drivers/sensor/adi/max32664c/CMakeLists.txt
new file mode 100644
index 0000000..b36663c
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/CMakeLists.txt
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: Apache-2.0
+
+zephyr_library()
+
+zephyr_sources_ifdef(CONFIG_MAX32664C_USE_FIRMWARE_LOADER max32664c_bl.c)
+
+zephyr_sources_ifdef(CONFIG_MAX32664C_USE_INTERRUPT max32664c_interrupt.c)
+
+zephyr_sources_ifdef(CONFIG_SENSOR_MAX32664C max32664c.c
+                                             max32664c_worker.c
+                                             max32664c_init.c
+                                             max32664c_acc.c
+                                             )
diff --git a/drivers/sensor/adi/max32664c/Kconfig b/drivers/sensor/adi/max32664c/Kconfig
new file mode 100644
index 0000000..5b939fa
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/Kconfig
@@ -0,0 +1,45 @@
+# Copyright(c) 2025, Daniel Kampert
+# SPDX-License-Identifier: Apache-2.0
+
+config SENSOR_MAX32664C
+	bool "MAX32664C Driver"
+	default y
+	depends on DT_HAS_MAXIM_MAX32664C_ENABLED
+	select I2C
+	help
+	  Enable the driver for the MAX32664C biometric sensor hub.
+
+if SENSOR_MAX32664C
+config MAX32664C_USE_FIRMWARE_LOADER
+	bool "Use this option if you want to flash the sensor hub over the I2C firmware loader"
+
+config MAX32664C_USE_EXTERNAL_ACC
+	bool "Use this option if you want to use an external accelerometer"
+
+config MAX32664C_USE_EXTENDED_REPORTS
+	bool "Use this option if you want to use extended reports instead of the default reports"
+
+config MAX32664C_USE_STATIC_MEMORY
+	bool "Disable this option if the driver should use dynamic memory"
+	default y
+
+config MAX32664C_QUEUE_SIZE
+	int "Length of the message queue"
+	default 32
+
+config MAX32664C_SAMPLE_BUFFER_SIZE
+	depends on MAX32664C_USE_STATIC_MEMORY
+	int "Length of the sample buffer for the I2C reading thread"
+	default 64
+	help
+	  This is the number of samples that will be read from the sensor hub in one go.
+	  The maximum value is 64, but you can set it lower if you want to reduce memory usage.
+
+config MAX32664C_THREAD_STACK_SIZE
+	int "MAX32664C sample thread stack size"
+	default 4096
+
+config MAX32664C_USE_INTERRUPT
+	bool "Use this option if you want to use the MFIO interrupt support"
+	depends on GPIO
+endif
diff --git a/drivers/sensor/adi/max32664c/max32664c.c b/drivers/sensor/adi/max32664c/max32664c.c
new file mode 100644
index 0000000..99d89cc
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c.c
@@ -0,0 +1,1101 @@
+/*
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/sys/byteorder.h>
+
+#include "max32664c.h"
+
+#define DT_DRV_COMPAT maxim_max32664c
+
+#if (DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0)
+#warning "max32664c driver enabled without any devices"
+#endif
+
+LOG_MODULE_REGISTER(maxim_max32664c, CONFIG_SENSOR_LOG_LEVEL);
+
+int max32664c_i2c_transmit(const struct device *dev, uint8_t *tx_buf, uint8_t tx_len,
+			   uint8_t *rx_buf, uint32_t rx_len, uint16_t delay_ms)
+{
+	const struct max32664c_config *config = dev->config;
+
+	/* Wake up the sensor hub before the transmission starts (min. 300 us) */
+	gpio_pin_set_dt(&config->mfio_gpio, false);
+	k_usleep(500);
+
+	if (i2c_write_dt(&config->i2c, tx_buf, tx_len)) {
+		LOG_ERR("I2C transmission error!");
+		return -EBUSY;
+	}
+
+	k_msleep(delay_ms);
+
+	if (i2c_read_dt(&config->i2c, rx_buf, rx_len)) {
+		LOG_ERR("I2C read error!");
+		return -EBUSY;
+	}
+
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY);
+
+	/* The sensor hub can enter sleep mode again now */
+	gpio_pin_set_dt(&config->mfio_gpio, true);
+	k_usleep(300);
+
+	/* Check the status byte for a valid transaction */
+	if (rx_buf[0] != 0) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/** @brief      Check the accelerometer and AFE WHOAMI registers.
+ *              This function is called during device initialization.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_check_sensors(const struct device *dev)
+{
+	uint8_t afe_id;
+	uint8_t tx[3];
+	uint8_t rx[2];
+	struct max32664c_data *data = dev->data;
+	const struct max32664c_config *config = dev->config;
+
+	LOG_DBG("Checking sensors...");
+
+	/* Read MAX86141 WHOAMI */
+	tx[0] = 0x41;
+	tx[1] = 0x00;
+	tx[2] = 0xFF;
+	if (max32664c_i2c_transmit(dev, tx, 3, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	if (config->use_max86141) {
+		LOG_DBG("\tUsing MAX86141 as AFE");
+		afe_id = 0x25;
+	} else if (config->use_max86161) {
+		LOG_DBG("\tUsing MAX86161 as AFE");
+		afe_id = 0x36;
+	} else {
+		LOG_ERR("\tNo AFE defined!");
+		return -ENODEV;
+	}
+
+	data->afe_id = rx[1];
+	if (data->afe_id != afe_id) {
+		LOG_ERR("\tAFE WHOAMI failed: 0x%X", data->afe_id);
+		return -ENODEV;
+	}
+
+	LOG_DBG("\tAFE WHOAMI OK: 0x%X", data->afe_id);
+
+	/* Read Accelerometer WHOAMI */
+	tx[0] = 0x41;
+	tx[1] = 0x04;
+	tx[2] = 0x0F;
+	if (max32664c_i2c_transmit(dev, tx, 3, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	data->accel_id = rx[1];
+	/* The sensor hub firmware supports only two accelerometers and one is set to */
+	/* EoL. The remaining one is the ST LIS2DS12. */
+	if (data->accel_id != 0x43) {
+		LOG_ERR("\tAccelerometer WHOAMI failed: 0x%X", data->accel_id);
+		return -ENODEV;
+	}
+
+	LOG_DBG("\tAccelerometer WHOAMI OK: 0x%X", data->accel_id);
+
+	return 0;
+}
+
+/** @brief      Stop the current algorithm.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_stop_algo(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[3];
+	struct max32664c_data *data = dev->data;
+
+	if (data->op_mode == MAX32664C_OP_MODE_IDLE) {
+		LOG_DBG("No algorithm running, nothing to stop.");
+		return 0;
+	}
+
+	LOG_DBG("Stop the current algorithm...");
+
+	/* Stop the algorithm */
+	tx[0] = 0x52;
+	tx[1] = 0x07;
+	tx[2] = 0x00;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, 120)) {
+		return -EINVAL;
+	}
+
+	switch (data->op_mode) {
+	case MAX32664C_OP_MODE_RAW: {
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+		k_msgq_cleanup(&data->raw_report_queue);
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+		break;
+	}
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	case MAX32664C_OP_MODE_ALGO_AEC_EXT:
+	case MAX32664C_OP_MODE_ALGO_AGC_EXT: {
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+		k_msgq_cleanup(&data->ext_report_queue);
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+		break;
+	}
+#else
+	case MAX32664C_OP_MODE_ALGO_AEC:
+	case MAX32664C_OP_MODE_ALGO_AGC: {
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+		k_msgq_cleanup(&data->report_queue);
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+		break;
+	}
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+	case MAX32664C_OP_MODE_SCD: {
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+		k_msgq_cleanup(&data->scd_report_queue);
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+		break;
+	}
+	default: {
+		LOG_ERR("Unknown algorithm mode: %d", data->op_mode);
+		return -EINVAL;
+	}
+	};
+
+	data->op_mode = MAX32664C_OP_MODE_IDLE;
+
+	k_thread_suspend(data->thread_id);
+
+	return 0;
+}
+
+/** @brief      Put the device into raw measurement mode.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_set_mode_raw(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[4];
+	struct max32664c_data *data = dev->data;
+
+	/* Stop the current algorithm mode */
+	if (max32664c_stop_algo(dev)) {
+		LOG_ERR("Failed to stop the algorithm!");
+		return -EINVAL;
+	}
+
+	LOG_INF("Entering RAW mode...");
+
+	/* Set the output format to sensor data only */
+	tx[0] = 0x10;
+	tx[1] = 0x00;
+	tx[2] = MAX32664C_OUT_SENSOR_ONLY;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Enable the AFE */
+	tx[0] = 0x44;
+	tx[1] = 0x00;
+	tx[2] = 0x01;
+	tx[3] = 0x00;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, 250)) {
+		return -EINVAL;
+	}
+
+	/* Enable the accelerometer */
+	if (max32664c_acc_enable(dev, true)) {
+		return -EINVAL;
+	}
+
+	/* Set AFE sample rate to 100 Hz */
+	tx[0] = 0x40;
+	tx[1] = 0x00;
+	tx[2] = 0x12;
+	tx[3] = 0x18;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Set the LED current */
+	for (uint8_t i = 0; i < sizeof(data->led_current); i++) {
+		tx[0] = 0x40;
+		tx[1] = 0x00;
+		tx[2] = 0x23 + i;
+		tx[3] = data->led_current[i];
+		LOG_INF("Set LED%d current: %u", i + 1, data->led_current[i]);
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			LOG_ERR("Can not set LED%d current", i + 1);
+			return -EINVAL;
+		}
+	}
+
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	if (k_msgq_alloc_init(&data->raw_report_queue, sizeof(struct max32664c_raw_report_t),
+			      CONFIG_MAX32664C_QUEUE_SIZE)) {
+		LOG_ERR("Failed to allocate RAW report queue!");
+		return -ENOMEM;
+	}
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+	data->op_mode = MAX32664C_OP_MODE_RAW;
+
+	k_thread_resume(data->thread_id);
+
+	return 0;
+}
+
+/** @brief              Put the sensor hub into algorithm mode.
+ *  @param dev          Pointer to device
+ *  @param device_mode  Target device mode
+ *  @param algo_mode    Target algorithm mode
+ *  @param extended     Set to #true when the extended mode should be used
+ *  @return             0 when successful
+ */
+static int max32664c_set_mode_algo(const struct device *dev, enum max32664c_device_mode device_mode,
+				   enum max32664c_algo_mode algo_mode, bool extended)
+{
+	uint8_t rx;
+	uint8_t tx[5];
+	struct max32664c_data *data = dev->data;
+
+	/* Stop the current algorithm mode */
+	if (max32664c_stop_algo(dev)) {
+		LOG_ERR("Failed to stop the algorithm!");
+		return -EINVAL;
+	}
+
+	LOG_DBG("Entering algorithm mode...");
+
+#ifndef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	if (extended) {
+		LOG_ERR("No support for extended reports enabled!");
+		return -EINVAL;
+	}
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+
+	/* Set the output mode to sensor and algorithm data */
+	tx[0] = 0x10;
+	tx[1] = 0x00;
+	tx[2] = MAX32664C_OUT_ALGO_AND_SENSOR;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Set the algorithm mode */
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x0A;
+	tx[3] = algo_mode;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	if (device_mode == MAX32664C_OP_MODE_ALGO_AEC) {
+		LOG_DBG("Entering AEC mode...");
+
+		/* Enable AEC */
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x0B;
+		tx[3] = 0x01;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		/* Enable Auto PD */
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x12;
+		tx[3] = 0x01;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		/* Enable SCD */
+		LOG_DBG("Enabling SCD...");
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x0C;
+		tx[3] = 0x01;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		data->op_mode = MAX32664C_OP_MODE_ALGO_AEC;
+
+		if (extended) {
+			data->op_mode = MAX32664C_OP_MODE_ALGO_AEC_EXT;
+		}
+	} else if (device_mode == MAX32664C_OP_MODE_ALGO_AGC) {
+		LOG_DBG("Entering AGC mode...");
+
+		/* TODO: Test if this works */
+		/* Set the LED current */
+		for (uint8_t i = 0; i < sizeof(data->led_current); i++) {
+			tx[0] = 0x40;
+			tx[1] = 0x00;
+			tx[2] = 0x23 + i;
+			tx[3] = data->led_current[i];
+			LOG_INF("Set LED%d current: %u", i + 1, data->led_current[i]);
+			if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1,
+						   MAX32664C_DEFAULT_CMD_DELAY)) {
+				LOG_ERR("Can not set LED%d current", i + 1);
+				return -EINVAL;
+			}
+		}
+
+		/* Enable AEC */
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x0B;
+		tx[3] = 0x01;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		/* Disable PD auto current calculation */
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x12;
+		tx[3] = 0x00;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		/* Disable SCD */
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x0C;
+		tx[3] = 0x00;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		/* Set AGC target PD current to 10 uA */
+		/* TODO: Add setting of PD current via API or DT? */
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x11;
+		tx[3] = 0x00;
+		tx[4] = 0x64;
+		if (max32664c_i2c_transmit(dev, tx, 5, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+
+		data->op_mode = MAX32664C_OP_MODE_ALGO_AGC;
+
+		if (extended) {
+			data->op_mode = MAX32664C_OP_MODE_ALGO_AGC_EXT;
+		}
+	} else {
+		LOG_ERR("Invalid mode!");
+		return -EINVAL;
+	}
+
+	/* Enable HR and SpO2 algorithm */
+	tx[2] = 0x01;
+	if (extended) {
+		tx[2] = 0x02;
+	}
+
+	tx[0] = 0x52;
+	tx[1] = 0x07;
+
+	/* Use the maximum time to cover all modes (see Table 6 and 12 in the User Guide) */
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, 500)) {
+		return -EINVAL;
+	}
+
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	if (k_msgq_alloc_init(&data->raw_report_queue, sizeof(struct max32664c_raw_report_t),
+			      CONFIG_MAX32664C_QUEUE_SIZE)) {
+		LOG_ERR("Failed to allocate RAW report queue!");
+		return -ENOMEM;
+	}
+
+	if (!extended && k_msgq_alloc_init(&data->report_queue, sizeof(struct max32664c_report_t),
+					   CONFIG_MAX32664C_QUEUE_SIZE)) {
+		LOG_ERR("Failed to allocate report queue!");
+		return -ENOMEM;
+	}
+
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	if (extended &&
+	    k_msgq_alloc_init(&data->ext_report_queue, sizeof(struct max32664c_ext_report_t),
+			      CONFIG_MAX32664C_QUEUE_SIZE)) {
+		LOG_ERR("Failed to allocate extended report queue!");
+		return -ENOMEM;
+	}
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+	k_thread_resume(data->thread_id);
+
+	return 0;
+}
+
+/** @brief      Enable the skin contact detection only mode.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_set_mode_scd(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[4];
+	struct max32664c_data *data = dev->data;
+
+	/* Stop the current algorithm mode */
+	if (max32664c_stop_algo(dev)) {
+		LOG_ERR("Failed to stop the algorithm!");
+		return -EINVAL;
+	}
+
+	LOG_DBG("MAX32664C entering SCD mode...");
+
+	/* Use LED2 for SCD */
+	tx[0] = 0xE5;
+	tx[1] = 0x02;
+	if (max32664c_i2c_transmit(dev, tx, 2, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Set the output mode to algorithm data */
+	tx[0] = 0x10;
+	tx[1] = 0x00;
+	tx[2] = MAX32664C_OUT_ALGORITHM_ONLY;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Enable SCD only algorithm */
+	tx[0] = 0x52;
+	tx[1] = 0x07;
+	tx[2] = 0x03;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, 500)) {
+		return -EINVAL;
+	}
+
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	if (k_msgq_alloc_init(&data->scd_report_queue, sizeof(struct max32664c_scd_report_t),
+			      CONFIG_MAX32664C_QUEUE_SIZE)) {
+		LOG_ERR("Failed to allocate SCD report queue!");
+		return -ENOMEM;
+	}
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+	data->op_mode = MAX32664C_OP_MODE_SCD;
+
+	k_thread_resume(data->thread_id);
+
+	return 0;
+}
+
+static int max32664c_set_mode_wake_on_motion(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[6];
+	struct max32664c_data *data = dev->data;
+
+	LOG_DBG("MAX32664C entering wake on motion mode...");
+
+	/* Stop the current algorithm */
+	tx[0] = 0x52;
+	tx[1] = 0x07;
+	tx[2] = 0x00;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Set the motion detection threshold (see Table 12 in the SpO2 and Heart Rate Using Guide)
+	 */
+	tx[0] = 0x46;
+	tx[1] = 0x04;
+	tx[2] = 0x00;
+	tx[3] = 0x01;
+	tx[4] = MAX32664C_MOTION_TIME(data->motion_time);
+	tx[5] = MAX32664C_MOTION_THRESHOLD(data->motion_threshold);
+	if (max32664c_i2c_transmit(dev, tx, 6, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Set the output mode to sensor data */
+	tx[0] = 0x10;
+	tx[1] = 0x00;
+	tx[2] = MAX32664C_OUT_SENSOR_ONLY;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Enable the accelerometer */
+	if (max32664c_acc_enable(dev, true)) {
+		return -EINVAL;
+	}
+
+	data->op_mode = MAX32664C_OP_MODE_WAKE_ON_MOTION;
+
+	return 0;
+}
+
+static int max32664c_exit_mode_wake_on_motion(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[6];
+	struct max32664c_data *data = dev->data;
+
+	LOG_DBG("MAX32664C exiting wake on motion mode...");
+
+	/* Exit wake on motion mode */
+	tx[0] = 0x46;
+	tx[1] = 0x04;
+	tx[2] = 0x00;
+	tx[3] = 0x00;
+	tx[4] = 0xFF;
+	tx[5] = 0xFF;
+	if (max32664c_i2c_transmit(dev, tx, 6, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	/* Disable the accelerometer */
+	if (max32664c_acc_enable(dev, false)) {
+		return -EINVAL;
+	}
+
+	data->op_mode = MAX32664C_OP_MODE_IDLE;
+
+	return 0;
+}
+
+static int max32664c_disable_sensors(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[4];
+	struct max32664c_data *data = dev->data;
+
+	if (max32664c_stop_algo(dev)) {
+		LOG_ERR("Failed to stop the algorithm!");
+		return -EINVAL;
+	}
+
+	/* Leave wake on motion first because we disable the accelerometer */
+	if (max32664c_exit_mode_wake_on_motion(dev)) {
+		LOG_ERR("Failed to exit wake on motion mode!");
+		return -EINVAL;
+	}
+
+	LOG_DBG("Disable the sensors...");
+
+	/* Disable the AFE */
+	tx[0] = 0x44;
+	tx[1] = 0x00;
+	tx[2] = 0x00;
+	tx[3] = 0x00;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, 250)) {
+		return -EINVAL;
+	}
+
+	/* Disable the accelerometer */
+	if (max32664c_acc_enable(dev, false)) {
+		return -EINVAL;
+	}
+
+	data->op_mode = MAX32664C_OP_MODE_IDLE;
+
+	return 0;
+}
+
+static int max32664c_sample_fetch(const struct device *dev, enum sensor_channel chan)
+{
+	struct max32664c_data *data = dev->data;
+
+	switch (data->op_mode) {
+	case MAX32664C_OP_MODE_STOP_ALGO:
+	case MAX32664C_OP_MODE_IDLE:
+		LOG_DBG("Device is idle, no data to fetch!");
+		return -EAGAIN;
+	case MAX32664C_OP_MODE_SCD:
+		k_msgq_get(&data->scd_report_queue, &data->scd, K_NO_WAIT);
+		return 0;
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	case MAX32664C_OP_MODE_ALGO_AEC_EXT:
+	case MAX32664C_OP_MODE_ALGO_AGC_EXT:
+		k_msgq_get(&data->ext_report_queue, &data->ext, K_NO_WAIT);
+		return 0;
+#else
+	case MAX32664C_OP_MODE_ALGO_AEC:
+	case MAX32664C_OP_MODE_ALGO_AGC:
+		k_msgq_get(&data->report_queue, &data->report, K_NO_WAIT);
+		return 0;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+	/* Raw data are reported with normal and extended algorithms so we need to fetch them too */
+	case MAX32664C_OP_MODE_RAW:
+		k_msgq_get(&data->raw_report_queue, &data->raw, K_NO_WAIT);
+		return 0;
+	default:
+		return -ENOTSUP;
+	}
+}
+
+static int max32664c_channel_get(const struct device *dev, enum sensor_channel chan,
+				 struct sensor_value *val)
+{
+	struct max32664c_data *data = dev->data;
+
+	switch ((int)chan) {
+	case SENSOR_CHAN_ACCEL_X: {
+		val->val1 = data->raw.acc.x;
+		break;
+	}
+	case SENSOR_CHAN_ACCEL_Y: {
+		val->val1 = data->raw.acc.y;
+		break;
+	}
+	case SENSOR_CHAN_ACCEL_Z: {
+		val->val1 = data->raw.acc.z;
+		break;
+	}
+	case SENSOR_CHAN_GREEN: {
+		val->val1 = data->raw.PPG1;
+		break;
+	}
+	case SENSOR_CHAN_IR: {
+		val->val1 = data->raw.PPG2;
+		break;
+	}
+	case SENSOR_CHAN_RED: {
+		val->val1 = data->raw.PPG3;
+		break;
+	}
+	case SENSOR_CHAN_MAX32664C_HEARTRATE: {
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+		val->val1 = data->ext.hr;
+		val->val2 = data->ext.hr_confidence;
+#else
+		val->val1 = data->report.hr;
+		val->val2 = data->report.hr_confidence;
+#endif
+		break;
+	}
+	case SENSOR_CHAN_MAX32664C_RESPIRATION_RATE: {
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+		val->val1 = data->ext.rr;
+		val->val2 = data->ext.rr_confidence;
+#else
+		val->val1 = data->report.rr;
+		val->val2 = data->report.rr_confidence;
+#endif
+		break;
+	}
+	case SENSOR_CHAN_MAX32664C_BLOOD_OXYGEN_SATURATION: {
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+		val->val1 = data->ext.spo2_meas.value;
+		val->val2 = data->ext.spo2_meas.confidence;
+#else
+		val->val1 = data->report.spo2_meas.value;
+		val->val2 = data->report.spo2_meas.confidence;
+#endif
+		break;
+	}
+	case SENSOR_CHAN_MAX32664C_SKIN_CONTACT: {
+		val->val1 = data->report.scd_state;
+		break;
+	}
+	default: {
+		LOG_ERR("Channel %u not supported!", chan);
+		return -ENOTSUP;
+	}
+	}
+
+	return 0;
+}
+
+static int max32664c_attr_set(const struct device *dev, enum sensor_channel chan,
+			      enum sensor_attribute attr, const struct sensor_value *val)
+{
+	int err;
+	uint8_t tx[5];
+	uint8_t rx;
+	struct max32664c_data *data = dev->data;
+
+	err = 0;
+
+	switch ((int)attr) {
+	case SENSOR_ATTR_SAMPLING_FREQUENCY: {
+		break;
+	}
+	case SENSOR_ATTR_MAX32664C_HEIGHT: {
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x06;
+		tx[3] = (val->val1 & 0xFF00) >> 8;
+		tx[4] = val->val1 & 0x00FF;
+		if (max32664c_i2c_transmit(dev, tx, 5, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			LOG_ERR("Can not set height!");
+			return -EINVAL;
+		}
+
+		break;
+	}
+	case SENSOR_ATTR_MAX32664C_WEIGHT: {
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x07;
+		tx[3] = (val->val1 & 0xFF00) >> 8;
+		tx[4] = val->val1 & 0x00FF;
+		if (max32664c_i2c_transmit(dev, tx, 5, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			LOG_ERR("Can not set weight!");
+			return -EINVAL;
+		}
+
+		break;
+	}
+	case SENSOR_ATTR_MAX32664C_AGE: {
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x08;
+		tx[3] = val->val1 & 0x00FF;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			LOG_ERR("Can not set age!");
+			return -EINVAL;
+		}
+
+		break;
+	}
+	case SENSOR_ATTR_MAX32664C_GENDER: {
+		tx[0] = 0x50;
+		tx[1] = 0x07;
+		tx[2] = 0x08;
+		tx[3] = val->val1 & 0x00FF;
+		if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			LOG_ERR("Can not set gender!");
+			return -EINVAL;
+		}
+
+		break;
+	}
+	case SENSOR_ATTR_SLOPE_DUR: {
+		data->motion_time = val->val1;
+		break;
+	}
+	case SENSOR_ATTR_SLOPE_TH: {
+		data->motion_threshold = val->val1;
+		break;
+	}
+	case SENSOR_ATTR_CONFIGURATION: {
+		switch ((int)chan) {
+		case SENSOR_CHAN_GREEN: {
+			data->led_current[0] = val->val1 & 0xFF;
+			break;
+		}
+		case SENSOR_CHAN_IR: {
+			data->led_current[1] = val->val1 & 0xFF;
+			break;
+		}
+		case SENSOR_CHAN_RED: {
+			data->led_current[2] = val->val1 & 0xFF;
+			break;
+		}
+		default: {
+			LOG_ERR("Channel %u not supported for setting attribute!", (int)chan);
+			return -ENOTSUP;
+		}
+		}
+		break;
+	}
+	case SENSOR_ATTR_MAX32664C_OP_MODE: {
+		switch (val->val1) {
+		case MAX32664C_OP_MODE_ALGO_AEC: {
+#ifndef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+			err = max32664c_set_mode_algo(dev, MAX32664C_OP_MODE_ALGO_AEC, val->val2,
+						      false);
+#else
+			return -EINVAL;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+			break;
+		}
+		case MAX32664C_OP_MODE_ALGO_AEC_EXT: {
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+			err = max32664c_set_mode_algo(dev, MAX32664C_OP_MODE_ALGO_AEC, val->val2,
+						      true);
+#else
+			return -EINVAL;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+			break;
+		}
+		case MAX32664C_OP_MODE_ALGO_AGC: {
+#ifndef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+			err = max32664c_set_mode_algo(dev, MAX32664C_OP_MODE_ALGO_AGC, val->val2,
+						      false);
+#else
+			return -EINVAL;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+			break;
+		}
+		case MAX32664C_OP_MODE_ALGO_AGC_EXT: {
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+			err = max32664c_set_mode_algo(dev, MAX32664C_OP_MODE_ALGO_AGC, val->val2,
+						      true);
+#else
+			return -EINVAL;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+			break;
+		}
+		case MAX32664C_OP_MODE_RAW: {
+			err = max32664c_set_mode_raw(dev);
+			break;
+		}
+		case MAX32664C_OP_MODE_SCD: {
+			err = max32664c_set_mode_scd(dev);
+			break;
+		}
+		case MAX32664C_OP_MODE_WAKE_ON_MOTION: {
+			err = max32664c_set_mode_wake_on_motion(dev);
+			break;
+		}
+		case MAX32664C_OP_MODE_EXIT_WAKE_ON_MOTION: {
+			err = max32664c_exit_mode_wake_on_motion(dev);
+			break;
+		}
+		case MAX32664C_OP_MODE_STOP_ALGO: {
+			err = max32664c_stop_algo(dev);
+			break;
+		}
+		case MAX32664C_OP_MODE_IDLE: {
+			err = max32664c_disable_sensors(dev);
+			break;
+		}
+		default: {
+			LOG_ERR("Unsupported sensor operation mode");
+			return -ENOTSUP;
+		}
+		}
+
+		break;
+	}
+	default: {
+		LOG_ERR("Unsupported sensor attribute!");
+		return -ENOTSUP;
+	}
+	}
+
+	return err;
+}
+
+static int max32664c_attr_get(const struct device *dev, enum sensor_channel chan,
+			      enum sensor_attribute attr, struct sensor_value *val)
+{
+	struct max32664c_data *data = dev->data;
+
+	switch ((int)attr) {
+	case SENSOR_ATTR_MAX32664C_OP_MODE: {
+		val->val1 = data->op_mode;
+		val->val2 = 0;
+		break;
+	}
+	case SENSOR_ATTR_CONFIGURATION: {
+		switch ((int)chan) {
+		case SENSOR_CHAN_GREEN: {
+			val->val1 = data->led_current[0];
+			break;
+		}
+		case SENSOR_CHAN_IR: {
+			val->val1 = data->led_current[1];
+			break;
+		}
+		case SENSOR_CHAN_RED: {
+			val->val1 = data->led_current[2];
+			break;
+		}
+		default: {
+			LOG_ERR("Channel %u not supported for getting attribute!", (int)chan);
+			return -ENOTSUP;
+		}
+		}
+		break;
+	}
+	default: {
+		LOG_ERR("Unsupported sensor attribute!");
+		return -ENOTSUP;
+	}
+	}
+
+	return 0;
+}
+
+static DEVICE_API(sensor, max32664c_driver_api) = {
+	.attr_set = max32664c_attr_set,
+	.attr_get = max32664c_attr_get,
+	.sample_fetch = max32664c_sample_fetch,
+	.channel_get = max32664c_channel_get,
+};
+
+static int max32664c_init(const struct device *dev)
+{
+	uint8_t tx[2];
+	uint8_t rx[4];
+	const struct max32664c_config *config = dev->config;
+	struct max32664c_data *data = dev->data;
+
+	if (!i2c_is_ready_dt(&config->i2c)) {
+		LOG_ERR("I2C not ready");
+		return -ENODEV;
+	}
+
+	gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT);
+	gpio_pin_configure_dt(&config->mfio_gpio, GPIO_OUTPUT);
+
+	/* Put the hub into application mode */
+	LOG_DBG("Set app mode");
+	gpio_pin_set_dt(&config->reset_gpio, false);
+	k_msleep(20);
+
+	gpio_pin_set_dt(&config->mfio_gpio, true);
+	k_msleep(20);
+
+	/* Wait for 50 ms (switch into app mode) + 1500 ms (initialization) */
+	/* (see page 17 of the User Guide) */
+	gpio_pin_set_dt(&config->reset_gpio, true);
+	k_msleep(1600);
+
+	/* Read the device mode */
+	tx[0] = 0x02;
+	tx[1] = 0x00;
+	if (max32664c_i2c_transmit(dev, tx, 2, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	data->op_mode = rx[1];
+	LOG_DBG("Mode: %x ", data->op_mode);
+	if (data->op_mode != 0) {
+		return -EINVAL;
+	}
+
+	/* Read the firmware version */
+	tx[0] = 0xFF;
+	tx[1] = 0x03;
+	if (max32664c_i2c_transmit(dev, tx, 2, rx, 4, MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	memcpy(data->hub_ver, &rx[1], 3);
+
+	LOG_DBG("Version: %d.%d.%d", data->hub_ver[0], data->hub_ver[1], data->hub_ver[2]);
+
+	if (max32664c_check_sensors(dev)) {
+		return -EINVAL;
+	}
+
+	if (max32664c_init_hub(dev)) {
+		return -EINVAL;
+	}
+
+#ifdef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	k_msgq_init(&data->raw_report_queue, data->raw_report_queue_buffer,
+		    sizeof(struct max32664c_raw_report_t),
+		    sizeof(data->raw_report_queue_buffer) / sizeof(struct max32664c_raw_report_t));
+
+	k_msgq_init(&data->scd_report_queue, data->scd_report_queue_buffer,
+		    sizeof(struct max32664c_scd_report_t),
+		    sizeof(data->scd_report_queue_buffer) / sizeof(struct max32664c_scd_report_t));
+
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	k_msgq_init(&data->ext_report_queue, data->ext_report_queue_buffer,
+		    sizeof(struct max32664c_ext_report_t),
+		    sizeof(data->ext_report_queue_buffer) / sizeof(struct max32664c_ext_report_t));
+#else
+	k_msgq_init(&data->report_queue, data->report_queue_buffer,
+		    sizeof(struct max32664c_report_t),
+		    sizeof(data->report_queue_buffer) / sizeof(struct max32664c_report_t));
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_DEVICE
+static int max32664c_pm_action(const struct device *dev, enum pm_device_action action)
+{
+	switch (action) {
+	case PM_DEVICE_ACTION_RESUME: {
+		break;
+	}
+	case PM_DEVICE_ACTION_SUSPEND: {
+		const struct max32664c_config *config = dev->config;
+
+		/* Pulling MFIO high will cause the hub to enter sleep mode */
+		gpio_pin_set_dt(&config->mfio_gpio, true);
+		k_msleep(20);
+		break;
+	}
+	case PM_DEVICE_ACTION_TURN_OFF: {
+		uint8_t rx;
+		uint8_t tx[3];
+
+		/* Send a shut down command */
+		/* NOTE: Toggling RSTN is needed to wake the device */
+		tx[0] = 0x01;
+		tx[1] = 0x00;
+		tx[2] = 0x01;
+		if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+			return -EINVAL;
+		}
+		break;
+	}
+	case PM_DEVICE_ACTION_TURN_ON: {
+		/* Toggling RSTN is needed to turn the device on */
+		max32664c_init(dev);
+		break;
+	}
+	default: {
+		return -ENOTSUP;
+	}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_PM_DEVICE */
+
+#define MAX32664C_INIT(inst)                                                                       \
+	static struct max32664c_data max32664c_data_##inst;                                        \
+                                                                                                   \
+	static const struct max32664c_config max32664c_config_##inst = {                           \
+		.i2c = I2C_DT_SPEC_INST_GET(inst),                                                 \
+		.reset_gpio = GPIO_DT_SPEC_INST_GET(inst, reset_gpios),                            \
+		.mfio_gpio = GPIO_DT_SPEC_INST_GET(inst, mfio_gpios),                              \
+		.spo2_calib = DT_INST_PROP(inst, spo2_calib),                                      \
+		.hr_config = DT_INST_PROP(inst, hr_config),                                        \
+		.spo2_config = DT_INST_PROP(inst, spo2_config),                                    \
+		.use_max86141 = DT_INST_PROP(inst, use_max86141),                                  \
+		.use_max86161 = DT_INST_PROP(inst, use_max86161),                                  \
+		.motion_time = DT_INST_PROP(inst, motion_time),                                    \
+		.motion_threshold = DT_INST_PROP(inst, motion_threshold),                          \
+		.min_integration_time_idx = DT_INST_ENUM_IDX(inst, min_integration_time),          \
+		.min_sampling_rate_idx = DT_INST_ENUM_IDX(inst, min_sampling_rate),                \
+		.max_integration_time_idx = DT_INST_ENUM_IDX(inst, max_integration_time),          \
+		.max_sampling_rate_idx = DT_INST_ENUM_IDX(inst, max_sampling_rate),                \
+		.report_period = DT_INST_PROP(inst, report_period),                                \
+		.led_current = DT_INST_PROP(inst, led_current),                                    \
+	};                                                                                         \
+                                                                                                   \
+	PM_DEVICE_DT_INST_DEFINE(inst, max32664c_pm_action);                                       \
+                                                                                                   \
+	SENSOR_DEVICE_DT_INST_DEFINE(inst, max32664c_init, PM_DEVICE_DT_INST_GET(inst),            \
+				     &max32664c_data_##inst, &max32664c_config_##inst,             \
+				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,                     \
+				     &max32664c_driver_api)
+
+DT_INST_FOREACH_STATUS_OKAY(MAX32664C_INIT)
diff --git a/drivers/sensor/adi/max32664c/max32664c.h b/drivers/sensor/adi/max32664c/max32664c.h
new file mode 100644
index 0000000..facca74
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/pm/device.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/drivers/i2c.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/logging/log.h>
+
+#include <zephyr/drivers/sensor/max32664c.h>
+
+#define MAX32664C_BIT_STATUS_NO_ERR   1
+#define MAX32664C_BIT_STATUS_DATA_RDY 3
+#define MAX32664C_BIT_STATUS_OUT_OVFL 4
+#define MAX32664C_BIT_STATUS_IN_OVFL  5
+#define MAX32664C_BIT_STATUS_BUSY     6
+
+#define MAX32664C_DEFAULT_CMD_DELAY 10
+
+/** @brief Output formats of the sensor hub.
+ */
+enum max32664c_output_format {
+	MAX32664C_OUT_PAUSE,
+	MAX32664C_OUT_SENSOR_ONLY,
+	MAX32664C_OUT_ALGORITHM_ONLY,
+	MAX32664C_OUT_ALGO_AND_SENSOR,
+};
+
+/** @brief Skin contact detection states.
+ *  @note The SCD states are only available when the SCD only mode is enabled.
+ */
+enum max32664c_scd_states {
+	MAX32664C_SCD_STATE_UNKNOWN,
+	MAX32664C_SCD_STATE_OFF_SKIN,
+	MAX32664C_SCD_STATE_ON_OBJECT,
+	MAX32664C_SCD_STATE_ON_SKIN,
+};
+
+/** @brief LED current structure.
+ */
+struct max32664c_led_current_t {
+	uint8_t adj_flag;
+	uint16_t adj_val;
+} __packed;
+
+/** @brief SpO2 measurement result structure.
+ */
+struct max32664c_spo2_meas_t {
+	uint8_t confidence;
+	uint16_t value;
+	uint8_t complete;
+	uint8_t low_signal_quality;
+	uint8_t motion;
+	uint8_t low_pi;
+	uint8_t unreliable_r;
+	uint8_t state;
+} __packed;
+
+/** @brief Extended SpO2 measurement result structure.
+ */
+struct max32664c_ext_spo2_meas_t {
+	uint8_t confidence;
+	uint16_t value;
+	uint8_t valid_percent;
+	uint8_t low_signal_flag;
+	uint8_t motion_flag;
+	uint8_t low_pi_flag;
+	uint8_t unreliable_r_flag;
+	uint8_t state;
+} __packed;
+
+/** @brief Raw data structure, reported by the sensor hub.
+ */
+struct max32664c_raw_report_t {
+	uint32_t PPG1: 24;
+	uint32_t PPG2: 24;
+	uint32_t PPG3: 24;
+	uint32_t PPG4: 24;
+	uint32_t PPG5: 24;
+	uint32_t PPG6: 24;
+	struct max32664c_acc_data_t acc;
+} __packed;
+
+/** @brief SCD only data structure, reported by the sensor hub.
+ */
+struct max32664c_scd_report_t {
+	uint8_t scd_classifier;
+} __packed;
+
+/** @brief Algorithm data structure, reported by the sensor hub.
+ */
+struct max32664c_report_t {
+	uint8_t op_mode;
+	uint16_t hr;
+	uint8_t hr_confidence;
+	uint16_t rr;
+	uint8_t rr_confidence;
+	uint8_t activity_class;
+	uint16_t r;
+	struct max32664c_spo2_meas_t spo2_meas;
+	uint8_t scd_state;
+} __packed;
+
+/** @brief Extended algorithm data structure, reported by the sensor hub.
+ */
+struct max32664c_ext_report_t {
+	uint8_t op_mode;
+	uint16_t hr;
+	uint8_t hr_confidence;
+	uint16_t rr;
+	uint8_t rr_confidence;
+	uint8_t activity_class;
+
+	uint32_t total_walk_steps;
+	uint32_t total_run_steps;
+	uint32_t total_energy_kcal;
+	uint32_t total_amr_kcal;
+
+	struct max32664c_led_current_t led_current_adj1;
+	struct max32664c_led_current_t led_current_adj2;
+	struct max32664c_led_current_t led_current_adj3;
+
+	uint8_t integration_time_adj_flag;
+	uint8_t requested_integration_time;
+
+	uint8_t sampling_rate_adj_flag;
+	uint8_t requested_sampling_rate;
+	uint8_t requested_sampling_average;
+
+	uint8_t hrm_afe_ctrl_state;
+	uint8_t is_high_motion_for_hrm;
+
+	uint8_t scd_state;
+
+	uint16_t r_value;
+	struct max32664c_ext_spo2_meas_t spo2_meas;
+
+	uint8_t ibi_offset;
+	uint8_t unreliable_orientation_flag;
+
+	uint8_t reserved[2];
+} __packed;
+
+/** @brief Device configuration structure.
+ */
+struct max32664c_config {
+	struct i2c_dt_spec i2c;
+	struct gpio_dt_spec reset_gpio;
+
+#ifdef CONFIG_MAX32664C_USE_INTERRUPT
+	const struct device *dev;
+	struct gpio_callback gpio_cb;
+	struct k_work interrupt_work;
+#endif /* CONFIG_MAX32664C_USE_INTERRUPT */
+
+	struct gpio_dt_spec mfio_gpio;
+
+	int32_t spo2_calib[3];
+	uint16_t motion_time;
+	uint16_t motion_threshold;
+
+	uint8_t hr_config[2];
+	uint8_t spo2_config[2];
+	uint8_t led_current[3];           /**< Initial LED current in mA */
+	uint8_t min_integration_time_idx;
+	uint8_t min_sampling_rate_idx;
+	uint8_t max_integration_time_idx;
+	uint8_t max_sampling_rate_idx;
+	uint8_t report_period;            /*< Samples report period */
+
+	bool use_max86141;
+	bool use_max86161;
+};
+
+/** @brief Device runtime data structure.
+ */
+struct max32664c_data {
+	struct max32664c_raw_report_t raw;
+	struct max32664c_scd_report_t scd;
+	struct max32664c_report_t report;
+	struct max32664c_ext_report_t ext;
+
+	enum max32664c_device_mode op_mode; /**< Current device mode */
+
+	uint8_t motion_time;              /**< Motion time in milliseconds */
+	uint8_t motion_threshold;         /**< Motion threshold in milli-g */
+	uint8_t led_current[3];           /**< LED current in mA */
+	uint8_t min_integration_time_idx;
+	uint8_t min_sampling_rate_idx;
+	uint8_t max_integration_time_idx;
+	uint8_t max_sampling_rate_idx;
+	uint8_t report_period;            /*< Samples report period */
+	uint8_t afe_id;
+	uint8_t accel_id;
+	uint8_t hub_ver[3];
+
+	/* Internal */
+	struct k_thread thread;
+	k_tid_t thread_id;
+	bool is_thread_running;
+
+#ifdef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	/** @brief This buffer is used to read all available messages from the sensor hub plus the
+	 * status byte. The buffer size is defined by the CONFIG_MAX32664C_SAMPLE_BUFFER_SIZE
+	 * Kconfig and the largest possible message. The buffer must contain enough space to store
+	 * all available messages at every time because it is not possible to read a single message
+	 * from the sensor hub.
+	 */
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	uint8_t max32664_i2c_buffer[(CONFIG_MAX32664C_SAMPLE_BUFFER_SIZE *
+				     (sizeof(struct max32664c_raw_report_t) +
+				      sizeof(struct max32664c_ext_report_t))) +
+				    1];
+#else
+	uint8_t max32664_i2c_buffer[(CONFIG_MAX32664C_SAMPLE_BUFFER_SIZE *
+				     (sizeof(struct max32664c_raw_report_t) +
+				      sizeof(struct max32664c_report_t))) +
+				    1];
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+#else
+	uint8_t *max32664_i2c_buffer;
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+	K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_MAX32664C_THREAD_STACK_SIZE);
+
+	struct k_msgq raw_report_queue;
+	struct k_msgq scd_report_queue;
+
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	struct k_msgq ext_report_queue;
+#else
+	struct k_msgq report_queue;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+
+#ifdef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	uint8_t raw_report_queue_buffer[CONFIG_MAX32664C_QUEUE_SIZE *
+					sizeof(struct max32664c_raw_report_t)];
+	uint8_t scd_report_queue_buffer[CONFIG_MAX32664C_QUEUE_SIZE *
+					sizeof(struct max32664c_scd_report_t)];
+
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	uint8_t ext_report_queue_buffer[CONFIG_MAX32664C_QUEUE_SIZE *
+					(sizeof(struct max32664c_raw_report_t) +
+					 sizeof(struct max32664c_ext_report_t))];
+#else
+	uint8_t report_queue_buffer[CONFIG_MAX32664C_QUEUE_SIZE *
+				    (sizeof(struct max32664c_raw_report_t) +
+				     sizeof(struct max32664c_report_t))];
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY*/
+};
+
+/** @brief          Enable / Disable the accelerometer.
+ *                  NOTE: This code is untested and may not work as expected.
+ *  @param dev      Pointer to device
+ *  @param enable   Enable / Disable
+ *  @return         0 when successful
+ */
+int max32664c_acc_enable(const struct device *dev, bool enable);
+
+/** @brief      Background worker for reading the sensor hub.
+ *  @param dev  Pointer to device
+ */
+void max32664c_worker(const struct device *dev);
+
+/** @brief          Read / write data from / to the sensor hub.
+ *  @param dev      Pointer to device
+ *  @param tx_buf   Pointer to transmit buffer
+ *  @param tx_len   Length of transmit buffer
+ *  @param rx_buf   Pointer to receive buffer
+ *                  NOTE: The buffer must be large enough to store the response and the status byte!
+ *  @param rx_len   Length of the receive buffer
+ *  @param delay    Command delay (milliseconds)
+ *  @return         0 when successful
+ */
+int max32664c_i2c_transmit(const struct device *dev, uint8_t *tx_buf, uint8_t tx_len,
+			   uint8_t *rx_buf, uint32_t rx_len, uint16_t delay);
+
+/** @brief      Run a basic initialization on the sensor hub.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+int max32664c_init_hub(const struct device *dev);
+
+#if CONFIG_MAX32664C_USE_INTERRUPT
+/** @brief      Initialize the interrupt support for the sensor hub.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+int max32664c_init_interrupt(const struct device *dev);
+#endif /* CONFIG_MAX32664C_USE_INTERRUPT */
diff --git a/drivers/sensor/adi/max32664c/max32664c_acc.c b/drivers/sensor/adi/max32664c/max32664c_acc.c
new file mode 100644
index 0000000..1b8dfc1
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c_acc.c
@@ -0,0 +1,58 @@
+/*
+ * External accelerometer driver for the MAX32664C biometric sensor hub.
+ *
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "max32664c.h"
+
+LOG_MODULE_REGISTER(maxim_max32664c_acc, CONFIG_SENSOR_LOG_LEVEL);
+
+int max32664c_acc_enable(const struct device *dev, bool enable)
+{
+	uint8_t tx[4];
+	uint8_t rx;
+
+	tx[0] = 0x44;
+	tx[1] = 0x04;
+	tx[2] = enable;
+
+#if CONFIG_MAX32664C_USE_EXTERNAL_ACC
+	tx[3] = 1;
+#else
+	tx[3] = 0;
+#endif /* CONFIG_MAX32664C_USE_EXTERNAL_ACC */
+
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, 20)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_MAX32664C_USE_EXTERNAL_ACC
+int max32664c_acc_fill_fifo(const struct device *dev, struct max32664c_acc_data_t *data,
+			    uint8_t length)
+{
+	uint8_t tx[2 + 16 * sizeof(struct max32664c_acc_data_t)];
+	uint8_t rx;
+
+	if (length > 16) {
+		LOG_ERR("Length exceeds maximum of 16 samples!");
+		return -EINVAL;
+	}
+
+	tx[0] = 0x14;
+	tx[1] = 0x00;
+	memcpy(&tx[2], data, length * sizeof(struct max32664c_acc_data_t));
+
+	if (max32664c_i2c_transmit(dev, tx, 2 + (length * sizeof(struct max32664c_acc_data_t)), &rx,
+				   1, 20)) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_MAX32664C_USE_EXTERNAL_ACC */
diff --git a/drivers/sensor/adi/max32664c/max32664c_bl.c b/drivers/sensor/adi/max32664c/max32664c_bl.c
new file mode 100644
index 0000000..147c9ae
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c_bl.c
@@ -0,0 +1,383 @@
+/*
+ * I2C firmware loader for the MAX32664C biometric sensor hub.
+ *
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/kernel.h>
+#include <zephyr/drivers/i2c.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/sys/byteorder.h>
+#include <zephyr/logging/log.h>
+
+#include "max32664c.h"
+
+#define MAX32664C_FW_PAGE_SIZE         8192
+#define MAX32664C_FW_UPDATE_CRC_SIZE   16
+#define MAX32664C_FW_UPDATE_WRITE_SIZE (MAX32664C_FW_PAGE_SIZE + MAX32664C_FW_UPDATE_CRC_SIZE)
+#define MAX32664C_DEFAULT_CMD_DELAY_MS 10
+#define MAX32664C_PAGE_WRITE_DELAY_MS  680
+
+static uint8_t max32664c_fw_init_vector[11];
+static uint8_t max32664c_fw_auth_vector[16];
+
+LOG_MODULE_REGISTER(max32664_loader, CONFIG_SENSOR_LOG_LEVEL);
+
+/** @brief          Read / write bootloader data from / to the sensor hub.
+ *  @param dev      Pointer to device
+ *  @param tx_buf   Pointer to transmit buffer
+ *  @param tx_len   Length of transmit buffer
+ *  @param rx_buf   Pointer to receive buffer
+ *                  NOTE: The buffer must be large enough to store the response and the status byte!
+ *  @param rx_len   Length of the receive buffer
+ *  @return         0 when successful
+ */
+static int max32664c_bl_i2c_transmit(const struct device *dev, uint8_t *tx_buf, uint8_t tx_len,
+				     uint8_t *rx_buf, uint8_t rx_len)
+{
+	int err;
+	const struct max32664c_config *config = dev->config;
+
+	err = i2c_write_dt(&config->i2c, tx_buf, tx_len);
+	if (err) {
+		LOG_ERR("I2C transmission error %d!", err);
+		return err;
+	}
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY_MS);
+	err = i2c_read_dt(&config->i2c, rx_buf, rx_len);
+	if (err) {
+		LOG_ERR("I2C transmission error %d!", err);
+		return err;
+	}
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY_MS);
+
+	/* Check the status byte for a valid transaction */
+	LOG_DBG("Status: %u", rx_buf[0]);
+	if (rx_buf[0] != 0) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/** @brief          Read application data from the sensor hub.
+ *  @param dev      Pointer to device
+ *  @param family   Family byte
+ *  @param index    Index byte
+ *  @param rx_buf   Pointer to receive buffer
+ *                  NOTE: The buffer must be large enough to store the response and the status byte!
+ *  @param rx_len   Length of receive buffer
+ *  @return         0 when successful
+ */
+static int max32664c_app_i2c_read(const struct device *dev, uint8_t family, uint8_t index,
+				  uint8_t *rx_buf, uint8_t rx_len)
+{
+	uint8_t tx_buf[] = {family, index};
+	const struct max32664c_config *config = dev->config;
+
+	/* Wake the sensor hub before starting an I2C read (see page 17 of the user Guide) */
+	gpio_pin_set_dt(&config->mfio_gpio, false);
+	k_usleep(300);
+
+	i2c_write_dt(&config->i2c, tx_buf, sizeof(tx_buf));
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY_MS);
+	i2c_read_dt(&config->i2c, rx_buf, rx_len);
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY_MS);
+
+	gpio_pin_set_dt(&config->mfio_gpio, true);
+
+	/* Check the status byte for a valid transaction */
+	if (rx_buf[0] != 0) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/** @brief          Write a page of data into the sensor hub.
+ *  @param dev      Pointer to device
+ *  @param data     Pointer to firmware data
+ *  @param offset   Start address in the firmware data
+ *  @return         0 when successful
+ */
+static int max32664c_bl_write_page(const struct device *dev, const uint8_t *data, uint32_t offset)
+{
+	int err;
+	uint8_t rx_buf;
+	uint8_t *tx_buf;
+	const struct max32664c_config *config = dev->config;
+
+	/* Alloc memory for one page plus two command bytes */
+	tx_buf = (uint8_t *)k_malloc(MAX32664C_FW_UPDATE_WRITE_SIZE + 2);
+	if (tx_buf == NULL) {
+		return -ENOMEM;
+	}
+
+	/* Copy the data for one page into the buffer but leave space for the two command bytes */
+	memcpy(&tx_buf[2], &data[offset], MAX32664C_FW_UPDATE_WRITE_SIZE);
+
+	/* Set the two command bytes */
+	tx_buf[0] = 0x80;
+	tx_buf[1] = 0x04;
+
+	if (i2c_write_dt(&config->i2c, tx_buf, MAX32664C_FW_UPDATE_WRITE_SIZE + 2)) {
+		err = -EINVAL;
+		goto max32664c_bl_write_page_exit;
+	};
+	k_msleep(MAX32664C_PAGE_WRITE_DELAY_MS);
+	err = i2c_read_dt(&config->i2c, &rx_buf, 1);
+	if (err) {
+		LOG_ERR("I2C read error %d!", err);
+		err = -EINVAL;
+		goto max32664c_bl_write_page_exit;
+	};
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY_MS);
+
+	err = rx_buf;
+
+	LOG_DBG("Write page status: %u", err);
+
+max32664c_bl_write_page_exit:
+	k_free(tx_buf);
+	return err;
+}
+
+/** @brief      Erase the application from the sensor hub.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_bl_erase_app(const struct device *dev)
+{
+	uint8_t tx_buf[2] = {0x80, 0x03};
+	uint8_t rx_buf;
+	const struct max32664c_config *config = dev->config;
+
+	if (i2c_write_dt(&config->i2c, tx_buf, sizeof(tx_buf))) {
+		return -EINVAL;
+	};
+
+	k_msleep(1500);
+
+	if (i2c_read_dt(&config->i2c, &rx_buf, sizeof(rx_buf))) {
+		return -EINVAL;
+	};
+
+	k_msleep(MAX32664C_DEFAULT_CMD_DELAY_MS);
+
+	/* Check the status byte for a valid transaction */
+	if (rx_buf != 0) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/** @brief          Load the firmware into the hub.
+ *                  NOTE: See User Guide, Table 9 for the required steps.
+ *  @param dev      Pointer to device
+ *  @param firmware Pointer to firmware data
+ *  @param size     Firmware size
+ *  @return         0 when successful
+ */
+static int max32664c_bl_load_fw(const struct device *dev, const uint8_t *firmware, uint32_t size)
+{
+	uint8_t rx_buf;
+	uint8_t tx_buf[18] = {0};
+	uint32_t page_offset;
+
+	/* Get the number of pages from the firmware file (see User Guide page 53) */
+	uint8_t num_pages = firmware[0x44];
+
+	LOG_INF("Loading firmware...");
+	LOG_INF("\tSize: %u", size);
+	LOG_INF("\tPages: %u", num_pages);
+
+	/* Set the number of pages */
+	tx_buf[0] = 0x80;
+	tx_buf[1] = 0x02;
+	tx_buf[2] = 0x00;
+	tx_buf[3] = num_pages;
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 4, &rx_buf, 1)) {
+		return -EINVAL;
+	}
+
+	if (rx_buf != 0) {
+		LOG_ERR("Failed to set number of pages: %d", rx_buf);
+		return -EINVAL;
+	}
+
+	/* Get the initialization and authentication vectors from the firmware */
+	/* (see User Guide page 53) */
+	memcpy(max32664c_fw_init_vector, &firmware[0x28], sizeof(max32664c_fw_init_vector));
+	memcpy(max32664c_fw_auth_vector, &firmware[0x34], sizeof(max32664c_fw_auth_vector));
+
+	/* Write the initialization vector */
+	LOG_INF("\tWriting init vector...");
+	tx_buf[0] = 0x80;
+	tx_buf[1] = 0x00;
+	memcpy(&tx_buf[2], max32664c_fw_init_vector, sizeof(max32664c_fw_init_vector));
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 13, &rx_buf, 1)) {
+		return -EINVAL;
+	}
+	if (rx_buf != 0) {
+		LOG_ERR("Failed to set init vector: %d", rx_buf);
+		return -EINVAL;
+	}
+
+	/* Write the authentication vector */
+	LOG_INF("\tWriting auth vector...");
+	tx_buf[0] = 0x80;
+	tx_buf[1] = 0x01;
+	memcpy(&tx_buf[2], max32664c_fw_auth_vector, sizeof(max32664c_fw_auth_vector));
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 18, &rx_buf, 1)) {
+		return -EINVAL;
+	}
+	if (rx_buf != 0) {
+		LOG_ERR("Failed to set auth vector: %d", rx_buf);
+		return -EINVAL;
+	}
+
+	/* Remove the old app from the hub */
+	LOG_INF("\tRemove old app...");
+	if (max32664c_bl_erase_app(dev)) {
+		return -EINVAL;
+	}
+
+	/* Write the new firmware */
+	LOG_INF("\tWriting new firmware...");
+	page_offset = 0x4C;
+	for (uint8_t i = 0; i < num_pages; i++) {
+		uint8_t status;
+
+		LOG_INF("\t\tPage: %d of %d", (i + 1), num_pages);
+		LOG_INF("\t\tOffset: 0x%x", page_offset);
+		status = max32664c_bl_write_page(dev, firmware, page_offset);
+		LOG_INF("\t\tStatus: %u", status);
+		if (status != 0) {
+			return -EINVAL;
+		}
+
+		page_offset += MAX32664C_FW_UPDATE_WRITE_SIZE;
+	}
+
+	LOG_INF("\tSuccessful!");
+
+	return max32664c_bl_leave(dev);
+}
+
+int max32664c_bl_enter(const struct device *dev, const uint8_t *firmware, uint32_t size)
+{
+	uint8_t rx_buf[4] = {0};
+	uint8_t tx_buf[3];
+	const struct max32664c_config *config = dev->config;
+
+	gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT);
+	gpio_pin_configure_dt(&config->mfio_gpio, GPIO_OUTPUT);
+
+	/* Put the processor into bootloader mode */
+	LOG_INF("Entering bootloader mode");
+	gpio_pin_set_dt(&config->reset_gpio, false);
+	k_msleep(20);
+
+	gpio_pin_set_dt(&config->mfio_gpio, false);
+	k_msleep(20);
+
+	gpio_pin_set_dt(&config->reset_gpio, true);
+	k_msleep(200);
+
+	/* Set bootloader mode */
+	tx_buf[0] = 0x01;
+	tx_buf[1] = 0x00;
+	tx_buf[2] = 0x08;
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 3, rx_buf, 1)) {
+		return -EINVAL;
+	}
+
+	/* Read the device mode */
+	tx_buf[0] = 0x02;
+	tx_buf[1] = 0x00;
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 2, rx_buf, 2)) {
+		return -EINVAL;
+	}
+
+	LOG_DBG("Mode: %x ", rx_buf[1]);
+	if (rx_buf[1] != 8) {
+		LOG_ERR("Device not in bootloader mode!");
+		return -EINVAL;
+	}
+
+	/* Read the bootloader information */
+	tx_buf[0] = 0x81;
+	tx_buf[1] = 0x00;
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 2, rx_buf, 4)) {
+		return -EINVAL;
+	}
+
+	LOG_INF("Version: %d.%d.%d", rx_buf[1], rx_buf[2], rx_buf[3]);
+
+	/* Read the bootloader page size */
+	tx_buf[0] = 0x81;
+	tx_buf[1] = 0x01;
+	if (max32664c_bl_i2c_transmit(dev, tx_buf, 2, rx_buf, 3)) {
+		return -EINVAL;
+	}
+
+	LOG_INF("Page size: %u", (uint16_t)(rx_buf[1] << 8) | rx_buf[2]);
+
+	return max32664c_bl_load_fw(dev, firmware, size);
+}
+
+int max32664c_bl_leave(const struct device *dev)
+{
+	uint8_t hub_ver[3];
+	uint8_t rx_buf[4] = {0};
+	const struct max32664c_config *config = dev->config;
+
+	gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT);
+	gpio_pin_configure_dt(&config->mfio_gpio, GPIO_OUTPUT);
+
+	LOG_INF("Entering app mode");
+	gpio_pin_set_dt(&config->reset_gpio, true);
+	gpio_pin_set_dt(&config->mfio_gpio, false);
+	k_msleep(2000);
+
+	gpio_pin_set_dt(&config->reset_gpio, false);
+	k_msleep(5);
+
+	gpio_pin_set_dt(&config->mfio_gpio, true);
+	k_msleep(15);
+
+	gpio_pin_set_dt(&config->reset_gpio, true);
+	k_msleep(1700);
+
+	/* Read the device mode */
+	if (max32664c_app_i2c_read(dev, 0x02, 0x00, rx_buf, 2)) {
+		return -EINVAL;
+	}
+
+	LOG_DBG("Mode: %x ", rx_buf[1]);
+	if (rx_buf[1] != 0) {
+		LOG_ERR("Device not in application mode!");
+		return -EINVAL;
+	}
+
+	/* Read the MCU type */
+	if (max32664c_app_i2c_read(dev, 0xFF, 0x00, rx_buf, 2)) {
+		return -EINVAL;
+	}
+
+	LOG_INF("MCU type: %u", rx_buf[1]);
+
+	/* Read the firmware version */
+	if (max32664c_app_i2c_read(dev, 0xFF, 0x03, rx_buf, 4)) {
+		return -EINVAL;
+	}
+
+	memcpy(hub_ver, &rx_buf[1], 3);
+
+	LOG_INF("Version: %d.%d.%d", hub_ver[0], hub_ver[1], hub_ver[2]);
+
+	return 0;
+}
diff --git a/drivers/sensor/adi/max32664c/max32664c_init.c b/drivers/sensor/adi/max32664c/max32664c_init.c
new file mode 100644
index 0000000..e65d2ec
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c_init.c
@@ -0,0 +1,246 @@
+/*
+ * Initialization code for the MAX32664C biometric sensor hub.
+ *
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "max32664c.h"
+
+LOG_MODULE_REGISTER(maxim_max32664c_init, CONFIG_SENSOR_LOG_LEVEL);
+
+/** @brief      Set the SpO2 calibration coefficients.
+ *              NOTE: See page 10 of the SpO2 and Heart Rate User Guide for additional information.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_set_spo2_coeffs(const struct device *dev)
+{
+	const struct max32664c_config *config = dev->config;
+
+	uint8_t tx[15];
+	uint8_t rx;
+
+	/* Write the calibration coefficients */
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x00;
+
+	/* Copy the A value (index 0) into the transmission buffer */
+	memcpy(&tx[3], &config->spo2_calib[0], sizeof(int32_t));
+
+	/* Copy the B value (index 1) into the transmission buffer */
+	memcpy(&tx[7], &config->spo2_calib[1], sizeof(int32_t));
+
+	/* Copy the C value (index 2) into the transmission buffer */
+	memcpy(&tx[11], &config->spo2_calib[2], sizeof(int32_t));
+
+	return max32664c_i2c_transmit(dev, tx, sizeof(tx), &rx, sizeof(rx),
+				      MAX32664C_DEFAULT_CMD_DELAY);
+}
+
+/** @brief      Write the default configuration to the sensor hub.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_write_config(const struct device *dev)
+{
+	uint8_t rx;
+	uint8_t tx[5];
+	const struct max32664c_config *config = dev->config;
+	struct max32664c_data *data = dev->data;
+
+	/* Write the default settings */
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x13;
+	tx[3] = config->min_integration_time_idx;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not write minimum integration time!");
+		return -EINVAL;
+	}
+
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x14;
+	tx[3] = config->min_sampling_rate_idx;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not write minimum sampling rate!");
+		return -EINVAL;
+	}
+
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x15;
+	tx[3] = config->max_integration_time_idx;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not write maximum integration time!");
+		return -EINVAL;
+	}
+
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x16;
+	tx[3] = config->max_sampling_rate_idx;
+	if (max32664c_i2c_transmit(dev, tx, 4, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not write maximum sampling rate!");
+		return -EINVAL;
+	}
+
+	tx[0] = 0x10;
+	tx[1] = 0x02;
+	tx[2] = config->report_period;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not set report period!");
+		return -EINVAL;
+	}
+
+	/* Configure WHRM */
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x17;
+	tx[3] = config->hr_config[0];
+	tx[4] = config->hr_config[1];
+	LOG_DBG("Configuring WHRM: 0x%02X%02X", tx[3], tx[4]);
+	if (max32664c_i2c_transmit(dev, tx, 5, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not configure WHRM!");
+		return -EINVAL;
+	}
+
+	/* Configure SpO2 */
+	tx[0] = 0x50;
+	tx[1] = 0x07;
+	tx[2] = 0x18;
+	tx[3] = config->spo2_config[0];
+	tx[4] = config->spo2_config[1];
+	LOG_DBG("Configuring SpO2: 0x%02X%02X", tx[3], tx[4]);
+	if (max32664c_i2c_transmit(dev, tx, 5, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not configure SpO2!");
+		return -EINVAL;
+	}
+
+	/* Set the interrupt threshold */
+	tx[0] = 0x10;
+	tx[1] = 0x01;
+	tx[2] = 0x01;
+	if (max32664c_i2c_transmit(dev, tx, 3, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not set interrupt threshold!");
+		return -EINVAL;
+	}
+
+	if (max32664c_set_spo2_coeffs(dev)) {
+		LOG_ERR("Can not set SpO2 calibration coefficients!");
+		return -EINVAL;
+	}
+
+	data->motion_time = config->motion_time;
+	data->motion_threshold = config->motion_threshold;
+	memcpy(data->led_current, config->led_current, sizeof(data->led_current));
+
+	return 0;
+}
+
+/** @brief      Read the configuration from the sensor hub.
+ *  @param dev  Pointer to device
+ *  @return     0 when successful
+ */
+static int max32664c_read_config(const struct device *dev)
+{
+	uint8_t tx[3];
+	uint8_t rx[2];
+	struct max32664c_data *data = dev->data;
+
+	tx[0] = 0x11;
+	tx[1] = 0x02;
+	if (max32664c_i2c_transmit(dev, tx, 2, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not read report period!");
+		return -EINVAL;
+	}
+	data->report_period = rx[1];
+
+	tx[0] = 0x51;
+	tx[1] = 0x07;
+	tx[2] = 0x13;
+	if (max32664c_i2c_transmit(dev, tx, 3, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not read minimum integration time!");
+		return -EINVAL;
+	}
+	data->min_integration_time_idx = rx[1];
+
+	tx[0] = 0x51;
+	tx[1] = 0x07;
+	tx[2] = 0x14;
+	if (max32664c_i2c_transmit(dev, tx, 3, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not read minimum sampling rate!");
+		return -EINVAL;
+	}
+	data->min_sampling_rate_idx = rx[1];
+
+	tx[0] = 0x51;
+	tx[1] = 0x07;
+	tx[2] = 0x15;
+	if (max32664c_i2c_transmit(dev, tx, 3, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not read maximum integration time!");
+		return -EINVAL;
+	}
+	data->max_integration_time_idx = rx[1];
+
+	tx[0] = 0x51;
+	tx[1] = 0x07;
+	tx[2] = 0x16;
+	if (max32664c_i2c_transmit(dev, tx, 3, rx, 2, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not read maximum sampling rate!");
+		return -EINVAL;
+	}
+	data->max_sampling_rate_idx = rx[1];
+
+	return 0;
+}
+
+int max32664c_init_hub(const struct device *dev)
+{
+	struct max32664c_data *data = dev->data;
+
+	LOG_DBG("Initialize sensor hub");
+
+	if (max32664c_write_config(dev)) {
+		LOG_ERR("Can not write default configuration!");
+		return -EINVAL;
+	}
+
+	if (max32664c_read_config(dev)) {
+		LOG_ERR("Can not read configuration!");
+		return -EINVAL;
+	}
+
+	data->is_thread_running = true;
+	data->thread_id = k_thread_create(&data->thread, data->thread_stack,
+					  K_THREAD_STACK_SIZEOF(data->thread_stack),
+					  (k_thread_entry_t)max32664c_worker, (void *)dev, NULL,
+					  NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);
+	k_thread_suspend(data->thread_id);
+	k_thread_name_set(data->thread_id, "max32664c_worker");
+
+	LOG_DBG("Initial configuration:");
+
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+	LOG_DBG("\tUsing dynamic memory for queues and buffers");
+#else
+	LOG_DBG("\tUsing static memory for queues and buffers");
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+	LOG_DBG("\tUsing extended reports");
+#else
+	LOG_DBG("\tUsing normal reports");
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS*/
+
+	LOG_DBG("\tReport period: %u", data->report_period);
+	LOG_DBG("\tMinimum integration time: %u", data->min_integration_time_idx);
+	LOG_DBG("\tMinimum sampling rate: %u", data->min_sampling_rate_idx);
+	LOG_DBG("\tMaximum integration time: %u", data->max_integration_time_idx);
+	LOG_DBG("\tMaximum sampling rate: %u", data->max_sampling_rate_idx);
+
+	return 0;
+}
diff --git a/drivers/sensor/adi/max32664c/max32664c_interrupt.c b/drivers/sensor/adi/max32664c/max32664c_interrupt.c
new file mode 100644
index 0000000..492cdf3
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c_interrupt.c
@@ -0,0 +1,80 @@
+/*
+ * Trigger code for the MAX32664C biometric sensor hub.
+ *
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "max32664c.h"
+
+LOG_MODULE_REGISTER(maxim_max32664c_interrupt, CONFIG_SENSOR_LOG_LEVEL);
+
+#ifdef CONFIG_MAX32664C_USE_INTERRUPT
+static void max32664c_interrupt_worker(struct k_work *p_work)
+{
+	struct max32664c_data *data = CONTAINER_OF(p_work, struct max32664c_data, interrupt_work);
+
+	/* TODO */
+}
+
+static void max32664c_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 max32664c_data *data = CONTAINER_OF(p_cb, struct max32664c_data, gpio_cb);
+
+	k_work_submit(&data->interrupt_work);
+}
+
+int max32664c_init_interrupt(const struct device *dev)
+{
+	LOG_DBG("\tUsing MFIO interrupt mode");
+
+	int err;
+	uint8_t tx[2];
+	uint8_t rx;
+	struct max32664c_data *data = dev->data;
+	const struct max32664c_config *config = dev->config;
+
+	LOG_DBG("Configure interrupt pin");
+	if (!gpio_is_ready_dt(&config->int_gpio)) {
+		LOG_ERR("GPIO not ready!");
+		return -ENODEV;
+	}
+
+	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, max32664c_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;
+	}
+
+	data->interrupt_work.handler = max32664c_interrupt_worker;
+
+	tx[0] = 0xB8;
+	tx[1] = 0x01;
+	if (max32664c_i2c_transmit(dev, tx, 2, &rx, 1, MAX32664C_DEFAULT_CMD_DELAY)) {
+		LOG_ERR("Can not enable interrupt mode!");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_MAX32664C_USE_INTERRUPT */
diff --git a/drivers/sensor/adi/max32664c/max32664c_worker.c b/drivers/sensor/adi/max32664c/max32664c_worker.c
new file mode 100644
index 0000000..4943c62
--- /dev/null
+++ b/drivers/sensor/adi/max32664c/max32664c_worker.c
@@ -0,0 +1,369 @@
+/*
+ * Background worker for the MAX32664C biometric sensor hub.
+ *
+ * Copyright (c) 2025, Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/sys/byteorder.h>
+
+#include "max32664c.h"
+
+LOG_MODULE_REGISTER(maxim_max32664c_worker, CONFIG_SENSOR_LOG_LEVEL);
+
+/** @brief              Read the status from the sensor hub.
+ *                      NOTE: Table 7 Sensor Hub Status Byte
+ *  @param dev          Pointer to device
+ *  @param status       Pointer to status byte
+ *  @param i2c_error    Pointer to I2C error byte
+ *  @return             0 when successful, otherwise an error code
+ */
+static int max32664c_get_hub_status(const struct device *dev, uint8_t *status, uint8_t *i2c_error)
+{
+	uint8_t tx[2] = {0x00, 0x00};
+	uint8_t rx[2];
+
+	if (max32664c_i2c_transmit(dev, tx, sizeof(tx), rx, sizeof(rx),
+				   MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	*i2c_error = rx[0];
+	*status = rx[1];
+
+	return 0;
+}
+
+/** @brief      Read the FIFO sample count.
+ *  @param dev  Pointer to device
+ *  @param fifo Pointer to FIFO count
+ */
+static int max32664c_get_fifo_count(const struct device *dev, uint8_t *fifo)
+{
+	uint8_t tx[2] = {0x12, 0x00};
+	uint8_t rx[2];
+
+	if (max32664c_i2c_transmit(dev, tx, sizeof(tx), rx, sizeof(rx),
+				   MAX32664C_DEFAULT_CMD_DELAY)) {
+		return -EINVAL;
+	}
+
+	*fifo = rx[1];
+
+	return rx[0];
+}
+
+/** @brief      Push a item into the message queue.
+ *  @param msgq Pointer to message queue
+ *  @param data Pointer to data to push
+ */
+static void max32664c_push_to_queue(struct k_msgq *msgq, const void *data)
+{
+	while (k_msgq_put(msgq, data, K_NO_WAIT) != 0) {
+		k_msgq_purge(msgq);
+	}
+}
+
+/** @brief          Process the buffer to get the raw data from the sensor hub.
+ *  @param dev      Pointer to device
+ */
+static void max32664c_parse_and_push_raw(const struct device *dev)
+{
+	struct max32664c_data *data = dev->data;
+	struct max32664c_raw_report_t report;
+
+	report.PPG1 = ((uint32_t)(data->max32664_i2c_buffer[1]) << 16) |
+		       ((uint32_t)(data->max32664_i2c_buffer[2]) << 8) |
+		       data->max32664_i2c_buffer[3];
+	report.PPG2 = ((uint32_t)(data->max32664_i2c_buffer[4]) << 16) |
+		       ((uint32_t)(data->max32664_i2c_buffer[5]) << 8) |
+		       data->max32664_i2c_buffer[6];
+	report.PPG3 = ((uint32_t)(data->max32664_i2c_buffer[7]) << 16) |
+		       ((uint32_t)(data->max32664_i2c_buffer[8]) << 8) |
+		       data->max32664_i2c_buffer[9];
+
+	/* PPG4 to 6 are used for PD2 */
+	report.PPG4 = 0;
+	report.PPG5 = 0;
+	report.PPG6 = 0;
+
+	report.acc.x =
+		((int16_t)(data->max32664_i2c_buffer[19]) << 8) | data->max32664_i2c_buffer[20];
+	report.acc.y =
+		((int16_t)(data->max32664_i2c_buffer[21]) << 8) | data->max32664_i2c_buffer[22];
+	report.acc.z =
+		((int16_t)(data->max32664_i2c_buffer[23]) << 8) | data->max32664_i2c_buffer[24];
+
+	max32664c_push_to_queue(&data->raw_report_queue, &report);
+}
+
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+/** @brief          Process the buffer to get the extended report data from the sensor hub.
+ *  @param dev      Pointer to device
+ */
+static void max32664c_parse_and_push_ext_report(const struct device *dev)
+{
+	struct max32664c_data *data = dev->data;
+	struct max32664c_ext_report_t report;
+
+	report.op_mode = data->max32664_i2c_buffer[25];
+	report.hr =
+		(((uint16_t)(data->max32664_i2c_buffer[26]) << 8) | data->max32664_i2c_buffer[27]) /
+		10;
+	report.hr_confidence = data->max32664_i2c_buffer[28];
+	report.rr =
+		(((uint16_t)(data->max32664_i2c_buffer[29]) << 8) | data->max32664_i2c_buffer[30]) /
+		10;
+	report.rr_confidence = data->max32664_i2c_buffer[31];
+	report.activity_class = data->max32664_i2c_buffer[32];
+	report.total_walk_steps = data->max32664_i2c_buffer[33] |
+				   ((uint32_t)(data->max32664_i2c_buffer[34]) << 8) |
+				   ((uint32_t)(data->max32664_i2c_buffer[35]) << 16) |
+				   ((uint32_t)(data->max32664_i2c_buffer[36]) << 24);
+	report.total_run_steps = data->max32664_i2c_buffer[37] |
+				  ((uint32_t)(data->max32664_i2c_buffer[38]) << 8) |
+				  ((uint32_t)(data->max32664_i2c_buffer[39]) << 16) |
+				  ((uint32_t)(data->max32664_i2c_buffer[40]) << 24);
+	report.total_energy_kcal = data->max32664_i2c_buffer[41] |
+				    ((uint32_t)(data->max32664_i2c_buffer[42]) << 8) |
+				    ((uint32_t)(data->max32664_i2c_buffer[43]) << 16) |
+				    ((uint32_t)(data->max32664_i2c_buffer[44]) << 24);
+	report.total_amr_kcal = data->max32664_i2c_buffer[45] |
+				 ((uint32_t)(data->max32664_i2c_buffer[46]) << 8) |
+				 ((uint32_t)(data->max32664_i2c_buffer[47]) << 16) |
+				 ((uint32_t)(data->max32664_i2c_buffer[48]) << 24);
+	report.led_current_adj1.adj_flag = data->max32664_i2c_buffer[49];
+	report.led_current_adj1.adj_val =
+		(((uint16_t)(data->max32664_i2c_buffer[50]) << 8) | data->max32664_i2c_buffer[51]) /
+		10;
+	report.led_current_adj2.adj_flag = data->max32664_i2c_buffer[52];
+	report.led_current_adj2.adj_val =
+		(((uint16_t)(data->max32664_i2c_buffer[53]) << 8) | data->max32664_i2c_buffer[54]) /
+		10;
+	report.led_current_adj3.adj_flag = data->max32664_i2c_buffer[55];
+	report.led_current_adj3.adj_val =
+		(((uint16_t)(data->max32664_i2c_buffer[56]) << 8) | data->max32664_i2c_buffer[57]) /
+		10;
+	report.integration_time_adj_flag = data->max32664_i2c_buffer[58];
+	report.requested_integration_time = data->max32664_i2c_buffer[59];
+	report.sampling_rate_adj_flag = data->max32664_i2c_buffer[60];
+	report.requested_sampling_rate = data->max32664_i2c_buffer[61];
+	report.requested_sampling_average = data->max32664_i2c_buffer[62];
+	report.hrm_afe_ctrl_state = data->max32664_i2c_buffer[63];
+	report.is_high_motion_for_hrm = data->max32664_i2c_buffer[64];
+	report.scd_state = data->max32664_i2c_buffer[65];
+	report.r_value =
+		(((uint16_t)(data->max32664_i2c_buffer[66]) << 8) | data->max32664_i2c_buffer[67]) /
+		1000;
+	report.spo2_meas.confidence = data->max32664_i2c_buffer[68];
+	report.spo2_meas.value =
+		(((uint16_t)(data->max32664_i2c_buffer[69]) << 8) | data->max32664_i2c_buffer[70]) /
+		10;
+	report.spo2_meas.valid_percent = data->max32664_i2c_buffer[71];
+	report.spo2_meas.low_signal_flag = data->max32664_i2c_buffer[72];
+	report.spo2_meas.motion_flag = data->max32664_i2c_buffer[73];
+	report.spo2_meas.low_pi_flag = data->max32664_i2c_buffer[74];
+	report.spo2_meas.unreliable_r_flag = data->max32664_i2c_buffer[75];
+	report.spo2_meas.state = data->max32664_i2c_buffer[76];
+	report.ibi_offset = data->max32664_i2c_buffer[77];
+	report.unreliable_orientation_flag = data->max32664_i2c_buffer[78];
+	report.reserved[0] = data->max32664_i2c_buffer[79];
+	report.reserved[1] = data->max32664_i2c_buffer[80];
+
+	max32664c_push_to_queue(&data->ext_report_queue, &report);
+}
+#else
+/** @brief          Process the buffer to get the report data from the sensor hub.
+ *  @param dev      Pointer to device
+ */
+static void max32664c_parse_and_push_report(const struct device *dev)
+{
+	struct max32664c_data *data = dev->data;
+	struct max32664c_report_t report;
+
+	report.op_mode = data->max32664_i2c_buffer[25];
+	report.hr =
+		(((uint16_t)(data->max32664_i2c_buffer[26]) << 8) | data->max32664_i2c_buffer[27]) /
+		10;
+	report.hr_confidence = data->max32664_i2c_buffer[28];
+	report.rr =
+		(((uint16_t)(data->max32664_i2c_buffer[29]) << 8) | data->max32664_i2c_buffer[30]) /
+		10;
+	report.rr_confidence = data->max32664_i2c_buffer[31];
+	report.activity_class = data->max32664_i2c_buffer[32];
+	report.r =
+		(((uint16_t)(data->max32664_i2c_buffer[33]) << 8) | data->max32664_i2c_buffer[34]) /
+		1000;
+	report.spo2_meas.confidence = data->max32664_i2c_buffer[35];
+	report.spo2_meas.value =
+		(((uint16_t)(data->max32664_i2c_buffer[36]) << 8) | data->max32664_i2c_buffer[37]) /
+		10;
+	report.spo2_meas.complete = data->max32664_i2c_buffer[38];
+	report.spo2_meas.low_signal_quality = data->max32664_i2c_buffer[39];
+	report.spo2_meas.motion = data->max32664_i2c_buffer[40];
+	report.spo2_meas.low_pi = data->max32664_i2c_buffer[41];
+	report.spo2_meas.unreliable_r = data->max32664_i2c_buffer[42];
+	report.spo2_meas.state = data->max32664_i2c_buffer[43];
+	report.scd_state = data->max32664_i2c_buffer[44];
+
+	max32664c_push_to_queue(&data->report_queue, &report);
+}
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+
+/** @brief      Worker thread to read the sensor hub.
+ *              This thread does the following:
+ *                  - It polls the sensor hub periodically for new results
+ *                  - If new messages are available it reads the number of samples
+ *                  - Then it reads all the samples to clear the FIFO.
+ *                    It's necessary to clear the complete FIFO because the sensor hub
+ *                    doesn´t support the reading of a single message and not clearing
+ *                    the FIFO can cause a FIFO overrun.
+ *                  - Extract the message data from the FIRST item from the FIFO and
+ *                    copy them into the right message structure
+ *                  - Put the message into a message queue
+ *  @param dev  Pointer to device
+ */
+void max32664c_worker(const struct device *dev)
+{
+	int err;
+	uint8_t fifo = 0;
+	uint8_t status = 0;
+	uint8_t i2c_error = 0;
+	struct max32664c_data *data = dev->data;
+
+	LOG_DBG("Starting worker thread for device: %s", dev->name);
+
+	while (data->is_thread_running) {
+		err = max32664c_get_hub_status(dev, &status, &i2c_error);
+		if (err) {
+			LOG_ERR("Failed to get hub status! Error: %d", err);
+			continue;
+		}
+
+		if (!(status & (1 << MAX32664C_BIT_STATUS_DATA_RDY))) {
+			LOG_WRN("No data ready! Status: 0x%X", status);
+			k_msleep(100);
+			continue;
+		}
+
+		err = max32664c_get_fifo_count(dev, &fifo);
+		if (err) {
+			LOG_ERR("Failed to get FIFO count! Error: %d", err);
+			continue;
+		}
+
+		if (fifo == 0) {
+			LOG_DBG("No data available in the FIFO.");
+			continue;
+		}
+#ifdef CONFIG_MAX32664C_USE_STATIC_MEMORY
+		else if (fifo > CONFIG_MAX32664C_SAMPLE_BUFFER_SIZE) {
+			LOG_ERR("FIFO count %u exceeds maximum buffer size %u!",
+				fifo, CONFIG_MAX32664C_SAMPLE_BUFFER_SIZE);
+
+			/* TODO: Find a good way to clear the FIFO */
+			continue;
+		}
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+		size_t buffer_size = fifo * (sizeof(struct max32664c_raw_report_t) +
+							sizeof(struct max32664c_ext_report_t)) +
+						1;
+#else
+		size_t buffer_size = fifo * (sizeof(struct max32664c_raw_report_t) +
+							sizeof(struct max32664c_report_t)) +
+						1;
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+
+		LOG_DBG("Allocating memory %u samples", fifo);
+		LOG_DBG("Allocating memory for the I2C buffer with size: %u", buffer_size);
+		data->max32664_i2c_buffer = (uint8_t *)k_malloc(buffer_size);
+
+		if (data->max32664_i2c_buffer == NULL) {
+			LOG_ERR("Can not allocate memory for the I2C buffer!");
+			continue;
+		}
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+		uint8_t tx[2] = {0x12, 0x01};
+
+		switch (data->op_mode) {
+		case MAX32664C_OP_MODE_RAW: {
+			/* Get all samples to clear the FIFO */
+			max32664c_i2c_transmit(
+				dev, tx, 2, data->max32664_i2c_buffer,
+				(fifo * (sizeof(struct max32664c_raw_report_t))) +
+					1,
+				MAX32664C_DEFAULT_CMD_DELAY);
+
+			if (data->max32664_i2c_buffer[0] != 0) {
+				break;
+			}
+
+			max32664c_parse_and_push_raw(dev);
+
+			break;
+		}
+#ifdef CONFIG_MAX32664C_USE_EXTENDED_REPORTS
+		case MAX32664C_OP_MODE_ALGO_AEC_EXT:
+		case MAX32664C_OP_MODE_ALGO_AGC_EXT: {
+
+			/* Get all samples to clear the FIFO */
+			max32664c_i2c_transmit(
+				dev, tx, 2, data->max32664_i2c_buffer,
+				(fifo * (sizeof(struct max32664c_raw_report_t) +
+						sizeof(struct max32664c_ext_report_t))) +
+					1,
+				MAX32664C_DEFAULT_CMD_DELAY);
+
+			if (data->max32664_i2c_buffer[0] != 0) {
+				break;
+			}
+
+			max32664c_parse_and_push_raw(dev);
+			max32664c_parse_and_push_ext_report(dev);
+
+			break;
+		}
+#else
+		case MAX32664C_OP_MODE_ALGO_AEC:
+		case MAX32664C_OP_MODE_ALGO_AGC: {
+
+			/* Get all samples to clear the FIFO */
+			max32664c_i2c_transmit(
+				dev, tx, 2, data->max32664_i2c_buffer,
+				(fifo * (sizeof(struct max32664c_raw_report_t) +
+						sizeof(struct max32664c_report_t))) +
+					1,
+				MAX32664C_DEFAULT_CMD_DELAY);
+
+			if (data->max32664_i2c_buffer[0] != 0) {
+				break;
+			}
+
+			max32664c_parse_and_push_raw(dev);
+			max32664c_parse_and_push_report(dev);
+
+			break;
+		}
+#endif /* CONFIG_MAX32664C_USE_EXTENDED_REPORTS */
+		default: {
+			break;
+		}
+		}
+
+		if (data->max32664_i2c_buffer[0] != 0) {
+			LOG_ERR("Can not read report! Status: 0x%X",
+				data->max32664_i2c_buffer[0]);
+		}
+
+#ifndef CONFIG_MAX32664C_USE_STATIC_MEMORY
+		k_free(data->max32664_i2c_buffer);
+#endif /* CONFIG_MAX32664C_USE_STATIC_MEMORY */
+
+		k_msleep(100);
+	}
+}
diff --git a/dts/bindings/sensor/maxim,max32664c.yml b/dts/bindings/sensor/maxim,max32664c.yml
new file mode 100644
index 0000000..3f8c36c
--- /dev/null
+++ b/dts/bindings/sensor/maxim,max32664c.yml
@@ -0,0 +1,159 @@
+title: |
+  MAX32664 biometric sensor hub
+
+description: |
+  The MAX32664 is a ultra-low power biometric sensor hub.
+
+  NOTES:
+  This driver is primarily written to work with a MAX86141. Other sensors can be
+  used but they are untested! The driver supports up to two photodetectors (PDs)
+  and three LEDs fix. It requires a specific LED
+  configuration for the MAX86141.
+    LED1 -> Green
+    LED2 -> IR
+    LED3 -> Red
+  The LEDs can be changed manually but this may require changes in the driver.
+
+  This driver is tested with Sensor Hub firmware 30.13.31 and an external
+  Accelerometer (e.g. LIS2DH12).
+
+  See more info at:
+  https://www.analog.com/media/en/technical-documentation/data-sheets/MAX32664.pdf
+
+compatible: "maxim,max32664c"
+
+include: [sensor-device.yaml, i2c-device.yaml]
+
+properties:
+  reset-gpios:
+    type: phandle-array
+    required: true
+    description:
+      External System Reset (Active-Low) Input.
+
+  mfio-gpios:
+    type: phandle-array
+    required: true
+    description:
+      MFIO asserts low as an output when the sensor hub needs to
+      communication with the host; MFIO acts as an input and when held
+      low during a reset, the sensor hub enters bootloader mode.
+
+  use-max86141:
+    type: boolean
+    description:
+      Use the MAX86141 as the AFE for the MAX32664C. This is the
+      default and recommended configuration. The driver is optimized for
+      this sensor.
+
+  use-max86161:
+    type: boolean
+    description:
+      Use the MAX86161 as the AFE for the MAX32664C.
+
+  motion-time:
+    type: int
+    default: 200
+    description:
+      Sensor Hub configuration - Motion activation time in milliseconds.
+      The default corresponds to Table 12 in the HR and SpO2 User guide.
+
+  motion-threshold:
+    type: int
+    default: 500
+    description:
+      Sensor Hub configuration - Motion activation time in milli-g.
+      The default corresponds to Table 12 in the HR and SpO2 User guide.
+
+  report-period:
+    type: int
+    default: 1
+    description:
+      Sensor Hub configuration - Set the samples report period (e.g., a value
+      of 25 means a samples report is generated once every 25 samples).
+      The default corresponds to Table 16 in the HR and SpO2 User guide.
+
+  spo2-calib:
+    type: array
+    default: [0xFFE69196, 0x000CB735, 0x00989680]
+    description:
+      Algorithm configuration - SpO2 calibration coefficients.
+      The default corresponds to Table 12 in the HR and SpO2 User guide.
+
+  min-integration-time:
+    type: int
+    default: 14
+    enum:
+      - 14
+      - 29
+      - 58
+      - 117
+    description:
+      Algorithm configuration - Minimum integration time in microseconds.
+      The default corresponds to Table 11 in the HR and SpO2 User guide.
+
+  min-sampling-rate:
+    type: int
+    default: 50
+    enum:
+      - 25
+      - 50
+      - 100
+      - 200
+      - 400
+    description:
+      Algorithm configuration - Minimum sampling rate (samples per second)
+      and averaging (samples).
+      The default corresponds to Table 11 in the HR and SpO2 User guide.
+
+  max-integration-time:
+    type: int
+    default: 117
+    enum:
+      - 14
+      - 29
+      - 58
+      - 117
+    description:
+      Algorithm configuration - Maximum integration time in microseconds.
+      The default corresponds to Table 11 in the HR and SpO2 User guide.
+
+  max-sampling-rate:
+    type: int
+    default: 100
+    enum:
+      - 25
+      - 50
+      - 100
+      - 200
+      - 400
+    description:
+      Algorithm configuration - Maximum sampling rate (samples per second)
+      and averaging (samples).
+      The default corresponds to Table 11 in the HR and SpO2 User guide.
+
+  led-current:
+    type: uint8-array
+    default: [0x7F, 0x7F, 0x7F]
+    description:
+      Initial LED current configuration in bits. Please check the datasheet
+      of the attached AFE to determine the appropriate values.
+      The current can also be changed later by the firmware.
+      Index 0 corresponds to LED1, index 1 to LED2, and index 2 to LED3.
+      The default corresponds to Table 5 in the HR and SpO2 User guide.
+
+  hr-config:
+    type: uint8-array
+    default: [0x00, 0x01]
+    description:
+      Algorithm configuration - LED and PD configuration for the heartrate measurement.
+      The first entry configures channel 1, the second channel 2.
+      The default corresponds to Table 15 in the HR and SpO2 User guide.
+
+  spo2-config:
+    type: uint8-array
+    default: [0x10, 0x20]
+    description:
+      Algorithm configuration - LED and PD configuration for the SpO2 measurement.
+      The first entry configures the IR channel, the second the red channel.
+      The default corresponds to Table 15 in the HR and SpO2 User guide.
diff --git a/include/zephyr/drivers/sensor/max32664c.h b/include/zephyr/drivers/sensor/max32664c.h
new file mode 100644
index 0000000..128f15e
--- /dev/null
+++ b/include/zephyr/drivers/sensor/max32664c.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2025 Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_MAX32664C_H_
+#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_MAX32664C_H_
+
+#include <zephyr/device.h>
+
+/** @brief Converts a motion time in milli-seconds to the corresponding value for the MAX32664C
+ * sensor. This macro should be used when configuring the motion based wake up settings for the
+ * sensor.
+ */
+#define MAX32664C_MOTION_TIME(ms) ((uint8_t)((ms * 25UL) / 1000))
+
+/** @brief Converts a motion threshold in milli-g (Acceleration) to the corresponding value for the
+ * MAX32664C sensor. This macro should be used when configuring the motion based wake up settings
+ * for the sensor.
+ */
+#define MAX32664C_MOTION_THRESHOLD(mg) ((uint8_t)((mg * 16UL) / 1000))
+
+/* MAX32664C specific channels */
+enum sensor_channel_max32664c {
+	/** Heart rate value (bpm) */
+	SENSOR_CHAN_MAX32664C_HEARTRATE = SENSOR_CHAN_PRIV_START,
+	/** SpO2 value (%) */
+	SENSOR_CHAN_MAX32664C_BLOOD_OXYGEN_SATURATION,
+	/** Respiration rate (breaths per minute) */
+	SENSOR_CHAN_MAX32664C_RESPIRATION_RATE,
+	/** Skin contact (1 -> Skin contact, 0, no contact) */
+	SENSOR_CHAN_MAX32664C_SKIN_CONTACT,
+	/** Activity class (index). The reported index is vendor specific. */
+	SENSOR_CHAN_MAX32664C_ACTIVITY,
+	/** Step counter */
+	SENSOR_CHAN_MAX32664C_STEP_COUNTER,
+};
+
+/* MAX32664C specific attributes */
+enum sensor_attribute_max32664c {
+	/** Gender of the subject being monitored */
+	SENSOR_ATTR_MAX32664C_GENDER = SENSOR_ATTR_PRIV_START,
+	/** Age of the subject being monitored */
+	SENSOR_ATTR_MAX32664C_AGE,
+	/** Weight of the subject being monitored */
+	SENSOR_ATTR_MAX32664C_WEIGHT,
+	/** Height of the subject being monitored */
+	SENSOR_ATTR_MAX32664C_HEIGHT,
+	/** Get / Set the operation mode of a sensor. This can be used to
+	 * switch between different measurement modes when a sensor supports them.
+	 */
+	SENSOR_ATTR_MAX32664C_OP_MODE,
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief Device operating modes for the MAX32664C sensor.
+ *
+ *  This enum defines the various operating modes that the MAX32664C sensor
+ *  can be configured to. These modes control the sensor's behavior and
+ *  functionality, such as calibration, idle state, raw data output, and
+ *  algorithm-based operations.
+ */
+enum max32664c_device_mode {
+	MAX32664C_OP_MODE_IDLE, /**< Idle mode, no algorithm, */
+				/**< sensors or wake on motion running */
+	MAX32664C_OP_MODE_RAW,  /**< Raw output mode */
+	/* For hardware testing purposes, the user may choose to start the sensor hub to collect
+	 * raw PPG samples. In this case, the host configures the sensor hub to work in Raw Data
+	 * mode (no algorithm) by enabling the accelerometer and the AFE.
+	 */
+	MAX32664C_OP_MODE_ALGO_AEC, /**< Algorithm AEC mode */
+	/* Automatic Exposure Control (AEC) is Maxim’s gain control algorithm that is superior to
+	 * AGC. The AEC algorithm optimally maintains the best SNR range and power optimization. The
+	 * targeted SNR range is maintained regardless of skin color or ambient temperature within
+	 * the limits of the LED currents configurations; The AEC dynamically manages the
+	 * appropriate register settings for sampling rate, LED current, pulse width and integration
+	 * time.
+	 */
+	MAX32664C_OP_MODE_ALGO_AEC_EXT, /**< Algorithm with extended reports */
+	MAX32664C_OP_MODE_ALGO_AGC,     /**< Algorithm AGC mode */
+	/* In this mode, the wearable algorithm suite (SpO2 and WHRM) is enabled and the R value,
+	 * SpO2, SpO2 confidence level, heart rate, heart rate confidence level, RR value, and
+	 * activity class are reported. Furthermore, automatic gain control (AGC) is enabled.
+	 * Because AGC is a subset of AEC functionality, to enable AGC, AEC still needs to be
+	 * enabled. However, automatic calculation of target PD should be turned off, and the
+	 * desired level of AGC target PD current is set by the user. The user may change the
+	 * algorithm to the desired configuration mode. If signal quality is poor, the user may need
+	 * to adjust the AGC settings to maintain optimal performance. If signal quality is low, a
+	 * LowSNR flag will be set. Excessive motion is also reported with a flag.
+	 */
+	MAX32664C_OP_MODE_ALGO_AGC_EXT,        /**< Algorithm AGC with extended reports */
+	MAX32664C_OP_MODE_SCD,                 /**< SCD only mode */
+	MAX32664C_OP_MODE_WAKE_ON_MOTION,      /**< Wake on motion mode */
+	MAX32664C_OP_MODE_EXIT_WAKE_ON_MOTION, /**< Exit wake on motion mode */
+	MAX32664C_OP_MODE_STOP_ALGO,           /**< Stop the current algorithm */
+};
+
+/** @brief Algorithm modes for the MAX32664C sensor.
+ *
+ *  This enum defines the various algorithm modes supported by the MAX32664C sensor.
+ *  These modes determine the type of data processing performed by the sensor,
+ *  such as continuous heart rate monitoring, SpO2 calculation, or activity tracking.
+ */
+enum max32664c_algo_mode {
+	MAX32664C_ALGO_MODE_CONT_HR_CONT_SPO2,
+	MAX32664C_ALGO_MODE_CONT_HR_SHOT_SPO2,
+	MAX32664C_ALGO_MODE_CONT_HRM,
+	/* NOTE: These algorithm modes are untested */
+	/*MAX32664C_ALGO_MODE_SAMPLED_HRM,*/
+	/*MAX32664C_ALGO_MODE_SAMPLED_HRM_SHOT_SPO2,*/
+	/*MAX32664C_ALGO_MODE_ACTIVITY_TRACK,*/
+	/*MAX32664C_ALGO_MODE_SAMPLED_HRM_FAST_SPO2 = 7,*/
+};
+
+/** @brief Gender settings for the MAX32664C sensor.
+ *
+ *  This enum defines the supported gender settings for the MAX32664C sensor.
+ */
+enum max32664c_algo_gender {
+	MAX32664_ALGO_GENDER_MALE,
+	MAX32664_ALGO_GENDER_FEMALE,
+};
+
+/** @brief Activity classes for the MAX32664C sensor.
+ *
+ *  This enum defines the supported activity classes for the MAX32664C sensor.
+ */
+enum max32664c_algo_activity {
+	MAX32664C_ALGO_ACTIVITY_REST,
+	MAX32664C_ALGO_ACTIVITY_OTHER,
+	MAX32664C_ALGO_ACTIVITY_WALK,
+	MAX32664C_ALGO_ACTIVITY_RUN,
+	MAX32664C_ALGO_ACTIVITY_BIKE,
+};
+
+/** @brief Data structure for external accelerometer data.
+ *
+ * This structure is used to represent the accelerometer data that can be
+ * collected from an external accelerometer and then fed into the MAX32664C
+ * sensor hub. It contains the x, y, and z acceleration values.
+ * This structure is only used when the external accelerometer is enabled.
+ */
+struct max32664c_acc_data_t {
+	int16_t x;
+	int16_t y;
+	int16_t z;
+} __packed;
+
+#ifdef CONFIG_MAX32664C_USE_FIRMWARE_LOADER
+/** @brief          Enter the bootloader mode and run a firmware update.
+ *  @param dev      Pointer to device
+ *  @param firmware Pointer to firmware data
+ *  @param size     Size of the firmware
+ *  @return         0 when successful
+ */
+int max32664c_bl_enter(const struct device *dev, const uint8_t *firmware, uint32_t size);
+
+/** @brief          Leave the bootloader and enter the application mode.
+ *  @param dev      Pointer to device
+ *  @return         0 when successful
+ */
+int max32664c_bl_leave(const struct device *dev);
+#endif /* CONFIG_MAX32664C_USE_FIRMWARE_LOADER */
+
+#ifdef CONFIG_MAX32664C_USE_EXTERNAL_ACC
+/** @brief          Fill the FIFO buffer with accelerometer data
+ *                  NOTE: This function supports up to 16 samples and it must be called
+ *                  periodically to provide accelerometer data to the MAX32664C!
+ *  @param dev      Pointer to device
+ *  @param data     Pointer to the accelerometer data structure
+ *  @param length   Number of samples to fill
+ *  @return         0 when successful
+ */
+int max32664c_acc_fill_fifo(const struct device *dev, struct max32664c_acc_data_t *data,
+			    uint8_t length);
+#endif /* CONFIG_MAX32664C_USE_EXTERNAL_ACC*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_MAX32664C_H_ */
diff --git a/samples/sensor/max32664c/CMakeLists.txt b/samples/sensor/max32664c/CMakeLists.txt
new file mode 100644
index 0000000..4992762
--- /dev/null
+++ b/samples/sensor/max32664c/CMakeLists.txt
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.20.0)
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(max32664c)
+
+target_sources(app PRIVATE src/main.c)
diff --git a/samples/sensor/max32664c/README.rst b/samples/sensor/max32664c/README.rst
new file mode 100644
index 0000000..0c25e00
--- /dev/null
+++ b/samples/sensor/max32664c/README.rst
@@ -0,0 +1,49 @@
+.. zephyr:code-sample:: max32664c
+   :name: MAX32664C + MAX86141 Sensor Hub
+   :relevant-api: sensor_interface
+
+Get health data from a MAX32664C and a MAX86141 sensor (polling mode).
+
+NOTE: This example requires sensor hub firmware 30.13.31!
+
+Overview
+********
+
+This sample measures the heart rate and the blood oxygen saturation on a wrist.
+It uses the MAX32664C sensor to control the MAX86141 sensor.
+
+Requirements
+************
+
+This sample uses the MAX32664 sensor controlled using the I2C30 interface at
+the nRF54L15-DK board.
+
+References
+**********
+
+- MAX32664C: https://www.analog.com/en/products/max32664.html
+
+Building and Running
+********************
+
+This project outputs sensor data to the console. It requires a MAX32664C
+sensor to be connected to the desired board. An additional MAX86141 sensor
+must be connected to the MAX32664C to provide the sensor data for the algorithms.
+
+.. zephyr-app-commands::
+   :zephyr-app: samples/sensor/max32664c/
+   :goals: build flash
+
+Sample Output
+=============
+
+.. code-block:: console
+
+    [00:00:00.000,000] <inf> sensor: MAX32664C: Initializing...
+    [00:00:01.600,000] <inf> sensor: MAX32664C: Initialization complete.
+    [00:00:01.600,000] <inf> sensor: MAX32664C: HR: 75 bpm
+    [00:00:01.600,100] <inf> sensor: MAX32664C: HR Confidence: 98
+    [00:00:02.600,000] <inf> sensor: MAX32664C: HR: 76 bpm
+    [00:00:02.600,100] <inf> sensor: MAX32664C: HR Confidence: 97
+    [00:00:03.600,000] <inf> sensor: MAX32664C: HR: 74 bpm
+    [00:00:03.600,100] <inf> sensor: MAX32664C: HR Confidence: 98
diff --git a/samples/sensor/max32664c/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/samples/sensor/max32664c/boards/nrf54l15dk_nrf54l15_cpuapp.overlay
new file mode 100644
index 0000000..92d64ae
--- /dev/null
+++ b/samples/sensor/max32664c/boards/nrf54l15dk_nrf54l15_cpuapp.overlay
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2025 Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+&pinctrl {
+	i2c30_default: i2c30_default {
+		group1 {
+			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
+					<NRF_PSEL(TWIM_SCL, 0, 3)>;
+					bias-pull-up;
+		};
+	};
+
+	i2c30_sleep: i2c30_sleep {
+		group1 {
+			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
+					<NRF_PSEL(TWIM_SCL, 0, 3)>;
+					low-power-enable;
+		};
+	};
+};
+
+/ {
+	aliases {
+		sensor = &biometric_hub;
+	};
+};
+
+&i2c30 {
+	compatible = "nordic,nrf-twim";
+	status = "okay";
+	clock-frequency = <I2C_BITRATE_FAST>;
+
+	// Flash buffer size is needed for the I2C bootloader to flash the firmware
+	zephyr,flash-buf-max-size = <8250>;
+
+	pinctrl-0 = <&i2c30_default>;
+	pinctrl-1 = <&i2c30_sleep>;
+	pinctrl-names = "default", "sleep";
+
+	biometric_hub: max32664c@55 {
+		compatible = "maxim,max32664c";
+		reg = <0x55>;
+		status = "okay";
+		reset-gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>;
+		mfio-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>;
+		use-max86141;
+	};
+};
+
+&dppic10 {
+	status = "okay";
+};
+
+&ppib11 {
+	status = "okay";
+};
+
+&ppib21 {
+	status = "okay";
+};
+
+&dppic20 {
+	status = "okay";
+};
+
+&ppib22 {
+	status = "okay";
+};
+
+&ppib30 {
+	status = "okay";
+};
+
+&dppic30 {
+	status = "okay";
+};
diff --git a/samples/sensor/max32664c/prj.conf b/samples/sensor/max32664c/prj.conf
new file mode 100644
index 0000000..653e8d1
--- /dev/null
+++ b/samples/sensor/max32664c/prj.conf
@@ -0,0 +1,7 @@
+CONFIG_I2C=y
+CONFIG_SENSOR=y
+
+CONFIG_LOG=y
+CONFIG_LOG_PRINTK=y
+
+CONFIG_HEAP_MEM_POOL_SIZE=16384
diff --git a/samples/sensor/max32664c/sample.yaml b/samples/sensor/max32664c/sample.yaml
new file mode 100644
index 0000000..e515826
--- /dev/null
+++ b/samples/sensor/max32664c/sample.yaml
@@ -0,0 +1,15 @@
+sample:
+  name: MAX32664C heart rate monitor sample
+tests:
+  sample.sensor.max32664c:
+    harness: sensor
+    platform_allow:
+      - nrf54l15dk/nrf54l15/cpuapp
+    integration_platforms:
+      - nrf54l15dk/nrf54l15/cpuapp
+    tags:
+      - sensors
+      - heart rate
+    filter: dt_compat_enabled("maxim,max32664c")
+    depends_on:
+      - i2c
diff --git a/samples/sensor/max32664c/src/main.c b/samples/sensor/max32664c/src/main.c
new file mode 100644
index 0000000..96a32cb
--- /dev/null
+++ b/samples/sensor/max32664c/src/main.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2025 Daniel Kampert
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/kernel.h>
+#include <zephyr/devicetree.h>
+#include <zephyr/logging/log.h>
+#include <zephyr/drivers/sensor.h>
+
+#include <zephyr/drivers/sensor/max32664c.h>
+
+static const struct device *const sensor_hub = DEVICE_DT_GET(DT_ALIAS(sensor));
+
+LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
+
+static void update(void)
+{
+	struct sensor_value value;
+
+	sensor_sample_fetch(sensor_hub);
+	sensor_attr_get(sensor_hub, SENSOR_CHAN_MAX32664C_HEARTRATE, SENSOR_ATTR_MAX32664C_OP_MODE,
+			&value);
+	if (value.val1 == MAX32664C_OP_MODE_RAW) {
+		struct sensor_value x;
+		struct sensor_value y;
+		struct sensor_value z;
+
+		if (sensor_channel_get(sensor_hub, SENSOR_CHAN_ACCEL_X, &x) ||
+		    sensor_channel_get(sensor_hub, SENSOR_CHAN_ACCEL_Y, &y) ||
+		    sensor_channel_get(sensor_hub, SENSOR_CHAN_ACCEL_Z, &z)) {
+			LOG_ERR("Failed to get accelerometer data");
+			return;
+		}
+
+		LOG_INF("\tx: %i", x.val1);
+		LOG_INF("\ty: %i", y.val1);
+		LOG_INF("\tz: %i", z.val1);
+	} else if (value.val1 == MAX32664C_OP_MODE_ALGO_AEC) {
+		struct sensor_value hr;
+
+		if (sensor_channel_get(sensor_hub, SENSOR_CHAN_MAX32664C_HEARTRATE, &hr)) {
+			LOG_ERR("Failed to get heart rate data");
+			return;
+		}
+
+		LOG_INF("HR: %u bpm", hr.val1);
+		LOG_INF("HR Confidence: %u", hr.val2);
+	} else {
+		LOG_WRN("Operation mode not implemented: %u", value.val1);
+	}
+}
+
+int main(void)
+{
+	struct sensor_value value;
+
+	if (!device_is_ready(sensor_hub)) {
+		LOG_ERR("Sensor hub not ready!");
+		return -1;
+	}
+
+	LOG_INF("Sensor hub ready");
+
+	value.val1 = MAX32664C_OP_MODE_ALGO_AEC;
+	value.val2 = MAX32664C_ALGO_MODE_CONT_HRM;
+	sensor_attr_set(sensor_hub, SENSOR_CHAN_MAX32664C_HEARTRATE, SENSOR_ATTR_MAX32664C_OP_MODE,
+			&value);
+
+	while (1) {
+		update();
+		k_msleep(1000);
+	}
+
+	return 0;
+}