From 1fb5da9bc569eded2dde26474e58b269004b1a06 Mon Sep 17 00:00:00 2001 From: Chaim Zax Date: Fri, 9 Aug 2024 13:06:15 +0200 Subject: [PATCH] drivers: sensor: adxl34x: Add sensor driver for ADXL3x motion sensors The current adxl345 driver doesn't support the extended driver features Zephyr has to offer, and only support the sensor sample_fetch and channel_get API. It also doesn't support adxl344 and adxl346 sensors. This new driver is a full replacement of the existing driver (using a compatibility flag) and, apart from the full driver API (adding support for attributes, triggers, RTIO and decoders), also support access to all registers of the adxl343, adxl344, adxl345 and adxl346 sensors. It also has power management support and comes with a full set of regression tests. The adxl345 driver API has an additional (unofficial) feature which uses the FIFO to return multiple values using a single sensor_sample_fetch command. To make the adxl34x driver a drop in replacement of the adxl345 driver an additional compile flag was introduced which mimics this behavior (default off). Both the adxl345 and adxl34x drivers have been tested using the various (application) use-cases to check functionality and behavior. Source code is available in a different repository. Signed-off-by: Chaim Zax --- drivers/sensor/adi/CMakeLists.txt | 1 + drivers/sensor/adi/Kconfig | 1 + drivers/sensor/adi/adxl34x/CMakeLists.txt | 20 + drivers/sensor/adi/adxl34x/Kconfig | 143 ++ drivers/sensor/adi/adxl34x/adxl34x.c | 391 +++ drivers/sensor/adi/adxl34x/adxl34x_attr.c | 394 +++ drivers/sensor/adi/adxl34x/adxl34x_attr.h | 19 + .../sensor/adi/adxl34x/adxl34x_configure.c | 2114 +++++++++++++++++ .../sensor/adi/adxl34x/adxl34x_configure.h | 18 + drivers/sensor/adi/adxl34x/adxl34x_convert.h | 60 + drivers/sensor/adi/adxl34x/adxl34x_decoder.c | 302 +++ drivers/sensor/adi/adxl34x/adxl34x_decoder.h | 79 + drivers/sensor/adi/adxl34x/adxl34x_emul.c | 639 +++++ drivers/sensor/adi/adxl34x/adxl34x_emul.h | 29 + drivers/sensor/adi/adxl34x/adxl34x_i2c.c | 88 + drivers/sensor/adi/adxl34x/adxl34x_i2c.h | 23 + drivers/sensor/adi/adxl34x/adxl34x_private.h | 94 + drivers/sensor/adi/adxl34x/adxl34x_reg.h | 164 ++ drivers/sensor/adi/adxl34x/adxl34x_rtio.c | 345 +++ drivers/sensor/adi/adxl34x/adxl34x_rtio.h | 15 + drivers/sensor/adi/adxl34x/adxl34x_spi.c | 134 ++ drivers/sensor/adi/adxl34x/adxl34x_spi.h | 28 + drivers/sensor/adi/adxl34x/adxl34x_trigger.c | 477 ++++ drivers/sensor/adi/adxl34x/adxl34x_trigger.h | 22 + dts/bindings/sensor/adi,adxl34x-common.yaml | 65 + dts/bindings/sensor/adi,adxl34x-i2c.yaml | 24 + dts/bindings/sensor/adi,adxl34x-spi.yaml | 23 + include/zephyr/drivers/sensor/adxl34x.h | 402 ++++ tests/drivers/build_all/sensor/app.overlay | 3 +- tests/drivers/build_all/sensor/i2c.dtsi | 11 + tests/drivers/build_all/sensor/spi.dtsi | 12 + tests/drivers/sensor/adxl34x/CMakeLists.txt | 25 + .../sensor/adxl34x/boards/adxl34x-i2c.dtsi | 34 + .../sensor/adxl34x/boards/adxl34x-i2c.overlay | 6 + .../sensor/adxl34x/boards/adxl34x-spi.dtsi | 37 + .../sensor/adxl34x/boards/adxl34x-spi.overlay | 6 + .../sensor/adxl34x/boards/native_sim.conf | 7 + .../sensor/adxl34x/boards/native_sim.overlay | 7 + .../adxl34x/boards/qemu_cortex_m0-i2c.dtsi | 30 + .../adxl34x/boards/qemu_cortex_m0-i2c.overlay | 6 + .../adxl34x/boards/qemu_cortex_m0-spi.dtsi | 61 + .../adxl34x/boards/qemu_cortex_m0-spi.overlay | 6 + .../sensor/adxl34x/boards/qemu_cortex_m0.conf | 9 + .../adxl34x/boards/qemu_cortex_m0.overlay | 7 + tests/drivers/sensor/adxl34x/prj.conf | 16 + .../sensor/adxl34x/src/adxl34x_basic.c | 527 ++++ .../sensor/adxl34x/src/adxl34x_decoder.c | 54 + .../sensor/adxl34x/src/adxl34x_extended.c | 60 + .../drivers/sensor/adxl34x/src/adxl34x_i2c.c | 59 + .../drivers/sensor/adxl34x/src/adxl34x_spi.c | 59 + .../sensor/adxl34x/src/adxl34x_streaming.c | 61 + .../drivers/sensor/adxl34x/src/adxl34x_test.c | 114 + .../drivers/sensor/adxl34x/src/adxl34x_test.h | 55 + tests/drivers/sensor/adxl34x/testcase.yaml | 116 + 54 files changed, 7501 insertions(+), 1 deletion(-) create mode 100644 drivers/sensor/adi/adxl34x/CMakeLists.txt create mode 100644 drivers/sensor/adi/adxl34x/Kconfig create mode 100644 drivers/sensor/adi/adxl34x/adxl34x.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_attr.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_attr.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_configure.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_configure.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_convert.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_decoder.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_decoder.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_emul.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_emul.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_i2c.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_i2c.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_private.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_reg.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_rtio.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_rtio.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_spi.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_spi.h create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_trigger.c create mode 100644 drivers/sensor/adi/adxl34x/adxl34x_trigger.h create mode 100644 dts/bindings/sensor/adi,adxl34x-common.yaml create mode 100644 dts/bindings/sensor/adi,adxl34x-i2c.yaml create mode 100644 dts/bindings/sensor/adi,adxl34x-spi.yaml create mode 100644 include/zephyr/drivers/sensor/adxl34x.h create mode 100644 tests/drivers/sensor/adxl34x/CMakeLists.txt create mode 100644 tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.dtsi create mode 100644 tests/drivers/sensor/adxl34x/boards/adxl34x-i2c.overlay create mode 100644 tests/drivers/sensor/adxl34x/boards/adxl34x-spi.dtsi create mode 100644 tests/drivers/sensor/adxl34x/boards/adxl34x-spi.overlay create mode 100644 tests/drivers/sensor/adxl34x/boards/native_sim.conf create mode 100644 tests/drivers/sensor/adxl34x/boards/native_sim.overlay create mode 100644 tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.dtsi create mode 100644 tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-i2c.overlay create mode 100644 tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.dtsi create mode 100644 tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0-spi.overlay create mode 100644 tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.conf create mode 100644 tests/drivers/sensor/adxl34x/boards/qemu_cortex_m0.overlay create mode 100644 tests/drivers/sensor/adxl34x/prj.conf create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_basic.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_decoder.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_extended.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_i2c.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_spi.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_streaming.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_test.c create mode 100644 tests/drivers/sensor/adxl34x/src/adxl34x_test.h create mode 100644 tests/drivers/sensor/adxl34x/testcase.yaml diff --git a/drivers/sensor/adi/CMakeLists.txt b/drivers/sensor/adi/CMakeLists.txt index cf2e79c2db61ec..de3e2dd53bffa1 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 76305d1fd2720f..a75623a6209057 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 00000000000000..bed190f91fccf7 --- /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 00000000000000..7ced4b751e4950 --- /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 00000000000000..fa72fb835a8b61 --- /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 00000000000000..10d4c127272c25 --- /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 00000000000000..add46d4d717a4d --- /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 00000000000000..598add738ae175 --- /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 00000000000000..587edfdf896a51 --- /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 00000000000000..b6596303acc404 --- /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 00000000000000..9c78caf612bd3c --- /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 00000000000000..a83ea5b8ed1d26 --- /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 00000000000000..3d90a4c0170649 --- /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 00000000000000..d7582e72e111af --- /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 00000000000000..11c777683a628a --- /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 00000000000000..d9ecdd65b7665a --- /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 00000000000000..7f5aea0d406308 --- /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 00000000000000..5973da2ed92a06 --- /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 00000000000000..111586567e51fb --- /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 00000000000000..9e1b521b9a32bb --- /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 00000000000000..6136170c4dedd9 --- /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 00000000000000..a3fcf0a9864771 --- /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 00000000000000..1a57f0d20d1239 --- /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 00000000000000..549d90a06190b2 --- /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 00000000000000..7a6c5082bf2fe4 --- /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 00000000000000..1951425c7fecd6 --- /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 00000000000000..9112a351d1c30d --- /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 00000000000000..beb7f36877413c --- /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 ebfa224848a32f..0b1a225300ed11 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 8bf865c14f21c1..ceb76f6ce982d8 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 c173c5ccecb61f..bfbc0158db1c22 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 00000000000000..e935798593cece --- /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 00000000000000..fdefb8fad88e17 --- /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 00000000000000..f36b85821e574a --- /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 00000000000000..2849a204a8f8ca --- /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 00000000000000..3131aabb2935d2 --- /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 00000000000000..908b878cac93d4 --- /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 00000000000000..0a30236b876c49 --- /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 00000000000000..a19b1a9b440447 --- /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 00000000000000..b85d37dad98a4e --- /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 00000000000000..e124dfff72abde --- /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 00000000000000..ae2c62299583c0 --- /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 00000000000000..8665e63d44e2ec --- /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 00000000000000..4531c2c996a1a4 --- /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 00000000000000..9cdbbe685d5f5d --- /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 00000000000000..30f73b79e2825e --- /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 00000000000000..edbe17f4f7e15b --- /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 00000000000000..7e55551e723ff1 --- /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 00000000000000..41dd857741d519 --- /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 00000000000000..4aa76c9efd6856 --- /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 00000000000000..8e4f885eb87299 --- /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 00000000000000..e48edf58c98d30 --- /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 00000000000000..db85b878908d49 --- /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 00000000000000..d7638abb580b25 --- /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