diff --git a/drivers/sensor/adi/CMakeLists.txt b/drivers/sensor/adi/CMakeLists.txt index cf2e79c2db61e..de3e2dd53bffa 100644 --- a/drivers/sensor/adi/CMakeLists.txt +++ b/drivers/sensor/adi/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory_ifdef(CONFIG_ADLTC2990 adltc2990) add_subdirectory_ifdef(CONFIG_ADT7310 adt7310) add_subdirectory_ifdef(CONFIG_ADT7420 adt7420) add_subdirectory_ifdef(CONFIG_ADXL345 adxl345) +add_subdirectory_ifdef(CONFIG_ADXL34X adxl34x) add_subdirectory_ifdef(CONFIG_ADXL362 adxl362) add_subdirectory_ifdef(CONFIG_ADXL367 adxl367) add_subdirectory_ifdef(CONFIG_ADXL372 adxl372) diff --git a/drivers/sensor/adi/Kconfig b/drivers/sensor/adi/Kconfig index 76305d1fd2720..a75623a620905 100644 --- a/drivers/sensor/adi/Kconfig +++ b/drivers/sensor/adi/Kconfig @@ -6,6 +6,7 @@ source "drivers/sensor/adi/adltc2990/Kconfig" source "drivers/sensor/adi/adt7310/Kconfig" source "drivers/sensor/adi/adt7420/Kconfig" source "drivers/sensor/adi/adxl345/Kconfig" +source "drivers/sensor/adi/adxl34x/Kconfig" source "drivers/sensor/adi/adxl362/Kconfig" source "drivers/sensor/adi/adxl367/Kconfig" source "drivers/sensor/adi/adxl372/Kconfig" diff --git a/drivers/sensor/adi/adxl34x/CMakeLists.txt b/drivers/sensor/adi/adxl34x/CMakeLists.txt new file mode 100644 index 0000000000000..bed190f91fccf --- /dev/null +++ b/drivers/sensor/adi/adxl34x/CMakeLists.txt @@ -0,0 +1,20 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +zephyr_library() + +zephyr_library_sources( + adxl34x.c + adxl34x_configure.c + adxl34x_attr.c +) +zephyr_library_sources_ifdef(CONFIG_ADXL34X_BUS_I2C adxl34x_i2c.c) +zephyr_library_sources_ifdef(CONFIG_ADXL34X_BUS_SPI adxl34x_spi.c) +zephyr_library_sources_ifdef(CONFIG_ADXL34X_TRIGGER adxl34x_trigger.c) +zephyr_library_sources_ifdef(CONFIG_ADXL34X_DECODER adxl34x_decoder.c) +zephyr_library_sources_ifdef(CONFIG_ADXL34X_ASYNC_API adxl34x_rtio.c) + +zephyr_library_sources_ifdef(CONFIG_EMUL_ADXL34X adxl34x_emul.c) +zephyr_include_directories_ifdef(CONFIG_EMUL_ADXL34X .) diff --git a/drivers/sensor/adi/adxl34x/Kconfig b/drivers/sensor/adi/adxl34x/Kconfig new file mode 100644 index 0000000000000..7ced4b751e495 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/Kconfig @@ -0,0 +1,143 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig ADXL34X + bool "ADXL343, ADXL344, ADXL345 and ADXL346 accelerometers" + default y + depends on DT_HAS_ADI_ADXL34X_ENABLED + select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ADI_ADXL34X),i2c) + select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ADI_ADXL34X),spi) + help + Enable support for the ADXL343, ADXL344, ADXL345 and ADXL346 Three + Axis accelerometer + +if ADXL34X + +config ADXL34X_BUS_I2C + bool + default y + depends on $(dt_compat_on_bus,$(DT_COMPAT_ADI_ADXL34X),i2c) + +config ADXL34X_BUS_SPI + bool + default y + depends on $(dt_compat_on_bus,$(DT_COMPAT_ADI_ADXL34X),spi) + +config ADXL34X_DECODER + bool "Decoder logic" + default y + select ADXL34X_ASYNC_API + depends on SENSOR_ASYNC_API + help + Compile the ADXL34X decoder API which allows decoding raw data + returned from the sensor. + +if ADXL34X_DECODER + +choice + prompt "Decoder data type" + default ADXL34X_DATA_TYPE_Q31 + help + Data type returned by the decoder. + +config ADXL34X_DATA_TYPE_Q31 + bool "q31" + help + Return q31 sensor type values from the decoder + +config ADXL34X_DATA_TYPE_SENSOR_VALUE + bool "sensor_value" + help + Return sensor_value sensor type values from the decoder + +config ADXL34X_DATA_TYPE_DOUBLE + bool "double" + help + Return double sensor type values from the decoder + +endchoice + +endif # ADXL34X_DECODER + +choice ADXL34X_FIFO_MODE + prompt "FIFO mode" + default ADXL34X_FIFO_MODE_BYPASS + help + Specify the FIFO mode to be used by the driver + +config ADXL34X_FIFO_MODE_BYPASS + bool "Bypass" + help + No FIFO, the FIFO of the adxl34x is bypassed. The sensor can only be + used in polling mode, no triggers are available. Use this mode when + the interrupt line of the adxl34x is not connected. + +config ADXL34X_FIFO_MODE_FIFO + bool "FIFO" + select ADXL34X_TRIGGER + select ADXL34X_ASYNC_API + depends on SENSOR_ASYNC_API + help + FIFO collects up to 32 values and then stops collecting data, + collecting new data only when FIFO is not full. Use this config option + to enable streaming sensor data via RTIO subsystem. This mode requires + the interrupt line of the adxl34x to be connected. + +config ADXL34X_FIFO_MODE_STREAM + bool "Stream" + select ADXL34X_TRIGGER + select ADXL34X_ASYNC_API + depends on SENSOR_ASYNC_API + help + FIFO holds the last 32 data values. When FIFO is full, the oldest data + is overwritten with newer data. This mode requires the interrupt line + of the adxl34x to be connected. + +config ADXL34X_FIFO_MODE_TRIGGER + bool "Trigger" + select ADXL34X_TRIGGER + select ADXL34X_ASYNC_API + depends on SENSOR_ASYNC_API + help + When triggered by the trigger bit, FIFO holds the last data samples + before the trigger event and then continues to collect data until + full. New data is collected only when FIFO is not full. This mode + requires the interrupt line of the adxl34x to be connected. + +endchoice + +config EMUL_ADXL34X + bool "Emulator for the ADXL34X" + default y + depends on EMUL + help + Enable the hardware emulator for the ADXL34X. Doing so allows + exercising sensor APIs for this driver in native_sim and qemu. + +config ADXL34X_EXTENDED_API + bool "Extended API" + default y + help + Enable the extended API for the ADXL34X. This option gives access to + all registers of the adxl34x when not using user space. + +config ADXL34X_ADXL345_COMPATIBLE + bool "ADXL345 compatible" + default n + help + Enable compatibility with the (old) ADXL345 driver. Enable this + feature when the (existing) application was written for the ADXL345 + driver. The ADXL345 driver uses the FIFO when calling + sensor_sample_fetch, and will return the number of samples collected + (which is not the official API). This allows sensor_sample_get to be + called multiple times to read all FIFO data. + +config ADXL34X_TRIGGER + bool + +config ADXL34X_ASYNC_API + bool + +endif # ADXL34X diff --git a/drivers/sensor/adi/adxl34x/adxl34x.c b/drivers/sensor/adi/adxl34x/adxl34x.c new file mode 100644 index 0000000000000..fa72fb835a8b6 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x_attr.h" +#include "adxl34x_configure.h" +#include "adxl34x_decoder.h" +#include "adxl34x_private.h" +#include "adxl34x_reg.h" +#include "adxl34x_rtio.h" +#include "adxl34x_trigger.h" +#include "adxl34x_convert.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include "adxl34x_i2c.h" +#endif +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include "adxl34x_spi.h" +#endif + +LOG_MODULE_REGISTER(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +/** + * Convert a raw sensor value from the sensor registery to a value in g + * + * @param[in] dev Pointer to the sensor device + * @param[out] value Pointer to store the result + * @param[in] raw_value The value of the raw sensor data + * @param[in] range_scale The scale of range + */ +static void adxl34x_convert_sample(struct sensor_value *value, int16_t raw_value, + uint16_t range_scale) +{ + int32_t val_ug = raw_value * range_scale * 100; + + sensor_ug_to_ms2(val_ug, value); +} + +/** + * Callback API for fetching data of the sensor + * + * @param[in] dev Pointer to the sensor device + * @param[in] chan The channel that needs updated + * @return 0 if successful, negative errno code if failure. + * @see sensor_sample_fetch() for more information. + */ +static int adxl34x_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + int rc; + uint8_t rx_buf[6]; + enum pm_device_state pm_state; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ACCEL_X && chan != SENSOR_CHAN_ACCEL_Y && + chan != SENSOR_CHAN_ACCEL_Z && chan != SENSOR_CHAN_ACCEL_XYZ) { + return -ENOTSUP; + } + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state != PM_DEVICE_STATE_ACTIVE) { + LOG_DBG("Device is suspended, fetch is unavailable"); + return -EIO; + } + + data->timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); + +#if CONFIG_ADXL34X_ADXL345_COMPATIBLE + struct adxl34x_fifo_status fifo_status; + + rc = adxl34x_get_fifo_status(dev, &fifo_status); + if (rc != 0) { + LOG_ERR("Failed to read from device"); + return -EIO; + } + + __ASSERT_NO_MSG(ARRAY_SIZE(data->accel_x) >= fifo_status.entries); + for (uint8_t i = 0; i < fifo_status.entries; i++) { + /* Read accel x, y and z values */ + rc = config->bus_read_buf(dev, ADXL34X_REG_DATA, rx_buf, sizeof(rx_buf)); + if (rc != 0) { + LOG_ERR("Failed to read from device"); + return -EIO; + } + + if (chan == SENSOR_CHAN_ACCEL_X || chan == SENSOR_CHAN_ACCEL_XYZ || + chan == SENSOR_CHAN_ALL) { + data->accel_x[i] = sys_get_le16(rx_buf); + } + if (chan == SENSOR_CHAN_ACCEL_Y || chan == SENSOR_CHAN_ACCEL_XYZ || + chan == SENSOR_CHAN_ALL) { + data->accel_y[i] = sys_get_le16(rx_buf + 2); + } + if (chan == SENSOR_CHAN_ACCEL_Z || chan == SENSOR_CHAN_ACCEL_XYZ || + chan == SENSOR_CHAN_ALL) { + data->accel_z[i] = sys_get_le16(rx_buf + 4); + } + } + + data->sample_number = 0; + return fifo_status.entries; /* Return the number of samples fetched */ + +#else + /* Read accel x, y and z values */ + rc = config->bus_read_buf(dev, ADXL34X_REG_DATA, rx_buf, sizeof(rx_buf)); + if (rc != 0) { + LOG_ERR("Failed to read from device"); + return -EIO; + } + + if (chan == SENSOR_CHAN_ACCEL_X || chan == SENSOR_CHAN_ACCEL_XYZ || + chan == SENSOR_CHAN_ALL) { + data->accel_x = sys_get_le16(rx_buf); + } + if (chan == SENSOR_CHAN_ACCEL_Y || chan == SENSOR_CHAN_ACCEL_XYZ || + chan == SENSOR_CHAN_ALL) { + data->accel_y = sys_get_le16(rx_buf + 2); + } + if (chan == SENSOR_CHAN_ACCEL_Z || chan == SENSOR_CHAN_ACCEL_XYZ || + chan == SENSOR_CHAN_ALL) { + data->accel_z = sys_get_le16(rx_buf + 4); + } + return 0; +#endif /* CONFIG_ADXL34X_ADXL345_COMPATIBLE */ +} + +/** + * Callback API for getting a reading from a sensor + * + * @param[in] dev Pointer to the sensor device + * @param[in] chan The channel to read + * @param[out] val Where to store the value + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct adxl34x_dev_data *data = dev->data; + uint16_t range_scale = adxl34x_range_conv[data->cfg.data_format.range]; + +#if CONFIG_ADXL34X_ADXL345_COMPATIBLE + if (data->sample_number >= ADXL34X_FIFO_SIZE) { + return -ENODATA; + } + switch (chan) { /* NOLINT(clang-diagnostic-switch-enum) */ + case SENSOR_CHAN_ACCEL_X: + adxl34x_convert_sample(val, data->accel_x[data->sample_number], range_scale); + break; + case SENSOR_CHAN_ACCEL_Y: + adxl34x_convert_sample(val, data->accel_y[data->sample_number], range_scale); + break; + case SENSOR_CHAN_ACCEL_Z: + adxl34x_convert_sample(val, data->accel_z[data->sample_number], range_scale); + break; + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ALL: + adxl34x_convert_sample(&val[0], data->accel_x[data->sample_number], range_scale); + adxl34x_convert_sample(&val[1], data->accel_y[data->sample_number], range_scale); + adxl34x_convert_sample(&val[2], data->accel_z[data->sample_number], range_scale); + break; + default: + return -ENOTSUP; + } + data->sample_number++; + +#else + switch (chan) { /* NOLINT(clang-diagnostic-switch-enum) */ + case SENSOR_CHAN_ACCEL_X: + adxl34x_convert_sample(val, data->accel_x, range_scale); + break; + case SENSOR_CHAN_ACCEL_Y: + adxl34x_convert_sample(val, data->accel_y, range_scale); + break; + case SENSOR_CHAN_ACCEL_Z: + adxl34x_convert_sample(val, data->accel_z, range_scale); + break; + case SENSOR_CHAN_ACCEL_XYZ: + case SENSOR_CHAN_ALL: + adxl34x_convert_sample(&val[0], data->accel_x, range_scale); + adxl34x_convert_sample(&val[1], data->accel_y, range_scale); + adxl34x_convert_sample(&val[2], data->accel_z, range_scale); + break; + default: + return -ENOTSUP; + } +#endif /* CONFIG_ADXL34X_ADXL345_COMPATIBLE */ + + return 0; +} + +/** + * @brief The sensor driver API callbacks + * @var adxl34x_api + */ +static const struct sensor_driver_api adxl34x_api = { + .sample_fetch = &adxl34x_sample_fetch, + .channel_get = &adxl34x_channel_get, + .attr_set = adxl34x_attr_set, + .attr_get = adxl34x_attr_get, +#ifdef CONFIG_ADXL34X_TRIGGER + .trigger_set = adxl34x_trigger_set, +#endif +#ifdef CONFIG_ADXL34X_DECODER + .get_decoder = adxl34x_get_decoder, +#endif +#ifdef CONFIG_ADXL34X_ASYNC_API + .submit = adxl34x_submit, +#endif +}; + +/** + * The init function of the driver + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_init(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc; + uint8_t devid; + + if (config->bus_init(dev)) { + LOG_ERR("Device not ready"); + return -ENODEV; + } + + rc = adxl34x_get_devid(dev, &devid); + const bool known_device = devid == ADXL343_DEVID || devid == ADXL344_DEVID || + devid == ADXL345_DEVID || /* NOLINT(misc-redundant-expression) */ + devid == ADXL346_DEVID; /* NOLINT(misc-redundant-expression) */ + if (rc != 0 || !known_device) { + LOG_ERR("Failed to read id from device (%s)", dev->name); + return -ENODEV; + } + + if (IS_ENABLED(CONFIG_ADXL34X_TRIGGER)) { + rc = adxl34x_trigger_init(dev); + if (rc != 0) { + LOG_ERR("Failed to initialize device (%s) triggers", dev->name); + return -EIO; + } + } + + /* Check if the configuration in the device tree provided is valid. */ + if (config->dt_int_pin != 1 && config->dt_int_pin != 2) { + LOG_ERR("Failed to configure device (%s), invalid int-pin provided (%d)", dev->name, + config->dt_int_pin); + return -ENOTSUP; + } + if (config->dt_packet_size < 1 || config->dt_packet_size > 31) { + LOG_ERR("Failed to configure device (%s), invalid packet-size provided (%d)", + dev->name, config->dt_packet_size); + return -ENOTSUP; + } + + /* The adxl34x doesn't have a reset option, so defaults are set explicitly. */ + rc = adxl34x_get_configuration(dev); + if (rc != 0) { + LOG_ERR("Failed to read configuration from device (%s)", dev->name); + return -EIO; + } + struct adxl34x_cfg cfg = { + /* Initialize the sensor in the suspended state when power management is active. */ + .power_ctl.measure = COND_CODE_1(CONFIG_PM_DEVICE_RUNTIME, (0), (1)), + /* Directly enable stream mode when in adxl345 compatibility mode. */ + .fifo_ctl.fifo_mode = + COND_CODE_1(CONFIG_ADXL34X_ADXL345_COMPATIBLE, (ADXL34X_FIFO_MODE_STREAM), + (ADXL34X_FIFO_MODE_BYPASS)), + .bw_rate.rate = config->dt_rate, + .data_format.range = config->dt_range, + }; + +#ifdef CONFIG_ADXL34X_EXTENDED_API + if (devid == ADXL344_DEVID || devid == ADXL346_DEVID) { + cfg.orient_conf = (struct adxl34x_orient_conf){ + .dead_zone = ADXL34X_DEAD_ZONE_ANGLE_15_2, + .divisor = ADXL34X_DIVISOR_ODR_400, + }; + } +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + + rc = adxl34x_configure(dev, &cfg); + if (rc != 0) { + LOG_ERR("Failed to write configuration to device (%s)", dev->name); + return -EIO; + } + +#ifdef CONFIG_PM_DEVICE_RUNTIME + /* Enable device runtime power management. */ + pm_device_init_suspended(dev); + + rc = pm_device_runtime_enable(dev); + if (rc < 0 && rc != -ENOSYS) { + LOG_ERR("Failed to enabled runtime power management"); + return -EIO; + } +#endif /* CONFIG_PM_DEVICE_RUNTIME */ + + return 0; +} + +#ifdef CONFIG_PM_DEVICE_RUNTIME + +/** + * Set the power state to active or inactive + * + * @param[in] dev Pointer to the sensor device + * @param[in] active The new state of the sensor + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_set_active_state(const struct device *dev, bool active) +{ + int rc; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_power_ctl power_ctl = data->cfg.power_ctl; + + power_ctl.measure = active; + rc = adxl34x_set_power_ctl(dev, &power_ctl); + if (rc != 0) { + LOG_WRN("Failed to set device into %s mode", active ? "active" : "suspended"); + } + return rc; +} + +/** + * Callback API for power management when PM state has changed + * + * @param[in] dev Pointer to the sensor device + * @param[in] action Requested action + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_pm(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_RESUME: + adxl34x_set_active_state(dev, true); + break; + case PM_DEVICE_ACTION_SUSPEND: + adxl34x_set_active_state(dev, false); + break; + case PM_DEVICE_ACTION_TURN_ON: + case PM_DEVICE_ACTION_TURN_OFF: + default: + return -ENOTSUP; + } + + return 0; +} + +#endif /* CONFIG_PM_DEVICE_RUNTIME */ + +#define ADXL34X_INIT(i) \ + static struct adxl34x_dev_data adxl34x_dev_data_##i; \ + \ + static const struct adxl34x_dev_config adxl34x_dev_config_##i = { \ + COND_CODE_1(DT_INST_ON_BUS(i, spi), (ADXL34X_CONFIG_SPI(i)), \ + (ADXL34X_CONFIG_I2C(i))), \ + .gpio_int1 = GPIO_DT_SPEC_INST_GET_OR(i, int_gpios, {0}), \ + .dt_int_pin = DT_INST_PROP(i, int_pin), \ + .dt_packet_size = DT_INST_PROP(i, packet_size), \ + .dt_rate = DT_INST_ENUM_IDX(i, accel_frequency), \ + .dt_range = DT_INST_ENUM_IDX(i, accel_range), \ + }; \ + \ + IF_ENABLED(CONFIG_PM_DEVICE_RUNTIME, (PM_DEVICE_DT_INST_DEFINE(i, adxl34x_pm))); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE( \ + i, adxl34x_init, \ + COND_CODE_1(CONFIG_PM_DEVICE_RUNTIME, (PM_DEVICE_DT_INST_GET(i)), (NULL)), \ + &adxl34x_dev_data_##i, &adxl34x_dev_config_##i, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &adxl34x_api); + +DT_INST_FOREACH_STATUS_OKAY(ADXL34X_INIT) diff --git a/drivers/sensor/adi/adxl34x/adxl34x_attr.c b/drivers/sensor/adi/adxl34x/adxl34x_attr.c new file mode 100644 index 0000000000000..10d4c127272c2 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_attr.c @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x_reg.h" +#include "adxl34x_private.h" +#include "adxl34x_attr.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +static const uint16_t offset_ug_lsb = 15600; + +/** + * @brief Converstion array to convert frequencies from sensor-values to its enumerated + * values, and back. + * + * @var reg_to_hz_conv + */ +static const struct sensor_value reg_to_hz_conv[] = { + /* clang-format off */ + [ADXL34X_ACCEL_FREQ_0_10] = {0, 100000}, + [ADXL34X_ACCEL_FREQ_0_20] = {0, 200000}, + [ADXL34X_ACCEL_FREQ_0_39] = {0, 390000}, + [ADXL34X_ACCEL_FREQ_0_78] = {0, 780000}, + [ADXL34X_ACCEL_FREQ_1_56] = {1, 560000}, + [ADXL34X_ACCEL_FREQ_3_13] = {3, 130000}, + [ADXL34X_ACCEL_FREQ_6_25] = {6, 250000}, + [ADXL34X_ACCEL_FREQ_12_5] = {12, 500000}, + [ADXL34X_ACCEL_FREQ_25] = {25, 0}, + [ADXL34X_ACCEL_FREQ_50] = {50, 0}, + [ADXL34X_ACCEL_FREQ_100] = {100, 0}, + [ADXL34X_ACCEL_FREQ_200] = {200, 0}, + [ADXL34X_ACCEL_FREQ_400] = {400, 0}, + [ADXL34X_ACCEL_FREQ_800] = {800, 0}, + [ADXL34X_ACCEL_FREQ_1600] = {1600, 0}, + [ADXL34X_ACCEL_FREQ_3200] = {3200, 0}, + /* clang-format on */ +}; + +/** + * @brief Converstion array to convert accelero range from sensor-values to its + * enumerated values, and back. + * + * @var reg_to_range_conv + */ +static const struct sensor_value reg_to_range_conv[] = { + [ADXL34X_RANGE_2G] = {2, 0}, + [ADXL34X_RANGE_4G] = {4, 0}, + [ADXL34X_RANGE_8G] = {8, 0}, + [ADXL34X_RANGE_16G] = {16, 0}, +}; + +/** + * Convert a single number to a sensor value + * + * @param[in] u_value The number to convert times 1000000 + * @param[out] out Pointer to store the converted number + */ +static void sensor_value_from_u_value(int64_t u_value, struct sensor_value *out) +{ + __ASSERT_NO_MSG(out != NULL); + out->val1 = u_value / 100000LL; + out->val2 = u_value - out->val1 * 1000000LL; +} + +/** + * Convert a sensor_value to an enumeration + * + * @param[in] in The sensor value to convert + * @param[in] conv An array of sensor_values to lookup the enumeration + * @param[in] max The maximum index of the enumeration + * @return 0 if successful, negative errno code if failure. + */ +static int sensor_value_to_enum(const struct sensor_value *in, const struct sensor_value conv[], + const int max) +{ + int index = max; + + while (index >= 0) { + if (conv[index].val1 < in->val1) { + return index; + } else if (conv[index].val1 == in->val1 && conv[index].val2 <= in->val2) { + return index; + } + index--; + } + return 0; +} + +/** + * Convert an enumeration to a sensor value + * + * @param[in] value The enumerated value + * @param[in] conv An array of sensor_values to lookup the enumeration + * @param[out] out Pointer to were the store the converted value + * @param[in] max The maximum index of the enumeration value + * @return 0 if successful, negative errno code if failure. + */ +static int sensor_value_from_enum(int value, const struct sensor_value conv[], + struct sensor_value *out, int max) +{ + if (value <= max) { + out->val1 = conv[value].val1; + out->val2 = conv[value].val2; + } else { + LOG_WRN("Unknown value when converting attribute"); + out->val1 = 0; + out->val2 = 0; + } + return 0; +} + +/** + * Set the frequency sensor attribute + * + * @param[in] dev Pointer to the sensor device + * @param[in] in The new frequency of the sensor + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_freq_to_reg(const struct device *dev, const struct sensor_value *in) +{ + __ASSERT_NO_MSG(in != NULL); + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + int rc; + struct adxl34x_bw_rate bw_rate = cfg->bw_rate; + + bw_rate.rate = sensor_value_to_enum(in, reg_to_hz_conv, ADXL34X_ACCEL_FREQ_3200); + rc = adxl34x_set_bw_rate(dev, &bw_rate); + return rc; +} + +/** + * Convert the frequency sensor attribute + * + * @param[in] freq The current frequency of the sensor + * @param[out] out Pointer to were the store the frequency + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_reg_to_freq(enum adxl34x_accel_freq freq, struct sensor_value *out) +{ + __ASSERT_NO_MSG(out != NULL); + return sensor_value_from_enum(freq, reg_to_hz_conv, out, ADXL34X_ACCEL_FREQ_3200); +} + +/** + * Set the range sensor attribute + * + * @param[in] dev Pointer to the sensor device + * @param[in] in The new range of the sensor + * @return 0 if successful, negative errno code if failure. + */ +static enum adxl34x_accel_range adxl34x_range_to_reg(const struct device *dev, + const struct sensor_value *in) +{ + __ASSERT_NO_MSG(in != NULL); + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + int rc; + struct sensor_value range; + struct adxl34x_data_format data_format = cfg->data_format; + int32_t range_ug = sensor_ms2_to_ug(in); + + sensor_value_from_u_value(range_ug, &range); + data_format.range = sensor_value_to_enum(&range, reg_to_range_conv, ADXL34X_RANGE_16G); + rc = adxl34x_set_data_format(dev, &data_format); + return rc; +} + +/** + * Convert the range sensor attribute + * + * @param[in] range The current range of the sensor + * @param[out] out Pointer to were the store the range + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_reg_to_range(enum adxl34x_accel_range range, struct sensor_value *out) +{ + __ASSERT_NO_MSG(out != NULL); + return sensor_value_from_enum(range, reg_to_range_conv, out, ADXL34X_RANGE_16G); +} + +/** + * Set the offset sensor attribute + * + * @param[in] dev Pointer to the sensor device + * @param[in] chan The channel the attribute belongs to, if any + * @param[in] in The value to set the offset attribute to + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_offset_to_reg(const struct device *dev, enum sensor_channel chan, + const struct sensor_value *in) +{ + __ASSERT_NO_MSG(in != NULL); + int rc = 0; + + if (chan == SENSOR_CHAN_ACCEL_X || chan == SENSOR_CHAN_ACCEL_XYZ) { + const int32_t offset_x_ug = sensor_ms2_to_ug(&in[0]); + const int8_t offset_x = + (int8_t)CLAMP(offset_x_ug / offset_ug_lsb, INT8_MIN, INT8_MAX); + rc |= adxl34x_set_ofsx(dev, offset_x); + } + if (chan == SENSOR_CHAN_ACCEL_Y || chan == SENSOR_CHAN_ACCEL_XYZ) { + const int32_t offset_y_ug = sensor_ms2_to_ug(&in[1]); + const int8_t offset_y = + (int8_t)CLAMP(offset_y_ug / offset_ug_lsb, INT8_MIN, INT8_MAX); + rc |= adxl34x_set_ofsy(dev, offset_y); + } + if (chan == SENSOR_CHAN_ACCEL_Z || chan == SENSOR_CHAN_ACCEL_XYZ) { + const int32_t offset_z_ug = sensor_ms2_to_ug(&in[2]); + const int8_t offset_z = + (int8_t)CLAMP(offset_z_ug / offset_ug_lsb, INT8_MIN, INT8_MAX); + rc |= adxl34x_set_ofsz(dev, offset_z); + } + return rc; +} + +/** + * Get the offset sensor attribute + * + * @param[in] dev Pointer to the sensor device + * @param[in] chan The channel the attribute belongs to, if any + * @param[out] out Pointer to where to store the offset attribute + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_reg_to_offset(const struct device *dev, enum sensor_channel chan, + struct sensor_value *out) +{ + __ASSERT_NO_MSG(out != NULL); + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + + if (chan == SENSOR_CHAN_ACCEL_X || chan == SENSOR_CHAN_ACCEL_XYZ) { + const int64_t offset_x_ug = cfg->ofsx * offset_ug_lsb; + + sensor_ug_to_ms2(offset_x_ug, &out[0]); + } + if (chan == SENSOR_CHAN_ACCEL_Y || chan == SENSOR_CHAN_ACCEL_XYZ) { + const int64_t offset_y_ug = cfg->ofsy * offset_ug_lsb; + + sensor_ug_to_ms2(offset_y_ug, &out[1]); + } + if (chan == SENSOR_CHAN_ACCEL_Z || chan == SENSOR_CHAN_ACCEL_XYZ) { + const int64_t offset_z_ug = cfg->ofsz * offset_ug_lsb; + + sensor_ug_to_ms2(offset_z_ug, &out[2]); + } + return 0; +} + +/** + * Callback API upon setting a sensor's attributes + * + * @param[in] dev Pointer to the sensor device + * @param[in] chan The channel the attribute belongs to, if any + * @param[in] attr The attribute to set + * @param[in] val The value to set the attribute to + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, + const struct sensor_value *val) +{ + int rc = -ENOTSUP; + enum pm_device_state pm_state; + + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(val != NULL); + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ACCEL_X && chan != SENSOR_CHAN_ACCEL_Y && + chan != SENSOR_CHAN_ACCEL_Z && chan != SENSOR_CHAN_ACCEL_XYZ) { + LOG_ERR("Unsupported channel"); + return -EINVAL; + } + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state == PM_DEVICE_STATE_OFF) { + return -EIO; + } + + switch (attr) { /* NOLINT(clang-diagnostic-switch-enum) */ + case SENSOR_ATTR_SAMPLING_FREQUENCY: + rc = adxl34x_freq_to_reg(dev, val); + break; + case SENSOR_ATTR_LOWER_THRESH: + case SENSOR_ATTR_UPPER_THRESH: + case SENSOR_ATTR_SLOPE_TH: + case SENSOR_ATTR_SLOPE_DUR: + case SENSOR_ATTR_HYSTERESIS: + case SENSOR_ATTR_OVERSAMPLING: + break; + case SENSOR_ATTR_FULL_SCALE: + rc = adxl34x_range_to_reg(dev, val); + break; + case SENSOR_ATTR_OFFSET: + rc = adxl34x_offset_to_reg(dev, chan, val); + break; + case SENSOR_ATTR_CALIB_TARGET: + case SENSOR_ATTR_CONFIGURATION: + case SENSOR_ATTR_CALIBRATION: + case SENSOR_ATTR_FEATURE_MASK: + case SENSOR_ATTR_ALERT: + case SENSOR_ATTR_FF_DUR: + case SENSOR_ATTR_BATCH_DURATION: + case SENSOR_ATTR_COMMON_COUNT: + case SENSOR_ATTR_GAIN: + case SENSOR_ATTR_RESOLUTION: + break; + case SENSOR_ATTR_MAX: + default: + LOG_ERR("Unknown attribute"); + rc = -ENOTSUP; + } + return rc; +} + +/** + * Callback API upon getting a sensor's attributes + * + * @param[in] dev Pointer to the sensor device + * @param[in] chan The channel the attribute belongs to, if any + * @param[in] attr The attribute to get + * @param[out] val Pointer to where to store the attribute + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, + struct sensor_value *val) +{ + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + int rc = -ENOTSUP; + enum pm_device_state pm_state; + + __ASSERT_NO_MSG(dev != NULL); + __ASSERT_NO_MSG(val != NULL); + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ACCEL_X && chan != SENSOR_CHAN_ACCEL_Y && + chan != SENSOR_CHAN_ACCEL_Z && chan != SENSOR_CHAN_ACCEL_XYZ) { + LOG_ERR("Unsupported channel"); + return -EINVAL; + } + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state == PM_DEVICE_STATE_OFF) { + return -EIO; + } + + switch (attr) { /* NOLINT(clang-diagnostic-switch-enum) */ + case SENSOR_ATTR_SAMPLING_FREQUENCY: + rc = adxl34x_reg_to_freq(cfg->bw_rate.rate, val); + break; + case SENSOR_ATTR_LOWER_THRESH: + case SENSOR_ATTR_UPPER_THRESH: + case SENSOR_ATTR_SLOPE_TH: + case SENSOR_ATTR_SLOPE_DUR: + case SENSOR_ATTR_HYSTERESIS: + case SENSOR_ATTR_OVERSAMPLING: + break; + case SENSOR_ATTR_FULL_SCALE: + rc = adxl34x_reg_to_range(cfg->data_format.range, val); + break; + case SENSOR_ATTR_OFFSET: + rc = adxl34x_reg_to_offset(dev, chan, val); + break; + case SENSOR_ATTR_CALIB_TARGET: + case SENSOR_ATTR_CONFIGURATION: + case SENSOR_ATTR_CALIBRATION: + case SENSOR_ATTR_FEATURE_MASK: + case SENSOR_ATTR_ALERT: + case SENSOR_ATTR_FF_DUR: + case SENSOR_ATTR_BATCH_DURATION: + case SENSOR_ATTR_COMMON_COUNT: + case SENSOR_ATTR_GAIN: + case SENSOR_ATTR_RESOLUTION: + break; + case SENSOR_ATTR_MAX: + default: + LOG_ERR("Unknown attribute"); + } + + return rc; +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_attr.h b/drivers/sensor/adi/adxl34x/adxl34x_attr.h new file mode 100644 index 0000000000000..add46d4d717a4 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_attr.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_ATTR_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_ATTR_H_ + +#include + +#include +#include + +int adxl34x_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, + const struct sensor_value *val); +int adxl34x_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, + struct sensor_value *val); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_ATTR_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_configure.c b/drivers/sensor/adi/adxl34x/adxl34x_configure.c new file mode 100644 index 0000000000000..598add738ae17 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_configure.c @@ -0,0 +1,2114 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include + +#include "adxl34x_reg.h" +#include "adxl34x_private.h" +#include "adxl34x_configure.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +#ifdef CONFIG_ADXL34X_EXTENDED_API + +/** + * Fetches register 0x1D from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_thresh_tap(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + rc = config->bus_read(dev, ADXL34X_REG_THRESH_TAP, ®_value); + if (rc != 0) { + return rc; + } + + cfg->thresh_tap = reg_value; + LOG_DBG("Get thresh_tap: %u", cfg->thresh_tap); + return 0; +} + +/** + * Get register 0x1D from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] thresh_tap The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_thresh_tap(const struct device *dev, uint8_t *thresh_tap, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_thresh_tap(dev); + if (rc != 0) { + *thresh_tap = 0; + return rc; + } + } + *thresh_tap = data->cfg.thresh_tap; + return 0; +} + +/** + * Set register 0x1D from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] thresh_tap The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_thresh_tap(const struct device *dev, uint8_t thresh_tap) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (thresh_tap != cfg->thresh_tap) { + LOG_DBG("Set thresh_tap: %u", thresh_tap); + rc = config->bus_write(dev, ADXL34X_REG_THRESH_TAP, thresh_tap); + if (rc != 0) { + return rc; + } + cfg->thresh_tap = thresh_tap; + } + return 0; +} + +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + +/** + * Fetches register 0x1E from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_ofsx(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_OFSX, ®_value); + if (rc != 0) { + return rc; + } + + cfg->ofsx = reg_value; + LOG_DBG("Get ofsx: %u", cfg->ofsx); + return 0; +} + +/** + * Get register 0x1E from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] ofsx The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_ofsx(const struct device *dev, int8_t *ofsx, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_ofsx(dev); + if (rc != 0) { + *ofsx = 0; + return rc; + } + } + *ofsx = data->cfg.ofsx; + return 0; +} + +/** + * Set register 0x1E from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] ofsx The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_ofsx(const struct device *dev, int8_t ofsx) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (ofsx != cfg->ofsx) { + LOG_DBG("Set ofsx: %u", ofsx); + rc = config->bus_write(dev, ADXL34X_REG_OFSX, ofsx); + if (rc != 0) { + return rc; + } + cfg->ofsx = ofsx; + } + return 0; +} + +/** + * Fetches register 0x1F from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_ofsy(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_OFSY, ®_value); + if (rc != 0) { + return rc; + } + + cfg->ofsy = reg_value; + LOG_DBG("Get ofsy: %u", cfg->ofsy); + return 0; +} + +/** + * Get register 0x1F from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] ofsy The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_ofsy(const struct device *dev, int8_t *ofsy, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_ofsy(dev); + if (rc != 0) { + *ofsy = 0; + return rc; + } + } + *ofsy = data->cfg.ofsy; + return 0; +} + +/** + * Set register 0x1F from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] ofsy The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_ofsy(const struct device *dev, int8_t ofsy) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (ofsy != cfg->ofsy) { + LOG_DBG("Set ofsy: %u", ofsy); + rc = config->bus_write(dev, ADXL34X_REG_OFSY, ofsy); + if (rc != 0) { + return rc; + } + cfg->ofsy = ofsy; + } + return 0; +} + +/** + * Fetches register 0x20 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_ofsz(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_OFSZ, ®_value); + if (rc != 0) { + return rc; + } + + cfg->ofsz = reg_value; + LOG_DBG("Get ofsz: %u", cfg->ofsz); + return 0; +} + +/** + * Get register 0x20 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] ofsz The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_ofsz(const struct device *dev, int8_t *ofsz, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_ofsz(dev); + if (rc != 0) { + *ofsz = 0; + return rc; + } + } + *ofsz = data->cfg.ofsz; + return 0; +} + +/** + * Set register 0x20 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] ofsz The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_ofsz(const struct device *dev, int8_t ofsz) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (ofsz != cfg->ofsz) { + LOG_DBG("Set ofsz: %u", ofsz); + rc = config->bus_write(dev, ADXL34X_REG_OFSZ, ofsz); + if (rc != 0) { + return rc; + } + cfg->ofsz = ofsz; + } + return 0; +} + +#ifdef CONFIG_ADXL34X_EXTENDED_API + +/** + * Fetches register 0x21 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_dur(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_DUR, ®_value); + if (rc != 0) { + return rc; + } + + cfg->dur = reg_value; + LOG_DBG("Get dur: %u", cfg->dur); + return 0; +} + +/** + * Get register 0x21 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] dur The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_dur(const struct device *dev, uint8_t *dur, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_dur(dev); + if (rc != 0) { + *dur = 0; + return rc; + } + } + *dur = data->cfg.dur; + return 0; +} + +/** + * Set register 0x21 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] dur The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_dur(const struct device *dev, uint8_t dur) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (dur != cfg->dur) { + LOG_DBG("Set dur: %u", dur); + rc = config->bus_write(dev, ADXL34X_REG_DUR, dur); + if (rc != 0) { + return rc; + } + cfg->dur = dur; + } + return 0; +} + +/** + * Fetches register 0x22 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_latent(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_LATENT, ®_value); + if (rc != 0) { + return rc; + } + + cfg->latent = reg_value; + LOG_DBG("Get latent: %u", cfg->latent); + return 0; +} + +/** + * Get register 0x22 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] latent The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_latent(const struct device *dev, uint8_t *latent, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_latent(dev); + if (rc != 0) { + *latent = 0; + return rc; + } + } + *latent = data->cfg.latent; + return 0; +} + +/** + * Set register 0x22 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] latent The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_latent(const struct device *dev, uint8_t latent) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (latent != cfg->latent) { + LOG_DBG("Set latent: %u", latent); + rc = config->bus_write(dev, ADXL34X_REG_LATENT, latent); + if (rc != 0) { + return rc; + } + cfg->latent = latent; + } + return 0; +} + +/** + * Fetches register 0x23 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_window(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_WINDOW, ®_value); + if (rc != 0) { + return rc; + } + + cfg->window = reg_value; + LOG_DBG("Get window: %u", cfg->window); + return 0; +} + +/** + * Get register 0x23 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] window The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_window(const struct device *dev, uint8_t *window, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_window(dev); + if (rc != 0) { + *window = 0; + return rc; + } + } + *window = data->cfg.window; + return 0; +} + +/** + * Set register 0x23 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] window The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_window(const struct device *dev, uint8_t window) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (window != cfg->window) { + LOG_DBG("Set window: %u", window); + rc = config->bus_write(dev, ADXL34X_REG_WINDOW, window); + if (rc != 0) { + return rc; + } + cfg->window = window; + } + return 0; +} + +/** + * Fetches register 0x24 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_thresh_act(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_THRESH_ACT, ®_value); + if (rc != 0) { + return rc; + } + + cfg->thresh_act = reg_value; + LOG_DBG("Get thresh_act: %u", cfg->thresh_act); + return 0; +} + +/** + * Get register 0x24 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] thresh_act The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_thresh_act(const struct device *dev, uint8_t *thresh_act, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_thresh_act(dev); + if (rc != 0) { + *thresh_act = 0; + return rc; + } + } + *thresh_act = data->cfg.thresh_act; + return 0; +} + +/** + * Set register 0x24 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] thresh_act The act of thresh + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_thresh_act(const struct device *dev, uint8_t thresh_act) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (thresh_act != cfg->thresh_act) { + LOG_DBG("Set thresh_act: %u", thresh_act); + rc = config->bus_write(dev, ADXL34X_REG_THRESH_ACT, thresh_act); + if (rc != 0) { + return rc; + } + cfg->thresh_act = thresh_act; + } + return 0; +} + +/** + * Fetches register 0x25 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_thresh_inact(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_THRESH_INACT, ®_value); + if (rc != 0) { + return rc; + } + + cfg->thresh_inact = reg_value; + LOG_DBG("Get thresh_inact: %u", cfg->thresh_inact); + return 0; +} + +/** + * Get register 0x25 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] thresh_inact The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_thresh_inact(const struct device *dev, uint8_t *thresh_inact, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_thresh_inact(dev); + if (rc != 0) { + *thresh_inact = 0; + return rc; + } + } + *thresh_inact = data->cfg.thresh_inact; + return 0; +} + +/** + * Set register 0x25 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] thresh_inact The inact of thresh + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_thresh_inact(const struct device *dev, uint8_t thresh_inact) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (thresh_inact != cfg->thresh_inact) { + LOG_DBG("Set thresh_inact: %u", thresh_inact); + rc = config->bus_write(dev, ADXL34X_REG_THRESH_INACT, thresh_inact); + if (rc != 0) { + return rc; + } + cfg->thresh_inact = thresh_inact; + } + return 0; +} + +/** + * Fetches register 0x26 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_time_inact(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_TIME_INACT, ®_value); + if (rc != 0) { + return rc; + } + + cfg->time_inact = reg_value; + LOG_DBG("Get time_inact: %u", cfg->time_inact); + return 0; +} + +/** + * Get register 0x26 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] time_inact The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_time_inact(const struct device *dev, uint8_t *time_inact, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_time_inact(dev); + if (rc != 0) { + *time_inact = 0; + return rc; + } + } + *time_inact = data->cfg.time_inact; + return 0; +} + +/** + * Set register 0x26 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] time_inact The inact of time + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_time_inact(const struct device *dev, uint8_t time_inact) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (time_inact != cfg->time_inact) { + LOG_DBG("Set time_inact: %u", time_inact); + rc = config->bus_write(dev, ADXL34X_REG_TIME_INACT, time_inact); + if (rc != 0) { + return rc; + } + cfg->time_inact = time_inact; + } + return 0; +} + +/** + * Fetches register 0x27 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_act_inact_ctl(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_ACT_INACT_CTL, ®_value); + if (rc != 0) { + return rc; + } + + cfg->act_inact_ctl.act_acdc = FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_ACT_ACDC, reg_value); + cfg->act_inact_ctl.act_x_enable = + FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_ACT_X_ENABLE, reg_value); + cfg->act_inact_ctl.act_y_enable = + FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_ACT_Y_ENABLE, reg_value); + cfg->act_inact_ctl.act_z_enable = + FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_ACT_Z_ENABLE, reg_value); + cfg->act_inact_ctl.inact_acdc = FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_INACT_ACDC, reg_value); + cfg->act_inact_ctl.inact_x_enable = + FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_INACT_X_ENABLE, reg_value); + cfg->act_inact_ctl.inact_y_enable = + FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_INACT_Y_ENABLE, reg_value); + cfg->act_inact_ctl.inact_z_enable = + FIELD_GET(ADXL34X_REG_ACT_INACT_CTL_INACT_Z_ENABLE, reg_value); + LOG_DBG("Get act_inact_ctl - act_acdc:%u, act_x_enable: %u, act_y_enable: %u, " + "act_z_enable: %u, inact_acdc: %u, inact_x_enable: %u, inact_y_enable: %u, " + "inact_z_enable: %u", + cfg->act_inact_ctl.act_acdc, cfg->act_inact_ctl.act_x_enable, + cfg->act_inact_ctl.act_y_enable, cfg->act_inact_ctl.act_z_enable, + cfg->act_inact_ctl.inact_acdc, cfg->act_inact_ctl.inact_x_enable, + cfg->act_inact_ctl.inact_y_enable, cfg->act_inact_ctl.inact_z_enable); + return 0; +} + +/** + * Get register 0x27 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] act_inact_ctl The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_act_inact_ctl(const struct device *dev, struct adxl34x_act_inact_ctl *act_inact_ctl, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_act_inact_ctl(dev); + if (rc != 0) { + *act_inact_ctl = (struct adxl34x_act_inact_ctl){0}; + return rc; + } + } + *act_inact_ctl = data->cfg.act_inact_ctl; + return 0; +} + +/** + * Set register 0x27 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] act_inact_ctl The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_act_inact_ctl(const struct device *dev, struct adxl34x_act_inact_ctl *act_inact_ctl) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (act_inact_ctl->act_acdc != cfg->act_inact_ctl.act_acdc || + act_inact_ctl->act_x_enable != cfg->act_inact_ctl.act_x_enable || + act_inact_ctl->act_y_enable != cfg->act_inact_ctl.act_y_enable || + act_inact_ctl->act_z_enable != cfg->act_inact_ctl.act_z_enable || + act_inact_ctl->inact_acdc != cfg->act_inact_ctl.inact_acdc || + act_inact_ctl->inact_x_enable != cfg->act_inact_ctl.inact_x_enable || + act_inact_ctl->inact_y_enable != cfg->act_inact_ctl.inact_y_enable || + act_inact_ctl->inact_z_enable != cfg->act_inact_ctl.inact_z_enable) { + + reg_value = + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_ACT_ACDC, act_inact_ctl->act_acdc) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_ACT_X_ENABLE, + act_inact_ctl->act_x_enable) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_ACT_Y_ENABLE, + act_inact_ctl->act_y_enable) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_ACT_Z_ENABLE, + act_inact_ctl->act_z_enable) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_INACT_ACDC, + act_inact_ctl->inact_acdc) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_INACT_X_ENABLE, + act_inact_ctl->inact_x_enable) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_INACT_Y_ENABLE, + act_inact_ctl->inact_y_enable) | + FIELD_PREP(ADXL34X_REG_ACT_INACT_CTL_INACT_Z_ENABLE, + act_inact_ctl->inact_z_enable); + + LOG_DBG("Set act_inact_ctl - act_acdc:%u, act_x_enable: %u, act_y_enable: %u, " + "act_z_enable: %u, inact_acdc: %u, inact_x_enable: %u, inact_y_enable: %u, " + "inact_z_enable: %u", + act_inact_ctl->act_acdc, act_inact_ctl->act_x_enable, + act_inact_ctl->act_y_enable, act_inact_ctl->act_z_enable, + act_inact_ctl->inact_acdc, act_inact_ctl->inact_x_enable, + act_inact_ctl->inact_y_enable, act_inact_ctl->inact_z_enable); + rc = config->bus_write(dev, ADXL34X_REG_ACT_INACT_CTL, reg_value); + if (rc != 0) { + return rc; + } + cfg->act_inact_ctl = *act_inact_ctl; + } + return 0; +} + +/** + * Fetches register 0x28 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_thresh_ff(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_THRESH_FF, ®_value); + if (rc != 0) { + return rc; + } + + cfg->thresh_ff = reg_value; + LOG_DBG("Get thresh_ff: %u", cfg->thresh_ff); + return 0; +} + +/** + * Get register 0x28 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] thresh_ff The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_thresh_ff(const struct device *dev, uint8_t *thresh_ff, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_thresh_ff(dev); + if (rc != 0) { + *thresh_ff = 0; + return rc; + } + } + *thresh_ff = data->cfg.thresh_ff; + return 0; +} + +/** + * Set register 0x28 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] thresh_ff The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_thresh_ff(const struct device *dev, uint8_t thresh_ff) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (thresh_ff != cfg->thresh_ff) { + LOG_DBG("Set thresh_ff: %u", thresh_ff); + rc = config->bus_write(dev, ADXL34X_REG_THRESH_FF, thresh_ff); + if (rc != 0) { + return rc; + } + cfg->thresh_ff = thresh_ff; + } + return 0; +} + +/** + * Fetches register 0x29 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_time_ff(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_TIME_FF, ®_value); + if (rc != 0) { + return rc; + } + + cfg->time_ff = reg_value; + LOG_DBG("Get time_ff: %u", cfg->time_ff); + return 0; +} + +/** + * Get register 0x29 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] time_ff The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_time_ff(const struct device *dev, uint8_t *time_ff, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_time_ff(dev); + if (rc != 0) { + *time_ff = 0; + return rc; + } + } + *time_ff = data->cfg.time_ff; + return 0; +} + +/** + * Set register 0x29 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[in] time_ff The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_time_ff(const struct device *dev, uint8_t time_ff) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + if (time_ff != cfg->time_ff) { + LOG_DBG("Set time_ff: %u", time_ff); + rc = config->bus_write(dev, ADXL34X_REG_TIME_FF, time_ff); + if (rc != 0) { + return rc; + } + cfg->time_ff = time_ff; + } + return 0; +} + +/** + * Fetches register 0x2A from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_tap_axes(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_TAP_AXES, ®_value); + if (rc != 0) { + return rc; + } + + cfg->tap_axes.improved_tab = FIELD_GET(ADXL34X_REG_TAP_AXES_IMPROVED_TAB, reg_value); + cfg->tap_axes.suppress = FIELD_GET(ADXL34X_REG_TAP_AXES_SUPPRESS, reg_value); + cfg->tap_axes.tap_x_enable = FIELD_GET(ADXL34X_REG_TAP_AXES_TAP_X_ENABLE, reg_value); + cfg->tap_axes.tap_y_enable = FIELD_GET(ADXL34X_REG_TAP_AXES_TAP_Y_ENABLE, reg_value); + cfg->tap_axes.tap_z_enable = FIELD_GET(ADXL34X_REG_TAP_AXES_TAP_Z_ENABLE, reg_value); + LOG_DBG("Get tap_axes - improved_tab:%u, suppress:%u, tap_x_enable: %u, tap_y_enable: %u, " + "tap_z_enable: %u", + cfg->tap_axes.improved_tab, cfg->tap_axes.suppress, cfg->tap_axes.tap_x_enable, + cfg->tap_axes.tap_y_enable, cfg->tap_axes.tap_z_enable); + return 0; +} + +/** + * Get register 0x2A from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] tap_axes The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_tap_axes(const struct device *dev, struct adxl34x_tap_axes *tap_axes, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_tap_axes(dev); + if (rc != 0) { + *tap_axes = (struct adxl34x_tap_axes){0}; + return rc; + } + } + *tap_axes = data->cfg.tap_axes; + return 0; +} + +/** + * Set register 0x2A from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] tap_axes The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_tap_axes(const struct device *dev, struct adxl34x_tap_axes *tap_axes) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (tap_axes->improved_tab != cfg->tap_axes.improved_tab || + tap_axes->suppress != cfg->tap_axes.suppress || + tap_axes->tap_x_enable != cfg->tap_axes.tap_x_enable || + tap_axes->tap_y_enable != cfg->tap_axes.tap_y_enable || + tap_axes->tap_z_enable != cfg->tap_axes.tap_z_enable) { + + reg_value = FIELD_PREP(ADXL34X_REG_TAP_AXES_IMPROVED_TAB, tap_axes->improved_tab) | + FIELD_PREP(ADXL34X_REG_TAP_AXES_SUPPRESS, tap_axes->suppress) | + FIELD_PREP(ADXL34X_REG_TAP_AXES_TAP_X_ENABLE, tap_axes->tap_x_enable) | + FIELD_PREP(ADXL34X_REG_TAP_AXES_TAP_Y_ENABLE, tap_axes->tap_y_enable) | + FIELD_PREP(ADXL34X_REG_TAP_AXES_TAP_Z_ENABLE, tap_axes->tap_z_enable); + + LOG_DBG("Set tap_axes - improved_tab:%u, suppress:%u, tap_x_enable: %u, " + "tap_y_enable: %u, tap_z_enable: %u", + tap_axes->improved_tab, tap_axes->suppress, tap_axes->tap_x_enable, + tap_axes->tap_y_enable, tap_axes->tap_z_enable); + rc = config->bus_write(dev, ADXL34X_REG_TAP_AXES, reg_value); + if (rc != 0) { + return rc; + } + cfg->tap_axes = *tap_axes; + } + return 0; +} + +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + +/** + * Fetches register 0x2C from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_bw_rate(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_BW_RATE, ®_value); + if (rc != 0) { + return rc; + } + + cfg->bw_rate.low_power = FIELD_GET(ADXL34X_REG_BW_RATE_LOW_POWER, reg_value); + cfg->bw_rate.rate = FIELD_GET(ADXL34X_REG_BW_RATE_RATE, reg_value); + LOG_DBG("Get bw_rate - low_power:%u, rate: %u", cfg->bw_rate.low_power, cfg->bw_rate.rate); + return 0; +} + +/** + * Get register 0x2C from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] bw_rate The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_bw_rate(const struct device *dev, struct adxl34x_bw_rate *bw_rate, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_bw_rate(dev); + if (rc != 0) { + *bw_rate = (struct adxl34x_bw_rate){0}; + return rc; + } + } + *bw_rate = data->cfg.bw_rate; + return 0; +} + +/** + * Set register 0x2C from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] bw_rate The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_bw_rate(const struct device *dev, struct adxl34x_bw_rate *bw_rate) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (bw_rate->low_power != cfg->bw_rate.low_power || bw_rate->rate != cfg->bw_rate.rate) { + reg_value = FIELD_PREP(ADXL34X_REG_BW_RATE_LOW_POWER, bw_rate->low_power) | + FIELD_PREP(ADXL34X_REG_BW_RATE_RATE, bw_rate->rate); + + LOG_DBG("Set bw_rate - low_power:%u, rate: %u", bw_rate->low_power, bw_rate->rate); + rc = config->bus_write(dev, ADXL34X_REG_BW_RATE, reg_value); + if (rc != 0) { + return rc; + } + cfg->bw_rate = *bw_rate; + } + return 0; +} + +/** + * Fetches register 0x2D from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_power_ctl(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_POWER_CTL, ®_value); + if (rc != 0) { + return rc; + } + + cfg->power_ctl.link = FIELD_GET(ADXL34X_REG_POWER_CTL_LINK, reg_value); + cfg->power_ctl.auto_sleep = FIELD_GET(ADXL34X_REG_POWER_CTL_AUTO_SLEEP, reg_value); + cfg->power_ctl.measure = FIELD_GET(ADXL34X_REG_POWER_CTL_MEASURE, reg_value); + cfg->power_ctl.sleep = FIELD_GET(ADXL34X_REG_POWER_CTL_SLEEP, reg_value); + cfg->power_ctl.wakeup = FIELD_GET(ADXL34X_REG_POWER_CTL_WAKEUP, reg_value); + LOG_DBG("Get power_ctl - link:%u, auto_sleep: %u, measure: %u, sleep: %u, wakeup: " + "%u", + cfg->power_ctl.link, cfg->power_ctl.auto_sleep, cfg->power_ctl.measure, + cfg->power_ctl.sleep, cfg->power_ctl.wakeup); + return 0; +} + +/** + * Get register 0x2D from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] power_ctl The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_power_ctl(const struct device *dev, struct adxl34x_power_ctl *power_ctl, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_power_ctl(dev); + if (rc != 0) { + *power_ctl = (struct adxl34x_power_ctl){0}; + return rc; + } + } + *power_ctl = data->cfg.power_ctl; + return 0; +} + +/** + * Set register 0x2D from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] power_ctl The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_power_ctl(const struct device *dev, struct adxl34x_power_ctl *power_ctl) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (power_ctl->link != cfg->power_ctl.link || + power_ctl->auto_sleep != cfg->power_ctl.auto_sleep || + power_ctl->measure != cfg->power_ctl.measure || + power_ctl->sleep != cfg->power_ctl.sleep || + power_ctl->wakeup != cfg->power_ctl.wakeup) { + + reg_value = FIELD_PREP(ADXL34X_REG_POWER_CTL_LINK, power_ctl->link) | + FIELD_PREP(ADXL34X_REG_POWER_CTL_AUTO_SLEEP, power_ctl->auto_sleep) | + FIELD_PREP(ADXL34X_REG_POWER_CTL_MEASURE, power_ctl->measure) | + FIELD_PREP(ADXL34X_REG_POWER_CTL_SLEEP, power_ctl->sleep) | + FIELD_PREP(ADXL34X_REG_POWER_CTL_WAKEUP, power_ctl->wakeup); + + LOG_DBG("Set power_ctl - link:%u, auto_sleep: %u, measure: %u, sleep: %u, wakeup: " + "%u", + power_ctl->link, power_ctl->auto_sleep, power_ctl->measure, + power_ctl->sleep, power_ctl->wakeup); + rc = config->bus_write(dev, ADXL34X_REG_POWER_CTL, reg_value); + if (rc != 0) { + return rc; + } + cfg->power_ctl = *power_ctl; + } + return 0; +} + +/** + * Fetches register 0x2E from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_int_enable(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_INT_ENABLE, ®_value); + if (rc != 0) { + return rc; + } + + cfg->int_enable.data_ready = FIELD_GET(ADXL34X_REG_INT_ENABLE_DATA_READY, reg_value); + cfg->int_enable.single_tap = FIELD_GET(ADXL34X_REG_INT_ENABLE_SINGLE_TAP, reg_value); + cfg->int_enable.double_tap = FIELD_GET(ADXL34X_REG_INT_ENABLE_DOUBLE_TAP, reg_value); + cfg->int_enable.activity = FIELD_GET(ADXL34X_REG_INT_ENABLE_ACTIVITY, reg_value); + cfg->int_enable.inactivity = FIELD_GET(ADXL34X_REG_INT_ENABLE_INACTIVITY, reg_value); + cfg->int_enable.free_fall = FIELD_GET(ADXL34X_REG_INT_ENABLE_FREE_FALL, reg_value); + cfg->int_enable.watermark = FIELD_GET(ADXL34X_REG_INT_ENABLE_WATERMARK, reg_value); + cfg->int_enable.overrun = FIELD_GET(ADXL34X_REG_INT_ENABLE_OVERRUN, reg_value); + LOG_DBG("Get int_enable - data_ready:%u, single_tap: %u, double_tap: %u, activity: " + "%u, inactivity: %u, free_fall: %u, watermark: %u, overrun: %u", + cfg->int_enable.data_ready, cfg->int_enable.single_tap, cfg->int_enable.double_tap, + cfg->int_enable.activity, cfg->int_enable.inactivity, cfg->int_enable.free_fall, + cfg->int_enable.watermark, cfg->int_enable.overrun); + return 0; +} + +/** + * Get register 0x2E from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] int_enable The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_int_enable(const struct device *dev, struct adxl34x_int_enable *int_enable, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_int_enable(dev); + if (rc != 0) { + *int_enable = (struct adxl34x_int_enable){0}; + return rc; + } + } + *int_enable = data->cfg.int_enable; + return 0; +} + +/** + * Set register 0x2E from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] int_enable The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_int_enable(const struct device *dev, struct adxl34x_int_enable *int_enable) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (int_enable->data_ready != cfg->int_enable.data_ready || + int_enable->single_tap != cfg->int_enable.single_tap || + int_enable->double_tap != cfg->int_enable.double_tap || + int_enable->activity != cfg->int_enable.activity || + int_enable->inactivity != cfg->int_enable.inactivity || + int_enable->free_fall != cfg->int_enable.free_fall || + int_enable->watermark != cfg->int_enable.watermark || + int_enable->overrun != cfg->int_enable.overrun) { + + reg_value = FIELD_PREP(ADXL34X_REG_INT_ENABLE_DATA_READY, int_enable->data_ready) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_SINGLE_TAP, int_enable->single_tap) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_DOUBLE_TAP, int_enable->double_tap) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_ACTIVITY, int_enable->activity) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_INACTIVITY, int_enable->inactivity) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_FREE_FALL, int_enable->free_fall) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_WATERMARK, int_enable->watermark) | + FIELD_PREP(ADXL34X_REG_INT_ENABLE_OVERRUN, int_enable->overrun); + + LOG_DBG("Set int_enable - data_ready:%u, single_tap: %u, double_tap: %u, activity: " + "%u, inactivity: %u, free_fall: %u, watermark: %u, overrun: %u", + int_enable->data_ready, int_enable->single_tap, int_enable->double_tap, + int_enable->activity, int_enable->inactivity, int_enable->free_fall, + int_enable->watermark, int_enable->overrun); + rc = config->bus_write(dev, ADXL34X_REG_INT_ENABLE, reg_value); + if (rc != 0) { + return rc; + } + cfg->int_enable = *int_enable; + } + return 0; +} + +/** + * Fetches register 0x2F from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_int_map(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_INT_MAP, ®_value); + if (rc != 0) { + return rc; + } + + cfg->int_map.data_ready = FIELD_GET(ADXL34X_REG_INT_MAP_DATA_READY, reg_value); + cfg->int_map.single_tap = FIELD_GET(ADXL34X_REG_INT_MAP_SINGLE_TAP, reg_value); + cfg->int_map.double_tap = FIELD_GET(ADXL34X_REG_INT_MAP_DOUBLE_TAP, reg_value); + cfg->int_map.activity = FIELD_GET(ADXL34X_REG_INT_MAP_ACTIVITY, reg_value); + cfg->int_map.inactivity = FIELD_GET(ADXL34X_REG_INT_MAP_INACTIVITY, reg_value); + cfg->int_map.free_fall = FIELD_GET(ADXL34X_REG_INT_MAP_FREE_FALL, reg_value); + cfg->int_map.watermark = FIELD_GET(ADXL34X_REG_INT_MAP_WATERMARK, reg_value); + cfg->int_map.overrun = FIELD_GET(ADXL34X_REG_INT_MAP_OVERRUN, reg_value); + LOG_DBG("Get int_map - data_ready:%u, single_tap: %u, double_tap: %u, activity: %u, " + "inactivity: %u, free_fall: %u, watermark: %u, overrun: %u", + cfg->int_map.data_ready, cfg->int_map.single_tap, cfg->int_map.double_tap, + cfg->int_map.activity, cfg->int_map.inactivity, cfg->int_map.free_fall, + cfg->int_map.watermark, cfg->int_map.overrun); + return 0; +} + +/** + * Get register 0x2F from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] int_map The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_int_map(const struct device *dev, struct adxl34x_int_map *int_map, bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_int_map(dev); + if (rc != 0) { + *int_map = (struct adxl34x_int_map){0}; + return rc; + } + } + *int_map = data->cfg.int_map; + return 0; +} + +/** + * Set register 0x2F from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] int_map The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_int_map(const struct device *dev, struct adxl34x_int_map *int_map) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (int_map->data_ready != cfg->int_map.data_ready || + int_map->single_tap != cfg->int_map.single_tap || + int_map->double_tap != cfg->int_map.double_tap || + int_map->activity != cfg->int_map.activity || + int_map->inactivity != cfg->int_map.inactivity || + int_map->free_fall != cfg->int_map.free_fall || + int_map->watermark != cfg->int_map.watermark || + int_map->overrun != cfg->int_map.overrun) { + + reg_value = FIELD_PREP(ADXL34X_REG_INT_MAP_DATA_READY, int_map->data_ready) | + FIELD_PREP(ADXL34X_REG_INT_MAP_SINGLE_TAP, int_map->single_tap) | + FIELD_PREP(ADXL34X_REG_INT_MAP_DOUBLE_TAP, int_map->double_tap) | + FIELD_PREP(ADXL34X_REG_INT_MAP_ACTIVITY, int_map->activity) | + FIELD_PREP(ADXL34X_REG_INT_MAP_INACTIVITY, int_map->inactivity) | + FIELD_PREP(ADXL34X_REG_INT_MAP_FREE_FALL, int_map->free_fall) | + FIELD_PREP(ADXL34X_REG_INT_MAP_WATERMARK, int_map->watermark) | + FIELD_PREP(ADXL34X_REG_INT_MAP_OVERRUN, int_map->overrun); + + LOG_DBG("Set int_map - data_ready:%u, single_tap: %u, double_tap: %u, activity: " + "%u, " + "inactivity: %u, free_fall: %u, watermark: %u, overrun: %u", + int_map->data_ready, int_map->single_tap, int_map->double_tap, + int_map->activity, int_map->inactivity, int_map->free_fall, + int_map->watermark, int_map->overrun); + rc = config->bus_write(dev, ADXL34X_REG_INT_MAP, reg_value); + if (rc != 0) { + return rc; + } + cfg->int_map = *int_map; + } + return 0; +} + +/** + * Fetches register 0x31 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_data_format(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_DATA_FORMAT, ®_value); + if (rc != 0) { + return rc; + } + + cfg->data_format.self_test = FIELD_GET(ADXL34X_REG_DATA_FORMAT_SELF_TEST, reg_value); + cfg->data_format.spi = FIELD_GET(ADXL34X_REG_DATA_FORMAT_SPI, reg_value); + cfg->data_format.int_invert = FIELD_GET(ADXL34X_REG_DATA_FORMAT_INT_INVERT, reg_value); + cfg->data_format.full_res = FIELD_GET(ADXL34X_REG_DATA_FORMAT_FULL_RES, reg_value); + cfg->data_format.justify = FIELD_GET(ADXL34X_REG_DATA_FORMAT_JUSTIFY, reg_value); + cfg->data_format.range = FIELD_GET(ADXL34X_REG_DATA_FORMAT_RANGE, reg_value); + LOG_DBG("Get data_format - self_test:%u, spi: %u, int_invert: %u, full_res: %u, " + "justify: %u, range: %u", + cfg->data_format.self_test, cfg->data_format.spi, cfg->data_format.int_invert, + cfg->data_format.full_res, cfg->data_format.justify, cfg->data_format.range); + return 0; +} + +/** + * Get register 0x31 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] data_format The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_data_format(const struct device *dev, struct adxl34x_data_format *data_format, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_data_format(dev); + if (rc != 0) { + *data_format = (struct adxl34x_data_format){0}; + return rc; + } + } + *data_format = data->cfg.data_format; + return 0; +} + +/** + * Set register 0x31 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] data_format The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_data_format(const struct device *dev, struct adxl34x_data_format *data_format) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (data_format->self_test != cfg->data_format.self_test || + data_format->spi != cfg->data_format.spi || + data_format->int_invert != cfg->data_format.int_invert || + data_format->full_res != cfg->data_format.full_res || + data_format->justify != cfg->data_format.justify || + data_format->range != cfg->data_format.range) { + + reg_value = + FIELD_PREP(ADXL34X_REG_DATA_FORMAT_SELF_TEST, data_format->self_test) | + FIELD_PREP(ADXL34X_REG_DATA_FORMAT_SPI, data_format->spi) | + FIELD_PREP(ADXL34X_REG_DATA_FORMAT_INT_INVERT, data_format->int_invert) | + FIELD_PREP(ADXL34X_REG_DATA_FORMAT_FULL_RES, data_format->full_res) | + FIELD_PREP(ADXL34X_REG_DATA_FORMAT_JUSTIFY, data_format->justify) | + FIELD_PREP(ADXL34X_REG_DATA_FORMAT_RANGE, data_format->range); + + LOG_DBG("Set data_format - self_test:%u, spi: %u, int_invert: %u, full_res: %u, " + "justify: %u, range: %u", + data_format->self_test, data_format->spi, data_format->int_invert, + data_format->full_res, data_format->justify, data_format->range); + rc = config->bus_write(dev, ADXL34X_REG_DATA_FORMAT, reg_value); + if (rc != 0) { + return rc; + } + cfg->data_format = *data_format; + } + return 0; +} + +/** + * Fetches register 0x38 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_fifo_ctl(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_FIFO_CTL, ®_value); + if (rc != 0) { + return rc; + } + + cfg->fifo_ctl.fifo_mode = FIELD_GET(ADXL34X_REG_FIFO_CTL_FIFO_MODE, reg_value); + cfg->fifo_ctl.trigger = FIELD_GET(ADXL34X_REG_FIFO_CTL_TRIGGER, reg_value); + cfg->fifo_ctl.samples = FIELD_GET(ADXL34X_REG_FIFO_CTL_SAMPLES, reg_value); + LOG_DBG("Get fifo_ctl - fifo_mode:%u, trigger: %u, samples: %u", cfg->fifo_ctl.fifo_mode, + cfg->fifo_ctl.trigger, cfg->fifo_ctl.samples); + return 0; +} + +/** + * Get register 0x38 from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] fifo_ctl The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_fifo_ctl(const struct device *dev, struct adxl34x_fifo_ctl *fifo_ctl, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (!use_cache) { + rc = adxl34x_load_fifo_ctl(dev); + if (rc != 0) { + *fifo_ctl = (struct adxl34x_fifo_ctl){0}; + return rc; + } + } + *fifo_ctl = data->cfg.fifo_ctl; + return 0; +} + +/** + * Set register 0x38 from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] fifo_ctl The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_fifo_ctl(const struct device *dev, struct adxl34x_fifo_ctl *fifo_ctl) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (fifo_ctl->fifo_mode != cfg->fifo_ctl.fifo_mode || + fifo_ctl->trigger != cfg->fifo_ctl.trigger || + fifo_ctl->samples != cfg->fifo_ctl.samples) { + + reg_value = FIELD_PREP(ADXL34X_REG_FIFO_CTL_FIFO_MODE, fifo_ctl->fifo_mode) | + FIELD_PREP(ADXL34X_REG_FIFO_CTL_TRIGGER, fifo_ctl->trigger) | + FIELD_PREP(ADXL34X_REG_FIFO_CTL_SAMPLES, fifo_ctl->samples); + + LOG_DBG("Set fifo_ctl - fifo_mode:%u, trigger: %u, samples: %u", + fifo_ctl->fifo_mode, fifo_ctl->trigger, fifo_ctl->samples); + rc = config->bus_write(dev, ADXL34X_REG_FIFO_CTL, reg_value); + if (rc != 0) { + return rc; + } + cfg->fifo_ctl = *fifo_ctl; + } + return 0; +} + +#ifdef CONFIG_ADXL34X_EXTENDED_API + +/** + * Fetches register 0x3B from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_orient_conf(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_ORIENT_CONF, ®_value); + if (rc != 0) { + return rc; + } + + cfg->orient_conf.int_orient = FIELD_GET(ADXL34X_REG_ORIENT_CONF_INT_ORIENT, reg_value); + cfg->orient_conf.dead_zone = FIELD_GET(ADXL34X_REG_ORIENT_CONF_DEAD_ZONE, reg_value); + cfg->orient_conf.int_3d = FIELD_GET(ADXL34X_REG_ORIENT_CONF_INT_3D, reg_value); + cfg->orient_conf.divisor = FIELD_GET(ADXL34X_REG_ORIENT_CONF_DIVISOR, reg_value); + LOG_DBG("Get orient_conf - int_orient:%u, dead_zone: %u, int_3d: %u, divisor: %u", + cfg->orient_conf.int_orient, cfg->orient_conf.dead_zone, cfg->orient_conf.int_3d, + cfg->orient_conf.divisor); + return 0; +} + +/** + * Get register 0x3B from the adxl34x, use cached values if needed + * + * @param[in] dev Pointer to the sensor device + * @param[out] orient_conf The value of the register + * @param[in] use_cache Do not fetch the register, used the cached value + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_orient_conf(const struct device *dev, struct adxl34x_orient_conf *orient_conf, + bool use_cache) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (data->cfg.devid != ADXL344_DEVID && data->cfg.devid != ADXL346_DEVID) { + return -EADDRNOTAVAIL; + } + + if (!use_cache) { + rc = adxl34x_load_orient_conf(dev); + if (rc != 0) { + *orient_conf = (struct adxl34x_orient_conf){0}; + return rc; + } + } + *orient_conf = data->cfg.orient_conf; + return 0; +} + +/** + * Set register 0x3B from the adxl34x, update cached values + * + * @param[in] dev Pointer to the sensor device + * @param[out] orient_conf The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_set_orient_conf(const struct device *dev, struct adxl34x_orient_conf *orient_conf) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + int rc; + uint8_t reg_value; + + if (data->cfg.devid != ADXL344_DEVID && data->cfg.devid != ADXL346_DEVID) { + return -EADDRNOTAVAIL; + } + + if (orient_conf->int_orient != cfg->orient_conf.int_orient || + orient_conf->dead_zone != cfg->orient_conf.dead_zone || + orient_conf->int_3d != cfg->orient_conf.int_3d || + orient_conf->divisor != cfg->orient_conf.divisor) { + + reg_value = + FIELD_PREP(ADXL34X_REG_ORIENT_CONF_INT_ORIENT, orient_conf->int_orient) | + FIELD_PREP(ADXL34X_REG_ORIENT_CONF_DEAD_ZONE, orient_conf->dead_zone) | + FIELD_PREP(ADXL34X_REG_ORIENT_CONF_INT_3D, orient_conf->int_3d) | + FIELD_PREP(ADXL34X_REG_ORIENT_CONF_DIVISOR, orient_conf->divisor); + + LOG_DBG("Set orient_conf - int_orient:%u, dead_zone: %u, int_3d: %u, divisor: %u", + orient_conf->int_orient, orient_conf->dead_zone, orient_conf->int_3d, + orient_conf->divisor); + rc = config->bus_write(dev, ADXL34X_REG_ORIENT_CONF, reg_value); + if (rc != 0) { + return rc; + } + cfg->orient_conf = *orient_conf; + } + return 0; +} + +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + +/* Read only registers */ + +/** + * Get register 0x00 from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_load_devid(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + struct adxl34x_cfg *cfg = &data->cfg; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_DEVID, ®_value); + if (rc != 0) { + return rc; + } + + cfg->devid = reg_value; + LOG_DBG("Get devid: 0x%02X", cfg->devid); + return 0; +} + +/** + * Get register 0x00 from the adxl34x, only fetch the value if not cached, update the cached value + * + * @param[in] dev Pointer to the sensor device + * @param[out] devid The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_devid(const struct device *dev, uint8_t *devid) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + if (data->cfg.devid == 0) { + rc = adxl34x_load_devid(dev); + if (rc != 0) { + *devid = 0; + return rc; + } + } + *devid = data->cfg.devid; + return 0; +} + +#ifdef CONFIG_ADXL34X_EXTENDED_API + +/** + * Get register 0x from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @param[out] act_tap_status The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_act_tap_status(const struct device *dev, + struct adxl34x_act_tap_status *act_tap_status) +{ + const struct adxl34x_dev_config *config = dev->config; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_ACT_TAP_STATUS, ®_value); + if (rc != 0) { + *act_tap_status = (struct adxl34x_act_tap_status){0}; + return rc; + } + + act_tap_status->act_x_source = + FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_ACT_X_SOURCE, reg_value); + act_tap_status->act_y_source = + FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_ACT_Y_SOURCE, reg_value); + act_tap_status->act_z_source = + FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_ACT_Z_SOURCE, reg_value); + act_tap_status->asleep = FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_ASLEEP, reg_value); + act_tap_status->tap_x_source = + FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_TAP_X_SOURCE, reg_value); + act_tap_status->tap_y_source = + FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_TAP_Y_SOURCE, reg_value); + act_tap_status->tap_z_source = + FIELD_GET(ADXL34X_REG_ACT_TAP_STATUS_TAP_Z_SOURCE, reg_value); + LOG_DBG("Get act_tap_status - act_x_source:%u, act_y_source:%u, act_z_source:%u, " + "asleep:%u, tap_x_source:%u, tap_y_source:%u, tap_z_source:%u", + act_tap_status->act_x_source, act_tap_status->act_y_source, + act_tap_status->act_z_source, act_tap_status->asleep, act_tap_status->tap_x_source, + act_tap_status->tap_y_source, act_tap_status->tap_z_source); + return 0; +} + +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + +/** + * Get register 0x from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @param[out] int_source The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_int_source(const struct device *dev, struct adxl34x_int_source *int_source) +{ + const struct adxl34x_dev_config *config = dev->config; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_INT_SOURCE, ®_value); + if (rc != 0) { + *int_source = (struct adxl34x_int_source){0}; + return rc; + } + + int_source->data_ready = FIELD_GET(ADXL34X_REG_INT_SOURCE_DATA_READY, reg_value); + int_source->single_tap = FIELD_GET(ADXL34X_REG_INT_SOURCE_SINGLE_TAP, reg_value); + int_source->double_tap = FIELD_GET(ADXL34X_REG_INT_SOURCE_DOUBLE_TAP, reg_value); + int_source->activity = FIELD_GET(ADXL34X_REG_INT_SOURCE_ACTIVITY, reg_value); + int_source->inactivity = FIELD_GET(ADXL34X_REG_INT_SOURCE_INACTIVITY, reg_value); + int_source->free_fall = FIELD_GET(ADXL34X_REG_INT_SOURCE_FREE_FALL, reg_value); + int_source->watermark = FIELD_GET(ADXL34X_REG_INT_SOURCE_WATERMARK, reg_value); + int_source->overrun = FIELD_GET(ADXL34X_REG_INT_SOURCE_OVERRUN, reg_value); + return 0; +} + +/** + * Get register 0x from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @param[out] fifo_status The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_fifo_status(const struct device *dev, struct adxl34x_fifo_status *fifo_status) +{ + const struct adxl34x_dev_config *config = dev->config; + uint8_t reg_value; + int rc; + + rc = config->bus_read(dev, ADXL34X_REG_FIFO_STATUS, ®_value); + if (rc != 0) { + *fifo_status = (struct adxl34x_fifo_status){0}; + return rc; + } + + fifo_status->fifo_trig = FIELD_GET(ADXL34X_REG_FIFO_STATUS_FIFO_TRIG, reg_value); + fifo_status->entries = FIELD_GET(ADXL34X_REG_FIFO_STATUS_ENTRIES, reg_value); + return 0; +} + +#ifdef CONFIG_ADXL34X_EXTENDED_API + +/** + * Get register 0x from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @param[out] tap_sign The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_tap_sign(const struct device *dev, struct adxl34x_tap_sign *tap_sign) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + uint8_t reg_value; + int rc; + + if (data->cfg.devid != ADXL344_DEVID && data->cfg.devid != ADXL346_DEVID) { + return -EADDRNOTAVAIL; + } + + rc = config->bus_read(dev, ADXL34X_REG_TAP_SIGN, ®_value); + if (rc != 0) { + *tap_sign = (struct adxl34x_tap_sign){0}; + return rc; + } + + tap_sign->xsign = FIELD_GET(ADXL34X_REG_TAP_SIGN_XSIGN, reg_value); + tap_sign->ysign = FIELD_GET(ADXL34X_REG_TAP_SIGN_YSIGN, reg_value); + tap_sign->zsign = FIELD_GET(ADXL34X_REG_TAP_SIGN_ZSIGN, reg_value); + tap_sign->xtap = FIELD_GET(ADXL34X_REG_TAP_SIGN_XTAP, reg_value); + tap_sign->ytap = FIELD_GET(ADXL34X_REG_TAP_SIGN_YTAP, reg_value); + tap_sign->ztap = FIELD_GET(ADXL34X_REG_TAP_SIGN_ZTAP, reg_value); + LOG_DBG("Get tap_sign - xsign:%u, ysign:%u, zsign:%u, xtap:%u, ytap:%u, ztap:%u", + tap_sign->xsign, tap_sign->ysign, tap_sign->zsign, tap_sign->xtap, tap_sign->ytap, + tap_sign->ztap); + return 0; +} + +/** + * Get register 0x from the adxl34x + * + * @param[in] dev Pointer to the sensor device + * @param[out] orient The value of the register + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_orient(const struct device *dev, struct adxl34x_orient *orient) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + uint8_t reg_value; + int rc; + + if (data->cfg.devid != ADXL344_DEVID && data->cfg.devid != ADXL346_DEVID) { + return -EADDRNOTAVAIL; + } + + rc = config->bus_read(dev, ADXL34X_REG_ORIENT, ®_value); + if (rc != 0) { + *orient = (struct adxl34x_orient){0}; + return rc; + } + + orient->v2 = FIELD_GET(ADXL34X_REG_ORIENT_V2, reg_value); + orient->orient_2d = FIELD_GET(ADXL34X_REG_ORIENT_2D_ORIENT, reg_value); + orient->v3 = FIELD_GET(ADXL34X_REG_ORIENT_V3, reg_value); + orient->orient_3d = FIELD_GET(ADXL34X_REG_ORIENT_3D_ORIENT, reg_value); + LOG_DBG("Get orient - v2:%u, orient_2d:%u, v3:%u, orient_3d:%u", orient->v2, + orient->orient_2d, orient->v3, orient->orient_3d); + return 0; +} + +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + +/** + * Update the registry of the adxl34x with the new configuration + * + * @param[in] dev Pointer to the sensor device + * @param[out] new_cfg The new configuration of the registry + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_configure(const struct device *dev, struct adxl34x_cfg *new_cfg) +{ + int rc = 0; + +#ifdef CONFIG_ADXL34X_EXTENDED_API + rc |= adxl34x_set_thresh_tap(dev, new_cfg->thresh_tap); +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + rc |= adxl34x_set_ofsx(dev, new_cfg->ofsx); + rc |= adxl34x_set_ofsy(dev, new_cfg->ofsy); + rc |= adxl34x_set_ofsz(dev, new_cfg->ofsz); +#ifdef CONFIG_ADXL34X_EXTENDED_API + rc |= adxl34x_set_dur(dev, new_cfg->dur); + rc |= adxl34x_set_latent(dev, new_cfg->latent); + rc |= adxl34x_set_window(dev, new_cfg->window); + rc |= adxl34x_set_thresh_act(dev, new_cfg->thresh_act); + rc |= adxl34x_set_thresh_inact(dev, new_cfg->thresh_inact); + rc |= adxl34x_set_time_inact(dev, new_cfg->time_inact); + rc |= adxl34x_set_act_inact_ctl(dev, &new_cfg->act_inact_ctl); + rc |= adxl34x_set_thresh_ff(dev, new_cfg->thresh_ff); + rc |= adxl34x_set_time_ff(dev, new_cfg->time_ff); + rc |= adxl34x_set_tap_axes(dev, &new_cfg->tap_axes); +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + rc |= adxl34x_set_bw_rate(dev, &new_cfg->bw_rate); + rc |= adxl34x_set_power_ctl(dev, &new_cfg->power_ctl); + rc |= adxl34x_set_int_enable(dev, &new_cfg->int_enable); + rc |= adxl34x_set_int_map(dev, &new_cfg->int_map); + rc |= adxl34x_set_data_format(dev, &new_cfg->data_format); +#ifdef CONFIG_ADXL34X_EXTENDED_API + rc |= adxl34x_set_fifo_ctl(dev, &new_cfg->fifo_ctl); + + struct adxl34x_dev_data *data = dev->data; + + if (data->cfg.devid == ADXL344_DEVID || data->cfg.devid == ADXL346_DEVID) { + rc |= adxl34x_set_orient_conf(dev, &new_cfg->orient_conf); + } +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + + return rc; +} + +/** + * Fetch the registry of the adxl34x to update the cached values + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_configuration(const struct device *dev) +{ + int rc = 0; + +#ifdef CONFIG_ADXL34X_EXTENDED_API + rc |= adxl34x_load_thresh_tap(dev); +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + rc |= adxl34x_load_ofsx(dev); + rc |= adxl34x_load_ofsy(dev); + rc |= adxl34x_load_ofsz(dev); +#ifdef CONFIG_ADXL34X_EXTENDED_API + rc |= adxl34x_load_dur(dev); + rc |= adxl34x_load_latent(dev); + rc |= adxl34x_load_window(dev); + rc |= adxl34x_load_thresh_act(dev); + rc |= adxl34x_load_thresh_inact(dev); + rc |= adxl34x_load_time_inact(dev); + rc |= adxl34x_load_act_inact_ctl(dev); + rc |= adxl34x_load_thresh_ff(dev); + rc |= adxl34x_load_time_ff(dev); + rc |= adxl34x_load_tap_axes(dev); +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + rc |= adxl34x_load_bw_rate(dev); + rc |= adxl34x_load_power_ctl(dev); + rc |= adxl34x_load_int_enable(dev); + rc |= adxl34x_load_int_map(dev); + rc |= adxl34x_load_data_format(dev); +#ifdef CONFIG_ADXL34X_EXTENDED_API + rc |= adxl34x_load_fifo_ctl(dev); + + struct adxl34x_dev_data *data = dev->data; + + if (data->cfg.devid == ADXL344_DEVID || data->cfg.devid == ADXL346_DEVID) { + rc |= adxl34x_load_orient_conf(dev); + } +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + + return rc; +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_configure.h b/drivers/sensor/adi/adxl34x/adxl34x_configure.h new file mode 100644 index 0000000000000..587edfdf896a5 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_configure.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_CONFIGURE_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_CONFIGURE_H_ + +#include +#include + +#include +#include + +int adxl34x_configure(const struct device *dev, struct adxl34x_cfg *new_cfg); +int adxl34x_get_configuration(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_CONFIGURE_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_convert.h b/drivers/sensor/adi/adxl34x/adxl34x_convert.h new file mode 100644 index 0000000000000..b6596303acc40 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_convert.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_CONVERT_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_CONVERT_H_ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Conversion from raw register values to g (see table 1: + * "Specifications" from the datasheet). + * + * @var adxl34x_range_conv + */ +static const uint16_t adxl34x_range_conv[] = { + [ADXL34X_RANGE_2G] = 39, + [ADXL34X_RANGE_4G] = 78, + [ADXL34X_RANGE_8G] = 156, + [ADXL34X_RANGE_16G] = 312, +}; + +/** + * @brief Conversion from register values to maximum g values + * + * @var adxl34x_max_g_conv + */ +static const uint8_t adxl34x_max_g_conv[] = { + [ADXL34X_RANGE_2G] = 2, + [ADXL34X_RANGE_4G] = 4, + [ADXL34X_RANGE_8G] = 8, + [ADXL34X_RANGE_16G] = 16, +}; + +/** + * @brief The shift values can be calculated by taking the maximum value of a + * specific range, convert it to m/s^2, round it up to the nearest power + * of two, the power is the shift value. + * + * @var adxl34x_shift_conv + */ +static const uint8_t adxl34x_shift_conv[] = { + [ADXL34X_RANGE_2G] = 5, /**< 2g => 19.6m/s^2 => 32 => 2^5 */ + [ADXL34X_RANGE_4G] = 6, /**< 4g => 39.2m/s^2 => 64 => 2^6 */ + [ADXL34X_RANGE_8G] = 7, /**< 8g => 78.4m/s^2 => 128 => 2^7 */ + [ADXL34X_RANGE_16G] = 8, /**< 16g => 156.9m/s^2 => 256 => 2^8 */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_CONVERT_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_decoder.c b/drivers/sensor/adi/adxl34x/adxl34x_decoder.c new file mode 100644 index 0000000000000..9c78caf612bd3 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_decoder.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_decoder.h" +#include "adxl34x_convert.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +#if defined(CONFIG_ADXL34X_DATA_TYPE_Q31) + +/** + * Convert the raw sensor data from the registery to the q31 format + * + * @param[in] value The raw value to convert + * @param[in] range_scale The scale of range + * @param[in] shift The shift value to use in the q31 number + * @param[out] out Pointer to the converted q31 number + */ +static void adxl34x_convert_raw_to_q31(const uint8_t *value, uint16_t range_scale, int8_t shift, + q31_t *out) +{ + const int16_t raw_value = sys_get_le16(value); + const int32_t ug = (int32_t)raw_value * range_scale * 100; + struct sensor_value ms2; + + sensor_ug_to_ms2(ug, &ms2); + const int64_t q31_temp = ((int64_t)ms2.val1 * INT64_C(1000000) + ms2.val2) * + ((int64_t)INT32_MAX + 1) / ((1 << shift) * INT64_C(1000000)); + *out = CLAMP(q31_temp, INT32_MIN, INT32_MAX); +} + +#elif defined(CONFIG_ADXL34X_DATA_TYPE_SENSOR_VALUE) + +/** + * Convert the raw sensor data from the registery to the sensor value format + * + * @param[in] value The raw value to convert + * @param[in] range_scale The scale of range + * @param[out] out Pointer to the converted sensor value + */ +static void adxl34x_convert_raw_to_sensor_value(const uint8_t *value, uint16_t range_scale, + struct sensor_value *out) +{ + const int16_t raw_value = sys_get_le16(value); + const int32_t ug = (int32_t)raw_value * range_scale * 100; + + sensor_ug_to_ms2(ug, out); +} + +#elif defined(CONFIG_ADXL34X_DATA_TYPE_DOUBLE) + +/** + * Convert the raw sensor data from the registery to the double format + * + * @param[in] value The raw value to convert + * @param[in] range_scale The scale of range + * @param[out] out Pointer to the converted double + */ +static void adxl34x_convert_raw_to_double(const uint8_t *value, uint16_t range_scale, double *out) +{ + const int16_t raw_value = sys_get_le16(value); + + *out = (double)raw_value * range_scale / 10000 * SENSOR_G / 1000000LL; +} + +#endif /* CONFIG_ADXL34X_DATA_TYPE_Q31 */ + +/** + * Get the number of x-y-z samples when reading one packet when using the decode + * function. One frame equals one tuple of a x, y and z value. + * + * @param[in] buffer The buffer provided on the @ref rtio context. + * @param[in] channel The channel to get the count for + * @param[out] frame_count The number of frames on the buffer (at least 1) + * @return 0 if successful, negative errno code if failure. + * @see sensor_get_decoder(), sensor_decode(), get_frame_count() + */ +static int adxl34x_decoder_get_frame_count(const uint8_t *buffer, struct sensor_chan_spec channel, + uint16_t *frame_count) +{ + const struct adxl34x_encoded_data *edata = (const struct adxl34x_encoded_data *)buffer; + const struct adxl34x_decoder_header *header = &edata->header; + + if (channel.chan_idx != 0) { + return -ENOTSUP; + } + if (header->entries == 0) { + return -ENOTSUP; + } + + switch (channel.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + *frame_count = edata->header.entries; + break; + default: + return -ENOTSUP; + } + return 0; +} + +/** + * Get the size required to decode a given channel + * + * @param[in] channel The channel to query + * @param[out] base_size The size of decoding the first frame + * @param[out] frame_size The additional size of every additional frame + * @return 0 if successful, negative errno code if failure. + * @see sensor_get_decoder(), sensor_decode(), get_size_info() + */ +static int adxl34x_decoder_get_size_info(struct sensor_chan_spec channel, size_t *base_size, + size_t *frame_size) +{ + switch (channel.chan_type) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: +#if defined(CONFIG_ADXL34X_DATA_TYPE_Q31) + *base_size = sizeof(struct sensor_three_axis_data); + *frame_size = sizeof(struct sensor_three_axis_sample_data); +#elif defined(CONFIG_ADXL34X_DATA_TYPE_SENSOR_VALUE) + *base_size = sizeof(struct sensor_value[3]); + *frame_size = sizeof(struct sensor_value[3]); +#elif defined(CONFIG_ADXL34X_DATA_TYPE_DOUBLE) + *base_size = sizeof(struct adxl343_sensor_data); + *frame_size = sizeof(struct adxl343_sample_value); +#endif + break; + default: + return -ENOTSUP; + } + return 0; +} + +/** + * Decode up to @p max_count samples from the buffer + * + * @param[in] buffer The buffer provided on the @ref rtio context + * @param[in] channel The channel to decode + * @param[out] fit The current frame iterator + * @param[in] max_count The maximum number of channels to decode. + * @param[out] data_out The decoded data + * @return 0 if successful, negative errno code if failure. + * @see sensor_get_decoder(), sensor_decode(), get_frame_count(), get_size_info() + */ +static int adxl34x_decoder_decode(const uint8_t *buffer, struct sensor_chan_spec channel, + uint32_t *fit, uint16_t max_count, void *data_out) +{ + ARG_UNUSED(fit); + ARG_UNUSED(max_count); + const struct adxl34x_encoded_data *edata = (const struct adxl34x_encoded_data *)buffer; + const struct adxl34x_decoder_header *header = &edata->header; + int nr_of_decoded_samples = 0; + + if (channel.chan_idx != 0) { + return -ENOTSUP; + } + if (header->entries == 0) { + return -ENOTSUP; + } + if (channel.chan_type != SENSOR_CHAN_ACCEL_X && channel.chan_type != SENSOR_CHAN_ACCEL_Y && + channel.chan_type != SENSOR_CHAN_ACCEL_Z && + channel.chan_type != SENSOR_CHAN_ACCEL_XYZ) { + return -ENOTSUP; + } + + const uint16_t range_scale = adxl34x_range_conv[header->range]; + uint8_t offset = 0; + +#if defined(CONFIG_ADXL34X_DATA_TYPE_Q31) + const uint8_t shift = adxl34x_shift_conv[header->range]; + struct sensor_three_axis_data *out = (struct sensor_three_axis_data *)data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = header->entries; + out->shift = shift; + +#elif defined(CONFIG_ADXL34X_DATA_TYPE_SENSOR_VALUE) + struct sensor_value *out = (struct sensor_value *)data_out; + +#elif defined(CONFIG_ADXL34X_DATA_TYPE_DOUBLE) + struct adxl343_sensor_data *out = (struct adxl343_sensor_data *)data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + out->header.reading_count = header->entries; +#endif + + for (int i = 0; i < header->entries; i++) { +#if defined(CONFIG_ADXL34X_DATA_TYPE_Q31) + adxl34x_convert_raw_to_q31(edata->fifo_data + offset, range_scale, shift, + &out->readings[i].x); + adxl34x_convert_raw_to_q31(edata->fifo_data + offset + 2, range_scale, shift, + &out->readings[i].y); + adxl34x_convert_raw_to_q31(edata->fifo_data + offset + 4, range_scale, shift, + &out->readings[i].z); + +#elif defined(CONFIG_ADXL34X_DATA_TYPE_SENSOR_VALUE) + adxl34x_convert_raw_to_sensor_value(edata->fifo_data + offset, range_scale, + &out[i * 3]); + adxl34x_convert_raw_to_sensor_value(edata->fifo_data + offset + 2, range_scale, + &out[i * 3 + 1]); + adxl34x_convert_raw_to_sensor_value(edata->fifo_data + offset + 4, range_scale, + &out[i * 3 + 2]); + +#elif defined(CONFIG_ADXL34X_DATA_TYPE_DOUBLE) + adxl34x_convert_raw_to_double(edata->fifo_data + offset, range_scale, + &out->readings[i].x); + adxl34x_convert_raw_to_double(edata->fifo_data + offset + 2, range_scale, + &out->readings[i].y); + adxl34x_convert_raw_to_double(edata->fifo_data + offset + 4, range_scale, + &out->readings[i].z); +#endif + offset += sizeof(edata->fifo_data); + nr_of_decoded_samples++; + } + return nr_of_decoded_samples; +} + +/** + * Check if the given trigger type is present + * + * @param[in] buffer The buffer provided on the @ref rtio context + * @param[in] trigger The trigger type in question + * @return 0 if successful, negative errno code if failure. + */ +static bool adxl34x_decoder_has_trigger(const uint8_t *buffer, enum sensor_trigger_type trigger) +{ + const struct adxl34x_encoded_data *edata = (const struct adxl34x_encoded_data *)buffer; + const struct adxl34x_decoder_header *header = &edata->header; + bool has_trigger = false; + + switch (trigger) { + case SENSOR_TRIG_DATA_READY: /* New data is ready */ + case SENSOR_TRIG_FIFO_WATERMARK: /* The FIFO watermark has been reached */ + case SENSOR_TRIG_FIFO_FULL: /* The FIFO becomes full */ + has_trigger = header->trigger.data_ready || header->trigger.watermark || + header->trigger.overrun; + break; + case SENSOR_TRIG_TAP: /* A single tap is detected. */ + has_trigger = header->trigger.single_tap; + break; + case SENSOR_TRIG_DOUBLE_TAP: /* A double tap is detected. */ + has_trigger = header->trigger.double_tap; + break; + case SENSOR_TRIG_FREEFALL: /* A free fall is detected. */ + has_trigger = header->trigger.free_fall; + break; + case SENSOR_TRIG_MOTION: /* Motion is detected. */ + has_trigger = header->trigger.activity; + break; + case SENSOR_TRIG_STATIONARY: /* No motion has been detected for a while. */ + has_trigger = header->trigger.inactivity; + break; + case SENSOR_TRIG_TIMER: + case SENSOR_TRIG_DELTA: + case SENSOR_TRIG_NEAR_FAR: + case SENSOR_TRIG_THRESHOLD: + case SENSOR_TRIG_COMMON_COUNT: + case SENSOR_TRIG_MAX: + default: + return false; + } + return has_trigger; +} + +/** + * @brief The sensor driver decoder API callbacks + */ +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = adxl34x_decoder_get_frame_count, + .get_size_info = adxl34x_decoder_get_size_info, + .decode = adxl34x_decoder_decode, + .has_trigger = adxl34x_decoder_has_trigger, +}; + +/** + * Callback API to get the decoder associated with the given device + * + * @param[in] dev Pointer to the sensor device + * @param[in] decoder Pointer to the decoder which will be set upon success + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + return 0; +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_decoder.h b/drivers/sensor/adi/adxl34x/adxl34x_decoder.h new file mode 100644 index 0000000000000..a83ea5b8ed1d2 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_decoder.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_DECODER_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_DECODER_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Header used to decode raw data + * + * The decoder is executed outside of the driver context, e.g. in user-space and/or in the + * application context. Any information needed to decode raw data needs to be provided in this + * header. + * + * @struct adxl34x_decoder_header + */ +struct adxl34x_decoder_header { + uint64_t timestamp; /**< The timestamp when the sample was collected */ + enum adxl34x_accel_range range: 2; /**< The range setting to convert the sample to g */ + uint8_t entries: 6; /**< The number of samples */ + struct adxl34x_int_source trigger; /**< The triggers active */ +} __attribute__((__packed__)); + +/** + * @brief Structure provided to the decoder containing the not yet decoded (encoded) data + * @struct adxl34x_encoded_data + */ +struct adxl34x_encoded_data { + struct adxl34x_decoder_header header; /**< Header containing conversion info */ + uint8_t fifo_data[6]; /**< The raw (encoded) samples */ +}; + +#ifdef CONFIG_ADXL34X_DATA_TYPE_DOUBLE + +/** + * @brief Header used when data is returned when CONFIG_ADXL34X_DATA_TYPE_DOUBLE is set + * @struct adxl343_data_header + */ +struct adxl343_data_header { + uint64_t base_timestamp_ns; /**< The timestamp when the sample was collected */ + uint16_t reading_count; /**< The number of samples */ +}; + +/** + * @brief Data structure used when data is returned when CONFIG_ADXL34X_DATA_TYPE_DOUBLE is set + * @struct adxl343_sample_value + */ +struct adxl343_sample_value { + double x; + double y; + double z; +}; + +/** + * @brief Structure used when data is returned when CONFIG_ADXL34X_DATA_TYPE_DOUBLE is set + * @struct adxl343_sensor_data + */ +struct adxl343_sensor_data { + struct adxl343_data_header header; /**< Header containing packet info */ + struct adxl343_sample_value readings[1]; /**< Size of the array depends on reading_count **/ +}; +#endif /* CONFIG_ADXL34X_DATA_TYPE_DOUBLE */ + +int adxl34x_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_DECODER_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_emul.c b/drivers/sensor/adi/adxl34x/adxl34x_emul.c new file mode 100644 index 0000000000000..3d90a4c017064 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_emul.c @@ -0,0 +1,639 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x_reg.h" +#include "adxl34x_emul.h" +#include "adxl34x_private.h" +#include "adxl34x_convert.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +#define Q31_SCALE ((int64_t)INT32_MAX + 1) +#define DOUBLE_TO_Q31(x, shift) ((int64_t)((double)(x) * (double)Q31_SCALE) >> shift) +#define Q31_TO_DOUBLE(x, shift) ((double)((int64_t)(x) << shift) / (double)Q31_SCALE) +#define G_TO_MS2(g) (g * SENSOR_G / 1000000LL) +#define MS2_TO_G(ms) (ms / SENSOR_G * 1000000LL) + +/** + * @brief Conversion from register values to their lsb values in ug + * @var adxl34x_lsb_conv + */ +static const uint16_t adxl34x_lsb_conv[] = { + [ADXL34X_RANGE_2G] = 3900, + [ADXL34X_RANGE_4G] = 7800, + [ADXL34X_RANGE_8G] = 15600, + [ADXL34X_RANGE_16G] = 31200, +}; + +/** + * Convert a q31 type value to a raw register value. + * + * @param[in] value The q31 to convert + * @param[in] shift The shift value to use for the q31 value provided + * @param[in] range The accelero range used when the value was collected + * @return 0 if successful, negative errno code if failure. + */ +static int32_t adxl34x_convert_q31_to_raw(const q31_t *value, uint8_t shift, + enum adxl34x_accel_range range) +{ + double ms2 = Q31_TO_DOUBLE(*value, shift); + double u_g = MS2_TO_G(ms2) * 1000000; + const uint16_t u_g_lsb = adxl34x_lsb_conv[range]; + const int32_t raw = (int32_t)(u_g / u_g_lsb); + return raw; +} + +/** + * Read from the virtual device registery + * + * @param[in] target Pointer to the emulation device + * @param[in] address The register address to read from + * @return 0 if successful, negative errno code if failure. + */ +static uint8_t reg_read(const struct emul *target, uint8_t address) +{ + const struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + uint8_t val = data->reg[address]; + + switch (address) { + case ADXL34X_REG_DEVID: + case ADXL34X_REG_THRESH_TAP: + case ADXL34X_REG_OFSX: + case ADXL34X_REG_OFSY: + case ADXL34X_REG_OFSZ: + case ADXL34X_REG_DUR: + case ADXL34X_REG_LATENT: + case ADXL34X_REG_WINDOW: + case ADXL34X_REG_THRESH_ACT: + case ADXL34X_REG_THRESH_INACT: + case ADXL34X_REG_TIME_INACT: + case ADXL34X_REG_ACT_INACT_CTL: + case ADXL34X_REG_THRESH_FF: + case ADXL34X_REG_TIME_FF: + case ADXL34X_REG_TAP_AXES: + case ADXL34X_REG_ACT_TAP_STATUS: + case ADXL34X_REG_BW_RATE: + case ADXL34X_REG_POWER_CTL: + case ADXL34X_REG_INT_ENABLE: + case ADXL34X_REG_INT_MAP: + case ADXL34X_REG_INT_SOURCE: + case ADXL34X_REG_DATA_FORMAT: + case ADXL34X_REG_DATA: + case ADXL34X_REG_DATAX1: + case ADXL34X_REG_DATAY0: + case ADXL34X_REG_DATAY1: + case ADXL34X_REG_DATAZ0: + case ADXL34X_REG_DATAZ1: + case ADXL34X_REG_FIFO_CTL: + case ADXL34X_REG_FIFO_STATUS: + break; + + /* Additional registers for the ADXL344 and ADXL346. */ + case ADXL34X_REG_TAP_SIGN: + case ADXL34X_REG_ORIENT_CONF: + case ADXL34X_REG_ORIENT: { + const uint8_t devid = data->reg[ADXL34X_REG_DEVID]; + + if (devid != ADXL344_DEVID && devid != ADXL346_DEVID) { + LOG_WRN("Trying to read from unknown address 0x%02X", address); + return -1; + } + break; + } + default: + LOG_WRN("Trying to read from unknown address 0x%02X", address); + return -1; + } + return val; +} + +/** + * Write from the virtual device registery + * + * @param[in] target Pointer to the emulation device + * @param[in] address The register address to write to + * @param[in] val The value of the register to write to + */ +static void reg_write(const struct emul *target, uint8_t address, uint8_t val) +{ + struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + + switch (address) { + case ADXL34X_REG_THRESH_TAP: + case ADXL34X_REG_OFSX: + case ADXL34X_REG_OFSY: + case ADXL34X_REG_OFSZ: + case ADXL34X_REG_DUR: + case ADXL34X_REG_LATENT: + case ADXL34X_REG_WINDOW: + case ADXL34X_REG_THRESH_ACT: + case ADXL34X_REG_THRESH_INACT: + case ADXL34X_REG_TIME_INACT: + case ADXL34X_REG_ACT_INACT_CTL: + case ADXL34X_REG_THRESH_FF: + case ADXL34X_REG_TIME_FF: + case ADXL34X_REG_TAP_AXES: + case ADXL34X_REG_BW_RATE: + case ADXL34X_REG_POWER_CTL: + case ADXL34X_REG_INT_ENABLE: + case ADXL34X_REG_INT_MAP: + case ADXL34X_REG_DATA_FORMAT: + case ADXL34X_REG_FIFO_CTL: + break; + + case ADXL34X_REG_DEVID: + case ADXL34X_REG_ACT_TAP_STATUS: + case ADXL34X_REG_INT_SOURCE: + case ADXL34X_REG_DATA: + case ADXL34X_REG_DATAX1: + case ADXL34X_REG_DATAY0: + case ADXL34X_REG_DATAY1: + case ADXL34X_REG_DATAZ0: + case ADXL34X_REG_DATAZ1: + case ADXL34X_REG_FIFO_STATUS: + LOG_WRN("Trying to write to read only address 0x%02X", address); + return; + + /* Additional registers for the ADXL344 and ADXL346. */ + case ADXL34X_REG_TAP_SIGN: + case ADXL34X_REG_ORIENT: + LOG_WRN("Trying to write to read only (and/or unknown) address 0x%02X", address); + return; + + case ADXL34X_REG_ORIENT_CONF: { + const uint8_t devid = data->reg[ADXL34X_REG_DEVID]; + + if (devid != ADXL344_DEVID && devid != ADXL346_DEVID) { + LOG_WRN("Trying to write to unknown address 0x%02X", address); + return; + } + break; + } + default: + LOG_WRN("Trying to write to unknown address 0x%02X", address); + return; + } + data->reg[address] = val; +} + +/** + * Callback API for initialising the emulation device + * + * @param[in] target Pointer to the emulation device + * @param[in] parent Device that is using the emulator + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_init(const struct emul *target, const struct device *parent) +{ + struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + uint8_t *reg = data->reg; + + LOG_DBG("Setting emulated device registers of %s/%s to default", parent->name, + target->dev->name); + /* Set the register defaults */ + reg[ADXL34X_REG_DEVID] = ADXL344_DEVID; + reg[ADXL34X_REG_BW_RATE] = 0x0A; + reg[ADXL34X_REG_INT_SOURCE] = 0x02; + reg[ADXL34X_REG_ORIENT_CONF] = 0x25; + return 0; +} + +/** + * Callback API for setting an expected value for a given channel + * + * @param[in] target Pointer to the emulation device + * @param[in] ch Sensor channel to set expected value for + * @param[in] value Expected value in fixed-point format using standard SI unit for sensor type + * @param[in] shift Shift value (scaling factor) applied to @p value + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_set_channel(const struct emul *target, struct sensor_chan_spec ch, + const q31_t *value, int8_t shift) +{ + struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + uint8_t *reg = data->reg; + + if (reg == NULL || value == NULL) { + return -ENOTSUP; + } + if (ch.chan_type != SENSOR_CHAN_ACCEL_X && ch.chan_type != SENSOR_CHAN_ACCEL_Y && + ch.chan_type != SENSOR_CHAN_ACCEL_Z) { + return -ENOTSUP; + } + + const enum adxl34x_accel_range range = + FIELD_GET(ADXL34X_REG_DATA_FORMAT_RANGE, reg[ADXL34X_REG_DATA_FORMAT]); + __ASSERT_NO_MSG(range >= ADXL34X_RANGE_2G && range <= ADXL34X_RANGE_16G); + const int16_t reg_value = + CLAMP(adxl34x_convert_q31_to_raw(value, shift, range), INT16_MIN, INT16_MAX); + const uint8_t base_address = ADXL34X_REG_DATA + (ch.chan_type - SENSOR_CHAN_ACCEL_X) * 2; + + if (base_address != ADXL34X_REG_DATAX0 && base_address != ADXL34X_REG_DATAY0 && + base_address != ADXL34X_REG_DATAZ0) { + return -ERANGE; + } + + /* Set the FIFO value */ + sys_put_le16(reg_value, ®[base_address]); + /* Set the FIFO number of entries */ + reg[ADXL34X_REG_FIFO_STATUS] = FIELD_PREP(ADXL34X_REG_FIFO_STATUS_ENTRIES, 1); + return 0; +} + +/** + * Callback API for getting the supported sample value range and tolerance for a given channel + * + * @param[in] target Pointer to the emulation device + * @param[in] ch The channel to request info for. If @p ch is unsupported, return `-ENOTSUP` + * @param[out] lower Minimum supported sample value in SI units, fixed-point format + * @param[out] upper Maximum supported sample value in SI units, fixed-point format + * @param[out] epsilon Tolerance to use comparing expected and actual values to account for rounding + * and sensor precision issues. This can usually be set to the minimum sample value step + * size. Uses SI units and fixed-point format. + * @param[out] shift The shift value (scaling factor) associated with @p lower, @p upper, and + * @p epsilon. + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_get_sample_range(const struct emul *target, struct sensor_chan_spec ch, + q31_t *lower, q31_t *upper, q31_t *epsilon, int8_t *shift) +{ + struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + uint8_t *reg = data->reg; + + if (reg == NULL) { + return -ENOTSUP; + } + if (ch.chan_type != SENSOR_CHAN_ACCEL_X && ch.chan_type != SENSOR_CHAN_ACCEL_Y && + ch.chan_type != SENSOR_CHAN_ACCEL_Z && ch.chan_type != SENSOR_CHAN_ACCEL_XYZ) { + return -ENOTSUP; + } + + const enum adxl34x_accel_range range = + FIELD_GET(ADXL34X_REG_DATA_FORMAT_RANGE, reg[ADXL34X_REG_DATA_FORMAT]); + __ASSERT_NO_MSG(range >= ADXL34X_RANGE_2G && range <= ADXL34X_RANGE_16G); + *shift = adxl34x_shift_conv[range]; + double epsilon_ms2 = G_TO_MS2((double)adxl34x_lsb_conv[range] / 1000000LL); + *epsilon = DOUBLE_TO_Q31(epsilon_ms2, *shift); + double upper_ms2 = G_TO_MS2((double)adxl34x_max_g_conv[range]); + *upper = DOUBLE_TO_Q31(upper_ms2, *shift); + *lower = -*upper; + return 0; +} + +/** + * Set the emulator's offset attribute value + * + * @param[in] target Pointer to the emulation device + * @param[in] ch The channel to use + * @param[in] value The offset to use + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_set_attr_offset(const struct emul *target, struct sensor_chan_spec ch, + const struct sensor_three_axis_attribute *value) +{ + struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + struct adxl34x_dev_data *dev_data = target->dev->data; + struct adxl34x_cfg *cfg = &dev_data->cfg; + + uint8_t *reg = data->reg; + const uint8_t shift = value->shift; + + if (ch.chan_type == SENSOR_CHAN_ACCEL_X || ch.chan_type == SENSOR_CHAN_ACCEL_XYZ) { + const int8_t offset_x = + CLAMP(adxl34x_convert_q31_to_raw(&value->x, shift, ADXL34X_RANGE_8G), + INT8_MIN, INT8_MAX); + reg[ADXL34X_REG_OFSX] = offset_x; + cfg->ofsx = offset_x; /* Update cached value as well. */ + } + if (ch.chan_type == SENSOR_CHAN_ACCEL_Y || ch.chan_type == SENSOR_CHAN_ACCEL_XYZ) { + const uint8_t offset_y = + CLAMP(adxl34x_convert_q31_to_raw(&value->y, shift, ADXL34X_RANGE_8G), + INT8_MIN, UINT8_MAX); + reg[ADXL34X_REG_OFSY] = offset_y; + cfg->ofsy = offset_y; /* Update cached value as well. */ + } + if (ch.chan_type == SENSOR_CHAN_ACCEL_Z || ch.chan_type == SENSOR_CHAN_ACCEL_XYZ) { + const uint8_t offset_z = + CLAMP(adxl34x_convert_q31_to_raw(&value->z, shift, ADXL34X_RANGE_8G), + INT8_MIN, UINT8_MAX); + reg[ADXL34X_REG_OFSZ] = offset_z; + cfg->ofsz = offset_z; /* Update cached value as well. */ + } + return 0; +} + +/** + * Get metadata about the offset attribute. + * + * @param[in] target Pointer to the emulation device + * @param[out] min The minimum value the attribute can be set to + * @param[out] max The maximum value the attribute can be set to + * @param[out] increment The value that the attribute increases by for every LSB + * @param[out] shift The shift for @p min, @p max, and @p increment + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_get_attr_offset_metadata(const struct emul *target, q31_t *min, q31_t *max, + q31_t *increment, int8_t *shift) +{ + *shift = adxl34x_shift_conv[ADXL34X_RANGE_2G]; + double min_ms2 = G_TO_MS2(-1.9968); /* -128 * 0.0156 */ + *min = DOUBLE_TO_Q31(min_ms2, *shift); + double max_ms2 = G_TO_MS2(1.9812); /* 127 * 0.0156 */ + *max = DOUBLE_TO_Q31(max_ms2, *shift); + double increment_ms2 = G_TO_MS2(0.0156); + *increment = DOUBLE_TO_Q31(increment_ms2, *shift); + return 0; +} + +/** + * Callback API to set the attribute value(s) of a given chanel + * + * @param[in] target Pointer to the emulation device + * @param[in] ch The channel to use. If @p ch is unsupported, return `-ENOTSUP` + * @param[in] attribute The attribute to set + * @param[in] value the value to use (cast according to the channel/attribute pair) + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_set_attribute(const struct emul *target, struct sensor_chan_spec ch, + enum sensor_attribute attribute, const void *value) +{ + const struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + const uint8_t *reg = data->reg; + + if (reg == NULL || value == NULL) { + return -ENOTSUP; + } + if (ch.chan_type != SENSOR_CHAN_ACCEL_X && ch.chan_type != SENSOR_CHAN_ACCEL_Y && + ch.chan_type != SENSOR_CHAN_ACCEL_Z && ch.chan_type != SENSOR_CHAN_ACCEL_XYZ) { + return -ENOTSUP; + } + + switch (attribute) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + case SENSOR_ATTR_LOWER_THRESH: + case SENSOR_ATTR_UPPER_THRESH: + case SENSOR_ATTR_SLOPE_TH: + case SENSOR_ATTR_SLOPE_DUR: + case SENSOR_ATTR_HYSTERESIS: + case SENSOR_ATTR_OVERSAMPLING: + case SENSOR_ATTR_FULL_SCALE: + break; + case SENSOR_ATTR_OFFSET: + return adxl34x_emul_set_attr_offset(target, ch, value); + case SENSOR_ATTR_CALIB_TARGET: + case SENSOR_ATTR_CONFIGURATION: + case SENSOR_ATTR_CALIBRATION: + case SENSOR_ATTR_FEATURE_MASK: + case SENSOR_ATTR_ALERT: + case SENSOR_ATTR_FF_DUR: + case SENSOR_ATTR_BATCH_DURATION: + case SENSOR_ATTR_COMMON_COUNT: + case SENSOR_ATTR_GAIN: + case SENSOR_ATTR_RESOLUTION: + break; + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } + return -ENOTSUP; +} + +/** + * Callback API to get metadata about an attribute + * + * @param[in] target Pointer to the emulation device + * @param[in] ch The channel to request info for. If @p ch is unsupported, return '-ENOTSUP' + * @param[in] attribute The attribute to request info for. If @p attribute is unsupported, return + * '-ENOTSUP' + * @param[out] min The minimum value the attribute can be set to + * @param[out] max The maximum value the attribute can be set to + * @param[out] increment The value that the attribute increases by for every LSB + * @param[out] shift The shift for @p min, @p max, and @p increment + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_emul_get_attribute_metadata(const struct emul *target, + struct sensor_chan_spec ch, + enum sensor_attribute attribute, q31_t *min, + q31_t *max, q31_t *increment, int8_t *shift) +{ + const struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + const uint8_t *reg = data->reg; + + if (reg == NULL) { + return -ENOTSUP; + } + if (ch.chan_type != SENSOR_CHAN_ACCEL_X && ch.chan_type != SENSOR_CHAN_ACCEL_Y && + ch.chan_type != SENSOR_CHAN_ACCEL_Z && ch.chan_type != SENSOR_CHAN_ACCEL_XYZ) { + return -ENOTSUP; + } + + switch (attribute) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + case SENSOR_ATTR_LOWER_THRESH: + case SENSOR_ATTR_UPPER_THRESH: + case SENSOR_ATTR_SLOPE_TH: + case SENSOR_ATTR_SLOPE_DUR: + case SENSOR_ATTR_HYSTERESIS: + case SENSOR_ATTR_OVERSAMPLING: + case SENSOR_ATTR_FULL_SCALE: + break; + case SENSOR_ATTR_OFFSET: + return adxl34x_emul_get_attr_offset_metadata(target, min, max, increment, shift); + case SENSOR_ATTR_CALIB_TARGET: + case SENSOR_ATTR_CONFIGURATION: + case SENSOR_ATTR_CALIBRATION: + case SENSOR_ATTR_FEATURE_MASK: + case SENSOR_ATTR_ALERT: + case SENSOR_ATTR_FF_DUR: + case SENSOR_ATTR_BATCH_DURATION: + case SENSOR_ATTR_COMMON_COUNT: + case SENSOR_ATTR_GAIN: + case SENSOR_ATTR_RESOLUTION: + break; + default: + LOG_ERR("Unknown attribute"); + return -ENOTSUP; + } + return -ENOTSUP; +} + +/** + * @brief The sensor driver emulator API callbacks + * @var adxl34x_emul_api + */ +static const struct emul_sensor_driver_api adxl34x_emul_api = { + .set_channel = adxl34x_emul_set_channel, + .get_sample_range = adxl34x_emul_get_sample_range, + .set_attribute = adxl34x_emul_set_attribute, + .get_attribute_metadata = adxl34x_emul_get_attribute_metadata, +}; + +#ifdef CONFIG_ADXL34X_BUS_SPI + +/** + * Callback API to emulate spi communication + * + * Passes SPI messages to the emulator. The emulator updates the data with what + * was read back. + * + * @param[in] target Pointer to the emulation device + * @param[in] config Pointer to a valid spi_config structure instance + * @param[in] tx_bufs Buffer array where data to be sent originates from, or NULL if none. + * @param[in] rx_bufs Buffer array where data to be read will be written to, or NULL if none. + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_spi_emul_io(const struct emul *target, const struct spi_config *config, + const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs) +{ + const struct spi_buf *tx, *txd, *rxd; + unsigned int address, val; + int count; + bool is_read_cmd, is_multi_byte; + + ARG_UNUSED(config); + + __ASSERT_NO_MSG(tx_bufs || rx_bufs); + __ASSERT_NO_MSG(!tx_bufs || !rx_bufs || tx_bufs->count == rx_bufs->count); + count = tx_bufs ? tx_bufs->count : rx_bufs->count; + + if (count != 2) { + LOG_DBG("Unsupported nr of packages (%d) in spi transaction", count); + return -EIO; + } + tx = tx_bufs->buffers; + txd = &tx_bufs->buffers[1]; + rxd = rx_bufs ? &rx_bufs->buffers[1] : NULL; + + if (tx->len != 1) { + LOG_DBG("Unsupported nr of bytes (%d) in spi transaction", tx->len); + return -EIO; + } + + address = *(uint8_t *)tx->buf; + is_read_cmd = address & ADXL34X_SPI_MSG_READ; + is_multi_byte = address & ADXL34X_SPI_MULTI_BYTE; + address &= ~ADXL34X_SPI_MSG_READ; + address &= ~ADXL34X_SPI_MULTI_BYTE; + + if (is_read_cmd && rxd == NULL) { + LOG_DBG("Spi read transaction, but no read buffer supplied"); + return -EINVAL; + } + if (is_multi_byte && txd->len <= 1) { + LOG_DBG("Spi transaction contains single byte, but multi-bit is set"); + return -EINVAL; + } + if (!is_multi_byte && txd->len > 1) { + LOG_DBG("Spi transaction contains multiple bytes, but multi-bit is not set"); + return -EINVAL; + } + + if (is_read_cmd) { + for (int i = 0; i < txd->len; ++i) { + ((uint8_t *)rxd->buf)[i] = reg_read(target, address + i); + LOG_DBG("SPI read - address:0x%02X, value:0x%02X", address + i, + ((uint8_t *)rxd->buf)[i]); + } + } else if (txd->len == 1) { + val = *(uint8_t *)txd->buf; + LOG_DBG("SPI write - address:0x%02X, value:0x%02X", address, val); + reg_write(target, address, val); + } else { + LOG_DBG("Unsupported nr of bytes (%d) in spi write transaction", txd->len); + return -EIO; + } + return 0; +} + +/** + * @brief The sensor driver emulator spi API callbacks + * @var adxl34x_spi_emul_api + */ +static struct spi_emul_api adxl34x_spi_emul_api = { + .io = adxl34x_spi_emul_io, +}; + +#endif /* CONFIG_ADXL34X_BUS_SPI */ + +#ifdef CONFIG_ADXL34X_BUS_I2C + +/** + * Callback API to emulate a i2c transfer + * + * @param[in] target Pointer to the emulation device + * @param[out] msgs Array of messages to transfer. For 'read' messages, this function + * updates the 'buf' member with the data that was read. + * @param[in] num_msgs Number of messages to transfer + * @param[in] addr Address of the I2C target device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_i2c_emul_transfer(const struct emul *target, struct i2c_msg *msgs, int num_msgs, + int addr) +{ + if (msgs == NULL || msgs[0].flags & I2C_MSG_READ) { + LOG_ERR("Unexpected i2c message"); + return -EIO; + } + + const uint8_t address = msgs[0].buf[0]; + + if (num_msgs == 1 && ((msgs[0].flags & I2C_MSG_READ) == 0)) { + /* I2C write transaction. */ + LOG_DBG("I2C write - address:0x%02X, value:0x%02X", address, msgs[0].buf[1]); + if (msgs[0].len != 2) { + LOG_ERR("Unexpected i2c message length %d", msgs[0].len); + return -EIO; + } + reg_write(target, address, msgs[0].buf[1]); + + } else if (num_msgs == 2 && msgs[1].flags & I2C_MSG_READ) { + /* I2C read transaction. */ + for (int i = 0; i < msgs[1].len; i++) { + msgs[1].buf[i] = reg_read(target, address + i); + LOG_DBG("I2C read - address:0x%02X, value:0x%02X", address + i, + msgs[1].buf[i]); + } + } else { + LOG_ERR("Unexpected i2c message - address:0x%02X", address); + return -EIO; + } + return 0; +} + +/** + * @brief The sensor driver emulator i2c API callbacks + * @var adxl34x_i2c_emul_api + */ +static struct i2c_emul_api adxl34x_i2c_emul_api = { + .transfer = adxl34x_i2c_emul_transfer, +}; + +#endif /* CONFIG_ADXL34X_BUS_I2C */ + +#define ADXL34X_EMUL_DEVICE(i) \ + static struct adxl34x_emul_data adxl34x_emul_data_##i; \ + \ + static const struct adxl34x_emul_config adxl34x_emul_config_##i = { \ + .addr = DT_INST_REG_ADDR(i), \ + }; \ + \ + EMUL_DT_INST_DEFINE(i, adxl34x_emul_init, &adxl34x_emul_data_##i, \ + &adxl34x_emul_config_##i, \ + COND_CODE_1(DT_INST_ON_BUS(i, spi), (&adxl34x_spi_emul_api), \ + (&adxl34x_i2c_emul_api)), \ + &adxl34x_emul_api) + +DT_INST_FOREACH_STATUS_OKAY(ADXL34X_EMUL_DEVICE) diff --git a/drivers/sensor/adi/adxl34x/adxl34x_emul.h b/drivers/sensor/adi/adxl34x/adxl34x_emul.h new file mode 100644 index 0000000000000..d7582e72e111a --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_emul.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_EMUL_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_EMUL_H_ + +#include + +#include "adxl34x_reg.h" + +/** + * @brief Virtual registry of the adxl34x used in emulation mode + * @struct adxl34x_emul_data + */ +struct adxl34x_emul_data { + uint8_t reg[ADXL34X_REG_MAX + 1]; +}; + +/** + * @brief Virtual (static) configuration used in emulation mode + * @struct adxl34x_emul_config + */ +struct adxl34x_emul_config { + uint16_t addr; /**< Address of emulator */ +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_EMUL_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_i2c.c b/drivers/sensor/adi/adxl34x/adxl34x_i2c.c new file mode 100644 index 0000000000000..11c777683a628 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_i2c.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_private.h" +#include "adxl34x_i2c.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +/** + * Initialise the I2C device + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_i2c_init(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + + if (!i2c_is_ready_dt(&config->i2c)) { + LOG_ERR("Device not ready"); + return -ENODEV; + } + return 0; +} + +/** + * Function called when a write to the device is initiated + * + * @param[in] dev Pointer to the sensor device + * @param[in] reg_addr Address of the register to write to + * @param[in] reg_data Value to write + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_i2c_write(const struct device *dev, uint8_t reg_addr, uint8_t reg_data) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc; + + rc = i2c_reg_write_byte_dt(&config->i2c, reg_addr, reg_data); + return rc; +} + +/** + * Function called when a read of a single register from the device is initiated + * + * @param[in] dev Pointer to the sensor device + * @param[in] reg_addr Address of the register to read from + * @param[out] reg_data Pointer to store the value read + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_i2c_read(const struct device *dev, uint8_t reg_addr, uint8_t *reg_data) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc; + + rc = i2c_write_read_dt(&config->i2c, ®_addr, sizeof(reg_addr), reg_data, + sizeof(*reg_data)); + return rc; +} + +/** + * Function called when a read of multiple registers from the device is initiated + * + * @param[in] dev Pointer to the sensor device + * @param[in] reg_addr Address of the register to read from + * @param[out] rx_buf Pointer to store the data read + * @param[in] size Size of the @p rx_buf buffer in bytes + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_i2c_read_buf(const struct device *dev, uint8_t reg_addr, uint8_t *rx_buf, uint8_t size) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc; + + rc = i2c_write_read_dt(&config->i2c, ®_addr, sizeof(reg_addr), rx_buf, size); + return rc; +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_i2c.h b/drivers/sensor/adi/adxl34x/adxl34x_i2c.h new file mode 100644 index 0000000000000..d9ecdd65b7665 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_i2c.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_I2C_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_I2C_H_ + +#include + +#include + +#define ADXL34X_CONFIG_I2C(i) \ + .i2c = I2C_DT_SPEC_INST_GET(i), .bus_init = &adxl34x_i2c_init, \ + .bus_write = &adxl34x_i2c_write, .bus_read = &adxl34x_i2c_read, \ + .bus_read_buf = &adxl34x_i2c_read_buf + +int adxl34x_i2c_init(const struct device *dev); +int adxl34x_i2c_write(const struct device *dev, uint8_t reg_addr, uint8_t reg_data); +int adxl34x_i2c_read(const struct device *dev, uint8_t reg_addr, uint8_t *reg_data); +int adxl34x_i2c_read_buf(const struct device *dev, uint8_t reg_addr, uint8_t *rx_buf, uint8_t size); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_I2C_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_private.h b/drivers/sensor/adi/adxl34x/adxl34x_private.h new file mode 100644 index 0000000000000..7f5aea0d40630 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_private.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_PRIVATE_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_PRIVATE_H_ + +#define DT_DRV_COMPAT adi_adxl34x + +#include + +#include +#include +#include +#include +#include + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include +#endif +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include +#endif + +#include "adxl34x_reg.h" + +/** + * @brief Device data for each adxl34x device instance + * + * The data in this structure can (and will) change runtime. + * + * @struct adxl34x_dev_data + */ +struct adxl34x_dev_data { + uint64_t timestamp; +#if CONFIG_ADXL34X_ADXL345_COMPATIBLE + int16_t accel_x[ADXL34X_FIFO_SIZE]; + int16_t accel_y[ADXL34X_FIFO_SIZE]; + int16_t accel_z[ADXL34X_FIFO_SIZE]; + uint8_t sample_number; +#else + int16_t accel_x; + int16_t accel_y; + int16_t accel_z; +#endif /* CONFIG_ADXL34X_ADXL345_COMPATIBLE */ + struct adxl34x_cfg cfg; +#ifdef CONFIG_ADXL34X_TRIGGER + struct gpio_callback gpio_cb; + sensor_trigger_handler_t data_ready_handler; /**< Callback to user app */ + sensor_trigger_handler_t motion_event_handler; /**< Callback to user app */ + const struct sensor_trigger *data_ready_trigger; + const struct sensor_trigger *tap_trigger; + const struct sensor_trigger *double_tap_trigger; + const struct sensor_trigger *freefall_trigger; + const struct sensor_trigger *motion_trigger; + const struct sensor_trigger *stationary_trigger; +#endif /* CONFIG_ADXL34X_TRIGGER */ +#ifdef CONFIG_ADXL34X_ASYNC_API + struct k_work work; + const struct device *dev; + struct rtio_iodev_sqe *iodev_sqe; +#endif /* CONFIG_ADXL34X_ASYNC_API */ +}; + +/** + * @brief Device (static) configuration for each adxl34x device instance + * + * The data in this structure is static can not change runtime. It contains configuration from the + * device tree, and function pointers to read and write to the device (using i2c or spi). + * + * @struct adxl34x_dev_config + */ +struct adxl34x_dev_config { +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + struct i2c_dt_spec i2c; +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + struct spi_dt_spec spi; +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ + struct gpio_dt_spec gpio_int1; + uint8_t dt_int_pin; + uint8_t dt_packet_size; + enum adxl34x_accel_range dt_range; + enum adxl34x_accel_freq dt_rate; + + int (*bus_init)(const struct device *dev); + int (*bus_write)(const struct device *dev, uint8_t reg_addr, uint8_t reg_data); + int (*bus_read)(const struct device *dev, uint8_t reg_addr, uint8_t *reg_data); + int (*bus_read_buf)(const struct device *dev, uint8_t reg_addr, uint8_t *rx_buf, + uint8_t size); +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_PRIVATE_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_reg.h b/drivers/sensor/adi/adxl34x/adxl34x_reg.h new file mode 100644 index 0000000000000..5973da2ed92a0 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_reg.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_REG_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_REG_H_ + +#include + +/* registers */ +#define ADXL34X_REG_DEVID 0x00 /**< Device ID */ +#define ADXL34X_REG_THRESH_TAP 0x1D /**< Tap threshold */ +#define ADXL34X_REG_OFSX 0x1E /**< X-axis offset */ +#define ADXL34X_REG_OFSY 0x1F /**< Y-axis offset */ +#define ADXL34X_REG_OFSZ 0x20 /**< Z-axis offset */ +#define ADXL34X_REG_DUR 0x21 /**< Tap duration */ +#define ADXL34X_REG_LATENT 0x22 /**< Tap latency */ +#define ADXL34X_REG_WINDOW 0x23 /**< Tap window */ +#define ADXL34X_REG_THRESH_ACT 0x24 /**< Activity threshold */ +#define ADXL34X_REG_THRESH_INACT 0x25 /**< Inactivity threshold */ +#define ADXL34X_REG_TIME_INACT 0x26 /**< Inactivity time */ +#define ADXL34X_REG_ACT_INACT_CTL 0x27 /**< Axis control for activity and inactivity detection */ +#define ADXL34X_REG_THRESH_FF 0x28 /**< Free-fall threshold */ +#define ADXL34X_REG_TIME_FF 0x29 /**< Free-fall time */ +#define ADXL34X_REG_TAP_AXES 0x2A /**< Axis control for single tap/double tap */ +#define ADXL34X_REG_ACT_TAP_STATUS 0x2B /**< Source of single tap/double tap */ +#define ADXL34X_REG_BW_RATE 0x2C /**< Data rate and power mode control */ +#define ADXL34X_REG_POWER_CTL 0x2D /**< Power-saving features control */ +#define ADXL34X_REG_INT_ENABLE 0x2E /**< Interrupt enable control */ +#define ADXL34X_REG_INT_MAP 0x2F /**< Interrupt mapping control */ +#define ADXL34X_REG_INT_SOURCE 0x30 /**< Source of interrupts */ +#define ADXL34X_REG_DATA_FORMAT 0x31 /**< Data format control */ +#define ADXL34X_REG_DATA 0x32 /**< FIFO data */ +#define ADXL34X_REG_DATAX0 0x32 /**< X-Axis Data 0 */ +#define ADXL34X_REG_DATAX1 0x33 /**< X-Axis Data 1 */ +#define ADXL34X_REG_DATAY0 0x34 /**< Y-Axis Data 0 */ +#define ADXL34X_REG_DATAY1 0x35 /**< Y-Axis Data 1 */ +#define ADXL34X_REG_DATAZ0 0x36 /**< Z-Axis Data 0 */ +#define ADXL34X_REG_DATAZ1 0x37 /**< Z-Axis Data 1 */ +#define ADXL34X_REG_FIFO_CTL 0x38 /**< FIFO control */ +#define ADXL34X_REG_FIFO_STATUS 0x39 /**< FIFO status */ +/* additional registers for the ADXL344 and ADXL346 */ +#define ADXL34X_REG_TAP_SIGN 0x3A /**< Sign and source for single tap/double tap */ +#define ADXL34X_REG_ORIENT_CONF 0x3B /**< Orientation configuration */ +#define ADXL34X_REG_ORIENT 0x3C /**< Orientation status */ +/* keep as last */ +#define ADXL34X_REG_MAX 0x3C /**< The highest register address */ + +/* reg ACT_INACT_CTL */ +#define ADXL34X_REG_ACT_INACT_CTL_ACT_ACDC BIT(7) +#define ADXL34X_REG_ACT_INACT_CTL_ACT_X_ENABLE BIT(6) +#define ADXL34X_REG_ACT_INACT_CTL_ACT_Y_ENABLE BIT(5) +#define ADXL34X_REG_ACT_INACT_CTL_ACT_Z_ENABLE BIT(4) +#define ADXL34X_REG_ACT_INACT_CTL_INACT_ACDC BIT(3) +#define ADXL34X_REG_ACT_INACT_CTL_INACT_X_ENABLE BIT(2) +#define ADXL34X_REG_ACT_INACT_CTL_INACT_Y_ENABLE BIT(1) +#define ADXL34X_REG_ACT_INACT_CTL_INACT_Z_ENABLE BIT(0) + +/* reg TAP_AXES */ +#define ADXL34X_REG_TAP_AXES_IMPROVED_TAB BIT(4) +#define ADXL34X_REG_TAP_AXES_SUPPRESS BIT(3) +#define ADXL34X_REG_TAP_AXES_TAP_X_ENABLE BIT(2) +#define ADXL34X_REG_TAP_AXES_TAP_Y_ENABLE BIT(1) +#define ADXL34X_REG_TAP_AXES_TAP_Z_ENABLE BIT(0) + +/* reg ACT_TAP_STATUS */ +#define ADXL34X_REG_ACT_TAP_STATUS_ACT_X_SOURCE BIT(6) +#define ADXL34X_REG_ACT_TAP_STATUS_ACT_Y_SOURCE BIT(5) +#define ADXL34X_REG_ACT_TAP_STATUS_ACT_Z_SOURCE BIT(4) +#define ADXL34X_REG_ACT_TAP_STATUS_ASLEEP BIT(3) +#define ADXL34X_REG_ACT_TAP_STATUS_TAP_X_SOURCE BIT(2) +#define ADXL34X_REG_ACT_TAP_STATUS_TAP_Y_SOURCE BIT(1) +#define ADXL34X_REG_ACT_TAP_STATUS_TAP_Z_SOURCE BIT(0) + +/* reg BW_RATE */ +#define ADXL34X_REG_BW_RATE_LOW_POWER BIT(4) +#define ADXL34X_REG_BW_RATE_RATE GENMASK(3, 0) + +/* reg POWER_CTL */ +#define ADXL34X_REG_POWER_CTL_LINK BIT(5) +#define ADXL34X_REG_POWER_CTL_AUTO_SLEEP BIT(4) +#define ADXL34X_REG_POWER_CTL_MEASURE BIT(3) +#define ADXL34X_REG_POWER_CTL_SLEEP BIT(2) +#define ADXL34X_REG_POWER_CTL_WAKEUP GENMASK(1, 0) + +/* reg INT_ENABLE */ +#define ADXL34X_REG_INT_ENABLE_DATA_READY BIT(7) +#define ADXL34X_REG_INT_ENABLE_SINGLE_TAP BIT(6) +#define ADXL34X_REG_INT_ENABLE_DOUBLE_TAP BIT(5) +#define ADXL34X_REG_INT_ENABLE_ACTIVITY BIT(4) +#define ADXL34X_REG_INT_ENABLE_INACTIVITY BIT(3) +#define ADXL34X_REG_INT_ENABLE_FREE_FALL BIT(2) +#define ADXL34X_REG_INT_ENABLE_WATERMARK BIT(1) +#define ADXL34X_REG_INT_ENABLE_OVERRUN BIT(0) + +/* reg INT_MAP */ +#define ADXL34X_REG_INT_MAP_DATA_READY BIT(7) +#define ADXL34X_REG_INT_MAP_SINGLE_TAP BIT(6) +#define ADXL34X_REG_INT_MAP_DOUBLE_TAP BIT(5) +#define ADXL34X_REG_INT_MAP_ACTIVITY BIT(4) +#define ADXL34X_REG_INT_MAP_INACTIVITY BIT(3) +#define ADXL34X_REG_INT_MAP_FREE_FALL BIT(2) +#define ADXL34X_REG_INT_MAP_WATERMARK BIT(2) +#define ADXL34X_REG_INT_MAP_OVERRUN BIT(0) + +/* reg INT_SOURCE */ +#define ADXL34X_REG_INT_SOURCE_DATA_READY BIT(7) +#define ADXL34X_REG_INT_SOURCE_SINGLE_TAP BIT(6) +#define ADXL34X_REG_INT_SOURCE_DOUBLE_TAP BIT(5) +#define ADXL34X_REG_INT_SOURCE_ACTIVITY BIT(4) +#define ADXL34X_REG_INT_SOURCE_INACTIVITY BIT(3) +#define ADXL34X_REG_INT_SOURCE_FREE_FALL BIT(2) +#define ADXL34X_REG_INT_SOURCE_WATERMARK BIT(1) +#define ADXL34X_REG_INT_SOURCE_OVERRUN BIT(0) + +/* reg DATA_FORMAT */ +#define ADXL34X_REG_DATA_FORMAT_SELF_TEST BIT(7) +#define ADXL34X_REG_DATA_FORMAT_SPI BIT(6) +#define ADXL34X_REG_DATA_FORMAT_INT_INVERT BIT(5) +#define ADXL34X_REG_DATA_FORMAT_FULL_RES BIT(3) +#define ADXL34X_REG_DATA_FORMAT_JUSTIFY BIT(2) +#define ADXL34X_REG_DATA_FORMAT_RANGE GENMASK(1, 0) + +/* reg FIFO_CTL */ +#define ADXL34X_REG_FIFO_CTL_FIFO_MODE GENMASK(7, 6) +#define ADXL34X_REG_FIFO_CTL_TRIGGER BIT(5) +#define ADXL34X_REG_FIFO_CTL_SAMPLES GENMASK(4, 0) + +/* reg FIFO_STATUS */ +#define ADXL34X_REG_FIFO_STATUS_FIFO_TRIG BIT(7) +#define ADXL34X_REG_FIFO_STATUS_ENTRIES GENMASK(5, 0) + +/* reg TAP_SIGN */ +#define ADXL34X_REG_TAP_SIGN_XSIGN BIT(6) +#define ADXL34X_REG_TAP_SIGN_YSIGN BIT(5) +#define ADXL34X_REG_TAP_SIGN_ZSIGN BIT(4) +#define ADXL34X_REG_TAP_SIGN_XTAP BIT(2) +#define ADXL34X_REG_TAP_SIGN_YTAP BIT(1) +#define ADXL34X_REG_TAP_SIGN_ZTAP BIT(0) + +/* reg ORIENT_CONF */ +#define ADXL34X_REG_ORIENT_CONF_INT_ORIENT BIT(7) +#define ADXL34X_REG_ORIENT_CONF_DEAD_ZONE GENMASK(6, 4) +#define ADXL34X_REG_ORIENT_CONF_INT_3D BIT(3) +#define ADXL34X_REG_ORIENT_CONF_DIVISOR GENMASK(2, 0) + +/* reg ORIENT */ +#define ADXL34X_REG_ORIENT_V2 BIT(6) +#define ADXL34X_REG_ORIENT_2D_ORIENT GENMASK(5, 4) +#define ADXL34X_REG_ORIENT_V3 BIT(3) +#define ADXL34X_REG_ORIENT_3D_ORIENT GENMASK(2, 0) + +/* Various */ +#define ADXL343_DEVID 0xE5 /**< Device ID of the ADXL343 */ +#define ADXL344_DEVID 0xE6 /**< Device ID of the ADXL344 */ +#define ADXL345_DEVID 0xE5 /**< Device ID of the ADXL345 */ +#define ADXL346_DEVID 0xE6 /**< Device ID of the ADXL346 */ +#define ADXL34X_FIFO_SIZE 32 /**< Maximum number of x, y, z values in the FIFO */ + +#define ADXL34X_SPI_MSG_READ BIT(7) +#define ADXL34X_SPI_MULTI_BYTE BIT(6) + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_REG_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_rtio.c b/drivers/sensor/adi/adxl34x/adxl34x_rtio.c new file mode 100644 index 0000000000000..111586567e51f --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_rtio.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x_reg.h" +#include "adxl34x_private.h" +#include "adxl34x_rtio.h" +#include "adxl34x_decoder.h" +#include "adxl34x_trigger.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +/** + * Fetch a single sample from the sensor + * + * @param[in] dev Pointer to the sensor device + * @param[out] rx_buf Pointer to store the result + * @param[in] buf_size Size of the @p rx_buf buffer in bytes + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_rtio_sample_fetch(const struct device *dev, uint8_t *rx_buf, uint8_t buf_size) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc; + + /* Read accel x, y and z values. */ + rc = config->bus_read_buf(dev, ADXL34X_REG_DATA, rx_buf, buf_size); + if (rc) { + LOG_ERR("Failed to read from device"); + return rc; + } + return 0; +} + +/** + * Find the trigger (if any) configured in the sensor read configuration + * + * @param[in] cfg Read configuration of this driver instance + * @param[in] trig The trigger to lookup + * @return The trigger if found, NULL otherwise + */ +static struct sensor_stream_trigger * +adxl34x_get_stream_trigger(const struct sensor_read_config *cfg, enum sensor_trigger_type trig) +{ + for (unsigned int i = 0; i < cfg->count; ++i) { + if (cfg->triggers[i].trigger == trig) { + return &cfg->triggers[i]; + } + } + return NULL; +} + +/** + * Flush all sensor data when indicated by the trigger + * + * @param[in] dev Pointer to the sensor device + * @param[in] sensor_config Read configuration of this driver instance + * @param[in] interrupted Indicate if an (specific) interrupt was detected + * @param[in] trigger_type The type of trigger + * @return 1 if the sensor data was dropped, 0 otherwise + */ +static int adxl34x_drop_data_on_trigger(const struct device *dev, + const struct sensor_read_config *sensor_config, + bool interrupted, enum sensor_trigger_type trigger_type) +{ + if (!interrupted) { + return 0; + } + const struct sensor_stream_trigger *trigger = + adxl34x_get_stream_trigger(sensor_config, trigger_type); + if (trigger == NULL) { + return 0; + } + if (trigger->opt == SENSOR_STREAM_DATA_NOP || trigger->opt == SENSOR_STREAM_DATA_DROP) { + adxl34x_trigger_flush(dev); /* Clear the FIFO of the adxl34x. */ + return 1; + } + return 0; +} + +/** + * Submit a single packet to the RTIO stream + * + * @param[in] dev Pointer to the sensor device + * @param[in] int_source The source(s) of the interrupt + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_submit_packet(const struct device *dev, struct adxl34x_int_source int_source) +{ + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + const uint8_t nr_of_samples = cfg->fifo_ctl.samples; + struct rtio_iodev_sqe *iodev_sqe = data->iodev_sqe; + struct adxl34x_encoded_data *edata; + const uint32_t min_buf_len = sizeof(struct adxl34x_encoded_data) + + sizeof(edata->fifo_data) * (nr_of_samples - 1); + int rc; + uint8_t *buf; + uint32_t buf_len; + uint8_t offset = 0; + + /* Get the buffer for the frame, it may be allocated dynamically by the rtio context. */ + rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len); + if (rc != 0 || buf == NULL || buf_len < min_buf_len) { + LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len); + rtio_iodev_sqe_err(iodev_sqe, rc); + return -ENOBUFS; + } + + /* Prepare response. */ + edata = (struct adxl34x_encoded_data *)buf; + edata->header.entries = nr_of_samples; + edata->header.range = cfg->data_format.range; + edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); + edata->header.trigger = int_source; + + /* Readout FIFO (x, y and z) data. */ + for (int i = 0; i < nr_of_samples; i++) { + rc = adxl34x_rtio_sample_fetch(dev, edata->fifo_data + offset, + sizeof(edata->fifo_data)); + if (rc != 0) { + LOG_ERR("Failed to get sensor samples"); + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + offset += sizeof(edata->fifo_data); + } + + rtio_iodev_sqe_ok(iodev_sqe, nr_of_samples); + return 0; +} + +/** + * Handle both sensor data and trigger events + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_rtio_handle_motion_data(const struct device *dev) +{ + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + struct rtio_iodev_sqe *iodev_sqe = data->iodev_sqe; + int rc; + + if (iodev_sqe == NULL) { + LOG_WRN("Not submitting packet, stream not started"); + return -ENOSTR; + } + + const struct sensor_read_config *sensor_config = iodev_sqe->sqe.iodev->data; + + if (!sensor_config->is_streaming) { + LOG_ERR("Failed to setup stream correctly"); + return -ENOSTR; + } + + /* Read (and clear) any interrupts. */ + struct adxl34x_int_source int_source; + + rc = adxl34x_get_int_source(dev, &int_source); + ARG_UNUSED(rc); /* Satisfy the static code analysers. */ + __ASSERT_NO_MSG(rc == 0); + + /* Handle motion related events as well (only if triggers are registered). */ + adxl34x_handle_motion_events(dev, int_source); + + if (int_source.overrun) { + LOG_WRN("Lost accel samples, overrun detected"); + } + /* Drop the data from the FIFO when the configured trigger indicates to do so. */ + if (adxl34x_drop_data_on_trigger(dev, sensor_config, int_source.overrun, + SENSOR_TRIG_FIFO_FULL) == 1) { + return 0; + } + if (adxl34x_drop_data_on_trigger(dev, sensor_config, int_source.watermark, + SENSOR_TRIG_FIFO_WATERMARK) == 1) { + return 0; + } + + /* Check for spurious interrupts. */ + struct adxl34x_fifo_status fifo_status; + + rc = adxl34x_get_fifo_status(dev, &fifo_status); + ARG_UNUSED(rc); /* Satisfy the static code analysers. */ + __ASSERT_NO_MSG(rc == 0); + + /* Check if the FIFO has enough data to create a packet. */ + const uint8_t nr_of_samples = cfg->fifo_ctl.samples; + + if (fifo_status.entries < nr_of_samples) { + /* No (or not enough) samples to collect due to a spurious interrupt or motion + * event. + */ + return -ENODATA; + } + + /* Create and send (submit) packet to user. */ + rc = adxl34x_submit_packet(dev, int_source); + return rc; +} + +/** + * Start collecting streaming sensor data + * + * Streaming data is created when data ready interrupts arrive. This function only prepares the + * driver to receive these interrupts, and makes sure the submission queue is available when data + * arrives. + * + * @param[in] dev Pointer to the sensor device + * @param[out] iodev_sqe IO device submission queue entry + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ +#ifdef CONFIG_ADXL34X_TRIGGER + struct adxl34x_dev_data *data = dev->data; + int rc; + + /* We only 'setup' the stream once to start the submitting of packages based on + * interrupts. + */ + if (data->iodev_sqe != NULL) { + return 0; + } + data->iodev_sqe = iodev_sqe; + + /* Enable interrupts on both the MCU and ADXL34x side. */ + rc = adxl34x_trigger_init(dev); + if (rc != 0) { + LOG_ERR("Failed to enable the stream"); + return rc; + } + rc = adxl34x_trigger_reset(dev); + if (rc != 0) { + LOG_ERR("Failed to enable the stream"); + return rc; + } + return 0; +#else + ARG_UNUSED(dev); + ARG_UNUSED(iodev_sqe); + return -ENOTSUP; +#endif /* CONFIG_ADXL34X_TRIGGER */ +} + +/** + * Collect a single sample of data (x, y and z value) from the sensor + * + * @param[in] dev Pointer to the sensor device + * @param[out] iodev_sqe IO device submission queue entry + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct adxl34x_dev_data *data = dev->data; + const struct adxl34x_cfg *cfg = &data->cfg; + int rc; + + const struct sensor_read_config *sensor_config = iodev_sqe->sqe.iodev->data; + const struct sensor_chan_spec *const channels = sensor_config->channels; + const size_t num_channels = sensor_config->count; + const uint32_t min_buf_len = sizeof(struct adxl34x_encoded_data); + struct adxl34x_encoded_data *edata; + uint8_t *buf; + uint32_t buf_len; + + /* Get the buffer for the frame, it may be allocated dynamically by the rtio context. */ + rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len); + if (rc != 0) { + LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len); + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + + /* Determine what channels we need to fetch. */ + for (unsigned int i = 0; i < num_channels; i++) { + switch (channels[i].chan_type) { + case SENSOR_CHAN_ALL: + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + break; + default: + rc = -ENOTSUP; + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + } + + /* Prepare response. */ + edata = (struct adxl34x_encoded_data *)buf; + edata->header.entries = 1; + edata->header.range = cfg->data_format.range; + edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); + edata->header.trigger = (struct adxl34x_int_source){.data_ready = 1}; + + rc = adxl34x_rtio_sample_fetch(dev, edata->fifo_data, sizeof(edata->fifo_data)); + if (rc != 0) { + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + + rtio_iodev_sqe_ok(iodev_sqe, 0); + return 0; +} + +/** + * Collect a single sample or a stream of samples from the sensor + * + * @param[in] dev Pointer to the sensor device + * @param[out] iodev_sqe IO device submission queue entry + */ +void adxl34x_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + const struct sensor_read_config *sensor_config = iodev_sqe->sqe.iodev->data; + enum pm_device_state pm_state; + int rc; + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state != PM_DEVICE_STATE_ACTIVE) { + LOG_DBG("Device is suspended, sensor is unavailable"); + return; + } + + if (sensor_config->is_streaming) { + adxl34x_submit_stream(dev, iodev_sqe); + } else { + adxl34x_submit_one_shot(dev, iodev_sqe); + } +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_rtio.h b/drivers/sensor/adi/adxl34x/adxl34x_rtio.h new file mode 100644 index 0000000000000..9e1b521b9a32b --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_rtio.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_RTIO_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_RTIO_H_ + +#include +#include + +void adxl34x_submit(const struct device *dev, struct rtio_iodev_sqe *sqe); +int adxl34x_rtio_handle_motion_data(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_RTIO_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_spi.c b/drivers/sensor/adi/adxl34x/adxl34x_spi.c new file mode 100644 index 0000000000000..6136170c4dedd --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_spi.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_private.h" +#include "adxl34x_spi.h" +#include "adxl34x_reg.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +/** + * Initialise the SPI device + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_spi_init(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + + if (!spi_is_ready_dt(&config->spi)) { + LOG_ERR("Device not ready"); + return -ENODEV; + } + return 0; +} + +/** + * Function called when a write to the device is initiated + * + * @param[in] dev Pointer to the sensor device + * @param[in] reg_addr Address of the register to write to + * @param[in] reg_data Value to write + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_spi_write(const struct device *dev, uint8_t reg_addr, uint8_t reg_data) +{ + const struct adxl34x_dev_config *config = dev->config; + uint8_t address = reg_addr & ~ADXL34X_SPI_MSG_READ; + int rc; + + const struct spi_buf buf[2] = { + /* clang-format off */ + { + .buf = &address, + .len = 1, + }, + { + .buf = ®_data, + .len = 1, + } + /* clang-format on */ + }; + const struct spi_buf_set tx = { + .buffers = buf, + .count = 2, + }; + + rc = spi_write_dt(&config->spi, &tx); + return rc; +} + +/** + * Function called when a read of a single register from the device is initiated + * + * @param[in] dev Pointer to the sensor device + * @param[in] reg_addr Address of the register to read from + * @param[out] reg_data Pointer to store the value read + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_spi_read(const struct device *dev, uint8_t reg_addr, uint8_t *reg_data) +{ + int rc; + + rc = adxl34x_spi_read_buf(dev, reg_addr, reg_data, sizeof(*reg_data)); + return rc; +} + +/** + * Function called when a read of multiple registers from the device is initiated + * + * @param[in] dev Pointer to the sensor device + * @param[in] reg_addr Address of the register to read from + * @param[out] rx_buf Pointer to store the data read + * @param[in] size Size of the @p rx_buf buffer in bytes + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_spi_read_buf(const struct device *dev, uint8_t reg_addr, uint8_t *rx_buffer, + uint8_t size) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc; + uint8_t address = reg_addr | ADXL34X_SPI_MSG_READ; + + if (size > 1) { + address |= ADXL34X_SPI_MULTI_BYTE; + } + bzero(rx_buffer, size); + + const struct spi_buf buf[2] = { + /* clang-format off */ + { + .buf = &address, + .len = 1, + }, + { + .buf = rx_buffer, + .len = size, + } + /* clang-format on */ + }; + const struct spi_buf_set tx = { + .buffers = buf, + .count = 2, + }; + const struct spi_buf_set rx = { + .buffers = buf, + .count = 2, + }; + + rc = spi_transceive_dt(&config->spi, &tx, &rx); + return rc; +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_spi.h b/drivers/sensor/adi/adxl34x/adxl34x_spi.h new file mode 100644 index 0000000000000..a3fcf0a986477 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_spi.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_SPI_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_SPI_H_ + +#include + +#include +#include + +#define ADXL34X_SPI_CFG \ + (SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_TRANSFER_MSB) +#define ADXL34X_SPI_READ_BIT BIT(7) /**< Address value has a read bit */ + +#define ADXL34X_CONFIG_SPI(i) \ + .spi = SPI_DT_SPEC_INST_GET(i, ADXL34X_SPI_CFG, 0U), .bus_init = &adxl34x_spi_init, \ + .bus_write = &adxl34x_spi_write, .bus_read = &adxl34x_spi_read, \ + .bus_read_buf = &adxl34x_spi_read_buf + +int adxl34x_spi_init(const struct device *dev); +int adxl34x_spi_write(const struct device *dev, uint8_t reg_addr, uint8_t reg_data); +int adxl34x_spi_read(const struct device *dev, uint8_t reg_addr, uint8_t *reg_data); +int adxl34x_spi_read_buf(const struct device *dev, uint8_t reg_addr, uint8_t *rx_buf, uint8_t size); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_SPI_H_ */ diff --git a/drivers/sensor/adi/adxl34x/adxl34x_trigger.c b/drivers/sensor/adi/adxl34x/adxl34x_trigger.c new file mode 100644 index 0000000000000..1a57f0d20d123 --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_trigger.c @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x_private.h" +#include "adxl34x_reg.h" +#include "adxl34x_trigger.h" +#include "adxl34x_rtio.h" + +LOG_MODULE_DECLARE(adxl34x, CONFIG_SENSOR_LOG_LEVEL); + +#define GPIO_INT_TRIGGER GPIO_INT_EDGE_TO_ACTIVE + +/** + * Callback handler when an interrupt was detected + * + * @param[in] dev Pointer to the sensor device + * @param[in,out] cb Original GPIO callback structure owning this handler + * @param[in] pins Mask of pins that triggers the callback handler + * @note Called from ISR + */ +static void adxl34x_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pins); + struct adxl34x_dev_data *data = CONTAINER_OF(cb, struct adxl34x_dev_data, gpio_cb); + + if (data->work.handler != NULL) { + k_work_submit(&data->work); + } +} + +/** + * Handler used after an interrupt was detected when RTIO is not enabled/used + * + * @param[in,out] work Pointer to the work item + * @note Called from worker thread + */ +static void adxl34x_work_handler(struct k_work *work) +{ + struct adxl34x_dev_data *data = CONTAINER_OF(work, struct adxl34x_dev_data, work); + const struct device *dev = data->dev; + const struct adxl34x_dev_config *cfg = dev->config; + int rc; + enum pm_device_state pm_state; + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state != PM_DEVICE_STATE_ACTIVE) { + return; + } + + struct adxl34x_int_source int_source; + + rc = adxl34x_get_int_source(dev, &int_source); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + if ((data->data_ready_handler != NULL) && + (int_source.data_ready || int_source.watermark || int_source.overrun)) { + /* Keep reading samples until the interrupt de-asserts */ + while (gpio_pin_get_dt(&cfg->gpio_int1)) { + data->data_ready_handler(dev, data->data_ready_trigger); + } + } + adxl34x_handle_motion_events(dev, int_source); +} + +/** + * Handler used after an interrupt was detected when RTIO is enabled/used + * + * @param[in,out] work Pointer to the work item + * @note Called from worker thread + */ +static void adxl34x_rtio_work_handler(struct k_work *work) +{ + const struct adxl34x_dev_data *data = CONTAINER_OF(work, struct adxl34x_dev_data, work); + const struct device *dev = data->dev; + const struct adxl34x_dev_config *cfg = dev->config; + int rc; + enum pm_device_state pm_state; + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state != PM_DEVICE_STATE_ACTIVE) { + return; + } + + /* Keep reading samples from the FIFO until the interrupt de-asserts. */ + while (gpio_pin_get_dt(&cfg->gpio_int1)) { + rc = adxl34x_rtio_handle_motion_data(dev); + if (rc != 0) { + break; + } + } +} + +/** + * Handle any motion events detected + * + * @param[in] dev Pointer to the sensor device + * @param[in] int_source The source of the event + * @note Called from worker thread + */ +void adxl34x_handle_motion_events(const struct device *dev, struct adxl34x_int_source int_source) +{ + struct adxl34x_dev_data *data = dev->data; + + if (data->motion_event_handler != NULL && int_source.single_tap) { + data->motion_event_handler(dev, data->tap_trigger); + } + if (data->motion_event_handler != NULL && int_source.double_tap) { + data->motion_event_handler(dev, data->double_tap_trigger); + } + if (data->motion_event_handler != NULL && int_source.free_fall) { + data->motion_event_handler(dev, data->freefall_trigger); + } + if (data->motion_event_handler != NULL && int_source.activity) { + data->motion_event_handler(dev, data->motion_trigger); + } + if (data->motion_event_handler != NULL && int_source.inactivity) { + data->motion_event_handler(dev, data->stationary_trigger); + } +} + +/** + * Clear the FIFO of all data + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_trigger_flush(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + int rc = 0; + uint8_t rx_buf[6]; + + LOG_DBG("Flushing the FIFO"); + /* Read all data from the fifo and discard it */ + for (int i = 0; i < ADXL34X_FIFO_SIZE; i++) { + rc |= config->bus_read_buf(dev, ADXL34X_REG_DATA, rx_buf, sizeof(rx_buf)); + } + return rc; +} + +/** + * Setup the adxl34x to send interrupts when needed + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_trigger_enable_interrupt(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + int rc; + struct adxl34x_fifo_ctl fifo_ctl; + + if (IS_ENABLED(CONFIG_ADXL34X_FIFO_MODE_BYPASS)) { + fifo_ctl.fifo_mode = ADXL34X_FIFO_MODE_BYPASS; + } else if (IS_ENABLED(CONFIG_ADXL34X_FIFO_MODE_FIFO)) { + fifo_ctl.fifo_mode = ADXL34X_FIFO_MODE_FIFO; + } else if (IS_ENABLED(CONFIG_ADXL34X_FIFO_MODE_STREAM)) { + fifo_ctl.fifo_mode = ADXL34X_FIFO_MODE_STREAM; + } else if (IS_ENABLED(CONFIG_ADXL34X_FIFO_MODE_TRIGGER)) { + fifo_ctl.fifo_mode = ADXL34X_FIFO_MODE_TRIGGER; + } else { + LOG_ERR("Unsupported FIFO mode (see CONFIG_ADXL34X_FIFO_MODE...)"); + } + + const uint8_t int_pin = config->dt_int_pin - 1; + + fifo_ctl.trigger = int_pin; + fifo_ctl.samples = config->dt_packet_size; + rc = adxl34x_set_fifo_ctl(dev, &fifo_ctl); + if (rc != 0) { + LOG_ERR("Failed to enable fifo mode"); + return rc; + } + + struct adxl34x_data_format data_format = data->cfg.data_format; + + data_format.int_invert = (uint8_t)(config->gpio_int1.dt_flags | GPIO_ACTIVE_LOW); + rc = adxl34x_set_data_format(dev, &data_format); + if (rc) { + LOG_ERR("Failed to set interrupt level on device (%s)", dev->name); + return rc; + } + + /* Use INT1 or INT2 to trigger each interrupt */ + struct adxl34x_int_map int_map = { + .data_ready = int_pin, + .watermark = int_pin, + .overrun = int_pin, + .single_tap = int_pin, + .double_tap = int_pin, + .free_fall = int_pin, + .activity = int_pin, + .inactivity = int_pin, + }; + rc = adxl34x_set_int_map(dev, &int_map); + if (rc != 0) { + LOG_ERR("Failed to enable trigger interrupt"); + return rc; + } + + struct adxl34x_int_enable int_enable = {0}; + + if (data->iodev_sqe != NULL) { + /* Enable the FIFO interrupts when in streaming mode */ + int_enable.watermark = true; + int_enable.overrun = true; + } else if (data->data_ready_handler != NULL) { + /* When NOT in streaming mode */ + int_enable.data_ready = true; + int_enable.watermark = true; + int_enable.overrun = true; + } + if (data->motion_event_handler != NULL) { + if (data->tap_trigger) { + int_enable.single_tap = true; + } + if (data->double_tap_trigger) { + int_enable.double_tap = true; + } + if (data->freefall_trigger) { + int_enable.free_fall = true; + } + if (data->motion_trigger) { + int_enable.activity = true; + } + if (data->stationary_trigger) { + int_enable.inactivity = true; + } + } + rc = adxl34x_set_int_enable(dev, &int_enable); + if (rc != 0) { + LOG_ERR("Failed to enable trigger interrupt"); + return rc; + } + return 0; +} + +/** + * Suspend the adxl34x from collecting data and sending interrupts + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_suspend(const struct device *dev) +{ + const struct adxl34x_dev_config *cfg = dev->config; + struct adxl34x_dev_data *data = dev->data; + int rc; + + rc = gpio_pin_interrupt_configure_dt(&cfg->gpio_int1, GPIO_INT_DISABLE); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Disable the interrupts on the adxl34x */ + struct adxl34x_int_enable int_enable = {0}; + + rc = adxl34x_set_int_enable(dev, &int_enable); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Stop the adxl34x from sampling */ + struct adxl34x_power_ctl power_ctl = data->cfg.power_ctl; + + power_ctl.measure = 0; + rc = adxl34x_set_power_ctl(dev, &power_ctl); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Clear the FIFO of the adxl34x */ + rc = adxl34x_trigger_flush(dev); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + return rc; +} + +/** + * Resume normal operation of the adxl34x, continue data collection and send interrupts + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +static int adxl34x_resume(const struct device *dev) +{ + const struct adxl34x_dev_config *cfg = dev->config; + struct adxl34x_dev_data *data = dev->data; + int rc; + + rc = gpio_pin_interrupt_configure_dt(&cfg->gpio_int1, GPIO_INT_TRIGGER); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Re-configure and enable the interrupts of the adxl34x */ + rc = adxl34x_trigger_enable_interrupt(dev); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Start the adxl34x, enable sampling data */ + struct adxl34x_power_ctl power_ctl = data->cfg.power_ctl; + + power_ctl.measure = 1; + rc = adxl34x_set_power_ctl(dev, &power_ctl); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + return rc; +} + +/** + * Reset the adxl34x data interrupt to make sure it's de-asserted + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_trigger_reset(const struct device *dev) +{ + struct adxl34x_dev_data *data = dev->data; + int rc; + + /* Clear the FIFO of the adxl34x */ + rc = adxl34x_trigger_flush(dev); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Re-configure and enable the interrupts of the adxl34x */ + rc = adxl34x_trigger_enable_interrupt(dev); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + + /* Start the adxl34x, enable sampling data */ + struct adxl34x_power_ctl power_ctl = data->cfg.power_ctl; + + power_ctl.measure = 1; + rc = adxl34x_set_power_ctl(dev, &power_ctl); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + return rc; +} + +/** + * Callback API for setting a sensor's trigger and handler + * + * Prepare the MCU and adxl34x for receiving sensor interrupts + * + * @param[in] dev Pointer to the sensor device + * @param[in] trig The trigger to activate + * @param[in] handler The function that should be called when the trigger + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct adxl34x_dev_data *data = dev->data; + enum pm_device_state pm_state; + int rc; + + if (trig == NULL || handler == NULL) { + return -EINVAL; + } + + rc = pm_device_state_get(dev, &pm_state); + if (rc == 0 && pm_state != PM_DEVICE_STATE_ACTIVE) { + return -EIO; + } + + adxl34x_suspend(dev); + + switch (trig->type) { + case SENSOR_TRIG_DATA_READY: /* New data is ready */ + case SENSOR_TRIG_FIFO_WATERMARK: /* The FIFO watermark has been reached */ + case SENSOR_TRIG_FIFO_FULL: /* The FIFO becomes full */ + data->data_ready_handler = handler; + data->data_ready_trigger = trig; + break; + case SENSOR_TRIG_TAP: /* A single tap is detected. */ + data->motion_event_handler = handler; + data->tap_trigger = trig; + break; + case SENSOR_TRIG_DOUBLE_TAP: /* A double tap is detected. */ + data->motion_event_handler = handler; + data->double_tap_trigger = trig; + break; + case SENSOR_TRIG_FREEFALL: /* A free fall is detected. */ + data->motion_event_handler = handler; + data->freefall_trigger = trig; + break; + case SENSOR_TRIG_MOTION: /* Motion is detected. */ + data->motion_event_handler = handler; + data->motion_trigger = trig; + break; + case SENSOR_TRIG_STATIONARY: /* No motion has been detected for a while. */ + data->motion_event_handler = handler; + data->stationary_trigger = trig; + break; + case SENSOR_TRIG_TIMER: + case SENSOR_TRIG_DELTA: + case SENSOR_TRIG_NEAR_FAR: + case SENSOR_TRIG_THRESHOLD: + case SENSOR_TRIG_COMMON_COUNT: + case SENSOR_TRIG_MAX: + default: + return -ENOTSUP; + } + + adxl34x_resume(dev); + return 0; +} + +/** + * Setup this driver so it can support triggers + * + * @param[in] dev Pointer to the sensor device + * @return 0 if successful, negative errno code if failure. + */ +int adxl34x_trigger_init(const struct device *dev) +{ + const struct adxl34x_dev_config *config = dev->config; + struct adxl34x_dev_data *data = dev->data; + int rc = 0; + + if (data->work.handler == NULL) { + if (!config->gpio_int1.port) { + LOG_ERR("trigger enabled but no interrupt gpio supplied"); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&config->gpio_int1)) { + LOG_ERR("gpio_int1 not ready"); + return -ENODEV; + } + + /* Prepare the pin to receive interrupts */ + rc = gpio_pin_configure_dt(&config->gpio_int1, GPIO_INPUT); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + gpio_init_callback(&data->gpio_cb, adxl34x_gpio_callback, + BIT(config->gpio_int1.pin)); + rc = gpio_add_callback(config->gpio_int1.port, &data->gpio_cb); + if (rc != 0) { + LOG_ERR("Failed to set gpio callback"); + return rc; + } + } + + data->dev = dev; + /* Prepare to handle interrupt callback(s). Register the streaming handler when and rtio-sqe + * instance is available, otherwize register the normal handler. When the trigger is + * initialized twice the streaming handler always takes preference. + */ + if (data->iodev_sqe != NULL) { + data->work.handler = adxl34x_rtio_work_handler; + } else if (data->work.handler == NULL) { + data->work.handler = adxl34x_work_handler; + } + + /* Finally enable the interrupt it self */ + rc = gpio_pin_interrupt_configure_dt(&config->gpio_int1, GPIO_INT_TRIGGER); + ARG_UNUSED(rc); + __ASSERT_NO_MSG(rc == 0); + return 0; +} diff --git a/drivers/sensor/adi/adxl34x/adxl34x_trigger.h b/drivers/sensor/adi/adxl34x/adxl34x_trigger.h new file mode 100644 index 0000000000000..549d90a06190b --- /dev/null +++ b/drivers/sensor/adi/adxl34x/adxl34x_trigger.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_ADXL34X_TRIGGER_H_ +#define ZEPHYR_DRIVERS_SENSOR_ADXL34X_TRIGGER_H_ + +#include + +#include +#include +#include + +int adxl34x_trigger_init(const struct device *dev); +int adxl34x_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); +int adxl34x_trigger_reset(const struct device *dev); +int adxl34x_trigger_flush(const struct device *dev); +void adxl34x_handle_motion_events(const struct device *dev, struct adxl34x_int_source int_source); + +#endif /* ZEPHYR_DRIVERS_SENSOR_ADXL34X_TRIGGER_H_ */ diff --git a/dts/bindings/sensor/adi,adxl34x-common.yaml b/dts/bindings/sensor/adi,adxl34x-common.yaml new file mode 100644 index 0000000000000..7a6c5082bf2fe --- /dev/null +++ b/dts/bindings/sensor/adi,adxl34x-common.yaml @@ -0,0 +1,65 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +include: sensor-device.yaml + +properties: + int-gpios: + type: phandle-array + description: | + Interrupt GPIO to support FIFO mode and other interrupts. + + int-pin: + type: int + default: 1 + enum: + - 1 # interrupt is generated from INT1 + - 2 # interrupt is generated from INT2 + description: | + Select the interrupt pin in use when data is available, or an event + occurred (1 or 2). The ADXL34x has two interrupt pins of which only one is + needed for this driver. The other interrupt can be used to control other + hardware directly. Defaults to 1 (INT1), the power-on reset value. + + packet-size: + type: int + default: 16 + description: | + The number of accelerometer samples in one packet when streaming data. The + size ranges from 1 to 31 samples and defaults to halve the FIFO size. + + accel-frequency: + type: string + default: "100" + description: | + Default frequency of accelerometer. Power-on reset value is 100 Hz. + enum: + - "0.10" + - "0.20" + - "0.39" + - "0.78" + - "1.56" + - "3.13" + - "6.25" + - "12.5" + - "25" + - "50" + - "100" + - "200" + - "400" + - "800" + - "1600" + - "3200" + + accel-range: + type: int + default: 16 + description: | + Default range of accelerometer. Power-on reset value is +-16 g. + enum: + - 2 + - 4 + - 8 + - 16 diff --git a/dts/bindings/sensor/adi,adxl34x-i2c.yaml b/dts/bindings/sensor/adi,adxl34x-i2c.yaml new file mode 100644 index 0000000000000..1951425c7fecd --- /dev/null +++ b/dts/bindings/sensor/adi,adxl34x-i2c.yaml @@ -0,0 +1,24 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + ADXL34x accelerometer sensors of ADI. Supported are the ADXL343, ADXL344, + ADXL345 and ADXL346 Three Axis I2C accelerometer. + + Example definition in devicetree: + + accel: adxl34x@12 { + compatible = "adi,adxl34x"; + reg = <0x12>; + int-gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "800"; + accel-range = 16; + }; + +compatible: "adi,adxl34x" + +include: ["sensor-device.yaml", "i2c-device.yaml", "adi,adxl34x-common.yaml"] diff --git a/dts/bindings/sensor/adi,adxl34x-spi.yaml b/dts/bindings/sensor/adi,adxl34x-spi.yaml new file mode 100644 index 0000000000000..9112a351d1c30 --- /dev/null +++ b/dts/bindings/sensor/adi,adxl34x-spi.yaml @@ -0,0 +1,23 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + ADXL34x accelerometer sensors of ADI. Supported are the ADXL343, ADXL344, + ADXL345 and ADXL346 Three Axis SPI accelerometer. + + Example definition in devicetree: + + adxl34x { + compatible = "adi,adxl34x"; + int-gpios = <&gpio0 0 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "800"; + accel-range = 16; + }; + +compatible: "adi,adxl34x" + +include: ["sensor-device.yaml", "spi-device.yaml", "adi,adxl34x-common.yaml"] diff --git a/include/zephyr/drivers/sensor/adxl34x.h b/include/zephyr/drivers/sensor/adxl34x.h new file mode 100644 index 0000000000000..beb7f36877413 --- /dev/null +++ b/include/zephyr/drivers/sensor/adxl34x.h @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Extended public API for ADI's ADXL34x motion sensor + * + * This exposes additional attributes for the ADXL34x. + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_ADXL34X_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_ADXL34X_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Accelerometer range options + */ +enum adxl34x_accel_freq { + ADXL34X_ACCEL_FREQ_0_10, + ADXL34X_ACCEL_FREQ_0_20, + ADXL34X_ACCEL_FREQ_0_39, + ADXL34X_ACCEL_FREQ_0_78, + ADXL34X_ACCEL_FREQ_1_56, + ADXL34X_ACCEL_FREQ_3_13, + ADXL34X_ACCEL_FREQ_6_25, + ADXL34X_ACCEL_FREQ_12_5, + ADXL34X_ACCEL_FREQ_25, + ADXL34X_ACCEL_FREQ_50, + ADXL34X_ACCEL_FREQ_100, + ADXL34X_ACCEL_FREQ_200, + ADXL34X_ACCEL_FREQ_400, + ADXL34X_ACCEL_FREQ_800, + ADXL34X_ACCEL_FREQ_1600, + ADXL34X_ACCEL_FREQ_3200, +}; + +/** + * @brief Accelerometer range options + */ +enum adxl34x_accel_range { + ADXL34X_RANGE_2G, + ADXL34X_RANGE_4G, + ADXL34X_RANGE_8G, + ADXL34X_RANGE_16G, +}; + +/** + * @brief FIFO mode + */ +enum adxl34x_fifo_mode { + ADXL34X_FIFO_MODE_BYPASS, + ADXL34X_FIFO_MODE_FIFO, + ADXL34X_FIFO_MODE_STREAM, + ADXL34X_FIFO_MODE_TRIGGER, +}; + +/** + * @brief Frequency when sleeping + */ +enum adxl34x_sleep_freq { + ADXL34X_SLEEP_FREQ_8_HZ, + ADXL34X_SLEEP_FREQ_4_HZ, + ADXL34X_SLEEP_FREQ_2_HZ, + ADXL34X_SLEEP_FREQ_1_HZ, +}; + +/** + * @brief Dead zone angle + */ +enum adxl34x_dead_zone_angle { + ADXL34X_DEAD_ZONE_ANGLE_5_1, + ADXL34X_DEAD_ZONE_ANGLE_10_2, + ADXL34X_DEAD_ZONE_ANGLE_15_2, + ADXL34X_DEAD_ZONE_ANGLE_20_4, + ADXL34X_DEAD_ZONE_ANGLE_25_5, + ADXL34X_DEAD_ZONE_ANGLE_30_8, + ADXL34X_DEAD_ZONE_ANGLE_36_1, + ADXL34X_DEAD_ZONE_ANGLE_41_4, +}; + +/** + * @brief Divisor Bandwidth (Hz) + */ +enum adxl34x_divisor { + ADXL34X_DIVISOR_ODR_9, + ADXL34X_DIVISOR_ODR_22, + ADXL34X_DIVISOR_ODR_50, + ADXL34X_DIVISOR_ODR_100, + ADXL34X_DIVISOR_ODR_200, + ADXL34X_DIVISOR_ODR_400, + ADXL34X_DIVISOR_ODR_800, + ADXL34X_DIVISOR_ODR_1600, +}; + +/** + * @brief Orientation Codes 2D + */ +enum adxl34x_orient_2d { + ADXL34X_ORIENT_2D_POS_X, + ADXL34X_ORIENT_2D_NEG_X, + ADXL34X_ORIENT_2D_POS_Y, + ADXL34X_ORIENT_2D_NEG_Y, +}; + +/** + * @brief Orientation Codes 3D + */ +enum adxl34x_orient_3d { + ADXL34X_ORIENT_3D_POS_X, + ADXL34X_ORIENT_3D_NEG_X, + ADXL34X_ORIENT_3D_POS_Y, + ADXL34X_ORIENT_3D_NEG_Y, + ADXL34X_ORIENT_3D_POS_Z, + ADXL34X_ORIENT_3D_NEG_Z, +}; + +/** + * @brief Axis control for activity and inactivity detection + * @struct adxl34x_act_inact_ctl + */ +struct adxl34x_act_inact_ctl { + uint8_t act_acdc: 1; /**< Enable AC coupling for activity detection */ + uint8_t act_x_enable: 1; /**< Enable acitivy detection on X axis */ + uint8_t act_y_enable: 1; /**< Enable acitivy detection on Y axis */ + uint8_t act_z_enable: 1; /**< Enable acitivy detection on Z axis */ + uint8_t inact_acdc: 1; /**< Enable AC coupling for inactivity detection */ + uint8_t inact_x_enable: 1; /**< Enable acitivy indetection on X axis */ + uint8_t inact_y_enable: 1; /**< Enable acitivy indetection on Y axis */ + uint8_t inact_z_enable: 1; /**< Enable acitivy indetection on Z axis */ +} __attribute__((__packed__)); + +/** + * @brief Axis control for single tap/double tap + * @struct adxl34x_tap_axes + */ +struct adxl34x_tap_axes { + uint8_t improved_tab: 1; /**< Enable improved tap detection (only adxl344 and adxl346) */ + uint8_t suppress: 1; /**< Suppresses double-tap detection */ + uint8_t tap_x_enable: 1; /**< Enable tab detection on X axis */ + uint8_t tap_y_enable: 1; /**< Enable tab detection on Y axis */ + uint8_t tap_z_enable: 1; /**< Enable tab detection on Z axis */ +} __attribute__((__packed__)); + +/** + * @brief Source of single tap/double tap + * @struct adxl34x_act_tap_status + */ +struct adxl34x_act_tap_status { + uint8_t act_x_source: 1; /**< Indicate the activity event was detected on X axis */ + uint8_t act_y_source: 1; /**< Indicate the activity event was detected on Y axis */ + uint8_t act_z_source: 1; /**< Indicate the activity event was detected on Z axis */ + uint8_t asleep: 1; /**< Indicate if the device is sleeping */ + uint8_t tap_x_source: 1; /**< Indicate the tab event was detected on X axis */ + uint8_t tap_y_source: 1; /**< Indicate the tab event was detected on X axis */ + uint8_t tap_z_source: 1; /**< Indicate the tab event was detected on X axis */ +} __attribute__((__packed__)); + +/** + * @brief Data rate and power mode control + * @struct adxl34x_bw_rate + */ +struct adxl34x_bw_rate { + uint8_t low_power: 1; /**< Enable reduced power operation */ + enum adxl34x_accel_freq rate: 4; /**< Bit/sample rate */ +} __attribute__((__packed__)); + +/** + * @brief Power-saving features control + * @struct adxl34x_power_ctl + */ +struct adxl34x_power_ctl { + uint8_t link: 1; /**< Link activity with inactivity detection */ + uint8_t auto_sleep: 1; /**< Enable autosleep */ + uint8_t measure: 1; /**< Enable measurements (data sampling) */ + uint8_t sleep: 1; /**< Enable sleep mode */ + enum adxl34x_sleep_freq wakeup: 2; /**< Bit/sample rate used when sleeping */ +} __attribute__((__packed__)); + +/** + * @brief Interrupt enable control + * @struct adxl34x_int_enable + */ +struct adxl34x_int_enable { + uint8_t data_ready: 1; /**< Enable data ready event interrupt */ + uint8_t single_tap: 1; /**< Enable single tap event interrupt */ + uint8_t double_tap: 1; /**< Enable double tap event interrupt */ + uint8_t activity: 1; /**< Enable activity event interrupt */ + uint8_t inactivity: 1; /**< Enable inactivity event interrupt */ + uint8_t free_fall: 1; /**< Enable free fall event interrupt */ + uint8_t watermark: 1; /**< Enable watermark event interrupt */ + uint8_t overrun: 1; /**< Enable overrun event interrupt */ +} __attribute__((__packed__)); + +/** + * @brief Interrupt mapping control + * @struct adxl34x_int_map + */ +struct adxl34x_int_map { + uint8_t data_ready: 1; /**< Use pin INT2 on data ready event */ + uint8_t single_tap: 1; /**< Use pin INT2 on single tap event */ + uint8_t double_tap: 1; /**< Use pin INT2 on double tap event */ + uint8_t activity: 1; /**< Use pin INT2 on activity event */ + uint8_t inactivity: 1; /**< Use pin INT2 on inactivity event */ + uint8_t free_fall: 1; /**< Use pin INT2 on free fall event */ + uint8_t watermark: 1; /**< Use pin INT2 on watermark event */ + uint8_t overrun: 1; /**< Use pin INT2 on overrun event */ +} __attribute__((__packed__)); + +/** + * @brief Source of interrupts + * @struct adxl34x_int_source + */ +struct adxl34x_int_source { + uint8_t data_ready: 1; /**< A data ready event occurred */ + uint8_t single_tap: 1; /**< A single tap event occurred */ + uint8_t double_tap: 1; /**< A double tap event occurred */ + uint8_t activity: 1; /**< A activity event occurred */ + uint8_t inactivity: 1; /**< A inactivity event occurred */ + uint8_t free_fall: 1; /**< A free fall event occurred */ + uint8_t watermark: 1; /**< A watermark event occurred */ + uint8_t overrun: 1; /**< A overrun event occurred */ +} __attribute__((__packed__)); + +/** + * @brief Data format control + * @struct adxl34x_data_format + */ +struct adxl34x_data_format { + uint8_t self_test: 1; /**< Enable self-test force to sensor */ + uint8_t spi: 1; /**< Enable spi 3 wire mode, not 4 wire mode */ + uint8_t int_invert: 1; /**< Enable active low interrupts */ + uint8_t full_res: 1; /**< Enable full resolution mode */ + uint8_t justify: 1; /**< Left justify data (MSB mode), not right justify */ + enum adxl34x_accel_range range: 2; /**< G range of sensor */ +} __attribute__((__packed__)); + +/** + * @brief FIFO control + * @struct adxl34x_fifo_ctl + */ +struct adxl34x_fifo_ctl { + enum adxl34x_fifo_mode fifo_mode: 2; /**< FIFO mode */ + uint8_t trigger: 1; /**< Use pin INT2 for trigger events */ + uint8_t samples: 5; /**< FIFO watermark level for interrupts */ +} __attribute__((__packed__)); + +/** + * @brief FIFO status + * @struct adxl34x_fifo_status + */ +struct adxl34x_fifo_status { + uint8_t fifo_trig: 1; /**< Indicate a FIFO watermark event occurred */ + uint8_t entries: 6; /**< Nr of samples currently in the FIFO */ +} __attribute__((__packed__)); + +/** + * @brief Sign and source for single tap/double tap + * @struct adxl34x_tap_sign + */ +struct adxl34x_tap_sign { + uint8_t xsign: 1; /**< Initial direction (pos or neg) of the tap detected in X axis */ + uint8_t ysign: 1; /**< Initial direction (pos or neg) of the tap detected in Y axis */ + uint8_t zsign: 1; /**< Initial direction (pos or neg) of the tap detected in Z axis */ + uint8_t xtap: 1; /**< Indicate the X axis was involved first in the tab event */ + uint8_t ytap: 1; /**< Indicate the Y axis was involved first in the tab event */ + uint8_t ztap: 1; /**< Indicate the Z axis was involved first in the tab event */ +} __attribute__((__packed__)); + +/** + * @brief Orientation configuration + * @struct adxl34x_orient_conf + */ +struct adxl34x_orient_conf { + uint8_t int_orient: 1; /**< Enable the orientation interrupt */ + enum adxl34x_dead_zone_angle + dead_zone: 3; /**< The region between two adjacent orientations */ + uint8_t int_3d: 1; /**< Enable 3D orientation detection, not 2D */ + enum adxl34x_divisor divisor: 3; /**< Low pass filter */ +} __attribute__((__packed__)); + +/** + * @brief @brief Orientation status + * @struct adxl34x_orient + */ +struct adxl34x_orient { + uint8_t v2: 1; /**< Indicate a valid 2D orientation */ + enum adxl34x_orient_2d + orient_2d: 2; /**< Indicate the 2D direction of the axis when event occurred */ + uint8_t v3: 1; /**< Indicate a valid 3D orientation */ + enum adxl34x_orient_3d + orient_3d: 3; /**< Indicate the 3D direction of the axis when event occurred */ +} __attribute__((__packed__)); + +/** + * @brief Registry mapping of the adxl34x + * @struct adxl34x_cfg + */ +struct adxl34x_cfg { + uint8_t devid; /**< Device ID */ +#ifdef CONFIG_ADXL34X_EXTENDED_API + uint8_t thresh_tap; /**< Tap threshold */ +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + int8_t ofsx; /**< X-axis offset */ + int8_t ofsy; /**< Y-axis offset */ + int8_t ofsz; /**< Z-axis offset */ +#ifdef CONFIG_ADXL34X_EXTENDED_API + uint8_t dur; /**< Tap duration */ + uint8_t latent; /**< Tap latency */ + uint8_t window; /**< Tap window */ + uint8_t thresh_act; /**< Activity threshold */ + uint8_t thresh_inact; /**< Inactivity threshold */ + uint8_t time_inact; /**< Inactivity time */ + struct adxl34x_act_inact_ctl + act_inact_ctl; /**< Axis control for activity and inactivity detection */ + uint8_t thresh_ff; /**< Free-fall threshold */ + uint8_t time_ff; /**< Free-fall time */ + struct adxl34x_tap_axes tap_axes; /**< Axis control for single tap/double tap */ +#endif /* CONFIG_ADXL34X_EXTENDED_API */ + struct adxl34x_bw_rate bw_rate; /**< Data rate and power mode control */ + struct adxl34x_power_ctl power_ctl; /**< Power-saving features control */ + struct adxl34x_int_enable int_enable; /**< Interrupt enable control */ + struct adxl34x_int_map int_map; /**< Interrupt mapping control */ + struct adxl34x_data_format data_format; /**< Data format control */ + struct adxl34x_fifo_ctl fifo_ctl; /**< FIFO control */ +#ifdef CONFIG_ADXL34X_EXTENDED_API + struct adxl34x_orient_conf orient_conf; /**< Orientation configuration */ +#endif /* CONFIG_ADXL34X_EXTENDED_API */ +}; + +/* Read/write registers */ +int adxl34x_get_thresh_tap(const struct device *dev, uint8_t *thresh_tap, bool use_cache); +int adxl34x_set_thresh_tap(const struct device *dev, uint8_t thresh_tap); +int adxl34x_get_ofsx(const struct device *dev, int8_t *ofsx, bool use_cache); +int adxl34x_set_ofsx(const struct device *dev, int8_t ofsx); +int adxl34x_get_ofsy(const struct device *dev, int8_t *ofsy, bool use_cache); +int adxl34x_set_ofsy(const struct device *dev, int8_t ofsy); +int adxl34x_get_ofsz(const struct device *dev, int8_t *ofsz, bool use_cache); +int adxl34x_set_ofsz(const struct device *dev, int8_t ofsz); +int adxl34x_get_dur(const struct device *dev, uint8_t *dur, bool use_cache); +int adxl34x_set_dur(const struct device *dev, uint8_t dur); +int adxl34x_get_latent(const struct device *dev, uint8_t *latent, bool use_cache); +int adxl34x_set_latent(const struct device *dev, uint8_t latent); +int adxl34x_get_window(const struct device *dev, uint8_t *window, bool use_cache); +int adxl34x_set_window(const struct device *dev, uint8_t window); +int adxl34x_get_thresh_act(const struct device *dev, uint8_t *thresh_act, bool use_cache); +int adxl34x_set_thresh_act(const struct device *dev, uint8_t thresh_act); +int adxl34x_get_thresh_inact(const struct device *dev, uint8_t *thresh_inact, bool use_cache); +int adxl34x_set_thresh_inact(const struct device *dev, uint8_t thresh_inact); +int adxl34x_get_time_inact(const struct device *dev, uint8_t *time_inact, bool use_cache); +int adxl34x_set_time_inact(const struct device *dev, uint8_t time_inact); +int adxl34x_get_act_inact_ctl(const struct device *dev, struct adxl34x_act_inact_ctl *act_inact_ctl, + bool use_cache); +int adxl34x_set_act_inact_ctl(const struct device *dev, + struct adxl34x_act_inact_ctl *act_inact_ctl); +int adxl34x_get_thresh_ff(const struct device *dev, uint8_t *thresh_ff, bool use_cache); +int adxl34x_set_thresh_ff(const struct device *dev, uint8_t thresh_ff); +int adxl34x_get_time_ff(const struct device *dev, uint8_t *time_ff, bool use_cache); +int adxl34x_set_time_ff(const struct device *dev, uint8_t time_ff); +int adxl34x_get_tap_axes(const struct device *dev, struct adxl34x_tap_axes *tap_axes, + bool use_cache); +int adxl34x_set_tap_axes(const struct device *dev, struct adxl34x_tap_axes *tap_axes); +int adxl34x_get_bw_rate(const struct device *dev, struct adxl34x_bw_rate *bw_rate, bool use_cache); +int adxl34x_set_bw_rate(const struct device *dev, struct adxl34x_bw_rate *bw_rate); +int adxl34x_get_power_ctl(const struct device *dev, struct adxl34x_power_ctl *power_ctl, + bool use_cache); +int adxl34x_set_power_ctl(const struct device *dev, struct adxl34x_power_ctl *power_ctl); +int adxl34x_get_int_enable(const struct device *dev, struct adxl34x_int_enable *int_enable, + bool use_cache); +int adxl34x_set_int_enable(const struct device *dev, struct adxl34x_int_enable *int_enable); +int adxl34x_get_int_map(const struct device *dev, struct adxl34x_int_map *int_map, bool use_cache); +int adxl34x_set_int_map(const struct device *dev, struct adxl34x_int_map *int_map); +int adxl34x_get_data_format(const struct device *dev, struct adxl34x_data_format *data_format, + bool use_cache); +int adxl34x_set_data_format(const struct device *dev, struct adxl34x_data_format *data_format); +int adxl34x_get_fifo_ctl(const struct device *dev, struct adxl34x_fifo_ctl *fifo_ctl, + bool use_cache); +int adxl34x_set_fifo_ctl(const struct device *dev, struct adxl34x_fifo_ctl *fifo_ctl); +int adxl34x_get_orient_conf(const struct device *dev, struct adxl34x_orient_conf *orient_conf, + bool use_cache); +int adxl34x_set_orient_conf(const struct device *dev, struct adxl34x_orient_conf *orient_conf); + +/* Read only registers */ +int adxl34x_get_devid(const struct device *dev, uint8_t *devid); +int adxl34x_get_act_tap_status(const struct device *dev, + struct adxl34x_act_tap_status *act_tap_status); +int adxl34x_get_int_source(const struct device *dev, struct adxl34x_int_source *int_source); +int adxl34x_get_fifo_status(const struct device *dev, struct adxl34x_fifo_status *fifo_status); +int adxl34x_get_tap_sign(const struct device *dev, struct adxl34x_tap_sign *tap_sign); +int adxl34x_get_orient(const struct device *dev, struct adxl34x_orient *orient); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_ADXL34X_H_ */ diff --git a/tests/drivers/build_all/sensor/app.overlay b/tests/drivers/build_all/sensor/app.overlay index ebfa224848a32..0b1a225300ed1 100644 --- a/tests/drivers/build_all/sensor/app.overlay +++ b/tests/drivers/build_all/sensor/app.overlay @@ -138,7 +138,8 @@ <&test_gpio 0 0>, <&test_gpio 0 0>, <&test_gpio 0 0>, - <&test_gpio 0 0>; /* 0x2e */ + <&test_gpio 0 0>, + <&test_gpio 0 0>; /* 0x2f */ #include "spi.dtsi" }; diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 8bf865c14f21c..ceb76f6ce982d 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1090,3 +1090,14 @@ test_i2c_fxls8974: fxls8974@93 { int1-gpios = <&test_gpio 0 0>; int2-gpios = <&test_gpio 0 0>; }; + +test_i2c_adxl34x: adxl34x@94 { + compatible = "adi,adxl34x"; + reg = <0x94>; + int-gpios = <&test_gpio 0 0>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "25"; + accel-range = <16>; + status = "okay"; +}; diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index c173c5ccecb61..bfbc0158db1c2 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -379,3 +379,15 @@ test_spi_tle9104: tle9104@2e { #sensor-cells = <0>; }; }; + +test_spi_adxl34x: adxl34x@2f { + compatible = "adi,adxl34x"; + reg = <0x2f>; + spi-max-frequency = <0>; + int-gpios = <&test_gpio 0 0>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "25"; + accel-range = <16>; + status = "okay"; +}; diff --git a/tests/drivers/sensor/adxl34x/CMakeLists.txt b/tests/drivers/sensor/adxl34x/CMakeLists.txt new file mode 100644 index 0000000000000..e935798593cec --- /dev/null +++ b/tests/drivers/sensor/adxl34x/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright (c) 2024, Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sensor_adxl34x_test) + +target_include_directories(app PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../../../../drivers/sensor/adi/adxl34x" +) +target_sources(app PRIVATE + src/adxl34x_test.c + src/adxl34x_test.h + src/adxl34x_basic.c + src/adxl34x_decoder.c + src/adxl34x_streaming.c + src/adxl34x_extended.c + src/adxl34x_i2c.c + src/adxl34x_spi.c +) +# Let's speedup development. DISABLE IN PRODUCTION! +#target_compile_options(app PRIVATE -Wno-error=unused-variable -Wno-error=unused-function) diff --git a/tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.dtsi b/tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.dtsi new file mode 100644 index 0000000000000..fdefb8fad88e1 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.dtsi @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + adxl34x@53 { + compatible = "adi,adxl34x"; + reg = <0x53>; + status = "okay"; + }; + + adxl34x@54 { + compatible = "adi,adxl34x"; + reg = <0x54>; + int-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "50"; + accel-range = <2>; + status = "okay"; + }; + + adxl34x@55 { + compatible = "adi,adxl34x"; + reg = <0x55>; + int-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + int-pin = <2>; + packet-size = <31>; + accel-frequency = "3200"; + accel-range = <16>; + status = "okay"; + }; +}; diff --git a/tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.overlay b/tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.overlay new file mode 100644 index 0000000000000..f36b85821e574 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.overlay @@ -0,0 +1,6 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "adxl34x-i2c.dtsi" diff --git a/tests/drivers/sensor/adxl34x/boards/adxl34x-spi.dtsi b/tests/drivers/sensor/adxl34x/boards/adxl34x-spi.dtsi new file mode 100644 index 0000000000000..2849a204a8f8c --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/adxl34x-spi.dtsi @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +&spi0 { + adxl34x@0 { + compatible = "adi,adxl34x"; + reg = <0>; + spi-max-frequency = <5000000>; + status = "okay"; + }; + + adxl34x@1 { + compatible = "adi,adxl34x"; + reg = <1>; + spi-max-frequency = <5000000>; + int-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "100"; + accel-range = <2>; + status = "okay"; + }; + + adxl34x@2 { + compatible = "adi,adxl34x"; + reg = <2>; + spi-max-frequency = <5000000>; + int-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + int-pin = <2>; + packet-size = <31>; + accel-frequency = "3200"; + accel-range = <16>; + status = "okay"; + }; +}; diff --git a/tests/drivers/sensor/adxl34x/boards/adxl34x-spi.overlay b/tests/drivers/sensor/adxl34x/boards/adxl34x-spi.overlay new file mode 100644 index 0000000000000..3131aabb2935d --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/adxl34x-spi.overlay @@ -0,0 +1,6 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "adxl34x-spi.dtsi" diff --git a/tests/drivers/sensor/adxl34x/boards/native_sim.conf b/tests/drivers/sensor/adxl34x/boards/native_sim.conf new file mode 100644 index 0000000000000..908b878cac93d --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/native_sim.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_I2C=y +CONFIG_SPI=y diff --git a/tests/drivers/sensor/adxl34x/boards/native_sim.overlay b/tests/drivers/sensor/adxl34x/boards/native_sim.overlay new file mode 100644 index 0000000000000..0a30236b876c4 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/native_sim.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "adxl34x-spi.dtsi" +#include "adxl34x-i2c.dtsi" diff --git a/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.dtsi b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.dtsi new file mode 100644 index 0000000000000..a19b1a9b44044 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.dtsi @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "adxl34x-i2c.dtsi" + +&pinctrl { + i2c0_default: i2c0_default { + group1 { + psels = , + ; + }; + }; + + i2c0_sleep: i2c0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +&i2c0 { + status = "okay"; + pinctrl-0 = <&i2c0_default>; + pinctrl-1 = <&i2c0_sleep>; + pinctrl-names = "default", "sleep"; +}; diff --git a/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.overlay b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.overlay new file mode 100644 index 0000000000000..b85d37dad98a4 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.overlay @@ -0,0 +1,6 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "qemu_cortex_m0-i2c.dtsi" diff --git a/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.dtsi b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.dtsi new file mode 100644 index 0000000000000..e124dfff72abd --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.dtsi @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + spi1_default: spi1_default { + group1 { + psels = , + , + ; + }; + }; + + spi1_sleep: spi1_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; + +&spi1 { + status = "okay"; + pinctrl-0 = <&spi1_default>; + pinctrl-1 = <&spi1_sleep>; + pinctrl-names = "default", "sleep"; + + adxl34x@0 { + compatible = "adi,adxl34x"; + reg = <0>; + spi-max-frequency = <5000000>; + status = "okay"; + }; + + adxl34x@1 { + compatible = "adi,adxl34x"; + reg = <1>; + spi-max-frequency = <5000000>; + int-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; + int-pin = <1>; + packet-size = <16>; + accel-frequency = "100"; + accel-range = <2>; + status = "okay"; + }; + + adxl34x@2 { + compatible = "adi,adxl34x"; + reg = <2>; + spi-max-frequency = <5000000>; + int-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; + int-pin = <2>; + packet-size = <31>; + accel-frequency = "3200"; + accel-range = <16>; + status = "okay"; + }; +}; diff --git a/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.overlay b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.overlay new file mode 100644 index 0000000000000..ae2c62299583c --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.overlay @@ -0,0 +1,6 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "qemu_cortex_m0-spi.dtsi" diff --git a/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.conf b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.conf new file mode 100644 index 0000000000000..8665e63d44e2e --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.conf @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_I2C=y +CONFIG_SPI=y + +CONFIG_QEMU_ICOUNT=y diff --git a/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.overlay b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.overlay new file mode 100644 index 0000000000000..4531c2c996a1a --- /dev/null +++ b/tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "qemu_cortex_m0-spi.dtsi" +#include "qemu_cortex_m0-i2c.dtsi" diff --git a/tests/drivers/sensor/adxl34x/prj.conf b/tests/drivers/sensor/adxl34x/prj.conf new file mode 100644 index 0000000000000..9cdbbe685d5f5 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/prj.conf @@ -0,0 +1,16 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_ZTEST=y +CONFIG_SENSOR_ASYNC_API=y + +# Enable GPIO +CONFIG_GPIO=y + +# Enable sensors +CONFIG_SENSOR=y + +# Enable emulation +CONFIG_EMUL=y diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_basic.c b/tests/drivers/sensor/adxl34x/src/adxl34x_basic.c new file mode 100644 index 0000000000000..30f73b79e2825 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_basic.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl34x_test.h" +#include "adxl34x_emul.h" +#include "adxl34x_private.h" +#include "adxl34x_convert.h" + +/* The test fixture type must have the same name as the test suite. Because the same fixture type is + * shared between all test suites a define is used to make them 'compatible'. + */ +#define adxl34x_basic_fixture adxl34x_fixture + +LOG_MODULE_DECLARE(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +static void adxl34x_basic_suite_before(void *fixture) +{ + /* The tests in this test suite are only used by the build(s) specific for these test-cases, + * which is defined in the testcase.yaml file. + */ + Z_TEST_SKIP_IFNDEF(ADXL34X_TEST_BASIC); + /* Setup all i2c and spi devices available. */ + adxl34x_suite_before(fixture); +} + +/** + * @brief Test sensor initialisation. + * + * @details All devices defined in the device tree are initialised at startup. Some devices should + * succeed initialisation, some should not, and some are not used in this test suite at all. This + * test verifies the devices which support the basic functionality are available. + * + * @ingroup driver_sensor_subsys_tests + * @see device_is_ready() + */ +ZTEST_USER_F(adxl34x_basic, test_device_is_ready_for_basic_tests) +{ + /* The devices below should be able to be used in these tests. */ + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_0); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_1); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_2); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_53); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_54); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_55); +} + +static void double_to_raw(const struct emul *target, const double value, int8_t *out) +{ + struct adxl34x_dev_data *dev_data = target->dev->data; + const double range_scale = + (double)adxl34x_range_conv[dev_data->cfg.data_format.range] / 10000; + const int16_t scaled_value = value / range_scale; + + sys_put_le16(scaled_value, (uint8_t *)out); +} + +static void set_simulated_sensor_values(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device, + const double value_in[]) +{ + const struct emul *target = fixture->device[test_device].emul; + struct adxl34x_emul_data *data = (struct adxl34x_emul_data *)target->data; + uint8_t *reg = data->reg; + + zassert_equal(reg[ADXL34X_REG_DEVID], ADXL344_DEVID, + "Device id doesn't match, sanity check failed"); + double_to_raw(target, value_in[0], ®[ADXL34X_REG_DATA]); + double_to_raw(target, value_in[1], ®[ADXL34X_REG_DATA + 2]); + double_to_raw(target, value_in[2], ®[ADXL34X_REG_DATA + 4]); + reg[ADXL34X_REG_FIFO_STATUS] = 1; /* FIFO entries = 1 */ +} + +static void test_get_value(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device, + const bool use_pm) +{ + LOG_DBG("Running test on %s/%s", adxl34x_get_bus_name(fixture, test_device), + adxl34x_get_name(fixture, test_device)); + struct sensor_value acc[3]; + + /* Setup the test-case/driver with pre-defined x, y and z values. */ + zassert_not_null(fixture->device[test_device].emul); + const struct adxl34x_dev_data *dev_data = fixture->device[test_device].emul->dev->data; + const uint8_t max_g = adxl34x_max_g_conv[dev_data->cfg.data_format.range]; + const double value_in[] = {-1.0 * max_g, 0.5 * max_g, 1.0 * max_g}; + + set_simulated_sensor_values(fixture, test_device, value_in); + +#if CONFIG_PM_DEVICE_RUNTIME + if (use_pm) { + zassert_ok(pm_device_runtime_get(fixture->device[test_device].dev)); + } else { + /* Fetching sensor data without a pm-get should fail. */ + zassert_false(sensor_sample_fetch(fixture->device[test_device].dev) >= 0); + return; + } +#endif + + /* Use the sensor as normal. */ + zassert_true(sensor_sample_fetch(fixture->device[test_device].dev) >= 0); + zassert_ok( + sensor_channel_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, acc)); + + /* Verify the set values are corresponding with the returned values. */ + for (unsigned int i = 0; i < 3; i++) { + const double value_out = MS2_TO_G(sensor_value_to_double(&acc[i])); + + zassert_true(fabs(value_in[i] - value_out) < 0.05); + } + +#if CONFIG_PM_DEVICE_RUNTIME + if (use_pm) { + zassert_ok( + pm_device_runtime_put_async(fixture->device[test_device].dev, K_NO_WAIT)); + } +#endif +} + +/** + * @brief Test getting basic sensor data using spi. + * + * @details Use the default polling mechanism to get sensor data. + * + * @ingroup driver_sensor_subsys_tests + * @see sensor_sample_fetch(), sensor_channel_get() + */ +ZTEST_USER_F(adxl34x_basic, test_get_value_spi) +{ + test_get_value(fixture, ADXL34X_TEST_SPI_0, true); + test_get_value(fixture, ADXL34X_TEST_SPI_1, true); + test_get_value(fixture, ADXL34X_TEST_SPI_2, true); +} + +/** + * @brief Test getting basic sensor data using i2c. + * + * @details Use the default polling mechanism to get sensor data. + * + * @ingroup driver_sensor_subsys_tests + * @see sensor_sample_fetch(), sensor_channel_get() + */ +ZTEST_USER_F(adxl34x_basic, test_get_value_i2c) +{ + test_get_value(fixture, ADXL34X_TEST_I2C_53, true); + test_get_value(fixture, ADXL34X_TEST_I2C_54, true); + test_get_value(fixture, ADXL34X_TEST_I2C_55, true); +} + +/** + * @brief Test getting basic sensor data with power management enabled. + * + * @details Use the default polling mechanism to get sensor data without a pm_device_runtime_get. + * + * @ingroup driver_sensor_subsys_tests + * @see sensor_sample_fetch(), sensor_channel_get(), pm_device_runtime_get() + */ +ZTEST_USER_F(adxl34x_basic, test_sample_fetch_pm) +{ + test_get_value(fixture, ADXL34X_TEST_I2C_53, false); +} + +static void test_sensor_attr_sampling_frequency(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device, + const struct sensor_value default_value) +{ + static const struct sensor_value value_in = {12, 500000}; /* 12.50 Hz */ + struct sensor_value value_out; + /* Check default value */ + zassert_ok(sensor_attr_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_SAMPLING_FREQUENCY, &value_out)); + zassert_true(is_equal_sensor_value(default_value, value_out)); + /* Check if setting and getting values */ + zassert_ok(sensor_attr_set(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_SAMPLING_FREQUENCY, &value_in)); + zassert_ok(sensor_attr_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_SAMPLING_FREQUENCY, &value_out)); + zassert_true(is_equal_sensor_value(value_in, value_out)); +} + +/** + * @brief Test changing the sample frequency. + * + * @details Depending on the device tree configuration the various sensors are initialised with + * various frequencies. This test not only tests if these defaults are correct, it also sets a new + * frequency and verifies this frequency is set correctly. + * + * @ingroup driver_sensor_subsys_tests + * @see sensor_attr_get() + */ +ZTEST_USER_F(adxl34x_basic, test_sensor_attr_sampling_frequency) +{ + /* No value provided in the dts file, verify default (100 Hz) */ + test_sensor_attr_sampling_frequency(fixture, ADXL34X_TEST_I2C_53, + (struct sensor_value){100, 0}); + /* Value set explicitly in dts file to 50 Hz. */ + test_sensor_attr_sampling_frequency(fixture, ADXL34X_TEST_I2C_54, + (struct sensor_value){50, 0}); + /* Value set explicitly in dts file to 3200 Hz. */ + test_sensor_attr_sampling_frequency(fixture, ADXL34X_TEST_I2C_55, + (struct sensor_value){3200, 0}); +} + +void test_sensor_attr_sampling_frequency_range(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device) +{ + static const double q31_delta_error = 0.0000005; + static const double frequency[] = { + 0.10, 0.20, 0.39, 0.78, 1.56, 3.13, 6.25, 12.50, + 25.0, 50.0, 100.0, 200.0, 400.0, 800.0, 1600.0, 3200.0, + }; + + for (unsigned int i = 0; i < ARRAY_SIZE(frequency); i++) { + struct sensor_value value_in, value_out; + /* Rounding down will be done when setting the sample frequency, + * to make sure this doesn't result in setting the value below + * the one needed we add a delta error to the set value. + */ + sensor_value_from_double(&value_in, frequency[i] + q31_delta_error); + + LOG_DBG("Setting frequency to %d.%d", value_in.val1, value_in.val2); + zassert_ok(sensor_attr_set(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_SAMPLING_FREQUENCY, &value_in)); + zassert_ok(sensor_attr_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_SAMPLING_FREQUENCY, &value_out)); + const double frequency_out = sensor_value_to_double(&value_out); + + zassert_true(fabs(frequency[i] - frequency_out) < q31_delta_error); + } +} + +/** + * @brief Test the complete range of sampling frequencies. + * + * @details Loop though the entire set of supported frequencies and verify each of the can be set + * correctly (by reading back the result). + * + * @ingroup driver_sensor_subsys_tests + * @see sensor_attr_set(), sensor_attr_get() + */ +ZTEST_USER_F(adxl34x_basic, test_sensor_attr_sampling_frequency_range) +{ + test_sensor_attr_sampling_frequency_range(fixture, ADXL34X_TEST_I2C_53); +} + +void test_sensor_attr_offset(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device) +{ + static const int32_t ug_in[] = {-1000000, 500000, 2000000}; + struct sensor_value value_in[3], value_out[3]; + + for (unsigned int i = 0; i < 3; i++) { + sensor_ug_to_ms2(ug_in[i], &value_in[i]); + } + zassert_ok(sensor_attr_set(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_OFFSET, value_in)); + zassert_ok(sensor_attr_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_OFFSET, value_out)); + + for (unsigned int i = 0; i < 3; i++) { + const int32_t ug_out = sensor_ms2_to_ug(&value_out[i]); + + zassert_true(fabs((double)ug_in[i] - (double)ug_out) / 1000000 < 0.02); + } +} + +/** + * @brief Test changing the offset value. + * + * @details Verify changing the offset of the sensor value works correctly by using a range of + * values (both positive and negative). + * + * @ingroup driver_sensor_subsys_tests + * @see sensor_attr_set(), sensor_attr_get() + */ +ZTEST_USER_F(adxl34x_basic, test_sensor_attr_offset) +{ + test_sensor_attr_offset(fixture, ADXL34X_TEST_I2C_55); +} + +void test_emul_set_attr_offset(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device) +{ + static const struct sensor_chan_spec channel = { + .chan_idx = 0, + .chan_type = SENSOR_CHAN_ACCEL_XYZ, + }; + static const double offset_lsb_ms2 = 0.152985; /* lsb = 15.6 mg = 0.152985 ms2 */ + static const uint8_t shift = 5; /* Maximum value of offset_in now is 19.6 ms2 (2g) */ + static const double offset_in[] = {offset_lsb_ms2, -offset_lsb_ms2, offset_lsb_ms2 * 100}; + const struct sensor_three_axis_attribute offset_in_q31 = { + .x = DOUBLE_TO_Q31(offset_in[0], shift), + .y = DOUBLE_TO_Q31(offset_in[1], shift), + .z = DOUBLE_TO_Q31(offset_in[2], shift), + .shift = shift, + }; + struct sensor_value offset_out[3]; + + zassert_ok(emul_sensor_backend_set_attribute(fixture->device[test_device].emul, channel, + SENSOR_ATTR_OFFSET, &offset_in_q31)); + zassert_ok(sensor_attr_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_XYZ, + SENSOR_ATTR_OFFSET, offset_out)); + + for (unsigned int i = 0; i < 3; i++) { + const double offset_out_ms2 = sensor_value_to_double(&offset_out[i]); + + zassert_true(fabs(offset_out_ms2 - offset_in[i]) < 0.0005); + } +} + +/** + * @brief Test the sensor emulation api. + * + * @details Using the sensor emulation api change the offset of the sensor, and verify by getting + * the offset using the normal sensor api. + * + * @ingroup driver_sensor_subsys_tests + * @see emul_sensor_backend_set_attribute(), sensor_attr_get() + */ +ZTEST_USER_F(adxl34x_basic, test_emul_set_attr_offset) +{ + test_emul_set_attr_offset(fixture, ADXL34X_TEST_I2C_55); +} + +void test_emul_get_sample_range(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device) +{ + static const struct sensor_chan_spec channel = { + .chan_idx = 0, + .chan_type = SENSOR_CHAN_ACCEL_XYZ, + }; + static const uint16_t resolution = 512; + const struct emul *target = fixture->device[test_device].emul; + const struct adxl34x_dev_data *dev_data = target->dev->data; + const double max_g = (double)adxl34x_max_g_conv[dev_data->cfg.data_format.range]; + q31_t lower, upper, epsilon; + int8_t shift; + + zassert_ok(emul_sensor_backend_get_sample_range(fixture->device[test_device].emul, channel, + &lower, &upper, &epsilon, &shift)); + + const double lower_f = MS2_TO_G(Q31_TO_DOUBLE(lower, shift)); + const double upper_f = MS2_TO_G(Q31_TO_DOUBLE(upper, shift)); + const double epsilon_f = MS2_TO_G(Q31_TO_DOUBLE(epsilon, shift)); + + zassert_true(fabs(upper_f - max_g) < 0.0001); + zassert_true(fabs(-lower_f - max_g) < 0.0001); + zassert_true(fabs(epsilon_f - max_g / resolution) < 0.0001); +} + +/** + * @brief Test the sensor emulation api. + * + * @details Using the sensor emulation api get the sensor range and verify using the one set in the + * device tree. the offset using the normal sensor api. + * + * @ingroup driver_sensor_subsys_tests + * @see emul_sensor_backend_get_sample_range() + */ +ZTEST_USER_F(adxl34x_basic, test_emul_get_sample_range) +{ + test_emul_get_sample_range(fixture, ADXL34X_TEST_I2C_53); + test_emul_get_sample_range(fixture, ADXL34X_TEST_I2C_54); + test_emul_get_sample_range(fixture, ADXL34X_TEST_I2C_55); +} + +void test_emul_set_channel(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device, + uint16_t chan_type) +{ + static const struct sensor_chan_spec channel_x = { + .chan_idx = 0, + .chan_type = SENSOR_CHAN_ACCEL_X, + }; + static const struct sensor_chan_spec channel_y = { + .chan_idx = 0, + .chan_type = SENSOR_CHAN_ACCEL_Y, + }; + static const struct sensor_chan_spec channel_z = { + .chan_idx = 0, + .chan_type = SENSOR_CHAN_ACCEL_Z, + }; + static const uint8_t shift = 5; /* Maximum value of offset_in now is 19.6 ms2 (2g) */ + static const double value_in[] = {9.80665, -9.80665, 19.6133}; + const struct sensor_three_axis_attribute value = { + .x = DOUBLE_TO_Q31(value_in[0], shift), + .y = DOUBLE_TO_Q31(value_in[1], shift), + .z = DOUBLE_TO_Q31(value_in[2], shift), + .shift = shift, + }; + struct sensor_value acc[3]; + + zassert_ok(emul_sensor_backend_set_channel(fixture->device[test_device].emul, channel_x, + &value.x, shift)); + zassert_ok(emul_sensor_backend_set_channel(fixture->device[test_device].emul, channel_y, + &value.y, shift)); + zassert_ok(emul_sensor_backend_set_channel(fixture->device[test_device].emul, channel_z, + &value.z, shift)); + +#if CONFIG_PM_DEVICE_RUNTIME + zassert_ok(pm_device_runtime_get(fixture->device[test_device].dev)); +#endif + + /* Check a non existent channel */ + zassert_false(sensor_sample_fetch_chan(fixture->device[test_device].dev, + SENSOR_CHAN_VOLTAGE) >= 0); + + /* Use the sensor as normal. */ + if (chan_type == SENSOR_CHAN_ALL) { + zassert_true(sensor_sample_fetch(fixture->device[test_device].dev) >= 0); + zassert_ok( + sensor_channel_get(fixture->device[test_device].dev, SENSOR_CHAN_ALL, acc)); + } else if (chan_type == SENSOR_CHAN_ACCEL_XYZ) { + zassert_true(sensor_sample_fetch_chan(fixture->device[test_device].dev, + SENSOR_CHAN_ACCEL_XYZ) >= 0); + zassert_ok(sensor_channel_get(fixture->device[test_device].dev, + SENSOR_CHAN_ACCEL_XYZ, acc)); + } else { + zassert_true(sensor_sample_fetch_chan(fixture->device[test_device].dev, + SENSOR_CHAN_ACCEL_X) >= 0); + zassert_ok(sensor_channel_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_X, + &acc[0])); + zassert_true(sensor_sample_fetch_chan(fixture->device[test_device].dev, + SENSOR_CHAN_ACCEL_Y) >= 0); + zassert_ok(sensor_channel_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_Y, + &acc[1])); + zassert_true(sensor_sample_fetch_chan(fixture->device[test_device].dev, + SENSOR_CHAN_ACCEL_Z) >= 0); + zassert_ok(sensor_channel_get(fixture->device[test_device].dev, SENSOR_CHAN_ACCEL_Z, + &acc[2])); + } + + /* Verify the set values are corresponding with the returned values. */ + for (unsigned int i = 0; i < 3; i++) { + const double value_out = sensor_value_to_double(&acc[i]); + + zassert_true(fabs(value_in[i] - value_out) < 0.05); + } + +#if CONFIG_PM_DEVICE_RUNTIME + zassert_ok(pm_device_runtime_put_async(fixture->device[test_device].dev, K_NO_WAIT)); +#endif +} + +/** + * @brief Test getting samples values which were set using the emul(ation) api. + * + * @details Simular to the previous test (getting basic sensor data) except pre-defined sensor + * values are set in advance, which are verified after getting the values. + * + * @ingroup driver_sensor_subsys_tests + * @see emul_sensor_backend_set_channel(), sensor_sample_fetch(), sensor_channel_get() + */ +ZTEST_USER_F(adxl34x_basic, test_emul_set_channel) +{ + test_emul_set_channel(fixture, ADXL34X_TEST_I2C_53, SENSOR_CHAN_ACCEL_XYZ); + test_emul_set_channel(fixture, ADXL34X_TEST_I2C_54, SENSOR_CHAN_ACCEL_XYZ); + test_emul_set_channel(fixture, ADXL34X_TEST_I2C_55, SENSOR_CHAN_ACCEL_XYZ); +} + +/** + * @brief Test getting samples values which were set using the emul(ation) api. + * + * @details Simular to the previous test (getting basic sensor data) except pre-defined sensor + * values are set in advance, which are verified after getting the values, and different channels + * are fetched/get. + * + * @ingroup driver_sensor_subsys_tests + * @see emul_sensor_backend_set_channel(), sensor_sample_fetch(), sensor_channel_get() + */ +ZTEST_USER_F(adxl34x_basic, test_emul_set_channel_x) +{ + test_emul_set_channel(fixture, ADXL34X_TEST_I2C_53, SENSOR_CHAN_ACCEL_X); + test_emul_set_channel(fixture, ADXL34X_TEST_I2C_53, SENSOR_CHAN_ALL); +} + +void test_emul_get_attr_offset_metadata(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device) +{ + static const struct sensor_chan_spec channel = { + .chan_idx = 0, + .chan_type = SENSOR_CHAN_ACCEL_XYZ, + }; + static const double min_ms2 = -19.58161; /* -2 g */ + static const double max_ms2 = 19.42863; /* 2 g */ + static const double increment_ms2 = 0.15298; /* 15.6 mg */ + q31_t min_q31, max_q31, increment_q31; + int8_t shift; + + zassert_ok(emul_sensor_backend_get_attribute_metadata(fixture->device[test_device].emul, + channel, SENSOR_ATTR_OFFSET, &min_q31, + &max_q31, &increment_q31, &shift)); + + const double min = Q31_TO_DOUBLE(min_q31, shift); + const double max = Q31_TO_DOUBLE(max_q31, shift); + const double increment = Q31_TO_DOUBLE(increment_q31, shift); + + zassert_true(fabs(min - min_ms2) < 0.001); + zassert_true(fabs(max - max_ms2) < 0.001); + zassert_true(fabs(increment - increment_ms2) < 0.0001); +} + +/** + * @brief Test the sensor emulation api. + * + * @details Using the sensor emulation api get the metadata of the sensor offset attribute. + * + * @ingroup driver_sensor_subsys_tests + * @see emul_sensor_backend_get_attribute_metadata() + */ +ZTEST_USER_F(adxl34x_basic, test_emul_get_attr_offset_metadata) +{ + test_emul_get_attr_offset_metadata(fixture, ADXL34X_TEST_I2C_53); +} + +ZTEST_SUITE(adxl34x_basic, NULL, adxl34x_suite_setup, adxl34x_basic_suite_before, NULL, NULL); diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_decoder.c b/tests/drivers/sensor/adxl34x/src/adxl34x_decoder.c new file mode 100644 index 0000000000000..edbe17f4f7e15 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_decoder.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_test.h" + +/* The test fixture type must have the same name as the test suite. Because the same fixture type is + * shared between all test suites a define is used to make them 'compatible'. + */ +#define adxl34x_decoder_fixture adxl34x_fixture + +LOG_MODULE_DECLARE(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +static void adxl34x_decoder_suite_before(void *fixture) +{ + /* The tests in this test suite are only used by the build(s) specific for these test-cases, + * which is defined in the testcase.yaml file. + */ + Z_TEST_SKIP_IFNDEF(ADXL34X_TEST_DECODER); + /* Setup all i2c and spi devices available. */ + adxl34x_suite_before(fixture); +} + +/** + * @brief Test sensor initialisation. + * + * @details All devices defined in the device tree are initialised at startup. Some devices should + * succeed initialisation, some should not, and some are not used in this test suite at all. This + * test verifies the devices which support the decoder functionality are available. + * + * @ingroup driver_sensor_subsys_tests + * @see device_is_ready() + */ +ZTEST_USER_F(adxl34x_decoder, test_device_is_ready_for_decoder_tests) +{ + /* The devices below should be able to be used in these tests. */ + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_0); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_1); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_2); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_53); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_54); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_55); +} + +ZTEST_SUITE(adxl34x_decoder, NULL, adxl34x_suite_setup, adxl34x_decoder_suite_before, NULL, NULL); diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_extended.c b/tests/drivers/sensor/adxl34x/src/adxl34x_extended.c new file mode 100644 index 0000000000000..7e55551e723ff --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_extended.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_test.h" + +/* The test fixture type must have the same name as the test suite. Because the same fixture type is + * shared between all test suites a define is used to make them 'compatible'. + */ +#define adxl34x_extended_fixture adxl34x_fixture + +LOG_MODULE_DECLARE(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +static void adxl34x_extended_suite_before(void *fixture) +{ + /* The tests in this test suite are only used by the build(s) specific for these test-cases, + * which is defined in the testcase.yaml file. + */ + Z_TEST_SKIP_IFNDEF(ADXL34X_TEST_EXTENDED); + /* Setup all i2c and spi devices available. */ + adxl34x_suite_before(fixture); +} + +/** + * @brief Test sensor initialisation. + * + * @details All devices defined in the device tree are initialised at startup. Some devices should + * succeed initialisation, some should not, and some are not used in this test suite at all. This + * test verifies the devices which support extended functionality are available. Because this + * requires additional dts configuration the devices which only have the basic configuration should + * not be available. + * + * @ingroup driver_sensor_subsys_tests + * @see device_is_ready() + */ +ZTEST_USER_F(adxl34x_extended, test_device_is_ready_for_extended_tests) +{ + /* The devices below should be able to be used in these tests. */ + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_1); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_2); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_54); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_55); + + /* The devices below should NOT be able to be used in these tests. They don't have all the + * nessesary dts configuration needed for this specific build. + */ + adxl34x_is_not_ready(fixture, ADXL34X_TEST_SPI_0); + adxl34x_is_not_ready(fixture, ADXL34X_TEST_I2C_53); +} + +ZTEST_SUITE(adxl34x_extended, NULL, adxl34x_suite_setup, adxl34x_extended_suite_before, NULL, NULL); diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_i2c.c b/tests/drivers/sensor/adxl34x/src/adxl34x_i2c.c new file mode 100644 index 0000000000000..41dd857741d51 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_i2c.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_test.h" + +/* The test fixture type must have the same name as the test suite. Because the same fixture type is + * shared between all test suites a define is used to make them 'compatible'. + */ +#define adxl34x_i2c_fixture adxl34x_fixture + +LOG_MODULE_DECLARE(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +static void adxl34x_i2c_suite_before(void *fixture) +{ + /* The tests in this test suite are only used by the build(s) specific for these test-cases, + * which is defined in the testcase.yaml file. + */ + Z_TEST_SKIP_IFNDEF(ADXL34X_TEST_I2C); + /* Setup all i2c and spi devices available. */ + adxl34x_suite_before(fixture); +} + +/** + * @brief Test sensor initialisation. + * + * @details All devices defined in the device tree are initialised at startup. Some devices should + * succeed initialisation, some should not, and some are not used in this test suite at all. This + * test verifies the devices which support i2c are available. The spi devices are not defined in + * this build, and are therefore excluded from this test. This test also makes sure no additional + * dependencies are needed. + * + * @ingroup driver_sensor_subsys_tests + * @see device_is_ready() + */ +ZTEST_USER_F(adxl34x_i2c, test_device_is_ready_for_i2c_tests) +{ + /* The devices below should be able to be used in these tests. For this build the overlay + * was used which defines only the i2c devices, so the spi devices are not used here. + */ + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_54); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_55); + + /* The devices below should NOT be able to be used in these tests. They don't have all the + * nessesary dts configuration needed for this specific build. + */ + adxl34x_is_not_ready(fixture, ADXL34X_TEST_I2C_53); +} + +ZTEST_SUITE(adxl34x_i2c, NULL, adxl34x_suite_setup, adxl34x_i2c_suite_before, NULL, NULL); diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_spi.c b/tests/drivers/sensor/adxl34x/src/adxl34x_spi.c new file mode 100644 index 0000000000000..4aa76c9efd685 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_spi.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_test.h" + +/* The test fixture type must have the same name as the test suite. Because the same fixture type is + * shared between all test suites a define is used to make them 'compatible'. + */ +#define adxl34x_spi_fixture adxl34x_fixture + +LOG_MODULE_DECLARE(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +static void adxl34x_spi_suite_before(void *fixture) +{ + /* The tests in this test suite are only used by the build(s) specific for these test-cases, + * which is defined in the testcase.yaml file. + */ + Z_TEST_SKIP_IFNDEF(ADXL34X_TEST_SPI); + /* Setup all spi and spi devices available. */ + adxl34x_suite_before(fixture); +} + +/** + * @brief Test sensor initialisation. + * + * @details All devices defined in the device tree are initialised at startup. Some devices should + * succeed initialisation, some should not, and some are not used in this test suite at all. This + * test verifies the devices which support spi are available. The i2c devices are not defined in + * this build, and are therefore excluded from this test. This test also makes sure no additional + * dependencies are needed. + * + * @ingroup driver_sensor_subsys_tests + * @see device_is_ready() + */ +ZTEST_USER_F(adxl34x_spi, test_device_is_ready_for_spi_tests) +{ + /* The devices below should be able to be used in these tests. For this build the overlay + * was used which defines only the spi devices, so the i2c devices are not used here. + */ + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_1); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_2); + + /* The devices below should NOT be able to be used in these tests. They don't have all the + * nessesary dts configuration needed for this specific build. + */ + adxl34x_is_not_ready(fixture, ADXL34X_TEST_SPI_0); +} + +ZTEST_SUITE(adxl34x_spi, NULL, adxl34x_suite_setup, adxl34x_spi_suite_before, NULL, NULL); diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_streaming.c b/tests/drivers/sensor/adxl34x/src/adxl34x_streaming.c new file mode 100644 index 0000000000000..8e4f885eb8729 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_streaming.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "adxl34x_test.h" + +/* The test fixture type must have the same name as the test suite. Because the same fixture type is + * shared between all test suites a define is used to make them 'compatible'. + */ +#define adxl34x_streaming_fixture adxl34x_fixture + +LOG_MODULE_DECLARE(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +static void adxl34x_streaming_suite_before(void *fixture) +{ + /* The tests in this test suite are only used by the build(s) specific for these test-cases, + * which is defined in the testcase.yaml file. + */ + Z_TEST_SKIP_IFNDEF(ADXL34X_TEST_STREAMING); + /* Setup all i2c and spi devices available. */ + adxl34x_suite_before(fixture); +} + +/** + * @brief Test sensor initialisation. + * + * @details All devices defined in the device tree are initialised at startup. Some devices should + * succeed initialisation, some should not, and some are not used in this test suite at all. This + * test verifies the devices which support streaming functionality are available. Because this + * requires additional dts configuration the devices which only have the basic configuration should + * not be available. + * + * @ingroup driver_sensor_subsys_tests + * @see device_is_ready() + */ +ZTEST_USER_F(adxl34x_streaming, test_device_is_ready_for_streaming_tests) +{ + /* The devices below should be able to be used in these tests. */ + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_1); + adxl34x_is_ready(fixture, ADXL34X_TEST_SPI_2); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_54); + adxl34x_is_ready(fixture, ADXL34X_TEST_I2C_55); + + /* The devices below should NOT be able to be used in streaming tests. They don't have all + * the nessesary dts configuration needed for this specific build. + */ + adxl34x_is_not_ready(fixture, ADXL34X_TEST_SPI_0); + adxl34x_is_not_ready(fixture, ADXL34X_TEST_I2C_53); +} + +ZTEST_SUITE(adxl34x_streaming, NULL, adxl34x_suite_setup, adxl34x_streaming_suite_before, NULL, + NULL); diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_test.c b/tests/drivers/sensor/adxl34x/src/adxl34x_test.c new file mode 100644 index 0000000000000..e48edf58c98d3 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_test.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Chaim Zax + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include "adxl34x_test.h" + +LOG_MODULE_REGISTER(adxl34x_test, CONFIG_SENSOR_LOG_LEVEL); + +#define ADXL34X_DEVICE_DEF(_dev, _path1, _path2) \ + [_dev] = { \ + .dev = DEVICE_DT_GET(DT_PATH(_path1, _path2)), \ + .emul = EMUL_DT_GET(DT_PATH(_path1, _path2)), \ + } +#define ADXL34X_DEVICE_NONE(_dev) [_dev] = {} +#define ADXL34X_DEVICE_FIXTURE(_dev, _path1, _path2) \ + COND_CODE_1(DT_NODE_EXISTS(DT_PATH(_path1, _path2)), \ + (ADXL34X_DEVICE_DEF(_dev, _path1, _path2)), (ADXL34X_DEVICE_NONE(_dev))) + +static const char unknown[] = "unknown"; +static char bus_name[][5] = { + "i2c", + "espi", + "spi", + "none", +}; + +void *adxl34x_suite_setup(void) +{ + /* clang-format off */ + static struct adxl34x_fixture fixture = { + .device = { + ADXL34X_DEVICE_FIXTURE(ADXL34X_TEST_SPI_0, spi_200, adxl34x_0), + ADXL34X_DEVICE_FIXTURE(ADXL34X_TEST_SPI_1, spi_200, adxl34x_1), + ADXL34X_DEVICE_FIXTURE(ADXL34X_TEST_SPI_2, spi_200, adxl34x_2), + ADXL34X_DEVICE_FIXTURE(ADXL34X_TEST_I2C_53, i2c_100, adxl34x_53), + ADXL34X_DEVICE_FIXTURE(ADXL34X_TEST_I2C_54, i2c_100, adxl34x_54), + ADXL34X_DEVICE_FIXTURE(ADXL34X_TEST_I2C_55, i2c_100, adxl34x_55), + }, + }; + /* clang-format on */ + return &fixture; +} + +void adxl34x_suite_before(void *a_fixture) +{ + const struct adxl34x_fixture *fixture = (struct adxl34x_fixture *)a_fixture; + + for (unsigned int i = 0; i < ADXL34X_TEST_NR_OF_DEVICES; i++) { + if (fixture->device[i].dev != NULL) { + k_object_access_grant(fixture->device[i].dev, k_current_get()); + } + } +} + +const char *adxl34x_get_name(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device) +{ + if (fixture == NULL || fixture->device[test_device].dev == NULL) { + return unknown; + } + return fixture->device[test_device].dev->name; +} + +const char *adxl34x_get_bus_name(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device) +{ + if (fixture == NULL || fixture->device[test_device].emul == NULL) { + return unknown; + } + const enum emul_bus_type bus_type = fixture->device[test_device].emul->bus_type; + + if (bus_type > 3) { + return unknown; + } + return bus_name[bus_type]; +} + +void adxl34x_is_ready(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device) +{ + zassert_ok(fixture->device[test_device].dev->state->init_res, + "Device %s/%s not initialized correctly", + adxl34x_get_bus_name(fixture, test_device), + adxl34x_get_name(fixture, test_device)); +} + +void adxl34x_is_not_ready(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device) +{ + zassert_not_ok(fixture->device[test_device].dev->state->init_res, + "Device %s/%s was initialized correctly unexpectedly", + adxl34x_get_bus_name(fixture, test_device), + adxl34x_get_name(fixture, test_device)); +} + +bool is_equal_sensor_value(const struct sensor_value value_1, const struct sensor_value value_2) +{ + return (value_1.val1 == value_2.val1 && value_1.val2 == value_2.val2); +} + +bool is_equal_float(const float value_1, const float value_2, const float error) +{ + return (fabsf(value_1 - value_2) < error); +} + +bool is_equal_double(const double value_1, const double value_2, const double error) +{ + return (fabs(value_1 - value_2) < error); +} diff --git a/tests/drivers/sensor/adxl34x/src/adxl34x_test.h b/tests/drivers/sensor/adxl34x/src/adxl34x_test.h new file mode 100644 index 0000000000000..db85b878908d4 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/src/adxl34x_test.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Chaim Zax + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_TEST_DRIVERS_SENSOR_ADXL34X_TEST_H_ +#define ZEPHYR_TEST_DRIVERS_SENSOR_ADXL34X_TEST_H_ + +#include +#include + +#include +#include + +#define Q31_SCALE ((int64_t)INT32_MAX + 1) +#define FLOAT_TO_Q31(x, shift) ((int64_t)((float)(x) * (float)Q31_SCALE) >> shift) +#define Q31_TO_FLOAT(x, shift) ((float)((int64_t)(x) << shift) / (float)Q31_SCALE) +#define DOUBLE_TO_Q31(x, shift) ((int64_t)((double)(x) * (double)Q31_SCALE) >> shift) +#define Q31_TO_DOUBLE(x, shift) ((double)((int64_t)(x) << shift) / (double)Q31_SCALE) +#define G_TO_MS2(g) (g * SENSOR_G / 1000000LL) +#define MS2_TO_G(ms) (ms / SENSOR_G * 1000000LL) + +enum ADXL34X_TEST { + ADXL34X_TEST_SPI_0, + ADXL34X_TEST_SPI_1, + ADXL34X_TEST_SPI_2, + ADXL34X_TEST_I2C_53, + ADXL34X_TEST_I2C_54, + ADXL34X_TEST_I2C_55, + /* Keep this entry last */ + ADXL34X_TEST_NR_OF_DEVICES, +}; + +struct adxl34x_device { + const struct device *dev; + const struct emul *emul; +}; + +struct adxl34x_fixture { + struct adxl34x_device device[ADXL34X_TEST_NR_OF_DEVICES]; +}; + +bool is_equal_sensor_value(struct sensor_value value_1, struct sensor_value value_2); + +void *adxl34x_suite_setup(void); +void adxl34x_suite_before(void *a_fixture); +void adxl34x_is_ready(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device); +void adxl34x_is_not_ready(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device); +const char *adxl34x_get_name(struct adxl34x_fixture *fixture, const enum ADXL34X_TEST test_device); +const char *adxl34x_get_bus_name(struct adxl34x_fixture *fixture, + const enum ADXL34X_TEST test_device); +bool is_equal_float(const float value_1, const float value_2, const float error); +bool is_equal_double(const double value_1, const double value_2, const double error); + +#endif /* ZEPHYR_TEST_DRIVERS_SENSOR_ADXL34X_TEST_H_ */ diff --git a/tests/drivers/sensor/adxl34x/testcase.yaml b/tests/drivers/sensor/adxl34x/testcase.yaml new file mode 100644 index 0000000000000..d7638abb580b2 --- /dev/null +++ b/tests/drivers/sensor/adxl34x/testcase.yaml @@ -0,0 +1,116 @@ +# +# Copyright (c) 2024 Chaim Zax +# SPDX-License-Identifier: Apache-2.0 +# + +common: + tags: + - drivers + - sensor +tests: + drivers.sensor.adxl34x.basic: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_BASIC + extra_configs: + - CONFIG_ADXL34X_DECODER=n + - CONFIG_ADXL34X_FIFO_MODE_BYPASS=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.basic.compatible: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_BASIC + extra_configs: + - CONFIG_ADXL34X_ADXL345_COMPATIBLE=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.basic.pm: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_BASIC + extra_configs: + - CONFIG_ADXL34X_DECODER=n + - CONFIG_ADXL34X_FIFO_MODE_BYPASS=y + - CONFIG_PM_DEVICE=y + - CONFIG_PM_DEVICE_RUNTIME=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.basic.arm: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_BASIC + extra_configs: + - CONFIG_ADXL34X_DECODER=n + - CONFIG_ADXL34X_FIFO_MODE_BYPASS=y + platform_allow: qemu_cortex_m0 + integration_platforms: + - qemu_cortex_m0 + build_only: true + drivers.sensor.adxl34x.decoder: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_DECODER + extra_configs: + - CONFIG_ADXL34X_DECODER=y + - CONFIG_ADXL34X_DATA_TYPE_SENSOR_VALUE=y + - CONFIG_ADXL34X_FIFO_MODE_BYPASS=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.streaming: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_STREAMING + extra_configs: + - CONFIG_ADXL34X_DECODER=n + - CONFIG_ADXL34X_FIFO_MODE_STREAM=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.extended: + extra_args: EXTRA_CFLAGS=-DADXL34X_TEST_EXTENDED + extra_configs: + - CONFIG_ADXL34X_DECODER=y + - CONFIG_ADXL34X_DATA_TYPE_Q31=y + - CONFIG_ADXL34X_FIFO_MODE_STREAM=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.i2c: + extra_args: + - EXTRA_CFLAGS=-DADXL34X_TEST_I2C + - DTC_OVERLAY_FILE="boards/adxl34x-i2c.overlay" + extra_configs: + - CONFIG_ADXL34X_DECODER=y + - CONFIG_ADXL34X_DATA_TYPE_Q31=y + - CONFIG_ADXL34X_FIFO_MODE_STREAM=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.i2c.arm: + extra_args: + - EXTRA_CFLAGS=-DADXL34X_TEST_I2C + - DTC_OVERLAY_FILE="boards/qemu_cortex_m0-i2c.dtsi" + extra_configs: + - CONFIG_ADXL34X_DECODER=y + - CONFIG_ADXL34X_DATA_TYPE_Q31=y + - CONFIG_ADXL34X_FIFO_MODE_STREAM=y + platform_allow: qemu_cortex_m0 + integration_platforms: + - qemu_cortex_m0 + build_only: true + drivers.sensor.adxl34x.spi: + extra_args: + - EXTRA_CFLAGS=-DADXL34X_TEST_SPI + - DTC_OVERLAY_FILE="boards/adxl34x-spi.overlay" + extra_configs: + - CONFIG_ADXL34X_DECODER=y + - CONFIG_ADXL34X_DATA_TYPE_Q31=y + - CONFIG_ADXL34X_FIFO_MODE_STREAM=y + platform_allow: native_sim + integration_platforms: + - native_sim + drivers.sensor.adxl34x.spi.arm: + extra_args: + - EXTRA_CFLAGS=-DADXL34X_TEST_SPI + - DTC_OVERLAY_FILE="boards/qemu_cortex_m0-spi.dtsi" + extra_configs: + - CONFIG_ADXL34X_DECODER=y + - CONFIG_ADXL34X_DATA_TYPE_Q31=y + - CONFIG_ADXL34X_FIFO_MODE_STREAM=y + platform_allow: qemu_cortex_m0 + integration_platforms: + - qemu_cortex_m0 + build_only: true