From a64c7c7b2d416a7d38e9a29f049d53f0ac1f96be Mon Sep 17 00:00:00 2001 From: Mario Tesi Date: Tue, 21 Mar 2023 14:56:18 +0100 Subject: [PATCH] drivers:iio:stm:imu:st_ism330is: add ST MEMS IMU ISM330IS sensor support Added support tu ISM330IS ST MEMS IMU sensor with following features: - Sensor Hub 1) Maximum of 2 device supported to be chosen from the following part numbers: - LIS2MDL 3-axis magnetometer - LPS22HH pressure 2) Possibility to configure pull up on I2C master interface through device tree 3) Self scan on I2C master interface of supported device, including multi address devices 4) Configure SHUB sensor ODR - Self-test verifying the functionality of the device without moving it. The Self Test procedure can be performed on the Accelerometer and also on Gyro sensor separately using the selftest entry of the sysfs. - Acc, gyro and temperature data reading (with sensitivity and raw data) - External trigger Signed-off-by: Mario Tesi Change-Id: I6de3f216b54244dae7127ae48e38b7939009e248 --- .../bindings/iio/stm/imu/st_ism330is.txt | 55 + drivers/iio/stm/imu/Kconfig | 1 + drivers/iio/stm/imu/Makefile | 1 + drivers/iio/stm/imu/st_ism330is/Kconfig | 26 + drivers/iio/stm/imu/st_ism330is/Makefile | 8 + drivers/iio/stm/imu/st_ism330is/st_ism330is.h | 344 +++++ .../stm/imu/st_ism330is/st_ism330is_core.c | 1210 +++++++++++++++++ .../iio/stm/imu/st_ism330is/st_ism330is_i2c.c | 64 + .../stm/imu/st_ism330is/st_ism330is_shub.c | 952 +++++++++++++ .../iio/stm/imu/st_ism330is/st_ism330is_spi.c | 63 + .../imu/st_ism330is/st_ism330is_triggers.c | 132 ++ stm_iio_configs/ism330is_defconfig | 3 + 12 files changed, 2859 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/stm/imu/st_ism330is.txt create mode 100644 drivers/iio/stm/imu/st_ism330is/Kconfig create mode 100644 drivers/iio/stm/imu/st_ism330is/Makefile create mode 100644 drivers/iio/stm/imu/st_ism330is/st_ism330is.h create mode 100644 drivers/iio/stm/imu/st_ism330is/st_ism330is_core.c create mode 100644 drivers/iio/stm/imu/st_ism330is/st_ism330is_i2c.c create mode 100644 drivers/iio/stm/imu/st_ism330is/st_ism330is_shub.c create mode 100644 drivers/iio/stm/imu/st_ism330is/st_ism330is_spi.c create mode 100644 drivers/iio/stm/imu/st_ism330is/st_ism330is_triggers.c create mode 100644 stm_iio_configs/ism330is_defconfig diff --git a/Documentation/devicetree/bindings/iio/stm/imu/st_ism330is.txt b/Documentation/devicetree/bindings/iio/stm/imu/st_ism330is.txt new file mode 100644 index 000000000000..11cadeb952e9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/stm/imu/st_ism330is.txt @@ -0,0 +1,55 @@ +* st_ism330is driver for imu MEMS sensors + +Required properties for all bus drivers: +- compatible: must be one of: + "st,ism330is" + +Required properties for the i2c bindings: +- reg: i2c slave address + +Required properties for the spi bindings: +- reg: the chipselect index + +- spi-max-frequency: maximal bus speed, should be set to 1000000 unless + constrained by external circuitry. + +Optional properties for all bus drivers: +- vdd-supply: an optional regulator that needs to be on to provide VDD + power to the sensor. + +- vddio-supply: an optional regulator that needs to be on to provide the + VDD IO power to the sensor. + +- mount-matrix: mount rotation matrix. + Refer to iio/mount-matrix.txt for details. + +- enable-sensor-hub: enable i2c master interface. Default is disabled. + +- drive-pullup-shub: enable pullup on master i2c line. + +Example for an spi device node on CS0: + +ism330is-imu@0 { + compatible = "st,ism330is"; + reg = <0x0>; + spi-max-frequency = <1000000>; + vddio-supply = <&sensors_vddio>; + vdd-supply = <&sensors_vdd>; + mount-matrix = "1", "0", "0", + "0", "1", "0", + "0", "0", "1"; + drive-pullup-shub; +}; + +Example for an i2c device node when SA0 tied to ground: + +ism330is-imu@6a { + compatible = "st,ism330is"; + reg = <0x6a>; + vddio-supply = <&sensors_vddio>; + vdd-supply = <&sensors_vdd>; + mount-matrix = "1", "0", "0", + "0", "1", "0", + "0", "0", "1"; + drive-pullup-shub; +}; diff --git a/drivers/iio/stm/imu/Kconfig b/drivers/iio/stm/imu/Kconfig index 2dc9e51bae19..6ad1a36e7a8f 100644 --- a/drivers/iio/stm/imu/Kconfig +++ b/drivers/iio/stm/imu/Kconfig @@ -18,5 +18,6 @@ source "drivers/iio/stm/imu/st_lsm6dsm/Kconfig" source "drivers/iio/stm/imu/st_lsm6dsvx/Kconfig" source "drivers/iio/stm/imu/st_lsm6dso16is/Kconfig" source "drivers/iio/stm/imu/st_lsm6dsv16bx/Kconfig" +source "drivers/iio/stm/imu/st_ism330is/Kconfig" endmenu diff --git a/drivers/iio/stm/imu/Makefile b/drivers/iio/stm/imu/Makefile index 96d4383be9e6..de6199a1c47f 100644 --- a/drivers/iio/stm/imu/Makefile +++ b/drivers/iio/stm/imu/Makefile @@ -16,3 +16,4 @@ obj-y += st_lsm6dsm/ obj-y += st_lsm6dsvx/ obj-y += st_lsm6dso16is/ obj-y += st_lsm6dsv16bx/ +obj-y += st_ism330is/ diff --git a/drivers/iio/stm/imu/st_ism330is/Kconfig b/drivers/iio/stm/imu/st_ism330is/Kconfig new file mode 100644 index 000000000000..5fbcc4a57b4f --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config IIO_ST_ISM330IS + tristate "STMicroelectronics ISM330IS sensor" + depends on (I2C || SPI) + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select IIO_ST_ISM330IS_I2C if (I2C) + select IIO_ST_ISM330IS_SPI if (SPI_MASTER) + help + Say yes here to build support for STMicroelectronics + ISM330IS imu sensors. + + To compile this driver as a module, choose M here: the module + will be called st_ism330is. + +config IIO_ST_ISM330IS_I2C + tristate + select REGMAP_I2C + depends on IIO_ST_ISM330IS + +config IIO_ST_ISM330IS_SPI + tristate + select REGMAP_SPI + depends on IIO_ST_ISM330IS + diff --git a/drivers/iio/stm/imu/st_ism330is/Makefile b/drivers/iio/stm/imu/st_ism330is/Makefile new file mode 100644 index 000000000000..be37599921c7 --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +st_ism330is-y := st_ism330is_core.o \ + st_ism330is_shub.o \ + st_ism330is_triggers.o + +obj-$(CONFIG_IIO_ST_ISM330IS) += st_ism330is.o +obj-$(CONFIG_IIO_ST_ISM330IS_I2C) += st_ism330is_i2c.o +obj-$(CONFIG_IIO_ST_ISM330IS_SPI) += st_ism330is_spi.o diff --git a/drivers/iio/stm/imu/st_ism330is/st_ism330is.h b/drivers/iio/stm/imu/st_ism330is/st_ism330is.h new file mode 100644 index 000000000000..50ca1e1e9d68 --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/st_ism330is.h @@ -0,0 +1,344 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * STMicroelectronics st_ism330is sensor driver + * + * MEMS Software Solutions Team + * + * Copyright 2023 STMicroelectronics Inc. + */ + +#ifndef ST_ISM330IS_H +#define ST_ISM330IS_H + +#include +#include +#include +#include +#include +#include + +#define ST_ISM330IS_DEV_NAME "ism330is" + +#define ST_ISM330IS_REG_FUNC_CFG_ACCESS_ADDR 0x01 +#define ST_ISM330IS_SHUB_REG_MASK BIT(6) + +#define ST_ISM330IS_REG_PIN_CTRL_ADDR 0x02 +#define ST_ISM330IS_SDO_PU_EN_MASK BIT(6) + +#define ST_ISM330IS_REG_WHOAMI_ADDR 0x0f +#define ST_ISM330IS_WHOAMI_VAL 0x22 + +#define ST_ISM330IS_REG_CTRL1_XL_ADDR 0x10 +#define ST_ISM330IS_ODR_XL_MASK GENMASK(7, 4) +#define ST_ISM330IS_FS_XL_MASK GENMASK(3, 2) + +#define ST_ISM330IS_REG_CTRL2_G_ADDR 0x11 +#define ST_ISM330IS_ODR_G_MASK GENMASK(7, 4) +#define ST_ISM330IS_FS_G_MASK GENMASK(3, 1) + +#define ST_ISM330IS_REG_CTRL3_C_ADDR 0x12 +#define ST_ISM330IS_BOOT_MASK BIT(7) +#define ST_ISM330IS_BDU_MASK BIT(6) +#define ST_ISM330IS_H_LACTIVE_MASK BIT(5) +#define ST_ISM330IS_PP_OD_MASK BIT(4) +#define ST_ISM330IS_SW_RESET_MASK BIT(0) + +#define ST_ISM330IS_REG_CTRL5_C_ADDR 0x14 +#define ST_ISM330IS_ST_G_MASK GENMASK(3, 2) +#define ST_ISM330IS_ST_XL_MASK GENMASK(1, 0) + +#define ST_ISM330IS_REG_STATUS_ADDR 0x1e +#define ST_ISM330IS_STATUS_TDA BIT(2) +#define ST_ISM330IS_STATUS_XLDA BIT(0) +#define ST_ISM330IS_STATUS_GDA BIT(1) + +#define ST_ISM330IS_REG_OUT_TEMP_L_ADDR 0x20 +#define ST_ISM330IS_REG_OUTX_L_G_ADDR 0x22 +#define ST_ISM330IS_REG_OUTY_L_G_ADDR 0x24 +#define ST_ISM330IS_REG_OUTZ_L_G_ADDR 0x26 +#define ST_ISM330IS_REG_OUTX_L_A_ADDR 0x28 +#define ST_ISM330IS_REG_OUTY_L_A_ADDR 0x2a +#define ST_ISM330IS_REG_OUTZ_L_A_ADDR 0x2c + +#define ST_ISM330IS_ST_ACCEL_MIN 737 +#define ST_ISM330IS_ST_ACCEL_MAX 13934 +#define ST_ISM330IS_ST_GYRO_MIN 2142 +#define ST_ISM330IS_ST_GYRO_MAX 10000 + +#define ST_ISM330IS_ST_DISABLED_VAL 0 +#define ST_ISM330IS_ST_POS_SIGN_VAL 1 +#define ST_ISM330IS_ST_NEG_ACCEL_SIGN_VAL 2 +#define ST_ISM330IS_ST_NEG_GYRO_SIGN_VAL 3 + +/* shub registers */ +#define ST_ISM330IS_REG_SENSOR_HUB_1_ADDR 0x02 + +#define ST_ISM330IS_REG_MASTER_CONFIG_ADDR 0x14 +#define ST_ISM330IS_WRITE_ONCE_MASK BIT(6) +#define ST_ISM330IS_SHUB_PU_EN_MASK BIT(3) +#define ST_ISM330IS_MASTER_ON_MASK BIT(2) +#define ST_ISM330IS_AUX_SENS_ON_MASK GENMASK(1, 0) + +#define ST_ISM330IS_REG_SLV0_ADDR 0x15 +#define ST_ISM330IS_REG_SLV0_CFG 0x17 +#define ST_ISM330IS_REG_SLV1_ADDR 0x18 +#define ST_ISM330IS_REG_SLV2_ADDR 0x1b +#define ST_ISM330IS_REG_SLV3_ADDR 0x1e + +#define ST_ISM330IS_REG_DATAWRITE_SLV0_ADDR 0x21 +#define ST_ISM330IS_SLAVE_NUMOP_MASK GENMASK(2, 0) + +#define ST_ISM330IS_REG_STATUS_MASTER_ADDR 0x22 +#define ST_ISM330IS_SENS_HUB_ENDOP_MASK BIT(0) + +/* Timestamp Tick 25us/LSB */ +#define ST_ISM330IS_TS_DELTA_NS 25000ULL + +/* Temperature in uC */ +#define ST_ISM330IS_TEMP_GAIN 256 +#define ST_ISM330IS_TEMP_OFFSET 6400 + +#define ST_ISM330IS_DATA_CHANNEL(chan_type, addr, mod, ch2, scan_idx, \ + rb, sb, sg, ext_inf) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = mod, \ + .channel2 = ch2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = sg, \ + .realbits = rb, \ + .storagebits = sb, \ + .endianness = IIO_LE, \ + }, \ + .ext_info = ext_inf, \ +} + +#define ST_ISM330IS_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) + +extern const struct dev_pm_ops st_ism330is_pm_ops; + +/** + * struct st_ism330is_reg - Generic sensor register + * description (addr + mask) + * + * @addr: Address of register. + * @mask: Bitmask register for proper usage. + */ +struct st_ism330is_reg { + u8 addr; + u8 mask; +}; + +/** + * struct st_ism330is_odr - Single ODR entry + * @mhz: Sensor ODR (milli Hz). + * @val: ODR register value. + */ +struct st_ism330is_odr { + u32 mhz; + u8 val; +}; + +/** + * struct st_ism330is_odr_table_entry - Sensor ODR table + * @size: Size of ODR table. + * @reg: ODR register. + * @odr_avl: Array of supported ODR value. + */ +struct st_ism330is_odr_table_entry { + u8 size; + struct st_ism330is_reg reg; + struct st_ism330is_odr odr_avl[8]; +}; + +/** + * struct st_ism330is_fs + * brief Full scale entry + * + * @gain: The gain to obtain data value from raw data (LSB). + * @val: Register value. + */ +struct st_ism330is_fs { + u32 gain; + u8 val; +}; + +/** + * struct st_ism330is_fs_table_entry - Full Scale sensor table + * @reg: st_ism330is_reg struct. + * @fs_avl: Full Scale list entries. + * @fs_len: Real size of fs_avl array. + */ +struct st_ism330is_fs_table_entry { + int fs_len; + struct st_ism330is_reg reg; + struct st_ism330is_fs fs_avl[4]; +}; + +enum st_ism330is_sensor_id { + ST_ISM330IS_ID_GYRO = 0, + ST_ISM330IS_ID_ACC, + ST_ISM330IS_ID_TEMP, + ST_ISM330IS_ID_EXT0, + ST_ISM330IS_ID_EXT1, + ST_ISM330IS_ID_MAX, +}; + +/** + * @enum st_ism330is_sensor_id + * @brief Sensor Table Identifier + */ +static const enum st_ism330is_sensor_id st_ism330is_main_sensor_list[] = { + [0] = ST_ISM330IS_ID_GYRO, + [1] = ST_ISM330IS_ID_ACC, + [2] = ST_ISM330IS_ID_TEMP, + [3] = ST_ISM330IS_ID_EXT0, + [4] = ST_ISM330IS_ID_EXT1, +}; + +static const enum st_ism330is_sensor_id +st_ism330is_triggered_main_sensor_list[] = { + [0] = ST_ISM330IS_ID_GYRO, + [1] = ST_ISM330IS_ID_ACC, + [2] = ST_ISM330IS_ID_TEMP, + [3] = ST_ISM330IS_ID_EXT0, + [4] = ST_ISM330IS_ID_EXT1, +}; + +struct st_ism330is_ext_dev_info { + const struct st_ism330is_ext_dev_settings *ext_dev_settings; + u8 ext_dev_i2c_addr; +}; + +/** + * struct st_ism330is_sensor - ST IMU sensor instance + * @ext_dev_info: For sensor hub indicate device info struct. + * @id: Sensor identifier. + * @hw: Pointer to instance of struct st_ism330is_hw. + * @name: Sensor name. + * @offset: Sensor data offset. + * @gain: Configured sensor sensitivity. + * @mhz: Output data rate of the sensor [milli Hz]. + * @selftest_status: Report last self test status. + * @min_st: Min self test raw data value. + * @max_st: Max self test raw data value. + */ +struct st_ism330is_sensor { + struct st_ism330is_ext_dev_info ext_dev_info; + enum st_ism330is_sensor_id id; + struct st_ism330is_hw *hw; + char name[32]; + + u32 offset; + u32 gain; + u32 mhz; + + /* self test */ + int8_t selftest_status; + int min_st; + int max_st; +}; + +/** + * struct st_ism330is_hw - ST IMU MEMS hw instance + * @iio_devs: Pointers to acc/gyro iio_dev instances. + * @orientation: Sensor orientation matrix. + * @vddio_supply: Voltage regulator for VDDIIO. + * @vdd_supply: Voltage regulator for VDD. + * @page_lock: Mutex to prevent concurrent access to the page selector. + * @regmap: Register map of the device. + * @dev: Pointer to instance of struct device (I2C or SPI). + * @i2c_master_pu: I2C master line Pull Up configuration. + * @enable_mask: Enabled sensor bitmask. + * @ext_data_len: Number of i2c slave devices connected to I2C master. + * @irq: Device interrupt line (I2C or SPI). + */ +struct st_ism330is_hw { + struct iio_dev *iio_devs[ST_ISM330IS_ID_MAX]; + struct iio_mount_matrix orientation; + struct regulator *vddio_supply; + struct regulator *vdd_supply; + struct mutex page_lock; + struct regmap *regmap; + struct device *dev; + u8 i2c_master_pu; + u32 enable_mask; + u8 ext_data_len; + int irq; +}; + +static inline int +__st_ism330is_write_with_mask(struct st_ism330is_hw *hw, + unsigned int addr, + unsigned int mask, + unsigned int val) +{ + return regmap_update_bits(hw->regmap, addr, mask, + ST_ISM330IS_SHIFT_VAL(val, mask)); +} + +static inline int +st_ism330is_update_bits_locked(struct st_ism330is_hw *hw, + unsigned int addr, + unsigned int mask, + unsigned int val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = __st_ism330is_write_with_mask(hw, addr, mask, val); + mutex_unlock(&hw->page_lock); + + return err; +} + +static inline int +st_ism330is_read_locked(struct st_ism330is_hw *hw, unsigned int addr, + void *val, unsigned int len) +{ + int err; + + mutex_lock(&hw->page_lock); + err = regmap_bulk_read(hw->regmap, addr, val, len); + mutex_unlock(&hw->page_lock); + + return err; +} + +static inline int +st_ism330is_write_locked(struct st_ism330is_hw *hw, unsigned int addr, + unsigned int val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = regmap_write(hw->regmap, addr, val); + mutex_unlock(&hw->page_lock); + + return err; +} + +static inline int +st_ism330is_set_page_access(struct st_ism330is_hw *hw, unsigned int mask, + unsigned int val) +{ + return __st_ism330is_write_with_mask(hw, + ST_ISM330IS_REG_FUNC_CFG_ACCESS_ADDR, + mask, val); +} + +int st_ism330is_probe(struct device *dev, int irq, struct regmap *regmap); +int st_ism330is_sensor_set_enable(struct st_ism330is_sensor *sensor, + bool enable); +int st_ism330is_shub_probe(struct st_ism330is_hw *hw); +int st_ism330is_shub_set_enable(struct st_ism330is_sensor *sensor, + bool enable); +int st_ism330is_shub_read(struct st_ism330is_sensor *sensor, + u8 addr, u8 *data, int len); +int st_ism330is_allocate_buffers(struct st_ism330is_hw *hw); +#endif /* ST_ISM330IS_H */ diff --git a/drivers/iio/stm/imu/st_ism330is/st_ism330is_core.c b/drivers/iio/stm/imu/st_ism330is/st_ism330is_core.c new file mode 100644 index 000000000000..92edbea11234 --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/st_ism330is_core.c @@ -0,0 +1,1210 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_ism330is sensor driver + * + * MEMS Software Solutions Team + * + * Copyright 2023 STMicroelectronics Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st_ism330is.h" + +static struct st_ism330is_selftest_table { + char *string_mode; + u8 accel_value; + u8 gyro_value; + u8 gyro_mask; +} st_ism330is_selftest_table[] = { + [0] = { + .string_mode = "disabled", + .accel_value = ST_ISM330IS_ST_DISABLED_VAL, + .gyro_value = ST_ISM330IS_ST_DISABLED_VAL, + }, + [1] = { + .string_mode = "positive-sign", + .accel_value = ST_ISM330IS_ST_POS_SIGN_VAL, + .gyro_value = ST_ISM330IS_ST_POS_SIGN_VAL + }, + [2] = { + .string_mode = "negative-sign", + .accel_value = ST_ISM330IS_ST_NEG_ACCEL_SIGN_VAL, + .gyro_value = ST_ISM330IS_ST_NEG_GYRO_SIGN_VAL + }, +}; + +static const struct st_ism330is_odr_table_entry st_ism330is_odr_table[] = { + [ST_ISM330IS_ID_ACC] = { + .size = 7, + .reg = { + .addr = ST_ISM330IS_REG_CTRL1_XL_ADDR, + .mask = ST_ISM330IS_ODR_XL_MASK, + }, + .odr_avl[0] = { 12500, 0x01 }, + .odr_avl[1] = { 26000, 0x02 }, + .odr_avl[2] = { 52000, 0x03 }, + .odr_avl[3] = { 104000, 0x04 }, + .odr_avl[4] = { 208000, 0x05 }, + .odr_avl[5] = { 416000, 0x06 }, + .odr_avl[6] = { 833000, 0x07 }, + }, + [ST_ISM330IS_ID_GYRO] = { + .size = 7, + .reg = { + .addr = ST_ISM330IS_REG_CTRL2_G_ADDR, + .mask = ST_ISM330IS_ODR_G_MASK, + }, + .odr_avl[0] = { 12500, 0x01 }, + .odr_avl[1] = { 26000, 0x02 }, + .odr_avl[2] = { 52000, 0x03 }, + .odr_avl[3] = { 104000, 0x04 }, + .odr_avl[4] = { 208000, 0x05 }, + .odr_avl[5] = { 416000, 0x06 }, + .odr_avl[6] = { 833000, 0x07 }, + }, + [ST_ISM330IS_ID_TEMP] = { + .size = 2, + .odr_avl[0] = { 12500, 0x01 }, + .odr_avl[1] = { 52000, 0x03 }, + }, +}; + +static struct st_ism330is_fs_table_entry st_ism330is_fs_table[] = { + [ST_ISM330IS_ID_ACC] = { + .fs_len = 4, + .reg = { + .addr = ST_ISM330IS_REG_CTRL1_XL_ADDR, + .mask = ST_ISM330IS_FS_XL_MASK, + }, + .fs_avl[0] = { IIO_G_TO_M_S_2(61000), 0x0 }, + .fs_avl[1] = { IIO_G_TO_M_S_2(122000), 0x2 }, + .fs_avl[2] = { IIO_G_TO_M_S_2(244000), 0x3 }, + .fs_avl[3] = { IIO_G_TO_M_S_2(488000), 0x1 }, + }, + [ST_ISM330IS_ID_GYRO] = { + .fs_len = 4, + .reg = { + .addr = ST_ISM330IS_REG_CTRL2_G_ADDR, + .mask = ST_ISM330IS_FS_G_MASK, + }, + .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750000), 0x0 }, + .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500000), 0x1 }, + .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000000), 0x2 }, + .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000000), 0x3 }, + }, + [ST_ISM330IS_ID_TEMP] = { + .fs_len = 1, + .fs_avl[0] = { ST_ISM330IS_TEMP_GAIN, 0x0 }, + }, +}; + +static const struct iio_mount_matrix * +st_ism330is_get_mount_matrix(const struct iio_dev *iio_dev, + const struct iio_chan_spec *ch) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + struct st_ism330is_hw *hw = sensor->hw; + + return &hw->orientation; +} + +static const struct iio_chan_spec_ext_info st_ism330is_chan_spec_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, st_ism330is_get_mount_matrix), + { } +}; + +static const struct iio_chan_spec st_ism330is_acc_channels[] = { + ST_ISM330IS_DATA_CHANNEL(IIO_ACCEL, ST_ISM330IS_REG_OUTX_L_A_ADDR, + 1, IIO_MOD_X, 0, 16, 16, 's', + st_ism330is_chan_spec_ext_info), + ST_ISM330IS_DATA_CHANNEL(IIO_ACCEL, ST_ISM330IS_REG_OUTY_L_A_ADDR, + 1, IIO_MOD_Y, 1, 16, 16, 's', + st_ism330is_chan_spec_ext_info), + ST_ISM330IS_DATA_CHANNEL(IIO_ACCEL, ST_ISM330IS_REG_OUTZ_L_A_ADDR, + 1, IIO_MOD_Z, 2, 16, 16, 's', + st_ism330is_chan_spec_ext_info), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_ism330is_gyro_channels[] = { + ST_ISM330IS_DATA_CHANNEL(IIO_ANGL_VEL, ST_ISM330IS_REG_OUTX_L_G_ADDR, + 1, IIO_MOD_X, 0, 16, 16, 's', + st_ism330is_chan_spec_ext_info), + ST_ISM330IS_DATA_CHANNEL(IIO_ANGL_VEL, ST_ISM330IS_REG_OUTY_L_G_ADDR, + 1, IIO_MOD_Y, 1, 16, 16, 's', + st_ism330is_chan_spec_ext_info), + ST_ISM330IS_DATA_CHANNEL(IIO_ANGL_VEL, ST_ISM330IS_REG_OUTZ_L_G_ADDR, + 1, IIO_MOD_Z, 2, 16, 16, 's', + st_ism330is_chan_spec_ext_info), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_ism330is_temp_channels[] = { + { + .type = IIO_TEMP, + .address = ST_ISM330IS_REG_OUT_TEMP_L_ADDR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_OFFSET) + | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + } + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static __maybe_unused int st_ism330is_reg_access(struct iio_dev *iio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + int ret; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + if (readval == NULL) + ret = regmap_write(sensor->hw->regmap, reg, writeval); + else + ret = regmap_read(sensor->hw->regmap, reg, readval); + + iio_device_release_direct_mode(iio_dev); + + return (ret < 0) ? ret : 0; +} + +/** + * st_ism330is_check_whoami - Detect device HW ID + * + * Check the value of the device HW ID if valid + * + * @param hw: ST IMU MEMS hw instance. + * @return 0 if OK, negative value for ERROR + */ +static int st_ism330is_check_whoami(struct st_ism330is_hw *hw) +{ + int err, data; + + err = regmap_read(hw->regmap, ST_ISM330IS_REG_WHOAMI_ADDR, &data); + if (err < 0) { + dev_err(hw->dev, "failed to read whoami register\n"); + + return err; + } + + if (data != ST_ISM330IS_WHOAMI_VAL) { + dev_err(hw->dev, "unsupported whoami [%02x]\n", data); + + return -ENODEV; + } + + return 0; +} + +/** + * st_ism330is_set_full_scale - Set sensor full scale + * + * Check the value of requested gain, apply it if supported. + * NOTE: support also sensor with only one FS available (Temp gain is fixed). + * + * @param sensor: ST IMU MEMS sensor instance. + * @param gain: Sensor gain. + * @return 0 if OK, negative value for ERROR + */ +static int st_ism330is_set_full_scale(struct st_ism330is_sensor *sensor, + u32 gain) +{ + enum st_ism330is_sensor_id id = sensor->id; + struct st_ism330is_hw *hw = sensor->hw; + int i, err; + u8 val; + + for (i = 0; i < st_ism330is_fs_table[id].fs_len; i++) + if (st_ism330is_fs_table[id].fs_avl[i].gain == gain) + break; + + if (i == st_ism330is_fs_table[id].fs_len) + return -EINVAL; + + val = st_ism330is_fs_table[id].fs_avl[i].val; + err = regmap_update_bits(hw->regmap, + st_ism330is_fs_table[id].reg.addr, + st_ism330is_fs_table[id].reg.mask, + ST_ISM330IS_SHIFT_VAL(val, + st_ism330is_fs_table[id].reg.mask)); + if (err < 0) + return err; + + sensor->gain = gain; + + return 0; +} + +static int st_ism330is_get_odr_val(enum st_ism330is_sensor_id id, u32 mhz, + u32 *val, u32 *modr) +{ + u32 sensor_odr; + int i; + + /* this avoid entry 0mHz to ODR table */ + if (mhz == 0) { + *val = 0; + *modr = 0; + + return 0; + } + + for (i = 0; i < st_ism330is_odr_table[id].size; i++) { + sensor_odr = st_ism330is_odr_table[id].odr_avl[i].mhz; + if (sensor_odr >= mhz) { + *val = st_ism330is_odr_table[id].odr_avl[i].val; + *modr = sensor_odr; + + return 0; + } + } + + return -EINVAL; +} + + +static u32 +st_ism330is_check_odr_dependency(struct st_ism330is_hw *hw, u32 mhz, + enum st_ism330is_sensor_id ref_id) +{ + struct st_ism330is_sensor *ref = iio_priv(hw->iio_devs[ref_id]); + u32 ret = mhz; + + if (mhz > 0) { + if (hw->enable_mask & BIT(ref_id)) + ret = max_t(u32, ref->mhz, mhz); + } else { + ret = (hw->enable_mask & BIT(ref_id)) ? ref->mhz : 0; + } + + return ret; +} + +static int st_ism330is_set_odr(struct st_ism330is_sensor *sensor, u32 mhz) +{ + enum st_ism330is_sensor_id id = sensor->id; + struct st_ism330is_hw *hw = sensor->hw; + int val, err, odr, modr; + + switch (id) { + case ST_ISM330IS_ID_EXT0: + case ST_ISM330IS_ID_EXT1: + case ST_ISM330IS_ID_TEMP: + case ST_ISM330IS_ID_ACC: { + int i; + + id = ST_ISM330IS_ID_ACC; + for (i = ST_ISM330IS_ID_ACC; i < ST_ISM330IS_ID_MAX; i++) { + if (!hw->iio_devs[i] || i == sensor->id) + continue; + + odr = st_ism330is_check_odr_dependency(hw, mhz, i); + if (odr != mhz) + return 0; + } + break; + } + default: + break; + } + + err = st_ism330is_get_odr_val(id, odr, &val, &modr); + if (err < 0) + return err; + + return st_ism330is_update_bits_locked(hw, + st_ism330is_odr_table[id].reg.addr, + st_ism330is_odr_table[id].reg.mask, + val); +} + +int st_ism330is_sensor_set_enable(struct st_ism330is_sensor *sensor, + bool enable) +{ + int mhz = enable ? sensor->mhz : 0; + int err; + + err = st_ism330is_set_odr(sensor, mhz); + if (err < 0) + return err; + + if (enable) + sensor->hw->enable_mask |= BIT(sensor->id); + else + sensor->hw->enable_mask &= ~BIT(sensor->id); + + return 0; +} + +static int st_ism330is_read_oneshot(struct st_ism330is_sensor *sensor, + u8 addr, int *val) +{ + struct st_ism330is_hw *hw = sensor->hw; + int err, delay; + __le16 data; + + /* + * adjust delay for data valid because of turn-on time: + * - Acc, Power-down -> High-performance discard 1 sample + * - Gyro, Power-down -> High-performance wait 70 ms + f(ODR) + * - Temp, 1 ODR + * NOTE: we use conversion 1100000000 to also take into account the + * internal oscillator tolerance of 10% + */ + switch (sensor->id) { + case ST_ISM330IS_ID_GYRO: { + int n = 3; + + if (sensor->mhz > + st_ism330is_odr_table[sensor->id].odr_avl[0].mhz) + n++; + + delay = 70000 + n * (1100000000 / sensor->mhz); + } + break; + case ST_ISM330IS_ID_ACC: + delay = 2 * (1100000000 / sensor->mhz); + break; + case ST_ISM330IS_ID_TEMP: + delay = 1100000000 / sensor->mhz; + break; + default: + return -EINVAL; + } + + err = st_ism330is_sensor_set_enable(sensor, true); + if (err < 0) + return err; + + usleep_range(delay, delay + (delay >> 1)); + err = st_ism330is_read_locked(hw, addr, &data, sizeof(data)); + st_ism330is_sensor_set_enable(sensor, false); + if (err < 0) + return err; + + *val = (s16)le16_to_cpu(data); + + return IIO_VAL_INT; +} + +static int st_ism330is_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + case IIO_ACCEL: + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static int st_ism330is_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + ret = st_ism330is_read_oneshot(sensor, ch->address, val); + iio_device_release_direct_mode(iio_dev); + break; + case IIO_CHAN_INFO_OFFSET: + switch (ch->type) { + case IIO_TEMP: + *val = sensor->offset; + ret = IIO_VAL_INT; + break; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->mhz / 1000; + *val2 = (sensor->mhz % 1000) * 1000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SCALE: + switch (ch->type) { + case IIO_TEMP: + *val = 1000; + *val2 = ST_ISM330IS_TEMP_GAIN; + ret = IIO_VAL_FRACTIONAL; + break; + case IIO_ACCEL: + case IIO_ANGL_VEL: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_NANO; + break; + default: + return -EINVAL; + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int st_ism330is_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + int err; + + err = iio_device_claim_direct_mode(iio_dev); + if (err) + return err; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_ism330is_set_full_scale(sensor, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: { + u32 reg, modr; + + val = val * 1000 + val2 / 1000; + + err = st_ism330is_get_odr_val(sensor->id, val, ®, &modr); + if (err < 0) + goto release; + + sensor->mhz = modr; + break; + } + default: + err = -EINVAL; + break; + } + +release: + iio_device_release_direct_mode(iio_dev); + + return err; +} + +static ssize_t +st_ism330is_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + const struct st_ism330is_odr_table_entry *odr_table; + enum st_ism330is_sensor_id id = sensor->id; + int i, len = 0; + + odr_table = &st_ism330is_odr_table[id]; + for (i = 0; i < st_ism330is_odr_table[id].size; i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ", + odr_table->odr_avl[i].mhz / 1000, + odr_table->odr_avl[i].mhz % 1000); + } + + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t st_ism330is_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + enum st_ism330is_sensor_id id = sensor->id; + int i, len = 0; + + for (i = 0; i < st_ism330is_fs_table[id].fs_len; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ", + st_ism330is_fs_table[id].fs_avl[i].gain); + buf[len - 1] = '\n'; + + return len; +} + +static int +st_ism330is_set_selftest(struct st_ism330is_sensor *sensor, int index) +{ + u8 mode, mask; + + switch (sensor->id) { + case ST_ISM330IS_ID_ACC: + mask = ST_ISM330IS_ST_XL_MASK; + mode = st_ism330is_selftest_table[index].accel_value; + break; + case ST_ISM330IS_ID_GYRO: + mask = ST_ISM330IS_ST_G_MASK; + mode = st_ism330is_selftest_table[index].gyro_value; + break; + default: + return -EINVAL; + } + + return st_ism330is_update_bits_locked(sensor->hw, + ST_ISM330IS_REG_CTRL5_C_ADDR, + mask, mode); +} + +static ssize_t +st_ism330is_sysfs_get_selftest_available(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s, %s\n", + st_ism330is_selftest_table[1].string_mode, + st_ism330is_selftest_table[2].string_mode); +} + +static ssize_t +st_ism330is_sysfs_get_selftest_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + enum st_ism330is_sensor_id id = sensor->id; + char *message = NULL; + int8_t result; + + if (id != ST_ISM330IS_ID_ACC && + id != ST_ISM330IS_ID_GYRO) + return -EINVAL; + + result = sensor->selftest_status; + if (result == 0) + message = "na"; + else if (result < 0) + message = "fail"; + else if (result > 0) + message = "pass"; + + return sprintf(buf, "%s\n", message); +} + +static int +st_ism330is_selftest_sensor(struct st_ism330is_sensor *sensor, int test) +{ + int x_selftest = 0, y_selftest = 0, z_selftest = 0; + int x = 0, y = 0, z = 0, try_count = 0; + u8 i, status, n = 0; + u8 reg, bitmask; + int ret, delay; + u8 raw_data[6]; + + switch (sensor->id) { + case ST_ISM330IS_ID_ACC: + reg = ST_ISM330IS_REG_OUTX_L_A_ADDR; + bitmask = ST_ISM330IS_STATUS_XLDA; + break; + case ST_ISM330IS_ID_GYRO: + reg = ST_ISM330IS_REG_OUTX_L_G_ADDR; + bitmask = ST_ISM330IS_STATUS_GDA; + break; + default: + return -EINVAL; + } + + /* set selftest normal mode */ + ret = st_ism330is_set_selftest(sensor, 0); + if (ret < 0) + return ret; + + ret = st_ism330is_sensor_set_enable(sensor, true); + if (ret < 0) + return ret; + + /* wait at least 2 ODRs to be sure */ + delay = 2 * (1000000000 / sensor->mhz); + + /* power up, wait 100 ms for stable output */ + msleep(100); + + /* for 5 times, after checking status bit, read the output registers */ + for (i = 0; i < 5; i++) { + try_count = 0; + while (try_count < 3) { + usleep_range(delay, 2 * delay); + ret = st_ism330is_read_locked(sensor->hw, + ST_ISM330IS_REG_STATUS_ADDR, + &status, sizeof(status)); + if (ret < 0) + goto selftest_failure; + + if (status & bitmask) { + ret = st_ism330is_read_locked(sensor->hw, + reg, raw_data, + sizeof(raw_data)); + if (ret < 0) + goto selftest_failure; + + /* + * for 5 times, after checking status bit, + * read the output registers + */ + x += ((s16)*(u16 *)&raw_data[0]) / 5; + y += ((s16)*(u16 *)&raw_data[2]) / 5; + z += ((s16)*(u16 *)&raw_data[4]) / 5; + n++; + + break; + } + try_count++; + } + } + + if (i != n) { + dev_err(sensor->hw->dev, + "some acc samples missing (expected %d, read %d)\n", + i, n); + ret = -1; + + goto selftest_failure; + } + + n = 0; + + /* set selftest mode */ + st_ism330is_set_selftest(sensor, test); + + /* wait 100 ms for stable output */ + msleep(100); + + /* for 5 times, after checking status bit, read the output registers */ + for (i = 0; i < 5; i++) { + try_count = 0; + while (try_count < 3) { + usleep_range(delay, 2 * delay); + ret = st_ism330is_read_locked(sensor->hw, + ST_ISM330IS_REG_STATUS_ADDR, + &status, sizeof(status)); + if (ret < 0) + goto selftest_failure; + + if (status & bitmask) { + ret = st_ism330is_read_locked(sensor->hw, + reg, raw_data, + sizeof(raw_data)); + if (ret < 0) + goto selftest_failure; + + x_selftest += ((s16)*(u16 *)&raw_data[0]) / 5; + y_selftest += ((s16)*(u16 *)&raw_data[2]) / 5; + z_selftest += ((s16)*(u16 *)&raw_data[4]) / 5; + n++; + + break; + } + try_count++; + } + } + + if (i != n) { + dev_err(sensor->hw->dev, + "some samples missing (expected %d, read %d)\n", + i, n); + ret = -1; + + goto selftest_failure; + } + + if ((abs(x_selftest - x) < sensor->min_st) || + (abs(x_selftest - x) > sensor->max_st)) { + sensor->selftest_status = -1; + goto selftest_failure; + } + + if ((abs(y_selftest - y) < sensor->min_st) || + (abs(y_selftest - y) > sensor->max_st)) { + sensor->selftest_status = -1; + goto selftest_failure; + } + + if ((abs(z_selftest - z) < sensor->min_st) || + (abs(z_selftest - z) > sensor->max_st)) { + sensor->selftest_status = -1; + goto selftest_failure; + } + + sensor->selftest_status = 1; + +selftest_failure: + /* restore selftest to normal mode */ + st_ism330is_set_selftest(sensor, 0); + + return st_ism330is_sensor_set_enable(sensor, false); +} + +static ssize_t +st_ism330is_sysfs_start_selftest(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct iio_dev *iio_dev = dev_to_iio_dev(dev); + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + enum st_ism330is_sensor_id id = sensor->id; + struct st_ism330is_hw *hw = sensor->hw; + int ret, test; + u32 gain, mhz; + + if (id != ST_ISM330IS_ID_ACC && + id != ST_ISM330IS_ID_GYRO) + return -EINVAL; + + for (test = 0; test < ARRAY_SIZE(st_ism330is_selftest_table); test++) { + if (strncmp(buf, st_ism330is_selftest_table[test].string_mode, + strlen(st_ism330is_selftest_table[test].string_mode)) == 0) + break; + } + + if (test == ARRAY_SIZE(st_ism330is_selftest_table)) + return -EINVAL; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + /* self test mode unavailable if sensor enabled */ + if (hw->enable_mask & BIT(id)) { + ret = -EBUSY; + + goto out_claim; + } + + gain = sensor->gain; + mhz = sensor->mhz; + if (id == ST_ISM330IS_ID_ACC) { + /* set BDU = 1, FS = 4 g, ODR = 52 Hz */ + st_ism330is_set_full_scale(sensor, IIO_G_TO_M_S_2(122)); + st_ism330is_set_odr(sensor, 52000); + st_ism330is_selftest_sensor(sensor, test); + } else { + /* set BDU = 1, ODR = 208 Hz, FS = 2000 dps */ + st_ism330is_set_full_scale(sensor, IIO_DEGREE_TO_RAD(70000)); + st_ism330is_set_odr(sensor, 208000); + st_ism330is_selftest_sensor(sensor, test); + } + + /* restore full scale after test */ + st_ism330is_set_full_scale(sensor, gain); + st_ism330is_set_odr(sensor, mhz); + +out_claim: + iio_device_release_direct_mode(iio_dev); + + return size; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_ism330is_sysfs_sampling_frequency_avail); +static IIO_DEVICE_ATTR(in_accel_scale_available, 0444, + st_ism330is_sysfs_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444, + st_ism330is_sysfs_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_temp_scale_available, 0444, + st_ism330is_sysfs_scale_avail, NULL, 0); + +static IIO_DEVICE_ATTR(selftest_available, 0444, + st_ism330is_sysfs_get_selftest_available, + NULL, 0); +static IIO_DEVICE_ATTR(selftest, 0644, + st_ism330is_sysfs_get_selftest_status, + st_ism330is_sysfs_start_selftest, 0); + +static struct attribute *st_ism330is_acc_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + &iio_dev_attr_selftest_available.dev_attr.attr, + &iio_dev_attr_selftest.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ism330is_acc_attribute_group = { + .attrs = st_ism330is_acc_attributes, +}; + +static const struct iio_info st_ism330is_acc_info = { + .attrs = &st_ism330is_acc_attribute_group, + .read_raw = st_ism330is_read_raw, + .write_raw = st_ism330is_write_raw, + .write_raw_get_fmt = st_ism330is_write_raw_get_fmt, + .debugfs_reg_access = st_ism330is_reg_access, +}; + +static struct attribute *st_ism330is_gyro_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, + &iio_dev_attr_selftest_available.dev_attr.attr, + &iio_dev_attr_selftest.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ism330is_gyro_attribute_group = { + .attrs = st_ism330is_gyro_attributes, +}; + +static const struct iio_info st_ism330is_gyro_info = { + .attrs = &st_ism330is_gyro_attribute_group, + .read_raw = st_ism330is_read_raw, + .write_raw = st_ism330is_write_raw, + .write_raw_get_fmt = st_ism330is_write_raw_get_fmt, +}; + +static struct attribute *st_ism330is_temp_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_temp_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ism330is_temp_attribute_group = { + .attrs = st_ism330is_temp_attributes, +}; + +static const struct iio_info st_ism330is_temp_info = { + .attrs = &st_ism330is_temp_attribute_group, + .read_raw = st_ism330is_read_raw, + .write_raw = st_ism330is_write_raw, + .write_raw_get_fmt = st_ism330is_write_raw_get_fmt, +}; + +static int st_ism330is_reset_device(struct st_ism330is_hw *hw) +{ + int err; + + /* sw reset */ + err = regmap_update_bits(hw->regmap, ST_ISM330IS_REG_CTRL3_C_ADDR, + ST_ISM330IS_SW_RESET_MASK, + FIELD_PREP(ST_ISM330IS_SW_RESET_MASK, 1)); + if (err < 0) + return err; + + /* software reset procedure takes a maximum of 50 µs */ + usleep_range(50, 60); + + return err; +} + +static int st_ism330is_init_device(struct st_ism330is_hw *hw) +{ + /* enable Block Data Update */ + return regmap_update_bits(hw->regmap, ST_ISM330IS_REG_CTRL3_C_ADDR, + ST_ISM330IS_BDU_MASK, + FIELD_PREP(ST_ISM330IS_BDU_MASK, 1)); +} + +static struct iio_dev *st_ism330is_alloc_iiodev(struct st_ism330is_hw *hw, + enum st_ism330is_sensor_id id) +{ + struct st_ism330is_sensor *sensor; + struct iio_dev *iio_dev; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return NULL; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = hw->dev; + + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + + switch (id) { + case ST_ISM330IS_ID_ACC: + iio_dev->channels = st_ism330is_acc_channels; + iio_dev->num_channels = ARRAY_SIZE(st_ism330is_acc_channels); + scnprintf(sensor->name, sizeof(sensor->name), "%s_accel", + ST_ISM330IS_DEV_NAME); + iio_dev->info = &st_ism330is_acc_info; + st_ism330is_set_full_scale(sensor, + st_ism330is_fs_table[id].fs_avl[0].gain); + sensor->offset = 0; + sensor->mhz = st_ism330is_odr_table[id].odr_avl[1].mhz; + sensor->min_st = ST_ISM330IS_ST_ACCEL_MIN; + sensor->max_st = ST_ISM330IS_ST_ACCEL_MAX; + break; + case ST_ISM330IS_ID_GYRO: + iio_dev->channels = st_ism330is_gyro_channels; + iio_dev->num_channels = ARRAY_SIZE(st_ism330is_gyro_channels); + scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro", + ST_ISM330IS_DEV_NAME); + iio_dev->info = &st_ism330is_gyro_info; + st_ism330is_set_full_scale(sensor, + st_ism330is_fs_table[id].fs_avl[0].gain); + sensor->offset = 0; + sensor->mhz = st_ism330is_odr_table[id].odr_avl[1].mhz; + sensor->min_st = ST_ISM330IS_ST_GYRO_MIN; + sensor->max_st = ST_ISM330IS_ST_GYRO_MAX; + break; + case ST_ISM330IS_ID_TEMP: + iio_dev->channels = st_ism330is_temp_channels; + iio_dev->num_channels = ARRAY_SIZE(st_ism330is_temp_channels); + scnprintf(sensor->name, sizeof(sensor->name), "%s_temp", + ST_ISM330IS_DEV_NAME); + iio_dev->info = &st_ism330is_temp_info; + sensor->offset = ST_ISM330IS_TEMP_OFFSET; + sensor->mhz = st_ism330is_odr_table[id].odr_avl[1].mhz; + break; + default: + return NULL; + } + + iio_dev->name = sensor->name; + + return iio_dev; +} + +static void st_ism330is_disable_regulator_action(void *_data) +{ + struct st_ism330is_hw *hw = _data; + + regulator_disable(hw->vddio_supply); + regulator_disable(hw->vdd_supply); +} + +static int st_ism330is_power_enable(struct st_ism330is_hw *hw) +{ + int err; + + hw->vdd_supply = devm_regulator_get(hw->dev, "vdd"); + if (IS_ERR(hw->vdd_supply)) { + if (PTR_ERR(hw->vdd_supply) != -EPROBE_DEFER) + dev_err(hw->dev, "Failed to get vdd regulator %d\n", + (int)PTR_ERR(hw->vdd_supply)); + + return PTR_ERR(hw->vdd_supply); + } + + hw->vddio_supply = devm_regulator_get(hw->dev, "vddio"); + if (IS_ERR(hw->vddio_supply)) { + if (PTR_ERR(hw->vddio_supply) != -EPROBE_DEFER) + dev_err(hw->dev, "Failed to get vddio regulator %d\n", + (int)PTR_ERR(hw->vddio_supply)); + + return PTR_ERR(hw->vddio_supply); + } + + err = regulator_enable(hw->vdd_supply); + if (err) { + dev_err(hw->dev, "Failed to enable vdd regulator: %d\n", err); + return err; + } + + err = regulator_enable(hw->vddio_supply); + if (err) { + regulator_disable(hw->vdd_supply); + return err; + } + + err = devm_add_action_or_reset(hw->dev, + st_ism330is_disable_regulator_action, + hw); + if (err) { + dev_err(hw->dev, + "Failed to setup regulator cleanup action %d\n", + err); + return err; + } + + return 0; +} + +/** + * Probe device function + * Implements [MODULE] feature for Power Management + * + * @param dev: Device pointer. + * @param irq: I2C/SPI/I3C client irq. + * @param hw_id: Sensor HW id. + * @param regmap: Bus Transfer Function pointer. + * @retval 0 if OK, < 0 for error + */ +int st_ism330is_probe(struct device *dev, int irq, struct regmap *regmap) +{ + struct st_ism330is_hw *hw; + int i, err; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + dev_set_drvdata(dev, (void *)hw); + + mutex_init(&hw->page_lock); + + hw->regmap = regmap; + hw->dev = dev; + hw->irq = irq; + + err = st_ism330is_power_enable(hw); + if (err != 0) + return err; + + /* + * after the device is powered up, it performs a 10 ms (maximum) boot + * procedure to load the trimming parameters. + * After the boot is completed, both the accelerometer and the gyroscope + * are automatically configured in power-down mode. + */ + usleep_range(10000, 11000); + + err = regmap_write(hw->regmap, + ST_ISM330IS_REG_FUNC_CFG_ACCESS_ADDR, 0); + if (err < 0) + return err; + + err = st_ism330is_check_whoami(hw); + if (err < 0) + return err; + + err = st_ism330is_reset_device(hw); + if (err < 0) + return err; + + err = st_ism330is_init_device(hw); + if (err < 0) + return err; + +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + err = iio_read_mount_matrix(hw->dev, &hw->orientation); +#elif KERNEL_VERSION(5, 2, 0) <= LINUX_VERSION_CODE + err = iio_read_mount_matrix(hw->dev, "mount-matrix", &hw->orientation); +#else /* LINUX_VERSION_CODE */ + err = of_iio_read_mount_matrix(hw->dev, "mount-matrix", + &hw->orientation); +#endif /* LINUX_VERSION_CODE */ + + if (err) { + dev_err(dev, "Failed to retrieve mounting matrix %d\n", err); + + return err; + } + + /* register only data sensors */ + for (i = 0; i < ARRAY_SIZE(st_ism330is_main_sensor_list); i++) { + enum st_ism330is_sensor_id id = st_ism330is_main_sensor_list[i]; + + hw->iio_devs[id] = st_ism330is_alloc_iiodev(hw, id); + if (!hw->iio_devs[id]) + continue; + } + + if (!dev_fwnode(dev) || + device_property_read_bool(dev, "enable-sensor-hub")) { + err = st_ism330is_shub_probe(hw); + if (err < 0) + return err; + } + + err = st_ism330is_allocate_buffers(hw); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(st_ism330is_main_sensor_list); i++) { + enum st_ism330is_sensor_id id = st_ism330is_main_sensor_list[i]; + + if (!hw->iio_devs[id]) + continue; + + err = devm_iio_device_register(hw->dev, hw->iio_devs[id]); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL(st_ism330is_probe); + +static int __maybe_unused st_ism330is_suspend(struct device *dev) +{ + struct st_ism330is_hw *hw = dev_get_drvdata(dev); + struct st_ism330is_sensor *sensor; + int i, err = 0; + + for (i = 0; i < ST_ISM330IS_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + if (!hw->iio_devs[i]) + continue; + + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + err = st_ism330is_set_odr(sensor, 0); + if (err < 0) + return err; + } + + return err < 0 ? err : 0; +} + +static int __maybe_unused st_ism330is_resume(struct device *dev) +{ + struct st_ism330is_hw *hw = dev_get_drvdata(dev); + struct st_ism330is_sensor *sensor; + int i, err = 0; + + for (i = 0; i < ST_ISM330IS_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + if (!hw->iio_devs[i]) + continue; + + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + err = st_ism330is_set_odr(sensor, sensor->mhz); + if (err < 0) + return err; + } + + return err < 0 ? err : 0; +} + +const struct dev_pm_ops st_ism330is_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(st_ism330is_suspend, st_ism330is_resume) +}; +EXPORT_SYMBOL(st_ism330is_pm_ops); + +MODULE_AUTHOR("MEMS Software Solutions Team"); +MODULE_DESCRIPTION("STMicroelectronics st_ism330is driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/stm/imu/st_ism330is/st_ism330is_i2c.c b/drivers/iio/stm/imu/st_ism330is/st_ism330is_i2c.c new file mode 100644 index 000000000000..28d347457287 --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/st_ism330is_i2c.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_ism330is i2c driver + * + * MEMS Software Solutions Team + * + * Copyright 2023 STMicroelectronics Inc. + */ + +#include +#include +#include +#include +#include + +#include "st_ism330is.h" + +static const struct regmap_config st_ism330is_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int st_ism330is_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &st_ism330is_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return st_ism330is_probe(&client->dev, client->irq, regmap); +} + +static const struct of_device_id st_ism330is_i2c_of_match[] = { + { .compatible = "st,ism330is" }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_ism330is_i2c_of_match); + +static const struct i2c_device_id st_ism330is_i2c_id_table[] = { + { ST_ISM330IS_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_ism330is_i2c_id_table); + +static struct i2c_driver st_ism330is_driver = { + .driver = { + .name = "st_" ST_ISM330IS_DEV_NAME "_i2c", + .pm = &st_ism330is_pm_ops, + .of_match_table = of_match_ptr(st_ism330is_i2c_of_match), + }, + .probe = st_ism330is_i2c_probe, + .id_table = st_ism330is_i2c_id_table, +}; +module_i2c_driver(st_ism330is_driver); + +MODULE_AUTHOR("MEMS Software Solutions Team"); +MODULE_DESCRIPTION("STMicroelectronics st_ism330is i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/stm/imu/st_ism330is/st_ism330is_shub.c b/drivers/iio/stm/imu/st_ism330is/st_ism330is_shub.c new file mode 100644 index 000000000000..eb822026f4c5 --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/st_ism330is_shub.c @@ -0,0 +1,952 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_ism330is sensor hub library driver + * + * MEMS Software Solutions Team + * + * Copyright 2023 STMicroelectronics Inc. + */ + +#include +#include +#include +#include +#include + +#include "st_ism330is.h" + +#define ST_ISM330IS_MAX_SLV_NUM 2 + +/** + * @struct st_ism330is_ext_pwr + * @brief External device Power Management description + * reg: Generic sensor register description. + * off_val: Value to write into register to power off external sensor. + * on_val: Value to write into register for power on external sensor. + */ +struct st_ism330is_ext_pwr { + struct st_ism330is_reg reg; + u8 off_val; + u8 on_val; +}; + +/** + * @struct st_ism330is_ext_dev_settings + * @brief External sensor descritor entry + * i2c_addr: External I2C device address (max two). + * wai_addr: Device ID address. + * wai_val: Device ID value. + * odr_table: ODR sensor table. + * fs_table: Full scale table. + * temp_comp_reg: Temperature compensation registers. + * pwr_table: External device Power Management description. + * off_canc_reg: Offset cancellation registers. + * bdu_reg: Block Data Update registers. + * ext_channels:IIO device channel specifications. + * ext_chan_depth: Max number of IIO device channel specifications. + * data_len: Sensor output data len. + */ +struct st_ism330is_ext_dev_settings { + u8 i2c_addr[2]; + u8 wai_addr; + u8 wai_val; + struct st_ism330is_odr_table_entry odr_table; + struct st_ism330is_fs_table_entry fs_table; + struct st_ism330is_reg temp_comp_reg; + struct st_ism330is_ext_pwr pwr_table; + struct st_ism330is_reg off_canc_reg; + struct st_ism330is_reg bdu_reg; + const struct iio_chan_spec ext_channels[5]; + u8 ext_chan_depth; + u8 data_len; +}; + +static const struct st_ism330is_ext_dev_settings st_ism330is_ext_dev_table[] = { + { + /* LIS2MDL */ + .i2c_addr = { 0x1e }, + .wai_addr = 0x4f, + .wai_val = 0x40, + .odr_table = { + .size = 5, + .reg = { + .addr = 0x60, + .mask = GENMASK(3, 2), + }, + .odr_avl[0] = { 5000, 0x0 }, + .odr_avl[1] = { 10000, 0x0 }, + .odr_avl[2] = { 20000, 0x1 }, + .odr_avl[3] = { 50000, 0x2 }, + .odr_avl[4] = { 100000, 0x3 }, + }, + .fs_table = { + .fs_len = 1, + .fs_avl[0] = { + .gain = 1500, + .val = 0x0, + }, /* 1500 uG/LSB */ + }, + .temp_comp_reg = { + .addr = 0x60, + .mask = BIT(7), + }, + .pwr_table = { + .reg = { + .addr = 0x60, + .mask = GENMASK(1, 0), + }, + .off_val = 0x2, + .on_val = 0x0, + }, + .off_canc_reg = { + .addr = 0x61, + .mask = BIT(1), + }, + .bdu_reg = { + .addr = 0x62, + .mask = BIT(4), + }, + .ext_channels[0] = ST_ISM330IS_DATA_CHANNEL(IIO_MAGN, 0x68, + 1, IIO_MOD_X, 0, + 16, 16, 's', NULL), + .ext_channels[1] = ST_ISM330IS_DATA_CHANNEL(IIO_MAGN, 0x6a, + 1, IIO_MOD_Y, 1, + 16, 16, 's', NULL), + .ext_channels[2] = ST_ISM330IS_DATA_CHANNEL(IIO_MAGN, 0x6c, + 1, IIO_MOD_Z, 2, + 16, 16, 's', NULL), + .ext_channels[3] = IIO_CHAN_SOFT_TIMESTAMP(3), + .ext_chan_depth = 4, + .data_len = 6, + }, + { + /* LPS22HH */ + .i2c_addr = { 0x5c, 0x5d }, + .wai_addr = 0x0f, + .wai_val = 0xb3, + .odr_table = { + .size = 5, + .reg = { + .addr = 0x10, + .mask = GENMASK(6, 4), + }, + .odr_avl[0] = { 1000, 0x1 }, + .odr_avl[1] = { 10000, 0x2 }, + .odr_avl[2] = { 25000, 0x3 }, + .odr_avl[3] = { 50000, 0x4 }, + .odr_avl[4] = { 100000, 0x6 }, + }, + .fs_table = { + .fs_len = 1, + /* hPa miscro scale */ + .fs_avl[0] = { + .gain = 1000000UL / 4096UL, + .val = 0x0, + }, + }, + .bdu_reg = { + .addr = 0x10, + .mask = BIT(1), + }, + .ext_channels[0] = ST_ISM330IS_DATA_CHANNEL(IIO_PRESSURE, 0x28, + 0, IIO_NO_MOD, 0, + 24, 32, 'u', NULL), + .ext_channels[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .ext_chan_depth = 2, + .data_len = 3, + }, +}; + +/** + * Wait write trigger [SHUB] + * + * In write on external device register, each operation is triggered + * by accel/gyro data ready, this means that wait time depends on ODR + * plus i2c time + * NOTE: Be sure to enable Acc or Gyro before this operation + * + * @param hw: ST IMU MEMS hw instance. + */ +static inline void st_ism330is_shub_wait_complete(struct st_ism330is_hw *hw) +{ + struct st_ism330is_sensor *sensor; + u32 odr; + + sensor = iio_priv(hw->iio_devs[ST_ISM330IS_ID_ACC]); + + /* check if acc is enabled (it should be) */ + if (hw->enable_mask & BIT(ST_ISM330IS_ID_ACC)) + odr = sensor->mhz; + else + odr = 12500; + + /* odr tollerance is 10 % */ + msleep(1100000 / odr); +} + +/** + * Read from sensor hub bank register [SHUB] + * + * NOTE: uses page_lock + * + * @param hw: ST IMU MEMS hw instance. + * @param addr: Remote address register. + * @param data: Data buffer. + * @param len: Data read len. + * @return 0 if OK, < 0 if ERROR + */ +static int st_ism330is_shub_read_reg(struct st_ism330is_hw *hw, u8 addr, + u8 *data, int len) +{ + int err; + + mutex_lock(&hw->page_lock); + err = st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, + true); + if (err < 0) + goto out; + + err = regmap_bulk_read(hw->regmap, (unsigned int)addr, + (unsigned int *)data, len); + st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/** + * Write to sensor hub bank register [SHUB] + * + * NOTE: uses page_lock + * + * @param hw: ST IMU MEMS hw instance. + * @param addr: Remote address register. + * @param data: Data buffer. + * @param len: Data read len. + * @return 0 if OK, < 0 if ERROR + */ +static int st_ism330is_shub_write_reg(struct st_ism330is_hw *hw, u8 addr, + u8 *data, int len) +{ + int err; + + mutex_lock(&hw->page_lock); + err = st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, true); + if (err < 0) + goto out; + + err = regmap_bulk_write(hw->regmap, (unsigned int)addr, + (unsigned int *)data, len); + st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/** + * Enable sensor hub interface [SHUB] + * + * NOTE: uses page_lock + * + * @param sensor: ST IMU sensor instance + * @param enable: Master Enable/Disable. + * @return 0 if OK, < 0 if ERROR + */ +static int +st_ism330is_shub_master_enable(struct st_ism330is_sensor *sensor, bool enable) +{ + struct st_ism330is_hw *hw = sensor->hw; + int err; + + /* enable main sensor as trigger */ + err = st_ism330is_sensor_set_enable(sensor, enable); + if (err < 0) + return err; + + mutex_lock(&hw->page_lock); + err = st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, true); + if (err < 0) + goto out; + + err = __st_ism330is_write_with_mask(hw, + ST_ISM330IS_REG_MASTER_CONFIG_ADDR, + ST_ISM330IS_MASTER_ON_MASK, + enable); + + st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, false); + +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/** + * Read sensor data register from shub interface + * + * NOTE: use SLV3 i2c slave for one-shot read operation + * + * @param sensor: ST IMU sensor instance + * @param addr: Remote address register. + * @param data: Data buffer. + * @param len: Data read len. + * @return 0 if OK, < 0 if ERROR + */ +int st_ism330is_shub_read(struct st_ism330is_sensor *sensor, + u8 addr, u8 *data, int len) +{ + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + struct st_ism330is_hw *hw = sensor->hw; + u8 out_addr = ST_ISM330IS_REG_SENSOR_HUB_1_ADDR + hw->ext_data_len; + u8 config[3]; + int err; + + config[0] = (ext_info->ext_dev_i2c_addr << 1) | 1; + config[1] = addr; + config[2] = len & 0x7; + + err = st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV3_ADDR, + config, sizeof(config)); + if (err < 0) + return err; + + err = st_ism330is_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_ism330is_shub_wait_complete(hw); + + err = st_ism330is_shub_read_reg(hw, out_addr, data, len & 0x7); + + st_ism330is_shub_master_enable(sensor, false); + + memset(config, 0, sizeof(config)); + + return st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV3_ADDR, + config, sizeof(config)); +} + +/** + * Write sensor data register from shub interface + * + * NOTE: use SLV0 i2c slave for write operation + * + * @param sensor: ST IMU sensor instance + * @param addr: Remote address register. + * @param data: Data buffer. + * @param len: Data read len. + * @return 0 if OK, < 0 if ERROR + */ +static int st_ism330is_shub_write(struct st_ism330is_sensor *sensor, + u8 addr, u8 *data, int len) +{ + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + struct st_ism330is_hw *hw = sensor->hw; + u8 mconfig = ST_ISM330IS_WRITE_ONCE_MASK | 3 | hw->i2c_master_pu; + u8 config[3] = {}; + int err, i; + + /* AuxSens = 3 + wr once + pull up configuration */ + err = st_ism330is_shub_write_reg(hw, + ST_ISM330IS_REG_MASTER_CONFIG_ADDR, + &mconfig, sizeof(mconfig)); + if (err < 0) + return err; + + config[0] = ext_info->ext_dev_i2c_addr << 1; + for (i = 0; i < len; i++) { + config[1] = addr + i; + + err = st_ism330is_shub_write_reg(hw, + ST_ISM330IS_REG_SLV0_ADDR, + config, sizeof(config)); + if (err < 0) + return err; + + err = st_ism330is_shub_write_reg(hw, + ST_ISM330IS_REG_DATAWRITE_SLV0_ADDR, + &data[i], 1); + if (err < 0) + return err; + + err = st_ism330is_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_ism330is_shub_wait_complete(hw); + + st_ism330is_shub_master_enable(sensor, false); + } + + return st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV0_ADDR, + config, sizeof(config)); +} + +/** + * Write sensor data register from shub interface using register bitmask + * + * @param sensor: ST IMU sensor instance + * @param addr: Remote address register. + * @param mask: Register bitmask. + * @param val: Data buffer. + * @return 0 if OK, < 0 if ERROR + */ +static int +st_ism330is_shub_write_with_mask(struct st_ism330is_sensor *sensor, + u8 addr, u8 mask, u8 val) +{ + int err; + u8 data; + + err = st_ism330is_shub_read(sensor, addr, &data, sizeof(data)); + if (err < 0) + return err; + + data = (data & ~mask) | ST_ISM330IS_SHIFT_VAL(val, mask); + + return st_ism330is_shub_write(sensor, addr, &data, sizeof(data)); +} + +/** + * Configure external sensor connected on master I2C interface + * + * NOTE: use SLV1/SLV2 i2c slave for FIFO read operation + * + * @param sensor: ST IMU sensor instance + * @param enable: Enable/Disable sensor. + * @return 0 if OK, < 0 if ERROR + */ +static int +st_ism330is_shub_config_channels(struct st_ism330is_sensor *sensor, bool enable) +{ + struct st_ism330is_ext_dev_info *ext_info; + struct st_ism330is_hw *hw = sensor->hw; + struct st_ism330is_sensor *cur_sensor; + u8 config[6] = {}, enable_mask; + int i, j = 0; + + enable_mask = enable ? hw->enable_mask | BIT(sensor->id) + : hw->enable_mask & ~BIT(sensor->id); + + for (i = ST_ISM330IS_ID_EXT0; i <= ST_ISM330IS_ID_EXT1; i++) { + if (!hw->iio_devs[i]) + continue; + + cur_sensor = iio_priv(hw->iio_devs[i]); + if (!(enable_mask & BIT(cur_sensor->id))) + continue; + + ext_info = &cur_sensor->ext_dev_info; + config[j] = (ext_info->ext_dev_i2c_addr << 1) | 1; + config[j + 1] = + ext_info->ext_dev_settings->ext_channels[0].address; + config[j + 2] = (ext_info->ext_dev_settings->data_len & + ST_ISM330IS_SLAVE_NUMOP_MASK); + j += 3; + } + + return st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV1_ADDR, + config, sizeof(config)); +} + +/** + * Get a valid ODR [SHUB] + * + * Check a valid ODR closest to the passed value + * + * @param sensor: SST IMU sensor instance. + * @param odr: ODR value (in Hz). + * @param val: ODR register value data pointer. + * @return 0 if OK, negative value for ERROR + */ +static int st_ism330is_shub_get_odr_val(struct st_ism330is_sensor *sensor, + u32 odr, u8 *val) +{ + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + int i; + + for (i = 0; i < ext_info->ext_dev_settings->odr_table.size; i++) + if (ext_info->ext_dev_settings->odr_table.odr_avl[i].mhz >= odr) + break; + + if (i == ext_info->ext_dev_settings->odr_table.size) + return -EINVAL; + + *val = ext_info->ext_dev_settings->odr_table.odr_avl[i].val; + + return 0; +} + +/** + * Set new ODR to sensor [SHUB] + * + * Set a valid ODR closest to the passed value + * + * @param sensor: ST IMU sensor instance + * @param odr: ODR value (in Hz). + * @return 0 if OK, negative value for ERROR + */ +static int st_ism330is_shub_set_odr(struct st_ism330is_sensor *sensor, u32 odr) +{ + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + struct st_ism330is_hw *hw = sensor->hw; + u8 odr_val; + int err; + + err = st_ism330is_shub_get_odr_val(sensor, odr, &odr_val); + if (err < 0) + return err; + + if (sensor->mhz == odr && (hw->enable_mask & BIT(sensor->id))) + return 0; + + return st_ism330is_shub_write_with_mask(sensor, + ext_info->ext_dev_settings->odr_table.reg.addr, + ext_info->ext_dev_settings->odr_table.reg.mask, + odr_val); +} + +/** + * Enable or Disable sensor [SHUB] + * + * @param sensor: ST IMU sensor instance + * @param enable: Enable or disable the sensor [true,false]. + * @return 0 if OK, negative value for ERROR + */ +int st_ism330is_shub_set_enable(struct st_ism330is_sensor *sensor, bool enable) +{ + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + int err; + + err = st_ism330is_shub_config_channels(sensor, enable); + if (err < 0) + return err; + + if (enable) { + err = st_ism330is_shub_set_odr(sensor, sensor->mhz); + if (err < 0) + return err; + } else { + err = st_ism330is_shub_write_with_mask(sensor, + ext_info->ext_dev_settings->odr_table.reg.addr, + ext_info->ext_dev_settings->odr_table.reg.mask, + 0); + if (err < 0) + return err; + } + + if (ext_info->ext_dev_settings->pwr_table.reg.addr) { + u8 val; + + val = enable ? ext_info->ext_dev_settings->pwr_table.on_val + : ext_info->ext_dev_settings->pwr_table.off_val; + err = st_ism330is_shub_write_with_mask(sensor, + ext_info->ext_dev_settings->pwr_table.reg.addr, + ext_info->ext_dev_settings->pwr_table.reg.mask, + val); + if (err < 0) + return err; + } + + return st_ism330is_shub_master_enable(sensor, enable); +} + +static inline u32 st_ism330is_get_unaligned_le24(const u8 *p) +{ + return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8; +} + +/** + * Single sensor read operation [SHUB] + * + * @param sensor: ST IMU sensor instance + * @param ch: IIO Channel. + * @param val: Output data register value. + * @return IIO_VAL_INT if OK, negative value for ERROR + */ +static int +st_ism330is_shub_read_oneshot(struct st_ism330is_sensor *sensor, + struct iio_chan_spec const *ch, int *val) +{ + int err, delay, len = ch->scan_type.realbits >> 3; + u8 data[4]; + + err = st_ism330is_shub_set_enable(sensor, true); + if (err < 0) + return err; + + delay = 1100000000 / sensor->mhz; + usleep_range(delay, delay + (delay >> 1)); + + err = st_ism330is_shub_read(sensor, ch->address, data, len); + if (err < 0) + return err; + + st_ism330is_shub_set_enable(sensor, false); + + switch (len) { + case 3: + *val = (s32)st_ism330is_get_unaligned_le24(data); + break; + case 2: + *val = (s16)get_unaligned_le16(data); + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + +/** + * Read Sensor data configuration [SHUB] + * + * @param iio_dev: IIO Device. + * @param ch: IIO Channel. + * @param val: Data Buffer (MSB). + * @param val2: Data Buffer (LSB). + * @param mask: Data Mask. + * @return 0 if OK, -EINVAL value for ERROR + */ +static int st_ism330is_shub_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + ret = st_ism330is_shub_read_oneshot(sensor, ch, val); + iio_device_release_direct_mode(iio_dev); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->mhz; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * Write Sensor data configuration [SHUB] + * + * @param iio_dev: IIO Device. + * @param chan: IIO Channel. + * @param val: Data Buffer (MSB). + * @param val2: Data Buffer (LSB). + * @param mask: Data Mask. + * @return 0 if OK, -EINVAL value for ERROR + */ +static int st_ism330is_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + int err; + + mutex_lock(&iio_dev->mlock); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + u8 data; + + val = val * 1000 + val2 / 1000; + err = st_ism330is_shub_get_odr_val(sensor, val, &data); + if (!err) + sensor->mhz = val; + break; + } + case IIO_CHAN_INFO_SCALE: + err = 0; + break; + default: + err = -EINVAL; + break; + } + + mutex_unlock(&iio_dev->mlock); + + return err; +} + +/** + * Get a list of available sensor ODR [SHUB] + * + * List of available ODR returned separated by commas + * + * @param dev: IIO Device. + * @param attr: IIO Channel attribute. + * @param buf: User buffer. + * @return buffer len + */ +static ssize_t +st_ism330is_sysfs_shub_sampling_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + int i, len = 0; + + for (i = 0; i < ext_info->ext_dev_settings->odr_table.size; i++) { + u16 val = ext_info->ext_dev_settings->odr_table.odr_avl[i].mhz; + + if (val > 0) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ", + val / 1000, val % 1000); + } + buf[len - 1] = '\n'; + + return len; +} + +/** + * Get a list of available sensor Full Scale [SHUB] + * + * List of available Full Scale returned separated by commas + * + * @param dev: IIO Device. + * @param attr: IIO Channel attribute. + * @param buf: User buffer. + * @return buffer len + */ +static ssize_t st_ism330is_sysfs_shub_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + int i, len = 0; + + for (i = 0; i < ext_info->ext_dev_settings->fs_table.fs_len; i++) { + u16 val = ext_info->ext_dev_settings->fs_table.fs_avl[i].gain; + + if (val > 0) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + val); + } + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_ism330is_sysfs_shub_sampling_freq_avail); +static IIO_DEVICE_ATTR(in_ext_scale_available, 0444, + st_ism330is_sysfs_shub_scale_avail, NULL, 0); + +static struct attribute *st_ism330is_ext_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_ext_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_ism330is_ext_attribute_group = { + .attrs = st_ism330is_ext_attributes, +}; + +static const struct iio_info st_ism330is_ext_info = { + .attrs = &st_ism330is_ext_attribute_group, + .read_raw = st_ism330is_shub_read_raw, + .write_raw = st_ism330is_shub_write_raw, +}; + +/** + * Allocate IIO device [SHUB] + * + * @param hw: ST IMU MEMS hw instance. + * @param ext_settings: xternal sensor descritor entry. + * @param id: Sensor Identifier. + * @param i2c_addr: external I2C address on master bus. + * @return struct iio_dev *, NULL if ERROR + */ +static struct iio_dev *st_ism330is_shub_alloc_iio_dev(struct st_ism330is_hw *hw, + const struct st_ism330is_ext_dev_settings *ext_settings, + enum st_ism330is_sensor_id id, u8 i2c_addr) +{ + struct st_ism330is_sensor *sensor; + struct iio_dev *iio_dev; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return NULL; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = hw->dev; + iio_dev->info = &st_ism330is_ext_info; + iio_dev->channels = ext_settings->ext_channels; + iio_dev->num_channels = ext_settings->ext_chan_depth; + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + sensor->mhz = ext_settings->odr_table.odr_avl[0].mhz; + sensor->gain = ext_settings->fs_table.fs_avl[0].gain; + sensor->ext_dev_info.ext_dev_i2c_addr = i2c_addr; + sensor->ext_dev_info.ext_dev_settings = ext_settings; + + switch (iio_dev->channels[0].type) { + case IIO_MAGN: + scnprintf(sensor->name, sizeof(sensor->name), "%s_magn", + ST_ISM330IS_DEV_NAME); + break; + case IIO_PRESSURE: + scnprintf(sensor->name, sizeof(sensor->name), "%s_press", + ST_ISM330IS_DEV_NAME); + break; + default: + scnprintf(sensor->name, sizeof(sensor->name), "%s_ext", + ST_ISM330IS_DEV_NAME); + break; + } + + iio_dev->name = sensor->name; + + return iio_dev; +} + +static int +st_ism330is_shub_init_remote_sensor(struct st_ism330is_sensor *sensor) +{ + struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info; + int err = 0; + + if (ext_info->ext_dev_settings->bdu_reg.addr) + err = st_ism330is_shub_write_with_mask(sensor, + ext_info->ext_dev_settings->bdu_reg.addr, + ext_info->ext_dev_settings->bdu_reg.mask, 1); + + if (ext_info->ext_dev_settings->temp_comp_reg.addr) + err = st_ism330is_shub_write_with_mask(sensor, + ext_info->ext_dev_settings->temp_comp_reg.addr, + ext_info->ext_dev_settings->temp_comp_reg.mask, 1); + + if (ext_info->ext_dev_settings->off_canc_reg.addr) + err = st_ism330is_shub_write_with_mask(sensor, + ext_info->ext_dev_settings->off_canc_reg.addr, + ext_info->ext_dev_settings->off_canc_reg.mask, 1); + + return err; +} + +/** + * Probe device function [SHUB] + * + * @param hw: ST IMU MEMS hw instance. + * @return 0 if OK, negative for ERROR + */ +int st_ism330is_shub_probe(struct st_ism330is_hw *hw) +{ + const struct st_ism330is_ext_dev_settings *settings; + struct st_ism330is_sensor *acc_sensor, *sensor; + struct device_node *np = hw->dev->of_node; + u8 config[3], data, num_ext_dev = 0; + enum st_ism330is_sensor_id id; + int err, i = 0, j; + + if (np && of_property_read_bool(np, "drive-pullup-shub")) { + dev_info(hw->dev, "enabling pull up on i2c master\n"); + err = st_ism330is_shub_read_reg(hw, + ST_ISM330IS_REG_MASTER_CONFIG_ADDR, + &data, sizeof(data)); + if (err < 0) + return err; + + data |= ST_ISM330IS_SHUB_PU_EN_MASK; + err = st_ism330is_shub_write_reg(hw, + ST_ISM330IS_REG_MASTER_CONFIG_ADDR, + &data, sizeof(data)); + + if (err < 0) + return err; + + hw->i2c_master_pu = ST_ISM330IS_SHUB_PU_EN_MASK; + } + + acc_sensor = iio_priv(hw->iio_devs[ST_ISM330IS_ID_ACC]); + while (i < ARRAY_SIZE(st_ism330is_ext_dev_table) && + num_ext_dev < ST_ISM330IS_MAX_SLV_NUM) { + settings = &st_ism330is_ext_dev_table[i]; + + for (j = 0; j < ARRAY_SIZE(settings->i2c_addr); j++) { + if (!settings->i2c_addr[j]) + continue; + + /* read wai slave register */ + config[0] = (settings->i2c_addr[j] << 1) | 1; + config[1] = settings->wai_addr; + config[2] = 1; + + err = st_ism330is_shub_write_reg(hw, + ST_ISM330IS_REG_SLV0_ADDR, + config, sizeof(config)); + if (err < 0) + return err; + + err = st_ism330is_shub_master_enable(acc_sensor, true); + if (err < 0) + return err; + + st_ism330is_shub_wait_complete(hw); + + err = st_ism330is_shub_read_reg(hw, + ST_ISM330IS_REG_SENSOR_HUB_1_ADDR, + &data, sizeof(data)); + + st_ism330is_shub_master_enable(acc_sensor, false); + + if (err < 0) + return err; + + if (data != settings->wai_val) + continue; + + id = ST_ISM330IS_ID_EXT0 + num_ext_dev; + hw->iio_devs[id] = st_ism330is_shub_alloc_iio_dev(hw, + settings, id, + settings->i2c_addr[j]); + if (!hw->iio_devs[id]) + return -ENOMEM; + + sensor = iio_priv(hw->iio_devs[id]); + err = st_ism330is_shub_init_remote_sensor(sensor); + if (err < 0) + return err; + + num_ext_dev++; + hw->ext_data_len += settings->data_len; + break; + } + + i++; + } + + if (!num_ext_dev) + return 0; + + memset(config, 0, sizeof(config)); + err = st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV0_ADDR, + config, sizeof(config)); + if (err < 0) + return err; + + /* AuxSens = 3 + wr once */ + data = ST_ISM330IS_WRITE_ONCE_MASK | 3 | hw->i2c_master_pu; + return st_ism330is_shub_write_reg(hw, + ST_ISM330IS_REG_MASTER_CONFIG_ADDR, + &data, sizeof(data)); +} diff --git a/drivers/iio/stm/imu/st_ism330is/st_ism330is_spi.c b/drivers/iio/stm/imu/st_ism330is/st_ism330is_spi.c new file mode 100644 index 000000000000..6e76f5d400ff --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/st_ism330is_spi.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_ism330is spi driver + * + * MEMS Software Solutions Team + * + * Copyright 2023 STMicroelectronics Inc. + */ + +#include +#include +#include +#include +#include + +#include "st_ism330is.h" + +static const struct regmap_config st_ism330is_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int st_ism330is_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &st_ism330is_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to register spi regmap %d\n", + (int)PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return st_ism330is_probe(&spi->dev, spi->irq, regmap); +} + +static const struct of_device_id st_ism330is_spi_of_match[] = { + { .compatible = "st,ism330is" }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_ism330is_spi_of_match); + +static const struct spi_device_id st_ism330is_spi_id_table[] = { + { ST_ISM330IS_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_ism330is_spi_id_table); + +static struct spi_driver st_ism330is_driver = { + .driver = { + .name = "st_" ST_ISM330IS_DEV_NAME "_spi", + .pm = &st_ism330is_pm_ops, + .of_match_table = of_match_ptr(st_ism330is_spi_of_match), + }, + .probe = st_ism330is_spi_probe, + .id_table = st_ism330is_spi_id_table, +}; +module_spi_driver(st_ism330is_driver); + +MODULE_AUTHOR("MEMS Software Solutions Team"); +MODULE_DESCRIPTION("STMicroelectronics st_ism330is spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/stm/imu/st_ism330is/st_ism330is_triggers.c b/drivers/iio/stm/imu/st_ism330is/st_ism330is_triggers.c new file mode 100644 index 000000000000..5cf021eefd0c --- /dev/null +++ b/drivers/iio/stm/imu/st_ism330is/st_ism330is_triggers.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_ism330is trigger buffer library driver + * + * MEMS Software Solutions Team + * + * Copyright 2023 STMicroelectronics Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st_ism330is.h" + +#define ST_ISM330IS_AG_SAMPLE_SIZE 6 +#define ST_ISM330IS_PT_SAMPLE_SIZE 2 + +static int st_ism330is_buffer_enable(struct iio_dev *iio_dev, bool enable) +{ + struct st_ism330is_sensor *sensor = iio_priv(iio_dev); + + if (sensor->id == ST_ISM330IS_ID_EXT0 || + sensor->id == ST_ISM330IS_ID_EXT1) + return st_ism330is_shub_set_enable(sensor, enable); + + return st_ism330is_sensor_set_enable(sensor, enable); +} + +static int st_ism330is_fifo_preenable(struct iio_dev *iio_dev) +{ + return st_ism330is_buffer_enable(iio_dev, true); +} + +static int st_ism330is_fifo_postdisable(struct iio_dev *iio_dev) +{ + return st_ism330is_buffer_enable(iio_dev, false); +} + +static const struct iio_buffer_setup_ops st_ism330is_buffer_setup_ops = { + .preenable = st_ism330is_fifo_preenable, + +#if KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, +#endif /* LINUX_VERSION_CODE */ + + .postdisable = st_ism330is_fifo_postdisable, +}; + +static irqreturn_t st_ism330is_buffer_pollfunc(int irq, void *private) +{ + u8 iio_buf[ALIGN(ST_ISM330IS_AG_SAMPLE_SIZE, sizeof(s64)) + + sizeof(s64) + sizeof(s64)]; + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct st_ism330is_sensor *sensor = iio_priv(indio_dev); + struct st_ism330is_hw *hw = sensor->hw; + int addr = indio_dev->channels[0].address; + + switch (indio_dev->channels[0].type) { + case IIO_ACCEL: + case IIO_ANGL_VEL: + st_ism330is_read_locked(hw, addr, &iio_buf, + ST_ISM330IS_AG_SAMPLE_SIZE); + break; + case IIO_TEMP: + st_ism330is_read_locked(hw, addr, &iio_buf, + ST_ISM330IS_PT_SAMPLE_SIZE); + break; + case IIO_PRESSURE: + st_ism330is_shub_read(sensor, addr, (u8 *)&iio_buf, + ST_ISM330IS_PT_SAMPLE_SIZE); + break; + case IIO_MAGN: + st_ism330is_shub_read(sensor, addr, (u8 *)&iio_buf, + ST_ISM330IS_AG_SAMPLE_SIZE); + break; + default: + return -EINVAL; + } + + iio_push_to_buffers_with_timestamp(indio_dev, iio_buf, + iio_get_time_ns(indio_dev)); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int st_ism330is_trig_set_state(struct iio_trigger *trig, bool state) +{ + struct st_ism330is_hw *hw = iio_trigger_get_drvdata(trig); + + dev_dbg(hw->dev, "trigger set %d\n", state); + + return 0; +} + +static const struct iio_trigger_ops st_ism330is_trigger_ops = { + .set_trigger_state = st_ism330is_trig_set_state, +}; + +int st_ism330is_allocate_buffers(struct st_ism330is_hw *hw) +{ + int i; + + for (i = 0; + i < ARRAY_SIZE(st_ism330is_triggered_main_sensor_list); + i++) { + enum st_ism330is_sensor_id id; + int err; + + id = st_ism330is_triggered_main_sensor_list[i]; + if (!hw->iio_devs[id]) + continue; + + err = devm_iio_triggered_buffer_setup(hw->dev, + hw->iio_devs[id], NULL, + st_ism330is_buffer_pollfunc, + &st_ism330is_buffer_setup_ops); + if (err) + return err; + } + + return 0; +} diff --git a/stm_iio_configs/ism330is_defconfig b/stm_iio_configs/ism330is_defconfig new file mode 100644 index 000000000000..154edb935837 --- /dev/null +++ b/stm_iio_configs/ism330is_defconfig @@ -0,0 +1,3 @@ +CONFIG_IIO_ST_ISM330IS=m +CONFIG_IIO_ST_ISM330IS_I2C=m +CONFIG_IIO_ST_ISM330IS_SPI=m