]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iio: imu: smi330: Add driver
authorJianping Shen <Jianping.Shen@de.bosch.com>
Thu, 9 Oct 2025 15:31:49 +0000 (17:31 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sun, 9 Nov 2025 12:56:51 +0000 (12:56 +0000)
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>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/imu/Kconfig
drivers/iio/imu/Makefile
drivers/iio/imu/smi330/Kconfig [new file with mode: 0644]
drivers/iio/imu/smi330/Makefile [new file with mode: 0644]
drivers/iio/imu/smi330/smi330.h [new file with mode: 0644]
drivers/iio/imu/smi330/smi330_core.c [new file with mode: 0644]
drivers/iio/imu/smi330/smi330_i2c.c [new file with mode: 0644]
drivers/iio/imu/smi330/smi330_spi.c [new file with mode: 0644]

index 9d732bed9fcdac12a13713dba3455c1fdf9f4a53..7e0181c27bb6996bf6a59427cb11f314d2167098 100644 (file)
@@ -125,6 +125,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"
 
index 2ae6344f84699b2f85fff1c8077cb412f6ae2658..13fb7846e9c98448a91001baf8512a3acd8ef8de 100644 (file)
@@ -32,5 +32,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 (file)
index 0000000..856a315
--- /dev/null
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# SMI330 IMU driver
+#
+
+config SMI330
+       tristate
+       select IIO_BUFFER
+       select IIO_TRIGGERED_BUFFER
+
+config SMI330_I2C
+       tristate "Bosch SMI330 I2C driver"
+       depends on I2C
+       select SMI330
+       select REGMAP_I2C
+       help
+         Enable support for the Bosch SMI330 6-Axis IMU connected to I2C
+         interface.
+
+         This driver can also be built as a module. If so, the module will be
+         called smi330_i2c.
+
+config SMI330_SPI
+       tristate "Bosch SMI330 SPI driver"
+       depends on SPI
+       select SMI330
+       select REGMAP_SPI
+       help
+         Enable support for the Bosch SMI330 6-Axis IMU connected to SPI
+         interface.
+
+         This driver can also be built as a module. If so, the module will be
+         called smi330_spi.
diff --git a/drivers/iio/imu/smi330/Makefile b/drivers/iio/imu/smi330/Makefile
new file mode 100644 (file)
index 0000000..c663dcb
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Bosch SMI330 IMU
+#
+obj-$(CONFIG_SMI330) += smi330_core.o
+obj-$(CONFIG_SMI330_I2C) += smi330_i2c.o
+obj-$(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 (file)
index 0000000..a5c7656
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#ifndef _SMI330_H
+#define _SMI330_H
+
+#include <linux/iio/iio.h>
+
+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_SCAN_TIMESTAMP,
+       SMI330_SCAN_LEN = SMI330_SCAN_TIMESTAMP,
+};
+
+extern const struct regmap_config smi330_regmap_config;
+
+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 (file)
index 0000000..7564f12
--- /dev/null
@@ -0,0 +1,918 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "smi330.h"
+
+/* Register map */
+#define SMI330_CHIP_ID_REG 0x00
+#define SMI330_ERR_REG 0x01
+#define SMI330_STATUS_REG 0x02
+#define SMI330_ACCEL_X_REG 0x03
+#define SMI330_GYRO_X_REG 0x06
+#define SMI330_TEMP_REG 0x09
+#define SMI330_INT1_STATUS_REG 0x0D
+#define SMI330_ACCEL_CFG_REG 0x20
+#define SMI330_GYRO_CFG_REG 0x21
+#define SMI330_IO_INT_CTRL_REG 0x38
+#define SMI330_INT_CONF_REG 0x39
+#define SMI330_INT_MAP1_REG 0x3A
+#define SMI330_INT_MAP2_REG 0x3B
+#define SMI330_CMD_REG 0x7E
+
+/* Register mask */
+#define SMI330_CHIP_ID_MASK GENMASK(7, 0)
+#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_ACC_GYR_DRDY_MASK GENMASK(13, 12)
+#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_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_MAP2_ACC_DRDY_MASK GENMASK(11, 10)
+#define SMI330_INT_MAP2_GYR_DRDY_MASK GENMASK(9, 8)
+
+/* Register values */
+#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_CMD_SOFT_RESET 0xDEAF
+
+/* T°C = (temp / 512) + 23 */
+#define SMI330_TEMP_OFFSET 11776 /* 23 * 512 */
+#define SMI330_TEMP_SCALE 1953125 /* (1 / 512) * 1e9 */
+
+#define SMI330_CHIP_ID 0x42
+#define SMI330_SOFT_RESET_DELAY 2000
+
+/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
+#define smi330_field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+#define smi330_field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
+#define SMI330_ACCEL_CHANNEL(_axis) {                                  \
+       .type = IIO_ACCEL,                                              \
+       .modified = 1,                                                  \
+       .channel2 = IIO_MOD_##_axis,                                    \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
+       .info_mask_shared_by_type =                                     \
+               BIT(IIO_CHAN_INFO_SAMP_FREQ) |                          \
+               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_SCALE) |                              \
+               BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |                 \
+               BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),       \
+       .info_mask_shared_by_dir_available =                            \
+               BIT(IIO_CHAN_INFO_SAMP_FREQ),                           \
+       .scan_index = SMI330_SCAN_ACCEL_##_axis,                        \
+       .scan_type = {                                                  \
+               .sign = 's',                                            \
+               .realbits = 16,                                         \
+               .storagebits = 16,                                      \
+               .endianness = IIO_LE,                                   \
+       },                                                              \
+}
+
+#define SMI330_GYRO_CHANNEL(_axis) {                                   \
+       .type = IIO_ANGL_VEL,                                           \
+       .modified = 1,                                                  \
+       .channel2 = IIO_MOD_##_axis,                                    \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
+       .info_mask_shared_by_type =                                     \
+               BIT(IIO_CHAN_INFO_SAMP_FREQ) |                          \
+               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_SCALE) |                              \
+               BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |                 \
+               BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),       \
+       .info_mask_shared_by_dir_available =                            \
+               BIT(IIO_CHAN_INFO_SAMP_FREQ),                           \
+       .scan_index = SMI330_SCAN_GYRO_##_axis,                         \
+       .scan_type = {                                                  \
+               .sign = 's',                                            \
+               .realbits = 16,                                         \
+               .storagebits = 16,                                      \
+               .endianness = IIO_LE,                                   \
+       },                                                              \
+}
+
+#define SMI330_TEMP_CHANNEL(_index) {                  \
+       .type = IIO_TEMP,                               \
+       .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,                   \
+       },                                              \
+}
+
+enum smi330_accel_range {
+       SMI330_ACCEL_RANGE_2G = 0x00,
+       SMI330_ACCEL_RANGE_4G = 0x01,
+       SMI330_ACCEL_RANGE_8G = 0x02,
+       SMI330_ACCEL_RANGE_16G = 0x03
+};
+
+enum smi330_gyro_range {
+       SMI330_GYRO_RANGE_125 = 0x0,
+       SMI330_GYRO_RANGE_250 = 0x01,
+       SMI330_GYRO_RANGE_500 = 0x02
+};
+
+enum smi330_odr {
+       SMI330_ODR_12_5_HZ = 0x05,
+       SMI330_ODR_25_HZ = 0x06,
+       SMI330_ODR_50_HZ = 0x07,
+       SMI330_ODR_100_HZ = 0x08,
+       SMI330_ODR_200_HZ = 0x09,
+       SMI330_ODR_400_HZ = 0x0A,
+       SMI330_ODR_800_HZ = 0x0B,
+       SMI330_ODR_1600_HZ = 0x0C,
+       SMI330_ODR_3200_HZ = 0x0D,
+       SMI330_ODR_6400_HZ = 0x0E
+};
+
+enum smi330_avg_num {
+       SMI330_AVG_NUM_1 = 0x00,
+       SMI330_AVG_NUM_2 = 0x01,
+       SMI330_AVG_NUM_4 = 0x02,
+       SMI330_AVG_NUM_8 = 0x03,
+       SMI330_AVG_NUM_16 = 0x04,
+       SMI330_AVG_NUM_32 = 0x05,
+       SMI330_AVG_NUM_64 = 0x06
+};
+
+enum smi330_mode {
+       SMI330_MODE_SUSPEND = 0x00,
+       SMI330_MODE_GYRO_DRIVE = 0x01,
+       SMI330_MODE_LOW_POWER = 0x03,
+       SMI330_MODE_NORMAL = 0x04,
+       SMI330_MODE_HIGH_PERF = 0x07
+};
+
+enum smi330_bw {
+       SMI330_BW_2 = 0x00, /* ODR/2 */
+       SMI330_BW_4 = 0x01 /* ODR/4 */
+};
+
+enum smi330_operation_mode {
+       SMI330_POLLING,
+       SMI330_DATA_READY,
+};
+
+enum smi330_sensor {
+       SMI330_ACCEL,
+       SMI330_GYRO,
+};
+
+enum smi330_sensor_conf_select {
+       SMI330_ODR,
+       SMI330_RANGE,
+       SMI330_BW,
+       SMI330_AVG_NUM,
+};
+
+enum smi330_int_out {
+       SMI330_INT_DISABLED,
+       SMI330_INT_1,
+       SMI330_INT_2,
+};
+
+struct smi330_attributes {
+       int *reg_vals;
+       int *vals;
+       int len;
+       int type;
+       int mask;
+};
+
+struct smi330_cfg {
+       enum smi330_operation_mode op_mode;
+       enum smi330_int_out data_irq;
+};
+
+struct smi330_data {
+       struct regmap *regmap;
+       struct smi330_cfg cfg;
+       struct iio_trigger *trig;
+       IIO_DECLARE_BUFFER_WITH_TS(__le16, buf, SMI330_SCAN_LEN);
+};
+
+const struct regmap_config smi330_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+EXPORT_SYMBOL_NS_GPL(smi330_regmap_config, "IIO_SMI330");
+
+static const struct iio_chan_spec smi330_channels[] = {
+       SMI330_ACCEL_CHANNEL(X),
+       SMI330_ACCEL_CHANNEL(Y),
+       SMI330_ACCEL_CHANNEL(Z),
+       SMI330_GYRO_CHANNEL(X),
+       SMI330_GYRO_CHANNEL(Y),
+       SMI330_GYRO_CHANNEL(Z),
+       SMI330_TEMP_CHANNEL(-1), /* No buffer support */
+       IIO_CHAN_SOFT_TIMESTAMP(SMI330_SCAN_TIMESTAMP),
+};
+
+static const unsigned long smi330_avail_scan_masks[] = {
+       (BIT(SMI330_SCAN_ACCEL_X) | BIT(SMI330_SCAN_ACCEL_Y) |
+        BIT(SMI330_SCAN_ACCEL_Z) | BIT(SMI330_SCAN_GYRO_X) |
+        BIT(SMI330_SCAN_GYRO_Y) | BIT(SMI330_SCAN_GYRO_Z)),
+       0
+};
+
+static const struct smi330_attributes 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,
+       .mask = SMI330_CFG_RANGE_MASK
+};
+
+static const struct smi330_attributes 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,
+       .mask = SMI330_CFG_RANGE_MASK
+};
+
+static const struct smi330_attributes 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,
+       .mask = SMI330_CFG_AVG_NUM_MASK
+};
+
+static const struct smi330_attributes smi330_bandwidth_attr = {
+       .reg_vals = (int[]){ SMI330_BW_2, SMI330_BW_4 },
+       .vals = (int[]){ 2, 4 },
+       .len = 2,
+       .type = IIO_VAL_INT,
+       .mask = SMI330_CFG_BW_MASK
+};
+
+static const struct smi330_attributes smi330_odr_attr = {
+       .reg_vals = (int[]){ 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[]){ 12, 25, 50, 100, 200, 400, 800, 1600, 3200, 6400 },
+       .len = 10,
+       .type = IIO_VAL_INT,
+       .mask = SMI330_CFG_ODR_MASK
+};
+
+static int smi330_get_attributes(enum smi330_sensor_conf_select config,
+                                enum smi330_sensor sensor,
+                                const struct smi330_attributes **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;
+       default:
+               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;
+       default:
+               return -EINVAL;
+       }
+}
+
+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, reg_val, i;
+       const struct smi330_attributes *attr;
+
+       ret = smi330_get_config_reg(sensor, &reg);
+       if (ret)
+               return ret;
+
+       ret = regmap_read(data->regmap, reg, &reg_val);
+       if (ret)
+               return ret;
+
+       ret = smi330_get_attributes(config, sensor, &attr);
+       if (ret)
+               return ret;
+
+       reg_val = smi330_field_get(attr->mask, reg_val);
+
+       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(struct smi330_data *data,
+                                   enum smi330_sensor sensor,
+                                   enum smi330_sensor_conf_select config,
+                                   int value)
+{
+       int ret, i, reg, reg_val, error;
+       const struct smi330_attributes *attr;
+
+       ret = smi330_get_attributes(config, sensor, &attr);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < attr->len; i++) {
+               if (attr->vals[i] == value) {
+                       if (attr->type == IIO_VAL_INT)
+                               reg_val = attr->reg_vals[i];
+                       else
+                               reg_val = attr->reg_vals[i / 2];
+                       break;
+               }
+       }
+       if (i == attr->len)
+               return -EINVAL;
+
+       ret = smi330_get_config_reg(sensor, &reg);
+       if (ret)
+               return ret;
+
+       reg_val = smi330_field_prep(attr->mask, reg_val);
+       ret = regmap_update_bits(data->regmap, reg, attr->mask, reg_val);
+       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 0;
+}
+
+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_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;
+       }
+}
+
+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_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)
+{
+       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_SCALE:
+               return smi330_set_sensor_config(data, sensor, SMI330_RANGE,
+                                               val2);
+       case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+               return smi330_set_sensor_config(data, sensor, SMI330_AVG_NUM,
+                                               val);
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+               return smi330_set_sensor_config(data, sensor, SMI330_BW, val);
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               return smi330_set_sensor_config(data, sensor, SMI330_ODR, val);
+       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 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);
+
+       return 0;
+}
+
+static irqreturn_t smi330_trigger_handler(int irq, void *p)
+{
+       int ret;
+       struct iio_poll_func *pf = p;
+       struct iio_dev *indio_dev = pf->indio_dev;
+       struct smi330_data *data = iio_priv(indio_dev);
+
+       ret = regmap_bulk_read(data->regmap, SMI330_ACCEL_X_REG, data->buf,
+                              SMI330_SCAN_LEN);
+       if (ret)
+               goto out;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, data->buf, pf->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);
+
+       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_ACC_GYR_DRDY_MASK, int_stat)) {
+               indio_dev->pollfunc->timestamp = iio_get_time_ns(indio_dev);
+               iio_trigger_poll_nested(data->trig);
+       }
+
+       return IRQ_HANDLED;
+}
+
+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, 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:
+               val = FIELD_PREP(SMI330_IO_INT_CTRL_INT1_MASK, val);
+               ret = regmap_update_bits(data->regmap, SMI330_IO_INT_CTRL_REG,
+                                        SMI330_IO_INT_CTRL_INT1_MASK, val);
+               if (ret)
+                       return ret;
+               break;
+       case SMI330_INT_2:
+               val = FIELD_PREP(SMI330_IO_INT_CTRL_INT2_MASK, val);
+               ret = regmap_update_bits(data->regmap, SMI330_IO_INT_CTRL_REG,
+                                        SMI330_IO_INT_CTRL_INT2_MASK, val);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       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;
+
+       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;
+       }
+
+       open_drain = device_property_read_bool(dev, "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, NULL,
+                                        smi330_irq_thread_handler,
+                                        irq_type | IRQF_ONESHOT,
+                                        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 fwnode_handle *fwnode;
+
+       fwnode = dev_fwnode(dev);
+       if (!fwnode)
+               return -ENODEV;
+
+       data->cfg.data_irq = SMI330_INT_DISABLED;
+
+       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;
+               data->cfg.data_irq = SMI330_INT_1;
+       } else {
+               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;
+                       data->cfg.data_irq = SMI330_INT_2;
+               }
+       }
+
+       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);
+
+       if (enable)
+               data->cfg.op_mode = SMI330_DATA_READY;
+       else
+               data->cfg.op_mode = SMI330_POLLING;
+
+       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_ACC_DRDY_MASK |
+                                         SMI330_INT_MAP2_GYR_DRDY_MASK,
+                                 val);
+}
+
+static const struct iio_trigger_ops smi330_trigger_ops = {
+       .set_trigger_state = &smi330_set_drdy_trigger_state,
+};
+
+static struct iio_info smi330_info = {
+       .read_avail = smi330_read_avail,
+       .read_raw = smi330_read_raw,
+       .write_raw = smi330_write_raw,
+       .write_raw_get_fmt = smi330_write_raw_get_fmt,
+};
+
+static int smi330_dev_init(struct smi330_data *data)
+{
+       int ret, chip_id, val, mode;
+       struct device *dev = regmap_get_device(data->regmap);
+
+       ret = regmap_read(data->regmap, SMI330_CHIP_ID_REG, &chip_id);
+       if (ret)
+               return ret;
+
+       chip_id = FIELD_GET(SMI330_CHIP_ID_MASK, chip_id);
+       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)
+               return ret;
+       if (FIELD_GET(SMI330_ERR_FATAL_MASK, val))
+               return -ENODEV;
+
+       ret = regmap_read(data->regmap, SMI330_STATUS_REG, &val);
+       if (ret)
+               return ret;
+       if (FIELD_GET(SMI330_STATUS_POR_MASK, val) == 0)
+               return -ENODEV;
+
+       mode = FIELD_PREP(SMI330_CFG_MODE_MASK, SMI330_MODE_NORMAL);
+
+       ret = regmap_update_bits(data->regmap, SMI330_ACCEL_CFG_REG,
+                                SMI330_CFG_MODE_MASK, mode);
+       if (ret)
+               return ret;
+
+       return regmap_update_bits(data->regmap, SMI330_GYRO_CFG_REG,
+                                 SMI330_CFG_MODE_MASK, mode);
+}
+
+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);
+       data->regmap = regmap;
+
+       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->available_scan_masks = smi330_avail_scan_masks;
+       indio_dev->name = "smi330";
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->info = &smi330_info;
+
+       data->cfg.op_mode = SMI330_POLLING;
+
+       ret = smi330_dev_init(data);
+       if (ret)
+               return dev_err_probe(dev, ret, "Init failed\n");
+
+       ret = smi330_register_irq(dev, indio_dev);
+       if (ret)
+               return dev_err_probe(dev, ret, "Register IRQ failed\n");
+
+       if (data->cfg.data_irq != SMI330_INT_DISABLED) {
+               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. */
+               indio_dev->trig = iio_trigger_get(data->trig);
+       }
+
+       ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+                                             iio_pollfunc_store_time,
+                                             smi330_trigger_handler, NULL);
+       if (ret)
+               return dev_err_probe(dev, ret, "IIO buffer setup failed\n");
+
+       ret = devm_iio_device_register(dev, indio_dev);
+       if (ret)
+               return dev_err_probe(dev, ret, "Register IIO device failed\n");
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(smi330_core_probe, "IIO_SMI330");
+
+MODULE_AUTHOR("Stefan Gutmann <stefan.gutmann@de.bosch.com>");
+MODULE_AUTHOR("Roman Huber <roman.huber@de.bosch.com>");
+MODULE_AUTHOR("Filip Andrei <Andrei.Filip@ro.bosch.com>");
+MODULE_AUTHOR("Drimbarean Avram Andrei <Avram-Andrei.Drimbarean@ro.bosch.com>");
+MODULE_DESCRIPTION("Bosch SMI330 IMU 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 (file)
index 0000000..e5f1825
--- /dev/null
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "smi330.h"
+
+#define SMI330_NUM_DUMMY_BYTES 2
+#define SMI330_I2C_MAX_RX_BUFFER_SIZE \
+       (SMI330_NUM_DUMMY_BYTES + SMI330_SCAN_LEN * sizeof(s16))
+
+struct smi330_i2c_priv {
+       struct i2c_client *i2c;
+       u8 rx_buffer[SMI330_I2C_MAX_RX_BUFFER_SIZE];
+};
+
+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;
+
+       if (SMI330_NUM_DUMMY_BYTES + val_size > SMI330_I2C_MAX_RX_BUFFER_SIZE)
+               return -EINVAL;
+
+       /*
+        * 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 = SMI330_NUM_DUMMY_BYTES + val_size,
+                       .buf = priv->rx_buffer,
+               },
+       };
+
+       ret = i2c_transfer(priv->i2c->adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret < 0)
+               return ret;
+
+       memcpy(val_buf, priv->rx_buffer + SMI330_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 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",
+       },
+};
+module_i2c_driver(smi330_i2c_driver);
+
+MODULE_AUTHOR("Stefan Gutmann <stefan.gutmann@de.bosch.com>");
+MODULE_AUTHOR("Roman Huber <roman.huber@de.bosch.com>");
+MODULE_AUTHOR("Filip Andrei <Andrei.Filip@ro.bosch.com>");
+MODULE_AUTHOR("Drimbarean Avram Andrei <Avram-Andrei.Drimbarean@ro.bosch.com>");
+MODULE_DESCRIPTION("Bosch SMI330 I2C driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("IIO_SMI330");
diff --git a/drivers/iio/imu/smi330/smi330_spi.c b/drivers/iio/imu/smi330/smi330_spi.c
new file mode 100644 (file)
index 0000000..a6044e0
--- /dev/null
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2025 Robert Bosch GmbH.
+ */
+#include <linux/mod_devicetable.h>
+#include <linux/module.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;
+
+       /* Insert pad byte for reading */
+       u8 reg[] = { *(u8 *)reg_buf, 0 };
+
+       if (reg_size + 1 != ARRAY_SIZE(reg)) {
+               dev_err(&spi->dev, "Invalid register size %zu\n", reg_size);
+               return -EINVAL;
+       }
+
+       return spi_write_then_read(spi, reg, ARRAY_SIZE(reg), val_buf,
+                                  val_size);
+}
+
+static int smi330_regmap_spi_write(void *context, const void *data,
+                                  size_t count)
+{
+       struct spi_device *spi = context;
+
+       return spi_write(spi, data, count);
+}
+
+static const struct regmap_bus smi330_regmap_bus = {
+       .read = smi330_regmap_spi_read,
+       .write = smi330_regmap_spi_write,
+       .read_flag_mask = 0x80,
+};
+
+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",
+       },
+};
+module_spi_driver(smi330_spi_driver);
+
+MODULE_AUTHOR("Stefan Gutmann <stefan.gutmann@de.bosch.com>");
+MODULE_AUTHOR("Roman Huber <roman.huber@de.bosch.com>");
+MODULE_AUTHOR("Filip Andrei <Andrei.Filip@ro.bosch.com>");
+MODULE_AUTHOR("Drimbarean Avram Andrei <Avram-Andrei.Drimbarean@ro.bosch.com>");
+MODULE_DESCRIPTION("Bosch SMI330 SPI driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("IIO_SMI330");