* [PATCH v1 0/3] iio: imu: smi330: add bosch smi330 driver
@ 2025-05-05 15:16 Jianping.Shen
2025-05-05 15:16 ` [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation Jianping.Shen
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Jianping.Shen @ 2025-05-05 15:16 UTC (permalink / raw)
To: jic23, lars, robh, krzk+dt, conor+dt, dima.fedrau,
marcelo.schmitt1, linux-iio, devicetree, linux-kernel,
Jianping.Shen, Christian.Lorenz3, Ulrike.Frauendorf, Kai.Dolde
From: Jianping Shen <Jianping.Shen@de.bosch.com>
Add the iio driver for bosch imu smi330. The smi330 is a combined
three axis angular rate and three axis acceleration sensor module.
This driver provides raw data access for each axis through sysfs,
and tiggered buffer for continuous sampling.
Jianping Shen (3):
docs: iio: imu: smi330: Add ABI documentation
dt-bindings: iio: imu: smi330: Add binding
iio: imu: smi330: Add driver
.../ABI/testing/sysfs-bus-iio-smi330 | 149 +
.../bindings/iio/imu/bosch,smi330.yaml | 89 +
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/smi330/Kconfig | 129 +
drivers/iio/imu/smi330/Makefile | 5 +
drivers/iio/imu/smi330/smi330.h | 351 +++
drivers/iio/imu/smi330/smi330_core.c | 2608 +++++++++++++++++
drivers/iio/imu/smi330/smi330_i2c.c | 140 +
drivers/iio/imu/smi330/smi330_spi.c | 77 +
10 files changed, 3550 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-smi330
create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
create mode 100644 drivers/iio/imu/smi330/Kconfig
create mode 100644 drivers/iio/imu/smi330/Makefile
create mode 100644 drivers/iio/imu/smi330/smi330.h
create mode 100644 drivers/iio/imu/smi330/smi330_core.c
create mode 100644 drivers/iio/imu/smi330/smi330_i2c.c
create mode 100644 drivers/iio/imu/smi330/smi330_spi.c
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation
2025-05-05 15:16 [PATCH v1 0/3] iio: imu: smi330: add bosch smi330 driver Jianping.Shen
@ 2025-05-05 15:16 ` Jianping.Shen
2025-05-05 19:06 ` Jonathan Cameron
2025-05-05 15:16 ` [PATCH v1 2/3] dt-bindings: iio: imu: smi330: Add binding Jianping.Shen
2025-05-05 15:16 ` [PATCH v1 3/3] iio: imu: smi330: Add driver Jianping.Shen
2 siblings, 1 reply; 7+ messages in thread
From: Jianping.Shen @ 2025-05-05 15:16 UTC (permalink / raw)
To: jic23, lars, robh, krzk+dt, conor+dt, dima.fedrau,
marcelo.schmitt1, linux-iio, devicetree, linux-kernel,
Jianping.Shen, Christian.Lorenz3, Ulrike.Frauendorf, Kai.Dolde
From: Jianping Shen <Jianping.Shen@de.bosch.com>
Add ABI documentation for Bosch imu smi330.
Signed-off-by: Jianping Shen <Jianping.Shen@de.bosch.com>
---
.../ABI/testing/sysfs-bus-iio-smi330 | 149 ++++++++++++++++++
1 file changed, 149 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-smi330
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-smi330 b/Documentation/ABI/testing/sysfs-bus-iio-smi330
new file mode 100644
index 00000000000..68220926beb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-smi330
@@ -0,0 +1,149 @@
+What: /sys/bus/iio/devices/iio:deviceX/events/self_cal
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ The device offers self-calibration for the gyroscope sensitivity error and the gyroscope offset.
+ self-calibration to reduce the gyroscope sensitivity error is also known as component re-trim (CRT).
+ The self-calibration ABI will run the calibration routine and update the data path registers in the device.
+ Before initiating the self-calibration, the accelerometer is required to be enabled in high performance
+ mode with a sample rate preferred in the range of 25 Hz up to 200 Hz and the alternative sensor
+ configurations for accelerometer and gyroscope must be disabled.
+ If these preconditions are not fulfilled, the driver will make sure they are fulfilled by changing appropriate
+ register values and then restore the configuration after the self-calibration has been performed.
+ The self-calibration can be triggered by writing '1' to the sysfs entry.
+
+What: /sys/bus/iio/devices/iio:deviceX/events/self_test
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ The self-test of the device checks for a correct function of the accelerometer as well as the gyroscope.
+ The execution of the self-test expects the following prerequisites to be fulfilled.
+ For the self-test, the accelerometer must be configured to high performance mode at least, and the
+ alternative sensor configurations for accelerometer and gyroscope must be disabled. If these preconditions
+ are not fulfilled, the driver will make sure they are fulfilled by changing appropriate register values
+ and then restore the configuration after the self-test has been performed.
+ Once a self test is initiated, the output of data of the device to the registers and FIFO data buffer
+ as well as all features are disabled. While the self-test is in progress, the host is not allowed to modify
+ the configuration of the device.
+ The self-test can be triggered by writing '1' to the sysfs entry.
+
+What: /sys/bus/iio/devices/iio:deviceX/events/soft_reset
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ The softreset performs a fundamental reset to the device which is largely equivalent to a power cycle.
+ Following a delay, all user configuration settings are overwritten with their default state wherever applicable.
+ To access the serial interface after a soft reset, the same timing constraints apply as for power on.
+ The soft-reset can be triggered by writing '1' to the sysfs entry.
+
+What: /sys/bus/iio/devices/iio:deviceX/events/control_auto_op_mode
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ The auto-operation mode change is a built-in feature to support the smart power management of the device.
+ The function provides automatic switching among two sets of operation modes for its accelerometer and gyroscope.
+ In the following, the one set of configurations consists of ACC_CONF and GYR_CONF for the accelerometer and gyroscope
+ and is called user configuration. The other set sensor of configurations consists of ALT_ACC_CONF and ALT_GYR_CONF,
+ and is called alternative configuration. The switching is initiated by events of enabled advanced features
+ or by commands sent from the host to switch from alternative configuration to user configuration.
+ The advanced feature engine and interrupts must be enabled for the auto-operation mode to take effect.
+ Available options:
+ 0 - Enables switching possiblility to alternate configuration for accelerometer
+ 1 - Enables switching possiblility to alternate configuration for gyroscope
+ 2 - Enables switching possiblility to alternate configuration for accelerometer and gyroscope
+ 3 - Disables auto-operation mode change
+
+What: /sys/bus/iio/devices/iio:deviceX/events/set_auto_op_mode_cond
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ The conditions to switch operation mode must be configured by selecting one of the advanced features
+ that will trigger the auto-operation mode change.
+ The same advanced feature cannot be selected as a trigger for both user configuration and alternative configuration.
+ Available options:
+ 0 - Change to user settings by A_NO_MOTION
+ 1 - Change to user settings by A_ANY_MOTION
+ 2 - Change to user settings by H_TILT_DETECTION
+ 3 - Change to alternative settings by A_NO_MOTION
+ 4 - Change to alternative settings by A_ANY_MOTION
+ 5 - Change to alternative settings by H_TILT_DETECTION
+
+What: /sys/bus/iio/devices/iio:deviceX/events/config_user_overwrite
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ The configurations of the sensors can be instantly reset to the user configuration by directly writing
+ to either ACC_CONF or GYR_CONF, if the option is enabled.
+ Available options:
+ 0 - No mode change when writing to ACC_CONF or GYR_CONF
+ 1 - Any write to ACC_CONF or GYR_CONF will instanly switch back to associated user configuration
+
+What: /sys/bus/iio/devices/iio:deviceX/events/alt_status
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ Reports the active configuration for the accelerometer and gyroscope.
+ Available options:
+ 0x00 - ACC_CONF and GYR_CONF are used
+ 0x01 - ALT_ACC_CONF and GYR_CONF are used
+ 0x10 - ACC_CONF and ALT_GYR_CONF are used
+ 0x11 - ALT_ACC_CONF and ALT_GYR_CONF are used
+
+What: /sys/bus/iio/devices/iio:deviceX/events/alt_odr
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ Set alternative output data rate (for accelerometer and gyroscope).
+ Available options:
+ 0 - 0.78125 Hz
+ 1 - 1.5625 Hz
+ 3 - 3.125 Hz
+ 6 - 6.25 Hz
+ 12 - 12.5 Hz
+ 25 - 25 Hz
+ 50 - 50 Hz
+ 100 - 100 Hz
+ 200 - 200 Hz
+ 400 - 400 Hz
+ 800 - 800 Hz
+ 1600 - 1600 Hz
+ 3200 - 3200 Hz
+ 6400 - 6400 Hz
+
+What: /sys/bus/iio/devices/iio:deviceX/events/alt_acc_mode
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ Set alternative accelerometer power mode.
+ Available options:
+ 0 - Suspend
+ 3 - Low power
+ 4 - Normal
+ 7 - Performance
+
+What: /sys/bus/iio/devices/iio:deviceX/events/alt_gyr_mode
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ Set alternative gyroscope power mode.
+ Available options:
+ 0 - Suspend
+ 3 - Low power
+ 4 - Normal
+ 7 - Performance
+
+What: /sys/bus/iio/devices/iio:deviceX/events/alt_acc_avg_num
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ Set alternative accelerometer averaging number.
+ Available options:
+ 1, 2, 4, 8, 16, 32, 64
+
+What: /sys/bus/iio/devices/iio:deviceX/events/alt_gyr_avg_num
+KernelVersion: 6.16
+Contact: Stefan.Gutmann@de.bosch.com
+Description:
+ Set alternative gyroscope averaging number.
+ Available options:
+ 1, 2, 4, 8, 16, 32, 64
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 2/3] dt-bindings: iio: imu: smi330: Add binding
2025-05-05 15:16 [PATCH v1 0/3] iio: imu: smi330: add bosch smi330 driver Jianping.Shen
2025-05-05 15:16 ` [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation Jianping.Shen
@ 2025-05-05 15:16 ` Jianping.Shen
2025-05-06 16:19 ` Conor Dooley
2025-05-05 15:16 ` [PATCH v1 3/3] iio: imu: smi330: Add driver Jianping.Shen
2 siblings, 1 reply; 7+ messages in thread
From: Jianping.Shen @ 2025-05-05 15:16 UTC (permalink / raw)
To: jic23, lars, robh, krzk+dt, conor+dt, dima.fedrau,
marcelo.schmitt1, linux-iio, devicetree, linux-kernel,
Jianping.Shen, Christian.Lorenz3, Ulrike.Frauendorf, Kai.Dolde
From: Jianping Shen <Jianping.Shen@de.bosch.com>
Add devicetree binding for Bosch imu smi330.
The smi330 is a combined three axis angular rate and
three axis acceleration sensor module.
Signed-off-by: Jianping Shen <Jianping.Shen@de.bosch.com>
---
.../bindings/iio/imu/bosch,smi330.yaml | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
new file mode 100644
index 00000000000..fb65bd26ada
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/imu/bosch,smi330.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Bosch SMI330 6-Axis IMU
+
+maintainers:
+ - Stefan Gutmann <stefam.gutmann@de.bosch.com>
+
+description:
+ SMI330 is a 6-axis inertial measurement unit that supports acceleration and
+ gyroscopic measurements with hardware fifo buffering. Sensor also provides
+ events information such as motion, no-motion and tilt detection.
+
+properties:
+ compatible:
+ const: bosch,smi330
+
+ reg:
+ maxItems: 1
+
+ vdd-supply:
+ description: provide VDD power to the sensor.
+
+ vddio-supply:
+ description: provide VDD IO power to the sensor.
+
+ interrupts:
+ minItems: 1
+ maxItems: 2
+
+ interrupt-names:
+ minItems: 1
+ maxItems: 2
+ items:
+ enum:
+ - INT1
+ - INT2
+
+ drive-open-drain:
+ description:
+ set if the specified interrupt pin should be configured as
+ open drain. If not set, defaults to push-pull.
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ // Example for I2C
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ imu@68 {
+ compatible = "bosch,smi330";
+ reg = <0x68>;
+ vddio-supply = <&vddio>;
+ vdd-supply = <&vdd>;
+ interrupt-parent = <&gpio>;
+ interrupts = <26 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "INT1";
+ };
+ };
+
+ // Example for SPI
+ #include <dt-bindings/interrupt-controller/irq.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ imu@0 {
+ compatible = "bosch,smi330";
+ reg = <0>;
+ spi-max-frequency = <10000000>;
+ interrupt-parent = <&gpio>;
+ interrupts = <26 IRQ_TYPE_EDGE_RISING>, <20 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "INT1", "INT2";
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v1 3/3] iio: imu: smi330: Add driver
2025-05-05 15:16 [PATCH v1 0/3] iio: imu: smi330: add bosch smi330 driver Jianping.Shen
2025-05-05 15:16 ` [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation Jianping.Shen
2025-05-05 15:16 ` [PATCH v1 2/3] dt-bindings: iio: imu: smi330: Add binding Jianping.Shen
@ 2025-05-05 15:16 ` Jianping.Shen
2025-05-09 15:58 ` kernel test robot
2 siblings, 1 reply; 7+ messages in thread
From: Jianping.Shen @ 2025-05-05 15:16 UTC (permalink / raw)
To: jic23, lars, robh, krzk+dt, conor+dt, dima.fedrau,
marcelo.schmitt1, linux-iio, devicetree, linux-kernel,
Jianping.Shen, Christian.Lorenz3, Ulrike.Frauendorf, Kai.Dolde
From: Jianping Shen <Jianping.Shen@de.bosch.com>
Add the iio driver for bosch imu smi330. The smi330 is a combined
three axis angular rate and three axis acceleration sensor.
Signed-off-by: Jianping Shen <Jianping.Shen@de.bosch.com>
---
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/smi330/Kconfig | 129 ++
drivers/iio/imu/smi330/Makefile | 5 +
drivers/iio/imu/smi330/smi330.h | 351 ++++
drivers/iio/imu/smi330/smi330_core.c | 2608 ++++++++++++++++++++++++++
drivers/iio/imu/smi330/smi330_i2c.c | 140 ++
drivers/iio/imu/smi330/smi330_spi.c | 77 +
8 files changed, 3312 insertions(+)
create mode 100644 drivers/iio/imu/smi330/Kconfig
create mode 100644 drivers/iio/imu/smi330/Makefile
create mode 100644 drivers/iio/imu/smi330/smi330.h
create mode 100644 drivers/iio/imu/smi330/smi330_core.c
create mode 100644 drivers/iio/imu/smi330/smi330_i2c.c
create mode 100644 drivers/iio/imu/smi330/smi330_spi.c
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 15612f0f189..01d21e4034c 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -124,6 +124,7 @@ config SMI240
This driver can also be built as a module. If so, the module will be
called smi240.
+source "drivers/iio/imu/smi330/Kconfig"
source "drivers/iio/imu/st_lsm6dsx/Kconfig"
source "drivers/iio/imu/st_lsm9ds0/Kconfig"
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index e901aea498d..25948247df9 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -31,5 +31,6 @@ obj-$(CONFIG_KMX61) += kmx61.o
obj-$(CONFIG_SMI240) += smi240.o
+obj-y += smi330/
obj-y += st_lsm6dsx/
obj-y += st_lsm9ds0/
diff --git a/drivers/iio/imu/smi330/Kconfig b/drivers/iio/imu/smi330/Kconfig
new file mode 100644
index 00000000000..71ef01c66f2
--- /dev/null
+++ b/drivers/iio/imu/smi330/Kconfig
@@ -0,0 +1,129 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# SMI330 IMU driver
+#
+
+config SMI330
+ tristate "Bosch Sensor SMI330 Inertial Measurement Unit"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Enable support for the Bosch SMI330 IMU.
+
+ The driver supports different operation modes like polling,
+ data ready or fifo mode and advanced features like no-motion,
+ no-motion, any-motion or tilt detection.
+
+if SMI330
+
+choice
+ prompt "Select communication interface"
+ help
+ Choose communication interface for the sensor.
+
+ Make sure the sensor is connected accordingly.
+ SPI and I2C are not supported at the same time,
+ choose either SPI or I2C to build the driver.
+
+ config SMI330_SPI
+ bool "Enable SPI connection"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Enables SPI communication interface.
+
+ Make sure the sensor is connected to the SPI bus.
+ To specifiy further parameters like max frequency
+ use the device tree.
+
+ config SMI330_I2C
+ bool "Enable I2C connection"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enables I2C communication interface.
+
+ Make sure the sensor is connected to the I2C bus.
+ To specifiy further parameters like the address
+ use the device tree.
+endchoice
+
+choice
+ prompt "Map data interrupt"
+ default SMI330_IRQ_DATA_INT1
+ help
+ Map data interrupt to INT1 or INT2.
+
+ NONE: data interrupt disabled
+ INT1: data interrupt mapped to INT1
+ INT2: data interrupt mapped to INT2
+
+ config SMI330_IRQ_DATA_NONE
+ bool "NONE"
+ help
+ Data interrupt disabled.
+
+ In this mode only polling is possible.
+ To activate data ready of fifo mode choose
+ INT1 or INT2.
+
+ config SMI330_IRQ_DATA_INT1
+ bool "INT1"
+ help
+ Data interrupt mapped to INT1.
+
+ In this mode data ready is the default mode.
+ The interrupt is triggered when new data is available.
+ When fifo watermark is set, fifo mode is activated.
+ The interrupt is triggered when fifo watermark level is reached.
+
+ config SMI330_IRQ_DATA_INT2
+ bool "INT2"
+ help
+ Data interrupt mapped to INT2.
+
+ In this mode data ready is the default mode.
+ The interrupt is triggered when new data is available.
+ When fifo watermark is set, fifo mode is activated.
+ The interrupt is triggered when fifo watermark level is reached.
+endchoice
+
+choice
+ prompt "Map advanced features interrupt"
+ default SMI330_IRQ_ADV_FEAT_NONE
+ help
+ Map advanced features interrupt to INT1 or INT2.
+
+ NONE: advanced features interrupt disabled
+ INT1: advanced features interrupt mapped to INT1
+ INT2: advanced features interrupt mapped to INT2
+
+ config SMI330_IRQ_ADV_FEAT_NONE
+ bool "NONE"
+ help
+ Advanced features interrupt disabled.
+
+ In this mode no-motion, any-motion or tilt detection
+ is disabled.
+ To enable these features choose INT1 or INT2.
+
+ config SMI330_IRQ_ADV_FEAT_INT1
+ bool "INT1"
+ help
+ Advanced features interrupt mapped to INT1.
+
+ In this mode no-motion, any-motion or tilt detection
+ is enabled.
+ The interrupt is triggered when the feature is detected.
+
+ config SMI330_IRQ_ADV_FEAT_INT2
+ bool "INT2"
+ help
+ Advanced features interrupt mapped to INT1.
+
+ In this mode no-motion, any-motion or tilt detection
+ is enabled.
+ The interrupt is triggered when the feature is detected.
+endchoice
+
+endif # SMI330
diff --git a/drivers/iio/imu/smi330/Makefile b/drivers/iio/imu/smi330/Makefile
new file mode 100644
index 00000000000..3dfb98f6345
--- /dev/null
+++ b/drivers/iio/imu/smi330/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_SMI330) += smi330.o
+smi330-$(CONFIG_SMI330) := smi330_core.o
+smi330-$(CONFIG_SMI330_I2C) += smi330_i2c.o
+smi330-$(CONFIG_SMI330_SPI) += smi330_spi.o
diff --git a/drivers/iio/imu/smi330/smi330.h b/drivers/iio/imu/smi330/smi330.h
new file mode 100644
index 00000000000..20e72107b0a
--- /dev/null
+++ b/drivers/iio/imu/smi330/smi330.h
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#ifndef _SMI330_H
+#define _SMI330_H
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#define SMI330_NO_ERROR_MASK (BIT(2) | BIT(0))
+#define SMI330_ST_SUCCESS_MASK GENMASK(6, 0)
+
+#define SMI330_ALL_CHAN_MSK GENMASK(6, 0)
+
+#define SMI330_FIFO_SIZE 2048
+#define SMI330_FIFO_MAX_LENGTH 1024
+#define SMI330_FIFO_MAX_FRAME_LENGTH 8
+
+#define SMI330_CHIP_ID 0x42
+
+#define SMI330_SPI_WR_MASK GENMASK(6, 0)
+#define SMI330_SPI_RD_MASK BIT(7)
+
+#define SMI330_SOFT_RESET_DELAY 2000
+#define SMI330_FEAT_ENG_POLL 10000
+#define SMI330_FEAT_ENG_TIMEOUT 1000000
+
+/* Register map */
+#define SMI330_CHIP_ID_REG U8_C(0x00)
+#define SMI330_ERR_REG U8_C(0x01)
+#define SMI330_STATUS_REG U8_C(0x02)
+#define SMI330_ACCEL_X_REG U8_C(0x03)
+#define SMI330_GYRO_X_REG U8_C(0x06)
+#define SMI330_TEMP_REG U8_C(0x09)
+#define SMI330_INT1_STATUS_REG U8_C(0x0D)
+#define SMI330_FEATURE_IO0_REG U8_C(0x10)
+#define SMI330_FEATURE_IO1_REG U8_C(0x11)
+#define SMI330_FEATURE_IO2_REG U8_C(0x12)
+#define SMI330_FEATURE_IO_STATUS_REG U8_C(0x14)
+#define SMI330_FIFO_FILL_LEVEL_REG U8_C(0x15)
+#define SMI330_FIFO_DATA_REG U8_C(0x16)
+#define SMI330_ACCEL_CFG_REG U8_C(0x20)
+#define SMI330_GYRO_CFG_REG U8_C(0x21)
+#define SMI330_ALT_ACCEL_CFG_REG U8_C(0x28)
+#define SMI330_ALT_GYRO_CFG_REG U8_C(0x29)
+#define SMI330_ALT_CONF_REG U8_C(0x2A)
+#define SMI330_ALT_STATUS_REG U8_C(0x2B)
+#define SMI330_FIFO_WATERMARK_REG U8_C(0x35)
+#define SMI330_FIFO_CONF_REG U8_C(0x36)
+#define SMI330_FIFO_CTRL_REG U8_C(0x37)
+#define SMI330_IO_INT_CTRL_REG U8_C(0x38)
+#define SMI330_INT_CONF_REG U8_C(0x39)
+#define SMI330_INT_MAP1_REG U8_C(0x3A)
+#define SMI330_INT_MAP2_REG U8_C(0x3B)
+#define SMI330_FEATURE_CTRL_REG U8_C(0x40)
+#define SMI330_FEATURE_DATA_ADDR_REG U8_C(0x41)
+#define SMI330_FEATURE_DATA_TX_REG U8_C(0x42)
+#define SMI330_FEATURE_DATA_STATUS_REG U8_C(0x43)
+#define SMI330_CMD_REG U8_C(0x7E)
+#define SMI330_RES_CFG_REG U8_C(0x7F)
+
+/* Register mask */
+#define SMI330_ERR_FATAL_MASK BIT(0)
+#define SMI330_ERR_ACC_CONF_MASK BIT(5)
+#define SMI330_ERR_GYR_CONF_MASK BIT(6)
+#define SMI330_STATUS_POR_MASK BIT(0)
+#define SMI330_INT_STATUS_NOMO_MASK BIT(0)
+#define SMI330_INT_STATUS_ANYMO_MASK BIT(1)
+#define SMI330_INT_STATUS_TILT_MASK BIT(7)
+#define SMI330_INT_STATUS_GYR_DRDY_MASK BIT(12)
+#define SMI330_INT_STATUS_ACC_DRDY_MASK BIT(13)
+#define SMI330_INT_STATUS_FWM_MASK BIT(14)
+#define SMI330_INT_STATUS_FFULL_MASK BIT(15)
+#define SMI330_FEATURE_IO0_NOMO_X_EN_MASK BIT(0)
+#define SMI330_FEATURE_IO0_NOMO_Y_EN_MASK BIT(1)
+#define SMI330_FEATURE_IO0_NOMO_Z_EN_MASK BIT(2)
+#define SMI330_FEATURE_IO0_ANYMO_X_EN_MASK BIT(3)
+#define SMI330_FEATURE_IO0_ANYMO_Y_EN_MASK BIT(4)
+#define SMI330_FEATURE_IO0_ANYMO_Z_EN_MASK BIT(5)
+#define SMI330_FEATURE_IO0_TILT_EN_MASK BIT(11)
+#define SMI330_FEATURE_IO1_ERROR_MASK GENMASK(3, 0)
+#define SMI330_FEATURE_IO1_SC_COMPLETE_MASK BIT(4)
+#define SMI330_FEATURE_IO1_ST_RESULT_MASK BIT(6)
+#define SMI330_FEATURE_IO1_STATE_MASK GENMASK(12, 11)
+#define SMI330_FEATURE_IO_STATUS_MASK BIT(0)
+#define SMI330_FIFO_FILL_LEVEL_MASK GENMASK(10, 0)
+#define SMI330_CFG_MASK GENMASK(15, 0)
+#define SMI330_CFG_ODR_MASK GENMASK(3, 0)
+#define SMI330_CFG_RANGE_MASK GENMASK(6, 4)
+#define SMI330_CFG_BW_MASK BIT(7)
+#define SMI330_CFG_AVG_NUM_MASK GENMASK(10, 8)
+#define SMI330_CFG_MODE_MASK GENMASK(14, 12)
+#define SMI330_ALT_CONF_ACC_EN_MASK BIT(0)
+#define SMI330_ALT_CONF_GYR_EN_MASK BIT(4)
+#define SMI330_ALT_CONF_EN_MASK \
+ (SMI330_ALT_CONF_ACC_EN_MASK | SMI330_ALT_CONF_GYR_EN_MASK)
+#define SMI330_ALT_CONF_RST_CONF_EN_MASK BIT(8)
+#define SMI330_FIFO_WATERMARK_MASK GENMASK(9, 0)
+#define SMI330_FIFO_CONF_MASK GENMASK(11, 9)
+#define SMI330_FIFO_CONF_TEMP_MASK BIT(11)
+#define SMI330_FIFO_CONF_GYR_MASK BIT(10)
+#define SMI330_FIFO_CONF_ACC_MASK BIT(9)
+#define SMI330_FIFO_CTRL_FLUSH_MASK BIT(0)
+#define SMI330_IO_INT_CTRL_INT1_MASK GENMASK(2, 0)
+#define SMI330_IO_INT_CTRL_INT2_MASK GENMASK(10, 8)
+#define SMI330_INT_CONF_LATCH_MASK BIT(0)
+#define SMI330_INT_MAP1_TILT_MASK GENMASK(15, 14)
+#define SMI330_INT_MAP1_ANYMO_MASK GENMASK(3, 2)
+#define SMI330_INT_MAP1_NOMO_MASK GENMASK(1, 0)
+#define SMI330_INT_MAP2_FIFO_FULL_MASK GENMASK(15, 14)
+#define SMI330_INT_MAP2_FIFO_WM_MASK GENMASK(13, 12)
+#define SMI330_INT_MAP2_ACC_DRDY_MASK GENMASK(11, 10)
+#define SMI330_INT_MAP2_GYR_DRDY_MASK GENMASK(9, 8)
+#define SMI330_INT_MAP2_FIFO_MASK \
+ (SMI330_INT_MAP2_FIFO_FULL_MASK | SMI330_INT_MAP2_FIFO_WM_MASK)
+#define SMI330_INT_MAP2_DRDY_MASK \
+ (SMI330_INT_MAP2_ACC_DRDY_MASK | SMI330_INT_MAP2_GYR_DRDY_MASK)
+#define SMI330_FEATURE_DATA_STATUS_TX_READY_MASK BIT(1)
+
+/* Register values */
+#define SMI330_FEATURE_IO2_STARTUP_CONFIG 0x012C
+#define SMI330_IO_INT_CTRL_LVL BIT(0)
+#define SMI330_IO_INT_CTRL_OD BIT(1)
+#define SMI330_IO_INT_CTRL_EN BIT(2)
+#define SMI330_FEATURE_CTRL_ENABLE BIT(0)
+#define SMI330_CMD_SELF_CALIBRATION (BIT(0) | BIT(8))
+#define SMI330_CMD_SELF_TEST BIT(8)
+#define SMI330_CMD_SOFT_RESET 0xDEAF
+
+/* Extended register map */
+#define SMI330_GYRO_SC_ST_VALUES_EX_REG U8_C(0x28)
+#define SMI330_GYRO_SC_SELECT_EX_REG U8_C(0x26)
+#define SMI330_ST_SELECT_EX_REG U8_C(0x25)
+#define SMI330_ST_RESULT_EX_REG U8_C(0x24)
+#define SMI330_ALT_CONF_CHG_EX_REG U8_C(0x23)
+#define SMI330_TILT_2_EX_REG U8_C(0x22)
+#define SMI330_TILT_1_EX_REG U8_C(0x21)
+#define SMI330_NOMO_3_EX_REG U8_C(0x0A)
+#define SMI330_NOMO_2_EX_REG U8_C(0x09)
+#define SMI330_NOMO_1_EX_REG U8_C(0x08)
+#define SMI330_ANYMO_3_EX_REG U8_C(0x07)
+#define SMI330_ANYMO_2_EX_REG U8_C(0x06)
+#define SMI330_ANYMO_1_EX_REG U8_C(0x05)
+
+/* Extended register mask */
+#define SMI330_GYRO_SC_SELECT_ALL_MASK GENMASK(2, 0)
+#define SMI330_ST_SELECT_ACC_GYR_MASK GENMASK(1, 0)
+#define SMI330_ALT_CONF_CHG_USER_MASK GENMASK(7, 4)
+#define SMI330_ALT_CONF_CHG_ALT_MASK GENMASK(3, 0)
+#define SMI330_TILT2_BETA_ACC_MEAN_MASK GENMASK(15, 0)
+#define SMI330_TILT1_MIN_ANGLE_MASK GENMASK(15, 8)
+#define SMI330_TILT1_SEGMENT_SIZE_MASK GENMASK(7, 0)
+#define SMI330_MOTION3_WAIT_TIME_MASK GENMASK(15, 13)
+#define SMI330_MOTION3_DURATION_MASK GENMASK(12, 0)
+#define SMI330_MOTION2_HYSTERESIS_MASK GENMASK(9, 0)
+#define SMI330_MOTION1_ACC_REF_MASK BIT(12)
+#define SMI330_MOTION1_SLOPE_THRES_MASK GENMASK(11, 0)
+
+/* Extended register values */
+#define SMI330_SC_ST_VALUE_0 0x5A2E
+#define SMI330_SC_ST_VALUE_1 0x9219
+#define SMI330_SC_ST_VALUE_2 0x5637
+#define SMI330_SC_ST_VALUE_3 0xFFE8
+#define SMI330_SC_ST_VALUE_4 0xFFEF
+#define SMI330_SC_ST_VALUE_5 0x000D
+#define SMI330_SC_ST_VALUE_6 0x07CA
+#define SMI330_SC_ST_VALUE_7 0xFFCD
+#define SMI330_SC_ST_VALUE_8 0xEF6C
+
+/* T°C = (temp / 512) + 23 */
+#define SMI330_TEMP_OFFSET 11776 /* 23 * 512 */
+#define SMI330_TEMP_SCALE 1953125 /* (1 / 512) * 1e9 */
+
+enum {
+ SMI330_SCAN_ACCEL_X,
+ SMI330_SCAN_ACCEL_Y,
+ SMI330_SCAN_ACCEL_Z,
+ SMI330_SCAN_GYRO_X,
+ SMI330_SCAN_GYRO_Y,
+ SMI330_SCAN_GYRO_Z,
+ SMI330_TEMP_OBJECT,
+ SMI330_SCAN_TIMESTAMP,
+};
+
+enum smi330_accel_range {
+ SMI330_ACCEL_RANGE_2G = U8_C(0x00),
+ SMI330_ACCEL_RANGE_4G = U8_C(0x01),
+ SMI330_ACCEL_RANGE_8G = U8_C(0x02),
+ SMI330_ACCEL_RANGE_16G = U8_C(0x03)
+};
+
+enum smi330_gyro_range {
+ SMI330_GYRO_RANGE_125 = U8_C(0x00),
+ SMI330_GYRO_RANGE_250 = U8_C(0x01),
+ SMI330_GYRO_RANGE_500 = U8_C(0x02)
+};
+
+enum smi330_odr {
+ SMI330_ODR_0_78125_HZ = U8_C(0x01),
+ SMI330_ODR_1_5625_HZ = U8_C(0x02),
+ SMI330_ODR_3_125_HZ = U8_C(0x03),
+ SMI330_ODR_6_25_HZ = U8_C(0x04),
+ SMI330_ODR_12_5_HZ = U8_C(0x05),
+ SMI330_ODR_25_HZ = U8_C(0x06),
+ SMI330_ODR_50_HZ = U8_C(0x07),
+ SMI330_ODR_100_HZ = U8_C(0x08),
+ SMI330_ODR_200_HZ = U8_C(0x09),
+ SMI330_ODR_400_HZ = U8_C(0x0A),
+ SMI330_ODR_800_HZ = U8_C(0x0B),
+ SMI330_ODR_1600_HZ = U8_C(0x0C),
+ SMI330_ODR_3200_HZ = U8_C(0x0D),
+ SMI330_ODR_6400_HZ = U8_C(0x0E)
+};
+
+enum smi330_avg_num {
+ SMI330_AVG_NUM_1 = U8_C(0x00),
+ SMI330_AVG_NUM_2 = U8_C(0x01),
+ SMI330_AVG_NUM_4 = U8_C(0x02),
+ SMI330_AVG_NUM_8 = U8_C(0x03),
+ SMI330_AVG_NUM_16 = U8_C(0x04),
+ SMI330_AVG_NUM_32 = U8_C(0x05),
+ SMI330_AVG_NUM_64 = U8_C(0x06)
+};
+
+enum smi330_mode {
+ SMI330_MODE_SUSPEND = U8_C(0x00),
+ SMI330_MODE_GYRO_DRIVE = U8_C(0x01),
+ SMI330_MODE_LOW_POWER = U8_C(0x03),
+ SMI330_MODE_NORMAL = U8_C(0x04),
+ SMI330_MODE_HIGH_PERF = U8_C(0x07)
+};
+
+enum smi330_bw {
+ SMI330_BW_2 = U8_C(0x00), /* ODR/2 */
+ SMI330_BW_4 = U8_C(0x01) /* ODR/4 */
+};
+
+enum smi330_auto_op_adv_feat {
+ A_NO_MOTION = U8_C(0x01),
+ B_ANY_MOTION = U8_C(0x02),
+ H_TILT_DETECTION = U8_C(0x08)
+};
+
+enum smi330_auto_op_use {
+ SMI330_AUTO_OP_USER_A,
+ SMI330_AUTO_OP_USER_B,
+ SMI330_AUTO_OP_USER_H,
+ SMI330_AUTO_OP_ALT_A,
+ SMI330_AUTO_OP_ALT_B,
+ SMI330_AUTO_OP_ALT_H,
+};
+
+enum smi330_operation_mode {
+ SMI330_IDLE,
+ SMI330_DATA_READY,
+ SMI330_FIFO,
+};
+
+enum smi330_auto_op_setup {
+ SMI330_AUTO_OP_RESET,
+ SMI330_AUTO_OP_SET,
+};
+
+enum smi330_auto_op_mode {
+ SMI330_AUTO_OP_EN_ACC,
+ SMI330_AUTO_OP_EN_GYR,
+ SMI330_AUTO_OP_EN_ALL,
+ SMI330_AUTO_OP_DISABLE,
+};
+
+enum smi330_auto_op_config {
+ SMI330_AUTO_OP_CONFIG_USER,
+ SMI330_AUTO_OP_CONFIG_ALT,
+};
+
+enum smi330_sensor {
+ SMI330_ACCEL,
+ SMI330_GYRO,
+ SMI330_ALT_ACCEL,
+ SMI330_ALT_GYRO,
+};
+
+enum smi330_sensor_conf_select {
+ SMI330_ODR,
+ SMI330_RANGE,
+ SMI330_BW,
+ SMI330_AVG_NUM,
+ SMI330_MODE,
+};
+
+enum smi330_int_out {
+ SMI330_INT_DISABLED,
+ SMI330_INT_1,
+ SMI330_INT_2,
+ SMI330_INT_I3C_IBI,
+};
+
+struct smi330_sysfs_attr {
+ int *reg_vals;
+ int *vals;
+ int len;
+ int type;
+};
+
+struct smi330_cfg {
+ s64 odr_ns;
+ enum smi330_operation_mode op_mode;
+ enum smi330_int_out data_irq;
+ enum smi330_int_out feat_irq;
+};
+
+struct smi330_pwr_savestate {
+ int acc_pwr;
+ int gyr_pwr;
+ int alt_acc_pwr;
+ int alt_gyr_pwr;
+};
+
+struct smi330_data {
+ struct regmap *regmap;
+ struct smi330_cfg cfg;
+ struct smi330_pwr_savestate savestate;
+ s64 current_timestamp;
+ s64 last_timestamp;
+ atomic64_t irq_timestamp;
+ struct mutex lock;
+ struct iio_trigger *trig;
+ s16 fifo[SMI330_FIFO_MAX_LENGTH];
+ /*
+ * Ensure natural alignment for timestamp if present.
+ * Channel size: 2 bytes.
+ * Max length needed: 2 * 3 channels + temp channel + 2 bytes padding + 8 byte ts.
+ * If fewer channels are enabled, less space may be needed, as
+ * long as the timestamp is still aligned to 8 bytes.
+ */
+ s16 buf[12] __aligned(8);
+};
+
+extern const struct dev_pm_ops smi330_pm_ops;
+
+int smi330_core_probe(struct device *dev, struct regmap *regmap);
+
+#endif /* _SMI330_H */
diff --git a/drivers/iio/imu/smi330/smi330_core.c b/drivers/iio/imu/smi330/smi330_core.c
new file mode 100644
index 00000000000..97cc5d36c8f
--- /dev/null
+++ b/drivers/iio/imu/smi330/smi330_core.c
@@ -0,0 +1,2608 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "smi330.h"
+
+static struct iio_event_spec smi330_accel_events[] = {
+ /* Any-Motion */
+ {
+ .type = IIO_EV_TYPE_ROC,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all =
+ BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HYSTERESIS) | BIT(IIO_EV_INFO_TIMEOUT),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+ /* No-Motion */
+ {
+ .type = IIO_EV_TYPE_ROC,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all =
+ BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HYSTERESIS) | BIT(IIO_EV_INFO_TIMEOUT),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+ /* Tilt-Detection */
+ {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_LOW_PASS_FILTER_3DB),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+// clang-format off
+#define SMI330_ACCEL_CHANNEL(_type, _axis, _index) { \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_ENABLE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_ENABLE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_dir = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_dir_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+ .event_spec = smi330_accel_events, \
+ .num_event_specs = ARRAY_SIZE(smi330_accel_events), \
+}
+
+#define SMI330_GYRO_CHANNEL(_type, _axis, _index) { \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_ENABLE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_ENABLE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_dir = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_dir_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
+#define SMI330_TEMP_CHANNEL(_index) { \
+ .type = IIO_TEMP, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_TEMP_OBJECT, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
+// clang-format on
+
+static const struct iio_chan_spec smi330_channels[] = {
+ SMI330_ACCEL_CHANNEL(IIO_ACCEL, X, SMI330_SCAN_ACCEL_X),
+ SMI330_ACCEL_CHANNEL(IIO_ACCEL, Y, SMI330_SCAN_ACCEL_Y),
+ SMI330_ACCEL_CHANNEL(IIO_ACCEL, Z, SMI330_SCAN_ACCEL_Z),
+ SMI330_GYRO_CHANNEL(IIO_ANGL_VEL, X, SMI330_SCAN_GYRO_X),
+ SMI330_GYRO_CHANNEL(IIO_ANGL_VEL, Y, SMI330_SCAN_GYRO_Y),
+ SMI330_GYRO_CHANNEL(IIO_ANGL_VEL, Z, SMI330_SCAN_GYRO_Z),
+ SMI330_TEMP_CHANNEL(SMI330_TEMP_OBJECT),
+ IIO_CHAN_SOFT_TIMESTAMP(SMI330_SCAN_TIMESTAMP),
+};
+
+static const struct smi330_sysfs_attr smi330_mode_attr = {
+ .reg_vals = (int[]){ SMI330_MODE_SUSPEND, SMI330_MODE_LOW_POWER,
+ SMI330_MODE_NORMAL, SMI330_MODE_HIGH_PERF },
+ .vals = (int[]){ 0, 3, 4, 7 },
+ .len = 4,
+ .type = IIO_VAL_INT
+};
+
+static const struct smi330_sysfs_attr smi330_mode_alt_gyro_attr = {
+ .reg_vals = (int[]){ SMI330_MODE_SUSPEND, SMI330_MODE_GYRO_DRIVE,
+ SMI330_MODE_LOW_POWER, SMI330_MODE_NORMAL,
+ SMI330_MODE_HIGH_PERF },
+ .vals = (int[]){ 0, 1, 3, 4, 7 },
+ .len = 5,
+ .type = IIO_VAL_INT
+};
+
+static const struct smi330_sysfs_attr smi330_accel_scale_attr = {
+ .reg_vals = (int[]){ SMI330_ACCEL_RANGE_2G, SMI330_ACCEL_RANGE_4G,
+ SMI330_ACCEL_RANGE_8G, SMI330_ACCEL_RANGE_16G },
+ .vals = (int[]){ 0, 61035, 0, 122070, 0, 244140, 0, 488281 },
+ .len = 8,
+ .type = IIO_VAL_INT_PLUS_NANO
+};
+
+static const struct smi330_sysfs_attr smi330_gyro_scale_attr = {
+ .reg_vals = (int[]){ SMI330_GYRO_RANGE_125, SMI330_GYRO_RANGE_250,
+ SMI330_GYRO_RANGE_500 },
+ .vals = (int[]){ 0, 3814697, 0, 7629395, 0, 15258789 },
+ .len = 6,
+ .type = IIO_VAL_INT_PLUS_NANO
+};
+
+static const struct smi330_sysfs_attr smi330_average_attr = {
+ .reg_vals = (int[]){ SMI330_AVG_NUM_1, SMI330_AVG_NUM_2,
+ SMI330_AVG_NUM_4, SMI330_AVG_NUM_8,
+ SMI330_AVG_NUM_16, SMI330_AVG_NUM_32,
+ SMI330_AVG_NUM_64 },
+ .vals = (int[]){ 1, 2, 4, 8, 16, 32, 64 },
+ .len = 7,
+ .type = IIO_VAL_INT
+};
+
+static const struct smi330_sysfs_attr smi330_bandwidth_attr = {
+ .reg_vals = (int[]){ SMI330_BW_2, SMI330_BW_4 },
+ .vals = (int[]){ 2, 4 },
+ .len = 2,
+ .type = IIO_VAL_INT
+};
+
+static const struct smi330_sysfs_attr smi330_odr_attr = {
+ .reg_vals = (int[]){ SMI330_ODR_0_78125_HZ, SMI330_ODR_1_5625_HZ,
+ SMI330_ODR_3_125_HZ, SMI330_ODR_6_25_HZ,
+ SMI330_ODR_12_5_HZ, SMI330_ODR_25_HZ,
+ SMI330_ODR_50_HZ, SMI330_ODR_100_HZ,
+ SMI330_ODR_200_HZ, SMI330_ODR_400_HZ,
+ SMI330_ODR_800_HZ, SMI330_ODR_1600_HZ,
+ SMI330_ODR_3200_HZ, SMI330_ODR_6400_HZ },
+ .vals = (int[]){ 0, 1, 3, 6, 12, 25, 50, 100, 200, 400, 800, 1600, 3200,
+ 6400 },
+ .len = 14,
+ .type = IIO_VAL_INT
+};
+
+static int smi330_dev_init(struct smi330_data *data);
+
+static int smi330_get_regs_dma(u8 reg_addr, int *reg_data,
+ struct smi330_data *data)
+{
+ int ret, status;
+
+ ret = regmap_read(data->regmap, SMI330_FEATURE_DATA_STATUS_REG,
+ &status);
+ if (ret)
+ return ret;
+
+ if (!FIELD_GET(SMI330_FEATURE_DATA_STATUS_TX_READY_MASK, status))
+ return -EBUSY;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ reg_addr);
+ if (ret)
+ return ret;
+
+ return regmap_read(data->regmap, SMI330_FEATURE_DATA_TX_REG, reg_data);
+}
+
+static int smi330_set_regs_dma(u8 reg_addr, int reg_data,
+ struct smi330_data *data)
+{
+ int ret, status;
+
+ ret = regmap_read(data->regmap, SMI330_FEATURE_DATA_STATUS_REG,
+ &status);
+ if (ret)
+ return ret;
+
+ if (!FIELD_GET(SMI330_FEATURE_DATA_STATUS_TX_READY_MASK, status))
+ return -EBUSY;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ reg_addr);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, SMI330_FEATURE_DATA_TX_REG, reg_data);
+}
+
+static int smi330_get_sysfs_attr(enum smi330_sensor_conf_select config,
+ enum smi330_sensor sensor,
+ const struct smi330_sysfs_attr **attr)
+{
+ switch (config) {
+ case SMI330_ODR:
+ *attr = &smi330_odr_attr;
+ return 0;
+ case SMI330_RANGE:
+ if (sensor == SMI330_ACCEL)
+ *attr = &smi330_accel_scale_attr;
+ else
+ *attr = &smi330_gyro_scale_attr;
+ return 0;
+ case SMI330_BW:
+ *attr = &smi330_bandwidth_attr;
+ return 0;
+ case SMI330_AVG_NUM:
+ *attr = &smi330_average_attr;
+ return 0;
+ case SMI330_MODE:
+ if (sensor == SMI330_ALT_GYRO)
+ *attr = &smi330_mode_alt_gyro_attr;
+ else
+ *attr = &smi330_mode_attr;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smi330_get_odr_ns(enum smi330_odr odr, s64 *odr_ns)
+{
+ switch (odr) {
+ case SMI330_ODR_0_78125_HZ:
+ *odr_ns = 1280000000;
+ break;
+ case SMI330_ODR_1_5625_HZ:
+ *odr_ns = 640000000;
+ break;
+ case SMI330_ODR_3_125_HZ:
+ *odr_ns = 320000000;
+ break;
+ case SMI330_ODR_6_25_HZ:
+ *odr_ns = 160000000;
+ break;
+ case SMI330_ODR_12_5_HZ:
+ *odr_ns = 80000000;
+ break;
+ case SMI330_ODR_25_HZ:
+ *odr_ns = 40000000;
+ break;
+ case SMI330_ODR_50_HZ:
+ *odr_ns = 20000000;
+ break;
+ case SMI330_ODR_100_HZ:
+ *odr_ns = 10000000;
+ break;
+ case SMI330_ODR_200_HZ:
+ *odr_ns = 5000000;
+ break;
+ case SMI330_ODR_400_HZ:
+ *odr_ns = 2500000;
+ break;
+ case SMI330_ODR_800_HZ:
+ *odr_ns = 1250000;
+ break;
+ case SMI330_ODR_1600_HZ:
+ *odr_ns = 625000;
+ break;
+ case SMI330_ODR_3200_HZ:
+ *odr_ns = 312500;
+ break;
+ case SMI330_ODR_6400_HZ:
+ *odr_ns = 156250;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smi330_get_avg_num(enum smi330_avg_num avg, int *avg_num)
+{
+ int i;
+
+ for (i = 0; i < smi330_average_attr.len; i++) {
+ if (smi330_average_attr.reg_vals[i] == avg) {
+ *avg_num = smi330_average_attr.vals[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int smi330_get_config_reg(enum smi330_sensor sensor, int *reg)
+{
+ switch (sensor) {
+ case SMI330_ACCEL:
+ *reg = SMI330_ACCEL_CFG_REG;
+ return 0;
+ case SMI330_GYRO:
+ *reg = SMI330_GYRO_CFG_REG;
+ return 0;
+ case SMI330_ALT_ACCEL:
+ *reg = SMI330_ALT_ACCEL_CFG_REG;
+ return 0;
+ case SMI330_ALT_GYRO:
+ *reg = SMI330_ALT_GYRO_CFG_REG;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smi330_validate_sensor_config(enum smi330_mode mode,
+ enum smi330_odr odr,
+ enum smi330_avg_num avg)
+{
+ int ret, avg_num;
+ s64 odr_ns, min_odr_ns, skipped_samples;
+
+ switch (mode) {
+ case SMI330_MODE_LOW_POWER:
+ if (odr > SMI330_ODR_400_HZ)
+ return -EINVAL;
+
+ ret = smi330_get_odr_ns(SMI330_ODR_6400_HZ, &min_odr_ns);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_odr_ns(odr, &odr_ns);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_avg_num(avg, &avg_num);
+ if (ret)
+ return ret;
+
+ skipped_samples = div_s64(odr_ns, min_odr_ns) - avg_num;
+
+ if (skipped_samples <= 0)
+ return -EINVAL;
+ return 0;
+
+ case SMI330_MODE_NORMAL:
+ case SMI330_MODE_HIGH_PERF:
+ if (odr <= SMI330_ODR_6_25_HZ)
+ return -EINVAL;
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+static int smi330_get_sensor_config_reg(struct smi330_data *data,
+ enum smi330_sensor sensor, int *cfg)
+{
+ int ret, reg;
+
+ ret = smi330_get_config_reg(sensor, ®);
+ if (ret)
+ return ret;
+
+ return regmap_read(data->regmap, reg, cfg);
+}
+
+static int smi330_get_sensor_config(struct smi330_data *data,
+ enum smi330_sensor sensor,
+ enum smi330_sensor_conf_select config,
+ int *value)
+
+{
+ int ret, reg_val, i;
+ const struct smi330_sysfs_attr *attr;
+
+ ret = smi330_get_sensor_config_reg(data, sensor, ®_val);
+ if (ret)
+ return ret;
+
+ switch (config) {
+ case SMI330_ODR:
+ reg_val = FIELD_GET(SMI330_CFG_ODR_MASK, reg_val);
+ break;
+ case SMI330_RANGE:
+ reg_val = FIELD_GET(SMI330_CFG_RANGE_MASK, reg_val);
+ break;
+ case SMI330_BW:
+ reg_val = FIELD_GET(SMI330_CFG_BW_MASK, reg_val);
+ break;
+ case SMI330_AVG_NUM:
+ reg_val = FIELD_GET(SMI330_CFG_AVG_NUM_MASK, reg_val);
+ break;
+ case SMI330_MODE:
+ reg_val = FIELD_GET(SMI330_CFG_MODE_MASK, reg_val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = smi330_get_sysfs_attr(config, sensor, &attr);
+ if (ret)
+ return ret;
+
+ if (attr->type == IIO_VAL_INT) {
+ for (i = 0; i < attr->len; i++) {
+ if (attr->reg_vals[i] == reg_val) {
+ *value = attr->vals[i];
+ return 0;
+ }
+ }
+ } else {
+ for (i = 0; i < attr->len / 2; i++) {
+ if (attr->reg_vals[i] == reg_val) {
+ *value = attr->vals[2 * i + 1];
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int smi330_set_sensor_config_reg(struct smi330_data *data,
+ enum smi330_sensor sensor, int mask,
+ int val)
+{
+ int ret, reg, cfg, error;
+ enum smi330_mode mode;
+ enum smi330_odr odr;
+ enum smi330_avg_num avg;
+ struct device *dev = regmap_get_device(data->regmap);
+
+ ret = smi330_get_config_reg(sensor, ®);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, reg, &cfg);
+ if (ret)
+ return ret;
+
+ cfg = (val & mask) | (cfg & ~mask);
+
+ mode = FIELD_GET(SMI330_CFG_MODE_MASK, cfg);
+ odr = FIELD_GET(SMI330_CFG_ODR_MASK, cfg);
+ avg = FIELD_GET(SMI330_CFG_AVG_NUM_MASK, cfg);
+
+ ret = smi330_validate_sensor_config(mode, odr, avg);
+ if (ret) {
+ dev_err(dev, "Invalid sensor configuration\n");
+ return ret;
+ }
+
+ ret = regmap_write(data->regmap, reg, cfg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, SMI330_ERR_REG, &error);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(SMI330_ERR_ACC_CONF_MASK, error) ||
+ FIELD_GET(SMI330_ERR_GYR_CONF_MASK, error))
+ return -EIO;
+
+ return smi330_get_odr_ns(odr, &data->cfg.odr_ns);
+}
+
+static int smi330_set_sensor_config(struct smi330_data *data,
+ enum smi330_sensor sensor,
+ enum smi330_sensor_conf_select config,
+ int value, bool is_reg_value)
+{
+ int ret, i, reg_value, mask;
+ const struct smi330_sysfs_attr *attr;
+
+ if (is_reg_value) {
+ reg_value = value;
+ } else {
+ ret = smi330_get_sysfs_attr(config, sensor, &attr);
+ if (ret)
+ return ret;
+
+ ret = -EINVAL;
+ for (i = 0; i < attr->len; i++) {
+ if (attr->vals[i] == value) {
+ if (attr->type == IIO_VAL_INT)
+ reg_value = attr->reg_vals[i];
+ else
+ reg_value = attr->reg_vals[i / 2];
+ ret = 0;
+ }
+ }
+ if (ret)
+ return ret;
+ }
+
+ switch (config) {
+ case SMI330_ODR:
+ mask = SMI330_CFG_ODR_MASK;
+ reg_value = FIELD_PREP(mask, reg_value);
+ break;
+ case SMI330_RANGE:
+ mask = SMI330_CFG_RANGE_MASK;
+ reg_value = FIELD_PREP(mask, reg_value);
+ break;
+ case SMI330_BW:
+ mask = SMI330_CFG_BW_MASK;
+ reg_value = FIELD_PREP(mask, reg_value);
+ break;
+ case SMI330_AVG_NUM:
+ mask = SMI330_CFG_AVG_NUM_MASK;
+ reg_value = FIELD_PREP(mask, reg_value);
+ break;
+ case SMI330_MODE:
+ mask = SMI330_CFG_MODE_MASK;
+ reg_value = FIELD_PREP(mask, reg_value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return smi330_set_sensor_config_reg(data, sensor, mask, reg_value);
+}
+
+static int smi330_get_data(struct smi330_data *data, int chan_type, int axis,
+ int *val)
+{
+ u8 reg;
+ int ret, sample;
+
+ switch (chan_type) {
+ case IIO_ACCEL:
+ reg = SMI330_ACCEL_X_REG + (axis - IIO_MOD_X);
+ break;
+ case IIO_ANGL_VEL:
+ reg = SMI330_GYRO_X_REG + (axis - IIO_MOD_X);
+ break;
+ case IIO_TEMP:
+ reg = SMI330_TEMP_REG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_read(data->regmap, reg, &sample);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(sample, 15);
+
+ return 0;
+}
+
+static int smi330_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_ENABLE:
+ *vals = smi330_mode_attr.vals;
+ *length = smi330_mode_attr.len;
+ *type = smi330_mode_attr.type;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_ACCEL) {
+ *vals = smi330_accel_scale_attr.vals;
+ *length = smi330_accel_scale_attr.len;
+ *type = smi330_accel_scale_attr.type;
+ } else {
+ *vals = smi330_gyro_scale_attr.vals;
+ *length = smi330_gyro_scale_attr.len;
+ *type = smi330_gyro_scale_attr.type;
+ }
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = smi330_average_attr.vals;
+ *length = smi330_average_attr.len;
+ *type = smi330_average_attr.type;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *vals = smi330_bandwidth_attr.vals;
+ *length = smi330_bandwidth_attr.len;
+ *type = smi330_bandwidth_attr.type;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = smi330_odr_attr.vals;
+ *length = smi330_odr_attr.len;
+ *type = smi330_odr_attr.type;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int smi330_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ struct smi330_data *data = iio_priv(indio_dev);
+ enum smi330_sensor sensor;
+
+ /* valid for all channel types */
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = smi330_get_data(data, chan->type, chan->channel2, val);
+ iio_device_release_direct(indio_dev);
+ return ret ? ret : IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ sensor = SMI330_ACCEL;
+ break;
+ case IIO_ANGL_VEL:
+ sensor = SMI330_GYRO;
+ break;
+ case IIO_TEMP:
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = SMI330_TEMP_SCALE / GIGA;
+ *val2 = SMI330_TEMP_SCALE % GIGA;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = SMI330_TEMP_OFFSET;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ /* valid for acc and gyro channels */
+ switch (mask) {
+ case IIO_CHAN_INFO_ENABLE:
+ ret = smi330_get_sensor_config(data, sensor, SMI330_MODE, val);
+ return ret ? ret : IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = smi330_get_sensor_config(data, sensor, SMI330_AVG_NUM,
+ val);
+ return ret ? ret : IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ ret = smi330_get_sensor_config(data, sensor, SMI330_BW, val);
+ return ret ? ret : IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = smi330_get_sensor_config(data, sensor, SMI330_ODR, val);
+ return ret ? ret : IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ ret = smi330_get_sensor_config(data, sensor, SMI330_RANGE,
+ val2);
+ return ret ? ret : IIO_VAL_INT_PLUS_NANO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smi330_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ int ret;
+ struct smi330_data *data = iio_priv(indio_dev);
+ enum smi330_sensor sensor;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ sensor = SMI330_ACCEL;
+ break;
+ case IIO_ANGL_VEL:
+ sensor = SMI330_GYRO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_ENABLE:
+ ret = smi330_set_sensor_config(data, sensor, SMI330_MODE, val,
+ false);
+ if (ret)
+ return ret;
+
+ if (sensor == SMI330_ACCEL)
+ data->savestate.acc_pwr = val;
+ else
+ data->savestate.gyr_pwr = val;
+ return 0;
+ case IIO_CHAN_INFO_SCALE:
+ return smi330_set_sensor_config(data, sensor, SMI330_RANGE,
+ val2, false);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return smi330_set_sensor_config(data, sensor, SMI330_AVG_NUM,
+ val, false);
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return smi330_set_sensor_config(data, sensor, SMI330_BW, val,
+ false);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = smi330_set_sensor_config(data, SMI330_ACCEL, SMI330_ODR,
+ val, false);
+ if (ret)
+ return ret;
+
+ return smi330_set_sensor_config(data, SMI330_GYRO, SMI330_ODR,
+ val, false);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smi330_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static u16 smi330_get_fifo_length(struct smi330_data *data)
+{
+ int ret, fill_level;
+
+ ret = regmap_read(data->regmap, SMI330_FIFO_FILL_LEVEL_REG,
+ &fill_level);
+ if (ret)
+ return 0;
+
+ return fill_level & SMI330_FIFO_FILL_LEVEL_MASK;
+}
+
+static int smi330_get_fifo_frame_length(struct iio_dev *indio_dev)
+{
+ int chan;
+ int fifo_frame_length = 0;
+
+ iio_for_each_active_channel(indio_dev, chan) {
+ fifo_frame_length++;
+ }
+
+ return fifo_frame_length;
+}
+
+static int smi330_config_auto_op_mode(struct smi330_data *data,
+ enum smi330_auto_op_mode auto_op_mode)
+{
+ switch (auto_op_mode) {
+ case SMI330_AUTO_OP_EN_ALL:
+ return regmap_update_bits(data->regmap, SMI330_ALT_CONF_REG,
+ SMI330_ALT_CONF_EN_MASK,
+ SMI330_ALT_CONF_EN_MASK);
+ case SMI330_AUTO_OP_EN_ACC:
+ return regmap_update_bits(data->regmap, SMI330_ALT_CONF_REG,
+ SMI330_ALT_CONF_EN_MASK,
+ SMI330_ALT_CONF_ACC_EN_MASK);
+ case SMI330_AUTO_OP_EN_GYR:
+ return regmap_update_bits(data->regmap, SMI330_ALT_CONF_REG,
+ SMI330_ALT_CONF_EN_MASK,
+ SMI330_ALT_CONF_GYR_EN_MASK);
+ case SMI330_AUTO_OP_DISABLE:
+ return regmap_update_bits(data->regmap, SMI330_ALT_CONF_REG,
+ SMI330_ALT_CONF_EN_MASK, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smi330_auto_op_cond(struct smi330_data *data,
+ enum smi330_auto_op_config auto_op_config,
+ enum smi330_auto_op_adv_feat auto_op_adv_feat)
+{
+ int ret, val, mask;
+
+ ret = smi330_get_regs_dma(SMI330_ALT_CONF_CHG_EX_REG, &val, data);
+ if (ret)
+ return ret;
+
+ switch (auto_op_config) {
+ case SMI330_AUTO_OP_CONFIG_ALT:
+ if (FIELD_GET(SMI330_ALT_CONF_CHG_USER_MASK, val) !=
+ auto_op_adv_feat) {
+ mask = SMI330_ALT_CONF_CHG_ALT_MASK;
+ break;
+ }
+ return -EIO;
+ case SMI330_AUTO_OP_CONFIG_USER:
+ if (FIELD_GET(SMI330_ALT_CONF_CHG_ALT_MASK, val) !=
+ auto_op_adv_feat) {
+ mask = SMI330_ALT_CONF_CHG_USER_MASK;
+ break;
+ }
+ return -EIO;
+ default:
+ return -EINVAL;
+ }
+
+ /* FIELD_PREP is not possible with non-const mask */
+ val = ((auto_op_adv_feat << (__builtin_ffs(mask) - 1)) & mask) |
+ (val & ~mask);
+
+ return smi330_set_regs_dma(SMI330_ALT_CONF_CHG_EX_REG, val, data);
+}
+
+static int smi330_get_st_result(struct smi330_data *data)
+{
+ int ret, st_result, io1_data;
+
+ ret = regmap_read_poll_timeout(
+ data->regmap, SMI330_FEATURE_IO1_REG, io1_data,
+ FIELD_GET(SMI330_FEATURE_IO1_SC_COMPLETE_MASK, io1_data) == 1,
+ SMI330_FEAT_ENG_POLL, SMI330_FEAT_ENG_TIMEOUT);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(SMI330_FEATURE_IO1_ST_RESULT_MASK, io1_data)) {
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ SMI330_ST_RESULT_EX_REG);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, SMI330_FEATURE_DATA_TX_REG,
+ &st_result);
+ if (ret)
+ return ret;
+
+ if (st_result != SMI330_ST_SUCCESS_MASK)
+ return -EIO;
+ } else {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int smi330_disable_alt_conf_acc_gyr_mode(struct smi330_data *data)
+{
+ int ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+ if (ret)
+ return ret;
+
+ return smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+}
+
+static int smi330_st_precondition(struct smi330_data *data)
+{
+ int mask, acc_cfg;
+
+ mask = SMI330_CFG_MODE_MASK | SMI330_CFG_ODR_MASK;
+
+ acc_cfg = FIELD_PREP(SMI330_CFG_MODE_MASK, SMI330_MODE_NORMAL) |
+ FIELD_PREP(SMI330_CFG_ODR_MASK, SMI330_ODR_100_HZ);
+
+ return smi330_set_sensor_config_reg(data, SMI330_ACCEL, mask, acc_cfg);
+}
+
+static int smi330_trigger_self_test(struct smi330_data *data)
+{
+ int ret;
+
+ ret = smi330_st_precondition(data);
+ if (ret)
+ return ret;
+
+ ret = smi330_disable_alt_conf_acc_gyr_mode(data);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, SMI330_CMD_REG, SMI330_CMD_SELF_TEST);
+}
+
+static int smi330_set_gyro_filter_coefficients(struct smi330_data *data)
+{
+ int ret;
+ s16 data_array[9];
+
+ data_array[0] = SMI330_SC_ST_VALUE_0;
+ data_array[1] = SMI330_SC_ST_VALUE_1;
+ data_array[2] = SMI330_SC_ST_VALUE_2;
+ data_array[3] = SMI330_SC_ST_VALUE_3;
+ data_array[4] = SMI330_SC_ST_VALUE_4;
+ data_array[5] = SMI330_SC_ST_VALUE_5;
+ data_array[6] = SMI330_SC_ST_VALUE_6;
+ data_array[7] = SMI330_SC_ST_VALUE_7;
+ data_array[8] = SMI330_SC_ST_VALUE_8;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ SMI330_GYRO_SC_ST_VALUES_EX_REG);
+ if (ret)
+ return ret;
+
+ return regmap_bulk_write(data->regmap, SMI330_FEATURE_DATA_TX_REG,
+ data_array, ARRAY_SIZE(data_array));
+}
+
+static int smi330_set_self_test_mode(struct smi330_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ SMI330_ST_SELECT_EX_REG);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, SMI330_FEATURE_DATA_TX_REG,
+ SMI330_ST_SELECT_ACC_GYR_MASK);
+}
+
+static int smi330_perform_self_test(struct smi330_data *data)
+{
+ int ret, acc_cfg, cfg_restore;
+ s16 data_array[9] = { 0 };
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ SMI330_GYRO_SC_ST_VALUES_EX_REG);
+ if (ret == 0)
+ ret = regmap_bulk_write(data->regmap,
+ SMI330_FEATURE_DATA_TX_REG, data_array,
+ ARRAY_SIZE(data_array));
+ if (ret == 0)
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ SMI330_GYRO_SC_ST_VALUES_EX_REG);
+ if (ret == 0)
+ ret = regmap_bulk_read(data->regmap, SMI330_FEATURE_DATA_TX_REG,
+ data_array, ARRAY_SIZE(data_array));
+
+ if (ret == 0 && (data_array[3] != SMI330_SC_ST_VALUE_3 &&
+ (data_array[0] != SMI330_SC_ST_VALUE_0 ||
+ data_array[1] != SMI330_SC_ST_VALUE_1 ||
+ data_array[2] != SMI330_SC_ST_VALUE_2 ||
+ data_array[4] != SMI330_SC_ST_VALUE_4 ||
+ data_array[5] != SMI330_SC_ST_VALUE_5 ||
+ data_array[6] != SMI330_SC_ST_VALUE_6 ||
+ data_array[7] != SMI330_SC_ST_VALUE_7 ||
+ data_array[8] != SMI330_SC_ST_VALUE_8))) {
+ ret = smi330_set_gyro_filter_coefficients(data);
+ }
+
+ if (ret == 0)
+ ret = smi330_set_self_test_mode(data);
+
+ /*
+ * Save accel configurations in separate variable to
+ * restore configuration independent from other errors.
+ */
+ cfg_restore =
+ smi330_get_sensor_config_reg(data, SMI330_ACCEL, &acc_cfg);
+ ret = cfg_restore;
+
+ if (ret == 0)
+ ret = smi330_trigger_self_test(data);
+
+ if (ret == 0)
+ ret = smi330_get_st_result(data);
+
+ if (cfg_restore == 0)
+ /* Restore accel configurations */
+ ret = smi330_set_sensor_config_reg(data, SMI330_ACCEL,
+ SMI330_CFG_MASK, acc_cfg);
+
+ return ret;
+}
+
+static int smi330_self_calib_select(struct smi330_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_DATA_ADDR_REG,
+ SMI330_GYRO_SC_SELECT_EX_REG);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, SMI330_FEATURE_DATA_TX_REG,
+ SMI330_GYRO_SC_SELECT_ALL_MASK);
+}
+
+static int smi330_self_calib_preconfig(struct smi330_data *data)
+{
+ int ret, acc_cfg;
+ int mask = SMI330_CFG_MODE_MASK | SMI330_CFG_ODR_MASK;
+
+ acc_cfg = FIELD_PREP(SMI330_CFG_MODE_MASK, SMI330_MODE_HIGH_PERF) |
+ FIELD_PREP(SMI330_CFG_ODR_MASK, SMI330_ODR_100_HZ);
+
+ ret = smi330_set_sensor_config_reg(data, SMI330_ACCEL, mask, acc_cfg);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+ if (ret)
+ return ret;
+
+ return smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+}
+
+static void smi330_self_calib_restore_config(struct smi330_data *data,
+ int accel_config,
+ int accel_alt_config,
+ int gyro_alt_config)
+{
+ smi330_set_sensor_config_reg(data, SMI330_ACCEL, SMI330_CFG_MASK,
+ accel_config);
+
+ smi330_set_sensor_config_reg(data, SMI330_ALT_ACCEL, SMI330_CFG_MASK,
+ accel_alt_config);
+
+ smi330_set_sensor_config_reg(data, SMI330_ALT_GYRO, SMI330_CFG_MASK,
+ gyro_alt_config);
+}
+
+static int smi330_self_calibration(struct smi330_data *data,
+ struct iio_dev *indio_dev)
+{
+ int ret, io1_data, acc_cfg, acc_alt_cfg, gyr_alt_cfg;
+
+ ret = regmap_read(data->regmap, SMI330_FEATURE_IO1_REG, &io1_data);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(SMI330_FEATURE_IO1_STATE_MASK, io1_data))
+ return -EBUSY;
+
+ ret = smi330_self_calib_select(data);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_sensor_config_reg(data, SMI330_ACCEL, &acc_cfg);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_sensor_config_reg(data, SMI330_ALT_ACCEL,
+ &acc_alt_cfg);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_sensor_config_reg(data, SMI330_ALT_GYRO, &gyr_alt_cfg);
+ if (ret)
+ return ret;
+
+ ret = smi330_self_calib_preconfig(data);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(data->regmap, SMI330_CMD_REG,
+ SMI330_CMD_SELF_CALIBRATION);
+ if (ret)
+ goto out;
+
+ ret = regmap_read_poll_timeout(
+ data->regmap, SMI330_FEATURE_IO1_REG, io1_data,
+ FIELD_GET(SMI330_FEATURE_IO1_SC_COMPLETE_MASK, io1_data) == 1,
+ SMI330_FEAT_ENG_POLL, SMI330_FEAT_ENG_TIMEOUT);
+
+out:
+ smi330_self_calib_restore_config(data, acc_cfg, acc_alt_cfg,
+ gyr_alt_cfg);
+ return ret;
+}
+
+static int smi330_enable_feature_engine(struct smi330_data *data)
+{
+ int ret, io1;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_IO2_REG,
+ SMI330_FEATURE_IO2_STARTUP_CONFIG);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_IO_STATUS_REG,
+ SMI330_FEATURE_IO_STATUS_MASK);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, SMI330_FEATURE_CTRL_REG,
+ SMI330_FEATURE_CTRL_ENABLE);
+ if (ret)
+ return ret;
+
+ return regmap_read_poll_timeout(data->regmap, SMI330_FEATURE_IO1_REG,
+ io1,
+ FIELD_GET(SMI330_FEATURE_IO1_ERROR_MASK,
+ io1) == 1,
+ SMI330_FEAT_ENG_POLL,
+ SMI330_FEAT_ENG_TIMEOUT);
+}
+
+static int smi330_enable_adv_feat(struct smi330_data *data, int mask, int val)
+{
+ int ret, io_status;
+
+ /* update advanced feature config */
+ ret = regmap_update_bits(data->regmap, SMI330_FEATURE_IO0_REG, mask,
+ val);
+ if (ret)
+ return ret;
+
+ /* sync settings */
+ io_status = FIELD_PREP(SMI330_FEATURE_IO_STATUS_MASK, 1);
+ ret = regmap_write(data->regmap, SMI330_FEATURE_IO_STATUS_REG,
+ io_status);
+ return ret;
+}
+
+static int smi330_soft_reset(struct smi330_data *data)
+{
+ int ret, dummy_byte;
+
+ ret = regmap_write(data->regmap, SMI330_CMD_REG, SMI330_CMD_SOFT_RESET);
+ if (ret)
+ return ret;
+ fsleep(SMI330_SOFT_RESET_DELAY);
+
+ /* Performing a dummy read after a soft-reset */
+ regmap_read(data->regmap, SMI330_CHIP_ID_REG, &dummy_byte);
+ if (ret)
+ return ret;
+
+ if (data->cfg.feat_irq != SMI330_INT_DISABLED)
+ ret = smi330_enable_feature_engine(data);
+
+ return ret;
+}
+
+static int smi330_fifo_handler(struct iio_dev *indio_dev)
+{
+ int ret, index, chan, i, fifo_frame_length, fifo_length, frame_count;
+ struct smi330_data *data = iio_priv(indio_dev);
+ s16 *iio_buffer_iter = data->buf;
+ s16 *fifo_iter = data->fifo;
+ s64 tsamp = data->cfg.odr_ns;
+ s64 timestamp;
+
+ /* Ignore if buffer disabled */
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ fifo_frame_length = smi330_get_fifo_frame_length(indio_dev);
+ fifo_length = smi330_get_fifo_length(data);
+ frame_count = fifo_length / fifo_frame_length;
+
+ if (data->last_timestamp != 0 && frame_count != 0)
+ tsamp = div_s64(data->current_timestamp - data->last_timestamp,
+ frame_count);
+
+ ret = regmap_noinc_read(data->regmap, SMI330_FIFO_DATA_REG, data->fifo,
+ fifo_length * sizeof(s16));
+ if (ret)
+ return ret;
+
+ for (i = 0; i < frame_count; i++) {
+ index = 0;
+ fifo_iter = &data->fifo[i * fifo_frame_length];
+
+ iio_for_each_active_channel(indio_dev, chan) {
+ iio_buffer_iter[index++] = fifo_iter[chan];
+ }
+ timestamp =
+ data->current_timestamp - tsamp * (frame_count - i - 1);
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buf,
+ timestamp);
+ }
+
+ data->last_timestamp = data->current_timestamp;
+
+ return 0;
+}
+
+static irqreturn_t smi330_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct smi330_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret, sample, chan;
+ int i = 0;
+
+ /* Ignore if buffer disabled */
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ if (data->cfg.op_mode == SMI330_FIFO) {
+ dev_warn(dev, "Can't use trigger when FIFO enabled\n");
+ return -EBUSY;
+ }
+
+ if (data->cfg.op_mode == SMI330_IDLE)
+ data->current_timestamp = iio_get_time_ns(indio_dev);
+
+ if (*indio_dev->active_scan_mask == SMI330_ALL_CHAN_MSK) {
+ ret = regmap_bulk_read(data->regmap, SMI330_ACCEL_X_REG,
+ data->buf, ARRAY_SIZE(smi330_channels));
+ if (ret)
+ goto out;
+ } else {
+ iio_for_each_active_channel(indio_dev, chan) {
+ ret = regmap_read(data->regmap,
+ SMI330_ACCEL_X_REG + chan, &sample);
+ if (ret)
+ goto out;
+ data->buf[i++] = sample;
+ }
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buf,
+ data->current_timestamp);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t smi330_irq_thread_handler(int irq, void *indio_dev_)
+{
+ int ret, int_stat;
+ s16 int_status[2] = { 0 };
+ struct iio_dev *indio_dev = indio_dev_;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->lock);
+
+ data->current_timestamp = atomic64_read(&data->irq_timestamp);
+
+ ret = regmap_bulk_read(data->regmap, SMI330_INT1_STATUS_REG, int_status,
+ 2);
+ if (ret)
+ return IRQ_NONE;
+
+ int_stat = int_status[0] | int_status[1];
+
+ if (FIELD_GET(SMI330_INT_STATUS_FWM_MASK, int_stat) ||
+ FIELD_GET(SMI330_INT_STATUS_FFULL_MASK, int_stat)) {
+ ret = smi330_fifo_handler(indio_dev);
+ if (ret)
+ return IRQ_NONE;
+ }
+
+ if (FIELD_GET(SMI330_INT_STATUS_ACC_DRDY_MASK, int_stat) ||
+ FIELD_GET(SMI330_INT_STATUS_GYR_DRDY_MASK, int_stat)) {
+ iio_trigger_poll_nested(data->trig);
+ }
+
+ if (FIELD_GET(SMI330_INT_STATUS_ANYMO_MASK, int_stat)) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_ROC,
+ IIO_EV_DIR_RISING),
+ data->current_timestamp);
+ }
+
+ if (FIELD_GET(SMI330_INT_STATUS_NOMO_MASK, int_stat)) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_ROC,
+ IIO_EV_DIR_FALLING),
+ data->current_timestamp);
+ }
+
+ if (FIELD_GET(SMI330_INT_STATUS_TILT_MASK, int_stat)) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_EITHER),
+ data->current_timestamp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t smi330_irq_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ atomic64_set(&data->irq_timestamp, iio_get_time_ns(indio_dev));
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int smi330_set_int_pin_config(struct smi330_data *data,
+ enum smi330_int_out irq_num,
+ bool active_high, bool open_drain,
+ bool latch)
+{
+ int ret, mask, val;
+
+ val = active_high ? SMI330_IO_INT_CTRL_LVL : 0;
+ val |= open_drain ? SMI330_IO_INT_CTRL_OD : 0;
+ val |= SMI330_IO_INT_CTRL_EN;
+
+ switch (irq_num) {
+ case SMI330_INT_1:
+ mask = SMI330_IO_INT_CTRL_INT1_MASK;
+ val = FIELD_PREP(mask, val);
+ break;
+ case SMI330_INT_2:
+ mask = SMI330_IO_INT_CTRL_INT2_MASK;
+ val = FIELD_PREP(mask, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, SMI330_IO_INT_CTRL_REG, mask,
+ val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, SMI330_INT_CONF_REG,
+ SMI330_INT_CONF_LATCH_MASK,
+ FIELD_PREP(SMI330_INT_CONF_LATCH_MASK,
+ latch));
+}
+
+static int smi330_setup_irq(struct device *dev, struct iio_dev *indio_dev,
+ int irq, enum smi330_int_out irq_num)
+{
+ int ret, irq_type;
+ bool open_drain, active_high, latch;
+ struct smi330_data *data = iio_priv(indio_dev);
+ struct irq_data *desc;
+ struct fwnode_handle *fwnode;
+
+ desc = irq_get_irq_data(irq);
+ if (!desc)
+ return -EINVAL;
+
+ irq_type = irqd_get_trigger_type(desc);
+ switch (irq_type) {
+ case IRQF_TRIGGER_RISING:
+ latch = false;
+ active_high = true;
+ break;
+ case IRQF_TRIGGER_HIGH:
+ latch = true;
+ active_high = true;
+ break;
+ case IRQF_TRIGGER_FALLING:
+ latch = false;
+ active_high = false;
+ break;
+ case IRQF_TRIGGER_LOW:
+ latch = true;
+ active_high = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fwnode = dev_fwnode(dev);
+ if (!fwnode)
+ return -ENODEV;
+
+ open_drain = fwnode_property_read_bool(fwnode, "drive-open-drain");
+
+ ret = smi330_set_int_pin_config(data, irq_num, active_high, open_drain,
+ latch);
+ if (ret)
+ return ret;
+
+ return devm_request_threaded_irq(dev, irq, smi330_irq_handler,
+ smi330_irq_thread_handler, irq_type,
+ indio_dev->name, indio_dev);
+}
+
+static int smi330_register_irq(struct device *dev, struct iio_dev *indio_dev)
+{
+ int ret, irq;
+ struct smi330_data *data = iio_priv(indio_dev);
+ struct smi330_cfg *cfg = &data->cfg;
+ struct fwnode_handle *fwnode;
+
+ fwnode = dev_fwnode(dev);
+ if (!fwnode)
+ return -ENODEV;
+
+ irq = fwnode_irq_get_byname(fwnode, "INT1");
+ if (irq > 0) {
+ ret = smi330_setup_irq(dev, indio_dev, irq, SMI330_INT_1);
+ if (ret)
+ return ret;
+ } else if (cfg->data_irq == SMI330_INT_1 ||
+ cfg->feat_irq == SMI330_INT_1) {
+ return -ENODEV;
+ }
+
+ irq = fwnode_irq_get_byname(fwnode, "INT2");
+ if (irq > 0) {
+ ret = smi330_setup_irq(dev, indio_dev, irq, SMI330_INT_2);
+ if (ret)
+ return ret;
+ } else if (cfg->data_irq == SMI330_INT_2 ||
+ cfg->feat_irq == SMI330_INT_2) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int smi330_hw_fifo_enable(struct iio_dev *indio_dev)
+{
+ int ret, cfg, chan;
+ int val = 0;
+ enum smi330_mode acc_mode = SMI330_MODE_SUSPEND;
+ enum smi330_mode gyr_mode = SMI330_MODE_SUSPEND;
+ struct smi330_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+
+ data->last_timestamp = 0;
+
+ ret = smi330_get_sensor_config_reg(data, SMI330_ACCEL, &cfg);
+ if (ret)
+ return ret;
+
+ acc_mode = FIELD_GET(SMI330_CFG_MODE_MASK, cfg);
+ ret = smi330_get_sensor_config_reg(data, SMI330_GYRO, &cfg);
+ if (ret)
+ return ret;
+
+ gyr_mode = FIELD_GET(SMI330_CFG_MODE_MASK, cfg);
+
+ if (acc_mode == SMI330_MODE_LOW_POWER ||
+ gyr_mode == SMI330_MODE_LOW_POWER) {
+ dev_warn(dev, "Fifo can't be enabled in low power mode");
+ return -EINVAL;
+ }
+
+ iio_for_each_active_channel(indio_dev, chan) {
+ switch (chan) {
+ case SMI330_SCAN_ACCEL_X:
+ case SMI330_SCAN_ACCEL_Y:
+ case SMI330_SCAN_ACCEL_Z:
+ val |= FIELD_PREP(SMI330_FIFO_CONF_ACC_MASK, 1);
+ break;
+ case SMI330_SCAN_GYRO_X:
+ case SMI330_SCAN_GYRO_Y:
+ case SMI330_SCAN_GYRO_Z:
+ val |= FIELD_PREP(SMI330_FIFO_CONF_GYR_MASK, 1);
+ break;
+ case SMI330_TEMP_OBJECT:
+ val |= FIELD_PREP(SMI330_FIFO_CONF_TEMP_MASK, 1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ret = regmap_update_bits(data->regmap, SMI330_FIFO_CONF_REG,
+ SMI330_FIFO_CONF_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(SMI330_INT_MAP2_FIFO_WM_MASK, data->cfg.data_irq) |
+ FIELD_PREP(SMI330_INT_MAP2_FIFO_FULL_MASK, data->cfg.data_irq);
+ ret = regmap_update_bits(data->regmap, SMI330_INT_MAP2_REG,
+ SMI330_INT_MAP2_FIFO_MASK, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, SMI330_FIFO_CTRL_REG,
+ SMI330_FIFO_CTRL_FLUSH_MASK);
+ if (ret)
+ return ret;
+
+ data->cfg.op_mode = SMI330_FIFO;
+
+ return 0;
+}
+
+static int smi330_hw_fifo_disable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = regmap_update_bits(data->regmap, SMI330_FIFO_CONF_REG,
+ SMI330_FIFO_CONF_MASK, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, SMI330_INT_MAP2_REG,
+ SMI330_INT_MAP2_FIFO_MASK, 0);
+ if (ret)
+ return ret;
+
+ data->cfg.op_mode = SMI330_IDLE;
+
+ return 0;
+}
+
+static int smi330_hwfifo_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ unsigned int fifo_frame_length =
+ smi330_get_fifo_frame_length(indio_dev);
+ unsigned int abs_max_wm = (SMI330_FIFO_MAX_LENGTH - 1);
+ unsigned int max_wm = abs_max_wm - 3 * max(fifo_frame_length, 3u);
+ unsigned int watermark = min(fifo_frame_length * val, max_wm);
+
+ return regmap_write(data->regmap, SMI330_FIFO_WATERMARK_REG, watermark);
+}
+
+static int smi330_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED)
+ return 0;
+
+ if (data->cfg.data_irq != SMI330_INT_DISABLED)
+ return smi330_hw_fifo_enable(indio_dev);
+
+ return 0;
+}
+
+static int smi330_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ /* Don't break the current interrupt thread handler */
+ mutex_lock(&data->lock);
+
+ if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_TRIGGERED)
+ return 0;
+
+ if (data->cfg.data_irq != SMI330_INT_DISABLED)
+ return smi330_hw_fifo_disable(indio_dev);
+
+ return 0;
+}
+
+static int smi330_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+static int smi330_set_drdy_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ int val;
+ struct smi330_data *data = iio_trigger_get_drvdata(trig);
+ struct device *dev = regmap_get_device(data->regmap);
+
+ if (data->cfg.op_mode == SMI330_FIFO) {
+ dev_warn(dev, "Can't set trigger when FIFO enabled\n");
+ return -EBUSY;
+ }
+
+ if (enable)
+ data->cfg.op_mode = SMI330_DATA_READY;
+ else
+ data->cfg.op_mode = SMI330_IDLE;
+
+ val = FIELD_PREP(SMI330_INT_MAP2_ACC_DRDY_MASK,
+ enable ? data->cfg.data_irq : 0);
+ val |= FIELD_PREP(SMI330_INT_MAP2_GYR_DRDY_MASK,
+ enable ? data->cfg.data_irq : 0);
+ return regmap_update_bits(data->regmap, SMI330_INT_MAP2_REG,
+ SMI330_INT_MAP2_DRDY_MASK, val);
+}
+
+static int smi330_read_odr_reg_value(struct smi330_data *data,
+ enum smi330_odr *odr)
+{
+ int ret, cfg, acc_odr, gyr_odr;
+
+ ret = smi330_get_sensor_config_reg(data, SMI330_ACCEL, &cfg);
+ if (ret)
+ return ret;
+ acc_odr = FIELD_GET(SMI330_CFG_ODR_MASK, cfg);
+
+ ret = smi330_get_sensor_config_reg(data, SMI330_GYRO, &cfg);
+ if (ret)
+ return ret;
+ gyr_odr = FIELD_GET(SMI330_CFG_ODR_MASK, cfg);
+
+ if (acc_odr == gyr_odr)
+ *odr = acc_odr;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t alt_odr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret, acc_odr, gyr_odr;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_sensor_config(data, SMI330_ALT_ACCEL, SMI330_ODR,
+ &acc_odr);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_sensor_config(data, SMI330_ALT_GYRO, SMI330_ODR,
+ &gyr_odr);
+ if (ret)
+ return ret;
+
+ if (acc_odr == gyr_odr)
+ return snprintf(buf, PAGE_SIZE, "%d\n", acc_odr);
+
+ return snprintf(buf, PAGE_SIZE,
+ "ODR read failed, potential ACC and GYRO mismatch\n");
+}
+
+static ssize_t alt_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, status;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, SMI330_ALT_STATUS_REG, &status);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", status);
+}
+
+static ssize_t alt_acc_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, mode;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_sensor_config(data, SMI330_ALT_ACCEL, SMI330_MODE,
+ &mode);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t alt_acc_avg_num_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, avg_num;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_sensor_config(data, SMI330_ALT_ACCEL, SMI330_AVG_NUM,
+ &avg_num);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", avg_num);
+}
+
+static ssize_t alt_gyr_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, mode;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_sensor_config(data, SMI330_ALT_GYRO, SMI330_MODE,
+ &mode);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t alt_gyr_avg_num_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, avg_num;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_sensor_config(data, SMI330_ALT_GYRO, SMI330_AVG_NUM,
+ &avg_num);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", avg_num);
+}
+
+static ssize_t hwfifo_watermark_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, watermark;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, SMI330_FIFO_WATERMARK_REG, &watermark);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n",
+ watermark & SMI330_FIFO_WATERMARK_MASK);
+}
+
+static ssize_t hwfifo_watermark_min_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", 1);
+}
+
+static ssize_t hwfifo_watermark_max_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", SMI330_FIFO_MAX_LENGTH - 1);
+}
+
+static ssize_t hwfifo_fill_level_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ int fill_level = smi330_get_fifo_length(data);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", fill_level);
+}
+
+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 smi330_data *data = iio_priv(indio_dev);
+
+ if (data->cfg.op_mode == SMI330_FIFO)
+ return sprintf(buf, "1\n");
+ else
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t control_auto_op_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 op_mode;
+
+ ret = kstrtou16(buf, 10, &op_mode);
+ if (ret)
+ return ret;
+
+ if (op_mode != SMI330_AUTO_OP_EN_ACC &&
+ op_mode != SMI330_AUTO_OP_EN_GYR &&
+ op_mode != SMI330_AUTO_OP_EN_ALL &&
+ op_mode != SMI330_AUTO_OP_DISABLE) {
+ return -EINVAL;
+ }
+
+ ret = smi330_config_auto_op_mode(data, op_mode);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t config_user_overwrite_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 user_overwrite;
+
+ ret = kstrtou16(buf, 10, &user_overwrite);
+ if (ret)
+ return ret;
+
+ if (user_overwrite != SMI330_AUTO_OP_RESET &&
+ user_overwrite != SMI330_AUTO_OP_SET) {
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, SMI330_ALT_CONF_REG,
+ SMI330_ALT_CONF_RST_CONF_EN_MASK,
+ FIELD_PREP(SMI330_ALT_CONF_RST_CONF_EN_MASK,
+ user_overwrite));
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t set_auto_op_mode_cond_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 option;
+
+ ret = kstrtou16(buf, 10, &option);
+ if (ret)
+ return ret;
+
+ switch (option) {
+ case SMI330_AUTO_OP_USER_A:
+ ret = smi330_auto_op_cond(data, SMI330_AUTO_OP_CONFIG_USER,
+ A_NO_MOTION);
+ break;
+
+ case SMI330_AUTO_OP_USER_B:
+ ret = smi330_auto_op_cond(data, SMI330_AUTO_OP_CONFIG_USER,
+ B_ANY_MOTION);
+ break;
+
+ case SMI330_AUTO_OP_USER_H:
+ ret = smi330_auto_op_cond(data, SMI330_AUTO_OP_CONFIG_USER,
+ H_TILT_DETECTION);
+ break;
+
+ case SMI330_AUTO_OP_ALT_A:
+ ret = smi330_auto_op_cond(data, SMI330_AUTO_OP_CONFIG_ALT,
+ A_NO_MOTION);
+ break;
+
+ case SMI330_AUTO_OP_ALT_B:
+ ret = smi330_auto_op_cond(data, SMI330_AUTO_OP_CONFIG_ALT,
+ B_ANY_MOTION);
+ break;
+
+ case SMI330_AUTO_OP_ALT_H:
+ ret = smi330_auto_op_cond(data, SMI330_AUTO_OP_CONFIG_ALT,
+ H_TILT_DETECTION);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t alt_odr_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 odr;
+
+ ret = kstrtou16(buf, 10, &odr);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_ODR, odr,
+ false);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_ODR, odr,
+ false);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t alt_acc_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 mode;
+
+ ret = kstrtou16(buf, 10, &mode);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_MODE,
+ mode, false);
+ if (ret)
+ return ret;
+
+ data->savestate.alt_acc_pwr = mode;
+
+ return count;
+}
+
+static ssize_t alt_acc_avg_num_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 avg_num;
+
+ ret = kstrtou16(buf, 10, &avg_num);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_AVG_NUM,
+ avg_num, false);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t alt_gyr_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 mode;
+
+ ret = kstrtou16(buf, 10, &mode);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_MODE, mode,
+ false);
+ if (ret)
+ return ret;
+
+ data->savestate.alt_gyr_pwr = mode;
+
+ return count;
+}
+
+static ssize_t alt_gyr_avg_num_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+ u16 avg_num;
+
+ ret = kstrtou16(buf, 10, &avg_num);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_AVG_NUM,
+ avg_num, false);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t self_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_perform_self_test(data);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t self_cal_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_self_calibration(data, indio_dev);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t soft_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_soft_reset(data);
+ if (ret)
+ return ret;
+
+ ret = smi330_dev_init(data);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static int smi330_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ int ret, cfg;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = regmap_read(data->regmap, SMI330_FEATURE_IO0_REG, &cfg);
+ if (ret)
+ return ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_ROC:
+ if (dir == IIO_EV_DIR_RISING) {
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ return FIELD_GET(SMI330_FEATURE_IO0_ANYMO_X_EN_MASK,
+ cfg);
+ case IIO_MOD_Y:
+ return FIELD_GET(SMI330_FEATURE_IO0_ANYMO_Y_EN_MASK,
+ cfg);
+ case IIO_MOD_Z:
+ return FIELD_GET(SMI330_FEATURE_IO0_ANYMO_Z_EN_MASK,
+ cfg);
+ default:
+ return 0;
+ }
+ }
+ if (dir == IIO_EV_DIR_FALLING) {
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ return FIELD_GET(SMI330_FEATURE_IO0_NOMO_X_EN_MASK,
+ cfg);
+ case IIO_MOD_Y:
+ return FIELD_GET(SMI330_FEATURE_IO0_NOMO_Y_EN_MASK,
+ cfg);
+ case IIO_MOD_Z:
+ return FIELD_GET(SMI330_FEATURE_IO0_NOMO_Z_EN_MASK,
+ cfg);
+ default:
+ return 0;
+ }
+ }
+ return 0;
+ case IIO_EV_TYPE_CHANGE:
+ if (dir == IIO_EV_DIR_EITHER)
+ return FIELD_GET(SMI330_FEATURE_IO0_TILT_EN_MASK, cfg);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static int smi330_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, bool state)
+{
+ int ret, int_mask, int_val, en_mask, en_val;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ switch (type) {
+ case IIO_EV_TYPE_ROC:
+ if (dir == IIO_EV_DIR_RISING) {
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ en_mask = SMI330_FEATURE_IO0_ANYMO_X_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ break;
+ case IIO_MOD_Y:
+ en_mask = SMI330_FEATURE_IO0_ANYMO_Y_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ break;
+ case IIO_MOD_Z:
+ en_mask = SMI330_FEATURE_IO0_ANYMO_Z_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ break;
+ default:
+ break;
+ }
+ int_mask = SMI330_INT_MAP1_ANYMO_MASK;
+ int_val = FIELD_PREP(int_mask, data->cfg.feat_irq);
+ break;
+ } else if (dir == IIO_EV_DIR_FALLING) {
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ en_mask = SMI330_FEATURE_IO0_NOMO_X_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ break;
+ case IIO_MOD_Y:
+ en_mask = SMI330_FEATURE_IO0_NOMO_Y_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ break;
+ case IIO_MOD_Z:
+ en_mask = SMI330_FEATURE_IO0_NOMO_Z_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ break;
+ default:
+ break;
+ }
+ int_mask = SMI330_INT_MAP1_NOMO_MASK;
+ int_val = FIELD_PREP(int_mask, data->cfg.feat_irq);
+ break;
+ }
+ return -EINVAL;
+ case IIO_EV_TYPE_CHANGE:
+ if (dir == IIO_EV_DIR_EITHER) {
+ en_mask = SMI330_FEATURE_IO0_TILT_EN_MASK;
+ en_val = FIELD_PREP(en_mask, state);
+ int_mask = SMI330_INT_MAP1_TILT_MASK;
+ int_val = FIELD_PREP(int_mask, data->cfg.feat_irq);
+ break;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+
+ ret = smi330_enable_adv_feat(data, en_mask, en_val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, SMI330_INT_MAP1_REG, int_mask,
+ int_val);
+}
+
+static int smi330_get_event_reg_mask(enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *reg,
+ int *mask)
+{
+ switch (type) {
+ case IIO_EV_TYPE_ROC:
+ if (dir == IIO_EV_DIR_RISING) {
+ switch (info) {
+ case IIO_EV_INFO_PERIOD:
+ *reg = SMI330_ANYMO_3_EX_REG;
+ *mask = SMI330_MOTION3_DURATION_MASK;
+ return 0;
+ case IIO_EV_INFO_VALUE:
+ *reg = SMI330_ANYMO_1_EX_REG;
+ *mask = SMI330_MOTION1_SLOPE_THRES_MASK;
+ return 0;
+ case IIO_EV_INFO_HYSTERESIS:
+ *reg = SMI330_ANYMO_2_EX_REG;
+ *mask = SMI330_MOTION2_HYSTERESIS_MASK;
+ return 0;
+ case IIO_EV_INFO_TIMEOUT:
+ *reg = SMI330_ANYMO_3_EX_REG;
+ *mask = SMI330_MOTION3_WAIT_TIME_MASK;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ } else if (dir == IIO_EV_DIR_FALLING) {
+ switch (info) {
+ case IIO_EV_INFO_PERIOD:
+ *reg = SMI330_NOMO_3_EX_REG;
+ *mask = SMI330_MOTION3_DURATION_MASK;
+ return 0;
+ case IIO_EV_INFO_VALUE:
+ *reg = SMI330_NOMO_1_EX_REG;
+ *mask = SMI330_MOTION1_SLOPE_THRES_MASK;
+ return 0;
+ case IIO_EV_INFO_HYSTERESIS:
+ *reg = SMI330_NOMO_2_EX_REG;
+ *mask = SMI330_MOTION2_HYSTERESIS_MASK;
+ return 0;
+ case IIO_EV_INFO_TIMEOUT:
+ *reg = SMI330_NOMO_3_EX_REG;
+ *mask = SMI330_MOTION3_WAIT_TIME_MASK;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+ case IIO_EV_TYPE_CHANGE:
+ if (dir == IIO_EV_DIR_EITHER) {
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ *reg = SMI330_TILT_1_EX_REG;
+ *mask = SMI330_TILT1_MIN_ANGLE_MASK;
+ return 0;
+ case IIO_EV_INFO_PERIOD:
+ *reg = SMI330_TILT_1_EX_REG;
+ *mask = SMI330_TILT1_SEGMENT_SIZE_MASK;
+ return 0;
+ case IIO_EV_INFO_LOW_PASS_FILTER_3DB:
+ *reg = SMI330_TILT_2_EX_REG;
+ *mask = SMI330_TILT2_BETA_ACC_MEAN_MASK;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smi330_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val,
+ int *val2)
+{
+ int ret, reg, mask, config;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_event_reg_mask(type, dir, info, ®, &mask);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_regs_dma(reg, &config, data);
+ if (ret)
+ return ret;
+
+ /* FIELD_GET is not possible with non-const mask */
+ *val = ((config) & (mask)) >> (__builtin_ffs(mask) - 1);
+
+ return IIO_VAL_INT;
+}
+
+static int smi330_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val, int val2)
+{
+ int ret, reg, mask, config;
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_get_event_reg_mask(type, dir, info, ®, &mask);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_regs_dma(reg, &config, data);
+ if (ret)
+ return ret;
+
+ /* FIELD_PREP is not possible with non-const mask */
+ config = ((val << (__builtin_ffs(mask) - 1)) & mask) | (config & ~mask);
+
+ return smi330_set_regs_dma(reg, config, data);
+}
+
+static int smi330_suspend(struct device *dev)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = iio_device_suspend_triggering(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ACCEL, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_GYRO, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+ if (ret)
+ return ret;
+
+ return smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_MODE,
+ SMI330_MODE_SUSPEND, true);
+}
+
+static int smi330_resume(struct device *dev)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct smi330_data *data = iio_priv(indio_dev);
+
+ ret = smi330_set_sensor_config(data, SMI330_ACCEL, SMI330_MODE,
+ data->savestate.acc_pwr, true);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_GYRO, SMI330_MODE,
+ data->savestate.gyr_pwr, true);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_ACCEL, SMI330_MODE,
+ data->savestate.alt_acc_pwr, true);
+ if (ret)
+ return ret;
+
+ ret = smi330_set_sensor_config(data, SMI330_ALT_GYRO, SMI330_MODE,
+ data->savestate.alt_gyr_pwr, true);
+ if (ret)
+ return ret;
+
+ if (data->cfg.op_mode == SMI330_FIFO) {
+ ret = regmap_write(data->regmap, SMI330_FIFO_CTRL_REG,
+ SMI330_FIFO_CTRL_FLUSH_MASK);
+ if (ret)
+ return ret;
+ }
+
+ return iio_device_resume_triggering(indio_dev);
+}
+
+DEFINE_SIMPLE_DEV_PM_OPS(smi330_pm_ops, smi330_suspend, smi330_resume);
+
+static IIO_DEVICE_ATTR_WO(control_auto_op_mode, 0);
+static IIO_DEVICE_ATTR_WO(set_auto_op_mode_cond, 0);
+static IIO_DEVICE_ATTR_WO(config_user_overwrite, 0);
+
+static IIO_DEVICE_ATTR_WO(self_test, 0);
+static IIO_DEVICE_ATTR_WO(self_cal, 0);
+static IIO_DEVICE_ATTR_WO(soft_reset, 0);
+
+static IIO_DEVICE_ATTR_RW(alt_acc_mode, 0);
+static IIO_DEVICE_ATTR_RW(alt_acc_avg_num, 0);
+static IIO_DEVICE_ATTR_RW(alt_gyr_mode, 0);
+static IIO_DEVICE_ATTR_RW(alt_gyr_avg_num, 0);
+static IIO_DEVICE_ATTR_RW(alt_odr, 0);
+static IIO_DEVICE_ATTR_RO(alt_status, 0);
+
+static struct attribute *smi330_event_attributes[] = {
+ &iio_dev_attr_control_auto_op_mode.dev_attr.attr,
+ &iio_dev_attr_set_auto_op_mode_cond.dev_attr.attr,
+ &iio_dev_attr_config_user_overwrite.dev_attr.attr,
+ &iio_dev_attr_self_test.dev_attr.attr,
+ &iio_dev_attr_self_cal.dev_attr.attr,
+ &iio_dev_attr_soft_reset.dev_attr.attr,
+ &iio_dev_attr_alt_acc_mode.dev_attr.attr,
+ &iio_dev_attr_alt_acc_avg_num.dev_attr.attr,
+ &iio_dev_attr_alt_gyr_mode.dev_attr.attr,
+ &iio_dev_attr_alt_gyr_avg_num.dev_attr.attr,
+ &iio_dev_attr_alt_odr.dev_attr.attr,
+ &iio_dev_attr_alt_status.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group smi330_event_attribute_group = {
+ .attrs = smi330_event_attributes,
+};
+
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_enabled, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_fill_level, 0);
+
+static const struct iio_dev_attr *smi330_fifo_attributes[] = {
+ &iio_dev_attr_hwfifo_watermark_min, &iio_dev_attr_hwfifo_watermark_max,
+ &iio_dev_attr_hwfifo_watermark, &iio_dev_attr_hwfifo_enabled,
+ &iio_dev_attr_hwfifo_fill_level, NULL,
+};
+
+static const struct iio_buffer_setup_ops smi330_buffer_ops = {
+ .postenable = smi330_buffer_postenable,
+ .predisable = smi330_buffer_predisable,
+ .postdisable = smi330_buffer_postdisable,
+};
+
+static const struct iio_trigger_ops smi330_trigger_ops = {
+ .set_trigger_state = &smi330_set_drdy_trigger_state,
+};
+
+static struct iio_info smi330_info = {
+ .read_event_config = smi330_read_event_config,
+ .read_event_value = smi330_read_event_value,
+ .write_event_config = smi330_write_event_config,
+ .write_event_value = smi330_write_event_value,
+ .read_avail = smi330_read_avail,
+ .read_raw = smi330_read_raw,
+ .write_raw = smi330_write_raw,
+ .write_raw_get_fmt = smi330_write_raw_get_fmt,
+ .hwfifo_set_watermark = smi330_hwfifo_set_watermark,
+};
+
+static int smi330_dev_init(struct smi330_data *data)
+{
+ int ret, chip_id, val;
+ enum smi330_odr odr;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct smi330_cfg *cfg = &data->cfg;
+
+ ret = regmap_read(data->regmap, SMI330_CHIP_ID_REG, &chip_id);
+ if (ret)
+ return ret;
+
+ chip_id &= 0x00FF;
+
+ if (chip_id != SMI330_CHIP_ID)
+ dev_info(dev, "Unknown chip id: 0x%04x\n", chip_id);
+
+ ret = regmap_read(data->regmap, SMI330_ERR_REG, &val);
+ if (ret || FIELD_GET(SMI330_ERR_FATAL_MASK, val))
+ return -ENODEV;
+
+ ret = regmap_read(data->regmap, SMI330_STATUS_REG, &val);
+ if (ret || FIELD_GET(SMI330_STATUS_POR_MASK, val) == 0)
+ return -ENODEV;
+
+ ret = smi330_read_odr_reg_value(data, &odr);
+ if (ret)
+ return ret;
+
+ ret = smi330_get_odr_ns(odr, &cfg->odr_ns);
+ return ret;
+}
+
+int smi330_core_probe(struct device *dev, struct regmap *regmap)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct smi330_data *data;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+ data->regmap = regmap;
+ mutex_init(&data->lock);
+
+ if (IS_ENABLED(CONFIG_SMI330_IRQ_DATA_INT1))
+ data->cfg.data_irq = SMI330_INT_1;
+ else if (IS_ENABLED(CONFIG_SMI330_IRQ_DATA_INT2))
+ data->cfg.data_irq = SMI330_INT_2;
+ else
+ data->cfg.data_irq = SMI330_INT_DISABLED;
+
+ if (IS_ENABLED(CONFIG_SMI330_IRQ_ADV_FEAT_INT1))
+ data->cfg.feat_irq = SMI330_INT_1;
+ else if (IS_ENABLED(CONFIG_SMI330_IRQ_ADV_FEAT_INT2))
+ data->cfg.feat_irq = SMI330_INT_2;
+ else
+ data->cfg.feat_irq = SMI330_INT_DISABLED;
+
+ ret = smi330_soft_reset(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Soft reset failed\n");
+
+ indio_dev->channels = smi330_channels;
+ indio_dev->num_channels = ARRAY_SIZE(smi330_channels);
+ indio_dev->name = "smi330";
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->info = &smi330_info;
+
+ data->cfg.op_mode = SMI330_IDLE;
+ data->savestate.acc_pwr = SMI330_MODE_SUSPEND;
+ data->savestate.gyr_pwr = SMI330_MODE_SUSPEND;
+ data->savestate.alt_acc_pwr = SMI330_MODE_SUSPEND;
+ data->savestate.alt_gyr_pwr = SMI330_MODE_SUSPEND;
+
+ ret = smi330_dev_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Init failed\n");
+
+ if (data->cfg.data_irq != SMI330_INT_DISABLED) {
+ ret = smi330_register_irq(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(
+ dev, ret,
+ "Register IRQ failed - check Kconfig and devicetree\n");
+
+ data->trig = devm_iio_trigger_alloc(dev, "%s-drdy-trigger",
+ indio_dev->name);
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->ops = &smi330_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, data);
+
+ ret = devm_iio_trigger_register(dev, data->trig);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "IIO register trigger failed\n");
+
+ /*
+ * Set default operation mode to data ready,
+ * remove the trigger if you want to use HW fifo.
+ */
+ indio_dev->trig = iio_trigger_get(data->trig);
+ }
+
+ ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev,
+ iio_pollfunc_store_time,
+ smi330_trigger_handler,
+ IIO_BUFFER_DIRECTION_IN,
+ &smi330_buffer_ops,
+ smi330_fifo_attributes);
+ if (ret)
+ return dev_err_probe(dev, ret, "IIO buffer setup failed\n");
+
+ if (data->cfg.feat_irq != SMI330_INT_DISABLED)
+ smi330_info.event_attrs = &smi330_event_attribute_group;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Register IIO device failed\n");
+
+ return 0;
+}
+
+MODULE_AUTHOR("Roman Huber <roman.huber@de.bosch.com>");
+MODULE_AUTHOR("Stefan Gutmann <stefan.gutmann@de.bosch.com>");
+MODULE_DESCRIPTION("Bosch SMI330 driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/iio/imu/smi330/smi330_i2c.c b/drivers/iio/imu/smi330/smi330_i2c.c
new file mode 100644
index 00000000000..42bfc01389b
--- /dev/null
+++ b/drivers/iio/imu/smi330/smi330_i2c.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "smi330.h"
+
+#define I2C_XFER_MAX_RETRY 10
+#define I2C_WRITE_DELAY_TIME 10
+
+#define NUM_DUMMY_BYTES 2
+#define SMI330_I2C_MAX_RX_BUFFER_SIZE (NUM_DUMMY_BYTES + SMI330_FIFO_SIZE)
+
+struct smi330_i2c_priv {
+ struct i2c_client *i2c;
+ u8 rx_buffer[SMI330_I2C_MAX_RX_BUFFER_SIZE] __aligned(IIO_DMA_MINALIGN);
+};
+
+static int smi330_regmap_i2c_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct smi330_i2c_priv *priv = context;
+ int ret, retry;
+
+ /*
+ * SMI330 I2C read frame:
+ * <Slave address[6:0], RnW> <x, Register address[6:0]>
+ * <Slave address[6:0], RnW> <Dummy[7:0]> <Dummy[7:0]> <Data_0[7:0]> <Data_1[15:8]>...
+ * <Data_N[7:0]> <Data_N[15:8]>
+ * Remark: Slave address is not considered part of the frame in the following definitions
+ */
+ struct i2c_msg msgs[] = {
+ {
+ .addr = priv->i2c->addr,
+ .flags = priv->i2c->flags,
+ .len = reg_size,
+ .buf = (u8 *)reg_buf,
+ },
+ {
+ .addr = priv->i2c->addr,
+ .flags = priv->i2c->flags | I2C_M_RD,
+ .len = NUM_DUMMY_BYTES + val_size,
+ .buf = priv->rx_buffer,
+ },
+ };
+
+ for (retry = 0; retry < I2C_XFER_MAX_RETRY; retry++) {
+ ret = i2c_transfer(priv->i2c->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret > 0)
+ break;
+
+ usleep_range(I2C_WRITE_DELAY_TIME * 1000,
+ I2C_WRITE_DELAY_TIME * 1000);
+ }
+
+ if (retry >= I2C_XFER_MAX_RETRY) {
+ dev_err(&priv->i2c->adapter->dev, "Xfer error");
+ return -EIO;
+ }
+
+ memcpy(val_buf, priv->rx_buffer + NUM_DUMMY_BYTES, val_size);
+
+ return 0;
+}
+
+static int smi330_regmap_i2c_write(void *context, const void *data,
+ size_t count)
+{
+ struct smi330_i2c_priv *priv = context;
+ u8 reg;
+
+ /*
+ * SMI330 I2C write frame:
+ * <Slave address[6:0], RnW> <x, Register address[6:0]> <Data_0[7:0]> <Data_1[15:8]>...
+ * <Data_N[7:0]> <Data_N[15:8]>
+ * Remark: Slave address is not considered part of the frame in the following definitions
+ */
+ reg = *(u8 *)data;
+ return i2c_smbus_write_i2c_block_data(priv->i2c, reg,
+ count - sizeof(u8),
+ data + sizeof(u8));
+}
+
+static const struct regmap_bus smi330_regmap_bus = {
+ .read = smi330_regmap_i2c_read,
+ .write = smi330_regmap_i2c_write,
+};
+
+static const struct regmap_config smi330_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int smi330_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct smi330_i2c_priv *priv;
+ struct regmap *regmap;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->i2c = i2c;
+ regmap = devm_regmap_init(dev, &smi330_regmap_bus, priv,
+ &smi330_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to initialize I2C Regmap\n");
+
+ return smi330_core_probe(dev, regmap);
+}
+
+static const struct i2c_device_id smi330_i2c_device_id[] = {
+ { .name = "smi330" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, smi330_i2c_device_id);
+
+static const struct of_device_id smi330_of_match[] = {
+ { .compatible = "bosch,smi330" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, smi330_of_match);
+
+static struct i2c_driver smi330_i2c_driver = {
+ .probe = smi330_i2c_probe,
+ .id_table = smi330_i2c_device_id,
+ .driver = { .of_match_table = smi330_of_match,
+ .name = "smi330_i2c",
+ .pm = pm_sleep_ptr(&smi330_pm_ops),
+ },
+};
+module_i2c_driver(smi330_i2c_driver);
diff --git a/drivers/iio/imu/smi330/smi330_spi.c b/drivers/iio/imu/smi330/smi330_spi.c
new file mode 100644
index 00000000000..833732ae281
--- /dev/null
+++ b/drivers/iio/imu/smi330/smi330_spi.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "smi330.h"
+
+static int smi330_regmap_spi_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct spi_device *spi = context;
+
+ return spi_write_then_read(spi, reg_buf, reg_size, val_buf, val_size);
+}
+
+static int smi330_regmap_spi_write(void *context, const void *data,
+ size_t count)
+{
+ struct spi_device *spi = context;
+ u8 *data_buff = (u8 *)data;
+
+ data_buff[1] = data_buff[0];
+ return spi_write(spi, data_buff + 1, count - 1);
+}
+
+static const struct regmap_bus smi330_regmap_bus = {
+ .read = smi330_regmap_spi_read,
+ .write = smi330_regmap_spi_write,
+ .read_flag_mask = 0x80,
+};
+
+static const struct regmap_config smi330_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .pad_bits = 8,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int smi330_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init(&spi->dev, &smi330_regmap_bus, &spi->dev,
+ &smi330_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&spi->dev, PTR_ERR(regmap),
+ "Failed to initialize SPI Regmap\n");
+
+ return smi330_core_probe(&spi->dev, regmap);
+}
+
+static const struct spi_device_id smi330_spi_device_id[] = {
+ { .name = "smi330" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, smi330_spi_device_id);
+
+static const struct of_device_id smi330_of_match[] = {
+ { .compatible = "bosch,smi330" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, smi330_of_match);
+
+static struct spi_driver smi330_spi_driver = {
+ .probe = smi330_spi_probe,
+ .id_table = smi330_spi_device_id,
+ .driver = { .of_match_table = smi330_of_match,
+ .name = "smi330_spi",
+ .pm = pm_sleep_ptr(&smi330_pm_ops),
+ },
+};
+module_spi_driver(smi330_spi_driver);
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation
2025-05-05 15:16 ` [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation Jianping.Shen
@ 2025-05-05 19:06 ` Jonathan Cameron
0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2025-05-05 19:06 UTC (permalink / raw)
To: Jianping.Shen
Cc: lars, robh, krzk+dt, conor+dt, dima.fedrau, marcelo.schmitt1,
linux-iio, devicetree, linux-kernel, Christian.Lorenz3,
Ulrike.Frauendorf, Kai.Dolde
On Mon, 5 May 2025 17:16:39 +0200
<Jianping.Shen@de.bosch.com> wrote:
> From: Jianping Shen <Jianping.Shen@de.bosch.com>
>
> Add ABI documentation for Bosch imu smi330.
The general rule is that new ABI is unused ABI so we need to think
hard about whether these can be mapped to existing userspace ABI.
Some of the complexity here is for features I'd not expect to even
see in an initial driver submission. The current code is more or
less unreviewable because is too large. Please strip it back to
a more minimal initial driver that we can review and then added
features that can be reviewed as incremental changes. I have no idea
when I'll have time to review a 3312 line driver - might be a few weeks
at least.
For an initial driver I'd remove all the ALT function stuff. That can
come later when we have the basic driver merged.
what to do in each patch of building up the driver depends a bit on
driver complexity but a common pattern would be something like this
(order varies)
1) Dt-binding. This needs to be as complete as possible.
2) Basic driver - sysfs only access to standard ABI.
3) Buffered support
4) Event support (often this comes in a lot of steps for a complex device)
5) Power management support
6) Features that require new ABI.
Aim for each patch / series being fairly short. < 1000ish lines
of code is a fairly hard rule unless it's lots of register defines
or similar easy to review stuff.
< 400 lines or so for other changes.
Complex code needs to be in even smaller chunks. The aim here is to
make good use of reviewer time when they can't get half a day or more
to try and look at a massive single patch.
Note that none of this is a comment on the actual code, just on
how to make it simpler to get a complex device supported upstream.
There are quite a few other drivers undergoing slow feature growth
at the moment across multiple kernel cycles (mostly ADCs right now
but we did this for many of the more complex IMUs in the past).
This is a common situation.
Thanks,
Jonathan
>
> Signed-off-by: Jianping Shen <Jianping.Shen@de.bosch.com>
> ---
> .../ABI/testing/sysfs-bus-iio-smi330 | 149 ++++++++++++++++++
> 1 file changed, 149 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-smi330
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-smi330 b/Documentation/ABI/testing/sysfs-bus-iio-smi330
> new file mode 100644
> index 00000000000..68220926beb
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-smi330
> @@ -0,0 +1,149 @@
> +What: /sys/bus/iio/devices/iio:deviceX/events/self_cal
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + The device offers self-calibration for the gyroscope sensitivity error and the gyroscope offset.
wrap to 80 chars.
> + self-calibration to reduce the gyroscope sensitivity error is also known as component re-trim (CRT).
> + The self-calibration ABI will run the calibration routine and update the data path registers in the device.
> + Before initiating the self-calibration, the accelerometer is required to be enabled in high performance
> + mode with a sample rate preferred in the range of 25 Hz up to 200 Hz and the alternative sensor
> + configurations for accelerometer and gyroscope must be disabled.
> + If these preconditions are not fulfilled, the driver will make sure they are fulfilled by changing appropriate
> + register values and then restore the configuration after the self-calibration has been performed.
Given this is all invisible outside of the driver, the need to save an restore settings does
not need to be mentioned in the ABI docs.
We normally don't expose this sort of calibration routine to userspace as it is highly disruptive and
we need to block it if anything else is going on. The solution is to run it once at driver
bind. Similar to reset below, if the usecase needs to re do it then unbinding and rebinding
the driver reflects the fact we are taking it effectively offline for a while.
> + The self-calibration can be triggered by writing '1' to the sysfs entry.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/self_test
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + The self-test of the device checks for a correct function of the accelerometer as well as the gyroscope.
> + The execution of the self-test expects the following prerequisites to be fulfilled.
> + For the self-test, the accelerometer must be configured to high performance mode at least, and the
> + alternative sensor configurations for accelerometer and gyroscope must be disabled. If these preconditions
> + are not fulfilled, the driver will make sure they are fulfilled by changing appropriate register values
> + and then restore the configuration after the self-test has been performed.
> + Once a self test is initiated, the output of data of the device to the registers and FIFO data buffer
> + as well as all features are disabled. While the self-test is in progress, the host is not allowed to modify
> + the configuration of the device.
Similar to above in that there is too much info here and typically we do this as well on
driver bind - not on demand because it is highly disruptive as you note.
> + The self-test can be triggered by writing '1' to the sysfs entry.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/soft_reset
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + The softreset performs a fundamental reset to the device which is largely equivalent to a power cycle.
> + Following a delay, all user configuration settings are overwritten with their default state wherever applicable.
> + To access the serial interface after a soft reset, the same timing constraints apply as for power on.
> + The soft-reset can be triggered by writing '1' to the sysfs entry.
Usual time we do a soft reset is on driver probe. If it needs to be controlled from
userspace later, a driver unbind and bind will have the same effect.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/control_auto_op_mode
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + The auto-operation mode change is a built-in feature to support the smart power management of the device.
> + The function provides automatic switching among two sets of operation modes for its accelerometer and gyroscope.
> + In the following, the one set of configurations consists of ACC_CONF and GYR_CONF for the accelerometer and gyroscope
> + and is called user configuration. The other set sensor of configurations consists of ALT_ACC_CONF and ALT_GYR_CONF,
> + and is called alternative configuration. The switching is initiated by events of enabled advanced features
> + or by commands sent from the host to switch from alternative configuration to user configuration.
A lot of device specific detail the user doesn't need to know about.
I assume this boils down to a form of runtime power management? Wire it up to that
power management infrastructure. If people don't want it they can disable
runtime PM and pay the power cost.
Seems unlikely to me that anyone will be interested in only doing it for
one or other sensor, so a single runtime pm standard control is probably
sufficient. Runtime PM is an advanced feature though so may not belong
in the 'initial' driver submission.
As the next attribute shows it can be triggered by other things.
> + The advanced feature engine and interrupts must be enabled for the auto-operation mode to take effect.
> + Available options:
> + 0 - Enables switching possiblility to alternate configuration for accelerometer
> + 1 - Enables switching possiblility to alternate configuration for gyroscope
> + 2 - Enables switching possiblility to alternate configuration for accelerometer and gyroscope
> + 3 - Disables auto-operation mode change
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/set_auto_op_mode_cond
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + The conditions to switch operation mode must be configured by selecting one of the advanced features
> + that will trigger the auto-operation mode change.
> + The same advanced feature cannot be selected as a trigger for both user configuration and alternative configuration.
> + Available options:
> + 0 - Change to user settings by A_NO_MOTION
> + 1 - Change to user settings by A_ANY_MOTION
> + 2 - Change to user settings by H_TILT_DETECTION
> + 3 - Change to alternative settings by A_NO_MOTION
> + 4 - Change to alternative settings by A_ANY_MOTION
> + 5 - Change to alternative settings by H_TILT_DETECTION
Ok. This is an interesting feature. However it is also an advanced feature
that probably doesn't belong in an initial driver submission where it will
just slow down the chances of anything getting merged. I'd drop this
support and ABI for now. I'm not immediately sure how we would control
it but the ABI will need to be a closer fit for existing ABI than this is.
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/config_user_overwrite
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + The configurations of the sensors can be instantly reset to the user configuration by directly writing
> + to either ACC_CONF or GYR_CONF, if the option is enabled.
> + Available options:
> + 0 - No mode change when writing to ACC_CONF or GYR_CONF
> + 1 - Any write to ACC_CONF or GYR_CONF will instanly switch back to associated user configuration
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/alt_status
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + Reports the active configuration for the accelerometer and gyroscope.
> + Available options:
> + 0x00 - ACC_CONF and GYR_CONF are used
> + 0x01 - ALT_ACC_CONF and GYR_CONF are used
> + 0x10 - ACC_CONF and ALT_GYR_CONF are used
> + 0x11 - ALT_ACC_CONF and ALT_GYR_CONF are used
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/alt_odr
For later discussion but it would need to map to something more directly
related to sampling_frequency which is the primary ABI for this.
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + Set alternative output data rate (for accelerometer and gyroscope).
> + Available options:
For what it is worth IIO ABI support decimals. So use them, not rounding to nearest
integer.
> + 0 - 0.78125 Hz
> + 1 - 1.5625 Hz
> + 3 - 3.125 Hz
> + 6 - 6.25 Hz
> + 12 - 12.5 Hz
> + 25 - 25 Hz
> + 50 - 50 Hz
> + 100 - 100 Hz
> + 200 - 200 Hz
> + 400 - 400 Hz
> + 800 - 800 Hz
> + 1600 - 1600 Hz
> + 3200 - 3200 Hz
> + 6400 - 6400 Hz
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/alt_acc_mode
Power modes are rarely a useful ABI because a user cares about characteristics
of those modes, not the datasheet terms for them. They care what frequency
or precision they get.
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + Set alternative accelerometer power mode.
> + Available options:
> + 0 - Suspend
Suspend is something we'd do via runtime PM or similar. Automatically establish
when the sensor isn't being used and save some power. Leaving it to userspace
control basically means it isn't used and the device ends up always powered up.
> + 3 - Low power
> + 4 - Normal
> + 7 - Performance
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/alt_gyr_mode
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + Set alternative gyroscope power mode.
> + Available options:
> + 0 - Suspend
> + 3 - Low power
> + 4 - Normal
> + 7 - Performance
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/alt_acc_avg_num
This would need to map to standard oversampling_ratio ABI.
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + Set alternative accelerometer averaging number.
> + Available options:
> + 1, 2, 4, 8, 16, 32, 64
> +
> +What: /sys/bus/iio/devices/iio:deviceX/events/alt_gyr_avg_num
> +KernelVersion: 6.16
> +Contact: Stefan.Gutmann@de.bosch.com
> +Description:
> + Set alternative gyroscope averaging number.
> + Available options:
> + 1, 2, 4, 8, 16, 32, 64
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 2/3] dt-bindings: iio: imu: smi330: Add binding
2025-05-05 15:16 ` [PATCH v1 2/3] dt-bindings: iio: imu: smi330: Add binding Jianping.Shen
@ 2025-05-06 16:19 ` Conor Dooley
0 siblings, 0 replies; 7+ messages in thread
From: Conor Dooley @ 2025-05-06 16:19 UTC (permalink / raw)
To: Jianping.Shen
Cc: jic23, lars, robh, krzk+dt, conor+dt, dima.fedrau,
marcelo.schmitt1, linux-iio, devicetree, linux-kernel,
Christian.Lorenz3, Ulrike.Frauendorf, Kai.Dolde
[-- Attachment #1: Type: text/plain, Size: 2216 bytes --]
On Mon, May 05, 2025 at 05:16:40PM +0200, Jianping.Shen@de.bosch.com wrote:
> From: Jianping Shen <Jianping.Shen@de.bosch.com>
>
> Add devicetree binding for Bosch imu smi330.
> The smi330 is a combined three axis angular rate and
> three axis acceleration sensor module.
>
> Signed-off-by: Jianping Shen <Jianping.Shen@de.bosch.com>
> ---
> .../bindings/iio/imu/bosch,smi330.yaml | 89 +++++++++++++++++++
> 1 file changed, 89 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
> new file mode 100644
> index 00000000000..fb65bd26ada
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/imu/bosch,smi330.yaml
> @@ -0,0 +1,89 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/imu/bosch,smi330.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Bosch SMI330 6-Axis IMU
> +
> +maintainers:
> + - Stefan Gutmann <stefam.gutmann@de.bosch.com>
> +
> +description:
> + SMI330 is a 6-axis inertial measurement unit that supports acceleration and
> + gyroscopic measurements with hardware fifo buffering. Sensor also provides
> + events information such as motion, no-motion and tilt detection.
> +
> +properties:
> + compatible:
> + const: bosch,smi330
> +
> + reg:
> + maxItems: 1
> +
> + vdd-supply:
> + description: provide VDD power to the sensor.
> +
> + vddio-supply:
> + description: provide VDD IO power to the sensor.
> +
> + interrupts:
> + minItems: 1
> + maxItems: 2
> +
> + interrupt-names:
> + minItems: 1
> + maxItems: 2
> + items:
> + enum:
> + - INT1
> + - INT2
> +
> + drive-open-drain:
This property is missing a type, no? boolean or flag
> + description:
> + set if the specified interrupt pin should be configured as
> + open drain. If not set, defaults to push-pull.
What "specified interrupt pin"? You've got two interrupts, does this
apply to one? Both?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 3/3] iio: imu: smi330: Add driver
2025-05-05 15:16 ` [PATCH v1 3/3] iio: imu: smi330: Add driver Jianping.Shen
@ 2025-05-09 15:58 ` kernel test robot
0 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-05-09 15:58 UTC (permalink / raw)
To: Jianping.Shen, jic23, lars, robh, krzk+dt, conor+dt, dima.fedrau,
marcelo.schmitt1, linux-iio, devicetree, linux-kernel,
Christian.Lorenz3, Ulrike.Frauendorf, Kai.Dolde
Cc: llvm, oe-kbuild-all
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on jic23-iio/togreg]
[also build test WARNING on linus/master v6.15-rc5 next-20250509]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jianping-Shen-de-bosch-com/docs-iio-imu-smi330-Add-ABI-documentation/20250505-232315
base: https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
patch link: https://lore.kernel.org/r/20250505151641.52878-4-Jianping.Shen%40de.bosch.com
patch subject: [PATCH v1 3/3] iio: imu: smi330: Add driver
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20250509/202505092349.jx1FRafM-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250509/202505092349.jx1FRafM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505092349.jx1FRafM-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/iio/imu/smi330/smi330_core.c:557:15: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
557 | reg_value = FIELD_PREP(mask, reg_value);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:561:15: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
561 | reg_value = FIELD_PREP(mask, reg_value);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:565:15: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
565 | reg_value = FIELD_PREP(mask, reg_value);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:569:15: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
569 | reg_value = FIELD_PREP(mask, reg_value);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:573:15: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
573 | reg_value = FIELD_PREP(mask, reg_value);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
>> drivers/iio/imu/smi330/smi330_core.c:1010:20: warning: result of comparison of constant 61292 with expression of type 's16' (aka 'short') is always true [-Wtautological-constant-out-of-range-compare]
1010 | data_array[8] != SMI330_SC_ST_VALUE_8))) {
| ~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:1009:20: warning: result of comparison of constant 65485 with expression of type 's16' (aka 'short') is always true [-Wtautological-constant-out-of-range-compare]
1009 | data_array[7] != SMI330_SC_ST_VALUE_7 ||
| ~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:1006:20: warning: result of comparison of constant 65519 with expression of type 's16' (aka 'short') is always true [-Wtautological-constant-out-of-range-compare]
1006 | data_array[4] != SMI330_SC_ST_VALUE_4 ||
| ~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:1004:20: warning: result of comparison of constant 37401 with expression of type 's16' (aka 'short') is always true [-Wtautological-constant-out-of-range-compare]
1004 | data_array[1] != SMI330_SC_ST_VALUE_1 ||
| ~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:1002:33: warning: result of comparison of constant 65512 with expression of type 's16' (aka 'short') is always true [-Wtautological-constant-out-of-range-compare]
1002 | if (ret == 0 && (data_array[3] != SMI330_SC_ST_VALUE_3 &&
| ~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:1374:9: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
1374 | val = FIELD_PREP(mask, val);
| ^~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:1378:9: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
1378 | val = FIELD_PREP(mask, val);
| ^~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
>> drivers/iio/imu/smi330/smi330_core.c:2156:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((en_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (en_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2156 | en_val = FIELD_PREP(en_mask, state);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:2160:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((en_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (en_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2160 | en_val = FIELD_PREP(en_mask, state);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:2164:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((en_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (en_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2164 | en_val = FIELD_PREP(en_mask, state);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
>> drivers/iio/imu/smi330/smi330_core.c:2170:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((int_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (int_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2170 | int_val = FIELD_PREP(int_mask, data->cfg.feat_irq);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:2176:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((en_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (en_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2176 | en_val = FIELD_PREP(en_mask, state);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:2180:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((en_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (en_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2180 | en_val = FIELD_PREP(en_mask, state);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:2184:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((en_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (en_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
2184 | en_val = FIELD_PREP(en_mask, state);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:115:3: note: expanded from macro 'FIELD_PREP'
115 | __BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/bitfield.h:72:53: note: expanded from macro '__BF_FIELD_CHECK'
72 | BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) > \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
73 | __bf_cast_unsigned(_reg, ~0ull), \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
74 | _pfx "type of reg too small for mask"); \
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:39:58: note: expanded from macro 'BUILD_BUG_ON_MSG'
39 | #define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
include/linux/compiler_types.h:557:22: note: expanded from macro 'compiletime_assert'
557 | _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:545:23: note: expanded from macro '_compiletime_assert'
545 | __compiletime_assert(condition, msg, prefix, suffix)
| ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:537:9: note: expanded from macro '__compiletime_assert'
537 | if (!(condition)) \
| ^~~~~~~~~
drivers/iio/imu/smi330/smi330_core.c:2190:14: warning: result of comparison of constant 18446744073709551615 with expression of type 'typeof (_Generic((int_mask), char: (unsigned char)0, unsigned char: (unsigned char)0, signed char: (unsigned char)0, unsigned short: (unsigned short)0, short: (unsigned short)0, unsigned int: (unsigned int)0, int: (unsigned int)0, unsigned long: (unsigned long)0, long: (unsigned long)0, unsigned long long: (unsigned long long)0, long long: (unsigned long long)0, default: (int_mask)))' (aka 'unsigned int') is always false [-Wtautological-constant-out-of-range-compare]
vim +557 drivers/iio/imu/smi330/smi330_core.c
524
525 static int smi330_set_sensor_config(struct smi330_data *data,
526 enum smi330_sensor sensor,
527 enum smi330_sensor_conf_select config,
528 int value, bool is_reg_value)
529 {
530 int ret, i, reg_value, mask;
531 const struct smi330_sysfs_attr *attr;
532
533 if (is_reg_value) {
534 reg_value = value;
535 } else {
536 ret = smi330_get_sysfs_attr(config, sensor, &attr);
537 if (ret)
538 return ret;
539
540 ret = -EINVAL;
541 for (i = 0; i < attr->len; i++) {
542 if (attr->vals[i] == value) {
543 if (attr->type == IIO_VAL_INT)
544 reg_value = attr->reg_vals[i];
545 else
546 reg_value = attr->reg_vals[i / 2];
547 ret = 0;
548 }
549 }
550 if (ret)
551 return ret;
552 }
553
554 switch (config) {
555 case SMI330_ODR:
556 mask = SMI330_CFG_ODR_MASK;
> 557 reg_value = FIELD_PREP(mask, reg_value);
558 break;
559 case SMI330_RANGE:
560 mask = SMI330_CFG_RANGE_MASK;
561 reg_value = FIELD_PREP(mask, reg_value);
562 break;
563 case SMI330_BW:
564 mask = SMI330_CFG_BW_MASK;
565 reg_value = FIELD_PREP(mask, reg_value);
566 break;
567 case SMI330_AVG_NUM:
568 mask = SMI330_CFG_AVG_NUM_MASK;
569 reg_value = FIELD_PREP(mask, reg_value);
570 break;
571 case SMI330_MODE:
572 mask = SMI330_CFG_MODE_MASK;
573 reg_value = FIELD_PREP(mask, reg_value);
574 break;
575 default:
576 return -EINVAL;
577 }
578
579 return smi330_set_sensor_config_reg(data, sensor, mask, reg_value);
580 }
581
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-05-09 15:59 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-05 15:16 [PATCH v1 0/3] iio: imu: smi330: add bosch smi330 driver Jianping.Shen
2025-05-05 15:16 ` [PATCH v1 1/3] docs: iio: imu: smi330: Add ABI documentation Jianping.Shen
2025-05-05 19:06 ` Jonathan Cameron
2025-05-05 15:16 ` [PATCH v1 2/3] dt-bindings: iio: imu: smi330: Add binding Jianping.Shen
2025-05-06 16:19 ` Conor Dooley
2025-05-05 15:16 ` [PATCH v1 3/3] iio: imu: smi330: Add driver Jianping.Shen
2025-05-09 15:58 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox