This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 3a6865aef50 drivers/sensors: Add QST QMI8658 6-axis IMU sensor support
3a6865aef50 is described below
commit 3a6865aef50b825a386229dd7ec67790d06d1c96
Author: Huang Qi <[email protected]>
AuthorDate: Sat Nov 29 21:53:48 2025 +0800
drivers/sensors: Add QST QMI8658 6-axis IMU sensor support
Add complete driver support for the QST QMI8658 6-axis IMU sensor featuring
3-axis accelerometer and 3-axis gyroscope with I2C interface and uORB
integration.
Key features implemented:
* Full I2C communication with configurable frequency (default 400kHz)
* Multiple accelerometer ranges (±2g, ±4g, ±8g, ±16g) and ODR settings
* Multiple gyroscope ranges (±16 to ±1024 dps) with high ODR support
* Low-pass filter configuration for both sensors
* Temperature sensing with 16-bit resolution
* Self-test capability for both accelerometer and gyroscope
* Calibration-on-demand support with offset registers
* FIFO buffer management (framework ready)
* Interrupt-driven and polling mode support
* Complete uORB integration with sensor_accel and sensor_gyro topics
Driver components added:
* Core driver implementation (qmi8658_uorb.c) with register operations
* Header file with register definitions and scale factors (qmi8658.h)
* Kconfig options for driver configuration and polling mode
* Build system integration (CMakeLists.txt, Make.defs)
* Comprehensive documentation with API reference and usage examples
The driver follows NuttX sensor framework conventions and provides
robust error handling, mutex protection, and comprehensive debugging
support through CONFIG_DEBUG_SENSORS.
Signed-off-by: Huang Qi <[email protected]>
---
.../components/drivers/special/sensors.rst | 1 +
.../components/drivers/special/sensors/qmi8658.rst | 232 +++
.../drivers/special/sensors/sensors_uorb.rst | 1 +
drivers/sensors/CMakeLists.txt | 6 +
drivers/sensors/Kconfig | 31 +
drivers/sensors/Make.defs | 6 +
drivers/sensors/qmi8658_uorb.c | 1750 ++++++++++++++++++++
include/nuttx/sensors/qmi8658.h | 132 ++
8 files changed, 2159 insertions(+)
diff --git a/Documentation/components/drivers/special/sensors.rst
b/Documentation/components/drivers/special/sensors.rst
index c1857b90506..064dcf077b3 100644
--- a/Documentation/components/drivers/special/sensors.rst
+++ b/Documentation/components/drivers/special/sensors.rst
@@ -30,6 +30,7 @@ general interface.
sensors/mcp9600.rst
sensors/mpl115a.rst
sensors/nau7802.rst
+ sensors/qmi8658.rst
sensors/sht4x.rst
sensors/lsm6dso32.rst
sensors/lis2mdl.rst
diff --git a/Documentation/components/drivers/special/sensors/qmi8658.rst
b/Documentation/components/drivers/special/sensors/qmi8658.rst
new file mode 100644
index 00000000000..cb11a46ce69
--- /dev/null
+++ b/Documentation/components/drivers/special/sensors/qmi8658.rst
@@ -0,0 +1,232 @@
+=======
+QMI8658
+=======
+
+The QMI8658 is a high-performance 6-axis IMU sensor by QST featuring a 3-axis
+accelerometer and 3-axis gyroscope. It supports both I2C and SPI interfaces,
+although this driver currently supports I2C communication only.
+
+This driver uses the :doc:`uorb
+</components/drivers/special/sensors/sensors_uorb>` interface. It supports the
+self-test capability for both the accelerometer and gyroscope.
+
+The driver supports comprehensive features including multiple full-scale
ranges,
+configurable ODR settings, low-pass filters, FIFO buffer management,
temperature
+sensing, and device calibration.
+
+.. note::
+ The QMI8658 is a feature-rich sensor with advanced capabilities like
+ tap detection, motion detection, and various low-power modes. This driver
+ implements the core functionality for accelerometer and gyroscope data
+ acquisition with room for future feature extensions.
+
+Application Programming Interface
+=================================
+
+.. code-block:: c
+
+ #include <nuttx/sensors/qmi8658.h>
+
+The QMI8658 driver provides one registration function:
+
+- **uORB Interface**: ``qmi8658_uorb_register()``
+
+Registration
+------------
+
+The uORB interface registers the driver and creates separate uORB topics for
+accelerometer and gyroscope data under ``/dev/uorb/``: ``sensor_accel<n>`` and
+``sensor_gyro<n>``, where ``n`` is the device number.
+
+.. code-block:: c
+
+ /* Example uORB registration */
+
+ ret = qmi8658_uorb_register(0, i2c_bus, QMI8658_I2C_ADDR_DEFAULT);
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "Couldn't register QMI8658 uORB: %d\n", ret);
+ return ret;
+ }
+
+The uORB interface registers the driver and creates separate uORB topics for
+accelerometer and gyroscope data under ``/dev/uorb/``: ``sensor_accel<n>`` and
+``sensor_gyro<n>``, where ``n`` is the device number.
+
+.. code-block:: c
+
+ /* Example uORB registration */
+
+ ret = qmi8658_uorb_register(0, i2c_bus, QMI8658_I2C_ADDR_DEFAULT);
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "Couldn't register QMI8658 uORB: %d\n", ret);
+ return ret;
+ }
+
+Configuration Options
+=====================
+
+The QMI8658 driver supports several Kconfig options:
+
+* ``CONFIG_SENSORS_QMI8658``: Enable QMI8658 driver support (requires UORB)
+* ``CONFIG_SENSORS_QMI8658_POLL``: Enable polling mode with configurable
interval
+* ``CONFIG_SENSORS_QMI8658_POLL_INTERVAL``: Set polling interval (default 1s)
+* ``CONFIG_QMI8658_I2C_FREQUENCY``: Set I2C communication frequency (default
400kHz)
+
+Supported Features
+==================
+
+Accelerometer
+-------------
+
+* **Full-Scale Ranges**: ±2g, ±4g, ±8g, ±16g
+* **Output Data Rates**: 1000Hz, 500Hz, 250Hz, 125Hz, 62.5Hz, 31.25Hz
+* **Low-Power ODR**: 128Hz, 21Hz, 11Hz, 3Hz
+* **Low-Pass Filters**: 4 different modes + OFF
+
+Gyroscope
+---------
+
+* **Full-Scale Ranges**: ±16, ±32, ±64, ±128, ±256, ±512, ±1024 dps
+* **Output Data Rates**: 7174.4Hz, 3587.2Hz, 1793.6Hz, 896.8Hz, 448.4Hz,
224.2Hz, 112.1Hz, 56.05Hz, 28.025Hz
+* **Low-Pass Filters**: 4 different modes + OFF
+
+Additional Features
+-----------------==
+
+* **Temperature Sensor**: 16-bit temperature data with 256 LSB/°C scale factor
+* **FIFO Buffer**: Configurable FIFO with watermark and interrupt support
+* **Sampling Modes**: Synchronous and asynchronous sampling
+* **Interrupt Support**: Data ready, FIFO watermark, motion detection
+* **Self-Test**: Built-in self-test capability for both sensors
+* **Calibration**: On-demand calibration support
+
+IOCTL Commands
+==============
+
+uORB IOCTLs
+-----------
+
+* ``SNIOC_SETFULLSCALE``: Set full-scale range (argument in g for accel, dps
for gyro)
+* ``SNIOC_SET_CALIBVALUE``: Set calibration offsets
+* ``SNIOC_SELFTEST``: Perform sensor self-test
+* ``SNIOC_WHO_AM_I``: Read device ID (should return 0x05)
+
+Scale Factors
+=============
+
+The driver provides predefined scale factors for converting raw sensor data
+to physical units:
+
+Accelerometer Scale Factors (LSB/g):
+
+.. code-block:: c
+
+ #define QMI8658_ACC_SCALE_2G (16384.0f)
+ #define QMI8658_ACC_SCALE_4G (8192.0f)
+ #define QMI8658_ACC_SCALE_8G (4096.0f)
+ #define QMI8658_ACC_SCALE_16G (2048.0f)
+
+Gyroscope Scale Factors (LSB/dps):
+
+.. code-block:: c
+
+ #define QMI8658_GYRO_SCALE_16DPS (2048.0f)
+ #define QMI8658_GYRO_SCALE_32DPS (1024.0f)
+ #define QMI8658_GYRO_SCALE_64DPS (512.0f)
+ #define QMI8658_GYRO_SCALE_128DPS (256.0f)
+ #define QMI8658_GYRO_SCALE_256DPS (128.0f)
+ #define QMI8658_GYRO_SCALE_512DPS (64.0f)
+ #define QMI8658_GYRO_SCALE_1024DPS (32.0f)
+
+Temperature Scale Factor (LSB/°C):
+
+.. code-block:: c
+
+ #define QMI8658_TEMP_SCALE (256.0f)
+
+Data Conversion
+===============
+
+To convert raw sensor data to physical units:
+
+.. code-block:: c
+
+ /* Convert accelerometer raw data to g */
+
+ float accel_x_g = (float)raw_accel_x / QMI8658_ACC_SCALE_8G;
+ float accel_y_g = (float)raw_accel_y / QMI8658_ACC_SCALE_8G;
+ float accel_z_g = (float)raw_accel_z / QMI8658_ACC_SCALE_8G;
+
+ /* Convert gyroscope raw data to dps */
+
+ float gyro_x_dps = (float)raw_gyro_x / QMI8658_GYRO_SCALE_512DPS;
+ float gyro_y_dps = (float)raw_gyro_y / QMI8658_GYRO_SCALE_512DPS;
+ float gyro_z_dps = (float)raw_gyro_z / QMI8658_GYRO_SCALE_512DPS;
+
+ /* Convert temperature raw data to °C */
+
+ float temp_c = (float)raw_temp / QMI8658_TEMP_SCALE;
+
+Debugging and Testing
+=====================
+
+To debug the QMI8658 device, you can:
+
+1. **Enable Debug Output**: Set ``CONFIG_DEBUG_SENSORS`` and
``CONFIG_DEBUG_INFO``
+2. **Use uORB Listener**: Include ``uorb_listener`` application to monitor
sensor data
+
+Performance Considerations
+==========================
+
+* **I2C Frequency**: Default 400kHz, configurable via
``CONFIG_QMI8658_I2C_FREQUENCY``
+* **Polling Overhead**: Use interrupt-driven mode when possible for better
efficiency
+* **FIFO Usage**: Enable FIFO to reduce I2C traffic and CPU overhead
+* **Power Management**: Utilize low-power ODR settings for battery-powered
applications
+
+Limitations
+===========
+
+* Currently supports I2C interface only (SPI support can be added)
+* Advanced features like tap detection and motion detection not yet implemented
+* FIFO interrupt handling not fully implemented
+* Some low-power modes require additional configuration
+
+Hardware Connections
+====================
+
+I2C Interface
+--------------
+
+* **VDD**: Power supply (1.71V to 3.6V)
+* **GND**: Ground
+* **SDA/SDI**: I2C Serial Data
+* **SCL/SCLK**: I2C Serial Clock
+* **CS**: Chip Select (connect to VDD for I2C mode)
+* **INT1/INT2**: Interrupt pins (optional)
+
+Typical I2C addresses:
+* **Primary**: 0x6B
+* **Secondary**: 0x6D (when SDO/SA0 pin is high)
+
+.. note::
+ Ensure proper pull-up resistors on SDA and SCL lines (typically 4.7kΩ for
3.3V).
+
+Troubleshooting
+===============
+
+**Device Not Responding**
+* Check I2C address and connections
+* Verify power supply voltage
+* Ensure CS pin is properly configured for I2C mode
+
+**Incorrect Readings**
+* Verify full-scale range settings
+* Check scale factor calculations
+* Ensure sensor is properly calibrated
+
+**Communication Errors**
+* Reduce I2C frequency
+* Check for signal integrity issues
+* Verify pull-up resistor values
diff --git a/Documentation/components/drivers/special/sensors/sensors_uorb.rst
b/Documentation/components/drivers/special/sensors/sensors_uorb.rst
index 75187b66541..ebdb66e55c8 100644
--- a/Documentation/components/drivers/special/sensors/sensors_uorb.rst
+++ b/Documentation/components/drivers/special/sensors/sensors_uorb.rst
@@ -496,6 +496,7 @@ Implemented Drivers
- mpu9250
- ms56xx
- :doc:`nau7802`
+- :doc:`qmi8658`
- :doc:`sht4x`
- :doc:`lsm6dso32`
- wtgahrs2
diff --git a/drivers/sensors/CMakeLists.txt b/drivers/sensors/CMakeLists.txt
index b3ca24e07b0..2a90d7555dd 100644
--- a/drivers/sensors/CMakeLists.txt
+++ b/drivers/sensors/CMakeLists.txt
@@ -344,6 +344,12 @@ if(CONFIG_SENSORS)
list(APPEND SRCS tmp112.c)
endif()
+ # QMI8658 6-axis IMU
+
+ if(CONFIG_SENSORS_QMI8658)
+ list(APPEND SRCS qmi8658_uorb.c)
+ endif()
+
endif() # CONFIG_I2C
# These drivers depend on SPI support
diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig
index f78e3449b91..8cbd9f0458e 100644
--- a/drivers/sensors/Kconfig
+++ b/drivers/sensors/Kconfig
@@ -2152,4 +2152,35 @@ config TMP112_I2C_FREQUENCY
endif #SENSORS_TMP112
+config SENSORS_QMI8658
+ bool "QST QMI8658 6-Axis IMU Sensor support"
+ default n
+ depends on I2C && UORB
+ ---help---
+ Enable driver support for the QST QMI8658 6-axis IMU sensor.
+
+if SENSORS_QMI8658
+
+config SENSORS_QMI8658_POLL
+ bool "Enables polling sensor data"
+ default n
+ ---help---
+ Enables polling of sensor data.
+
+config SENSORS_QMI8658_POLL_INTERVAL
+ int "Polling interval in microseconds, default 1s"
+ depends on SENSORS_QMI8658_POLL
+ default 1000000
+ range 0 4294967295
+ ---help---
+ The interval until a new sensor measurement will be triggered.
+
+config QMI8658_I2C_FREQUENCY
+ int "QMI8658 I2C frequency"
+ default 400000
+ ---help---
+ I2C frequency used to communicate with QMI8658.
+
+endif # SENSORS_QMI8658
+
endif # SENSORS
diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs
index 870afeb40df..c4505b7941d 100644
--- a/drivers/sensors/Make.defs
+++ b/drivers/sensors/Make.defs
@@ -418,6 +418,12 @@ ifeq ($(CONFIG_SENSORS_MPU9250),y)
CSRCS += mpu9250_uorb.c
endif
+# QMI8658 6-axis IMU
+
+ifeq ($(CONFIG_SENSORS_QMI8658),y)
+ CSRCS += qmi8658_uorb.c
+endif
+
# Quadrature encoder upper half
ifeq ($(CONFIG_SENSORS_QENCODER),y)
diff --git a/drivers/sensors/qmi8658_uorb.c b/drivers/sensors/qmi8658_uorb.c
new file mode 100644
index 00000000000..3d091f63f51
--- /dev/null
+++ b/drivers/sensors/qmi8658_uorb.c
@@ -0,0 +1,1750 @@
+/****************************************************************************
+ * drivers/sensors/qmi8658_uorb.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/mutex.h>
+#include <nuttx/signal.h>
+#include <nuttx/compiler.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/sensors/sensor.h>
+#include <nuttx/sensors/ioctl.h>
+#include <nuttx/sensors/qmi8658.h>
+#include <debug.h>
+#include <errno.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* QMI8658 Register Addresses */
+#define QMI8658_REG_WHOAMI (0x00) /* Chip ID register */
+#define QMI8658_REG_REVISION (0x01) /* Revision register */
+#define QMI8658_REG_CTRL1 (0x02) /* Control register 1 */
+#define QMI8658_REG_CTRL2 (0x03) /* Control register 2
(Accelerometer) */
+#define QMI8658_REG_CTRL3 (0x04) /* Control register 3 (Gyroscope) */
+#define QMI8658_REG_CTRL5 (0x06) /* Control register 5 (LPF settings)
*/
+#define QMI8658_REG_CTRL7 (0x08) /* Control register 7 (Sensor
enable) */
+#define QMI8658_REG_CTRL8 (0x09) /* Control register 8 (Motion
detection) */
+#define QMI8658_REG_CTRL9 (0x0A) /* Control register 9 (Commands) */
+#define QMI8658_REG_CAL1_L (0x0B) /* Calibration register 1 low */
+#define QMI8658_REG_CAL1_H (0x0C) /* Calibration register 1 high */
+#define QMI8658_REG_CAL2_L (0x0D) /* Calibration register 2 low */
+#define QMI8658_REG_CAL2_H (0x0E) /* Calibration register 2 high */
+#define QMI8658_REG_CAL3_L (0x0F) /* Calibration register 3 low */
+#define QMI8658_REG_CAL3_H (0x10) /* Calibration register 3 high */
+#define QMI8658_REG_CAL4_L (0x11) /* Calibration register 4 low */
+#define QMI8658_REG_CAL4_H (0x12) /* Calibration register 4 high */
+#define QMI8658_REG_FIFO_WTM_TH (0x13) /* FIFO watermark threshold */
+#define QMI8658_REG_FIFO_CTRL (0x14) /* FIFO control */
+#define QMI8658_REG_FIFO_COUNT (0x15) /* FIFO sample count */
+#define QMI8658_REG_FIFO_STATUS (0x16) /* FIFO status */
+#define QMI8658_REG_FIFO_DATA (0x17) /* FIFO data */
+#define QMI8658_REG_STATUS_INT (0x2D) /* Status interrupt */
+#define QMI8658_REG_STATUS0 (0x2E) /* Status register 0 */
+#define QMI8658_REG_STATUS1 (0x2F) /* Status register 1 */
+#define QMI8658_REG_TIMESTAMP_L (0x30) /* Timestamp low */
+#define QMI8658_REG_TIMESTAMP_M (0x31) /* Timestamp middle */
+#define QMI8658_REG_TIMESTAMP_H (0x32) /* Timestamp high */
+#define QMI8658_REG_TEMPERATURE_L (0x33) /* Temperature low */
+#define QMI8658_REG_TEMPERATURE_H (0x34) /* Temperature high */
+#define QMI8658_REG_AX_L (0x35) /* Accelerometer X low */
+#define QMI8658_REG_AX_H (0x36) /* Accelerometer X high */
+#define QMI8658_REG_AY_L (0x37) /* Accelerometer Y low */
+#define QMI8658_REG_AY_H (0x38) /* Accelerometer Y high */
+#define QMI8658_REG_AZ_L (0x39) /* Accelerometer Z low */
+#define QMI8658_REG_AZ_H (0x3A) /* Accelerometer Z high */
+#define QMI8658_REG_GX_L (0x3B) /* Gyroscope X low */
+#define QMI8658_REG_GX_H (0x3C) /* Gyroscope X high */
+#define QMI8658_REG_GY_L (0x3D) /* Gyroscope Y low */
+#define QMI8658_REG_GY_H (0x3E) /* Gyroscope Y high */
+#define QMI8658_REG_GZ_L (0x3F) /* Gyroscope Z low */
+#define QMI8658_REG_GZ_H (0x40) /* Gyroscope Z high */
+#define QMI8658_REG_COD_STATUS (0x46) /* Calibration-on-demand status */
+#define QMI8658_REG_DQW_L (0x49) /* COD quaternion W low */
+#define QMI8658_REG_DQW_H (0x4A) /* COD quaternion W high */
+#define QMI8658_REG_DQX_L (0x4B) /* COD quaternion X low */
+#define QMI8658_REG_DQX_H (0x4C) /* COD quaternion X high */
+#define QMI8658_REG_RST_RESULT (0x4D) /* Reset result register */
+#define QMI8658_REG_DQY_L (0x4E) /* COD quaternion Y low */
+#define QMI8658_REG_DQY_H (0x4F) /* COD quaternion Y high */
+#define QMI8658_REG_DQZ_L (0x50) /* COD quaternion Z low */
+#define QMI8658_REG_DQZ_H (0x51) /* COD quaternion Z high */
+#define QMI8658_REG_DVX_L (0x52) /* Self-test X low */
+#define QMI8658_REG_DVX_H (0x53) /* Self-test X high */
+#define QMI8658_REG_DVY_L (0x54) /* Self-test Y low */
+#define QMI8658_REG_DVY_H (0x55) /* Self-test Y high */
+#define QMI8658_REG_DVZ_L (0x56) /* Self-test Z low */
+#define QMI8658_REG_DVZ_H (0x57) /* Self-test Z high */
+#define QMI8658_REG_TAP_STATUS (0x59) /* Tap status */
+#define QMI8658_REG_STEP_CNT_LOW (0x5A) /* Step counter low */
+#define QMI8658_REG_STEP_CNT_MID (0x5B) /* Step counter middle */
+#define QMI8658_REG_STEP_CNT_HIGH (0x5C) /* Step counter high */
+#define QMI8658_REG_RESET (0x60) /* Reset register */
+
+/* Default values */
+#define QMI8658_REG_WHOAMI_DEFAULT (0x05)
+#define QMI8658_REG_STATUS_DEFAULT (0x03)
+#define QMI8658_REG_RESET_DEFAULT (0xB0)
+#define QMI8658_REG_RST_RESULT_VAL (0x80)
+
+/* Control register bit definitions */
+
+/* CTRL1 - Control Register 1 */
+#define QMI8658_CTRL1_ACC_EN (1 << 0)
+#define QMI8658_CTRL1_POWER_DOWN (1 << 1)
+#define QMI8658_CTRL1_FIFO_INT_EN (1 << 2)
+#define QMI8658_CTRL1_INT1_EN (1 << 3)
+#define QMI8658_CTRL1_INT2_EN (1 << 4)
+#define QMI8658_CTRL1_ADDR_AI_EN (1 << 6)
+
+/* CTRL5 - Control Register 5 (Low-pass filters */
+#define QMI8658_ACCEL_LPF_MASK (0xF9)
+#define QMI8658_GYRO_LPF_MASK (0x9F)
+
+/* CTRL7 - Control Register 7 (Enable/Disable) */
+#define QMI8658_CTRL7_ACC_EN (1 << 0) /* Accelerometer enable */
+#define QMI8658_CTRL7_GYRO_EN (1 << 1) /* Gyroscope enable */
+#define QMI8658_CTRL7_RESERVED (0x7C) /* Reserved bits 2-6 */
+#define QMI8658_CTRL7_SYNC_MODE (1 << 7) /* Synchronization mode */
+
+/* CTRL7 bit masks */
+#define QMI8658_CTRL7_ACC_EN_MASK (0x01) /* Accelerometer enable mask */
+#define QMI8658_CTRL7_GYRO_EN_MASK (0x02) /* Gyroscope enable mask */
+#define QMI8658_CTRL7_EN_MASK (0x03) /* Enable bits mask */
+
+/* CTRL9 - Control Register 9 (Commands) */
+#define QMI8658_CTRL9_CMD_ACK (0x00) /* Acknowledge command */
+#define QMI8658_CTRL9_CMD_RST_FIFO (0x04) /* Reset FIFO command */
+#define QMI8658_CTRL9_CMD_REQ_FIFO (0x05) /* Request FIFO data command */
+#define QMI8658_CTRL9_CMD_CALIB (0xA2) /* Calibration command */
+
+/* STATUS0 - Status Register 0 (Data ready status) */
+#define QMI8658_STATUS0_ACC_DRDY (1 << 0) /* Accelerometer data ready */
+#define QMI8658_STATUS0_GYRO_DRDY (1 << 1) /* Gyroscope data ready */
+#define QMI8658_STATUS0_RESERVED (0xFC) /* Reserved bits 2-7 */
+
+/* STATUS_INT - Status Interrupt Register */
+#define QMI8658_STATUS_INT_AVAIL (1 << 0) /* Interrupt available */
+#define QMI8658_STATUS_INT_LOCKED (1 << 1) /* Interrupt locked */
+#define QMI8658_STATUS_INT_RESERVED (0x7C) /* Reserved bits 2-6 */
+#define QMI8658_STATUS_INT_CMD_DONE (1 << 7) /* Command done interrupt */
+
+/* Legacy compatibility definitions */
+#define STATUS0_ACCEL_AVAIL QMI8658_STATUS0_ACC_DRDY
+#define STATUS0_GYRO_AVAIL QMI8658_STATUS0_GYRO_DRDY
+#define STATUS_INT_CTRL9_CMD_DONE QMI8658_STATUS_INT_CMD_DONE
+#define STATUS_INT_LOCKED QMI8658_STATUS_INT_LOCKED
+#define STATUS_INT_AVAIL QMI8658_STATUS_INT_AVAIL
+
+/* Low-Pass Filter Modes */
+#define QMI8658_LPF_MODE_0 (0x00)
+#define QMI8658_LPF_MODE_1 (0x01)
+#define QMI8658_LPF_MODE_2 (0x02)
+#define QMI8658_LPF_MODE_3 (0x03)
+#define QMI8658_LPF_OFF (0x04)
+
+/* Sample Synchronization Modes */
+#define QMI8658_SYNC_MODE (0x00)
+#define QMI8658_ASYNC_MODE (0x01)
+
+/* Scale factors are defined in nuttx/sensors/qmi8658.h */
+
+/* Sensor indices */
+
+enum qmi8658_idx_e
+{
+ QMI8658_ACCEL_IDX = 0,
+ QMI8658_GYRO_IDX,
+ QMI8658_MAX_IDX
+};
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* QMI8658 configuration structure */
+
+struct qmi8658_config_s
+{
+ uint8_t acc_range;
+ uint8_t gyro_range;
+ uint8_t acc_enable;
+ uint8_t gyro_enable;
+ uint8_t temp_enable;
+ uint8_t lp_mode;
+};
+
+/* Scale factors structure */
+
+struct qmi8658_scale_factors_s
+{
+ float acc_scale;
+ float gyro_scale;
+ float temp_scale;
+};
+
+struct qmi8658_dev_s
+{
+ FAR struct i2c_master_s *i2c;
+ uint8_t addr;
+ int freq;
+
+ uint8_t acc_range;
+ uint8_t gyro_range;
+ uint8_t acc_odr;
+ uint8_t gyro_odr;
+ uint8_t acc_lpf;
+ uint8_t gyro_lpf;
+ uint8_t sample_mode;
+
+ mutex_t dev_lock;
+
+ bool accel_enabled;
+ bool gyro_enabled;
+};
+
+struct qmi8658_sensor_s
+{
+ struct sensor_lowerhalf_s lower;
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+ struct work_s work;
+ uint32_t interval;
+#endif
+ float scale;
+ FAR struct qmi8658_dev_s *dev;
+ bool enabled;
+};
+
+struct qmi8658_uorb_dev_s
+{
+ struct qmi8658_dev_s base;
+ struct qmi8658_sensor_s priv[QMI8658_MAX_IDX];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* I2C Register Operations */
+
+static int qmi8658_readreg8(FAR struct qmi8658_dev_s *priv, uint8_t regaddr,
+ FAR uint8_t *regval);
+static int qmi8658_writereg8(FAR struct qmi8658_dev_s *priv, uint8_t regaddr,
+ uint8_t regval);
+static int qmi8658_modifyreg8(FAR struct qmi8658_dev_s *priv,
+ uint8_t regaddr, uint8_t clearbits,
+ uint8_t setbits);
+static int qmi8658_readregs(FAR struct qmi8658_dev_s *priv, uint8_t regaddr,
+ FAR uint8_t *buffer, uint8_t len);
+
+/* Basic Operations */
+
+static int qmi8658_checkid(FAR struct qmi8658_dev_s *priv);
+static int qmi8658_reset(FAR struct qmi8658_dev_s *priv);
+
+/* Sensor Configuration */
+
+static int qmi8658_config_accelerometer(FAR struct qmi8658_dev_s *priv,
+ uint8_t range,
+ uint8_t odr,
+ uint8_t lpf_mode);
+static int qmi8658_config_gyroscope(FAR struct qmi8658_dev_s *priv,
+ uint8_t range,
+ uint8_t odr,
+ uint8_t lpf_mode);
+
+/* Sensor Control */
+
+static int qmi8658_set_accelerometer(FAR struct qmi8658_dev_s *priv,
+ bool enable);
+static int qmi8658_set_gyroscope(FAR struct qmi8658_dev_s *priv,
+ bool enable);
+
+/* Sampling Mode */
+
+static int qmi8658_set_sample_mode(FAR struct qmi8658_dev_s *priv,
+ bool sync);
+
+/* Data Reading Functions */
+
+static int qmi8658_read_accel(FAR struct qmi8658_dev_s *priv,
+ FAR int16_t *data);
+static int qmi8658_read_gyro(FAR struct qmi8658_dev_s *priv,
+ FAR int16_t *data);
+static int qmi8658_read_temp(FAR struct qmi8658_dev_s *priv,
+ FAR int16_t *temperature);
+
+/* Scale Factors Functions */
+
+static int qmi8658_get_scale_factors(FAR struct qmi8658_dev_s *priv,
+ FAR struct qmi8658_scale_factors_s *factors);
+
+/* Initialization */
+
+static int qmi8658_initialize(FAR struct qmi8658_dev_s *priv);
+
+/* Sensor methods */
+
+static int qmi8658_activate(FAR struct sensor_lowerhalf_s *lower,
+ FAR struct file *filep, bool enable);
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+static int qmi8658_set_interval(FAR struct sensor_lowerhalf_s *lower,
+ FAR struct file *filep,
+ FAR uint32_t *period_us);
+#else
+static int qmi8658_fetch(FAR struct sensor_lowerhalf_s *lower,
+ FAR struct file *filep,
+ FAR char *buffer, size_t buflen);
+#endif
+
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+static void qmi8658_accel_worker(FAR void *arg);
+static void qmi8658_gyro_worker(FAR void *arg);
+#endif
+
+static int qmi8658_read_imu(FAR struct qmi8658_dev_s *dev,
+ FAR struct sensor_accel *accel,
+ FAR struct sensor_gyro *gyro);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct sensor_ops_s g_sensor_ops =
+{
+ NULL, /* open */
+ NULL, /* close */
+ qmi8658_activate, /* activate */
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+ qmi8658_set_interval, /* set_interval */
+#else
+ NULL, /* set_interval */
+#endif
+ NULL, /* batch */
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+ NULL, /* fetch */
+#else
+ qmi8658_fetch, /* fetch */
+#endif
+ NULL, /* flush */
+ NULL, /* selftest */
+ NULL, /* set_calibvalue */
+ NULL, /* calibrate */
+ NULL, /* get_info */
+ NULL, /* control */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qmi8658_readreg8
+ *
+ * Description:
+ * Read from an 8-bit QMI8658 register
+ *
+ ****************************************************************************/
+
+static int qmi8658_readreg8(FAR struct qmi8658_dev_s *priv, uint8_t regaddr,
+ FAR uint8_t *regval)
+{
+ struct i2c_config_s config;
+ int ret;
+
+ sninfo("Reading reg 0x%02x (addr=0x%02x, freq=%d)\n",
+ regaddr, priv->addr, priv->freq);
+
+ config.frequency = priv->freq;
+ config.address = priv->addr;
+ config.addrlen = 7;
+
+ ret = i2c_write(priv->i2c, &config, ®addr, 1);
+ if (ret < 0)
+ {
+ snerr(" i2c_write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_read(priv->i2c, &config, regval, 1);
+ if (ret < 0)
+ {
+ snerr(" i2c_read failed: %d\n", ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_writereg8
+ *
+ * Description:
+ * Write to an 8-bit QMI8658 register
+ *
+ ****************************************************************************/
+
+static int qmi8658_writereg8(FAR struct qmi8658_dev_s *priv, uint8_t regaddr,
+ uint8_t regval)
+{
+ struct i2c_config_s config;
+ uint8_t data[2];
+ int ret;
+
+ sninfo("Writing reg 0x%02x = 0x%02x (addr=0x%02x, freq=%d)\n",
+ regaddr, regval, priv->addr, priv->freq);
+
+ config.frequency = priv->freq;
+ config.address = priv->addr;
+ config.addrlen = 7;
+
+ data[0] = regaddr;
+ data[1] = regval;
+
+ ret = i2c_write(priv->i2c, &config, data, 2);
+ if (ret < 0)
+ {
+ snerr(" i2c_write failed: %d\n", ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_modifyreg8
+ *
+ * Description:
+ * Modify an 8-bit QMI8658 register (clear and set bits)
+ *
+ ****************************************************************************/
+
+static int qmi8658_modifyreg8(FAR struct qmi8658_dev_s *priv,
+ uint8_t regaddr, uint8_t clearbits,
+ uint8_t setbits)
+{
+ uint8_t regval;
+ int ret;
+
+ ret = qmi8658_readreg8(priv, regaddr, ®val);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ regval &= ~clearbits;
+ regval |= setbits;
+
+ ret = qmi8658_writereg8(priv, regaddr, regval);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_readregs
+ *
+ * Description:
+ * Read multiple consecutive registers using address auto-increment
+ *
+ ****************************************************************************/
+
+static int qmi8658_readregs(FAR struct qmi8658_dev_s *priv, uint8_t regaddr,
+ FAR uint8_t *buffer, uint8_t len)
+{
+ struct i2c_config_s config;
+ int ret;
+
+ sninfo("Reading %d registers starting from 0x%02x "
+ "(addr=0x%02x, freq=%d)\n",
+ len, regaddr, priv->addr, priv->freq);
+
+ config.frequency = priv->freq;
+ config.address = priv->addr;
+ config.addrlen = 7;
+
+ ret = i2c_write(priv->i2c, &config, ®addr, 1);
+ if (ret < 0)
+ {
+ snerr(" i2c_write failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Read multiple consecutive registers (auto-increment enabled) */
+
+ ret = i2c_read(priv->i2c, &config, buffer, len);
+ if (ret < 0)
+ {
+ snerr(" i2c_read failed: %d\n", ret);
+ return ret;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_checkid
+ *
+ * Description:
+ * Check if the QMI8658 chip ID is correct
+ *
+ ****************************************************************************/
+
+static int qmi8658_checkid(FAR struct qmi8658_dev_s *priv)
+{
+ uint8_t chip_id;
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ ret = qmi8658_readreg8(priv, QMI8658_REG_WHOAMI, &chip_id);
+ if (ret < 0)
+ {
+ snerr(" Failed to read chip ID\n");
+ goto err_unlock;
+ }
+
+ if (chip_id != QMI8658_REG_WHOAMI_DEFAULT)
+ {
+ snerr(" Invalid chip ID: 0x%02x (expected 0x%02x)\n",
+ chip_id, QMI8658_REG_WHOAMI_DEFAULT);
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ sninfo("QMI8658 chip ID verified: 0x%02x\n", chip_id);
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_reset
+ *
+ * Description:
+ * Reset the QMI8658 sensor with proper verification
+ *
+ ****************************************************************************/
+
+static int qmi8658_reset(FAR struct qmi8658_dev_s *priv)
+{
+ int ret;
+ int ret_lock;
+ uint8_t chip_id;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ /* Issue device reset command */
+
+ ret = qmi8658_writereg8(priv, QMI8658_REG_RESET,
+ QMI8658_REG_RESET_DEFAULT);
+ if (ret < 0)
+ {
+ snerr("Failed to write reset command to QMI8658: %d\n", ret);
+ goto err_unlock;
+ }
+
+ /* Wait for reset to complete (15ms minimum delay per datasheet) */
+
+ up_mdelay(15);
+
+ /* Verify device responsiveness after reset */
+
+ ret = qmi8658_readreg8(priv, QMI8658_REG_WHOAMI, &chip_id);
+ if (ret < 0)
+ {
+ snerr("Failed to read chip ID after reset: %d\n", ret);
+ goto err_unlock;
+ }
+
+ if (chip_id != QMI8658_REG_WHOAMI_DEFAULT)
+ {
+ snerr("Invalid chip ID after reset: 0x%02x (expected "
+ "0x%02x)\n", chip_id, QMI8658_REG_WHOAMI_DEFAULT);
+ ret = -EIO;
+ goto err_unlock;
+ }
+
+ /* Enable address auto-increment */
+
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL1, 0,
+ QMI8658_CTRL1_ADDR_AI_EN);
+ if (ret < 0)
+ {
+ snerr("Failed to enable address auto-increment: %d\n", ret);
+ goto err_unlock;
+ }
+
+ sninfo("QMI8658 reset completed successfully (chip ID: 0x%02x)\n",
+ chip_id);
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_config_accelerometer
+ *
+ * Description:
+ * Configure accelerometer settings (range, ODR, low-pass filter)
+ *
+ ****************************************************************************/
+
+static int qmi8658_config_accelerometer(FAR struct qmi8658_dev_s *priv,
+ uint8_t range, uint8_t odr,
+ uint8_t lpf_mode)
+{
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ sninfo("Configuring accelerometer range: %d (0x%02x)\n",
+ range, range << 4);
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL2, 0x30,
+ (range << 4));
+ if (ret < 0)
+ {
+ snerr(" Failed to configure accelerometer range\n");
+ goto err_unlock;
+ }
+
+ priv->acc_range = range;
+
+ sninfo("Configuring accelerometer ODR: %d (0x%02x)\n", odr, odr);
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL2, 0x0f, odr);
+ if (ret < 0)
+ {
+ snerr(" Failed to configure accelerometer ODR\n");
+ goto err_unlock;
+ }
+
+ uint8_t ctrl2_odr_val;
+ qmi8658_readreg8(priv, QMI8658_REG_CTRL2, &ctrl2_odr_val);
+ sninfo("CTRL2 after ODR config: 0x%02x\n", ctrl2_odr_val);
+
+ priv->acc_odr = odr;
+
+ uint8_t ctrl2_val;
+ ret = qmi8658_readreg8(priv, QMI8658_REG_CTRL2, &ctrl2_val);
+ if (ret < 0)
+ {
+ snerr(" Failed to read CTRL2 register\n");
+ }
+ else
+ {
+ sninfo("CTRL2 after accel config: 0x%02x\n", ctrl2_val);
+ }
+
+ if (lpf_mode != QMI8658_LPF_OFF)
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL5,
+ QMI8658_ACCEL_LPF_MASK, (lpf_mode << 1));
+ if (ret < 0)
+ {
+ snerr(" Failed to configure accelerometer LPF\n");
+ goto err_unlock;
+ }
+ }
+
+ priv->acc_lpf = lpf_mode;
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_config_gyroscope
+ *
+ * Description:
+ * Configure gyroscope settings (range, ODR, low-pass filter)
+ *
+ ****************************************************************************/
+
+static int qmi8658_config_gyroscope(FAR struct qmi8658_dev_s *priv,
+ uint8_t range, uint8_t odr,
+ uint8_t lpf_mode)
+{
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL3, 0x30,
+ (range << 4));
+ if (ret < 0)
+ {
+ snerr(" Failed to configure gyroscope range\n");
+ goto err_unlock;
+ }
+
+ priv->gyro_range = range;
+
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL3, 0x0f, odr);
+ if (ret < 0)
+ {
+ snerr(" Failed to configure gyroscope ODR\n");
+ goto err_unlock;
+ }
+
+ priv->gyro_odr = odr;
+
+ if (lpf_mode != QMI8658_LPF_OFF)
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL5,
+ QMI8658_GYRO_LPF_MASK, (lpf_mode << 5));
+ if (ret < 0)
+ {
+ snerr(" Failed to configure gyroscope LPF\n");
+ goto err_unlock;
+ }
+ }
+
+ priv->gyro_lpf = lpf_mode;
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_set_accelerometer
+ *
+ * Description:
+ * Enable or disable the accelerometer
+ *
+ ****************************************************************************/
+
+static int qmi8658_set_accelerometer(FAR struct qmi8658_dev_s *priv,
+ bool enable)
+{
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ if (enable)
+ {
+ uint8_t ctrl7_val;
+
+ ret = qmi8658_readreg8(priv, QMI8658_REG_CTRL7, &ctrl7_val);
+ if (ret < 0)
+ {
+ snerr(" Failed to read CTRL7 register\n");
+ goto err_unlock;
+ }
+
+ sninfo("CTRL7 before enable: 0x%02x\n", ctrl7_val);
+
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL7, 0,
+ QMI8658_CTRL7_ACC_EN);
+ if (ret < 0)
+ {
+ snerr(" Failed to enable accelerometer\n");
+ goto err_unlock;
+ }
+
+ ret = qmi8658_readreg8(priv, QMI8658_REG_CTRL7, &ctrl7_val);
+ if (ret < 0)
+ {
+ snerr(" Failed to read CTRL7 register after enable\n");
+ goto err_unlock;
+ }
+
+ sninfo("CTRL7 after enable: 0x%02x\n", ctrl7_val);
+
+ priv->accel_enabled = true;
+ sninfo("Accelerometer enabled\n");
+ }
+ else
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL7,
+ QMI8658_CTRL7_ACC_EN, 0);
+ if (ret < 0)
+ {
+ snerr(" Failed to disable accelerometer\n");
+ goto err_unlock;
+ }
+
+ priv->accel_enabled = false;
+ sninfo("Accelerometer disabled\n");
+ }
+
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_set_gyroscope
+ *
+ * Description:
+ * Enable or disable the gyroscope
+ *
+ ****************************************************************************/
+
+static int qmi8658_set_gyroscope(FAR struct qmi8658_dev_s *priv, bool enable)
+{
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ if (enable)
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL7, 0,
+ QMI8658_CTRL7_GYRO_EN);
+ if (ret < 0)
+ {
+ snerr(" Failed to enable gyroscope\n");
+ goto err_unlock;
+ }
+
+ priv->gyro_enabled = true;
+ sninfo("Gyroscope enabled\n");
+ }
+ else
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL7,
+ QMI8658_CTRL7_GYRO_EN, 0);
+ if (ret < 0)
+ {
+ snerr(" Failed to disable gyroscope\n");
+ goto err_unlock;
+ }
+
+ priv->gyro_enabled = false;
+ sninfo("Gyroscope disabled\n");
+ }
+
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_set_sample_mode
+ *
+ * Description:
+ * Enable or disable synchronous sampling mode. When disabled, the sensor
+ * operates in asynchronous mode where accelerometer and gyroscope data
+ * are sampled independently.
+ *
+ ****************************************************************************/
+
+static int qmi8658_set_sample_mode(FAR struct qmi8658_dev_s *priv, bool sync)
+{
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ if (sync)
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL7, 0,
+ QMI8658_CTRL7_SYNC_MODE);
+ if (ret < 0)
+ {
+ snerr(" Failed to enable synchronous mode\n");
+ goto err_unlock;
+ }
+
+ priv->sample_mode = QMI8658_SYNC_MODE;
+ sninfo("Synchronous mode enabled\n");
+ }
+ else
+ {
+ ret = qmi8658_modifyreg8(priv, QMI8658_REG_CTRL7,
+ QMI8658_CTRL7_SYNC_MODE, 0);
+ if (ret < 0)
+ {
+ snerr(" Failed to disable synchronous mode\n");
+ goto err_unlock;
+ }
+
+ priv->sample_mode = QMI8658_ASYNC_MODE;
+ sninfo("Synchronous mode disabled\n");
+ }
+
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_read_accel
+ *
+ * Description:
+ * Read accelerometer data (16-bit values) using batch reading
+ *
+ ****************************************************************************/
+
+static int qmi8658_read_accel(FAR struct qmi8658_dev_s *priv,
+ FAR int16_t *data)
+{
+ uint8_t buffer[6];
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ ret = qmi8658_readregs(priv, QMI8658_REG_AX_L, buffer, 6);
+ if (ret < 0)
+ {
+ snerr(" Failed to read accelerometer data registers\n");
+ goto err_unlock;
+ }
+
+ data[0] = (int16_t)((buffer[1] << 8) | buffer[0]);
+ data[1] = (int16_t)((buffer[3] << 8) | buffer[2]);
+ data[2] = (int16_t)((buffer[5] << 8) | buffer[4]);
+
+ sninfo("Accel: X=%d, Y=%d, Z=%d (raw: %02x%02x, %02x%02x, %02x%02x)\n",
+ data[0], data[1], data[2],
+ buffer[1], buffer[0], buffer[3], buffer[2],
+ buffer[5], buffer[4]);
+
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_read_gyro
+ *
+ * Description:
+ * Read gyroscope data (16-bit values) using batch reading
+ *
+ ****************************************************************************/
+
+static int qmi8658_read_gyro(FAR struct qmi8658_dev_s *priv,
+ FAR int16_t *data)
+{
+ uint8_t buffer[6];
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ ret = qmi8658_readregs(priv, QMI8658_REG_GX_L, buffer, 6);
+ if (ret < 0)
+ {
+ snerr(" Failed to read gyroscope data registers\n");
+ goto err_unlock;
+ }
+
+ /* Convert to 16-bit signed values */
+
+ data[0] = (int16_t)((buffer[1] << 8) | buffer[0]);
+ data[1] = (int16_t)((buffer[3] << 8) | buffer[2]);
+ data[2] = (int16_t)((buffer[5] << 8) | buffer[4]);
+
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_read_temp
+ *
+ * Description:
+ * Read temperature data from QMI8658 sensor
+ *
+ * Input Parameters:
+ * priv - Pointer to QMI8658 device structure
+ * temperature - Pointer to temperature data (16-bit signed)
+ *
+ * Returned Value:
+ * OK on success; ERROR on failure
+ *
+ * Notes:
+ * Temperature sensor is always enabled. Use QMI8658_TEMP_SCALE (256.0f)
+ * to convert raw value to degrees Celsius.
+ *
+ ****************************************************************************/
+
+static int qmi8658_read_temp(FAR struct qmi8658_dev_s *priv,
+ FAR int16_t *temperature)
+{
+ uint8_t buffer[2];
+ int ret;
+ int ret_lock;
+
+ ret_lock = nxmutex_lock(&priv->dev_lock);
+ if (ret_lock < 0)
+ {
+ return ret_lock;
+ }
+
+ /* Read temperature registers (0x33-0x34) */
+
+ ret = qmi8658_readregs(priv, QMI8658_REG_TEMPERATURE_L, buffer, 2);
+ if (ret < 0)
+ {
+ snerr(" Failed to read temperature data registers\n");
+ goto err_unlock;
+ }
+
+ /* Convert to 16-bit signed value (little-endian) */
+
+ *temperature = (int16_t)((buffer[1] << 8) | buffer[0]);
+
+ sninfo("Temperature: %d (raw value)\n", *temperature);
+ ret = OK;
+
+err_unlock:
+ nxmutex_unlock(&priv->dev_lock);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: qmi8658_initialize
+ *
+ * Description:
+ * Initialize the QMI8658 sensor with default configuration
+ *
+ ****************************************************************************/
+
+static int qmi8658_initialize(FAR struct qmi8658_dev_s *priv)
+{
+ int ret;
+
+ /* Initialize mutex */
+
+ nxmutex_init(&priv->dev_lock);
+
+ ret = qmi8658_checkid(priv);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = qmi8658_reset(priv);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = qmi8658_config_accelerometer(priv, QMI8658_ACC_FS_4G,
+ QMI8658_ACC_ODR_1000Hz,
+ QMI8658_LPF_MODE_0);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = qmi8658_config_gyroscope(priv, QMI8658_GYRO_FS_1024DPS,
+ QMI8658_GYRO_ODR_896_8Hz,
+ QMI8658_LPF_MODE_0);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = qmi8658_set_accelerometer(priv, true);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = qmi8658_set_gyroscope(priv, true);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = qmi8658_set_sample_mode(priv, true);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ sninfo("QMI8658 initialization completed successfully\n");
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_get_scale_factors
+ *
+ * Description:
+ * Get current scale factors for data conversion
+ *
+ ****************************************************************************/
+
+static int qmi8658_get_scale_factors(FAR struct qmi8658_dev_s *priv,
+ FAR struct qmi8658_scale_factors_s *factors)
+{
+ if (!priv || !factors)
+ {
+ return -EINVAL;
+ }
+
+ switch (priv->acc_range)
+ {
+ case QMI8658_ACC_FS_2G:
+ factors->acc_scale = QMI8658_ACC_SCALE_2G;
+ break;
+ case QMI8658_ACC_FS_4G:
+ factors->acc_scale = QMI8658_ACC_SCALE_4G;
+ break;
+ case QMI8658_ACC_FS_8G:
+ factors->acc_scale = QMI8658_ACC_SCALE_8G;
+ break;
+ case QMI8658_ACC_FS_16G:
+ factors->acc_scale = QMI8658_ACC_SCALE_16G;
+ break;
+ default:
+ factors->acc_scale = QMI8658_ACC_SCALE_2G;
+ break;
+ }
+
+ switch (priv->gyro_range)
+ {
+ case QMI8658_GYRO_FS_16DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_16DPS;
+ break;
+ case QMI8658_GYRO_FS_32DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_32DPS;
+ break;
+ case QMI8658_GYRO_FS_64DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_64DPS;
+ break;
+ case QMI8658_GYRO_FS_128DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_128DPS;
+ break;
+ case QMI8658_GYRO_FS_256DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_256DPS;
+ break;
+ case QMI8658_GYRO_FS_512DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_512DPS;
+ break;
+ case QMI8658_GYRO_FS_1024DPS:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_1024DPS;
+ break;
+
+ default:
+ factors->gyro_scale = QMI8658_GYRO_SCALE_64DPS;
+ break;
+ }
+
+ factors->temp_scale = QMI8658_TEMP_SCALE;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_read_imu
+ *
+ * Description:
+ * Read accelerometer and/or gyroscope data from the QMI8658 sensor.
+ * This function reads raw sensor data and applies appropriate scaling
+ * factors to convert to physical units. Temperature data is also
+ * read for sensor compensation.
+ *
+ * Input Parameters:
+ * dev - Pointer to the QMI8658 device structure
+ * accel - Pointer to accelerometer data structure (NULL if not needed)
+ * gyro - Pointer to gyroscope data structure (NULL if not needed)
+ *
+ * Returned Value:
+ * OK on success; negated errno on failure
+ *
+ * -EINVAL - Invalid device pointer
+ * -EIO - I2C communication failure
+ *
+ * Assumptions:
+ * The device must be properly initialized before calling this function.
+ * Either accel or gyro (or both) must be non-NULL.
+ *
+ ****************************************************************************/
+
+static int qmi8658_read_imu(FAR struct qmi8658_dev_s *dev,
+ FAR struct sensor_accel *accel,
+ FAR struct sensor_gyro *gyro)
+{
+ struct qmi8658_scale_factors_s scale_factors;
+ int16_t accel_data[3];
+ int16_t gyro_data[3];
+ int16_t temperature;
+ int ret;
+
+ if (!dev)
+ {
+ return -EINVAL;
+ }
+
+ ret = qmi8658_get_scale_factors(dev, &scale_factors);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ /* Read temperature data (shared by both accel and gyro) */
+
+ ret = qmi8658_read_temp(dev, &temperature);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (accel)
+ {
+ /* Read accelerometer data */
+
+ ret = qmi8658_read_accel(dev, accel_data);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ accel->x = (float)accel_data[0] / scale_factors.acc_scale;
+ accel->y = (float)accel_data[1] / scale_factors.acc_scale;
+ accel->z = (float)accel_data[2] / scale_factors.acc_scale;
+ accel->temperature = (float)temperature / scale_factors.temp_scale;
+ accel->timestamp = sensor_get_timestamp();
+ }
+
+ if (gyro)
+ {
+ /* Read gyroscope data */
+
+ ret = qmi8658_read_gyro(dev, gyro_data);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ gyro->x = (float)gyro_data[0] / scale_factors.gyro_scale;
+ gyro->y = (float)gyro_data[1] / scale_factors.gyro_scale;
+ gyro->z = (float)gyro_data[2] / scale_factors.gyro_scale;
+ gyro->temperature = (float)temperature / scale_factors.temp_scale;
+ gyro->timestamp = sensor_get_timestamp();
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: qmi8658_activate
+ *
+ * Description:
+ * Enable or disable the sensor and manage polling work if enabled.
+ * When enabling a sensor in polling mode, this function starts a
+ * periodic work queue job to read sensor data at the configured
+ * interval. When disabling, it cancels any pending work.
+ *
+ * Input Parameters:
+ * lower - Pointer to the sensor lower-half structure
+ * filep - Pointer to the file structure (unused)
+ * enable - true to enable sensor, false to disable
+ *
+ * Returned Value:
+ * OK on success; negated errno on failure
+ *
+ * -EINVAL - Invalid pointer parameters
+ * -EIO - Work queue operation failed
+ *
+ * Assumptions:
+ * This function is called from the sensor framework when applications
+ * open/close the sensor device.
+ *
+ * In polling mode, the work queue must be available for scheduling
+ * periodic sensor reads.
+ *
+ ****************************************************************************/
+
+static int qmi8658_activate(FAR struct sensor_lowerhalf_s *lower,
+ FAR struct file *filep, bool enable)
+{
+ FAR struct qmi8658_sensor_s *priv = (FAR struct qmi8658_sensor_s *)lower;
+ FAR struct qmi8658_uorb_dev_s *dev =
+ (FAR struct qmi8658_uorb_dev_s *)priv->dev;
+ int ret = OK;
+
+ if (!priv || !dev)
+ {
+ return -EINVAL;
+ }
+
+ int sensor_idx = priv - &dev->priv[0];
+
+ priv->enabled = enable;
+
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+ if (enable)
+ {
+ if (priv->interval > 0)
+ {
+ uint32_t delay = priv->interval / USEC_PER_TICK;
+ if (sensor_idx == QMI8658_ACCEL_IDX)
+ {
+ ret = work_queue(HPWORK, &priv->work, qmi8658_accel_worker,
+ priv, delay);
+ }
+ else if (sensor_idx == QMI8658_GYRO_IDX)
+ {
+ ret = work_queue(HPWORK, &priv->work, qmi8658_gyro_worker,
+ priv, delay);
+ }
+ }
+ }
+ else
+ {
+ work_cancel(HPWORK, &priv->work);
+ }
+#endif
+
+ return ret;
+}
+
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+/****************************************************************************
+ * Name: qmi8658_set_interval
+ *
+ * Description:
+ * Set the polling interval for sensor data acquisition.
+ * This function updates the interval at which sensor data will be
+ * read when polling mode is enabled. The interval is specified
+ * in microseconds.
+ *
+ * Input Parameters:
+ * lower - Pointer to the sensor lower-half structure
+ * filep - Pointer to the file structure (unused)
+ * period_us - Pointer to the polling period in microseconds
+ *
+ * Returned Value:
+ * OK on success; negated errno on failure
+ *
+ * -EINVAL - Invalid pointer parameters
+ *
+ * Assumptions:
+ * This function is called from the sensor framework when applications
+ * set the sensor polling interval via IOCTL or similar interface.
+ *
+ * The new interval takes effect immediately for the next polling cycle.
+ *
+ * interval_us should be greater than 0 for meaningful operation.
+ *
+ * CONFIG_SENSORS_QMI8658_POLL must be enabled for this function
+ * to be available.
+ *
+ ****************************************************************************/
+
+static int qmi8658_set_interval(FAR struct sensor_lowerhalf_s *lower,
+ FAR struct file *filep,
+ FAR uint32_t *period_us)
+{
+ FAR struct qmi8658_sensor_s *priv = (FAR struct qmi8658_sensor_s *)lower;
+
+ if (!priv || !period_us)
+ {
+ return -EINVAL;
+ }
+
+ priv->interval = *period_us;
+
+ return OK;
+}
+#endif
+
+#ifndef CONFIG_SENSORS_QMI8658_POLL
+/****************************************************************************
+ * Name: qmi8658_fetch
+ *
+ * Description:
+ * Fetch sensor data when polling mode is disabled.
+ * This function reads accelerometer or gyroscope data from the
+ * QMI8658 sensor and returns it to the caller. The function
+ * determines which sensor data to read based on the sensor index.
+ *
+ * Input Parameters:
+ * lower - Pointer to the sensor lower-half structure
+ * filep - Pointer to the file structure (unused)
+ * buffer - Buffer to store the sensor data
+ * buflen - Size of the buffer in bytes
+ *
+ * Returned Value:
+ * Number of bytes read on success; negated errno on failure
+ *
+ * -EINVAL - Invalid pointer parameters or insufficient buffer size
+ * -EIO - I2C communication failure
+ *
+ * For accelerometer: sizeof(struct sensor_accel) bytes
+ * For gyroscope: sizeof(struct sensor_gyro) bytes
+ *
+ * Assumptions:
+ * This function is called when polling mode is not enabled.
+ * The application is responsible for providing a sufficiently
+ * large buffer to hold the sensor data structure.
+ *
+ * The function detects the sensor type based on the sensor index
+ * and reads the appropriate data (accelerometer or gyroscope).
+ *
+ * This is a blocking read operation that communicates with the
+ * hardware via I2C.
+ *
+ * CONFIG_SENSORS_QMI8658_POLL must be disabled for this function
+ * to be available.
+ *
+ ****************************************************************************/
+
+static int qmi8658_fetch(FAR struct sensor_lowerhalf_s *lower,
+ FAR struct file *filep,
+ FAR char *buffer, size_t buflen)
+{
+ FAR struct qmi8658_sensor_s *priv = (FAR struct qmi8658_sensor_s *)lower;
+ FAR struct qmi8658_uorb_dev_s *dev =
+ (FAR struct qmi8658_uorb_dev_s *)priv->dev;
+ int ret;
+
+ if (!priv || !dev || !buffer)
+ {
+ return -EINVAL;
+ }
+
+ int sensor_idx = priv - &dev->priv[0];
+
+ if (sensor_idx == QMI8658_ACCEL_IDX)
+ {
+ struct sensor_accel accel;
+ ret = qmi8658_read_imu(&dev->base, &accel, NULL);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (buflen < sizeof(accel))
+ {
+ return -EINVAL;
+ }
+
+ memcpy(buffer, &accel, sizeof(accel));
+ return sizeof(accel);
+ }
+ else if (sensor_idx == QMI8658_GYRO_IDX)
+ {
+ struct sensor_gyro gyro;
+ ret = qmi8658_read_imu(&dev->base, NULL, &gyro);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (buflen < sizeof(gyro))
+ {
+ return -EINVAL;
+ }
+
+ memcpy(buffer, &gyro, sizeof(gyro));
+ return sizeof(gyro);
+ }
+
+ return -EINVAL;
+}
+#endif
+
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+/****************************************************************************
+ * Name: qmi8658_accel_worker
+ *
+ * Description:
+ * Worker function for accelerometer data polling. This function is
+ * scheduled by the work queue to periodically read accelerometer
+ * data from the QMI8658 sensor and push it to the sensor framework.
+ *
+ * Input Parameters:
+ * arg - Pointer to the qmi8658_sensor_s structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The sensor is enabled and has a valid polling interval.
+ * The work queue is available for rescheduling.
+ *
+ ****************************************************************************/
+
+static void qmi8658_accel_worker(FAR void *arg)
+{
+ FAR struct qmi8658_sensor_s *priv = (FAR struct qmi8658_sensor_s *)arg;
+ FAR struct qmi8658_uorb_dev_s *dev =
+ (FAR struct qmi8658_uorb_dev_s *)priv->dev;
+ struct sensor_accel accel;
+ int ret;
+
+ DEBUGASSERT(priv != NULL);
+ DEBUGASSERT(dev != NULL);
+
+ if (priv->enabled && priv->interval > 0)
+ {
+ uint32_t delay = priv->interval / USEC_PER_TICK;
+ work_queue(HPWORK, &priv->work, qmi8658_accel_worker, priv, delay);
+ }
+
+ ret = qmi8658_read_imu(&dev->base, &accel, NULL);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ if (priv->lower.push_event && priv->lower.priv)
+ {
+ priv->lower.push_event(priv->lower.priv, &accel, sizeof(accel));
+ }
+}
+
+/****************************************************************************
+ * Name: qmi8658_gyro_worker
+ *
+ * Description:
+ * Worker function for gyroscope data polling. This function is
+ * scheduled by the work queue to periodically read gyroscope
+ * data from the QMI8658 sensor and push it to the sensor framework.
+ *
+ * Input Parameters:
+ * arg - Pointer to the qmi8658_sensor_s structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * The sensor is enabled and has a valid polling interval.
+ * The work queue is available for rescheduling.
+ *
+ ****************************************************************************/
+
+static void qmi8658_gyro_worker(FAR void *arg)
+{
+ FAR struct qmi8658_sensor_s *priv = (FAR struct qmi8658_sensor_s *)arg;
+ FAR struct qmi8658_uorb_dev_s *dev =
+ (FAR struct qmi8658_uorb_dev_s *)priv->dev;
+ struct sensor_gyro gyro;
+ int ret;
+
+ DEBUGASSERT(priv != NULL);
+ DEBUGASSERT(dev != NULL);
+
+ if (priv->enabled && priv->interval > 0)
+ {
+ uint32_t delay = priv->interval / USEC_PER_TICK;
+ work_queue(HPWORK, &priv->work, qmi8658_gyro_worker, priv, delay);
+ }
+
+ ret = qmi8658_read_imu(&dev->base, NULL, &gyro);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ if (priv->lower.push_event && priv->lower.priv)
+ {
+ priv->lower.push_event(priv->lower.priv, &gyro, sizeof(gyro));
+ }
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: qmi8658_uorb_register
+ *
+ * Description:
+ * Register the QMI8658 IMU sensor device with the NuttX sensor
+ * framework. This function initializes the device, sets up the
+ * accelerometer and gyroscope sensors, and registers them with
+ * the sensor subsystem.
+ *
+ * Input Parameters:
+ * devno - Device number for sensor registration
+ * i2c - Pointer to the I2C master interface
+ * addr - I2C slave address of the QMI8658 device
+ *
+ * Returned Value:
+ * OK on success; negated errno on failure
+ *
+ * -EINVAL - Invalid I2C pointer
+ * -ENOMEM - Memory allocation failure
+ * -EIO - Device initialization or registration failure
+ *
+ * Assumptions:
+ * The I2C bus is properly configured and available.
+ * The QMI8658 device is connected and powered.
+ *
+ ****************************************************************************/
+
+int qmi8658_uorb_register(int devno, FAR struct i2c_master_s *i2c,
+ uint8_t addr)
+{
+ FAR struct qmi8658_uorb_dev_s *dev;
+ struct sensor_lowerhalf_s *lower;
+ struct qmi8658_scale_factors_s scale_factors;
+ int ret = OK;
+ int i;
+
+ if (!i2c)
+ {
+ return -EINVAL;
+ }
+
+ dev = (FAR struct qmi8658_uorb_dev_s *)
+ kmm_zalloc(sizeof(struct qmi8658_uorb_dev_s));
+ if (!dev)
+ {
+ return -ENOMEM;
+ }
+
+ dev->base.i2c = i2c;
+ dev->base.addr = addr;
+ dev->base.freq = CONFIG_QMI8658_I2C_FREQUENCY;
+
+ for (i = 0; i < QMI8658_MAX_IDX; i++)
+ {
+ FAR struct qmi8658_sensor_s *sensor = &dev->priv[i];
+
+ sensor->dev = &dev->base;
+ sensor->enabled = false;
+#ifdef CONFIG_SENSORS_QMI8658_POLL
+ sensor->interval = CONFIG_SENSORS_QMI8658_POLL_INTERVAL;
+
+ memset(&sensor->work, 0, sizeof(sensor->work));
+#endif
+
+ lower = &sensor->lower;
+ lower->type = (i == QMI8658_ACCEL_IDX) ? SENSOR_TYPE_ACCELEROMETER :
+ SENSOR_TYPE_GYROSCOPE;
+ lower->nbuffer = 2;
+ lower->ops = &g_sensor_ops;
+
+ ret = qmi8658_get_scale_factors(&dev->base, &scale_factors);
+ if (ret < 0)
+ {
+ snerr("Failed to get scale factors: %d\n", ret);
+ goto errout;
+ }
+
+ sensor->scale = (i == QMI8658_ACCEL_IDX) ? scale_factors.acc_scale :
+ scale_factors.gyro_scale;
+ }
+
+ ret = qmi8658_initialize(&dev->base);
+ if (ret < 0)
+ {
+ snerr("Failed to initialize QMI8658: %d\n", ret);
+ goto errout;
+ }
+
+ for (i = 0; i < QMI8658_MAX_IDX; i++)
+ {
+ FAR struct qmi8658_sensor_s *sensor = &dev->priv[i];
+ FAR const char *devname;
+
+ devname = (i == QMI8658_ACCEL_IDX) ? "qmi8658_accel" : "qmi8658_gyro";
+
+ ret = sensor_register(&sensor->lower, devno);
+ if (ret < 0)
+ {
+ snerr("Failed to register %s: %d\n", devname, ret);
+ goto errout;
+ }
+ }
+
+ return ret;
+
+errout:
+ for (i = 0; i < QMI8658_MAX_IDX; i++)
+ {
+ if (dev->priv[i].lower.type != 0)
+ {
+ sensor_unregister(&dev->priv[i].lower, devno);
+ }
+ }
+
+ kmm_free(dev);
+
+ return ret;
+}
diff --git a/include/nuttx/sensors/qmi8658.h b/include/nuttx/sensors/qmi8658.h
new file mode 100644
index 00000000000..fe48210bd56
--- /dev/null
+++ b/include/nuttx/sensors/qmi8658.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+ * include/nuttx/sensors/qmi8658.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_SENSORS_QMI8658_H
+#define __INCLUDE_NUTTX_SENSORS_QMI8658_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/sensors/sensor.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Accelerometer Full Scale Ranges */
+#define QMI8658_ACC_FS_2G (0x00)
+#define QMI8658_ACC_FS_4G (0x01)
+#define QMI8658_ACC_FS_8G (0x02)
+#define QMI8658_ACC_FS_16G (0x03)
+
+/* Gyroscope Full Scale Ranges */
+#define QMI8658_GYRO_FS_16DPS (0x00)
+#define QMI8658_GYRO_FS_32DPS (0x01)
+#define QMI8658_GYRO_FS_64DPS (0x02)
+#define QMI8658_GYRO_FS_128DPS (0x03)
+#define QMI8658_GYRO_FS_256DPS (0x04)
+#define QMI8658_GYRO_FS_512DPS (0x05)
+#define QMI8658_GYRO_FS_1024DPS (0x06)
+
+/* ODR (Output Data Rate) Definitions */
+
+/* Accelerometer ODR */
+#define QMI8658_ACC_ODR_1000Hz (0x03)
+#define QMI8658_ACC_ODR_500Hz (0x04)
+#define QMI8658_ACC_ODR_250Hz (0x05)
+#define QMI8658_ACC_ODR_125Hz (0x06)
+#define QMI8658_ACC_ODR_62_5Hz (0x07)
+#define QMI8658_ACC_ODR_31_25Hz (0x08)
+#define QMI8658_ACC_ODR_LOWPOWER_128Hz (0x0C)
+#define QMI8658_ACC_ODR_LOWPOWER_21Hz (0x0D)
+#define QMI8658_ACC_ODR_LOWPOWER_11Hz (0x0E)
+#define QMI8658_ACC_ODR_LOWPOWER_3Hz (0x0F)
+
+/* Gyroscope ODR */
+#define QMI8658_GYRO_ODR_7174_4Hz (0x00)
+#define QMI8658_GYRO_ODR_3587_2Hz (0x01)
+#define QMI8658_GYRO_ODR_1793_6Hz (0x02)
+#define QMI8658_GYRO_ODR_896_8Hz (0x03)
+#define QMI8658_GYRO_ODR_448_4Hz (0x04)
+#define QMI8658_GYRO_ODR_224_2Hz (0x05)
+#define QMI8658_GYRO_ODR_112_1Hz (0x06)
+#define QMI8658_GYRO_ODR_56_05Hz (0x07)
+#define QMI8658_GYRO_ODR_28_025Hz (0x08)
+
+/* Scale Factors */
+
+/* Accelerometer scale factors (LSB/g) */
+#define QMI8658_ACC_SCALE_2G (16384.0f)
+#define QMI8658_ACC_SCALE_4G (8192.0f)
+#define QMI8658_ACC_SCALE_8G (4096.0f)
+#define QMI8658_ACC_SCALE_16G (2048.0f)
+
+/* Gyroscope scale factors (LSB/dps) */
+#define QMI8658_GYRO_SCALE_16DPS (2048.0f)
+#define QMI8658_GYRO_SCALE_32DPS (1024.0f)
+#define QMI8658_GYRO_SCALE_64DPS (512.0f)
+#define QMI8658_GYRO_SCALE_128DPS (256.0f)
+#define QMI8658_GYRO_SCALE_256DPS (128.0f)
+#define QMI8658_GYRO_SCALE_512DPS (64.0f)
+#define QMI8658_GYRO_SCALE_1024DPS (32.0f)
+
+/* Temperature Scale Factor (LSB/°C) */
+#define QMI8658_TEMP_SCALE (256.0f)
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/****************************************************************************
+ * Name: qmi8658_uorb_register
+ *
+ * Description:
+ * Register the QMI8658 uORB sensor device
+ *
+ * Input Parameters:
+ * devno - Device number to use
+ * i2c - Pointer to the I2C master device
+ * addr - I2C address (use QMI8658_I2C_ADDR for default)
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int qmi8658_uorb_register(int devno, FAR struct i2c_master_s *i2c,
+ uint8_t addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_SENSORS_QMI8658_H */