mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/2] iio: imu: Add support for the ADI ADIS16607
@ 2026-10-01  9:11 Radu Sabau via B4 Relay
  2026-10-01  9:11 ` [PATCH 1/2] dt-bindings: iio: imu: Add bindings for " Radu Sabau via B4 Relay
  2026-10-01  9:11 ` [PATCH 2/2] iio: imu: Add driver for the " Radu Sabau via B4 Relay
  0 siblings, 2 replies; 5+ messages in thread
From: Radu Sabau via B4 Relay @ 2026-10-01  9:11 UTC (permalink / raw)
  To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-iio, devicetree, linux-kernel, Radu Sabau

This series adds support for the Analog Devices ADIS16607, a precision
MEMS IMU with triaxial gyroscope, triaxial accelerometer, and
temperature sensor, communicating over a non-standard SPI Half-Duplex
protocol.

Patch 1 adds the devicetree binding.

Patch 2 adds the driver, built on top of the existing ADIS IIO library
(drivers/iio/imu/adis.c). Notable points for reviewers:

 - The device does not implement the standard ADIS SPI protocol used
   by other parts in the family; reads/writes are done over a custom
   Half-Duplex sequence (see adis16607_spi_read()/adis16607_spi_write()
   and the adis_ops overrides).

 - An optional external clock (clocks property) selects between
   SYNC_DIRECT and SYNC_SCALED modes depending on the supplied
   frequency, with the internal 8 kHz clock used as the default when
   no clock is provided (adis16607_config_sync()).

 - An optional hardware FIFO mode (adi,fifo-enable) is supported as an
   alternative to the default single-sample burst-read trigger path.

 - Sensor self-test forces a settle delay (self_test_ms) between
   enabling and forcing the test before comparing before/after deltas
   against per-axis thresholds (adis16607_sensor_self_test()).

This has been tested on a Raspberry Pi 4 (arm64) with the device wired
over SPI, exercising both the default burst-read path and FIFO mode.

Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
Radu Sabau (2):
      dt-bindings: iio: imu: Add bindings for ADI ADIS16607
      iio: imu: Add driver for the ADI ADIS16607

 .../devicetree/bindings/iio/imu/adi,adis16607.yaml |   91 ++
 MAINTAINERS                                        |    8 +
 drivers/iio/imu/Kconfig                            |   13 +
 drivers/iio/imu/Makefile                           |    1 +
 drivers/iio/imu/adis16607.c                        | 1513 ++++++++++++++++++++
 5 files changed, 1626 insertions(+)
---
base-commit: 9c87e61e3c5797277407ba5eae4eac8a52be3fa3
change-id: 20260617-adis16607-089ffc655ceb

Best regards,
-- 
Radu Sabau <radu.sabau@analog.com>



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] dt-bindings: iio: imu: Add bindings for ADI ADIS16607
  2026-10-01  9:11 [PATCH 0/2] iio: imu: Add support for the ADI ADIS16607 Radu Sabau via B4 Relay
@ 2026-10-01  9:11 ` Radu Sabau via B4 Relay
  2026-10-01  9:16   ` Sabau, Radu bogdan
  2026-10-01 19:04   ` Conor Dooley
  2026-10-01  9:11 ` [PATCH 2/2] iio: imu: Add driver for the " Radu Sabau via B4 Relay
  1 sibling, 2 replies; 5+ messages in thread
From: Radu Sabau via B4 Relay @ 2026-10-01  9:11 UTC (permalink / raw)
  To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-iio, devicetree, linux-kernel, Radu Sabau

From: Radu Sabau <radu.sabau@analog.com>

Add device tree bindings for the Analog Devices ADIS16607 Precision
MEMS Inertial Measurement Unit. The ADIS16607 integrates a triaxial
gyroscope, triaxial accelerometer, and temperature sensor, and
communicates over SPI Half-Duplex.

Three compatible variants are supported: adis16607-1, adis16607-2,
and adis16607-3.

The binding exposes optional properties for:
  - An external clock for sync mode (falling back to the internal 8 kHz
    clock when absent)
  - A hardware reset GPIO (active low)
  - FIFO mode for buffered data acquisition with a configurable watermark

Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
 .../devicetree/bindings/iio/imu/adi,adis16607.yaml | 91 ++++++++++++++++++++++
 MAINTAINERS                                        |  7 ++
 2 files changed, 98 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
new file mode 100644
index 000000000000..5fb1ba36e0b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/adi,adis16607.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADIS16607 IMU
+
+maintainers:
+  - Radu Sabau <radu.sabau@analog.com>
+
+description: |
+  Analog Devices ADIS16607 Precision MEMS Inertial Measurement Unit (IMU)
+  with a triaxial gyroscope, triaxial accelerometer, and temperature sensor.
+  Uses SPI Half-Duplex communication protocol.
+
+properties:
+  compatible:
+    enum:
+      - adi,adis16607-2
+      - adi,adis16607-3
+
+  reg:
+    maxItems: 1
+
+  spi-cpha: true
+
+  spi-cpol: true
+
+  spi-max-frequency:
+    maximum: 15000000
+
+  vdd-supply: true
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    description:
+      Optional external clock for sync mode. If not provided, the internal
+      8 kHz clock is used.
+    maxItems: 1
+
+  reset-gpios:
+    description:
+      Optional GPIO for hardware reset. If specified, it will be asserted
+      during driver probe. The line is active low.
+    maxItems: 1
+
+  adi,fifo-enable:
+    type: boolean
+    description:
+      Enable hardware FIFO mode for buffered data acquisition. When enabled,
+      the driver uses the device FIFO with configurable watermark threshold
+      instead of single-sample burst reads.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - spi-cpha
+  - spi-cpol
+  - vdd-supply
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/gpio/gpio.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        imu@0 {
+            compatible = "adi,adis16607-2";
+            reg = <0>;
+            spi-cpha;
+            spi-cpol;
+            spi-max-frequency = <15000000>;
+            vdd-supply = <&vdd>;
+            interrupts = <4 IRQ_TYPE_EDGE_RISING>;
+            interrupt-parent = <&gpio>;
+            clocks = <&sync_clk>;
+            reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+            adi,fifo-enable;
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index a7fd31320b38..f6ad73be5260 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1717,6 +1717,13 @@ S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml
 
+ANALOG DEVICES INC ADIS16607 DRIVER
+M:	Radu Sabau <radu.sabau@analog.com>
+L:	linux-iio@vger.kernel.org
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
+
 ANALOG DEVICES INC ADL8113 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
 L:	linux-iio@vger.kernel.org

-- 
2.43.0



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] iio: imu: Add driver for the ADI ADIS16607
  2026-10-01  9:11 [PATCH 0/2] iio: imu: Add support for the ADI ADIS16607 Radu Sabau via B4 Relay
  2026-10-01  9:11 ` [PATCH 1/2] dt-bindings: iio: imu: Add bindings for " Radu Sabau via B4 Relay
@ 2026-10-01  9:11 ` Radu Sabau via B4 Relay
  1 sibling, 0 replies; 5+ messages in thread
From: Radu Sabau via B4 Relay @ 2026-10-01  9:11 UTC (permalink / raw)
  To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-iio, devicetree, linux-kernel, Radu Sabau

From: Radu Sabau <radu.sabau@analog.com>

The ADIS16607 is a Precision MEMS IMU from Analog Devices featuring
triaxial gyroscope, triaxial accelerometer, delta angle, delta velocity,
and temperature channels. The device communicates over SPI Half-Duplex,
requiring custom read and write ops that differ from the standard ADIS
library protocol.

Three variants are supported:
  - ADIS16607-1: ±125°/s gyroscope range
  - ADIS16607-2: ±450°/s gyroscope range
  - ADIS16607-3: ±2000°/s gyroscope range

All variants share a ±40g accelerometer range and an 8 kHz internal
clock.

Data is acquired via 32-bit burst reads over a single held-low CS
transfer, providing 24-bit real resolution for all inertial channels.

Supported features:
  - Hardware FIFO mode with configurable watermark threshold
  - External clock with direct and scaled synchronization modes
  - Calibration bias for gyroscope and accelerometer axes
  - Configurable data-ready interrupt pin (DIO1-4)
  - Optional hardware reset via GPIO
  - Sensor self-test on probe
  - Sampling frequency control via decimation rate register
  - DebugFS entries for serial number, device ID, firmware and revision IDs

Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
 MAINTAINERS                 |    1 +
 drivers/iio/imu/Kconfig     |   13 +
 drivers/iio/imu/Makefile    |    1 +
 drivers/iio/imu/adis16607.c | 1513 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1528 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f6ad73be5260..70b7a430c610 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1723,6 +1723,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
