diff --git a/drivers/sensor/microchip/CMakeLists.txt b/drivers/sensor/microchip/CMakeLists.txt index f26696ba1bb1c..49fc2c206cdc1 100644 --- a/drivers/sensor/microchip/CMakeLists.txt +++ b/drivers/sensor/microchip/CMakeLists.txt @@ -1,9 +1,11 @@ # Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2025 Microchip Technology Inc. # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start add_subdirectory_ifdef(CONFIG_MCP9600 mcp9600) add_subdirectory_ifdef(CONFIG_MCP970X mcp970x) +add_subdirectory_ifdef(CONFIG_MTCH9010 mtch9010) add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec) add_subdirectory_ifdef(CONFIG_TCN75A tcn75a) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/microchip/Kconfig b/drivers/sensor/microchip/Kconfig index dd33798135af9..981e82c96b5cb 100644 --- a/drivers/sensor/microchip/Kconfig +++ b/drivers/sensor/microchip/Kconfig @@ -1,9 +1,11 @@ # Copyright (c) 2024 Analog Devices, Inc. +# Copyright (c) 2025 Microchip Technology Inc. # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start source "drivers/sensor/microchip/mchp_tach_xec/Kconfig" source "drivers/sensor/microchip/mcp9600/Kconfig" source "drivers/sensor/microchip/mcp970x/Kconfig" +source "drivers/sensor/microchip/mtch9010/Kconfig" source "drivers/sensor/microchip/tcn75a/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/microchip/mtch9010/CMakeLists.txt b/drivers/sensor/microchip/mtch9010/CMakeLists.txt new file mode 100644 index 0000000000000..7ce061bd29906 --- /dev/null +++ b/drivers/sensor/microchip/mtch9010/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(mtch9010.c) diff --git a/drivers/sensor/microchip/mtch9010/Kconfig b/drivers/sensor/microchip/mtch9010/Kconfig new file mode 100644 index 0000000000000..44fd6f17c626f --- /dev/null +++ b/drivers/sensor/microchip/mtch9010/Kconfig @@ -0,0 +1,61 @@ +# Copyright (c) 2025 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +menuconfig MTCH9010 + bool "MTCH9010" + default y + depends on DT_HAS_MICROCHIP_MTCH9010_ENABLED + help + Configure the MTCH9010 driver + +if MTCH9010 + + config MTCH9010_RESET_ON_STARTUP + bool "Reset MTCH9010 on Startup" + default y + help + Resets the MTCH9010 before the driver configures it. + If disabled and the MTCH9010 was configured already, configuration will fail. + + config MTCH9010_LOCK_ON_STARTUP + bool "Save MTCH9010 Settings on Startup" + default n + help + If set, this option will assert SYS_LK after initialization. + The sensor will use these parameters if SYS_LK is asserted on the next POR. + + config MTCH9010_SAMPLE_DELAY_TIMEOUT_MS + int "Maximum wait time to get a sample (ms)" + default 500 + help + Sets the maximum amount of time in milliseconds to wait for a response from the MTCH9010 + when asking for a sample. Conductive mode is much faster than capacitive mode. + + config MTCH9010_OVERRIDE_DELAY_ENABLE + bool "Override Delay if not Wake on Request" + default y + help + If the MTCH9010 has a sleep period defined, the wake pin will not wake the device. + If this option is set, the API will allow for length + delays during fetch. Otherwise, the API will use the normal timeout, and likely fail. + + config MTCH9010_REFERENCE_AVERAGING_COUNT + int "Reference Averaging Count" + default 1 + range 1 64 + help + If the MTCH9010 is using the current measurement as its reference, then this + is the number of samples to average. + + config MTCH9010_HEARTBEAT_MONITORING_ENABLE + bool "Heartbeat Monitoring" + default y + help + Configures monitoring of the heartbeat output + + # Logging Settings + module = MTCH9010 + module-str = MTCH9010 + source "subsys/logging/Kconfig.template.log_config" + +endif diff --git a/drivers/sensor/microchip/mtch9010/mtch9010.c b/drivers/sensor/microchip/mtch9010/mtch9010.c new file mode 100644 index 0000000000000..e5ddd6d3f8c45 --- /dev/null +++ b/drivers/sensor/microchip/mtch9010/mtch9010.c @@ -0,0 +1,948 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT microchip_mtch9010 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtch9010_priv.h" + +LOG_MODULE_REGISTER(mtch9010); + +#define MTCH9010_INTERNAL_BUFFER_SIZE 64u + +/* Private Function Declarations */ +static int mtch9010_init(const struct device *dev); +static void mtch9010_verify_uart(const struct device *dev); +static int mtch9010_configure_device(const struct device *dev); +static int mtch9010_set_mode(const struct device *dev); +static int mtch9010_set_data_format(const struct device *dev); +static int mtch9010_set_reference(const struct device *dev, char *temp_buffer); +static int mtch9010_command_send(const struct device *dev, const char *str); +static int mtch9010_configure_gpio(const struct device *dev); +static int mtch9010_configure_int_gpio(const struct device *dev); +static int mtch9010_device_reset(const struct device *dev); +static int mtch9010_timeout_receive(const struct device *dev, char *buffer, uint8_t buffer_len, + uint16_t milliseconds); +static int mtch9010_lock_settings(const struct device *dev); + +/* Callbacks */ +static void mtch9010_heartbeat_callback(const struct device *dev, struct gpio_callback *cb, + uint32_t pins); + +/* API Functions */ +static int mtch9010_sample_fetch(const struct device *dev, enum sensor_channel chan); +static int mtch9010_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val); + +/* Private Function Implementations */ +static void mtch9010_verify_uart(const struct device *dev) +{ + const struct mtch9010_config *config = dev->config; + const struct device *uart_dev = config->uart_dev; + + /* Verify UART Setup */ + struct uart_config uart_cfg; + + if (uart_config_get(uart_dev, &uart_cfg) < 0) { + LOG_INST_WRN(config->log, "Failed to read UART Config; Settings were not verified"); + } else { + __ASSERT(uart_cfg.baudrate == MTCH9010_UART_BAUDRATE, + "Incorrect UART baudrate for MTCH9010"); + __ASSERT(uart_cfg.parity == MTCH9010_UART_PARITY, + "Incorrect UART parity for MTCH9010"); + __ASSERT(uart_cfg.stop_bits == MTCH9010_UART_STOP_BITS, + "Incorrect number of UART stop bits for MTCH9010"); + __ASSERT(uart_cfg.data_bits == MTCH9010_UART_DATA_BITS, + "Incorrect number of UART data bits for MTCH9010"); + } +} + +static int mtch9010_set_reference(const struct device *dev, char *temp_buffer) +{ + const struct mtch9010_config *config = dev->config; + struct mtch9010_data *data = dev->data; + + /* Convert the first value */ + struct mtch9010_result result = {0, 0, 0}; + + if (mtch9010_decode_char_buffer(temp_buffer, MTCH9010_OUTPUT_FORMAT_CURRENT, &result) < 0) { + LOG_INST_ERR(config->log, "Failed to decode reference value"); + return -EINVAL; + } + +#if (CONFIG_MTCH9010_REFERENCE_AVERAGING_COUNT == 1) + /* No Averaging */ + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_REF_MODE_CURRENT_VALUE) < 0) { + LOG_INST_ERR(config->log, "Failed to send reference mode command"); + return -EIO; + } + data->reference = result.measurement; +#else + uint32_t total = result.measurement; + + /* Average the value out */ + LOG_INST_DBG(config->log, "Averaging values"); + + uint8_t samples = 1; + + while (samples < CONFIG_MTCH9010_REFERENCE_AVERAGING_COUNT) { + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_REF_MODE_REPEAT_MEAS) < 0) { + LOG_INST_ERR(config->log, "Failed to send measurement repeat command"); + return -EIO; + } + + if (mtch9010_timeout_receive(dev, temp_buffer, sizeof(temp_buffer), + CONFIG_MTCH9010_SAMPLE_DELAY_TIMEOUT_MS) == 0) { + LOG_INST_ERR(config->log, "Reference value timed " + "out during averaging"); + temp_buffer[0] = '\0'; + return -EIO; + } + + if (mtch9010_decode_char_buffer(temp_buffer, MTCH9010_OUTPUT_FORMAT_CURRENT, + &result) < 0) { + LOG_INST_ERR(config->log, "Failed to decode reference value"); + return -EINVAL; + } + + total += result.measurement; + + samples++; + } + + /* Check to see if we received averages correctly */ + uint16_t calc_ref_val = lround(total / CONFIG_MTCH9010_REFERENCE_AVERAGING_COUNT); + + if (calc_ref_val > MTCH9010_MAX_RESULT) { + LOG_INST_ERR(config->log, "Computed reference out of range"); + return -EIO; + } + + /* Update the reference value */ + data->reference = calc_ref_val; + + LOG_INST_DBG(config->log, "Average reference value = %u", calc_ref_val); + + /* Set the average value as a custom value */ + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_REF_MODE_CUSTOM) < 0) { + LOG_INST_ERR(config->log, "Failed to send custom reference value " + "command (for averaging)"); + return -EIO; + } + + /* Send the computed reference */ + snprintf(temp_buffer, MTCH9010_INTERNAL_BUFFER_SIZE, "%u", data->reference); + if (mtch9010_command_send(dev, temp_buffer) < 0) { + LOG_INST_ERR(config->log, "Failed to send averaged reference value"); + return -EIO; + } +#endif /* CONFIG_MTCH9010_REFERENCE_AVERAGING_COUNT == 1*/ + + return 0; +} + +static int mtch9010_set_mode(const struct device *dev) +{ + const struct mtch9010_config *config = dev->config; + char *val_str; + + if (config->mode == MTCH9010_CAPACITIVE) { + val_str = MTCH9010_CMD_STR_CAPACITIVE_MODE; + } else if (config->mode == MTCH9010_CONDUCTIVE) { + val_str = MTCH9010_CMD_STR_CONDUCTIVE_MODE; + } else { + /* Should never get here */ + return -EINVAL; + } + + if (mtch9010_command_send(dev, val_str) < 0) { + return -EIO; + } + + return 0; +} + +static int mtch9010_set_data_format(const struct device *dev) +{ + const struct mtch9010_config *config = dev->config; + + /* Extended Mode Enable */ + if (config->extended_mode_enable) { + /* Extended Mode */ + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_EXTENDED_MODE_EN) < 0) { + return -EIO; + } + LOG_INST_DBG(config->log, "Extended mode is enabled"); + + if (config->format == MTCH9010_OUTPUT_FORMAT_DELTA) { + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_EXTENDED_FORMAT_DELTA) < + 0) { + return -EIO; + } + } else if (config->format == MTCH9010_OUTPUT_FORMAT_CURRENT) { + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_EXTENDED_FORMAT_CURRENT) < + 0) { + return -EIO; + } + } else if (config->format == MTCH9010_OUTPUT_FORMAT_BOTH) { + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_EXTENDED_FORMAT_BOTH) < 0) { + return -EIO; + } + } else if (config->format == MTCH9010_OUTPUT_FORMAT_MPLAB_DATA_VISUALIZER) { + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_EXTENDED_FORMAT_MPLAB_DV) < + 0) { + return -EIO; + } + } else { + return -EINVAL; + } + } else { + /* Extended Config Disabled */ + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_EXTENDED_MODE_DIS) < 0) { + return -EIO; + } + LOG_INST_DBG(config->log, "Extended mode is not enabled"); + } + + return 0; +} + +/* This function handles the UART init routine*/ +static int mtch9010_configure_device(const struct device *dev) +{ + const struct mtch9010_config *config = dev->config; + struct mtch9010_data *data = dev->data; + + /* Temp Char Buffer */ + char temp_buffer[MTCH9010_INTERNAL_BUFFER_SIZE]; + + /* Set the operating mode (capacitive or conductive) */ + if (mtch9010_set_mode(dev) < 0) { + LOG_INST_ERR(config->log, "Failed to set operating mode"); + return -EIO; + } + + /* Sleep Mode - enum matched to strings */ + snprintf(temp_buffer, sizeof(temp_buffer), "%d", config->sleep_time); + if (mtch9010_command_send(dev, temp_buffer) < 0) { + LOG_INST_ERR(config->log, "Failed to send sleep mode command"); + return -EIO; + } + + /* Configure UART Output formatting */ + if (mtch9010_set_data_format(dev) < 0) { + LOG_INST_ERR(config->log, "Failed to set output format"); + return -EIO; + } + + /* Now, the device will send it's electrode reference value */ + if (mtch9010_timeout_receive(dev, temp_buffer, sizeof(temp_buffer), + CONFIG_MTCH9010_SAMPLE_DELAY_TIMEOUT_MS) == 0) { + LOG_INST_ERR(config->log, "Reference value was not received"); + return -EIO; + } + + /* Reference Value */ + if (config->ref_mode == MTCH9010_REFERENCE_CURRENT_VALUE) { + + if (mtch9010_set_reference(dev, temp_buffer) < 0) { + LOG_INST_ERR(config->log, "Failed to set reference value"); + return -EIO; + } + } else if (config->ref_mode == MTCH9010_REFERENCE_CUSTOM_VALUE) { + /* The user manually set a value */ + if (mtch9010_command_send(dev, MTCH9010_CMD_STR_REF_MODE_CUSTOM) < 0) { + LOG_INST_ERR(config->log, "Failed to send custom reference value command"); + return -EIO; + } + snprintf(temp_buffer, MTCH9010_INTERNAL_BUFFER_SIZE, "%u", data->reference); + if (mtch9010_command_send(dev, temp_buffer) < 0) { + LOG_INST_ERR(config->log, "Failed to send custom reference value"); + return -EIO; + } + } else { + LOG_INST_ERR(config->log, "Invalid reference mode"); + return -EINVAL; + } + + /* Detection Threshold */ + snprintf(temp_buffer, MTCH9010_INTERNAL_BUFFER_SIZE, "%u", data->threshold); + if (mtch9010_command_send(dev, temp_buffer) < 0) { + LOG_INST_ERR(config->log, "Failed to send detection threshold value"); + return -EIO; + } + + mtch9010_lock_settings(dev); + + return 0; +} + +static int mtch9010_init(const struct device *dev) +{ + const struct mtch9010_config *config = dev->config; + struct mtch9010_data *data = dev->data; + const struct device *uart_dev = config->uart_dev; + + LOG_INST_DBG(config->log, "Starting device configuration"); + + /* Verify UART setup */ + mtch9010_verify_uart(dev); + + /* Configure heartbeat timing */ + k_sem_init(&data->heartbeat_sem, 0, 1); + + /* Configure device I/O, as needed */ + mtch9010_configure_gpio(dev); + + /* Configure INT I/O, as needed */ + mtch9010_configure_int_gpio(dev); + + /* Assert device reset, if set */ + mtch9010_device_reset(dev); + + /* Set the last heartbeat to post reset time */ + data->last_heartbeat = k_uptime_get(); + + /* Wait for boot-up */ + k_msleep(MTCH9010_BOOT_TIME_MS); + + if (config->uart_init) { + + if (!device_is_ready(uart_dev)) { + LOG_INST_ERR(config->log, "UART is not ready; Configuration skipped"); + return -EBUSY; + } + + int code = mtch9010_configure_device(dev); + + if (code < 0) { + return code; + } + } else { + LOG_INST_DBG(config->log, "UART setup not enabled"); + } + +#ifdef CONFIG_MTCH9010_OVERRIDE_DELAY_ENABLE + /* Print warning */ + if (config->sleep_time != 0) { + uint16_t timeout = + (config->sleep_time) * 1000 + CONFIG_MTCH9010_SAMPLE_DELAY_TIMEOUT_MS; + LOG_INST_WRN(config->log, "Device will wait up-to %u ms when fetching samples", + (timeout)); + } +#endif + + LOG_INST_DBG(config->log, "MTCH9010 configuration complete"); + + return 0; +} + +static int mtch9010_command_send(const struct device *dev, const char *str) +{ + const struct mtch9010_config *config = (const struct mtch9010_config *)dev->config; + const struct device *uart_dev = config->uart_dev; + + LOG_INST_INF(config->log, "\"%s\"", str); + + uint8_t len = strlen(str); + + for (uint8_t i = 0; i < len; i++) { + uart_poll_out(uart_dev, str[i]); + } + + uart_poll_out(uart_dev, MTCH9010_SUBMIT_CHAR); + + char temp_buffer[4] = {'\0', '\0', '\0', '\0'}; + + if (mtch9010_timeout_receive(dev, &temp_buffer[0], sizeof(temp_buffer), + MTCH9010_UART_COMMAND_TIMEOUT_MS) == 0) { + LOG_INST_ERR(config->log, "CMD Timeout waiting for response"); + return -EIO; + } + + if (temp_buffer[0] == MTCH9010_ACK_CHAR) { + LOG_INST_DBG(config->log, "ACK received"); + return 0; + } else if (temp_buffer[0] == MTCH9010_NACK_CHAR) { + LOG_INST_ERR(config->log, "NACK received from command"); + } else { + LOG_INST_ERR(config->log, "Unknown character received during setup"); + } + return -EIO; +} + +/* Returns the number of bytes received */ +static int mtch9010_timeout_receive(const struct device *dev, char *buffer, uint8_t buffer_len, + uint16_t milliseconds) +{ + const struct mtch9010_config *config = (const struct mtch9010_config *)dev->config; + const struct device *uart_dev = config->uart_dev; + + uint8_t count = 0; + k_timepoint_t end = sys_timepoint_calc(K_MSEC(milliseconds)); + + while ((count < (buffer_len - 1)) && (!sys_timepoint_expired(end))) { + int code = uart_poll_in(uart_dev, &buffer[count]); + + if (code == 0) { + count++; + if ((count >= 2) && (buffer[count - 1] == '\r') && + (buffer[count - 2] == '\n')) { + /* Found end of packet - exit early */ + buffer[count] = '\0'; + return count; + } + } + } + + buffer[count] = '\0'; + return count; +} + +static int mtch9010_configure_gpio(const struct device *dev) +{ + const struct mtch9010_config *config = (const struct mtch9010_config *)dev->config; + struct mtch9010_data *data = (struct mtch9010_data *)dev->data; + + /* Note: nRESET is handled in device reset function */ + int rtn = 0; + + /* UART EN (active LOW) */ + if (gpio_is_ready_dt(&config->enable_uart_gpio)) { + if (config->uart_init) { + gpio_pin_configure_dt(&config->enable_uart_gpio, + GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW); + } else { + gpio_pin_configure_dt(&config->enable_uart_gpio, + GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH); + } + } else { + LOG_INST_DBG(config->log, "UART EN line is not ready"); + } + + /* CFG EN */ + if (gpio_is_ready_dt(&config->enable_cfg_gpio)) { + if (config->extended_mode_enable) { + gpio_pin_configure_dt(&config->enable_cfg_gpio, + GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW); + } else { + gpio_pin_configure_dt(&config->enable_cfg_gpio, + GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH); + } + } else { + LOG_INST_DBG(config->log, "ECFG line is not ready"); + } + + /* OUT */ + if (gpio_is_ready_dt(&config->out_gpio)) { + /* Setup as Input */ + gpio_pin_configure_dt(&config->out_gpio, GPIO_INPUT); + data->last_out_state = gpio_pin_get_dt(&config->out_gpio); + } else { + LOG_INST_DBG(config->log, "OUT line is not ready"); + data->last_out_state = -EIO; + } + + /* SYSTEM LOCK */ + if (gpio_is_ready_dt(&config->lock_gpio)) { + /* Keep HIGH until ready to configure */ + gpio_pin_configure_dt(&config->lock_gpio, GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH); + } else { + LOG_INST_DBG(config->log, "System Lock line is not ready"); + } + + /* WAKE */ + if (gpio_is_ready_dt(&config->wake_gpio)) { + /* Keep HIGH until ready to assert WU / WAKE */ + gpio_pin_configure_dt(&config->wake_gpio, GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH); + } else { + LOG_INST_DBG(config->log, "Wake line is not ready."); + } + + /* MODE */ + if (gpio_is_ready_dt(&config->mode_gpio)) { + /* Keep HIGH until ready to assert */ + if (config->mode == MTCH9010_CAPACITIVE) { + gpio_pin_configure_dt(&config->mode_gpio, + GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH); + } else if (config->mode == MTCH9010_CONDUCTIVE) { + gpio_pin_configure_dt(&config->mode_gpio, + GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW); + } else { + LOG_INST_ERR(config->log, "Invalid operating mode specified"); + return -EINVAL; + } + } else { + LOG_INST_DBG(config->log, "Wake line is not ready."); + } + + return rtn; +} + +static int mtch9010_configure_int_gpio(const struct device *dev) +{ + const struct mtch9010_config *config = (const struct mtch9010_config *)dev->config; + struct mtch9010_data *data = (struct mtch9010_data *)dev->data; + + int rtn = 0; + + /* In some cases, data may not be used */ + (void)data; + + /* HEARTBEAT */ + if (gpio_is_ready_dt(&config->heartbeat_gpio)) { + gpio_pin_configure_dt(&config->heartbeat_gpio, GPIO_INPUT); +#ifdef CONFIG_MTCH9010_HEARTBEAT_MONITORING_ENABLE + gpio_init_callback(&data->heartbeat_cb, mtch9010_heartbeat_callback, + BIT(config->heartbeat_gpio.pin)); + rtn = gpio_add_callback_dt(&config->heartbeat_gpio, &data->heartbeat_cb); + if (rtn == 0) { + rtn = gpio_pin_interrupt_configure_dt(&config->heartbeat_gpio, + GPIO_INT_EDGE_RISING); + if (rtn < 0) { + LOG_INST_ERR(config->log, "Unable to configure interrupt; code %d", + rtn); + } else { + LOG_INST_DBG(config->log, "Configured Heartbeat Interrupt"); + } + } else { + LOG_INST_ERR(config->log, "Unable to add callback; code %d", rtn); + } +#endif + } else { + LOG_INST_DBG(config->log, "Heartbeat line is not ready."); + } + + return rtn; +} + +static int mtch9010_device_reset(const struct device *dev) +{ + const struct mtch9010_config *config = (const struct mtch9010_config *)dev->config; + +#ifndef CONFIG_MTCH9010_RESET_ON_STARTUP + LOG_INST_DBG(config->log, "MTCH9010 reset on startup is disabled."); + + return -ENOSYS; +#else + const struct gpio_dt_spec *reset_gpio_ptr = &config->reset_gpio; + + if (!gpio_is_ready_dt(reset_gpio_ptr)) { + LOG_INST_WRN(config->log, "Reset line is not ready. Reset was not performed."); + return -EBUSY; + } + + gpio_pin_configure_dt(reset_gpio_ptr, GPIO_OUTPUT_LOW); + + LOG_INST_DBG(config->log, "Resetting MTCH9010"); + k_msleep(MTCH9010_RESET_TIME_MS); + + gpio_pin_set_dt(reset_gpio_ptr, 1); + + return 0; +#endif +} + +int mtch9010_decode_char_buffer(const char *buffer, uint8_t format, struct mtch9010_result *result) +{ + if (result == NULL) { + return -EINVAL; + } + + /* Check to see if the first digit is valid */ + if (isdigit(buffer[0]) == 0) { + return -EINVAL; + } + + char *end_str_ptr = NULL; + long temp = 0; + + /* Ensure that these variables are always used*/ + (void)temp; + (void)end_str_ptr; + + switch (format) { + case MTCH9010_OUTPUT_FORMAT_CURRENT: + /* Data: \n\r */ + temp = strtol(buffer, &end_str_ptr, 10); + + if ((temp > MTCH9010_MAX_RESULT) || (temp < MTCH9010_MIN_RESULT)) { + return -EINVAL; + } + + result->prev_measurement = result->measurement; + result->measurement = (uint16_t)temp; + break; + case MTCH9010_OUTPUT_FORMAT_DELTA: + /* Data: \n\r */ + temp = strtol(buffer, &end_str_ptr, 10); + + /* Note: Delta can go negative */ + if ((temp > MTCH9010_MAX_RESULT) || (temp < -MTCH9010_MAX_RESULT)) { + return -EINVAL; + } + + result->delta = (int16_t)temp; + break; + case MTCH9010_OUTPUT_FORMAT_BOTH: + /* Data: \n\r */ + temp = strtol(buffer, &end_str_ptr, 10); + + if ((temp > MTCH9010_MAX_RESULT) || (temp < MTCH9010_MIN_RESULT)) { + return -EINVAL; + } + + result->prev_measurement = result->measurement; + result->measurement = (uint16_t)temp; + + if (*end_str_ptr == ' ') { + /* Increment string pointer to the next number */ + end_str_ptr++; + + temp = strtol(end_str_ptr, &end_str_ptr, 10); + + if ((temp > MTCH9010_MAX_RESULT) || (temp < -MTCH9010_MAX_RESULT)) { + return -EINVAL; + } + + result->delta = (int16_t)temp; + } else { + return -EINVAL; + } + + break; + case MTCH9010_OUTPUT_FORMAT_MPLAB_DATA_VISUALIZER: + /* Data: <~start> */ + return -ENOTSUP; + default: + return -EIO; + } + + /* Verify the \n\r is at the end */ + if ((*end_str_ptr == '\n') && (*(end_str_ptr + 1) == '\r')) { + return 0; + } + + return -EINVAL; +} + +static int mtch9010_lock_settings(const struct device *dev) +{ +#ifdef CONFIG_MTCH9010_LOCK_ON_STARTUP + const struct mtch9010_config *config = dev->config; + + if (config->lock_gpio.port == NULL) { + LOG_INST_ERR(config->log, "Lock line not ready"); + return -EIO; + } + + LOG_INST_INF(config->log, "Locking MTCH9010"); + + /* Set lock line */ + gpio_pin_set_dt(&config->lock_gpio, 0); +#endif + + return 0; +} + +static int mtch9010_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct mtch9010_config *config = dev->config; + struct mtch9010_data *data = dev->data; + + switch ((int)chan) { + case SENSOR_CHAN_MTCH9010_OUT_STATE: { + /* I/O output state - poll GPIO */ + + data->last_out_state = gpio_pin_get_dt(&config->out_gpio); + if (data->last_out_state < 0) { + LOG_ERR("GPIO Error %d", data->last_out_state); + return -EIO; + } + + return 0; + } + case SENSOR_CHAN_MTCH9010_REFERENCE_VALUE: { + /* Constant value - nothing to do */ + return 0; + } + case SENSOR_CHAN_MTCH9010_THRESHOLD_VALUE: { + /* Constant value - nothing to do */ + return 0; + } + case SENSOR_CHAN_ALL: + case SENSOR_CHAN_MTCH9010_SW_OUT_STATE: + case SENSOR_CHAN_MTCH9010_MEAS_RESULT: + case SENSOR_CHAN_MTCH9010_MEAS_DELTA: { + /* Get the Out State */ + + if (config->out_gpio.port != NULL) { + /* Hardware OUT State - SW OUT calculated elsewhere */ + data->last_out_state = gpio_pin_get_dt(&config->out_gpio); + } + + /* Check to see if >150 ms have passed */ + if (!sys_timepoint_expired(data->last_wake)) { + LOG_INST_ERR(config->log, "Insufficient time between wake provided"); + return -EBUSY; + } + + /* Blocking Wait for Sensor Data */ + uint16_t timeout = CONFIG_MTCH9010_SAMPLE_DELAY_TIMEOUT_MS; + + if (config->sleep_time != 0) { + +#ifdef CONFIG_MTCH9010_OVERRIDE_DELAY_ENABLE + timeout = (config->sleep_time) * 1000 + + CONFIG_MTCH9010_SAMPLE_DELAY_TIMEOUT_MS; +#else + LOG_INST_ERR(config->log, "Wake mode is disabled if sleep period is " + "defined. Use SENSOR_CHAN_MTCH9010_OUT_STATE or " + "set CONFIG_MTCH9010_OVERRIDE_DELAY_ENABLE."); + return -EBUSY; +#endif + } else { + const struct gpio_dt_spec *wake_gpio = &config->wake_gpio; + + if (!gpio_is_ready_dt(wake_gpio)) { + LOG_INST_ERR(config->log, "Wake GPIO is not ready"); + return -EIO; + } + + /* Wake is falling edge detected */ + gpio_pin_set_dt(wake_gpio, 0); + k_msleep(MTCH9010_WAKE_PULSE_WIDTH_MS); + gpio_pin_set_dt(wake_gpio, 1); + + /* Update last call */ + data->last_wake = sys_timepoint_calc(K_MSEC(MTCH9010_WAKE_TIME_BETWEEN_MS)); + } + + if (config->uart_dev == NULL) { + LOG_INST_ERR(config->log, "UART device is not ready"); + return -ENODEV; + } + + LOG_INST_DBG(config->log, "Fetching sample"); + char temp_buffer[MTCH9010_INTERNAL_BUFFER_SIZE]; + + int char_count = mtch9010_timeout_receive(dev, &temp_buffer[0], sizeof(temp_buffer), + timeout); + + if (char_count == 0) { + LOG_INST_ERR(config->log, "Unable to receive data during fetch."); + return -EIO; + } + + if (mtch9010_decode_char_buffer(&temp_buffer[0], config->format, + &data->last_result) < 0) { + LOG_INST_ERR(config->log, "Unable to decode result for channel %u", chan); + return -EINVAL; + } + } break; + case SENSOR_CHAN_MTCH9010_HEARTBEAT_ERROR_STATE: { + /* Returns true if the heartbeat is an error state */ +#ifdef CONFIG_MTCH9010_HEARTBEAT_MONITORING_ENABLE + if (k_sem_take(&data->heartbeat_sem, K_MSEC(MTCH9010_UART_COMMAND_TIMEOUT_MS)) < + 0) { + return -EBUSY; + } + + /* Compute the last access time */ + int64_t time_delta = k_uptime_delta(&data->last_heartbeat); + + k_sem_give(&data->heartbeat_sem); + + if (time_delta < MTCH9010_ERROR_PERIOD_MS) { + data->heartbeat_error_state = true; + } else { + data->heartbeat_error_state = false; + } + + return 0; +#else + return -ENOTSUP; +#endif + } + default: { + return -ENOTSUP; + } + } + + return 0; +} + +static int mtch9010_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct mtch9010_data *data = dev->data; + const struct mtch9010_config *config = dev->config; + + /* Val2 is not used */ + val->val2 = 0; + + switch ((int)chan) { + case SENSOR_CHAN_MTCH9010_OUT_STATE: { + /* I/O output state */ + val->val1 = data->last_out_state; + break; + } + case SENSOR_CHAN_MTCH9010_SW_OUT_STATE: { + /* Calculates if the OUT line would be asserted based on previous result */ + + if (config->format == MTCH9010_OUTPUT_FORMAT_DELTA) { + LOG_INST_ERR(config->log, "Cannot support SW decode in Delta mode"); + return -ENOTSUP; + } + + if ((data->last_result.measurement - data->reference) >= data->threshold) { + val->val1 = 1; + } else { + val->val1 = 0; + } + + break; + } + case SENSOR_CHAN_MTCH9010_REFERENCE_VALUE: { + /* Returns the reference value set for the sensor */ + val->val1 = data->reference; + break; + } + case SENSOR_CHAN_MTCH9010_THRESHOLD_VALUE: { + /* Returns the threshold value set for the sensor */ + val->val1 = data->threshold; + break; + } + case SENSOR_CHAN_MTCH9010_MEAS_RESULT: { + /* Returns the last measured result */ + val->val1 = data->last_result.measurement; + break; + } + case SENSOR_CHAN_MTCH9010_MEAS_DELTA: { + /* Returns the last delta */ + if ((config->format == MTCH9010_OUTPUT_FORMAT_DELTA) || + (config->format == MTCH9010_OUTPUT_FORMAT_BOTH)) { + val->val1 = data->last_result.delta; + } else if (config->format == MTCH9010_OUTPUT_FORMAT_CURRENT) { + /* Calculate delta from last measurement */ + val->val1 = + data->last_result.measurement - data->last_result.prev_measurement; + } else { + return -ENOTSUP; + } + + break; + } + case SENSOR_CHAN_MTCH9010_HEARTBEAT_ERROR_STATE: { + /* Returns true if the heartbeat is an error state */ + val->val1 = data->heartbeat_error_state; + break; + } + default: { + return -ENOTSUP; + } + } + + return 0; +} + +static void mtch9010_heartbeat_callback(const struct device *dev, struct gpio_callback *cb, + gpio_port_pins_t pins) +{ + (void)pins; + (void)dev; + + struct mtch9010_data *data = CONTAINER_OF(cb, struct mtch9010_data, heartbeat_cb); + + if (k_sem_take(&data->heartbeat_sem, K_NO_WAIT) == 0) { + data->last_heartbeat = k_uptime_get(); + k_sem_give(&data->heartbeat_sem); + } +} + +/* Sensor APIs */ +static DEVICE_API(sensor, mtch9010_api_funcs) = { + .sample_fetch = mtch9010_sample_fetch, + .channel_get = mtch9010_channel_get, +}; + +/* Device Init Macros */ +#define MTCH9010_OPERATING_MODE_INIT(inst) DT_STRING_UPPER_TOKEN(DT_DRV_INST(inst), operating_mode) + +#define MTCH9010_SLEEP_TIME_INIT(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, sleep_period),\ + (DT_INST_ENUM_IDX(inst, sleep_period)), (0)) + +#define MTCH9010_OUTPUT_MODE_INIT(inst) \ + COND_CODE_0(DT_INST_PROP_OR(inst, extended_output_enable, false),\ + (MTCH9010_OUTPUT_FORMAT_CURRENT),\ + (DT_STRING_UPPER_TOKEN(DT_DRV_INST(inst), extended_output_format))) + +#define MTCH9010_REF_MODE_INIT(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst,\ + reference_value),\ + (MTCH9010_REFERENCE_CUSTOM_VALUE), (MTCH9010_REFERENCE_CURRENT_VALUE)) + +#define MTCH9010_REF_VAL_INIT(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst,\ + reference_value),\ + (DT_INST_PROP(inst, reference_value)),\ + (0)) + +#define MTCH9010_USER_LABEL(inst) inst + +#define MTCH9010_DEFINE(inst) \ + LOG_INSTANCE_REGISTER(mtch9010, MTCH9010_USER_LABEL(inst), CONFIG_MTCH9010_LOG_LEVEL); \ + static struct mtch9010_data mtch9010_data_##inst = { \ + .reference = MTCH9010_REF_VAL_INIT(inst), \ + .threshold = DT_INST_PROP(inst, detect_value), \ + .heartbeat_error_state = false, \ + .last_wake = {0}, \ + .last_result = {0, 0, 0}, \ + }; \ + static const struct mtch9010_config mtch9010_config_##inst = { \ + .uart_init = DT_PROP(DT_DRV_INST(inst), uart_config_enable), \ + .uart_dev = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))), \ + .reset_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), reset_gpios, {0}), \ + .mode_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), mode_gpios, {0}), \ + .wake_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), wake_gpios, {0}), \ + .out_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), output_gpios, {0}), \ + .heartbeat_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), heartbeat_gpios, {0}), \ + .lock_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), system_lock_gpios, {0}), \ + .enable_uart_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), uart_en_gpios, {0}), \ + .enable_cfg_gpio = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(inst), cfg_en_gpios, {0}), \ + .mode = MTCH9010_OPERATING_MODE_INIT(inst), \ + .sleep_time = MTCH9010_SLEEP_TIME_INIT(inst), \ + .extended_mode_enable = DT_INST_PROP_OR(inst, extended_output_enable, false), \ + .format = MTCH9010_OUTPUT_MODE_INIT(inst), \ + .ref_mode = MTCH9010_REF_MODE_INIT(inst), \ + LOG_INSTANCE_PTR_INIT(log, mtch9010, MTCH9010_USER_LABEL(inst))}; \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, mtch9010_init, NULL, &mtch9010_data_##inst, \ + &mtch9010_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &mtch9010_api_funcs); + +DT_INST_FOREACH_STATUS_OKAY(MTCH9010_DEFINE) diff --git a/drivers/sensor/microchip/mtch9010/mtch9010_priv.h b/drivers/sensor/microchip/mtch9010/mtch9010_priv.h new file mode 100644 index 0000000000000..a6c3efc77ff45 --- /dev/null +++ b/drivers/sensor/microchip/mtch9010/mtch9010_priv.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_MICROCHIP_MTCH9010_PRIV_H_ +#define ZEPHYR_DRIVERS_SENSOR_MICROCHIP_MTCH9010_PRIV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Character to submit requests */ +#define MTCH9010_SUBMIT_CHAR '\r' + +/* Characters for determining if commands were accepted / rejected */ +#define MTCH9010_ACK_CHAR 0x06u +#define MTCH9010_NACK_CHAR 0x15u + +/* Command Strings w/o submit character */ +#define MTCH9010_CMD_STR_CAPACITIVE_MODE "0" +#define MTCH9010_CMD_STR_CONDUCTIVE_MODE "1" + +/* WOR - Wake on Request */ +#define MTCH9010_CMD_STR_SLEEP_TIME_WOR "0" +#define MTCH9010_CMD_STR_SLEEP_TIME_1S "1" +#define MTCH9010_CMD_STR_SLEEP_TIME_2S "2" +#define MTCH9010_CMD_STR_SLEEP_TIME_4S "3" +#define MTCH9010_CMD_STR_SLEEP_TIME_8S "4" +#define MTCH9010_CMD_STR_SLEEP_TIME_16S "5" +#define MTCH9010_CMD_STR_SLEEP_TIME_32S "6" +#define MTCH9010_CMD_STR_SLEEP_TIME_64S "7" +#define MTCH9010_CMD_STR_SLEEP_TIME_128S "8" +#define MTCH9010_CMD_STR_SLEEP_TIME_256S "9" + +#define MTCH9010_CMD_STR_EXTENDED_MODE_DIS "0" +#define MTCH9010_CMD_STR_EXTENDED_MODE_EN "1" + +#define MTCH9010_CMD_STR_EXTENDED_FORMAT_DELTA "0" +#define MTCH9010_CMD_STR_EXTENDED_FORMAT_CURRENT "1" +#define MTCH9010_CMD_STR_EXTENDED_FORMAT_BOTH "2" +#define MTCH9010_CMD_STR_EXTENDED_FORMAT_MPLAB_DV "3" + +#define MTCH9010_CMD_STR_REF_MODE_CURRENT_VALUE "0" +#define MTCH9010_CMD_STR_REF_MODE_REPEAT_MEAS "1" +#define MTCH9010_CMD_STR_REF_MODE_CUSTOM "2" + +/* Device Constants */ +#define MTCH9010_RESET_TIME_MS 10u +#define MTCH9010_BOOT_TIME_MS 10u +#define MTCH9010_UART_COMMAND_TIMEOUT_MS 20u +#define MTCH9010_WAKE_PULSE_WIDTH_MS 1u +#define MTCH9010_ERROR_PERIOD_MS 220u + +/* UART Constants */ +#define MTCH9010_UART_BAUDRATE 38400u +#define MTCH9010_UART_DATA_BITS UART_CFG_DATA_BITS_8 +#define MTCH9010_UART_PARITY UART_CFG_PARITY_NONE +#define MTCH9010_UART_STOP_BITS UART_CFG_STOP_BITS_1 + +/* Time between wake requests */ +#define MTCH9010_WAKE_TIME_BETWEEN_MS 150u + +struct mtch9010_result { + /* Received Reference Value */ + uint16_t measurement; + /* Last Measurement Value */ + uint16_t prev_measurement; + /* Received Delta Value */ + int16_t delta; +}; + +enum mtch9010_reference_value_init { + /* MTCH9010 sets the current value as the reference value */ + MTCH9010_REFERENCE_CURRENT_VALUE = 0, + /* MTCH9010 re-runs the measurement */ + MTCH9010_REFERENCE_RERUN_VALUE, + /* MTCH9010 sets the reference to the value the user defines */ + MTCH9010_REFERENCE_CUSTOM_VALUE +}; + +struct mtch9010_data { + /* Threshold of the Sensor */ + uint16_t threshold; + /* Reference (Dry) value of the Sensor */ + uint16_t reference; + /* Last time we asked for data */ + k_timepoint_t last_wake; + /* Last time a heartbeat was detected */ + int64_t last_heartbeat; + /* Semaphore for accessing heartbeat */ + struct k_sem heartbeat_sem; + /* Heartbeat GPIO Callback */ + struct gpio_callback heartbeat_cb; + /* Last state of the OUT pin */ + int last_out_state; + /* If true, the heartbeat is sending the error pattern */ + bool heartbeat_error_state; + /* Last result received from the sensor */ + struct mtch9010_result last_result; +}; + +struct mtch9010_config { + /* Set to true if the init function should configure the device */ + bool uart_init; + /* Pointer to UART Bus */ + const struct device *uart_dev; + /* OP_MODE Signal for I/O mode */ + const struct gpio_dt_spec mode_gpio; + /* nRESET Signal for MTCH9010 */ + const struct gpio_dt_spec reset_gpio; + /* Wake-Up (WU) Signal for MTCH9010 */ + const struct gpio_dt_spec wake_gpio; + /* OUT Signal for MTCH9010 */ + const struct gpio_dt_spec out_gpio; + /* SYS_LK Signal to Program Startup Settings */ + const struct gpio_dt_spec lock_gpio; + /* nUART_EN Signal to Enable UART Communication */ + const struct gpio_dt_spec enable_uart_gpio; + /* nCFG_EN Signal for I/O Mode*/ + const struct gpio_dt_spec enable_cfg_gpio; + /* Heartbeat (HB) Output of MTCH9010 */ + const struct gpio_dt_spec heartbeat_gpio; + /* Operating mode (Capacitive / Conductive) */ + uint8_t mode; + /* Sleep Time of device in seconds. Set to 0 for Wake on Request */ + int sleep_time; + /* Set to true if extended format output is configured */ + bool extended_mode_enable; + /* Format of the UART Output Data */ + uint8_t format; + /* Initialization mode of the reference */ + enum mtch9010_reference_value_init ref_mode; + /* Logging Instance */ + LOG_INSTANCE_PTR_DECLARE(log); +}; + +/* Helper function to decode a NULL-terminated string packet */ +int mtch9010_decode_char_buffer(const char *buffer, uint8_t format, + struct mtch9010_result *result); + +#endif /* ZEPHYR_DRIVERS_SENSOR_MICROCHIP_MTCH9010_PRIV_H_ */ diff --git a/dts/bindings/sensor/microchip,mtch9010.yaml b/dts/bindings/sensor/microchip,mtch9010.yaml new file mode 100644 index 0000000000000..1f72de98af7fa --- /dev/null +++ b/dts/bindings/sensor/microchip,mtch9010.yaml @@ -0,0 +1,127 @@ +# Copyright (c) 2025 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +title: MTCH9010 Liquid Detector + +description: | + Microchip MTCH9010 liquid leak detector + + - Detects liquid leaks using capacitance or conductance + - Interrupt driven or UART output + + - For more information: + - https://www.microchip.com/en-us/product/mtch9010 + +compatible: "microchip,mtch9010" + +include: [sensor-device.yaml, uart-device.yaml] + +properties: + operating-mode: + type: string + required: true + description: | + Sets moisture detection mode (capacitive or conductive) + Keep in sync with dt-bindings/sensor/mtch9010.h + enum: + - "MTCH9010_CAPACITIVE" + - "MTCH9010_CONDUCTIVE" + + sleep-period: + type: int + description: | + Amount of time (in seconds) the sensor will sleep between each check. + Leave undefined or at 0 to sleep until wake. + enum: + - 0 + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + - 64 + - 128 + - 256 + + uart-config-enable: + type: boolean + description: | + If set, the MTCH9010 is configured over the UART Interface. + To be disabled if the sensor is configured through its + I/O or a start-up configuration was previously defined. + + extended-output-enable: + type: boolean + description: | + If set, the output format of the MTCH9010 can be changed. + If not set, the MTCH9010 uses the default formatting. + + extended-output-format: + type: string + description: | + Output data formatting configuration. + enum: + - "MTCH9010_OUTPUT_FORMAT_DELTA" + - "MTCH9010_OUTPUT_FORMAT_CURRENT" + - "MTCH9010_OUTPUT_FORMAT_BOTH" + - "MTCH9010_OUTPUT_FORMAT_MPLAB_DATA_VISUALIZER" + + reference-value: + type: int + description: | + Set the reference "dry" value. + If not defined, the POR value of the sensor is used. + KConfig symbol MTCH9010_REFERENCE_AVERAGING_COUNT + can be used to setup averaging of this value. + + detect-value: + type: int + required: true + description: | + Set the detection threshold value. + + output-gpios: + type: phandle-array + description: Output Status Line for the MTCH9010 + + system-lock-gpios: + type: phandle-array + description: | + System lock I/O for the MTCH9010 + Unused if SYSTEM LOCK is not used. + + mode-gpios: + type: phandle-array + description: | + Mode select I/O for the MTCH9010 + Unused if device is configured with UART or connected externally. + + reset-gpios: + type: phandle-array + description: | + Reset I/O for MTCH9010 + Pulled low to reset the device. + + wake-gpios: + type: phandle-array + description: | + Wake I/O for the MTCH9010 + Not used if a sleep period is defined. + + uart-en-gpios: + type: phandle-array + description: | + UART enable line for the MTCH9010 + Unused if the UART EN pin is connected externally. + + cfg-en-gpios: + type: phandle-array + description: | + Enhanced configuration enable for the MTCH9010 + Unused if device is configured with UART or connected externally. + + heartbeat-gpios: + type: phandle-array + description: | + Heartbeat output of MTCH9010 diff --git a/include/zephyr/drivers/sensor/mtch9010.h b/include/zephyr/drivers/sensor/mtch9010.h new file mode 100644 index 0000000000000..787af3aefd6e5 --- /dev/null +++ b/include/zephyr/drivers/sensor/mtch9010.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_MTCH9010_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_MTCH9010_H_ + +#include + +#include +#include +#include +#include +#include + +#define MTCH9010_MAX_RESULT 65535 +#define MTCH9010_MIN_RESULT 0 + +enum sensor_channel_mtch9010 { + /* Polls the state of the OUT line */ + SENSOR_CHAN_MTCH9010_OUT_STATE = SENSOR_CHAN_PRIV_START, + /* Calculates if the OUT line would be asserted based on previous result */ + SENSOR_CHAN_MTCH9010_SW_OUT_STATE, + /* Returns the reference value set for the sensor */ + SENSOR_CHAN_MTCH9010_REFERENCE_VALUE, + /* Returns the threshold value set for the sensor */ + SENSOR_CHAN_MTCH9010_THRESHOLD_VALUE, + /* Returns the last measured result */ + SENSOR_CHAN_MTCH9010_MEAS_RESULT, + /* Returns the last measured result */ + SENSOR_CHAN_MTCH9010_MEAS_DELTA, + /* Returns true if the heartbeat is an error state */ + SENSOR_CHAN_MTCH9010_HEARTBEAT_ERROR_STATE +}; + +#endif diff --git a/include/zephyr/dt-bindings/sensor/mtch9010.h b/include/zephyr/dt-bindings/sensor/mtch9010.h new file mode 100644 index 0000000000000..678c40aa0f180 --- /dev/null +++ b/include/zephyr/dt-bindings/sensor/mtch9010.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_MTCH9010_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_MTCH9010_H_ + +/* Operating Mode */ +#define MTCH9010_CAPACITIVE 0x0 +#define MTCH9010_CONDUCTIVE 0x1 + +/* Output UART Data Formats */ +#define MTCH9010_OUTPUT_FORMAT_DELTA 0x0 +#define MTCH9010_OUTPUT_FORMAT_CURRENT 0x1 +#define MTCH9010_OUTPUT_FORMAT_BOTH 0x2 +#define MTCH9010_OUTPUT_FORMAT_MPLAB_DATA_VISUALIZER 0x3 + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_MTCH9010_H_ */ diff --git a/tests/drivers/sensor/mtch9010/CMakeLists.txt b/tests/drivers/sensor/mtch9010/CMakeLists.txt new file mode 100644 index 0000000000000..56608a1f75707 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(device) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) + +zephyr_include_directories(${ZEPHYR_BASE}/drivers/sensor/microchip/mtch9010) +zephyr_include_directories(./src) diff --git a/tests/drivers/sensor/mtch9010/prj.conf b/tests/drivers/sensor/mtch9010/prj.conf new file mode 100644 index 0000000000000..dfcf3ee5fec0a --- /dev/null +++ b/tests/drivers/sensor/mtch9010/prj.conf @@ -0,0 +1,13 @@ +CONFIG_ZTEST=y +CONFIG_GPIO=y +CONFIG_SERIAL=y + +# Device +CONFIG_SENSOR=y +CONFIG_MTCH9010=y + +# Emulation Settings +CONFIG_EMUL=y +CONFIG_GPIO_EMUL=y +CONFIG_UART_EMUL=y +CONFIG_LOG=y diff --git a/tests/drivers/sensor/mtch9010/src/main.c b/tests/drivers/sensor/mtch9010/src/main.c new file mode 100644 index 0000000000000..f24238776c00f --- /dev/null +++ b/tests/drivers/sensor/mtch9010/src/main.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../drivers/sensor/microchip/mtch9010/mtch9010_priv.h" +#include "zephyr/drivers/sensor/mtch9010.h" + +#define DUT_NODE DT_NODELABEL(dut) + +ZTEST_SUITE(mtch9010_utility, NULL, NULL, NULL, NULL, NULL); + +ZTEST(mtch9010_utility, test_result_decode) +{ + /* Basic Decode Tests */ + const char *test_pattern_1 = "12345\n\r"; + const char *test_pattern_2 = "10\n\r"; + const char *test_pattern_3 = "999 12405\n\r"; + const char *test_pattern_4 = "0 1234\n\r"; + const char *test_pattern_5 = "100 -99\n\r"; + + /* Bad Decodes */ + const char *bad_decode_pattern_1 = "10\n\r"; + const char *bad_decode_pattern_2 = "655636\n\r"; + const char *bad_decode_pattern_3 = "-100\n\r"; + const char *bad_decode_pattern_4 = "100"; + const char *bad_decode_pattern_5 = "100\t\n"; + const char *bad_decode_pattern_6 = "a100\n\r"; + + struct mtch9010_result test_result; + + /* Test Current decode */ + int ret = mtch9010_decode_char_buffer(test_pattern_1, MTCH9010_OUTPUT_FORMAT_CURRENT, + &test_result); + + zassert_equal(ret, 0, "Unable to decode test_pattern_1"); + zassert_equal(test_result.measurement, 12345, "Decoded value does not match expected"); + + /* Test DELTA decode */ + ret = mtch9010_decode_char_buffer(test_pattern_2, MTCH9010_OUTPUT_FORMAT_DELTA, + &test_result); + + zassert_equal(ret, 0, "Unable to decode test_pattern_2"); + zassert_equal(test_result.delta, 10, "Decoded value does not match expected"); + + /* Test Current and Delta decode */ + ret = mtch9010_decode_char_buffer(test_pattern_3, MTCH9010_OUTPUT_FORMAT_BOTH, + &test_result); + + zassert_equal(ret, 0, "Unable to decode test_pattern_3"); + zassert_equal(test_result.prev_measurement, 12345, + "Previous value does not match expected"); + zassert_equal(test_result.measurement, 999, "Decoded value does not match expected"); + zassert_equal(test_result.delta, 12405, "Decoded value does not match expected"); + + /* Test MPLAB Data Visualizer Format (should fail) */ + ret = mtch9010_decode_char_buffer( + test_pattern_4, MTCH9010_OUTPUT_FORMAT_MPLAB_DATA_VISUALIZER, &test_result); + zassert_equal(ret, -ENOTSUP, "Incorrectly decoded test_pattern_4"); + + /* Test Negative Delta */ + ret = mtch9010_decode_char_buffer(test_pattern_5, MTCH9010_OUTPUT_FORMAT_BOTH, + &test_result); + zassert_equal(ret, 0, "Unable to decode test_pattern_5"); + zassert_equal(test_result.measurement, 100, "Decoded value does not match expected"); + zassert_equal(test_result.delta, -99, "Decoded value does not match expected"); + + /* Test Bad Decode 1 - Incorrect format */ + ret = mtch9010_decode_char_buffer(bad_decode_pattern_1, MTCH9010_OUTPUT_FORMAT_BOTH, + &test_result); + zassert_equal(ret, -EINVAL, "Incorrectly decoded bad_decode_pattern_1"); + + /* Test Bad Decode 2 - UINT16 Buffer Overflow */ + ret = mtch9010_decode_char_buffer(bad_decode_pattern_2, MTCH9010_OUTPUT_FORMAT_CURRENT, + &test_result); + zassert_equal(ret, -EINVAL, "Incorrectly decoded bad_decode_pattern_2"); + + /* Test Bad Decode 3 - Negative Values */ + ret = mtch9010_decode_char_buffer(bad_decode_pattern_3, MTCH9010_OUTPUT_FORMAT_CURRENT, + &test_result); + zassert_equal(ret, -EINVAL, "Incorrectly decoded bad_decode_pattern_3"); + + /* Test Bad Decode 4 - Missing Return */ + ret = mtch9010_decode_char_buffer(bad_decode_pattern_4, MTCH9010_OUTPUT_FORMAT_CURRENT, + &test_result); + zassert_equal(ret, -EINVAL, "Incorrectly decoded bad_decode_pattern_4"); + + /* Test Bad Decode 5 - Invalid Return */ + ret = mtch9010_decode_char_buffer(bad_decode_pattern_5, MTCH9010_OUTPUT_FORMAT_CURRENT, + &test_result); + zassert_equal(ret, -EINVAL, "Incorrectly decoded bad_decode_pattern_5"); + + /* Test Bad Decode 6 - Invalid Starting Character */ + ret = mtch9010_decode_char_buffer(bad_decode_pattern_6, MTCH9010_OUTPUT_FORMAT_CURRENT, + &test_result); + zassert_equal(ret, -EINVAL, "Incorrectly decoded bad_decode_pattern_6"); +} + +struct mtch9010_config_fixture { + const struct device *dev; +}; + +static void *mtch9010_setup(void) +{ + static struct mtch9010_config_fixture fixture = { + .dev = DEVICE_DT_GET(DUT_NODE), + }; + + /* Verify we found a device */ + zassert_not_null(fixture.dev); + + /* Create the reference configuration */ + return &fixture; +} + +ZTEST_SUITE(mtch9010_config, NULL, mtch9010_setup, NULL, NULL, NULL); + +/* Check UART */ +ZTEST_F(mtch9010_config, test_uart_init) +{ + const struct mtch9010_config *config = fixture->dev->config; + + /* Verify the boolean flag */ + if (config->uart_init) { + zassert_true(DT_PROP_OR(DUT_NODE, mtch9010_uart_config_enable, false), + "UART Init was enabled, but was not set"); + } else { + zassert_false(DT_PROP_OR(DUT_NODE, mtch9010_uart_config_enable, false), + "UART Init was disabled, but was set"); + } + + /* Verify the UART Bus Pointer */ + const struct device *bus = DEVICE_DT_GET_OR_NULL(DT_BUS(DUT_NODE)); + + zassert_equal_ptr(bus, config->uart_dev, "UART Bus is not correctly assigned"); +} + +/* Check GPIO Assignments */ +ZTEST_F(mtch9010_config, test_gpio_bindings) +{ + const struct mtch9010_config *config = fixture->dev->config; + + /* GPIOs to Test */ + const struct gpio_dt_spec mode_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_mode_gpios, {0}); + const struct gpio_dt_spec output_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_output_gpios, {0}); + const struct gpio_dt_spec lock_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_system_lock_gpios, {0}); + const struct gpio_dt_spec reset_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_reset_gpios, {0}); + const struct gpio_dt_spec wake_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_wake_gpios, {0}); + const struct gpio_dt_spec uart_en_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_uart_en_gpios, {0}); + const struct gpio_dt_spec cfg_en_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_cfg_en_gpios, {0}); + const struct gpio_dt_spec heartbeat_gpio = + GPIO_DT_SPEC_GET_OR(DUT_NODE, mtch9010_heartbeat_gpios, {0}); + + if (mode_gpio.port != NULL) { + zassert_not_null(config->mode_gpio.port, "mode_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->mode_gpio.port, + "mode_gpio is not NULL, but was not assigned"); + } + + if (output_gpio.port != NULL) { + zassert_not_null(config->out_gpio.port, "output_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->out_gpio.port, + "output_gpio is not NULL, but was not assigned"); + } + + if (lock_gpio.port != NULL) { + zassert_not_null(config->lock_gpio.port, "lock_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->lock_gpio.port, + "lock_gpio is not NULL, but was not assigned"); + } + + if (reset_gpio.port != NULL) { + zassert_not_null(config->reset_gpio.port, "reset_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->reset_gpio.port, + "reset_gpio is not NULL, but was not assigned"); + } + + if (wake_gpio.port != NULL) { + zassert_not_null(config->wake_gpio.port, "wake_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->wake_gpio.port, + "wake_gpio is not NULL, but was not assigned"); + } + + if (uart_en_gpio.port != NULL) { + zassert_not_null(config->enable_uart_gpio.port, + "uart_en_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->enable_uart_gpio.port, + "uart_en_gpio is not NULL, but was not assigned"); + } + + if (cfg_en_gpio.port != NULL) { + zassert_not_null(config->enable_cfg_gpio.port, + "cfg_en_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->enable_cfg_gpio.port, + "cfg_en_gpio is not NULL, but was not assigned"); + } + + if (heartbeat_gpio.port != NULL) { + zassert_not_null(config->heartbeat_gpio.port, + "heartbeat_gpio is NULL, but was assigned"); + } else { + zassert_is_null(config->heartbeat_gpio.port, + "heartbeat_gpio is not NULL, but was not assigned"); + } +} + +ZTEST_F(mtch9010_config, test_sleep_time) +{ + const struct mtch9010_config *config = fixture->dev->config; + + zassert((config->sleep_time == DT_PROP_OR(DUT_NODE, mtch9010_sleep_period, 0)), + "sleepTime was not correctly assigned."); +} + +ZTEST_F(mtch9010_config, test_output_format) +{ + const struct mtch9010_config *config = fixture->dev->config; + + if (config->extended_mode_enable) { + zassert_true(DT_PROP_OR(DUT_NODE, extended_output_enable, false), + "Extended output was disabled, but was set"); + } else { + zassert_false(DT_PROP_OR(DUT_NODE, extended_output_enable, false), + "Extended output was enabled, but not set"); + + zassert_true((config->format == MTCH9010_OUTPUT_FORMAT_CURRENT), + "Current output format was not correctly implied"); + } +} + +ZTEST_F(mtch9010_config, test_custom_value) +{ + const struct mtch9010_config *config = fixture->dev->config; + const struct mtch9010_data *data = fixture->dev->data; + int custom_value = DT_PROP_OR(DUT_NODE, reference_value, -1); + + switch (config->ref_mode) { + case MTCH9010_REFERENCE_CURRENT_VALUE: { + zassert_equal(custom_value, -1, "Incorrect reference initialization mode set"); + break; + } + case MTCH9010_REFERENCE_CUSTOM_VALUE: { + zassert_not_equal(custom_value, -1, "Incorrect reference initialization mode set"); + zassert_equal(custom_value, data->reference, + "Reference value was not set to custom value"); + break; + } + case MTCH9010_REFERENCE_RERUN_VALUE: { + zassert_unreachable("Illegal reference value mode set"); + break; + } + default: { + zassert_unreachable("Unknown Reference Value set"); + break; + } + } +} + +ZTEST_F(mtch9010_config, test_threshold_value) +{ + const struct mtch9010_data *data = fixture->dev->data; + int custom_value = DT_PROP(DUT_NODE, detect_value); + + zassert_equal(data->threshold, custom_value, "Threshold value was not set to custom value"); +} diff --git a/tests/drivers/sensor/mtch9010/test_configs/data_format/both.overlay b/tests/drivers/sensor/mtch9010/test_configs/data_format/both.overlay new file mode 100644 index 0000000000000..df9a0640f9a40 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/data_format/both.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CONDUCTIVE"; + extended-output-enable; + extended-output-format = "MTCH9010_OUTPUT_FORMAT_BOTH"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/test_configs/data_format/current.overlay b/tests/drivers/sensor/mtch9010/test_configs/data_format/current.overlay new file mode 100644 index 0000000000000..40a34497a6344 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/data_format/current.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CAPACITIVE"; + extended-output-enable; + extended-output-format = "MTCH9010_OUTPUT_FORMAT_CURRENT"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/test_configs/data_format/data_visualizer.overlay b/tests/drivers/sensor/mtch9010/test_configs/data_format/data_visualizer.overlay new file mode 100644 index 0000000000000..90d4d88a95546 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/data_format/data_visualizer.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CONDUCTIVE"; + extended-output-enable; + extended-output-format = "MTCH9010_OUTPUT_FORMAT_MPLAB_DATA_VISUALIZER"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/test_configs/data_format/delta.overlay b/tests/drivers/sensor/mtch9010/test_configs/data_format/delta.overlay new file mode 100644 index 0000000000000..e72caaec2cb12 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/data_format/delta.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CAPACITIVE"; + extended-output-enable; + extended-output-format = "MTCH9010_OUTPUT_FORMAT_DELTA"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/test_configs/data_format/unspecified.overlay b/tests/drivers/sensor/mtch9010/test_configs/data_format/unspecified.overlay new file mode 100644 index 0000000000000..a642a85e31743 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/data_format/unspecified.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CONDUCTIVE"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/test_configs/operating_mode/capacitive.overlay b/tests/drivers/sensor/mtch9010/test_configs/operating_mode/capacitive.overlay new file mode 100644 index 0000000000000..e72caaec2cb12 --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/operating_mode/capacitive.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CAPACITIVE"; + extended-output-enable; + extended-output-format = "MTCH9010_OUTPUT_FORMAT_DELTA"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/test_configs/operating_mode/conductive.overlay b/tests/drivers/sensor/mtch9010/test_configs/operating_mode/conductive.overlay new file mode 100644 index 0000000000000..cf971aaf9e5fa --- /dev/null +++ b/tests/drivers/sensor/mtch9010/test_configs/operating_mode/conductive.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ +&uart1 { + status = "okay"; + current-speed = <38400>; + + dut: mtch9010 { + status = "okay"; + compatible = "microchip,mtch9010"; + + /* Device Configuration */ + operating-mode = "MTCH9010_CONDUCTIVE"; + extended-output-enable; + extended-output-format = "MTCH9010_OUTPUT_FORMAT_DELTA"; + reference-value = <0>; + detect-value = <600>; + }; +}; diff --git a/tests/drivers/sensor/mtch9010/testcase.yaml b/tests/drivers/sensor/mtch9010/testcase.yaml new file mode 100644 index 0000000000000..47ffb071847ad --- /dev/null +++ b/tests/drivers/sensor/mtch9010/testcase.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2025 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +common: + tags: + - drivers + - sensor + - subsys + platform_allow: + - native_sim +tests: + drivers.sensor.mtch9010_capacitive: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/operating_mode/capacitive.overlay + drivers.sensor.mtch9010_conductive: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/operating_mode/conductive.overlay + drivers.sensor.mtch9010_current_output: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/data_format/current.overlay + drivers.sensor.mtch9010_delta_output: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/data_format/delta.overlay + drivers.sensor.mtch9010_both_outputs: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/data_format/both.overlay + drivers.sensor.mtch9010_data_visualizer_output: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/data_format/data_visualizer.overlay + drivers.sensor.mtch9010_unspecified_output: + extra_args: EXTRA_DTC_OVERLAY_FILE=./test_configs/data_format/unspecified.overlay