diff --git a/drivers/sensor/wsen/CMakeLists.txt b/drivers/sensor/wsen/CMakeLists.txt index 9069a6d4c439f..856e5ff496479 100644 --- a/drivers/sensor/wsen/CMakeLists.txt +++ b/drivers/sensor/wsen/CMakeLists.txt @@ -4,6 +4,7 @@ # zephyr-keep-sorted-start add_subdirectory_ifdef(CONFIG_WSEN_HIDS_2525020210002 wsen_hids_2525020210002) +add_subdirectory_ifdef(CONFIG_WSEN_ISDS_2536030320001 wsen_isds_2536030320001) add_subdirectory_ifdef(CONFIG_WSEN_ITDS_2533020201601 wsen_itds_2533020201601) add_subdirectory_ifdef(CONFIG_WSEN_PADS_2511020213301 wsen_pads_2511020213301) add_subdirectory_ifdef(CONFIG_WSEN_PDUS_25131308XXXXX wsen_pdus_25131308XXXXX) diff --git a/drivers/sensor/wsen/Kconfig b/drivers/sensor/wsen/Kconfig index b4c9c0523fc79..5b01259aa7443 100644 --- a/drivers/sensor/wsen/Kconfig +++ b/drivers/sensor/wsen/Kconfig @@ -4,6 +4,7 @@ # zephyr-keep-sorted-start source "drivers/sensor/wsen/wsen_hids_2525020210002/Kconfig" +source "drivers/sensor/wsen/wsen_isds_2536030320001/Kconfig" source "drivers/sensor/wsen/wsen_itds_2533020201601/Kconfig" source "drivers/sensor/wsen/wsen_pads_2511020213301/Kconfig" source "drivers/sensor/wsen/wsen_pdus_25131308XXXXX/Kconfig" diff --git a/drivers/sensor/wsen/wsen_isds_2536030320001/CMakeLists.txt b/drivers/sensor/wsen/wsen_isds_2536030320001/CMakeLists.txt new file mode 100644 index 0000000000000..cbd280302b29f --- /dev/null +++ b/drivers/sensor/wsen/wsen_isds_2536030320001/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(wsen_isds_2536030320001.c) +zephyr_library_sources_ifdef(CONFIG_WSEN_ISDS_2536030320001_TRIGGER wsen_isds_2536030320001_trigger.c) diff --git a/drivers/sensor/wsen/wsen_isds_2536030320001/Kconfig b/drivers/sensor/wsen/wsen_isds_2536030320001/Kconfig new file mode 100644 index 0000000000000..d009210caa6b7 --- /dev/null +++ b/drivers/sensor/wsen/wsen_isds_2536030320001/Kconfig @@ -0,0 +1,98 @@ +# Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG +# SPDX-License-Identifier: Apache-2.0 + +menuconfig WSEN_ISDS_2536030320001 + bool "WSEN-ISDS 3D accelerometer and 3D gyroscope sensor" + default y + depends on DT_HAS_WE_WSEN_ISDS_2536030320001_ENABLED + select I2C if $(dt_compat_on_bus,$(DT_COMPAT_WE_WSEN_ISDS_2536030320001),i2c) + select SPI if $(dt_compat_on_bus,$(DT_COMPAT_WE_WSEN_ISDS_2536030320001),spi) + select HAS_WESENSORS + help + Enable driver for the WSEN-ISDS I2C/SPI-based 3D accelerometer and 3D gyroscope sensor + with integrated temperature sensor. + +if WSEN_ISDS_2536030320001 + +config WSEN_ISDS_2536030320001_DISABLE_ACCEL_HIGH_PERFORMANCE_MODE + bool "Disable accelerometer high performance mode" + help + Disables accelerometer high performance mode. If high performance mode is disabled, + the ODR is used to switch between power modes as follows: + - 1.6 Hz - 52 Hz Low power mode + - 104 Hz - 208 Hz Normal power mode + - 416 Hz - 6.66 kHz High performance mode + +config WSEN_ISDS_2536030320001_DISABLE_GYRO_HIGH_PERFORMANCE_MODE + bool "Disable gyroscope high performance mode" + help + Disables gyroscope high performance mode. If high performance mode is disabled, + the ODR is used to switch between power modes as follows: + - 12.5 Hz - 52 Hz Low power mode + - 104 Hz - 208 Hz Normal power mode + - 416 Hz - 6.66 kHz High performance mode + +choice WSEN_ISDS_2536030320001_TRIGGER_MODE + prompt "Trigger mode" + default WSEN_ISDS_2536030320001_TRIGGER_NONE + help + Specify the type of triggering to be used by the driver. + +config WSEN_ISDS_2536030320001_TRIGGER_NONE + bool "No trigger" + +config WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select WSEN_ISDS_2536030320001_TRIGGER + +config WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select WSEN_ISDS_2536030320001_TRIGGER + +endchoice # WSEN_ISDS_2536030320001_TRIGGER_MODE + +config WSEN_ISDS_2536030320001_TRIGGER + bool + +config WSEN_ISDS_2536030320001_EVENTS + bool + +config WSEN_ISDS_2536030320001_THREAD_PRIORITY + int "Thread priority" + depends on WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config WSEN_ISDS_2536030320001_THREAD_STACK_SIZE + int "Thread stack size" + depends on WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +config WSEN_ISDS_2536030320001_TAP + bool "Tap and double tap detection" + depends on WSEN_ISDS_2536030320001_TRIGGER + select WSEN_ISDS_2536030320001_EVENTS + help + Enable tap (single/double) detection + Note that the recommended ODRs for tap recognition are 416 Hz and 833 Hz. + +config WSEN_ISDS_2536030320001_FREEFALL + bool "Free-fall detection" + depends on WSEN_ISDS_2536030320001_TRIGGER + select WSEN_ISDS_2536030320001_EVENTS + help + Enable free-fall detection + +config WSEN_ISDS_2536030320001_DELTA + bool "Wake-up detection (SENSOR_TRIG_DELTA)" + depends on WSEN_ISDS_2536030320001_TRIGGER + select WSEN_ISDS_2536030320001_EVENTS + help + Enable wake-up detection (SENSOR_TRIG_DELTA) + +endif # WSEN_ISDS_2536030320001 diff --git a/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001.c b/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001.c new file mode 100644 index 0000000000000..24ff30bbab9f2 --- /dev/null +++ b/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001.c @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT we_wsen_isds_2536030320001 + +#include + +#include +#include +#include + +#include "wsen_isds_2536030320001.h" + +LOG_MODULE_REGISTER(WSEN_ISDS_2536030320001, CONFIG_SENSOR_LOG_LEVEL); + +/* + * List of supported accelerometer output data rates (sensor_value struct, input to + * sensor_attr_set()). Index into this list is used as argument for + * ISDS_setAccOutputDataRate(). + */ +static const struct sensor_value isds_2536030320001_accel_odr_list[] = { + {.val1 = 0, .val2 = 0}, {.val1 = 12, .val2 = 5 * 100000}, + {.val1 = 26, .val2 = 0}, {.val1 = 52, .val2 = 0}, + {.val1 = 104, .val2 = 0}, {.val1 = 208, .val2 = 0}, + {.val1 = 416, .val2 = 0}, {.val1 = 833, .val2 = 0}, + {.val1 = 1660, .val2 = 0}, {.val1 = 3330, .val2 = 0}, + {.val1 = 6660, .val2 = 0}, {.val1 = 1, .val2 = 6 * 100000}, +}; + +/* + * List of supported gyroscope output data rates (sensor_value struct, input to + * sensor_attr_set()). Index into this list is used as argument for + * ISDS_setGyroOutputDataRate(). + */ +static const struct sensor_value isds_2536030320001_gyro_odr_list[] = { + {.val1 = 0, .val2 = 0}, {.val1 = 12, .val2 = 5 * 100000}, {.val1 = 26, .val2 = 0}, + {.val1 = 52, .val2 = 0}, {.val1 = 104, .val2 = 0}, {.val1 = 208, .val2 = 0}, + {.val1 = 416, .val2 = 0}, {.val1 = 833, .val2 = 0}, {.val1 = 1660, .val2 = 0}, + {.val1 = 3330, .val2 = 0}, {.val1 = 6660, .val2 = 0}, +}; + +/* + * List of supported accelerometer full scale values (i.e. measurement ranges, in g). + * Index into this list is used as input for ISDS_setAccFullScale(). + */ +static const uint8_t isds_2536030320001_accel_full_scale_list[] = { + 2, + 16, + 4, + 8, +}; + +/* + * List of supported gyroscope full scale values (i.e. measurement ranges, in dps). + * Index into this list is used as input for ISDS_setGyroFullScale(). + */ +static const uint16_t isds_2536030320001_gyro_full_scale_list[] = { + 250, 125, 500, 0, 1000, 0, 2000, +}; + +#define MAX_POLL_STEP_COUNT 10 + +static int isds_2536030320001_sample_fetch(const struct device *dev, enum sensor_channel channel) +{ + struct isds_2536030320001_data *data = dev->data; + + uint32_t accel_step_sleep_duration, gyro_step_sleep_duration, step_sleep_duration; + + switch (channel) { + case SENSOR_CHAN_ALL: + case SENSOR_CHAN_AMBIENT_TEMP: + accel_step_sleep_duration = + ((uint32_t)1000000000 / + (uint32_t)sensor_value_to_milli( + &isds_2536030320001_accel_odr_list[data->accel_odr]) / + MAX_POLL_STEP_COUNT); + gyro_step_sleep_duration = + ((uint32_t)1000000000 / + (uint32_t)sensor_value_to_milli( + &isds_2536030320001_gyro_odr_list[data->gyro_odr]) / + MAX_POLL_STEP_COUNT); + step_sleep_duration = accel_step_sleep_duration < gyro_step_sleep_duration + ? gyro_step_sleep_duration + : accel_step_sleep_duration; + break; + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + step_sleep_duration = + ((uint32_t)1000000000 / + (uint32_t)sensor_value_to_milli( + &isds_2536030320001_accel_odr_list[data->accel_odr]) / + MAX_POLL_STEP_COUNT); + break; + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: + step_sleep_duration = ((uint32_t)1000000000 / + (uint32_t)sensor_value_to_milli( + &isds_2536030320001_gyro_odr_list[data->gyro_odr]) / + MAX_POLL_STEP_COUNT); + break; + default: + LOG_ERR("Fetching is not supported on channel %d.", channel); + return -ENOTSUP; + } + + ISDS_state_t acceleration_data_ready, gyro_data_ready, temp_data_ready; + + acceleration_data_ready = gyro_data_ready = temp_data_ready = ISDS_disable; + + bool data_ready = false; + int step_count = 0; + + while (1) { + switch (channel) { + case SENSOR_CHAN_ALL: { + if (ISDS_isAccelerationDataReady(&data->sensor_interface, + &acceleration_data_ready) != WE_SUCCESS) { + LOG_ERR("Failed to check if acceleration data is ready."); + return -EIO; + } + + if (ISDS_isGyroscopeDataReady(&data->sensor_interface, &gyro_data_ready) != + WE_SUCCESS) { + LOG_ERR("Failed to check if gyroscope data is ready."); + return -EIO; + } + + if (ISDS_isTemperatureDataReady(&data->sensor_interface, + &temp_data_ready) != WE_SUCCESS) { + LOG_ERR("Failed to check if temperature data is ready."); + return -EIO; + } + + data_ready = + (acceleration_data_ready == ISDS_enable && + gyro_data_ready == ISDS_enable && temp_data_ready == ISDS_enable); + break; + } + case SENSOR_CHAN_AMBIENT_TEMP: { + if (ISDS_isTemperatureDataReady(&data->sensor_interface, + &temp_data_ready) != WE_SUCCESS) { + LOG_ERR("Failed to check if temperature data is ready."); + return -EIO; + } + data_ready = (temp_data_ready == ISDS_enable); + break; + } + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: { + if (ISDS_isAccelerationDataReady(&data->sensor_interface, + &acceleration_data_ready) != WE_SUCCESS) { + LOG_ERR("Failed to check if acceleration data is ready."); + return -EIO; + } + + data_ready = (acceleration_data_ready == ISDS_enable); + break; + } + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: { + if (ISDS_isGyroscopeDataReady(&data->sensor_interface, &gyro_data_ready) != + WE_SUCCESS) { + LOG_ERR("Failed to check if gyroscope data is ready."); + return -EIO; + } + + data_ready = (gyro_data_ready == ISDS_enable); + break; + } + default: + break; + } + + if (data_ready) { + break; + } else if (step_count >= MAX_POLL_STEP_COUNT) { + return -EIO; + } + + step_count++; + k_sleep(K_USEC(step_sleep_duration)); + } + + int16_t temperature, acceleration_x, acceleration_y, acceleration_z, gyro_x, gyro_y, gyro_z; + + switch (channel) { + case SENSOR_CHAN_ALL: { + + if (ISDS_getRawAccelerations(&data->sensor_interface, &acceleration_x, + &acceleration_y, &acceleration_z) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "acceleration"); + return -EIO; + } + + if (ISDS_getRawAngularRates(&data->sensor_interface, &gyro_x, &gyro_y, &gyro_z) != + WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "gyro"); + return -EIO; + } + + if (ISDS_getRawTemperature(&data->sensor_interface, &temperature) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "temperature"); + return -EIO; + } + + data->acceleration_x = + ISDS_convertAcceleration_int(acceleration_x, data->accel_range); + data->acceleration_y = + ISDS_convertAcceleration_int(acceleration_y, data->accel_range); + data->acceleration_z = + ISDS_convertAcceleration_int(acceleration_z, data->accel_range); + + data->rate_x = ISDS_convertAngularRate_int(gyro_x, data->gyro_range); + data->rate_y = ISDS_convertAngularRate_int(gyro_y, data->gyro_range); + data->rate_z = ISDS_convertAngularRate_int(gyro_z, data->gyro_range); + + data->temperature = ISDS_convertTemperature_int(temperature); + break; + } + case SENSOR_CHAN_AMBIENT_TEMP: { + if (ISDS_getRawTemperature(&data->sensor_interface, &temperature) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "temperature"); + return -EIO; + } + data->temperature = ISDS_convertTemperature_int(temperature); + break; + } + case SENSOR_CHAN_ACCEL_X: { + if (ISDS_getRawAccelerationX(&data->sensor_interface, &acceleration_x) != + WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "acceleration"); + return -EIO; + } + data->acceleration_x = + ISDS_convertAcceleration_int(acceleration_x, data->accel_range); + break; + } + case SENSOR_CHAN_ACCEL_Y: { + if (ISDS_getRawAccelerationY(&data->sensor_interface, &acceleration_y) != + WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "acceleration"); + return -EIO; + } + data->acceleration_y = + ISDS_convertAcceleration_int(acceleration_y, data->accel_range); + break; + } + case SENSOR_CHAN_ACCEL_Z: { + if (ISDS_getRawAccelerationZ(&data->sensor_interface, &acceleration_z) != + WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "acceleration"); + return -EIO; + } + data->acceleration_z = + ISDS_convertAcceleration_int(acceleration_z, data->accel_range); + break; + } + case SENSOR_CHAN_ACCEL_XYZ: { + if (ISDS_getRawAccelerations(&data->sensor_interface, &acceleration_x, + &acceleration_y, &acceleration_z) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "acceleration"); + return -EIO; + } + + data->acceleration_x = + ISDS_convertAcceleration_int(acceleration_x, data->accel_range); + data->acceleration_y = + ISDS_convertAcceleration_int(acceleration_y, data->accel_range); + data->acceleration_z = + ISDS_convertAcceleration_int(acceleration_z, data->accel_range); + break; + } + case SENSOR_CHAN_GYRO_X: { + if (ISDS_getRawAngularRateX(&data->sensor_interface, &gyro_x) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "gyro"); + return -EIO; + } + data->rate_x = ISDS_convertAngularRate_int(gyro_x, data->gyro_range); + break; + } + case SENSOR_CHAN_GYRO_Y: { + if (ISDS_getRawAngularRateY(&data->sensor_interface, &gyro_y) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "gyro"); + return -EIO; + } + data->rate_y = ISDS_convertAngularRate_int(gyro_y, data->gyro_range); + break; + } + case SENSOR_CHAN_GYRO_Z: { + if (ISDS_getRawAngularRateZ(&data->sensor_interface, &gyro_z) != WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "gyro"); + return -EIO; + } + data->rate_z = ISDS_convertAngularRate_int(gyro_z, data->gyro_range); + break; + } + case SENSOR_CHAN_GYRO_XYZ: { + if (ISDS_getRawAngularRates(&data->sensor_interface, &gyro_x, &gyro_y, &gyro_z) != + WE_SUCCESS) { + LOG_ERR("Failed to fetch %s sample.", "gyro"); + return -EIO; + } + + data->rate_x = ISDS_convertAngularRate_int(gyro_x, data->gyro_range); + data->rate_y = ISDS_convertAngularRate_int(gyro_y, data->gyro_range); + data->rate_z = ISDS_convertAngularRate_int(gyro_z, data->gyro_range); + break; + } + default: + break; + } + + return 0; +} + +/* Convert acceleration value from mg (int16) to m/s^2 (sensor_value). */ +static inline void isds_2536030320001_convert_acceleration(struct sensor_value *val, + int16_t raw_val) +{ + int64_t dval; + + /* Convert to m/s^2 */ + dval = (((int64_t)raw_val) * SENSOR_G) / 1000000LL; + val->val1 = dval / 1000LL; + val->val2 = (dval % 1000LL) * 1000; +} + +/* Convert angular rate value from mdps (int32) to radians/s (sensor_value). */ +static inline void isds_2536030320001_convert_angular_rate(struct sensor_value *val, + int32_t raw_val) +{ + int64_t dval; + + /* Convert to radians/s */ + dval = ((((int64_t)raw_val) * SENSOR_PI) / 180000000LL); + val->val1 = dval / 1000LL; + val->val2 = (dval % 1000LL) * 1000; +} + +static int isds_2536030320001_channel_get(const struct device *dev, enum sensor_channel channel, + struct sensor_value *value) +{ + struct isds_2536030320001_data *data = dev->data; + + switch (channel) { + case SENSOR_CHAN_AMBIENT_TEMP: + /* Convert temperature from 0.01 degrees Celsius to degrees Celsius */ + value->val1 = (int32_t)data->temperature / 100; + value->val2 = ((int32_t)data->temperature % 100) * (1000000 / 100); + break; + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + /* Convert requested acceleration(s) */ + if (channel == SENSOR_CHAN_ACCEL_X || channel == SENSOR_CHAN_ACCEL_XYZ) { + isds_2536030320001_convert_acceleration(value, data->acceleration_x); + value++; + } + if (channel == SENSOR_CHAN_ACCEL_Y || channel == SENSOR_CHAN_ACCEL_XYZ) { + isds_2536030320001_convert_acceleration(value, data->acceleration_y); + value++; + } + if (channel == SENSOR_CHAN_ACCEL_Z || channel == SENSOR_CHAN_ACCEL_XYZ) { + isds_2536030320001_convert_acceleration(value, data->acceleration_z); + value++; + } + break; + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: + if (channel == SENSOR_CHAN_GYRO_X || channel == SENSOR_CHAN_GYRO_XYZ) { + isds_2536030320001_convert_angular_rate(value, data->rate_x); + value++; + } + if (channel == SENSOR_CHAN_GYRO_Y || channel == SENSOR_CHAN_GYRO_XYZ) { + isds_2536030320001_convert_angular_rate(value, data->rate_y); + value++; + } + if (channel == SENSOR_CHAN_GYRO_Z || channel == SENSOR_CHAN_GYRO_XYZ) { + isds_2536030320001_convert_angular_rate(value, data->rate_z); + value++; + } + break; + default: + LOG_ERR("Channel not supported %d", channel); + return -ENOTSUP; + } + + return 0; +} + +/* Set accelerometer output data rate. See isds_2536030320001_accel_odr_list for allowed values. */ +static int isds_2536030320001_accel_odr_set(const struct device *dev, + const struct sensor_value *odr) +{ + struct isds_2536030320001_data *data = dev->data; + int odr_index; + + for (odr_index = 0; odr_index < ARRAY_SIZE(isds_2536030320001_accel_odr_list); + odr_index++) { + if (odr->val1 == isds_2536030320001_accel_odr_list[odr_index].val1 && + odr->val2 == isds_2536030320001_accel_odr_list[odr_index].val2) { + break; + } + } + + if (odr_index == ARRAY_SIZE(isds_2536030320001_accel_odr_list)) { + /* ODR not allowed (was not found in isds_2536030320001_accel_odr_list) */ + LOG_ERR("Bad sampling frequency %d.%d", odr->val1, odr->val2); + return -EINVAL; + } + + if (ISDS_setAccOutputDataRate(&data->sensor_interface, + (ISDS_accOutputDataRate_t)odr_index) != WE_SUCCESS) { + LOG_ERR("Failed to set accelerometer output data rate"); + return -EIO; + } + + data->accel_odr = (ISDS_accOutputDataRate_t)odr_index; + + return 0; +} + +/* Get accelerometer output data rate. See isds_2536030320001_accel_odr_list for allowed values. */ +static int isds_2536030320001_accel_odr_get(const struct device *dev, struct sensor_value *odr) +{ + struct isds_2536030320001_data *data = dev->data; + ISDS_accOutputDataRate_t odr_index; + + if (ISDS_getAccOutputDataRate(&data->sensor_interface, &odr_index) != WE_SUCCESS) { + LOG_ERR("Failed to get output data rate"); + return -EIO; + } + + data->accel_odr = odr_index; + + odr->val1 = isds_2536030320001_accel_odr_list[odr_index].val1; + odr->val2 = isds_2536030320001_accel_odr_list[odr_index].val2; + + return 0; +} + +/* Set gyroscope output data rate. See isds_2536030320001_gyro_odr_list for allowed values. */ +static int isds_2536030320001_gyro_odr_set(const struct device *dev, const struct sensor_value *odr) +{ + struct isds_2536030320001_data *data = dev->data; + int odr_index; + + for (odr_index = 0; odr_index < ARRAY_SIZE(isds_2536030320001_gyro_odr_list); odr_index++) { + if (odr->val1 == isds_2536030320001_gyro_odr_list[odr_index].val1 && + odr->val2 == isds_2536030320001_gyro_odr_list[odr_index].val2) { + break; + } + } + + if (odr_index == ARRAY_SIZE(isds_2536030320001_gyro_odr_list)) { + /* ODR not allowed (was not found in isds_2536030320001_gyro_odr_list) */ + LOG_ERR("Bad sampling frequency %d.%d", odr->val1, odr->val2); + return -EINVAL; + } + + if (ISDS_setGyroOutputDataRate(&data->sensor_interface, + (ISDS_gyroOutputDataRate_t)odr_index) != WE_SUCCESS) { + LOG_ERR("Failed to set gyroscope output data rate"); + return -EIO; + } + + data->gyro_odr = (ISDS_gyroOutputDataRate_t)odr_index; + + return 0; +} + +/* Get gyroscope output data rate. See isds_2536030320001_gyro_odr_list for allowed values. */ +static int isds_2536030320001_gyro_odr_get(const struct device *dev, struct sensor_value *odr) +{ + struct isds_2536030320001_data *data = dev->data; + ISDS_gyroOutputDataRate_t odr_index; + + if (ISDS_getGyroOutputDataRate(&data->sensor_interface, &odr_index) != WE_SUCCESS) { + LOG_ERR("Failed to get output data rate"); + return -EIO; + } + + data->gyro_odr = odr_index; + + odr->val1 = isds_2536030320001_gyro_odr_list[odr_index].val1; + odr->val2 = isds_2536030320001_gyro_odr_list[odr_index].val2; + + return 0; +} + +/* + * Set accelerometer full scale (measurement range). See isds_2536030320001_accel_full_scale_list + * for allowed values. + */ +static int isds_2536030320001_accel_full_scale_set(const struct device *dev, + const struct sensor_value *fs) +{ + struct isds_2536030320001_data *data = dev->data; + + uint8_t scaleg = (uint8_t)sensor_ms2_to_g(fs); + + uint8_t idx; + + for (idx = 0; idx < ARRAY_SIZE(isds_2536030320001_accel_full_scale_list); idx++) { + if (isds_2536030320001_accel_full_scale_list[idx] == scaleg) { + break; + } + } + + if (idx == ARRAY_SIZE(isds_2536030320001_accel_full_scale_list)) { + /* fullscale not allowed (was not found in isds_2536030320001_accel_full_scale_list) + */ + LOG_ERR("Bad scale %d", scaleg); + return -EINVAL; + } + + if (ISDS_setAccFullScale(&data->sensor_interface, (ISDS_accFullScale_t)idx) != WE_SUCCESS) { + LOG_ERR("Failed to set accelerometer full scale."); + return -EIO; + } + + data->accel_range = (ISDS_accFullScale_t)idx; + + return 0; +} + +/* + * Get accelerometer full scale (measurement range). See isds_2536030320001_accel_full_scale_list + * for allowed values. + */ +static int isds_2536030320001_accel_full_scale_get(const struct device *dev, + struct sensor_value *fs) +{ + struct isds_2536030320001_data *data = dev->data; + + ISDS_accFullScale_t accel_fs; + + if (ISDS_getAccFullScale(&data->sensor_interface, &accel_fs) != WE_SUCCESS) { + LOG_ERR("Failed to get full scale"); + return -EIO; + } + + data->accel_range = accel_fs; + + fs->val1 = isds_2536030320001_accel_full_scale_list[accel_fs]; + fs->val2 = 0; + + return 0; +} + +/* + * Set gyroscope full scale (measurement range). See isds_2536030320001_gyro_full_scale_list for + * allowed values. + */ +static int isds_2536030320001_gyro_full_scale_set(const struct device *dev, + const struct sensor_value *fs) +{ + struct isds_2536030320001_data *data = dev->data; + + uint16_t scale_dps = (uint16_t)sensor_rad_to_degrees(fs); + + uint8_t idx; + + for (idx = 0; idx < ARRAY_SIZE(isds_2536030320001_gyro_full_scale_list); idx++) { + if (isds_2536030320001_gyro_full_scale_list[idx] == scale_dps) { + break; + } + } + + if (idx == ARRAY_SIZE(isds_2536030320001_gyro_full_scale_list)) { + /* fullscale not allowed (was not found in isds_2536030320001_accel_full_scale_list) + */ + LOG_ERR("Bad scale %d", scale_dps); + return -EINVAL; + } + + if (ISDS_setGyroFullScale(&data->sensor_interface, (ISDS_gyroFullScale_t)idx) != + WE_SUCCESS) { + LOG_ERR("Failed to set gyroscope full scale."); + return -EIO; + } + + data->gyro_range = (ISDS_gyroFullScale_t)idx; + + return 0; +} + +/* + * Get gyroscope full scale (measurement range). See isds_2536030320001_gyro_full_scale_list for + * allowed values. + */ +static int isds_2536030320001_gyro_full_scale_get(const struct device *dev, struct sensor_value *fs) +{ + struct isds_2536030320001_data *data = dev->data; + + ISDS_gyroFullScale_t gyro_fs; + + if (ISDS_getGyroFullScale(&data->sensor_interface, &gyro_fs) != WE_SUCCESS) { + LOG_ERR("Failed to get full scale"); + return -EIO; + } + + data->gyro_range = gyro_fs; + + fs->val1 = isds_2536030320001_gyro_full_scale_list[gyro_fs]; + fs->val2 = 0; + + return 0; +} + +static int isds_2536030320001_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return isds_2536030320001_accel_odr_set(dev, val); + case SENSOR_CHAN_GYRO_XYZ: + return isds_2536030320001_gyro_odr_set(dev, val); + default: + break; + } + break; + case SENSOR_ATTR_FULL_SCALE: + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return isds_2536030320001_accel_full_scale_set(dev, val); + case SENSOR_CHAN_GYRO_XYZ: + return isds_2536030320001_gyro_full_scale_set(dev, val); + default: + break; + } + break; + default: + break; + } + + LOG_ERR("attr_set() is not supported on channel %d.", chan); + return -ENOTSUP; +} + +static int isds_2536030320001_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + + if (val == NULL) { + LOG_WRN("address of passed value is NULL."); + return -EFAULT; + } + + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return isds_2536030320001_accel_odr_get(dev, val); + case SENSOR_CHAN_GYRO_XYZ: + return isds_2536030320001_gyro_odr_get(dev, val); + default: + break; + } + break; + case SENSOR_ATTR_FULL_SCALE: + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return isds_2536030320001_accel_full_scale_get(dev, val); + case SENSOR_CHAN_GYRO_XYZ: + return isds_2536030320001_gyro_full_scale_get(dev, val); + default: + break; + } + break; + default: + break; + } + + LOG_ERR("attr_get() is not supported on channel %d.", chan); + return -ENOTSUP; +} + +static DEVICE_API(sensor, isds_2536030320001_driver_api) = { + .attr_set = isds_2536030320001_attr_set, + .attr_get = isds_2536030320001_attr_get, +#if CONFIG_WSEN_ISDS_2536030320001_TRIGGER + .trigger_set = isds_2536030320001_trigger_set, +#endif + .sample_fetch = isds_2536030320001_sample_fetch, + .channel_get = isds_2536030320001_channel_get, +}; + +static int isds_2536030320001_init(const struct device *dev) +{ + const struct isds_2536030320001_config *config = dev->config; + struct isds_2536030320001_data *data = dev->data; + struct sensor_value accel_range, gyro_range; + uint8_t device_id; + ISDS_state_t sw_reset; + + /* Initialize WE sensor interface */ + WE_sensorInterfaceType_t interface_type = data->sensor_interface.interfaceType; + + ISDS_getDefaultInterface(&data->sensor_interface); + data->sensor_interface.interfaceType = interface_type; + + switch (data->sensor_interface.interfaceType) { +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + case WE_i2c: + if (!i2c_is_ready_dt(&config->bus_cfg.i2c)) { + LOG_ERR("I2C bus device not ready"); + return -ENODEV; + } + data->sensor_interface.handle = (void *)&config->bus_cfg.i2c; + break; +#endif +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + case WE_spi: + if (!spi_is_ready_dt(&config->bus_cfg.spi)) { + LOG_ERR("SPI bus device not ready"); + return -ENODEV; + } + data->sensor_interface.handle = (void *)&config->bus_cfg.spi; + break; +#endif + default: + LOG_ERR("Invalid interface type"); + return -EINVAL; + } + + /* First communication test - check device ID */ + if (ISDS_getDeviceID(&data->sensor_interface, &device_id) != WE_SUCCESS) { + LOG_ERR("Failed to read device ID."); + return -EIO; + } + + if (device_id != ISDS_DEVICE_ID_VALUE) { + LOG_ERR("Invalid device ID 0x%x.", device_id); + return -EINVAL; + } + + /* Perform soft reset of the sensor */ + ISDS_softReset(&data->sensor_interface, ISDS_enable); + + k_sleep(K_USEC(5)); + + do { + if (ISDS_getSoftResetState(&data->sensor_interface, &sw_reset) != WE_SUCCESS) { + LOG_ERR("Failed to get sensor reset state."); + return -EIO; + } + } while (sw_reset); + + if (isds_2536030320001_accel_odr_set( + dev, &isds_2536030320001_accel_odr_list[config->accel_odr]) < 0) { + LOG_ERR("Failed to set odr"); + return -EIO; + } + + if (isds_2536030320001_gyro_odr_set( + dev, &isds_2536030320001_gyro_odr_list[config->gyro_odr]) < 0) { + LOG_ERR("Failed to set odr"); + return -EIO; + } + + if (ISDS_enableAutoIncrement(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable auto increment."); + return -EIO; + } + + if (ISDS_enableBlockDataUpdate(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable block data update."); + return -EIO; + } + + sensor_g_to_ms2((int32_t)config->accel_range, &accel_range); + + if (isds_2536030320001_accel_full_scale_set(dev, &accel_range) < 0) { + LOG_ERR("Failed to set full scale"); + return -EIO; + } + + sensor_degrees_to_rad((int32_t)config->gyro_range, &gyro_range); + + if (isds_2536030320001_gyro_full_scale_set(dev, &gyro_range) < 0) { + LOG_ERR("Failed to set full scale"); + return -EIO; + } + +#if CONFIG_WSEN_ISDS_2536030320001_DISABLE_ACCEL_HIGH_PERFORMANCE_MODE + if (ISDS_disableAccHighPerformanceMode(&data->sensor_interface, ISDS_enable) != + WE_SUCCESS) { + LOG_ERR("Failed to disable accelerometer high performance mode."); + return -EIO; + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_DISABLE_ACCEL_HIGH_PERFORMANCE_MODE */ + +#if CONFIG_WSEN_ISDS_2536030320001_DISABLE_GYRO_HIGH_PERFORMANCE_MODE + if (ISDS_disableGyroHighPerformanceMode(&data->sensor_interface, ISDS_enable) != + WE_SUCCESS) { + LOG_ERR("Failed to disable gyroscope high performance mode."); + return -EIO; + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_DISABLE_GYRO_HIGH_PERFORMANCE_MODE */ + +#if CONFIG_WSEN_ISDS_2536030320001_TRIGGER + if (isds_2536030320001_init_interrupt(dev) < 0) { + LOG_ERR("Failed to initialize interrupt(s)."); + return -EIO; + } +#endif + + return 0; +} + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TRIGGER +#define ISDS_2536030320001_CFG_EVENTS_IRQ(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, events_interrupt_gpios), \ +(.events_interrupt_gpio = \ +GPIO_DT_SPEC_INST_GET(inst, events_interrupt_gpios),), \ +()) +#define ISDS_2536030320001_CFG_DRDY_IRQ(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, drdy_interrupt_gpios), \ +(.drdy_interrupt_gpio = \ +GPIO_DT_SPEC_INST_GET(inst, drdy_interrupt_gpios),), \ +()) +#else +#define ISDS_2536030320001_CFG_EVENTS_IRQ(inst) +#define ISDS_2536030320001_CFG_DRDY_IRQ(inst) +#endif /* CONFIG_WSEN_ISDS_2536030320001_TRIGGER */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TAP +#define ISDS_2536030320001_CONFIG_TAP(inst) \ + .tap_mode = DT_INST_PROP(inst, tap_mode), \ + .tap_threshold = DT_INST_PROP(inst, tap_threshold), \ + .tap_axis_enable = DT_INST_PROP(inst, tap_axis_enable), \ + .tap_shock = DT_INST_PROP(inst, tap_shock), \ + .tap_latency = DT_INST_PROP(inst, tap_latency), \ + .tap_quiet = DT_INST_PROP(inst, tap_quiet), +#else +#define ISDS_2536030320001_CONFIG_TAP(inst) +#endif /* CONFIG_WSEN_ISDS_2536030320001_TAP */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_FREEFALL +#define ISDS_2536030320001_CONFIG_FREEFALL(inst) \ + .freefall_duration = DT_INST_PROP(inst, freefall_duration), \ + .freefall_threshold = \ + (ISDS_freeFallThreshold_t)DT_INST_ENUM_IDX(inst, freefall_threshold), +#else +#define ISDS_2536030320001_CONFIG_FREEFALL(inst) +#endif /* CONFIG_WSEN_ISDS_2536030320001_FREEFALL */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_DELTA +#define ISDS_2536030320001_CONFIG_DELTA(inst) \ + .delta_threshold = DT_INST_PROP(inst, delta_threshold), \ + .delta_duration = DT_INST_PROP(inst, delta_duration), +#else +#define ISDS_2536030320001_CONFIG_DELTA(inst) +#endif /* CONFIG_WSEN_ISDS_2536030320001_DELTA */ + +#define ISDS_2536030320001_CONFIG_OPTIONAL(inst) \ + ISDS_2536030320001_CONFIG_TAP(inst) \ + ISDS_2536030320001_CONFIG_FREEFALL(inst) \ + ISDS_2536030320001_CONFIG_DELTA(inst) + +#define ISDS_2536030320001_CONFIG_IRQ(inst) \ + ISDS_2536030320001_CFG_EVENTS_IRQ(inst) \ + ISDS_2536030320001_CFG_DRDY_IRQ(inst) + +#define ISDS_2536030320001_CONFIG_COMMON(inst) \ + .accel_odr = (ISDS_accOutputDataRate_t)(DT_INST_ENUM_IDX(inst, accel_odr)), \ + .gyro_odr = (ISDS_gyroOutputDataRate_t)(DT_INST_ENUM_IDX(inst, gyro_odr)), \ + .accel_range = DT_INST_PROP(inst, accel_range), \ + .gyro_range = DT_INST_PROP(inst, gyro_range), \ + ISDS_2536030320001_CONFIG_OPTIONAL(inst) ISDS_2536030320001_CONFIG_IRQ(inst) + +/* + * Instantiation macros used when device is on SPI bus. + */ + +#define ISDS_2536030320001_SPI_OPERATION \ + (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA) + +#define ISDS_2536030320001_CONFIG_SPI(inst) \ + {.bus_cfg = \ + { \ + .spi = SPI_DT_SPEC_INST_GET(inst, ISDS_2536030320001_SPI_OPERATION, 0), \ + }, \ + ISDS_2536030320001_CONFIG_COMMON(inst)} + +/* + * Instantiation macros used when device is on I2C bus. + */ + +#define ISDS_2536030320001_CONFIG_I2C(inst) \ + {.bus_cfg = \ + { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + }, \ + ISDS_2536030320001_CONFIG_COMMON(inst)} + +#define ISDS_2536030320001_CONFIG_WE_INTERFACE(inst) \ + {COND_CODE_1(DT_INST_ON_BUS(inst, i2c), \ + (.sensor_interface = {.interfaceType = WE_i2c}), \ + ()) COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (.sensor_interface = {.interfaceType = WE_spi}), \ + ()) } + +#define ISDS_2536030320001_SELECT_CONFIG(inst) \ + COND_CODE_1(DT_INST_ON_BUS(inst, i2c), \ + (ISDS_2536030320001_CONFIG_I2C(inst)), \ + ()) \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (ISDS_2536030320001_CONFIG_SPI(inst)), \ + ()) + +/* + * Main instantiation macro. Use of COND_CODE_1() selects the right + * bus-specific macro at preprocessor time. + */ +#define ISDS_2536030320001_DEFINE(inst) \ + static struct isds_2536030320001_data isds_2536030320001_data_##inst = \ + ISDS_2536030320001_CONFIG_WE_INTERFACE(inst); \ + static const struct isds_2536030320001_config isds_2536030320001_config_##inst = \ + ISDS_2536030320001_SELECT_CONFIG(inst); \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, isds_2536030320001_init, NULL, \ + &isds_2536030320001_data_##inst, \ + &isds_2536030320001_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &isds_2536030320001_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(ISDS_2536030320001_DEFINE) diff --git a/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001.h b/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001.h new file mode 100644 index 0000000000000..baf3fd131f28f --- /dev/null +++ b/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_WSEN_ISDS_2536030320001_WSEN_ISDS_2536030320001_H_ +#define ZEPHYR_DRIVERS_SENSOR_WSEN_ISDS_2536030320001_WSEN_ISDS_2536030320001_H_ + +#include +#include + +#include + +#include "WSEN_ISDS_2536030320001_hal.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ + +struct isds_2536030320001_data { + /* WE sensor interface configuration */ + WE_sensorInterface_t sensor_interface; + + /* Last acceleration samples */ + int16_t acceleration_x; + int16_t acceleration_y; + int16_t acceleration_z; + + /* Last angular rate samples */ + int32_t rate_x; + int32_t rate_y; + int32_t rate_z; + + /* Last temperature sample */ + int16_t temperature; + + ISDS_accOutputDataRate_t accel_odr; + ISDS_gyroOutputDataRate_t gyro_odr; + + ISDS_accFullScale_t accel_range; + ISDS_gyroFullScale_t gyro_range; + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TRIGGER + const struct device *dev; + + /* Callback for interrupts */ + struct gpio_callback drdy_interrupt_cb; + +#ifdef CONFIG_WSEN_ISDS_2536030320001_EVENTS + struct gpio_callback events_interrupt_cb; +#endif + + /* Registered trigger handlers */ + sensor_trigger_handler_t accel_data_ready_handler; + sensor_trigger_handler_t gyro_data_ready_handler; + sensor_trigger_handler_t temp_data_ready_handler; + sensor_trigger_handler_t single_tap_handler; + sensor_trigger_handler_t double_tap_handler; + sensor_trigger_handler_t freefall_handler; + sensor_trigger_handler_t delta_handler; + + const struct sensor_trigger *accel_data_ready_trigger; + const struct sensor_trigger *gyro_data_ready_trigger; + const struct sensor_trigger *temp_data_ready_trigger; + const struct sensor_trigger *single_tap_trigger; + const struct sensor_trigger *double_tap_trigger; + const struct sensor_trigger *freefall_trigger; + const struct sensor_trigger *delta_trigger; + +#if defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD) + K_KERNEL_STACK_MEMBER(drdy_thread_stack, CONFIG_WSEN_ISDS_2536030320001_THREAD_STACK_SIZE); + struct k_thread drdy_thread; + struct k_sem drdy_sem; +#ifdef CONFIG_WSEN_ISDS_2536030320001_EVENTS + K_KERNEL_STACK_MEMBER(events_thread_stack, + CONFIG_WSEN_ISDS_2536030320001_THREAD_STACK_SIZE); + struct k_thread events_thread; + struct k_sem events_sem; +#endif +#elif defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD) + struct k_work drdy_work; +#ifdef CONFIG_WSEN_ISDS_2536030320001_EVENTS + struct k_work events_work; +#endif +#endif +#endif /* CONFIG_WSEN_ISDS_2536030320001_TRIGGER */ +}; + +struct isds_2536030320001_config { + union { +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + const struct i2c_dt_spec i2c; +#endif +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + const struct spi_dt_spec spi; +#endif + } bus_cfg; + + /* Output data rates */ + ISDS_accOutputDataRate_t accel_odr; + ISDS_gyroOutputDataRate_t gyro_odr; + + /* Measurement ranges (full scale) */ + uint8_t accel_range; + uint16_t gyro_range; + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TRIGGER + /* Interrupt pin (used for data-ready, tap, free-fall, delta/wake-up) */ + const struct gpio_dt_spec events_interrupt_gpio; + const struct gpio_dt_spec drdy_interrupt_gpio; + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TAP + uint8_t tap_mode; + uint8_t tap_threshold; + uint8_t tap_axis_enable[3]; + uint8_t tap_shock; + uint8_t tap_latency; + uint8_t tap_quiet; +#endif /* CONFIG_WSEN_ISDS_2536030320001_TAP */ +#ifdef CONFIG_WSEN_ISDS_2536030320001_FREEFALL + uint8_t freefall_duration; + ISDS_freeFallThreshold_t freefall_threshold; +#endif /* CONFIG_WSEN_ISDS_2536030320001_FREEFALL */ +#ifdef CONFIG_WSEN_ISDS_2536030320001_DELTA + uint8_t delta_threshold; + uint8_t delta_duration; +#endif /* CONFIG_WSEN_ISDS_2536030320001_DELTA */ +#endif /* CONFIG_WSEN_ISDS_2536030320001_TRIGGER */ +}; + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TRIGGER +int isds_2536030320001_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int isds_2536030320001_init_interrupt(const struct device *dev); +#endif /* CONFIG_WSEN_ISDS_2536030320001_TRIGGER */ + +#endif /* ZEPHYR_DRIVERS_SENSOR_WSEN_ISDS_2536030320001_WSEN_ISDS_2536030320001_H_ */ diff --git a/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001_trigger.c b/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001_trigger.c new file mode 100644 index 0000000000000..da51d40862a34 --- /dev/null +++ b/drivers/sensor/wsen/wsen_isds_2536030320001/wsen_isds_2536030320001_trigger.c @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT we_wsen_isds_2536030320001 + +#include + +#include "wsen_isds_2536030320001.h" + +LOG_MODULE_DECLARE(WSEN_ISDS_2536030320001, CONFIG_SENSOR_LOG_LEVEL); + +/* Enable/disable interrupt handling for data-ready, tap, free-fall, delta/wake-up */ +static inline int isds_2536030320001_setup_interrupt(const struct device *dev, + const struct gpio_dt_spec *pin, bool enable) +{ + unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE; + + return gpio_pin_interrupt_configure_dt(pin, flags); +} + +static inline void isds_2536030320001_handle_interrupt_1(const struct device *dev) +{ + struct isds_2536030320001_data *data = dev->data; + const struct isds_2536030320001_config *cfg = dev->config; + + /* Disable interrupt handling until the interrupt has been processed */ + isds_2536030320001_setup_interrupt(data->dev, &cfg->drdy_interrupt_gpio, false); + +#if defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD) + k_sem_give(&data->drdy_sem); +#elif defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->drdy_work); +#endif +} + +static void isds_2536030320001_process_interrupt_1(const struct device *dev) +{ + struct isds_2536030320001_data *data = dev->data; + const struct isds_2536030320001_config *cfg = dev->config; + ISDS_status_t status_reg; + + if (ISDS_getStatusRegister(&data->sensor_interface, &status_reg) != WE_SUCCESS) { + LOG_ERR("Failed to read status register"); + return; + } + + if (data->accel_data_ready_handler && status_reg.accDataReady) { + data->accel_data_ready_handler(dev, data->accel_data_ready_trigger); + } + + if (data->gyro_data_ready_handler && status_reg.gyroDataReady) { + data->gyro_data_ready_handler(dev, data->gyro_data_ready_trigger); + } + + if (data->temp_data_ready_handler && status_reg.tempDataReady) { + data->temp_data_ready_handler(dev, data->temp_data_ready_trigger); + } + + /* Re-enable interrupt handling */ + isds_2536030320001_setup_interrupt(dev, &cfg->drdy_interrupt_gpio, true); +} + +/* + * Is called when interrupt on INT1. + * Triggers asynchronous handling of interrupt in isds_2536030320001_process_interrupt(). + */ +static void isds_2536030320001_interrupt_1_gpio_callback(const struct device *dev, + struct gpio_callback *cb, uint32_t pins) +{ + struct isds_2536030320001_data *data = + CONTAINER_OF(cb, struct isds_2536030320001_data, drdy_interrupt_cb); + + ARG_UNUSED(pins); + + isds_2536030320001_handle_interrupt_1(data->dev); +} + +#ifdef CONFIG_WSEN_ISDS_2536030320001_EVENTS +static inline void isds_2536030320001_handle_interrupt_0(const struct device *dev) +{ + struct isds_2536030320001_data *data = dev->data; + const struct isds_2536030320001_config *cfg = dev->config; + + /* Disable interrupt handling until the interrupt has been processed */ + isds_2536030320001_setup_interrupt(data->dev, &cfg->events_interrupt_gpio, false); + +#if defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD) + k_sem_give(&data->events_sem); +#elif defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD) + k_work_submit(&data->events_work); +#endif +} + +/* Asynchronous handling of interrupt triggered in isds_2536030320001_gpio_callback() */ +static void isds_2536030320001_process_interrupt_0(const struct device *dev) +{ + struct isds_2536030320001_data *data = dev->data; + const struct isds_2536030320001_config *cfg = dev->config; + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TAP + ISDS_tapEvent_t tap_event; + /* Read tap event register to find out if a tap event interrupt occurred. */ + if (ISDS_getTapEventRegister(&data->sensor_interface, &tap_event) != WE_SUCCESS) { + LOG_ERR("Failed to read tap event register"); + return; + } + + if (data->single_tap_handler && tap_event.singleState) { + data->single_tap_handler(dev, data->single_tap_trigger); + } + + if (data->double_tap_handler && tap_event.doubleState) { + data->double_tap_handler(dev, data->double_tap_trigger); + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_TAP */ + +#if defined(CONFIG_WSEN_ISDS_2536030320001_FREEFALL) || \ + defined(CONFIG_WSEN_ISDS_2536030320001_DELTA) + + ISDS_wakeUpEvent_t wake_up_event; + /* Read wake-up event register to find out if freefall or wake-up interrupt occurred. */ + if (ISDS_getWakeUpEventRegister(&data->sensor_interface, &wake_up_event) != WE_SUCCESS) { + LOG_ERR("Failed to read wake-up event register"); + return; + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_FREEFALL || CONFIG_WSEN_ISDS_2536030320001_DELTA */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_FREEFALL + if (data->freefall_handler && wake_up_event.freeFallState) { + data->freefall_handler(dev, data->freefall_trigger); + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_FREEFALL */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_DELTA + if (data->delta_handler && wake_up_event.wakeUpState) { + data->delta_handler(dev, data->delta_trigger); + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_DELTA */ + + /* Re-enable interrupt handling */ + isds_2536030320001_setup_interrupt(dev, &cfg->events_interrupt_gpio, true); +} + +/* + * Is called when interrupt on INT0. + * Triggers asynchronous handling of interrupt in isds_2536030320001_process_interrupt(). + */ +static void isds_2536030320001_interrupt_0_gpio_callback(const struct device *dev, + struct gpio_callback *cb, uint32_t pins) +{ + struct isds_2536030320001_data *data = + CONTAINER_OF(cb, struct isds_2536030320001_data, events_interrupt_cb); + + ARG_UNUSED(pins); + + isds_2536030320001_handle_interrupt_0(data->dev); +} + +#endif /* CONFIG_WSEN_ISDS_2536030320001_EVENTS */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD +static void isds_2536030320001_drdy_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct isds_2536030320001_data *data = p1; + + while (true) { + k_sem_take(&data->drdy_sem, K_FOREVER); + isds_2536030320001_process_interrupt_1(data->dev); + } +} +#ifdef CONFIG_WSEN_ISDS_2536030320001_EVENTS +static void isds_2536030320001_events_thread(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + struct isds_2536030320001_data *data = p1; + + while (true) { + k_sem_take(&data->events_sem, K_FOREVER); + isds_2536030320001_process_interrupt_0(data->dev); + } +} +#endif /* CONFIG_WSEN_ISDS_2536030320001_EVENTS */ +#endif /* CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD +static void isds_2536030320001_drdy_work_cb(struct k_work *work) +{ + struct isds_2536030320001_data *isds_2536030320001 = + CONTAINER_OF(work, struct isds_2536030320001_data, drdy_work); + + isds_2536030320001_process_interrupt_1(isds_2536030320001->dev); +} + +#ifdef CONFIG_WSEN_ISDS_2536030320001_EVENTS +static void isds_2536030320001_events_work_cb(struct k_work *work) +{ + struct isds_2536030320001_data *isds_2536030320001 = + CONTAINER_OF(work, struct isds_2536030320001_data, events_work); + + isds_2536030320001_process_interrupt_0(isds_2536030320001->dev); +} +#endif /* CONFIG_WSEN_ISDS_2536030320001_EVENTS */ +#endif /* CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD */ + +/* (Un)register trigger handler and enable/disable the corresponding sensor interrupt */ +int isds_2536030320001_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct isds_2536030320001_data *data = dev->data; + const struct isds_2536030320001_config *cfg = dev->config; + + ISDS_state_t state = (handler != NULL) ? ISDS_enable : ISDS_disable; + + switch (trig->type) { + case SENSOR_TRIG_DATA_READY: { + int16_t x, y, z; + + switch (trig->chan) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: { + data->accel_data_ready_handler = handler; + data->accel_data_ready_trigger = trig; + isds_2536030320001_setup_interrupt(dev, &cfg->drdy_interrupt_gpio, + data->accel_data_ready_handler || + data->gyro_data_ready_handler || + data->temp_data_ready_handler); + if (state) { + /* Dummy read: re-trigger interrupt */ + ISDS_getRawAccelerations(&data->sensor_interface, &x, &y, &z); + } + if (ISDS_enableAccDataReadyINT1(&data->sensor_interface, state) != + WE_SUCCESS) { + return -EIO; + } + break; + } + case SENSOR_CHAN_GYRO_X: + case SENSOR_CHAN_GYRO_Y: + case SENSOR_CHAN_GYRO_Z: + case SENSOR_CHAN_GYRO_XYZ: { + data->gyro_data_ready_handler = handler; + data->gyro_data_ready_trigger = trig; + isds_2536030320001_setup_interrupt(dev, &cfg->drdy_interrupt_gpio, + data->accel_data_ready_handler || + data->gyro_data_ready_handler || + data->temp_data_ready_handler); + if (state) { + /* Dummy read: re-trigger interrupt */ + ISDS_getRawAngularRates(&data->sensor_interface, &x, &y, &z); + } + if (ISDS_enableGyroDataReadyINT1(&data->sensor_interface, state) != + WE_SUCCESS) { + return -EIO; + } + break; + } + case SENSOR_CHAN_AMBIENT_TEMP: { + data->temp_data_ready_handler = handler; + data->temp_data_ready_trigger = trig; + isds_2536030320001_setup_interrupt(dev, &cfg->drdy_interrupt_gpio, + data->accel_data_ready_handler || + data->gyro_data_ready_handler || + data->temp_data_ready_handler); + if (state) { + /* Dummy read: re-trigger interrupt */ + ISDS_getRawTemperature(&data->sensor_interface, &x); + } + if (ISDS_enableTemperatureDataReadyINT1(&data->sensor_interface, state) != + WE_SUCCESS) { + return -EIO; + } + break; + } + default: + goto error; + } + return 0; + } +#ifdef CONFIG_WSEN_ISDS_2536030320001_TAP + case SENSOR_TRIG_TAP: + if (trig->chan != SENSOR_CHAN_ALL) { + break; + } + data->single_tap_handler = handler; + data->single_tap_trigger = trig; + isds_2536030320001_setup_interrupt( + dev, &cfg->events_interrupt_gpio, + data->single_tap_handler || data->double_tap_handler || + data->freefall_handler || data->delta_handler); + if (ISDS_enableSingleTapINT0(&data->sensor_interface, state) != WE_SUCCESS) { + return -EIO; + } + return 0; + case SENSOR_TRIG_DOUBLE_TAP: + if (trig->chan != SENSOR_CHAN_ALL) { + break; + } + data->double_tap_handler = handler; + data->double_tap_trigger = trig; + isds_2536030320001_setup_interrupt( + dev, &cfg->events_interrupt_gpio, + data->single_tap_handler || data->double_tap_handler || + data->freefall_handler || data->delta_handler); + if (ISDS_enableDoubleTapINT0(&data->sensor_interface, state) != WE_SUCCESS) { + return -EIO; + } + return 0; +#endif /* CONFIG_WSEN_ISDS_2536030320001_TAP */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_FREEFALL + case SENSOR_TRIG_FREEFALL: + if (trig->chan != SENSOR_CHAN_ALL) { + break; + } + data->freefall_handler = handler; + data->freefall_trigger = trig; + isds_2536030320001_setup_interrupt( + dev, &cfg->events_interrupt_gpio, + data->single_tap_handler || data->double_tap_handler || + data->freefall_handler || data->delta_handler); + if (ISDS_enableFreeFallINT0(&data->sensor_interface, state) != WE_SUCCESS) { + return -EIO; + } + return 0; +#endif /* CONFIG_WSEN_ISDS_2536030320001_FREEFALL */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_DELTA + case SENSOR_TRIG_DELTA: + if (trig->chan != SENSOR_CHAN_ALL) { + break; + } + data->delta_handler = handler; + data->delta_trigger = trig; + isds_2536030320001_setup_interrupt( + dev, &cfg->events_interrupt_gpio, + data->single_tap_handler || data->double_tap_handler || + data->freefall_handler || data->delta_handler); + if (ISDS_enableWakeUpINT0(&data->sensor_interface, state) != WE_SUCCESS) { + return -EIO; + } + return 0; +#endif /* CONFIG_WSEN_ISDS_2536030320001_DELTA */ + default: + break; + } + +error: + LOG_ERR("Unsupported sensor trigger"); + return -ENOTSUP; +} + +int isds_2536030320001_init_interrupt(const struct device *dev) +{ + struct isds_2536030320001_data *data = dev->data; + const struct isds_2536030320001_config *cfg = dev->config; + + data->dev = dev; + + if (cfg->drdy_interrupt_gpio.port == NULL) { + LOG_ERR("drdy-interrupt-gpios is not defined in the device tree."); + return -EINVAL; + } + + if (!gpio_is_ready_dt(&cfg->drdy_interrupt_gpio)) { + LOG_ERR("Device %s is not ready", cfg->drdy_interrupt_gpio.port->name); + return -ENODEV; + } + + /* Setup interrupt gpio */ + if (gpio_pin_configure_dt(&cfg->drdy_interrupt_gpio, GPIO_INPUT) < 0) { + LOG_ERR("Failed to configure %s.%02u", cfg->drdy_interrupt_gpio.port->name, + cfg->drdy_interrupt_gpio.pin); + return -EIO; + } + + gpio_init_callback(&data->drdy_interrupt_cb, isds_2536030320001_interrupt_1_gpio_callback, + BIT(cfg->drdy_interrupt_gpio.pin)); + + if (gpio_add_callback(cfg->drdy_interrupt_gpio.port, &data->drdy_interrupt_cb) < 0) { + LOG_ERR("Failed to set gpio callback"); + return -EIO; + } + +#if defined(CONFIG_WSEN_ISDS_2536030320001_EVENTS) + + if (cfg->events_interrupt_gpio.port == NULL) { + LOG_DBG("events-interrupt-gpios is not defined in the device tree."); + return -EINVAL; + } + + if (!gpio_is_ready_dt(&cfg->events_interrupt_gpio)) { + LOG_ERR("Device %s is not ready", cfg->events_interrupt_gpio.port->name); + return -ENODEV; + } + + /* Setup interrupt gpio */ + if (gpio_pin_configure_dt(&cfg->events_interrupt_gpio, GPIO_INPUT) < 0) { + LOG_ERR("Failed to configure %s.%02u", cfg->events_interrupt_gpio.port->name, + cfg->events_interrupt_gpio.pin); + return -EIO; + } + + gpio_init_callback(&data->events_interrupt_cb, isds_2536030320001_interrupt_0_gpio_callback, + BIT(cfg->events_interrupt_gpio.pin)); + + if (gpio_add_callback(cfg->events_interrupt_gpio.port, &data->events_interrupt_cb) < 0) { + LOG_ERR("Failed to set gpio callback"); + return -EIO; + } + +#endif + +#if defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD) + k_sem_init(&data->drdy_sem, 0, K_SEM_MAX_LIMIT); + + k_thread_create(&data->drdy_thread, data->drdy_thread_stack, + CONFIG_WSEN_ISDS_2536030320001_THREAD_STACK_SIZE, + isds_2536030320001_drdy_thread, data, NULL, NULL, + K_PRIO_COOP(CONFIG_WSEN_ISDS_2536030320001_THREAD_PRIORITY), 0, K_NO_WAIT); +#if defined(CONFIG_WSEN_ISDS_2536030320001_EVENTS) + k_sem_init(&data->events_sem, 0, K_SEM_MAX_LIMIT); + + k_thread_create(&data->events_thread, data->events_thread_stack, + CONFIG_WSEN_ISDS_2536030320001_THREAD_STACK_SIZE, + isds_2536030320001_events_thread, data, NULL, NULL, + K_PRIO_COOP(CONFIG_WSEN_ISDS_2536030320001_THREAD_PRIORITY), 0, K_NO_WAIT); +#endif +#elif defined(CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD) + data->drdy_work.handler = isds_2536030320001_drdy_work_cb; +#if defined(CONFIG_WSEN_ISDS_2536030320001_EVENTS) + data->events_work.handler = isds_2536030320001_events_work_cb; +#endif +#endif + + /* Enable interrupt on INT_0/INT_1 in pulsed mode */ + if (ISDS_enableLatchedInterrupt(&data->sensor_interface, ISDS_disable) != WE_SUCCESS) { + LOG_ERR("Failed to disable latched mode"); + return -EIO; + } + + /* Enable data-ready interrupt on INT_0/INT_1 in pulsed mode */ + if (ISDS_enableDataReadyPulsed(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable data-ready pulsed mode"); + return -EIO; + } + + if (ISDS_enableInterrupts(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable interrupts"); + return -EIO; + } + +#ifdef CONFIG_WSEN_ISDS_2536030320001_TAP + if (cfg->accel_odr < ISDS_accOdr416Hz || cfg->accel_odr >= ISDS_accOdr1Hz6) { + LOG_WRN("The tap recognition feature requires a minimum output data rate " + "of 416 Hz"); + } + + if (ISDS_enableDoubleTapEvent(&data->sensor_interface, + (cfg->tap_mode == 1) ? ISDS_enable : ISDS_disable) != + WE_SUCCESS) { + LOG_ERR("Failed to enable/disable double tap event"); + return -EIO; + } + + if (ISDS_setTapThreshold(&data->sensor_interface, cfg->tap_threshold) != WE_SUCCESS) { + LOG_ERR("Failed to set tap threshold"); + return -EIO; + } + + if (cfg->tap_axis_enable[0] != 0) { + if (ISDS_enableTapX(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable tap recognition in X direction"); + return -EIO; + } + } + + if (cfg->tap_axis_enable[1] != 0) { + if (ISDS_enableTapY(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable tap recognition in Y direction"); + return -EIO; + } + } + + if (cfg->tap_axis_enable[2] != 0) { + if (ISDS_enableTapZ(&data->sensor_interface, ISDS_enable) != WE_SUCCESS) { + LOG_ERR("Failed to enable tap recognition in Z direction"); + return -EIO; + } + } + + if (ISDS_setTapShockTime(&data->sensor_interface, cfg->tap_shock) != WE_SUCCESS) { + LOG_ERR("Failed to set tap shock duration"); + return -EIO; + } + + if (ISDS_setTapLatencyTime(&data->sensor_interface, cfg->tap_latency) != WE_SUCCESS) { + LOG_ERR("Failed to set tap latency"); + return -EIO; + } + + if (ISDS_setTapQuietTime(&data->sensor_interface, cfg->tap_quiet) != WE_SUCCESS) { + LOG_ERR("Failed to set tap quiet time"); + return -EIO; + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_TAP */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_FREEFALL + if (ISDS_setFreeFallDuration(&data->sensor_interface, cfg->freefall_duration) != + WE_SUCCESS) { + LOG_ERR("Failed to set free-fall duration"); + return -EIO; + } + + if (ISDS_setFreeFallThreshold(&data->sensor_interface, cfg->freefall_threshold) != + WE_SUCCESS) { + LOG_ERR("Failed to set free-fall threshold"); + return -EIO; + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_FREEFALL */ + +#ifdef CONFIG_WSEN_ISDS_2536030320001_DELTA + if (ISDS_setWakeUpDuration(&data->sensor_interface, cfg->delta_duration) != WE_SUCCESS) { + LOG_ERR("Failed to set wake-up duration"); + return -EIO; + } + + if (ISDS_setWakeUpThreshold(&data->sensor_interface, cfg->delta_threshold) != WE_SUCCESS) { + LOG_ERR("Failed to set wake-up threshold"); + return -EIO; + } +#endif /* CONFIG_WSEN_ISDS_2536030320001_DELTA */ + + return 0; +} diff --git a/dts/bindings/sensor/we,wsen-isds-2536030320001-common.yaml b/dts/bindings/sensor/we,wsen-isds-2536030320001-common.yaml new file mode 100644 index 0000000000000..266bac00f7466 --- /dev/null +++ b/dts/bindings/sensor/we,wsen-isds-2536030320001-common.yaml @@ -0,0 +1,194 @@ +# Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG +# SPDX-License-Identifier: Apache-2.0 + +properties: + events-interrupt-gpios: + type: phandle-array + description: | + Events Interrupt pin (Tap, Freefall, .. etc) (INT0 Pin). + Interrupts are active high by default. + + drdy-interrupt-gpios: + type: phandle-array + description: | + DRDY Interrupt pin (INT1 Pin). + Interrupts are active high by default. + + accel-odr: + type: string + required: true + enum: + - "0" # Accelerometer turned off + - "12.5" + - "26" + - "52" + - "104" + - "208" + - "416" + - "833" + - "1660" + - "3330" + - "6660" + - "1.6" + description: Accelerometer output data rate in Hz + + accel-range: + type: int + default: 2 + enum: + - 2 # 2g + - 16 # 16g + - 4 # 4g + - 8 # 8g + description: Accelerometer range (full scale) in g. Defaults to 2, which is + the configuration at power-up. + + gyro-odr: + type: string + required: true + enum: + - "0" # Gyroscope turned off + - "12.5" + - "26" + - "52" + - "104" + - "208" + - "416" + - "833" + - "1660" + - "3330" + - "6660" + description: Gyroscope output data rate in Hz + + gyro-range: + type: int + default: 250 + enum: + - 250 + - 125 + - 500 + - 1000 + - 2000 + description: Gyroscope range (full scale) in degrees per second. Defaults to 250, + which is the configuration at power-up. + + tap-mode: + type: int + default: 0 + enum: + - 0 # Only single tap + - 1 # Single and double tap + description: Tap mode (only single tap or single and double tap). + Defaults to 0, which is the configuration at power-up. + + tap-threshold: + type: int + default: 0 + description: | + Tap X/Y/Z axis threshold (unsigned 5-bit, ranging from 0x00 to 0x1F). + Defaults to zero, which is the configuration at power-up. + + Threshold for tap recognition on the X/Y/Z axes depending on selected + measurement range (full scale). + + Example: + + tap-threshold = <6> + + For FS=2g, this sets the threshold to 6 \* 2g/32 = 375mg. + + tap-axis-enable: + type: array + default: [1, 1, 1] + description: | + Enable/disable tap recognition on X/Y/Z axis. + + Defaults to "<1>, <1>, <1>", i.e. tap recognition enabled for all three axes. + Setting elements of this array to zero disables tap recognition for the + corresponding axes. + + tap-shock: + type: int + default: 0x0 + description: | + Maximum duration of over-threshold event when detecting taps (unsigned + 2-bit, ranging from 0x0 to 0x3). Defaults to zero, which is the + configuration at power-up. + + A value of 0 corresponds to 4 \* 1/ODR and 1 LSB = 8 \* 1/ODR. + + tap-latency: + type: int + default: 0x0 + description: | + Maximum duration time gap for double-tap recognition (unsigned 4-bit, + ranging from 0x0 to 0xF). Defaults to zero, which is the + configuration at power-up. + + A value of 0 corresponds to 16 \* 1/ODR and 1 LSB = 32 \* 1/ODR. + + tap-quiet: + type: int + default: 0x0 + description: | + Expected quiet time for double-tap recognition (unsigned 2-bit, ranging + from 0x0 to 0x3). This defines the time after the first detected tap in + which there must not be any over-threshold event. Defaults to zero, which + is the configuration at power-up. + + A value of 0 corresponds to 2 \* 1/ODR and 1 LSB = 4 \* 1/ODR. + + freefall-duration: + type: int + default: 0x0 + description: | + Minimum duration of free-fall event (unsigned 6-bit, ranging from 0x0 + to 0x3F). Defaults to 0, which is the configuration at power-up. + + 1 LSB = 1 \* 1/ODR. + + freefall-threshold: + type: int + default: 0x0 + enum: + - 0 # 156 mg + - 1 # 219 mg + - 2 # 250 mg + - 3 # 312 mg + - 4 # 344 mg + - 5 # 406 mg + - 6 # 469 mg + - 7 # 500 mg + description: | + Free-fall threshold (amplitude of the "free-fall zone" around zero-g + level where the accelerations of all axes are small enough to generate + the free-fall interrupt). Defaults to 0, which is the configuration + at power-up. + + delta-duration: + type: int + default: 0x0 + enum: + - 0 + - 1 + - 2 + - 3 + description: | + Wake-up duration, i.e. minimum duration of wake-up event to be + recognized (unsigned 2-bit, ranging from 0x0 to 0x3). + Defaults to 0, which is the configuration at power-up. + + 1 LSB = 1 \* 1/ODR + + delta-threshold: + type: int + default: 0x0 + description: | + Wake-up threshold (unsigned 6-bit, ranging from 0x0 to 0x3F). + The threshold value is applicable to both positive and negative + acceleration data. A wake-up event is recognized, if at least + one of the acceleration axis values exceeds the threshold value. + + Defaults to 0, which is the configuration at power-up. + + 1 LSB = 1/64 of measurement range (full scale). diff --git a/dts/bindings/sensor/we,wsen-isds-2536030320001-i2c.yaml b/dts/bindings/sensor/we,wsen-isds-2536030320001-i2c.yaml new file mode 100644 index 0000000000000..18d0ac3eb3538 --- /dev/null +++ b/dts/bindings/sensor/we,wsen-isds-2536030320001-i2c.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG +# SPDX-License-Identifier: Apache-2.0 + +description: | + Würth Elektronik WSEN-ISDS-2536030320001 acceleration and gyroscope sensor with + integrated temperature sensor (I2C bus) + +compatible: "we,wsen-isds-2536030320001" + +include: ["i2c-device.yaml", "we,wsen-isds-2536030320001-common.yaml"] diff --git a/dts/bindings/sensor/we,wsen-isds-2536030320001-spi.yaml b/dts/bindings/sensor/we,wsen-isds-2536030320001-spi.yaml new file mode 100644 index 0000000000000..b6f4f49c7f3e4 --- /dev/null +++ b/dts/bindings/sensor/we,wsen-isds-2536030320001-spi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Würth Elektronik eiSos GmbH & Co. KG +# SPDX-License-Identifier: Apache-2.0 + +description: | + Würth Elektronik WSEN-ISDS-2536030320001 acceleration and gyroscope sensor with + integrated temperature sensor (SPI bus) + +compatible: "we,wsen-isds-2536030320001" + +include: ["spi-device.yaml", "we,wsen-isds-2536030320001-common.yaml"] diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 220e0e9969c4d..613c712e3455a 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1382,3 +1382,18 @@ test_i2c_lsm9ds1_mag: lsm9ds1_mag@b9 { compatible = "st,lsm9ds1_mag"; reg = <0xb9>; }; + +test_i2c_wsen_isds_2536030320001: wsen_isds_2536030320001@ba { + compatible = "we,wsen-isds-2536030320001"; + reg = <0xba>; + accel-odr = "416"; + gyro-odr = "416"; + tap-threshold = < 9 >; + tap-latency = < 5 >; + tap-shock = < 2 >; + tap-quiet = < 1 >; + tap-mode = < 1 >; + tap-axis-enable = < 0 0 1 >; + events-interrupt-gpios = <&test_gpio 0 0>; + drdy-interrupt-gpios = <&test_gpio 0 0>; +}; diff --git a/tests/drivers/build_all/sensor/sensors_trigger_global.conf b/tests/drivers/build_all/sensor/sensors_trigger_global.conf index a9134740db764..dfe6f8fe38f7e 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_global.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_global.conf @@ -69,6 +69,7 @@ CONFIG_TSL2540_TRIGGER_GLOBAL_THREAD=y CONFIG_TSL2591_TRIGGER_GLOBAL_THREAD=y CONFIG_VCNL36825T_TRIGGER_GLOBAL_THREAD=y CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD=y +CONFIG_WSEN_ISDS_2536030320001_TRIGGER_GLOBAL_THREAD=y CONFIG_WSEN_ITDS_2533020201601_TRIGGER_GLOBAL_THREAD=y CONFIG_WSEN_PADS_2511020213301_TRIGGER_GLOBAL_THREAD=y CONFIG_WSEN_TIDS_2521020222501_TRIGGER_GLOBAL_THREAD=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_none.conf b/tests/drivers/build_all/sensor/sensors_trigger_none.conf index 7c196b9e422c5..a70ff981b0bb5 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_none.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_none.conf @@ -71,6 +71,7 @@ CONFIG_TSL2540_TRIGGER_NONE=y CONFIG_TSL2591_TRIGGER_NONE=y CONFIG_VCNL36825T_TRIGGER_NONE=y CONFIG_VCNL4040_TRIGGER_NONE=y +CONFIG_WSEN_ISDS_2536030320001_TRIGGER_NONE=y CONFIG_WSEN_ITDS_2533020201601_TRIGGER_NONE=y CONFIG_WSEN_PADS_2511020213301_TRIGGER_NONE=y CONFIG_WSEN_TIDS_2521020222501_TRIGGER_NONE=y diff --git a/tests/drivers/build_all/sensor/sensors_trigger_own.conf b/tests/drivers/build_all/sensor/sensors_trigger_own.conf index bbee6eb03824f..56b76eab7cbc9 100644 --- a/tests/drivers/build_all/sensor/sensors_trigger_own.conf +++ b/tests/drivers/build_all/sensor/sensors_trigger_own.conf @@ -67,6 +67,7 @@ CONFIG_TSL2540_TRIGGER_OWN_THREAD=y CONFIG_TSL2591_TRIGGER_OWN_THREAD=y CONFIG_VCNL36825T_TRIGGER_OWN_THREAD=y CONFIG_VCNL4040_TRIGGER_OWN_THREAD=y +CONFIG_WSEN_ISDS_2536030320001_TRIGGER_OWN_THREAD=y CONFIG_WSEN_ITDS_2533020201601_TRIGGER_OWN_THREAD=y CONFIG_WSEN_PADS_2511020213301_TRIGGER_OWN_THREAD=y CONFIG_WSEN_TIDS_2521020222501_TRIGGER_OWN_THREAD=y