+F:	drivers/iio/imu/adis16607.c
 
 ANALOG DEVICES INC ADL8113 DRIVER
 M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 7e0181c27bb6..7781bd7081cd 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -65,6 +65,19 @@ config ADIS16550
 	  To compile this driver as a module, choose M here: the module will be
 	  called adis16550.
 
+config ADIS16607
+	tristate "Analog Devices ADIS16607 and similar IMU driver"
+	depends on SPI
+	select IIO_ADIS_LIB
+	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+	help
+	  Say yes here to build support for Analog Devices ADIS16607-2
+	  and ADIS16607-3 inertial sensors containing triaxis
+	  gyroscope and triaxis accelerometer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called adis16607.
+
 source "drivers/iio/imu/bmi160/Kconfig"
 source "drivers/iio/imu/bmi270/Kconfig"
 source "drivers/iio/imu/bmi323/Kconfig"
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 13fb7846e9c9..a5e21687cb5d 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ADIS16460) += adis16460.o
 obj-$(CONFIG_ADIS16475) += adis16475.o
 obj-$(CONFIG_ADIS16480) += adis16480.o
 obj-$(CONFIG_ADIS16550) += adis16550.o
+obj-$(CONFIG_ADIS16607) += adis16607.o
 
 adis_lib-y += adis.o
 adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o
