* [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; 7+ 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] 7+ 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; 7+ 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] 7+ 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
2026-10-02 10:38 ` Andy Shevchenko
1 sibling, 1 reply; 7+ 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] 7+ 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; 7+ 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] 7+ 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; 7+ 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] 7+ messages in thread
* Re: [PATCH 2/2] iio: imu: Add driver for the ADI ADIS16607
2026-10-01 9:11 ` [PATCH 2/2] iio: imu: Add driver for the " Radu Sabau via B4 Relay
@ 2026-10-02 10:38 ` Andy Shevchenko
2026-10-02 11:05 ` Nuno Sá
0 siblings, 1 reply; 7+ messages in thread
From: Andy Shevchenko @ 2026-10-02 10:38 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
On Thu, Oct 01, 2026 at 12:11:50PM +0300, Radu Sabau via B4 Relay wrote:
> 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
Good, now split this to be a series of 5, for example.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] iio: imu: Add driver for the ADI ADIS16607
2026-10-02 10:38 ` Andy Shevchenko
@ 2026-10-02 11:05 ` Nuno Sá
0 siblings, 0 replies; 7+ messages in thread
From: Nuno Sá @ 2026-10-02 11:05 UTC (permalink / raw)
To: Andy Shevchenko
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, David Lechner, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-iio, devicetree,
linux-kernel
On Fri, Oct 02, 2026 at 01:38:36PM +0300, Andy Shevchenko wrote:
> On Thu, Oct 01, 2026 at 12:11:50PM +0300, Radu Sabau via B4 Relay wrote:
>
> > 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
>
> Good, now split this to be a series of 5, for example.
On of that I see that the driver is skipping adislib helpers like
locking, startup, self tests. First consider and think on how the lib
can be improved, changed in order to accommodate this device. The less
code we duplicate, the better.
- Nuno Sá
>
> --
> With Best Regards,
> Andy Shevchenko
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-02 11:04 UTC | newest]
Thread overview: 7+ 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
2026-10-02 10:38 ` Andy Shevchenko
2026-10-02 11:05 ` Nuno Sá
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®