diff --git a/drivers/iio/imu/adis16607.c b/drivers/iio/imu/adis16607.c
new file mode 100644
index 000000000000..da9665aff818
--- /dev/null
+++ b/drivers/iio/imu/adis16607.c
@@ -0,0 +1,1513 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADIS16607 IMU driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/imu/adis.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math.h>
+#include <linux/units.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/unaligned.h>
+
+/* Register addresses */
+#define ADIS16607_REG_DEV_ID		0x00
+#define ADIS16607_REG_REV_ID		0x01
+#define ADIS16607_REG_SERIAL_NUM0	0x02
+#define ADIS16607_REG_SERIAL_NUM1	0x03
+#define ADIS16607_REG_SERIAL_NUM2	0x04
+#define ADIS16607_REG_DIAG_STAT		0x05
+#define ADIS16607_REG_X_ACCEL		0x06
+#define ADIS16607_REG_Y_ACCEL		0x08
+#define ADIS16607_REG_Z_ACCEL		0x0A
+#define ADIS16607_REG_X_GYRO		0x0C
+#define ADIS16607_REG_Y_GYRO		0x0E
+#define ADIS16607_REG_Z_GYRO		0x10
+#define ADIS16607_REG_X_DELTVEL		0x12
+#define ADIS16607_REG_Y_DELTVEL		0x14
+#define ADIS16607_REG_Z_DELTVEL		0x16
+#define ADIS16607_REG_X_DELTANG		0x18
+#define ADIS16607_REG_Y_DELTANG		0x1A
+#define ADIS16607_REG_Z_DELTANG		0x1C
+#define ADIS16607_REG_TEMP		0x20
+#define ADIS16607_REG_FIFO_DATA		0x29
+#define ADIS16607_REG_FIFO_WORD_CNT	0x2B
+#define ADIS16607_REG_USER_GPIO_CFG	0x2F
+#define ADIS16607_REG_SPI_HALFDUPLEX	0x32
+#define ADIS16607_REG_USER_SYNC		0x33
+#define ADIS16607_REG_USER_DATA_CFG	0x34
+#define ADIS16607_REG_USER_FIFO_CFG	0x35
+#define ADIS16607_REG_SW_RES		0x36
+#define ADIS16607_REG_SELF_TEST		0x39
+#define ADIS16607_REG_DEC_RATE		0x3A
+#define ADIS16607_REG_XA_BIAS		0x3B
+#define ADIS16607_REG_YA_BIAS		0x3C
+#define ADIS16607_REG_ZA_BIAS		0x3D
+#define ADIS16607_REG_XG_BIAS		0x3E
+#define ADIS16607_REG_YG_BIAS		0x3F
+#define ADIS16607_REG_ZG_BIAS		0x40
+#define ADIS16607_REG_DIGITAL_STATUS	0x4E
+
+/* SPI Half-Duplex lock value */
+#define ADIS16607_LOCK_SPI_HALFDUPLEX	0xB4B4
+
+/* Masks */
+#define ADIS16607_RESET_GPIO_MASK	GENMASK(2, 0)
+#define ADIS16607_SYNC_GPIO_MASK	GENMASK(8, 6)
+#define ADIS16607_DR_GPIO_MASK		GENMASK(11, 9)
+#define ADIS16607_DATA_CNTR_EN_MASK	BIT(14)
+#define ADIS16607_BURST32_MASK		BIT(15)
+#define ADIS16607_SYNC_MODE_MASK	BIT(15)
+#define ADIS16607_UP_SCALE_MASK		GENMASK(14, 0)
+#define ADIS16607_BOOTLOADER_BUSY_MASK	BIT(0)
+
+/* FIFO masks */
+#define ADIS16607_FIFO_THR_MASK		GENMASK(10, 0)
+#define ADIS16607_FIFO_FLUSH_MASK	BIT(15)
+#define ADIS16607_FIFO_THR_MET_MASK	BIT(0)
+#define ADIS16607_FIFO_DATA_CMD		0xA9
+
+/* FIFO limits */
+#define ADIS16607_MAX_FIFO_WM		38
+#define ADIS16607_FIFO_WORDS_PER_SAMPLE	26
+#define ADIS16607_FIFO_DATA_LEN		52
+
+/* Burst read - Half-Duplex mode (DATA_CNTR enabled) */
+#define ADIS16607_BURST_CMD		0x85
+#define ADIS16607_BURST_CMD_SIZE	4
+#define ADIS16607_BURST_DATA_LEN_32	54
+
+/* Self-test registers and masks */
+#define ADIS16607_REG_SELF_TEST_DATA(x)	((x) + 0x23)
+#define ADIS16607_SNSR_SELF_TEST_MASK	BIT(6)
+#define ADIS16607_ST_FORCE_MASK		BIT(7)
+
+/* Self-test delta check limits */
+#define ADIS16607_ACCEL_XY_DELTA_MAX	260
+#define ADIS16607_ACCEL_Z_DELTA_MAX	4000
+#define ADIS16607_GYRO_DELTA_MAX	2600
+
+/* Sync modes */
+enum adis16607_sync_mode {
+	ADIS16607_SYNC_DEFAULT,
+	ADIS16607_SYNC_DIRECT,
+	ADIS16607_SYNC_SCALED,
+};
+
+struct adis16607_sync {
+	u16 sync_mode;
+	u16 min_rate;
+	u16 max_rate;
+};
+
+struct adis16607_chip_info {
+	const struct iio_chan_spec *channels;
+	const struct adis16607_sync *sync_mode;
+	const char *name;
+	u32 num_channels;
+	u32 gyro_max_val;
+	u32 gyro_max_scale;
+	u32 accel_max_val;
+	u32 accel_max_scale;
+	u32 temp_scale;
+	u32 deltang_max_val;
+	u32 deltvel_max_val;
+	u32 int_clk;
+	u16 max_dec;
+	u8 num_sync;
+};
+
+struct adis16607 {
+	const struct adis16607_chip_info *info;
+	struct adis adis;
+	unsigned long clk_freq;	/* millihertz */
+	u64 sample_period_ns;
+	u32 sync_mode;
+	/* FIFO mode settings */
+	bool use_fifo;
+	u16 fifo_watermark;
+	/* Burst read: SPI message and transfer set up once, reused in trigger */
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	u8 *burst_buf;
+	/* IIO buffer for reordered data - sized for max channels */
+	u8 data[64] __aligned(IIO_DMA_MINALIGN);
+};
+
+enum {
+	ADIS16607_SCAN_ACCEL_X,
+	ADIS16607_SCAN_ACCEL_Y,
+	ADIS16607_SCAN_ACCEL_Z,
+	ADIS16607_SCAN_GYRO_X,
+	ADIS16607_SCAN_GYRO_Y,
+	ADIS16607_SCAN_GYRO_Z,
+	ADIS16607_SCAN_DELTVEL_X,
+	ADIS16607_SCAN_DELTVEL_Y,
+	ADIS16607_SCAN_DELTVEL_Z,
+	ADIS16607_SCAN_DELTANG_X,
+	ADIS16607_SCAN_DELTANG_Y,
+	ADIS16607_SCAN_DELTANG_Z,
+	ADIS16607_SCAN_TEMP,
+};
+
+static const u32 adis16607_calib_bias[] = {
+	[ADIS16607_SCAN_ACCEL_X] = ADIS16607_REG_XA_BIAS,
+	[ADIS16607_SCAN_ACCEL_Y] = ADIS16607_REG_YA_BIAS,
+	[ADIS16607_SCAN_ACCEL_Z] = ADIS16607_REG_ZA_BIAS,
+	[ADIS16607_SCAN_GYRO_X] = ADIS16607_REG_XG_BIAS,
+	[ADIS16607_SCAN_GYRO_Y] = ADIS16607_REG_YG_BIAS,
+	[ADIS16607_SCAN_GYRO_Z] = ADIS16607_REG_ZG_BIAS,
+};
+
+/*
+ * SPI Half-Duplex read operation for ADIS16607.
+ * Protocol:
+ *   - First transfer: Send [reg | 0x80, 0, 0, 0] - device latches command
+ *   - Second transfer: Send same command, device responds in bytes 2-3
+ *   - CS toggles between transfers (cs_change = 1 on first xfer)
+ * For 4-byte reads (24-bit sensor data):
+ *   - Read MSW from reg, read LSW from reg+1
+ *   - Combine as: ((val_msw << 8) & 0xFFFF00) | val_lsw
+ */
+static int adis16607_spi_read(struct adis *adis, unsigned int reg,
+			      unsigned int *val, unsigned int size)
+{
+	struct spi_message msg;
+	struct spi_transfer xfers[2] = {
+		{
+			.tx_buf = adis->tx,
+			.len = 4,
+			.bits_per_word = 8,
+			.cs_change = 1,
+		},
+		{
+			.tx_buf = adis->tx,
+			.rx_buf = adis->rx,
+			.len = 4,
+			.bits_per_word = 8,
+		},
+	};
+	unsigned int val_msw, val_lsw;
+	int ret;
+
+	switch (size) {
+	case 2:
+		adis->tx[0] = reg | BIT(7);
+		adis->tx[1] = 0;
+		adis->tx[2] = 0;
+		adis->tx[3] = 0;
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfers[0], &msg);
+		spi_message_add_tail(&xfers[1], &msg);
+
+		ret = spi_sync(adis->spi, &msg);
+		if (ret)
+			return ret;
+
+		*val = get_unaligned_be16(&adis->rx[2]);
+		break;
+
+	case 4:
+		/* Read MSW from reg */
+		adis->tx[0] = reg | BIT(7);
+		adis->tx[1] = 0;
+		adis->tx[2] = 0;
+		adis->tx[3] = 0;
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfers[0], &msg);
+		spi_message_add_tail(&xfers[1], &msg);
+
+		ret = spi_sync(adis->spi, &msg);
+		if (ret)
+			return ret;
+
+		val_msw = get_unaligned_be16(&adis->rx[2]);
+
+		/* Read LSW from reg+1 */
+		adis->tx[0] = (reg + 1) | BIT(7);
+
+		spi_message_init(&msg);
+		spi_message_add_tail(&xfers[0], &msg);
+		spi_message_add_tail(&xfers[1], &msg);
+
+		ret = spi_sync(adis->spi, &msg);
+		if (ret)
+			return ret;
+
+		val_lsw = adis->rx[2];
+
+		/* Combine: 24-bit value with MSW in upper 16 bits, LSW in lower 8 bits */
+		*val = ((val_msw << 8) & 0xFFFF00) | val_lsw;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * SPI Half-Duplex write operation for ADIS16607.
+ * Protocol:
+ *   - Send 3 bytes: [reg, val_high, val_low]
+ *   - Only 16-bit writes are supported
+ */
+static int adis16607_spi_write(struct adis *adis, unsigned int reg,
+			       unsigned int val, unsigned int size)
+{
+	if (size != 2)
+		return -EINVAL;
+
+	adis->tx[0] = reg;
+	adis->tx[1] = (val >> 8) & 0xFF;
+	adis->tx[2] = val & 0xFF;
+
+	return spi_write(adis->spi, adis->tx, 3);
+}
+
+static int adis16607_reset(struct adis *adis)
+{
+	return adis16607_spi_write(adis, ADIS16607_REG_SW_RES, BIT(0), 2);
+}
+
+static const struct adis_ops adis16607_ops = {
+	.read = adis16607_spi_read,
+	.write = adis16607_spi_write,
+	.reset = adis16607_reset,
+};
+
+/*
+ * Custom update_scan_mode for ADIS16607 burst reads.
+ * Sets up SPI transfer and message for Half-Duplex burst protocol.
+ * Uses 32-bit burst mode to get full 24-bit precision.
+ * The SPI message is initialized once here and reused in the trigger handler.
+ */
+static int adis16607_update_scan_mode(struct iio_dev *indio_dev,
+				      const unsigned long *scan_mask)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	unsigned int burst_len = ADIS16607_BURST_CMD_SIZE + ADIS16607_BURST_DATA_LEN_32;
+	int ret;
+
+	kfree(st->burst_buf);
+
+	st->burst_buf = kzalloc(burst_len, GFP_KERNEL);
+	if (!st->burst_buf)
+		return -ENOMEM;
+
+	/* Enable 32-bit burst mode and DATA_CNTR */
+	ret = adis_update_bits_base(adis, ADIS16607_REG_USER_DATA_CFG,
+				    ADIS16607_BURST32_MASK | ADIS16607_DATA_CNTR_EN_MASK,
+				    ADIS16607_BURST32_MASK | ADIS16607_DATA_CNTR_EN_MASK, 2);
+	if (ret) {
+		kfree(st->burst_buf);
+		st->burst_buf = NULL;
+		return ret;
+	}
+
+	/* Set up burst command */
+	st->burst_buf[0] = ADIS16607_BURST_CMD;
+
+	/*
+	 * Single SPI transfer for burst read - CS stays low throughout.
+	 * Command is sent, then device streams all data while CS is held low.
+	 * Initialize SPI message once - it will be reused in trigger handler.
+	 */
+	memset(&st->xfer, 0, sizeof(st->xfer));
+	st->xfer.tx_buf = st->burst_buf;
+	st->xfer.rx_buf = st->burst_buf;
+	st->xfer.len = burst_len;
+	st->xfer.bits_per_word = 8;
+
+	spi_message_init(&st->msg);
+	spi_message_add_tail(&st->xfer, &st->msg);
+
+	return 0;
+}
+
+/*
+ * Burst data offsets for each channel (in bytes from burst data start,
+ * i.e. after the 4-byte command prefix).
+ * Each 32-bit inertial channel occupies 4 bytes (MSW + LSW), TEMP is 2 bytes.
+ *
+ * 32-bit burst layout (54 bytes, DATA_CNTR enabled):
+ *   Offset 0-3:   ACCEL_X  (32-bit: MSW, LSW)
+ *   Offset 4-7:   ACCEL_Y
+ *   Offset 8-11:  ACCEL_Z
+ *   Offset 12-15: GYRO_X
+ *   Offset 16-19: GYRO_Y
+ *   Offset 20-23: GYRO_Z
+ *   Offset 24-27: DELTVEL_X
+ *   Offset 28-31: DELTVEL_Y
+ *   Offset 32-35: DELTVEL_Z
+ *   Offset 36-39: DELTANG_X
+ *   Offset 40-43: DELTANG_Y
+ *   Offset 44-47: DELTANG_Z
+ *   Offset 48-49: TEMP (16-bit)
+ *   Offset 50-51: DATA_CNTR (16-bit)
+ *   Offset 52-53: CHECKSUM (16-bit sum of preceding 26 words)
+ *
+ * Each 32-bit value encodes 24-bit sensor data as:
+ *   value_24bit = (MSW << 8) | (LSW >> 8)
+ */
+static const u8 adis16607_burst_offsets[] = {
+	[ADIS16607_SCAN_ACCEL_X] = 0,
+	[ADIS16607_SCAN_ACCEL_Y] = 4,
+	[ADIS16607_SCAN_ACCEL_Z] = 8,
+	[ADIS16607_SCAN_GYRO_X] = 12,
+	[ADIS16607_SCAN_GYRO_Y] = 16,
+	[ADIS16607_SCAN_GYRO_Z] = 20,
+	[ADIS16607_SCAN_DELTVEL_X] = 24,
+	[ADIS16607_SCAN_DELTVEL_Y] = 28,
+	[ADIS16607_SCAN_DELTVEL_Z] = 32,
+	[ADIS16607_SCAN_DELTANG_X] = 36,
+	[ADIS16607_SCAN_DELTANG_Y] = 40,
+	[ADIS16607_SCAN_DELTANG_Z] = 44,
+	[ADIS16607_SCAN_TEMP] = 48,
+};
+
+/*
+ * Verify burst checksum: 16-bit sum of the first 26 words (52 bytes) must
+ * equal the 16-bit value at bytes 52-53.
+ */
+static bool adis16607_burst_checksum_ok(const u8 *data)
+{
+	u16 sum = 0;
+	int i;
+
+	for (i = 0; i < 26; i++)
+		sum += get_unaligned_be16(&data[i * 2]);
+
+	return sum == get_unaligned_be16(&data[52]);
+}
+
+static irqreturn_t adis16607_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct adis16607 *st = iio_priv(indio_dev);
+	u8 *burst_data;
+	u16 msw, lsw;
+	s32 val24;
+	int ret, bit, offset = 0;
+
+	mutex_lock(&st->adis.state_lock);
+
+	/*
+	 * Reset burst command bytes - they get overwritten by rx data
+	 * since tx_buf and rx_buf point to the same memory.
+	 */
+	st->burst_buf[0] = ADIS16607_BURST_CMD;
+	st->burst_buf[1] = 0;
+	st->burst_buf[2] = 0;
+	st->burst_buf[3] = 0;
+
+	/* Reuse pre-initialized SPI message for burst transfer */
+	ret = spi_sync(st->adis.spi, &st->msg);
+	if (ret)
+		goto unlock;
+
+	/* Point to start of burst data (after command bytes) */
+	burst_data = st->burst_buf + ADIS16607_BURST_CMD_SIZE;
+
+	if (!adis16607_burst_checksum_ok(burst_data)) {
+		dev_warn_ratelimited(&st->adis.spi->dev, "burst checksum mismatch\n");
+		goto unlock;
+	}
+
+	memset(st->data, 0, sizeof(st->data));
+
+	/*
+	 * Pack only enabled channels into output buffer.
+	 * Iterate through active_scan_mask and extract each enabled channel
+	 * from its burst offset, storing contiguously in st->data.
+	 */
+	for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+		u8 burst_off = adis16607_burst_offsets[bit];
+
+		if (bit == ADIS16607_SCAN_TEMP) {
+			/* TEMP is 16-bit */
+			put_unaligned_be16(get_unaligned_be16(&burst_data[burst_off]),
+					   &st->data[offset]);
+			offset += 2;
+		} else {
+			/* All other channels are 24-bit stored as 32-bit */
+			msw = get_unaligned_be16(&burst_data[burst_off]);
+			lsw = get_unaligned_be16(&burst_data[burst_off + 2]);
+			val24 = sign_extend32(((u32)msw << 8) | (lsw >> 8), 23);
+			put_unaligned_be32(val24, &st->data[offset]);
+			offset += 4;
+		}
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->data, pf->timestamp);
+
+unlock:
+	mutex_unlock(&st->adis.state_lock);
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static void adis16607_burst_cleanup(void *arg)
+{
+	struct adis16607 *st = arg;
+
+	kfree(st->burst_buf);
+}
+
+/*
+ * FIFO update_scan_mode - sets up buffer for FIFO mode operation.
+ * Uses burst_buf for FIFO pop operations.
+ */
+static int adis16607_fifo_update_scan_mode(struct iio_dev *indio_dev,
+					   const unsigned long *scan_mask)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	unsigned int burst_len = ADIS16607_BURST_CMD_SIZE + ADIS16607_FIFO_DATA_LEN;
+	int ret;
+
+	kfree(st->burst_buf);
+
+	st->burst_buf = kzalloc(burst_len, GFP_KERNEL);
+	if (!st->burst_buf)
+		return -ENOMEM;
+
+	/* Enable 32-bit burst mode and DATA_CNTR */
+	ret = adis_update_bits_base(adis, ADIS16607_REG_USER_DATA_CFG,
+				    ADIS16607_BURST32_MASK | ADIS16607_DATA_CNTR_EN_MASK,
+				    ADIS16607_BURST32_MASK | ADIS16607_DATA_CNTR_EN_MASK, 2);
+	if (ret) {
+		kfree(st->burst_buf);
+		st->burst_buf = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Pop a single sample from FIFO using FIFO_DATA command (0xA9).
+ * Returns the raw FIFO data in the provided buffer.
+ */
+static int adis16607_fifo_pop(struct adis16607 *st, u8 *data)
+{
+	struct spi_message msg;
+	struct spi_transfer xfer = {
+		.tx_buf = st->burst_buf,
+		.rx_buf = st->burst_buf,
+		.len = ADIS16607_BURST_CMD_SIZE + ADIS16607_FIFO_DATA_LEN,
+		.bits_per_word = 8,
+	};
+	int ret;
+
+	/* FIFO data command */
+	st->burst_buf[0] = ADIS16607_FIFO_DATA_CMD;
+	st->burst_buf[1] = 0;
+	st->burst_buf[2] = 0;
+	st->burst_buf[3] = 0;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	ret = spi_sync(st->adis.spi, &msg);
+	if (ret)
+		return ret;
+
+	/* Copy data starting after command bytes */
+	memcpy(data, st->burst_buf + ADIS16607_BURST_CMD_SIZE, ADIS16607_FIFO_DATA_LEN);
+	return 0;
+}
+
+/*
+ * Process a single FIFO sample and push to IIO buffer.
+ * FIFO data format differs from regular burst - no DIAG_STAT, data starts immediately.
+ *
+ * FIFO data layout (52 bytes):
+ *   Offset 0-3:   ACCEL_X (32-bit)
+ *   Offset 4-7:   ACCEL_Y (32-bit)
+ *   Offset 8-11:  ACCEL_Z (32-bit)
+ *   Offset 12-15: GYRO_X (32-bit)
+ *   Offset 16-19: GYRO_Y (32-bit)
+ *   Offset 20-23: GYRO_Z (32-bit)
+ *   Offset 24-27: DELTVEL_X (32-bit)
+ *   Offset 28-31: DELTVEL_Y (32-bit)
+ *   Offset 32-35: DELTVEL_Z (32-bit)
+ *   Offset 36-39: DELTANG_X (32-bit)
+ *   Offset 40-43: DELTANG_Y (32-bit)
+ *   Offset 44-47: DELTANG_Z (32-bit)
+ *   Offset 48-49: TEMP (16-bit)
+ *   Offset 50-51: DATA_CNTR (16-bit)
+ */
+static void adis16607_fifo_push_sample(struct iio_dev *indio_dev,
+				       u8 *fifo_data, s64 timestamp)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	u16 msw, lsw;
+	s32 val24;
+	int bit, offset = 0;
+
+	memset(st->data, 0, sizeof(st->data));
+
+	for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+		u8 burst_off = adis16607_burst_offsets[bit];
+
+		if (bit == ADIS16607_SCAN_TEMP) {
+			/* TEMP is 16-bit */
+			put_unaligned_be16(get_unaligned_be16(&fifo_data[burst_off]),
+					   &st->data[offset]);
+			offset += 2;
+		} else {
+			/* All other channels are 24-bit stored as 32-bit */
+			msw = get_unaligned_be16(&fifo_data[burst_off]);
+			lsw = get_unaligned_be16(&fifo_data[burst_off + 2]);
+			val24 = sign_extend32(((u32)msw << 8) | (lsw >> 8), 23);
+			put_unaligned_be32(val24, &st->data[offset]);
+			offset += 4;
+		}
+	}
+
+	iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp);
+}
+
+/*
+ * FIFO trigger handler - reads samples from device FIFO.
+ * Triggered by data ready interrupt. Checks DIAG_STAT for FIFO threshold
+ * condition, then reads all available samples.
+ */
+static irqreturn_t adis16607_trigger_handler_with_fifo(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct adis16607 *st = iio_priv(indio_dev);
+	u8 fifo_sample[ADIS16607_FIFO_DATA_LEN];
+	u16 diag_stat, word_cnt, sample_cnt, threshold;
+	s64 timestamp = pf->timestamp;
+	int ret, i;
+
+	mutex_lock(&st->adis.state_lock);
+
+	/* Check if FIFO threshold is met */
+	ret = __adis_read_reg_16(&st->adis, ADIS16607_REG_DIAG_STAT, &diag_stat);
+	if (ret)
+		goto unlock;
+
+	if (!(diag_stat & ADIS16607_FIFO_THR_MET_MASK))
+		goto unlock;
+
+	/* Temporarily disable FIFO before reading */
+	ret = __adis_write_reg_16(&st->adis, ADIS16607_REG_USER_FIFO_CFG, 0);
+	if (ret)
+		goto unlock;
+
+	/* Read FIFO word count */
+	ret = __adis_read_reg_16(&st->adis, ADIS16607_REG_FIFO_WORD_CNT, &word_cnt);
+	if (ret)
+		goto reenable_fifo;
+
+	/* Calculate number of complete samples */
+	sample_cnt = word_cnt / ADIS16607_FIFO_WORDS_PER_SAMPLE;
+	if (!sample_cnt)
+		goto reenable_fifo;
+
+	/* Pop and process each sample */
+	for (i = 0; i < sample_cnt; i++) {
+		ret = adis16607_fifo_pop(st, fifo_sample);
+		if (ret)
+			goto reenable_fifo;
+
+		adis16607_fifo_push_sample(indio_dev, fifo_sample, timestamp);
+		timestamp += st->sample_period_ns;
+	}
+
+reenable_fifo:
+	/* Flush FIFO before re-enabling */
+	ret = __adis_write_reg_16(&st->adis, ADIS16607_REG_USER_FIFO_CFG,
+				  ADIS16607_FIFO_FLUSH_MASK);
+	if (ret)
+		dev_warn_ratelimited(&st->adis.spi->dev,
+				     "failed to flush FIFO: %d\n", ret);
+
+	/* Re-enable FIFO with watermark threshold */
+	threshold = st->fifo_watermark * ADIS16607_FIFO_WORDS_PER_SAMPLE;
+	ret = __adis_write_reg_16(&st->adis, ADIS16607_REG_USER_FIFO_CFG,
+				  threshold & ADIS16607_FIFO_THR_MASK);
+	if (ret)
+		dev_warn_ratelimited(&st->adis.spi->dev,
+				     "failed to re-enable FIFO: %d\n", ret);
+
+unlock:
+	mutex_unlock(&st->adis.state_lock);
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+/* FIFO sysfs attributes */
+IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1");
+IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max, __stringify(ADIS16607_MAX_FIFO_WM));
+
+static ssize_t hwfifo_watermark_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct adis16607 *st = iio_priv(indio_dev);
+
+	return sysfs_emit(buf, "%u\n", st->fifo_watermark);
+}
+
+static ssize_t hwfifo_enabled_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct adis16607 *st = iio_priv(indio_dev);
+	u16 val;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_USER_FIFO_CFG, &val);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", (val & ADIS16607_FIFO_THR_MASK) ? 1 : 0);
+}
+
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_enabled, 0);
+
+static const struct iio_dev_attr *adis16607_fifo_attributes[] = {
+	&iio_dev_attr_hwfifo_watermark_min,
+	&iio_dev_attr_hwfifo_watermark_max,
+	&iio_dev_attr_hwfifo_watermark,
+	&iio_dev_attr_hwfifo_enabled,
+	NULL
+};
+
+static int adis16607_fifo_buffer_postenable(struct iio_dev *indio_dev)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	u16 threshold;
+	int ret;
+
+	mutex_lock(&adis->state_lock);
+
+	/* Flush FIFO to start clean */
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_USER_FIFO_CFG,
+				  ADIS16607_FIFO_FLUSH_MASK);
+	if (ret)
+		goto unlock;
+
+	/* Enable FIFO with watermark threshold (in word count) */
+	threshold = st->fifo_watermark * ADIS16607_FIFO_WORDS_PER_SAMPLE;
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_USER_FIFO_CFG,
+				  threshold & ADIS16607_FIFO_THR_MASK);
+
+unlock:
+	mutex_unlock(&adis->state_lock);
+	return ret;
+}
+
+static int adis16607_fifo_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	struct adis *adis = &st->adis;
+	int ret;
+
+	mutex_lock(&adis->state_lock);
+
+	/* Disable FIFO */
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_USER_FIFO_CFG, 0);
+
+	mutex_unlock(&adis->state_lock);
+	return ret;
+}
+
+static const struct iio_buffer_setup_ops adis16607_fifo_buffer_ops = {
+	.postenable = adis16607_fifo_buffer_postenable,
+	.postdisable = adis16607_fifo_buffer_postdisable,
+};
+
+static int adis16607_set_watermark(struct iio_dev *indio_dev, unsigned int val)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+
+	val = clamp_t(unsigned int, val, 1, ADIS16607_MAX_FIFO_WM);
+	st->fifo_watermark = val;
+
+	return 0;
+}
+
+/*
+ * Check if the delta between two values is within specified range.
+ */
+static bool adis16607_delta_check(s16 val1, s16 val2, s16 delta_max)
+{
+	return abs(val1 - val2) <= delta_max;
+}
+
+/*
+ * Perform sensor self-test for ADIS16607.
+ * Reads self-test data before and after forcing self-test,
+ * then verifies delta values are within acceptable ranges.
+ */
+static int adis16607_sensor_self_test(struct adis16607 *st)
+{
+	struct adis *adis = &st->adis;
+	u16 st1_val[6], st2_val[6];
+	s16 delta_max[6] = {
+		ADIS16607_ACCEL_XY_DELTA_MAX,
+		ADIS16607_ACCEL_XY_DELTA_MAX,
+		ADIS16607_ACCEL_Z_DELTA_MAX,
+		ADIS16607_GYRO_DELTA_MAX,
+		ADIS16607_GYRO_DELTA_MAX,
+		ADIS16607_GYRO_DELTA_MAX,
+	};
+	int ret, i;
+
+	/* Enable sensor self-test */
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_SELF_TEST,
+				  ADIS16607_SNSR_SELF_TEST_MASK);
+	if (ret)
+		return ret;
+
+	/* Wait for self-test to settle before reading results */
+	msleep(adis->data->timeouts->self_test_ms);
+
+	/* Read first set of self-test data */
+	for (i = 0; i < 6; i++) {
+		ret = __adis_read_reg_16(adis, ADIS16607_REG_SELF_TEST_DATA(i),
+					 &st1_val[i]);
+		if (ret)
+			return ret;
+	}
+
+	/* Force self-test */
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_SELF_TEST,
+				  ADIS16607_SNSR_SELF_TEST_MASK | ADIS16607_ST_FORCE_MASK);
+	if (ret)
+		return ret;
+
+	/* Wait for forced self-test to settle before reading results */
+	msleep(adis->data->timeouts->self_test_ms);
+
+	/* Read second set of self-test data */
+	for (i = 0; i < 6; i++) {
+		ret = __adis_read_reg_16(adis, ADIS16607_REG_SELF_TEST_DATA(i),
+					 &st2_val[i]);
+		if (ret)
+			return ret;
+
+		if (!adis16607_delta_check((s16)st1_val[i], (s16)st2_val[i],
+					   delta_max[i])) {
+			dev_err(&adis->spi->dev,
+				"Self-test failed for axis %d: delta out of range\n", i);
+			return -EINVAL;
+		}
+	}
+
+	/* Clear self-test register */
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_SELF_TEST, 0);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int adis16607_initial_startup(struct adis16607 *st)
+{
+	struct adis *adis = &st->adis;
+	struct device *dev = &adis->spi->dev;
+	const struct adis_timeout *timeouts = adis->data->timeouts;
+	struct gpio_desc *gpio;
+	u16 diag_stat, digital_status, dev_id;
+	int ret;
+
+	/*
+	 * Assert hardware reset if available (active low, GPIO_ACTIVE_LOW in DT
+	 * so GPIOD_OUT_HIGH = logical high = pin low = device in reset), wait
+	 * the minimum hold time, then deassert.
+	 */
+	gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+
+	if (gpio) {
+		/* Minimum reset assert time per datasheet */
+		usleep_range(10, 12);
+		/* Deassert reset: logical 0 = pin high = device out of reset */
+		gpiod_set_value_cansleep(gpio, 0);
+		msleep(timeouts->reset_ms);
+	} else {
+		ret = adis16607_reset(adis);
+		if (ret)
+			return ret;
+		msleep(timeouts->sw_reset_ms);
+	}
+
+	/* Additional 100ms required at initial startup per datasheet */
+	msleep(100);
+
+	mutex_lock(&adis->state_lock);
+
+	/* Lock the device into SPI Half-Duplex mode */
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_SPI_HALFDUPLEX,
+				  ADIS16607_LOCK_SPI_HALFDUPLEX);
+	if (ret) {
+		dev_err(dev, "failed to lock SPI Half-Duplex mode: %d\n", ret);
+		goto unlock;
+	}
+
+	/* Verify the correct device is attached */
+	ret = __adis_read_reg_16(adis, ADIS16607_REG_DEV_ID, &dev_id);
+	if (ret)
+		goto unlock;
+
+	if (dev_id != adis->data->prod_id) {
+		dev_err(dev, "device ID mismatch: expected 0x%04x, got 0x%04x\n",
+			adis->data->prod_id, dev_id);
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	/* Read diag_stat to clear any latched faults */
+	ret = __adis_read_reg_16(adis, ADIS16607_REG_DIAG_STAT, &diag_stat);
+	if (ret)
+		goto unlock;
+
+	ret = __adis_check_status(adis);
+	if (ret) {
+		dev_err(dev, "status check failed: %d\n", ret);
+		goto unlock;
+	}
+
+	/* Check that bootloader is not busy */
+	ret = __adis_read_reg_16(adis, ADIS16607_REG_DIGITAL_STATUS, &digital_status);
+	if (ret)
+		goto unlock;
+
+	if (digital_status & ADIS16607_BOOTLOADER_BUSY_MASK) {
+		dev_err(dev, "bootloader is busy\n");
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = __adis_write_reg_16(adis, ADIS16607_REG_USER_GPIO_CFG,
+				  FIELD_PREP(ADIS16607_DR_GPIO_MASK, 1) |
+				  (gpio ? FIELD_PREP(ADIS16607_RESET_GPIO_MASK, 1) : 0));
+	if (ret)
+		goto unlock;
+
+	ret = adis16607_sensor_self_test(st);
+	if (ret)
+		goto unlock;
+
+	/* Read diag_stat again to clear any faults latched during self-test */
+	ret = __adis_read_reg_16(adis, ADIS16607_REG_DIAG_STAT, &diag_stat);
+	if (ret)
+		goto unlock;
+
+	ret = __adis_check_status(adis);
+	if (ret)
+		dev_err(dev, "status check after self-test failed: %d\n", ret);
+
+unlock:
+	mutex_unlock(&adis->state_lock);
+	return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int adis16607_show_firmware_id(void *arg, u64 *val)
+{
+	struct adis16607 *st = arg;
+	u16 rev;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_REV_ID, &rev);
+	if (ret)
+		return ret;
+
+	*val = (rev >> 8) & 0xff;
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16607_firmware_id_fops,
+			 adis16607_show_firmware_id, NULL, "%llu\n");
+
+static int adis16607_show_revision_id(void *arg, u64 *val)
+{
+	struct adis16607 *st = arg;
+	u16 rev;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_REV_ID, &rev);
+	if (ret)
+		return ret;
+
+	*val = rev & 0xff;
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16607_revision_id_fops,
+			 adis16607_show_revision_id, NULL, "%llu\n");
+
+static int adis16607_show_serial_number(void *arg, u64 *val)
+{
+	struct adis16607 *st = arg;
+	u64 serial;
+	u16 tmp;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_SERIAL_NUM0, &tmp);
+	if (ret)
+		return ret;
+
+	serial = (u64)tmp;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_SERIAL_NUM1, &tmp);
+	if (ret)
+		return ret;
+
+	serial |= ((u64)tmp << 16);
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_SERIAL_NUM2, &tmp);
+	if (ret)
+		return ret;
+
+	serial |= ((u64)tmp << 32);
+
+	*val = serial;
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16607_serial_number_fops,
+			 adis16607_show_serial_number, NULL, "0x%.8llx\n");
+
+static int adis16607_show_dev_id(void *arg, u64 *val)
+{
+	struct adis16607 *st = arg;
+	u16 dev_id;
+	int ret;
+
+	ret = adis_read_reg_16(&st->adis, ADIS16607_REG_DEV_ID, &dev_id);
+	if (ret)
+		return ret;
+
+	*val = dev_id;
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE(adis16607_dev_id_fops,
+			 adis16607_show_dev_id, NULL, "%llu\n");
+
+static void adis16607_debugfs_init(struct iio_dev *indio_dev)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	struct dentry *d = iio_get_debugfs_dentry(indio_dev);
+
+	debugfs_create_file_unsafe("serial_number", 0400, d, st,
+				   &adis16607_serial_number_fops);
+	debugfs_create_file_unsafe("dev_id", 0400, d, st,
+				   &adis16607_dev_id_fops);
+	debugfs_create_file_unsafe("firmware_id", 0400, d, st,
+				   &adis16607_firmware_id_fops);
+	debugfs_create_file_unsafe("revision_id", 0400, d, st,
+				   &adis16607_revision_id_fops);
+}
+#else
+static void adis16607_debugfs_init(struct iio_dev *indio_dev)
+{
+}
+#endif
+
+static int adis16607_get_freq(struct adis16607 *st, u32 *freq)
+{
+	u16 dec = 0;
+	int ret;
+
+	mutex_lock(&st->adis.state_lock);
+
+	ret = __adis_read_reg_16(&st->adis, ADIS16607_REG_DEC_RATE, &dec);
+	if (ret)
+		goto unlock;
+
+	*freq = DIV_ROUND_CLOSEST(st->clk_freq, dec + 1);
+
+unlock:
+	mutex_unlock(&st->adis.state_lock);
+	return ret;
+}
+
+static int adis16607_set_freq(struct adis16607 *st, u32 freq)
+{
+	u32 dec;
+	int ret;
+
+	if (!freq)
+		return -EINVAL;
+
+	mutex_lock(&st->adis.state_lock);
+
+	dec = DIV_ROUND_CLOSEST(st->clk_freq, freq);
+	if (dec)
+		dec--;
+
+	dec = min_t(u32, dec, st->info->max_dec);
+
+	ret = __adis_write_reg_16(&st->adis, ADIS16607_REG_DEC_RATE, dec);
+	if (!ret)
+		st->sample_period_ns = DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * 1000ULL * (dec + 1),
+							     st->clk_freq);
+
+	mutex_unlock(&st->adis.state_lock);
+	return ret;
+}
+
+static int adis16607_read_raw(struct iio_dev *indio_dev,
+			      const struct iio_chan_spec *chan,
+			      int *val, int *val2, long info)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	const int idx = chan->scan_index;
+	int ret;
+	u32 tmp;
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW:
+		return adis_single_conversion(indio_dev, chan, 0, val);
+
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->type) {
+		case IIO_ANGL_VEL:
+			*val = st->info->gyro_max_val;
+			*val2 = st->info->gyro_max_scale;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_ACCEL:
+			*val = st->info->accel_max_val;
+			*val2 = st->info->accel_max_scale;
+			return IIO_VAL_FRACTIONAL;
+		case IIO_TEMP:
+			*val = st->info->temp_scale;
+			return IIO_VAL_INT;
+		case IIO_DELTA_ANGL:
+			*val = st->info->deltang_max_val;
+			*val2 = 23;
+			return IIO_VAL_FRACTIONAL_LOG2;
+		case IIO_DELTA_VELOCITY:
+			*val = st->info->deltvel_max_val;
+			*val2 = 23;
+			return IIO_VAL_FRACTIONAL_LOG2;
+		default:
+			return -EINVAL;
+		}
+
+	case IIO_CHAN_INFO_OFFSET:
+		/* Temperature: 0 raw counts = 25°C; offset = 25000 mdeg / 5 mdeg per LSB = 5000 */
+		*val = 5000;
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_CALIBBIAS: {
+		u16 tmp16;
+
+		ret = adis_read_reg_16(&st->adis, adis16607_calib_bias[idx], &tmp16);
+		if (ret)
+			return ret;
+		*val = sign_extend32(tmp16, 15);
+		return IIO_VAL_INT;
+	}
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = adis16607_get_freq(st, &tmp);
+		if (ret)
+			return ret;
+		*val = tmp / 1000;
+		*val2 = (tmp % 1000) * 1000;
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int adis16607_write_raw(struct iio_dev *indio_dev,
+			       const struct iio_chan_spec *chan,
+			       int val, int val2, long info)
+{
+	struct adis16607 *st = iio_priv(indio_dev);
+	const int idx = chan->scan_index;
+	u32 tmp;
+
+	switch (info) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		tmp = val * 1000 + val2 / 1000;
+		return adis16607_set_freq(st, tmp);
+
+	case IIO_CHAN_INFO_CALIBBIAS:
+		return adis_write_reg_16(&st->adis, adis16607_calib_bias[idx],
+					 val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+#define ADIS16607_MOD_CHAN(_type, _mod, _address, _si, _bits) { \
+	.type = (_type), \
+	.modified = 1, \
+	.channel2 = (_mod), \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			      BIT(IIO_CHAN_INFO_CALIBBIAS), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (_address), \
+	.scan_index = (_si), \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = (_bits), \
+		.storagebits = 32, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16607_ACCEL_CHANNEL(_mod) \
+	ADIS16607_MOD_CHAN(IIO_ACCEL, IIO_MOD_ ## _mod, \
+			   ADIS16607_REG_ ## _mod ## _ACCEL, \
+			   ADIS16607_SCAN_ACCEL_ ## _mod, 24)
+
+#define ADIS16607_GYRO_CHANNEL(_mod) \
+	ADIS16607_MOD_CHAN(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
+			   ADIS16607_REG_ ## _mod ## _GYRO, \
+			   ADIS16607_SCAN_GYRO_ ## _mod, 24)
+
+#define ADIS16607_TEMP_CHANNEL() { \
+	.type = IIO_TEMP, \
+	.indexed = 1, \
+	.channel = 0, \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+			      BIT(IIO_CHAN_INFO_SCALE) | \
+			      BIT(IIO_CHAN_INFO_OFFSET), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = ADIS16607_REG_TEMP, \
+	.scan_index = ADIS16607_SCAN_TEMP, \
+	.scan_type = { \
+		.sign = 'u', \
+		.realbits = 16, \
+		.storagebits = 16, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16607_MOD_CHAN_DELTA(_type, _mod, _address, _si) { \
+	.type = (_type), \
+	.modified = 1, \
+	.channel2 = (_mod), \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+	.address = (_address), \
+	.scan_index = (_si), \
+	.scan_type = { \
+		.sign = 's', \
+		.realbits = 24, \
+		.storagebits = 32, \
+		.endianness = IIO_BE, \
+	}, \
+}
+
+#define ADIS16607_DELTANG_CHAN(_mod) \
+	ADIS16607_MOD_CHAN_DELTA(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \
+				 ADIS16607_REG_ ## _mod ## _DELTANG, \
+				 ADIS16607_SCAN_DELTANG_ ## _mod)
+
+#define ADIS16607_DELTVEL_CHAN(_mod) \
+	ADIS16607_MOD_CHAN_DELTA(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \
+				 ADIS16607_REG_ ## _mod ## _DELTVEL, \
+				 ADIS16607_SCAN_DELTVEL_ ## _mod)
+
+static const struct iio_chan_spec adis16607_channels[] = {
+	ADIS16607_ACCEL_CHANNEL(X),
+	ADIS16607_ACCEL_CHANNEL(Y),
+	ADIS16607_ACCEL_CHANNEL(Z),
+	ADIS16607_GYRO_CHANNEL(X),
+	ADIS16607_GYRO_CHANNEL(Y),
+	ADIS16607_GYRO_CHANNEL(Z),
+	ADIS16607_DELTVEL_CHAN(X),
+	ADIS16607_DELTVEL_CHAN(Y),
+	ADIS16607_DELTVEL_CHAN(Z),
+	ADIS16607_DELTANG_CHAN(X),
+	ADIS16607_DELTANG_CHAN(Y),
+	ADIS16607_DELTANG_CHAN(Z),
+	ADIS16607_TEMP_CHANNEL(),
+	IIO_CHAN_SOFT_TIMESTAMP(13),
+};
+
+static const struct iio_info adis16607_info = {
+	.read_raw = adis16607_read_raw,
+	.write_raw = adis16607_write_raw,
+	.update_scan_mode = adis16607_update_scan_mode,
+	.debugfs_reg_access = adis_debugfs_reg_access,
+};
+
+static const struct iio_info adis16607_fifo_info = {
+	.read_raw = adis16607_read_raw,
+	.write_raw = adis16607_write_raw,
+	.update_scan_mode = adis16607_fifo_update_scan_mode,
+	.debugfs_reg_access = adis_debugfs_reg_access,
+	.hwfifo_set_watermark = adis16607_set_watermark,
+};
+
+static const struct adis16607_sync adis16607_sync_modes[] = {
+	{ ADIS16607_SYNC_DEFAULT, 0, 0 },
+	{ ADIS16607_SYNC_DIRECT, 401, 8000 },
+	{ ADIS16607_SYNC_SCALED, 1, 400 },
+};
+
+static const char * const adis16607_status_error_msgs[] = {
+	[9] = "Boot Memory Failure",
+	[11] = "Power Supply Failure",
+	[12] = "Accelerometer Failure",
+	[13] = "Gyroscope Failure",
+};
+
+static const struct adis_timeout adis16607_timeouts = {
+	.reset_ms = 130,
+	.sw_reset_ms = 50,
+	.self_test_ms = 15,
+};
+
+static const struct adis_data adis16607_data = {
+	.diag_stat_reg = ADIS16607_REG_DIAG_STAT,
+	.diag_stat_size = 2,
+	.prod_id_reg = ADIS16607_REG_DEV_ID,
+	.prod_id = 0x6000,
+	.unmasked_drdy = true,
+	.status_error_msgs = adis16607_status_error_msgs,
+	.status_error_mask = BIT(9) | BIT(11) | BIT(12) | BIT(13),
+	.timeouts = &adis16607_timeouts,
+};
+
+/*
+ * ADIS16607-2: 450 deg/s gyro, 40g accel
+ * ADIS16607-3: 2000 deg/s gyro, 40g accel
+ */
+#define ADIS16607_CHIP_INFO_COMMON					\
+	.channels	= adis16607_channels,				\
+	.num_channels	= ARRAY_SIZE(adis16607_channels),		\
+	.accel_max_val	= IIO_G_TO_M_S_2(40),				\
+	.accel_max_scale = 31250 << 8,					\
+	.temp_scale	= 5,						\
+	.deltvel_max_val = IIO_G_TO_M_S_2(40),				\
+	.int_clk	= 8000,						\
+	.max_dec	= 65535,					\
+	.sync_mode	= adis16607_sync_modes,				\
+	.num_sync	= ARRAY_SIZE(adis16607_sync_modes)
+
+static const struct adis16607_chip_info adis16607_2_chip_info = {
+	ADIS16607_CHIP_INFO_COMMON,
+	.name		= "adis16607-2",
+	.gyro_max_val	= IIO_DEGREE_TO_RAD(450),
+	.gyro_max_scale	= 30000 << 8,
+	.deltang_max_val = IIO_DEGREE_TO_RAD(450),
+};
+
+static const struct adis16607_chip_info adis16607_3_chip_info = {
+	ADIS16607_CHIP_INFO_COMMON,
+	.name		= "adis16607-3",
+	.gyro_max_val	= IIO_DEGREE_TO_RAD(2000),
+	.gyro_max_scale	= 31250 << 8,
+	.deltang_max_val = IIO_DEGREE_TO_RAD(2000),
+};
+
+static int adis16607_config_sync(struct adis16607 *st)
+{
+	struct device *dev = &st->adis.spi->dev;
+	const struct adis16607_sync *sync_mode_data = NULL;
+	unsigned long ext_clk_hz;
+	struct clk *clk;
+	int i;
+
+	clk = devm_clk_get_optional_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	if (!clk) {
+		st->clk_freq = (unsigned long)st->info->int_clk * 1000;
+		st->sync_mode = ADIS16607_SYNC_DEFAULT;
+		st->sample_period_ns = DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * 1000ULL,
+							     st->clk_freq);
+		return 0;
+	}
+
+	ext_clk_hz = clk_get_rate(clk);
+
+	for (i = 1; i < st->info->num_sync; i++) {
+		if (ext_clk_hz >= st->info->sync_mode[i].min_rate &&
+		    ext_clk_hz <= st->info->sync_mode[i].max_rate) {
+			sync_mode_data = &st->info->sync_mode[i];
+			break;
+		}
+	}
+
+	if (!sync_mode_data)
+		return dev_err_probe(dev, -EINVAL,
+				     "Clk rate: %lu not in a valid range\n",
+				     ext_clk_hz);
+
+	st->sync_mode = sync_mode_data->sync_mode;
+	/* DIRECT: external clock is the effective internal rate */
+	st->clk_freq = ext_clk_hz * 1000;
+
+	/* Configure sync via USER_GPIO_CFG and USER_SYNC registers */
+	if (st->sync_mode != ADIS16607_SYNC_DEFAULT) {
+		int ret;
+
+		/* Enable sync GPIO function */
+		ret = adis_update_bits_base(&st->adis, ADIS16607_REG_USER_GPIO_CFG,
+					    ADIS16607_SYNC_GPIO_MASK,
+					    FIELD_PREP(ADIS16607_SYNC_GPIO_MASK, 1), 2);
+		if (ret)
+			return ret;
+
+		/* Set sync mode in USER_SYNC register */
+		if (st->sync_mode == ADIS16607_SYNC_SCALED) {
+			u16 sync_scale;
+
+			sync_scale = st->info->int_clk / ext_clk_hz;
+			ret = adis_write_reg_16(&st->adis, ADIS16607_REG_USER_SYNC,
+						ADIS16607_SYNC_MODE_MASK | sync_scale);
+			if (ret)
+				return ret;
+
+			/* SCALED: effective rate is int_clk (ext_clk × sync_scale ≈ int_clk) */
+			st->clk_freq = (unsigned long)st->info->int_clk * 1000;
+		}
+	}
+
+	/* Default dec=0 after reset: period = 1 / clk_freq */
+	st->sample_period_ns = DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * 1000ULL,
+						      st->clk_freq);
+
+	return 0;
+}
+
+static int adis16607_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct iio_dev *indio_dev;
+	struct adis16607 *st;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	st->info = spi_get_device_match_data(spi);
+	if (!st->info)
+		return -EINVAL;
+
+	indio_dev->name = st->info->name;
+	indio_dev->channels = st->info->channels;
+	indio_dev->num_channels = st->info->num_channels;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	st->use_fifo = device_property_read_bool(dev, "adi,fifo-enable");
+
+	if (st->use_fifo) {
+		indio_dev->info = &adis16607_fifo_info;
+		st->fifo_watermark = ADIS16607_MAX_FIFO_WM;
+	} else {
+		indio_dev->info = &adis16607_info;
+	}
+
+	st->adis.ops = &adis16607_ops;
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get vdd regulator\n");
+
+	ret = adis_init(&st->adis, indio_dev, spi, &adis16607_data);
+	if (ret)
+		return ret;
+
+	ret = adis16607_initial_startup(st);
+	if (ret)
+		return ret;
+
+	ret = adis16607_config_sync(st);
+	if (ret)
+		return ret;
+
+	/*
+	 * Register burst_buf cleanup before buffer/trigger setup so that on
+	 * devm teardown (LIFO order) the IRQ handler is removed first, then
+	 * burst_buf is freed, avoiding a use-after-free in the handler.
+	 */
+	ret = devm_add_action_or_reset(dev, adis16607_burst_cleanup, st);
+	if (ret)
+		return ret;
+
+	if (st->use_fifo) {
+		ret = devm_adis_setup_buffer_and_trigger_with_attrs(&st->adis,
+			indio_dev, adis16607_trigger_handler_with_fifo,
+			&adis16607_fifo_buffer_ops, adis16607_fifo_attributes);
+	} else {
+		ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev,
+							 adis16607_trigger_handler);
+	}
+	if (ret)
+		return ret;
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret)
+		return ret;
+
+	adis16607_debugfs_init(indio_dev);
+
+	return 0;
+}
+
+static const struct spi_device_id adis16607_id[] = {
+	{ "adis16607-2", (kernel_ulong_t)&adis16607_2_chip_info },
+	{ "adis16607-3", (kernel_ulong_t)&adis16607_3_chip_info },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, adis16607_id);
+
+static const struct of_device_id adis16607_of_match[] = {
+	{ .compatible = "adi,adis16607-2", .data = &adis16607_2_chip_info },
+	{ .compatible = "adi,adis16607-3", .data = &adis16607_3_chip_info },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adis16607_of_match);
+
+static struct spi_driver adis16607_driver = {
+	.driver = {
+		.name = "adis16607",
+		.of_match_table = adis16607_of_match,
+	},
+	.probe = adis16607_probe,
+	.id_table = adis16607_id,
+};
+module_spi_driver(adis16607_driver);
+
+MODULE_AUTHOR("Radu Sabau <radu.sabau@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16607 IMU driver");
+MODULE_IMPORT_NS("IIO_ADISLIB");
+MODULE_LICENSE("GPL");

-- 
2.43.0



^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: [PATCH 1/2] dt-bindings: iio: imu: Add bindings for ADI ADIS16607
  2026-10-01  9:11 ` [PATCH 1/2] dt-bindings: iio: imu: Add bindings for " Radu Sabau via B4 Relay
@ 2026-10-01  9:16   ` Sabau, Radu bogdan
  2026-10-01 19:04   ` Conor Dooley
  1 sibling, 0 replies; 5+ messages in thread
From: Sabau, Radu bogdan @ 2026-10-01  9:16 UTC (permalink / raw)
  To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
	Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-iio, devicetree, linux-kernel

> -----Original Message-----
> From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Sent: Thursday, October 1, 2026 12:12 PM
> To: Lars-Peter Clausen <lars@metafoo.de>; Hennerich, Michael
> <Michael.Hennerich@analog.com>; Jonathan Cameron <jic23@kernel.org>;
> David Lechner <dlechner@baylibre.com>; Sa, Nuno <Nuno.Sa@analog.com>;
> Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>;
> Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley
> <conor+dt@kernel.org>
> Cc: linux-iio@vger.kernel.org; devicetree@vger.kernel.org; linux-
> kernel@vger.kernel.org; Sabau, Radu bogdan <Radu.Sabau@analog.com>
> Subject: [PATCH 1/2] dt-bindings: iio: imu: Add bindings for ADI ADIS16607
> 
> [External]
> 
> From: Radu Sabau <radu.sabau@analog.com>
> 
> Add device tree bindings for the Analog Devices ADIS16607 Precision
> MEMS Inertial Measurement Unit. The ADIS16607 integrates a triaxial
> gyroscope, triaxial accelerometer, and temperature sensor, and
> communicates over SPI Half-Duplex.
> 
> Three compatible variants are supported: adis16607-1, adis16607-2,
> and adis16607-3.

Update for v2, the -1 variant will be removed from the commits messages
as well since it is not released.

Sorry in advance for any confusion


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] dt-bindings: iio: imu: Add bindings for ADI ADIS16607
  2026-10-01  9:11 ` [PATCH 1/2] dt-bindings: iio: imu: Add bindings for " Radu Sabau via B4 Relay
  2026-10-01  9:16   ` Sabau, Radu bogdan
@ 2026-10-01 19:04   ` Conor Dooley
  1 sibling, 0 replies; 5+ messages in thread
From: Conor Dooley @ 2026-10-01 19:04 UTC (permalink / raw)
  To: radu.sabau
  Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	linux-iio, devicetree, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4660 bytes --]

On Thu, Oct 01, 2026 at 12:11:49PM +0300, Radu Sabau via B4 Relay wrote:
> From: Radu Sabau <radu.sabau@analog.com>
> 
> Add device tree bindings for the Analog Devices ADIS16607 Precision
> MEMS Inertial Measurement Unit. The ADIS16607 integrates a triaxial
> gyroscope, triaxial accelerometer, and temperature sensor, and
> communicates over SPI Half-Duplex.
> 
> Three compatible variants are supported: adis16607-1, adis16607-2,
        ^^^^^^^^^^^^^^^^^^^

> and adis16607-3.
> 
> The binding exposes optional properties for:
>   - An external clock for sync mode (falling back to the internal 8 kHz
>     clock when absent)
>   - A hardware reset GPIO (active low)
>   - FIFO mode for buffered data acquisition with a configurable watermark
> 
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
>  .../devicetree/bindings/iio/imu/adi,adis16607.yaml | 91 ++++++++++++++++++++++
>  MAINTAINERS                                        |  7 ++
>  2 files changed, 98 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
> new file mode 100644
> index 000000000000..5fb1ba36e0b7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
> @@ -0,0 +1,91 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/imu/adi,adis16607.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices ADIS16607 IMU
> +
> +maintainers:
> +  - Radu Sabau <radu.sabau@analog.com>
> +
> +description: |
> +  Analog Devices ADIS16607 Precision MEMS Inertial Measurement Unit (IMU)
> +  with a triaxial gyroscope, triaxial accelerometer, and temperature sensor.
> +  Uses SPI Half-Duplex communication protocol.
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,adis16607-2
> +      - adi,adis16607-3

Yet they are not compatible. Why?
pw-bot: changes-requested

> +
> +  reg:
> +    maxItems: 1
> +
> +  spi-cpha: true
> +
> +  spi-cpol: true
> +
> +  spi-max-frequency:
> +    maximum: 15000000
> +
> +  vdd-supply: true
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    description:
> +      Optional external clock for sync mode. If not provided, the internal
> +      8 kHz clock is used.
> +    maxItems: 1
> +
> +  reset-gpios:
> +    description:
> +      Optional GPIO for hardware reset. If specified, it will be asserted
> +      during driver probe. The line is active low.
> +    maxItems: 1
> +
> +  adi,fifo-enable:
> +    type: boolean
> +    description:
> +      Enable hardware FIFO mode for buffered data acquisition. When enabled,
> +      the driver uses the device FIFO with configurable watermark threshold
> +      instead of single-sample burst reads.

What aspect of the usecase does setting this depend on?
Why not just always enable this?

Cheers,
Conor.

> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - spi-cpha
> +  - spi-cpol
> +  - vdd-supply
> +
> +allOf:
> +  - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    #include <dt-bindings/gpio/gpio.h>
> +    spi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        imu@0 {
> +            compatible = "adi,adis16607-2";
> +            reg = <0>;
> +            spi-cpha;
> +            spi-cpol;
> +            spi-max-frequency = <15000000>;
> +            vdd-supply = <&vdd>;
> +            interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> +            interrupt-parent = <&gpio>;
> +            clocks = <&sync_clk>;
> +            reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
> +            adi,fifo-enable;
> +        };
> +    };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7fd31320b38..f6ad73be5260 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1717,6 +1717,13 @@ S:	Supported
>  W:	https://ez.analog.com/linux-software-drivers
>  F:	Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml
>  
> +ANALOG DEVICES INC ADIS16607 DRIVER
> +M:	Radu Sabau <radu.sabau@analog.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Supported
> +W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/iio/imu/adi,adis16607.yaml
> +
>  ANALOG DEVICES INC ADL8113 DRIVER
>  M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
>  L:	linux-iio@vger.kernel.org
> 
> -- 
> 2.43.0
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-01 19:04 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-01  9:11 [PATCH 0/2] iio: imu: Add support for the ADI ADIS16607 Radu Sabau via B4 Relay
2026-10-01  9:11 ` [PATCH 1/2] dt-bindings: iio: imu: Add bindings for " Radu Sabau via B4 Relay
2026-10-01  9:16   ` Sabau, Radu bogdan
2026-10-01 19:04   ` Conor Dooley
2026-10-01  9:11 ` [PATCH 2/2] iio: imu: Add driver for the " Radu Sabau via B4 Relay

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®