From 92092c07b83220bdb9b9cdd9141c9479d469160e Mon Sep 17 00:00:00 2001 From: Milan Misic Date: Wed, 25 Mar 2026 22:31:09 +0100 Subject: Actually bump the version everywhere this time, minor postinst fixes --- DEBIAN/control | 2 +- DEBIAN/postinst | 8 +- usr/src/st-lsm6dsx-shift13mi-0.1/Makefile | 2 - usr/src/st-lsm6dsx-shift13mi-0.1/dkms.conf | 14 - usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx.h | 572 ---- .../st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_buffer.c | 872 ------ usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_core.c | 2808 -------------------- usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_i2c.c | 197 -- usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_shub.c | 922 ------- usr/src/st-lsm6dsx-shift13mi-1.1/Makefile | 2 + usr/src/st-lsm6dsx-shift13mi-1.1/dkms.conf | 14 + usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx.h | 572 ++++ .../st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_buffer.c | 872 ++++++ usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_core.c | 2808 ++++++++++++++++++++ usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_i2c.c | 197 ++ usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_shub.c | 922 +++++++ 16 files changed, 5392 insertions(+), 5392 deletions(-) delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/Makefile delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/dkms.conf delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx.h delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_buffer.c delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_core.c delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_i2c.c delete mode 100644 usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_shub.c create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/Makefile create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/dkms.conf create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx.h create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_buffer.c create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_core.c create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_i2c.c create mode 100644 usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_shub.c diff --git a/DEBIAN/control b/DEBIAN/control index e78517a..eddd8aa 100644 --- a/DEBIAN/control +++ b/DEBIAN/control @@ -1,5 +1,5 @@ Package: st-lsm6dsx-shift13mi -Version: 1.0-1 +Version: 1.1-1 Architecture: amd64 Maintainer: Milan Misic Depends: dkms, linux-headers-generic, iio-sensor-proxy, mokutil diff --git a/DEBIAN/postinst b/DEBIAN/postinst index ebcfc9f..cb2ba68 100755 --- a/DEBIAN/postinst +++ b/DEBIAN/postinst @@ -4,9 +4,9 @@ set -e lang=$(locale | grep LANG | cut -d= -f2 | cut -d_ -f1) # Install the module with dkms -dkms add st-lsm6dsx-shift13mi/0.1 || true -dkms build st-lsm6dsx-shift13mi/0.1 || true -dkms install st-lsm6dsx-shift13mi/0.1 || true +dkms add st-lsm6dsx-shift13mi/1.1 || true +dkms build st-lsm6dsx-shift13mi/1.1 || true +dkms install st-lsm6dsx-shift13mi/1.1 || true # Unload the old module and reload the new module modprobe -r st_lsm6dsx_i2c || true @@ -15,7 +15,7 @@ modprobe st_lsm6dsx_i2c || true # Reload udev and hwdb systemd-hwdb update udevadm trigger -systemctl restart iio-sensor-proxy +systemctl restart iio-sensor-proxy || true if mokutil --sb-state 2>/dev/null | grep -q "SecureBoot enabled"; then diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/Makefile b/usr/src/st-lsm6dsx-shift13mi-0.1/Makefile deleted file mode 100644 index 787d987..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-m += st_lsm6dsx.o st_lsm6dsx_i2c.o -st_lsm6dsx-objs := st_lsm6dsx_core.o st_lsm6dsx_buffer.o st_lsm6dsx_shub.o diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/dkms.conf b/usr/src/st-lsm6dsx-shift13mi-0.1/dkms.conf deleted file mode 100644 index bb13d3b..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/dkms.conf +++ /dev/null @@ -1,14 +0,0 @@ -PACKAGE_NAME="st-lsm6dsx-shift13mi" -PACKAGE_VERSION="0.1" - -BUILT_MODULE_NAME[0]="st_lsm6dsx_i2c" - -BUILT_MODULE_LOCATION[0]="" - -DEST_MODULE_LOCATION[0]="/kernel/drivers/iio/imu/st_lsm6dsx/" - -AUTOINSTALL="yes" - -MAKE="make -C /usr/lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build" -CLEAN="make -C /usr/lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean" - diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx.h b/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx.h deleted file mode 100644 index 6405a53..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx.h +++ /dev/null @@ -1,572 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * STMicroelectronics st_lsm6dsx sensor driver - * - * Copyright 2016 STMicroelectronics Inc. - * - * Lorenzo Bianconi - * Denis Ciocca - */ - -#ifndef ST_LSM6DSX_H -#define ST_LSM6DSX_H - -#include -#include -#include - -#define ST_LSM6DS3_DEV_NAME "lsm6ds3" -#define ST_LSM6DS3H_DEV_NAME "lsm6ds3h" -#define ST_LSM6DSL_DEV_NAME "lsm6dsl" -#define ST_LSM6DSM_DEV_NAME "lsm6dsm" -#define ST_ISM330DLC_DEV_NAME "ism330dlc" -#define ST_LSM6DSO_DEV_NAME "lsm6dso" -#define ST_ASM330LHH_DEV_NAME "asm330lhh" -#define ST_LSM6DSOX_DEV_NAME "lsm6dsox" -#define ST_LSM6DSR_DEV_NAME "lsm6dsr" -#define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c" -#define ST_ISM330DHCX_DEV_NAME "ism330dhcx" -#define ST_LSM9DS1_DEV_NAME "lsm9ds1-imu" -#define ST_LSM6DS0_DEV_NAME "lsm6ds0" -#define ST_LSM6DSRX_DEV_NAME "lsm6dsrx" -#define ST_LSM6DST_DEV_NAME "lsm6dst" -#define ST_LSM6DSOP_DEV_NAME "lsm6dsop" -#define ST_ASM330LHHX_DEV_NAME "asm330lhhx" -#define ST_LSM6DSTX_DEV_NAME "lsm6dstx" -#define ST_LSM6DSV_DEV_NAME "lsm6dsv" -#define ST_LSM6DSV16X_DEV_NAME "lsm6dsv16x" -#define ST_LSM6DSO16IS_DEV_NAME "lsm6dso16is" -#define ST_ISM330IS_DEV_NAME "ism330is" -#define ST_ASM330LHB_DEV_NAME "asm330lhb" -#define ST_ASM330LHHXG1_DEV_NAME "asm330lhhxg1" - -enum st_lsm6dsx_hw_id { - ST_LSM6DS3_ID = 1, - ST_LSM6DS3H_ID, - ST_LSM6DSL_ID, - ST_LSM6DSM_ID, - ST_ISM330DLC_ID, - ST_LSM6DSO_ID, - ST_ASM330LHH_ID, - ST_LSM6DSOX_ID, - ST_LSM6DSR_ID, - ST_LSM6DS3TRC_ID, - ST_ISM330DHCX_ID, - ST_LSM9DS1_ID, - ST_LSM6DS0_ID, - ST_LSM6DSRX_ID, - ST_LSM6DST_ID, - ST_LSM6DSOP_ID, - ST_ASM330LHHX_ID, - ST_LSM6DSTX_ID, - ST_LSM6DSV_ID, - ST_LSM6DSV16X_ID, - ST_LSM6DSO16IS_ID, - ST_ISM330IS_ID, - ST_ASM330LHB_ID, - ST_ASM330LHHXG1_ID, - ST_LSM6DSX_MAX_ID, -}; - -#define ST_LSM6DSX_BUFF_SIZE 512 -#define ST_LSM6DSX_CHAN_SIZE 2 -#define ST_LSM6DSX_SAMPLE_SIZE 6 -#define ST_LSM6DSX_TAG_SIZE 1 -#define ST_LSM6DSX_TAGGED_SAMPLE_SIZE (ST_LSM6DSX_SAMPLE_SIZE + \ - ST_LSM6DSX_TAG_SIZE) -#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \ - ST_LSM6DSX_SAMPLE_SIZE) -#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \ - * ST_LSM6DSX_TAGGED_SAMPLE_SIZE) -#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) - -#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx) \ -{ \ - .type = chan_type, \ - .address = addr, \ - .modified = 1, \ - .channel2 = mod, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .scan_index = scan_idx, \ - .scan_type = { \ - .sign = 's', \ - .realbits = 16, \ - .storagebits = 16, \ - .endianness = IIO_LE, \ - }, \ - .event_spec = &st_lsm6dsx_event, \ - .ext_info = st_lsm6dsx_ext_info, \ - .num_event_specs = 1, \ -} - -#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ -{ \ - .type = chan_type, \ - .address = addr, \ - .modified = 1, \ - .channel2 = mod, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .scan_index = scan_idx, \ - .scan_type = { \ - .sign = 's', \ - .realbits = 16, \ - .storagebits = 16, \ - .endianness = IIO_LE, \ - }, \ - .ext_info = st_lsm6dsx_ext_info, \ -} - -struct st_lsm6dsx_reg { - u8 addr; - u8 mask; -}; - -struct st_lsm6dsx_sensor; -struct st_lsm6dsx_hw; - -struct st_lsm6dsx_odr { - u32 milli_hz; - u8 val; -}; - -#define ST_LSM6DSX_ODR_LIST_SIZE 8 -struct st_lsm6dsx_odr_table_entry { - struct st_lsm6dsx_reg reg; - - struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE]; - int odr_len; -}; - -struct st_lsm6dsx_samples_to_discard { - struct { - u32 milli_hz; - u16 samples; - } val[ST_LSM6DSX_ODR_LIST_SIZE]; -}; - -struct st_lsm6dsx_fs { - u32 gain; - u8 val; -}; - -#define ST_LSM6DSX_FS_LIST_SIZE 4 -struct st_lsm6dsx_fs_table_entry { - struct st_lsm6dsx_reg reg; - - struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; - int fs_len; -}; - -/** - * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings - * @update_fifo: Update FIFO configuration callback. - * @read_fifo: Read FIFO callback. - * @fifo_th: FIFO threshold register info (addr + mask). - * @fifo_diff: FIFO diff status register info (addr + mask). - * @max_size: Sensor max fifo length in FIFO words. - * @th_wl: FIFO threshold word length. - */ -struct st_lsm6dsx_fifo_ops { - int (*update_fifo)(struct st_lsm6dsx_sensor *sensor, bool enable); - int (*read_fifo)(struct st_lsm6dsx_hw *hw); - struct { - u8 addr; - u16 mask; - } fifo_th; - struct { - u8 addr; - u16 mask; - } fifo_diff; - u16 max_size; - u8 th_wl; -}; - -/** - * struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings - * @timer_en: Hw timer enable register info (addr + mask). - * @hr_timer: Hw timer resolution register info (addr + mask). - * @fifo_en: Hw timer FIFO enable register info (addr + mask). - * @decimator: Hw timer FIFO decimator register info (addr + mask). - * @freq_fine: Difference in % of ODR with respect to the typical. - * @ts_sensitivity: Nominal timestamp sensitivity. - * @ts_trim_coeff: Coefficient for calculating the calibrated timestamp gain. - * This coefficient comes into play when linearizing the formula - * used to calculate the calibrated timestamp (please see the - * relevant formula in the AN for the specific IMU). - * For example, in the case of LSM6DSO we have: - * - * 1 / (1 + x) ~= 1 - x (Taylor’s Series) - * ttrim[s] = 1 / (40000 * (1 + 0.0015 * val)) (from AN5192) - * ttrim[ns] ~= 25000 - 37.5 * val - * ttrim[ns] ~= 25000 - (37500 * val) / 1000 - * - * so, replacing ts_sensitivity = 25000 and - * ts_trim_coeff = 37500 - * - * ttrim[ns] ~= ts_sensitivity - (ts_trim_coeff * val) / 1000 - */ -struct st_lsm6dsx_hw_ts_settings { - struct st_lsm6dsx_reg timer_en; - struct st_lsm6dsx_reg hr_timer; - struct st_lsm6dsx_reg fifo_en; - struct st_lsm6dsx_reg decimator; - u8 freq_fine; - u16 ts_sensitivity; - u16 ts_trim_coeff; -}; - -/** - * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings - * @page_mux: register page mux info (addr + mask). - * @master_en: master config register info (addr + mask). - * @pullup_en: i2c controller pull-up register info (addr + mask). - * @aux_sens: aux sensor register info (addr + mask). - * @wr_once: write_once register info (addr + mask). - * @emb_func: embedded function register info (addr + mask). - * @num_ext_dev: max number of slave devices. - * @shub_out: sensor hub first output register info. - * @slv0_addr: slave0 address in secondary page. - * @dw_slv0_addr: slave0 write register address in secondary page. - * @batch_en: Enable/disable FIFO batching. - * @pause: controller pause value. - */ -struct st_lsm6dsx_shub_settings { - struct st_lsm6dsx_reg page_mux; - struct { - bool sec_page; - u8 addr; - u8 mask; - } master_en; - struct { - bool sec_page; - u8 addr; - u8 mask; - } pullup_en; - struct st_lsm6dsx_reg aux_sens; - struct st_lsm6dsx_reg wr_once; - struct st_lsm6dsx_reg emb_func; - u8 num_ext_dev; - struct { - bool sec_page; - u8 addr; - } shub_out; - u8 slv0_addr; - u8 dw_slv0_addr; - u8 batch_en; - u8 pause; -}; - -struct st_lsm6dsx_event_settings { - struct st_lsm6dsx_reg enable_reg; - struct st_lsm6dsx_reg wakeup_reg; - u8 wakeup_src_reg; - u8 wakeup_src_status_mask; - u8 wakeup_src_z_mask; - u8 wakeup_src_y_mask; - u8 wakeup_src_x_mask; -}; - -enum st_lsm6dsx_sensor_id { - ST_LSM6DSX_ID_GYRO, - ST_LSM6DSX_ID_ACC, - ST_LSM6DSX_ID_EXT0, - ST_LSM6DSX_ID_EXT1, - ST_LSM6DSX_ID_EXT2, - ST_LSM6DSX_ID_MAX -}; - -enum st_lsm6dsx_ext_sensor_id { - ST_LSM6DSX_ID_MAGN, -}; - -/** - * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings - * @i2c_addr: I2c slave address list. - * @wai: Wai address info. - * @id: external sensor id. - * @odr_table: Output data rate of the sensor [Hz]. - * @fs_table: Configured sensor sensitivity table depending on full scale. - * @temp_comp: Temperature compensation register info (addr + mask). - * @pwr_table: Power on register info (addr + mask). - * @off_canc: Offset cancellation register info (addr + mask). - * @bdu: Block data update register info (addr + mask). - * @out: Output register info. - */ -struct st_lsm6dsx_ext_dev_settings { - u8 i2c_addr[2]; - struct { - u8 addr; - u8 val; - } wai; - enum st_lsm6dsx_ext_sensor_id id; - struct st_lsm6dsx_odr_table_entry odr_table; - struct st_lsm6dsx_fs_table_entry fs_table; - struct st_lsm6dsx_reg temp_comp; - struct { - struct st_lsm6dsx_reg reg; - u8 off_val; - u8 on_val; - } pwr_table; - struct st_lsm6dsx_reg off_canc; - struct st_lsm6dsx_reg bdu; - struct { - u8 addr; - u8 len; - } out; -}; - -/** - * struct st_lsm6dsx_settings - ST IMU sensor settings - * @reset: register address for reset. - * @boot: register address for boot. - * @bdu: register address for Block Data Update. - * @id: List of hw id/device name supported by the driver configuration. - * @channels: IIO channels supported by the device. - * @irq_config: interrupts related registers. - * @drdy_mask: register info for data-ready mask (addr + mask). - * @odr_table: Hw sensors odr table (Hz + val). - * @samples_to_discard: Number of samples to discard for filters settling time. - * @fs_table: Hw sensors gain table (gain + val). - * @decimator: List of decimator register info (addr + mask). - * @batch: List of FIFO batching register info (addr + mask). - * @fifo_ops: Sensor hw FIFO parameters. - * @ts_settings: Hw timer related settings. - * @shub_settings: i2c controller related settings. - */ -struct st_lsm6dsx_settings { - struct st_lsm6dsx_reg reset; - struct st_lsm6dsx_reg boot; - struct st_lsm6dsx_reg bdu; - struct { - enum st_lsm6dsx_hw_id hw_id; - const char *name; - u8 wai; - } id[ST_LSM6DSX_MAX_ID]; - struct { - const struct iio_chan_spec *chan; - int len; - } channels[2]; - struct { - struct st_lsm6dsx_reg irq1; - struct st_lsm6dsx_reg irq2; - struct st_lsm6dsx_reg irq1_func; - struct st_lsm6dsx_reg irq2_func; - struct st_lsm6dsx_reg lir; - struct st_lsm6dsx_reg clear_on_read; - struct st_lsm6dsx_reg hla; - struct st_lsm6dsx_reg od; - } irq_config; - struct st_lsm6dsx_reg drdy_mask; - struct st_lsm6dsx_odr_table_entry odr_table[2]; - struct st_lsm6dsx_samples_to_discard samples_to_discard[2]; - struct st_lsm6dsx_fs_table_entry fs_table[2]; - struct st_lsm6dsx_reg decimator[ST_LSM6DSX_ID_MAX]; - struct st_lsm6dsx_reg batch[2]; - struct st_lsm6dsx_fifo_ops fifo_ops; - struct st_lsm6dsx_hw_ts_settings ts_settings; - struct st_lsm6dsx_shub_settings shub_settings; - struct st_lsm6dsx_event_settings event_settings; -}; - -enum st_lsm6dsx_fifo_mode { - ST_LSM6DSX_FIFO_BYPASS = 0x0, - ST_LSM6DSX_FIFO_CONT = 0x6, -}; - -/** - * struct st_lsm6dsx_sensor - ST IMU sensor instance - * @name: Sensor name. - * @id: Sensor identifier. - * @hw: Pointer to instance of struct st_lsm6dsx_hw. - * @gain: Configured sensor sensitivity. - * @odr: Output data rate of the sensor [mHz]. - * hwfifo_odr_mHz: Batch data rate for hardware FIFO [mHz] - * @samples_to_discard: Number of samples to discard for filters settling time. - * @watermark: Sensor watermark level. - * @decimator: Sensor decimation factor. - * @sip: Number of samples in a given pattern. - * @ts_ref: Sensor timestamp reference for hw one. - * @ext_info: Sensor settings if it is connected to i2c controller - */ -struct st_lsm6dsx_sensor { - char name[32]; - enum st_lsm6dsx_sensor_id id; - struct st_lsm6dsx_hw *hw; - - u32 gain; - u32 odr; - u32 hwfifo_odr_mHz; - - u16 samples_to_discard; - u16 watermark; - u8 decimator; - u8 sip; - s64 ts_ref; - - struct { - const struct st_lsm6dsx_ext_dev_settings *settings; - u32 slv_odr; - u8 addr; - } ext_info; -}; - -/** - * struct st_lsm6dsx_hw - ST IMU MEMS hw instance - * @dev: Pointer to instance of struct device (I2C or SPI). - * @regmap: Register map of the device. - * @irq: Device interrupt line (I2C or SPI). - * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO. - * @conf_lock: Mutex to prevent concurrent FIFO configuration update. - * @page_lock: Mutex to prevent concurrent memory page configuration. - * @suspend_mask: Suspended sensor bitmask. - * @enable_mask: Enabled sensor bitmask. - * @fifo_mask: Enabled hw FIFO bitmask. - * @ts_gain: Hw timestamp rate after internal calibration. - * @ts_sip: Total number of timestamp samples in a given pattern. - * @sip: Total number of samples (acc/gyro/ts) in a given pattern. - * @buff: Device read buffer. - * @irq_routing: pointer to interrupt routing configuration. - * @event_threshold: wakeup event threshold. - * @enable_event: enabled event bitmask. - * @iio_devs: Pointers to acc/gyro iio_dev instances. - * @settings: Pointer to the specific sensor settings in use. - * @orientation: sensor chip orientation relative to main hardware. - * @scan: Temporary buffers used to align data before iio_push_to_buffers() - */ -struct st_lsm6dsx_hw { - struct device *dev; - struct regmap *regmap; - int irq; - - struct mutex fifo_lock; - struct mutex conf_lock; - struct mutex page_lock; - - u8 suspend_mask; - u8 enable_mask; - u8 fifo_mask; - s64 ts_gain; - u8 ts_sip; - u8 sip; - - const struct st_lsm6dsx_reg *irq_routing; - u8 event_threshold; - u8 enable_event; - - u8 *buff; - - struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX]; - - const struct st_lsm6dsx_settings *settings; - - struct iio_mount_matrix orientation; - /* Ensure natural alignment of buffer elements */ - struct { - __le16 channels[3]; - aligned_s64 ts; - } scan[ST_LSM6DSX_ID_MAX]; -}; - -static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_EITHER, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | - BIT(IIO_EV_INFO_ENABLE) -}; - -static __maybe_unused const unsigned long st_lsm6dsx_available_scan_masks[] = { - 0x7, 0x0, -}; - -extern const struct dev_pm_ops st_lsm6dsx_pm_ops; - -int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, - struct regmap *regmap); -int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, - bool enable); -int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw); -int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val); -int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, - u16 watermark); -int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable); -int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); -int st_lsm6dsx_resume_fifo(struct st_lsm6dsx_hw *hw); -int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); -int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw); -int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u32 odr, u8 *val); -int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name); -int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable); -int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len); -int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable); - -static inline int -st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr, - unsigned int mask, unsigned int val) -{ - int err; - - mutex_lock(&hw->page_lock); - err = regmap_update_bits(hw->regmap, addr, mask, val); - mutex_unlock(&hw->page_lock); - - return err; -} - -static inline int -st_lsm6dsx_read_locked(struct st_lsm6dsx_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_lsm6dsx_write_locked(struct st_lsm6dsx_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 const struct iio_mount_matrix * -st_lsm6dsx_get_mount_matrix(const struct iio_dev *iio_dev, - const struct iio_chan_spec *chan) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - - return &hw->orientation; -} - -static inline int -st_lsm6dsx_device_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) -{ - if (sensor->id == ST_LSM6DSX_ID_EXT0 || - sensor->id == ST_LSM6DSX_ID_EXT1 || - sensor->id == ST_LSM6DSX_ID_EXT2) - return st_lsm6dsx_shub_set_enable(sensor, enable); - - return st_lsm6dsx_sensor_set_enable(sensor, enable); -} - -static const -struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_ext_info[] = { - IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_lsm6dsx_get_mount_matrix), - { } -}; - -#endif /* ST_LSM6DSX_H */ diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_buffer.c b/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_buffer.c deleted file mode 100644 index 55d8777..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_buffer.c +++ /dev/null @@ -1,872 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * STMicroelectronics st_lsm6dsx FIFO buffer library driver - * - * Pattern FIFO: - * The FIFO buffer can be configured to store data from gyroscope and - * accelerometer. Samples are queued without any tag according to a - * specific pattern based on 'FIFO data sets' (6 bytes each): - * - 1st data set is reserved for gyroscope data - * - 2nd data set is reserved for accelerometer data - * The FIFO pattern changes depending on the ODRs and decimation factors - * assigned to the FIFO data sets. The first sequence of data stored in FIFO - * buffer contains the data of all the enabled FIFO data sets - * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the - * value of the decimation factor and ODR set for each FIFO data set. - * - * Supported devices: - * - ISM330DLC - * - LSM6DS3 - * - LSM6DS3H - * - LSM6DS3TR-C - * - LSM6DSL - * - LSM6DSM - * - * Tagged FIFO: - * The FIFO buffer can be configured to store data from gyroscope and - * accelerometer. Each sample is queued with a tag (1B) indicating data - * source (gyroscope, accelerometer, hw timer). - * - * Supported devices: - * - ASM330LHB - * - ASM330LHH - * - ASM330LHHX - * - ASM330LHHXG1 - * - ISM330DHCX - * - LSM6DSO - * - LSM6DSOP - * - LSM6DSOX - * - LSM6DSR - * - LSM6DSRX - * - LSM6DST - * - LSM6DSTX - * - LSM6DSV - * - * FIFO supported modes: - * - BYPASS: FIFO disabled - * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index - * restarts from the beginning and the oldest sample is overwritten - * - * Copyright 2016 STMicroelectronics Inc. - * - * Lorenzo Bianconi - * Denis Ciocca - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "st_lsm6dsx.h" - -#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a -#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0) -#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) -#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) -#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e -#define ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR 0x78 -#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42 - -#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 - -#define ST_LSM6DSX_TS_RESET_VAL 0xaa - -struct st_lsm6dsx_decimator_entry { - u8 decimator; - u8 val; -}; - -enum st_lsm6dsx_fifo_tag { - ST_LSM6DSX_GYRO_TAG = 0x01, - ST_LSM6DSX_ACC_TAG = 0x02, - ST_LSM6DSX_TS_TAG = 0x04, - ST_LSM6DSX_EXT0_TAG = 0x0f, - ST_LSM6DSX_EXT1_TAG = 0x10, - ST_LSM6DSX_EXT2_TAG = 0x11, -}; - -static const -struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { - { 0, 0x0 }, - { 1, 0x1 }, - { 2, 0x2 }, - { 3, 0x3 }, - { 4, 0x4 }, - { 8, 0x5 }, - { 16, 0x6 }, - { 32, 0x7 }, -}; - -static int -st_lsm6dsx_get_decimator_val(struct st_lsm6dsx_sensor *sensor, u32 max_odr) -{ - const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table); - u32 decimator = max_odr / sensor->hwfifo_odr_mHz; - int i; - - if (decimator > 1) - decimator = round_down(decimator, 2); - - for (i = 0; i < max_size; i++) { - if (st_lsm6dsx_decimator_table[i].decimator == decimator) - break; - } - - sensor->decimator = decimator; - return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val; -} - -static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, - u32 *max_odr, u32 *min_odr) -{ - struct st_lsm6dsx_sensor *sensor; - int i; - - *max_odr = 0, *min_odr = ~0; - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - sensor = iio_priv(hw->iio_devs[i]); - - if (!(hw->enable_mask & BIT(sensor->id))) - continue; - - *max_odr = max(*max_odr, sensor->hwfifo_odr_mHz); - *min_odr = min(*min_odr, sensor->hwfifo_odr_mHz); - } -} - -static u8 st_lsm6dsx_get_sip(struct st_lsm6dsx_sensor *sensor, u32 min_odr) -{ - u8 sip = sensor->hwfifo_odr_mHz / min_odr; - - return sip > 1 ? round_down(sip, 2) : sip; -} - -static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_reg *ts_dec_reg; - struct st_lsm6dsx_sensor *sensor; - u16 sip = 0, ts_sip = 0; - u32 max_odr, min_odr; - int err = 0, i; - u8 data; - - st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr); - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - const struct st_lsm6dsx_reg *dec_reg; - - if (!hw->iio_devs[i]) - continue; - - sensor = iio_priv(hw->iio_devs[i]); - /* update fifo decimators and sample in pattern */ - if (hw->enable_mask & BIT(sensor->id)) { - sensor->sip = st_lsm6dsx_get_sip(sensor, min_odr); - data = st_lsm6dsx_get_decimator_val(sensor, max_odr); - } else { - sensor->sip = 0; - data = 0; - } - ts_sip = max_t(u16, ts_sip, sensor->sip); - - dec_reg = &hw->settings->decimator[sensor->id]; - if (dec_reg->addr) { - int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask); - - err = st_lsm6dsx_update_bits_locked(hw, dec_reg->addr, - dec_reg->mask, - val); - if (err < 0) - return err; - } - sip += sensor->sip; - } - hw->sip = sip + ts_sip; - hw->ts_sip = ts_sip; - - /* - * update hw ts decimator if necessary. Decimator for hw timestamp - * is always 1 or 0 in order to have a ts sample for each data - * sample in FIFO - */ - ts_dec_reg = &hw->settings->ts_settings.decimator; - if (ts_dec_reg->addr) { - int val, ts_dec = !!hw->ts_sip; - - val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask); - err = st_lsm6dsx_update_bits_locked(hw, ts_dec_reg->addr, - ts_dec_reg->mask, val); - } - return err; -} - -static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, - enum st_lsm6dsx_fifo_mode fifo_mode) -{ - unsigned int data; - - data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode); - return st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_MODE_MASK, data); -} - -static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, - bool enable) -{ - struct st_lsm6dsx_hw *hw = sensor->hw; - const struct st_lsm6dsx_reg *batch_reg; - u8 data; - - batch_reg = &hw->settings->batch[sensor->id]; - if (batch_reg->addr) { - int val; - - if (enable) { - int err; - - err = st_lsm6dsx_check_odr(sensor, sensor->hwfifo_odr_mHz, - &data); - if (err < 0) - return err; - } else { - data = 0; - } - val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask); - return st_lsm6dsx_update_bits_locked(hw, batch_reg->addr, - batch_reg->mask, val); - } else { - data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; - return st_lsm6dsx_update_bits_locked(hw, - ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_ODR_MASK, - FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, - data)); - } -} - -int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) -{ - u16 fifo_watermark = ~0, cur_watermark, fifo_th_mask; - struct st_lsm6dsx_hw *hw = sensor->hw; - struct st_lsm6dsx_sensor *cur_sensor; - int i, err, data; - __le16 wdata; - - if (!hw->sip) - return 0; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - cur_sensor = iio_priv(hw->iio_devs[i]); - - if (!(hw->enable_mask & BIT(cur_sensor->id))) - continue; - - cur_watermark = (cur_sensor == sensor) ? watermark - : cur_sensor->watermark; - - fifo_watermark = min_t(u16, fifo_watermark, cur_watermark); - } - - fifo_watermark = max_t(u16, fifo_watermark, hw->sip); - fifo_watermark = (fifo_watermark / hw->sip) * hw->sip; - fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl; - - mutex_lock(&hw->page_lock); - err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1, - &data); - if (err < 0) - goto out; - - fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask; - fifo_watermark = ((data << 8) & ~fifo_th_mask) | - (fifo_watermark & fifo_th_mask); - - wdata = cpu_to_le16(fifo_watermark); - err = regmap_bulk_write(hw->regmap, - hw->settings->fifo_ops.fifo_th.addr, - &wdata, sizeof(wdata)); -out: - mutex_unlock(&hw->page_lock); - return err; -} - -static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) -{ - struct st_lsm6dsx_sensor *sensor; - int i, err; - - /* reset hw ts counter */ - err = st_lsm6dsx_write_locked(hw, ST_LSM6DSX_REG_TS_RESET_ADDR, - ST_LSM6DSX_TS_RESET_VAL); - if (err < 0) - return err; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - sensor = iio_priv(hw->iio_devs[i]); - /* - * store enable buffer timestamp as reference for - * hw timestamp - */ - sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]); - } - return 0; -} - -int st_lsm6dsx_resume_fifo(struct st_lsm6dsx_hw *hw) -{ - int err; - - /* reset hw ts counter */ - err = st_lsm6dsx_reset_hw_ts(hw); - if (err < 0) - return err; - - return st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); -} - -/* - * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN - * in order to avoid a kmalloc for each bus access - */ -static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, - u8 *data, unsigned int data_len, - unsigned int max_word_len) -{ - unsigned int word_len, read_len = 0; - int err; - - while (read_len < data_len) { - word_len = min_t(unsigned int, data_len - read_len, - max_word_len); - err = st_lsm6dsx_read_locked(hw, addr, data + read_len, - word_len); - if (err < 0) - return err; - read_len += word_len; - } - return 0; -} - -#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ - sizeof(s64)) + sizeof(s64)) -/** - * st_lsm6dsx_read_fifo() - hw FIFO read routine - * @hw: Pointer to instance of struct st_lsm6dsx_hw. - * - * Read samples from the hw FIFO and push them to IIO buffers. - * - * Return: Number of bytes read from the FIFO - */ -int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) -{ - struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor, *ext_sensor = NULL; - int err, sip, acc_sip, gyro_sip, ts_sip, ext_sip, read_len, offset; - u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; - u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; - bool reset_ts = false; - __le16 fifo_status; - s64 ts = 0; - - err = st_lsm6dsx_read_locked(hw, - hw->settings->fifo_ops.fifo_diff.addr, - &fifo_status, sizeof(fifo_status)); - if (err < 0) { - dev_err(hw->dev, "failed to read fifo status (err=%d)\n", - err); - return err; - } - - if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK)) - return 0; - - if (!pattern_len) - pattern_len = ST_LSM6DSX_SAMPLE_SIZE; - - fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * - ST_LSM6DSX_CHAN_SIZE; - fifo_len = (fifo_len / pattern_len) * pattern_len; - - acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); - if (hw->iio_devs[ST_LSM6DSX_ID_EXT0]) - ext_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_EXT0]); - - for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { - err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, - hw->buff, pattern_len, - ST_LSM6DSX_MAX_WORD_LEN); - if (err < 0) { - dev_err(hw->dev, - "failed to read pattern from fifo (err=%d)\n", - err); - return err; - } - - /* - * Data are written to the FIFO with a specific pattern - * depending on the configured ODRs. The first sequence of data - * stored in FIFO contains the data of all enabled sensors - * (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated - * depending on the value of the decimation factor set for each - * sensor. - * - * Supposing the FIFO is storing data from gyroscope and - * accelerometer at different ODRs: - * - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz - * Since the gyroscope ODR is twice the accelerometer one, the - * following pattern is repeated every 9 samples: - * - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, .. - */ - ext_sip = ext_sensor ? ext_sensor->sip : 0; - gyro_sip = gyro_sensor->sip; - acc_sip = acc_sensor->sip; - ts_sip = hw->ts_sip; - offset = 0; - sip = 0; - - while (acc_sip > 0 || gyro_sip > 0 || ext_sip > 0) { - if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { - memcpy(hw->scan[ST_LSM6DSX_ID_GYRO].channels, - &hw->buff[offset], - sizeof(hw->scan[ST_LSM6DSX_ID_GYRO].channels)); - offset += sizeof(hw->scan[ST_LSM6DSX_ID_GYRO].channels); - } - if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { - memcpy(hw->scan[ST_LSM6DSX_ID_ACC].channels, - &hw->buff[offset], - sizeof(hw->scan[ST_LSM6DSX_ID_ACC].channels)); - offset += sizeof(hw->scan[ST_LSM6DSX_ID_ACC].channels); - } - if (ext_sip > 0 && !(sip % ext_sensor->decimator)) { - memcpy(hw->scan[ST_LSM6DSX_ID_EXT0].channels, - &hw->buff[offset], - sizeof(hw->scan[ST_LSM6DSX_ID_EXT0].channels)); - offset += sizeof(hw->scan[ST_LSM6DSX_ID_EXT0].channels); - } - - if (ts_sip-- > 0) { - u8 data[ST_LSM6DSX_SAMPLE_SIZE]; - - memcpy(data, &hw->buff[offset], sizeof(data)); - /* - * hw timestamp is 3B long and it is stored - * in FIFO using 6B as 4th FIFO data set - * according to this schema: - * B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0] - */ - ts = data[1] << 16 | data[0] << 8 | data[3]; - /* - * check if hw timestamp engine is going to - * reset (the sensor generates an interrupt - * to signal the hw timestamp will reset in - * 1.638s) - */ - if (!reset_ts && ts >= 0xff0000) - reset_ts = true; - ts *= hw->ts_gain; - - offset += ST_LSM6DSX_SAMPLE_SIZE; - } - - if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { - /* - * We need to discards gyro samples during - * filters settling time - */ - if (gyro_sensor->samples_to_discard > 0) - gyro_sensor->samples_to_discard--; - else - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_GYRO], - &hw->scan[ST_LSM6DSX_ID_GYRO], - gyro_sensor->ts_ref + ts); - gyro_sip--; - } - if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { - /* - * We need to discards accel samples during - * filters settling time - */ - if (acc_sensor->samples_to_discard > 0) - acc_sensor->samples_to_discard--; - else - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_ACC], - &hw->scan[ST_LSM6DSX_ID_ACC], - acc_sensor->ts_ref + ts); - acc_sip--; - } - if (ext_sip > 0 && !(sip % ext_sensor->decimator)) { - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_EXT0], - &hw->scan[ST_LSM6DSX_ID_EXT0], - ext_sensor->ts_ref + ts); - ext_sip--; - } - sip++; - } - } - - if (unlikely(reset_ts)) { - err = st_lsm6dsx_reset_hw_ts(hw); - if (err < 0) { - dev_err(hw->dev, "failed to reset hw ts (err=%d)\n", - err); - return err; - } - } - return read_len; -} - -#define ST_LSM6DSX_INVALID_SAMPLE 0x7ffd -static int -st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, - u8 *data, s64 ts) -{ - s16 val = le16_to_cpu(*(__le16 *)data); - struct st_lsm6dsx_sensor *sensor; - struct iio_dev *iio_dev; - - /* invalid sample during bootstrap phase */ - if (val >= ST_LSM6DSX_INVALID_SAMPLE) - return -EINVAL; - - /* - * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG - * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG - * to the second one and ST_LSM6DSX_EXT2_TAG to the last enabled - * channel - */ - switch (tag) { - case ST_LSM6DSX_GYRO_TAG: - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO]; - break; - case ST_LSM6DSX_ACC_TAG: - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC]; - break; - case ST_LSM6DSX_EXT0_TAG: - if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0]; - else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1)) - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; - else - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; - break; - case ST_LSM6DSX_EXT1_TAG: - if ((hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) && - (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))) - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; - else - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; - break; - case ST_LSM6DSX_EXT2_TAG: - iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; - break; - default: - return -EINVAL; - } - - sensor = iio_priv(iio_dev); - iio_push_to_buffers_with_timestamp(iio_dev, data, - ts + sensor->ts_ref); - - return 0; -} - -/** - * st_lsm6dsx_read_tagged_fifo() - tagged hw FIFO read routine - * @hw: Pointer to instance of struct st_lsm6dsx_hw. - * - * Read samples from the hw FIFO and push them to IIO buffers. - * - * Return: Number of bytes read from the FIFO - */ -int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) -{ - u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE; - u16 fifo_len, fifo_diff_mask; - /* - * Alignment needed as this can ultimately be passed to a - * call to iio_push_to_buffers_with_timestamp() which - * must be passed a buffer that is aligned to 8 bytes so - * as to allow insertion of a naturally aligned timestamp. - */ - u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8); - u8 tag; - bool reset_ts = false; - int i, err, read_len; - __le16 fifo_status; - s64 ts = 0; - - err = st_lsm6dsx_read_locked(hw, - hw->settings->fifo_ops.fifo_diff.addr, - &fifo_status, sizeof(fifo_status)); - if (err < 0) { - dev_err(hw->dev, "failed to read fifo status (err=%d)\n", - err); - return err; - } - - fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; - fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * - ST_LSM6DSX_TAGGED_SAMPLE_SIZE; - if (!fifo_len) - return 0; - - if (!pattern_len) - pattern_len = ST_LSM6DSX_TAGGED_SAMPLE_SIZE; - - for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { - err = st_lsm6dsx_read_block(hw, - ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR, - hw->buff, pattern_len, - ST_LSM6DSX_MAX_TAGGED_WORD_LEN); - if (err < 0) { - dev_err(hw->dev, - "failed to read pattern from fifo (err=%d)\n", - err); - return err; - } - - for (i = 0; i < pattern_len; - i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) { - memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE], - ST_LSM6DSX_SAMPLE_SIZE); - - tag = hw->buff[i] >> 3; - if (tag == ST_LSM6DSX_TS_TAG) { - /* - * hw timestamp is 4B long and it is stored - * in FIFO according to this schema: - * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16], - * B3 = ts[31:24] - */ - ts = le32_to_cpu(*((__le32 *)iio_buff)); - /* - * check if hw timestamp engine is going to - * reset (the sensor generates an interrupt - * to signal the hw timestamp will reset in - * 1.638s) - */ - if (!reset_ts && ts >= 0xffff0000) - reset_ts = true; - ts *= hw->ts_gain; - } else { - st_lsm6dsx_push_tagged_data(hw, tag, iio_buff, - ts); - } - } - } - - if (unlikely(reset_ts)) { - err = st_lsm6dsx_reset_hw_ts(hw); - if (err < 0) - return err; - } - return read_len; -} - -int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) -{ - int err; - - if (!hw->settings->fifo_ops.read_fifo) - return -ENOTSUPP; - - mutex_lock(&hw->fifo_lock); - - hw->settings->fifo_ops.read_fifo(hw); - err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS); - - mutex_unlock(&hw->fifo_lock); - - return err; -} - -static void -st_lsm6dsx_update_samples_to_discard(struct st_lsm6dsx_sensor *sensor) -{ - const struct st_lsm6dsx_samples_to_discard *data; - struct st_lsm6dsx_hw *hw = sensor->hw; - int i; - - if (sensor->id != ST_LSM6DSX_ID_GYRO && - sensor->id != ST_LSM6DSX_ID_ACC) - return; - - /* check if drdy mask is supported in hw */ - if (hw->settings->drdy_mask.addr) - return; - - data = &hw->settings->samples_to_discard[sensor->id]; - for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { - if (data->val[i].milli_hz == sensor->hwfifo_odr_mHz) { - sensor->samples_to_discard = data->val[i].samples; - return; - } - } -} - -int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) -{ - struct st_lsm6dsx_hw *hw = sensor->hw; - u8 fifo_mask; - int err; - - mutex_lock(&hw->conf_lock); - - if (enable) - fifo_mask = hw->fifo_mask | BIT(sensor->id); - else - fifo_mask = hw->fifo_mask & ~BIT(sensor->id); - - if (hw->fifo_mask) { - err = st_lsm6dsx_flush_fifo(hw); - if (err < 0) - goto out; - } - - if (enable) - st_lsm6dsx_update_samples_to_discard(sensor); - - err = st_lsm6dsx_device_set_enable(sensor, enable); - if (err < 0) - goto out; - - err = st_lsm6dsx_set_fifo_odr(sensor, enable); - if (err < 0) - goto out; - - err = st_lsm6dsx_update_decimators(hw); - if (err < 0) - goto out; - - err = st_lsm6dsx_update_watermark(sensor, sensor->watermark); - if (err < 0) - goto out; - - if (fifo_mask) { - err = st_lsm6dsx_resume_fifo(hw); - if (err < 0) - goto out; - } - - hw->fifo_mask = fifo_mask; - -out: - mutex_unlock(&hw->conf_lock); - - return err; -} - -static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - - if (!hw->settings->fifo_ops.update_fifo) - return -ENOTSUPP; - - return hw->settings->fifo_ops.update_fifo(sensor, true); -} - -static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - - if (!hw->settings->fifo_ops.update_fifo) - return -ENOTSUPP; - - return hw->settings->fifo_ops.update_fifo(sensor, false); -} - -static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { - .preenable = st_lsm6dsx_buffer_preenable, - .postdisable = st_lsm6dsx_buffer_postdisable, -}; - -static ssize_t st_lsm6dsx_hwfifo_odr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); - - return sysfs_emit(buf, "%d.%03d\n", sensor->hwfifo_odr_mHz / 1000, - sensor->hwfifo_odr_mHz % 1000); -} - -static ssize_t st_lsm6dsx_hwfifo_odr_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *iio_dev = dev_to_iio_dev(dev); - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int integer, milli; - int ret; - u32 hwfifo_odr; - u8 data; - - if (!iio_device_claim_direct(iio_dev)) - return -EBUSY; - - ret = iio_str_to_fixpoint(buf, 100, &integer, &milli); - if (ret) - goto out; - - hwfifo_odr = integer * 1000 + milli; - ret = st_lsm6dsx_check_odr(sensor, hwfifo_odr, &data); - if (ret < 0) - goto out; - - hwfifo_odr = ret; - - /* the batch data rate must not exceed the sensor output data rate */ - if (hwfifo_odr <= sensor->odr) - sensor->hwfifo_odr_mHz = hwfifo_odr; - else - ret = -EINVAL; - -out: - iio_device_release_direct(iio_dev); - - return ret < 0 ? ret : len; -} - -static IIO_DEV_ATTR_SAMP_FREQ(0664, st_lsm6dsx_hwfifo_odr_show, st_lsm6dsx_hwfifo_odr_store); - -static const struct iio_dev_attr *st_lsm6dsx_buffer_attrs[] = { - &iio_dev_attr_sampling_frequency, - NULL -}; - -int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) -{ - int i, ret; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - ret = devm_iio_kfifo_buffer_setup_ext(hw->dev, hw->iio_devs[i], - &st_lsm6dsx_buffer_ops, - st_lsm6dsx_buffer_attrs); - if (ret) - return ret; - } - - return 0; -} diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_core.c b/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_core.c deleted file mode 100644 index dc78227..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_core.c +++ /dev/null @@ -1,2808 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * STMicroelectronics st_lsm6dsx sensor driver - * - * The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer - * and 3D digital gyroscope system-in-package with a digital I2C/SPI serial - * interface standard output. - * LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale - * acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of - * +-125/+-245/+-500/+-1000/+-2000 dps - * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer - * allowing dynamic batching of sensor data. - * LSM9DSx series is similar but includes an additional magnetometer, handled - * by a different driver. - * - * Supported sensors: - * - * - LSM6DS3 - * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 - * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 - * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 - * - FIFO size: 8KB - * - * - ISM330DLC - * - LSM6DS3H - * - LSM6DS3TR-C - * - LSM6DSL - * - LSM6DSM - * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 - * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 - * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 - * - FIFO size: 4KB - * - * - ASM330LHH - * - ASM330LHHX - * - ASM330LHHXG1 - * - ISM330DHCX - * - ISM330IS - * - LSM6DSO - * - LSM6DSO16IS - * - LSM6DSOP - * - LSM6DSOX - * - LSM6DSR - * - LSM6DST - * - LSM6DSTX - * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, - * 833 - * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 - * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 - * - FIFO size: 3KB - * - * - LSM6DSV - * - LSM6DSV16X - * - Accelerometer/Gyroscope supported ODR [Hz]: 7.5, 15, 30, 60, 120, 240, - * 480, 960 - * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 - * - Gyroscope supported full-scale [dps]: +-125/+-250/+-500/+-1000/+-2000 - * - FIFO size: 3KB - * - * - LSM6DS0 - * - LSM9DS1 - * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952 - * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 - * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952 - * - Gyroscope supported full-scale [dps]: +-245/+-500/+-2000 - * - FIFO size: 32 - * - * Copyright 2016 STMicroelectronics Inc. - * - * Lorenzo Bianconi - * Denis Ciocca - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "st_lsm6dsx.h" - -#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f - -static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { - ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0), - ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1), - ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2), - IIO_CHAN_SOFT_TIMESTAMP(3), -}; - -static const struct iio_chan_spec st_lsm6ds0_acc_channels[] = { - ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0), - ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1), - ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2), - IIO_CHAN_SOFT_TIMESTAMP(3), -}; - -static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = { - ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x22, IIO_MOD_X, 0), - ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x24, IIO_MOD_Y, 1), - ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x26, IIO_MOD_Z, 2), - IIO_CHAN_SOFT_TIMESTAMP(3), -}; - -static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = { - ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x18, IIO_MOD_X, 0), - ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x1a, IIO_MOD_Y, 1), - ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x1c, IIO_MOD_Z, 2), - IIO_CHAN_SOFT_TIMESTAMP(3), -}; - -static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { - { - .reset = { - .addr = 0x22, - .mask = BIT(0), - }, - .boot = { - .addr = 0x22, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x22, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM9DS1_ID, - .name = ST_LSM9DS1_DEV_NAME, - .wai = 0x68, - }, { - .hw_id = ST_LSM6DS0_ID, - .name = ST_LSM6DS0_DEV_NAME, - .wai = 0x68, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6ds0_acc_channels, - .len = ARRAY_SIZE(st_lsm6ds0_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6ds0_gyro_channels, - .len = ARRAY_SIZE(st_lsm6ds0_gyro_channels), - }, - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x20, - .mask = GENMASK(7, 5), - }, - .odr_avl[0] = { 10000, 0x01 }, - .odr_avl[1] = { 50000, 0x02 }, - .odr_avl[2] = { 119000, 0x03 }, - .odr_avl[3] = { 238000, 0x04 }, - .odr_avl[4] = { 476000, 0x05 }, - .odr_avl[5] = { 952000, 0x06 }, - .odr_len = 6, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 5), - }, - .odr_avl[0] = { 14900, 0x01 }, - .odr_avl[1] = { 59500, 0x02 }, - .odr_avl[2] = { 119000, 0x03 }, - .odr_avl[3] = { 238000, 0x04 }, - .odr_avl[4] = { 476000, 0x05 }, - .odr_avl[5] = { 952000, 0x06 }, - .odr_len = 6, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x20, - .mask = GENMASK(4, 3), - }, - .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(732000), 0x1 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(4, 3), - }, - - .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(70000000), 0x3 }, - .fs_len = 3, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0c, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .hla = { - .addr = 0x22, - .mask = BIT(5), - }, - .od = { - .addr = 0x22, - .mask = BIT(4), - }, - }, - .fifo_ops = { - .max_size = 32, - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM6DS3_ID, - .name = ST_LSM6DS3_DEV_NAME, - .wai = 0x69, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6dsx_acc_channels, - .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 4), - }, - .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_len = 6, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(7, 4), - }, - .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_len = 6, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0e, - .mask = BIT(3), - }, - .lir = { - .addr = 0x58, - .mask = BIT(0), - }, - .irq1_func = { - .addr = 0x5e, - .mask = BIT(5), - }, - .irq2_func = { - .addr = 0x5f, - .mask = BIT(5), - }, - .hla = { - .addr = 0x12, - .mask = BIT(5), - }, - .od = { - .addr = 0x12, - .mask = BIT(4), - }, - }, - .decimator = { - [ST_LSM6DSX_ID_ACC] = { - .addr = 0x08, - .mask = GENMASK(2, 0), - }, - [ST_LSM6DSX_ID_GYRO] = { - .addr = 0x08, - .mask = GENMASK(5, 3), - }, - }, - .fifo_ops = { - .update_fifo = st_lsm6dsx_update_fifo, - .read_fifo = st_lsm6dsx_read_fifo, - .fifo_th = { - .addr = 0x06, - .mask = GENMASK(11, 0), - }, - .fifo_diff = { - .addr = 0x3a, - .mask = GENMASK(11, 0), - }, - .max_size = 1365, - .th_wl = 3, /* 1LSB = 2B */ - }, - .ts_settings = { - .timer_en = { - .addr = 0x58, - .mask = BIT(7), - }, - .hr_timer = { - .addr = 0x5c, - .mask = BIT(4), - }, - .fifo_en = { - .addr = 0x07, - .mask = BIT(7), - }, - .decimator = { - .addr = 0x09, - .mask = GENMASK(5, 3), - }, - }, - .event_settings = { - .wakeup_reg = { - .addr = 0x5B, - .mask = GENMASK(5, 0), - }, - .wakeup_src_reg = 0x1b, - .wakeup_src_status_mask = BIT(3), - .wakeup_src_z_mask = BIT(0), - .wakeup_src_y_mask = BIT(1), - .wakeup_src_x_mask = BIT(2), - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM6DS3H_ID, - .name = ST_LSM6DS3H_DEV_NAME, - .wai = 0x69, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6dsx_acc_channels, - .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 4), - }, - .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_len = 6, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(7, 4), - }, - .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_len = 6, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0e, - .mask = BIT(3), - }, - .lir = { - .addr = 0x58, - .mask = BIT(0), - }, - .irq1_func = { - .addr = 0x5e, - .mask = BIT(5), - }, - .irq2_func = { - .addr = 0x5f, - .mask = BIT(5), - }, - .hla = { - .addr = 0x12, - .mask = BIT(5), - }, - .od = { - .addr = 0x12, - .mask = BIT(4), - }, - }, - .decimator = { - [ST_LSM6DSX_ID_ACC] = { - .addr = 0x08, - .mask = GENMASK(2, 0), - }, - [ST_LSM6DSX_ID_GYRO] = { - .addr = 0x08, - .mask = GENMASK(5, 3), - }, - }, - .fifo_ops = { - .update_fifo = st_lsm6dsx_update_fifo, - .read_fifo = st_lsm6dsx_read_fifo, - .fifo_th = { - .addr = 0x06, - .mask = GENMASK(11, 0), - }, - .fifo_diff = { - .addr = 0x3a, - .mask = GENMASK(11, 0), - }, - .max_size = 682, - .th_wl = 3, /* 1LSB = 2B */ - }, - .ts_settings = { - .timer_en = { - .addr = 0x58, - .mask = BIT(7), - }, - .hr_timer = { - .addr = 0x5c, - .mask = BIT(4), - }, - .fifo_en = { - .addr = 0x07, - .mask = BIT(7), - }, - .decimator = { - .addr = 0x09, - .mask = GENMASK(5, 3), - }, - }, - .event_settings = { - .wakeup_reg = { - .addr = 0x5B, - .mask = GENMASK(5, 0), - }, - .wakeup_src_reg = 0x1b, - .wakeup_src_status_mask = BIT(3), - .wakeup_src_z_mask = BIT(0), - .wakeup_src_y_mask = BIT(1), - .wakeup_src_x_mask = BIT(2), - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM6DSL_ID, - .name = ST_LSM6DSL_DEV_NAME, - .wai = 0x6a, - }, { - .hw_id = ST_LSM6DSM_ID, - .name = ST_LSM6DSM_DEV_NAME, - .wai = 0x6a, - }, { - .hw_id = ST_ISM330DLC_ID, - .name = ST_ISM330DLC_DEV_NAME, - .wai = 0x6a, - }, { - .hw_id = ST_LSM6DS3TRC_ID, - .name = ST_LSM6DS3TRC_DEV_NAME, - .wai = 0x6a, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6dsx_acc_channels, - .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 4), - }, - .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_len = 6, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(7, 4), - }, - .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_len = 6, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - }, - .samples_to_discard = { - [ST_LSM6DSX_ID_ACC] = { - .val[0] = { 12500, 1 }, - .val[1] = { 26000, 1 }, - .val[2] = { 52000, 1 }, - .val[3] = { 104000, 2 }, - .val[4] = { 208000, 2 }, - .val[5] = { 416000, 2 }, - }, - [ST_LSM6DSX_ID_GYRO] = { - .val[0] = { 12500, 2 }, - .val[1] = { 26000, 5 }, - .val[2] = { 52000, 7 }, - .val[3] = { 104000, 12 }, - .val[4] = { 208000, 20 }, - .val[5] = { 416000, 36 }, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0e, - .mask = BIT(3), - }, - .lir = { - .addr = 0x58, - .mask = BIT(0), - }, - .irq1_func = { - .addr = 0x5e, - .mask = BIT(5), - }, - .irq2_func = { - .addr = 0x5f, - .mask = BIT(5), - }, - .hla = { - .addr = 0x12, - .mask = BIT(5), - }, - .od = { - .addr = 0x12, - .mask = BIT(4), - }, - }, - .decimator = { - [ST_LSM6DSX_ID_ACC] = { - .addr = 0x08, - .mask = GENMASK(2, 0), - }, - [ST_LSM6DSX_ID_GYRO] = { - .addr = 0x08, - .mask = GENMASK(5, 3), - }, - [ST_LSM6DSX_ID_EXT0] = { - .addr = 0x09, - .mask = GENMASK(2, 0), - }, - }, - .fifo_ops = { - .update_fifo = st_lsm6dsx_update_fifo, - .read_fifo = st_lsm6dsx_read_fifo, - .fifo_th = { - .addr = 0x06, - .mask = GENMASK(10, 0), - }, - .fifo_diff = { - .addr = 0x3a, - .mask = GENMASK(10, 0), - }, - .max_size = 682, - .th_wl = 3, /* 1LSB = 2B */ - }, - .ts_settings = { - .timer_en = { - .addr = 0x19, - .mask = BIT(5), - }, - .hr_timer = { - .addr = 0x5c, - .mask = BIT(4), - }, - .fifo_en = { - .addr = 0x07, - .mask = BIT(7), - }, - .decimator = { - .addr = 0x09, - .mask = GENMASK(5, 3), - }, - }, - .shub_settings = { - .page_mux = { - .addr = 0x01, - .mask = BIT(7), - }, - .master_en = { - .addr = 0x1a, - .mask = BIT(0), - }, - .pullup_en = { - .addr = 0x1a, - .mask = BIT(3), - }, - .aux_sens = { - .addr = 0x04, - .mask = GENMASK(5, 4), - }, - .wr_once = { - .addr = 0x07, - .mask = BIT(5), - }, - .emb_func = { - .addr = 0x19, - .mask = BIT(2), - }, - .num_ext_dev = 1, - .shub_out = { - .addr = 0x2e, - }, - .slv0_addr = 0x02, - .dw_slv0_addr = 0x0e, - .pause = 0x7, - }, - .event_settings = { - .enable_reg = { - .addr = 0x58, - .mask = BIT(7), - }, - .wakeup_reg = { - .addr = 0x5B, - .mask = GENMASK(5, 0), - }, - .wakeup_src_reg = 0x1b, - .wakeup_src_status_mask = BIT(3), - .wakeup_src_z_mask = BIT(0), - .wakeup_src_y_mask = BIT(1), - .wakeup_src_x_mask = BIT(2), - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM6DSR_ID, - .name = ST_LSM6DSR_DEV_NAME, - .wai = 0x6b, - }, { - .hw_id = ST_ISM330DHCX_ID, - .name = ST_ISM330DHCX_DEV_NAME, - .wai = 0x6b, - }, { - .hw_id = ST_LSM6DSRX_ID, - .name = ST_LSM6DSRX_DEV_NAME, - .wai = 0x6b, - }, { - .hw_id = ST_LSM6DSO_ID, - .name = ST_LSM6DSO_DEV_NAME, - .wai = 0x6c, - }, { - .hw_id = ST_LSM6DSOX_ID, - .name = ST_LSM6DSOX_DEV_NAME, - .wai = 0x6c, - }, { - .hw_id = ST_LSM6DST_ID, - .name = ST_LSM6DST_DEV_NAME, - .wai = 0x6d, - }, { - .hw_id = ST_ASM330LHHX_ID, - .name = ST_ASM330LHHX_DEV_NAME, - .wai = 0x6b, - }, { - .hw_id = ST_ASM330LHHXG1_ID, - .name = ST_ASM330LHHXG1_DEV_NAME, - .wai = 0x6b, - }, { - .hw_id = ST_LSM6DSTX_ID, - .name = ST_LSM6DSTX_DEV_NAME, - .wai = 0x6d, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6dsx_acc_channels, - .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .drdy_mask = { - .addr = 0x13, - .mask = BIT(3), - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 4), - }, - .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 }, - .odr_len = 7, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(7, 4), - }, - .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 }, - .odr_len = 7, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0e, - .mask = BIT(3), - }, - .lir = { - .addr = 0x56, - .mask = BIT(0), - }, - .clear_on_read = { - .addr = 0x56, - .mask = BIT(6), - }, - .irq1_func = { - .addr = 0x5e, - .mask = BIT(5), - }, - .irq2_func = { - .addr = 0x5f, - .mask = BIT(5), - }, - .hla = { - .addr = 0x12, - .mask = BIT(5), - }, - .od = { - .addr = 0x12, - .mask = BIT(4), - }, - }, - .batch = { - [ST_LSM6DSX_ID_ACC] = { - .addr = 0x09, - .mask = GENMASK(3, 0), - }, - [ST_LSM6DSX_ID_GYRO] = { - .addr = 0x09, - .mask = GENMASK(7, 4), - }, - }, - .fifo_ops = { - .update_fifo = st_lsm6dsx_update_fifo, - .read_fifo = st_lsm6dsx_read_tagged_fifo, - .fifo_th = { - .addr = 0x07, - .mask = GENMASK(8, 0), - }, - .fifo_diff = { - .addr = 0x3a, - .mask = GENMASK(9, 0), - }, - .max_size = 512, - .th_wl = 1, - }, - .ts_settings = { - .timer_en = { - .addr = 0x19, - .mask = BIT(5), - }, - .decimator = { - .addr = 0x0a, - .mask = GENMASK(7, 6), - }, - .freq_fine = 0x63, - .ts_sensitivity = 25000, - .ts_trim_coeff = 37500, - }, - .shub_settings = { - .page_mux = { - .addr = 0x01, - .mask = BIT(6), - }, - .master_en = { - .sec_page = true, - .addr = 0x14, - .mask = BIT(2), - }, - .pullup_en = { - .sec_page = true, - .addr = 0x14, - .mask = BIT(3), - }, - .aux_sens = { - .addr = 0x14, - .mask = GENMASK(1, 0), - }, - .wr_once = { - .addr = 0x14, - .mask = BIT(6), - }, - .num_ext_dev = 3, - .shub_out = { - .sec_page = true, - .addr = 0x02, - }, - .slv0_addr = 0x15, - .dw_slv0_addr = 0x21, - .batch_en = BIT(3), - }, - .event_settings = { - .enable_reg = { - .addr = 0x58, - .mask = BIT(7), - }, - .wakeup_reg = { - .addr = 0x5b, - .mask = GENMASK(5, 0), - }, - .wakeup_src_reg = 0x1b, - .wakeup_src_status_mask = BIT(3), - .wakeup_src_z_mask = BIT(0), - .wakeup_src_y_mask = BIT(1), - .wakeup_src_x_mask = BIT(2), - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_ASM330LHH_ID, - .name = ST_ASM330LHH_DEV_NAME, - .wai = 0x6b, - }, { - .hw_id = ST_LSM6DSOP_ID, - .name = ST_LSM6DSOP_DEV_NAME, - .wai = 0x6c, - }, { - .hw_id = ST_ASM330LHB_ID, - .name = ST_ASM330LHB_DEV_NAME, - .wai = 0x6b, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6dsx_acc_channels, - .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .drdy_mask = { - .addr = 0x13, - .mask = BIT(3), - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 4), - }, - .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 }, - .odr_len = 7, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(7, 4), - }, - .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 }, - .odr_len = 7, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0e, - .mask = BIT(3), - }, - .lir = { - .addr = 0x56, - .mask = BIT(0), - }, - .clear_on_read = { - .addr = 0x56, - .mask = BIT(6), - }, - .irq1_func = { - .addr = 0x5e, - .mask = BIT(5), - }, - .irq2_func = { - .addr = 0x5f, - .mask = BIT(5), - }, - .hla = { - .addr = 0x12, - .mask = BIT(5), - }, - .od = { - .addr = 0x12, - .mask = BIT(4), - }, - }, - .batch = { - [ST_LSM6DSX_ID_ACC] = { - .addr = 0x09, - .mask = GENMASK(3, 0), - }, - [ST_LSM6DSX_ID_GYRO] = { - .addr = 0x09, - .mask = GENMASK(7, 4), - }, - }, - .fifo_ops = { - .update_fifo = st_lsm6dsx_update_fifo, - .read_fifo = st_lsm6dsx_read_tagged_fifo, - .fifo_th = { - .addr = 0x07, - .mask = GENMASK(8, 0), - }, - .fifo_diff = { - .addr = 0x3a, - .mask = GENMASK(9, 0), - }, - .max_size = 512, - .th_wl = 1, - }, - .ts_settings = { - .timer_en = { - .addr = 0x19, - .mask = BIT(5), - }, - .decimator = { - .addr = 0x0a, - .mask = GENMASK(7, 6), - }, - .freq_fine = 0x63, - .ts_sensitivity = 25000, - .ts_trim_coeff = 37500, - }, - .event_settings = { - .enable_reg = { - .addr = 0x58, - .mask = BIT(7), - }, - .wakeup_reg = { - .addr = 0x5B, - .mask = GENMASK(5, 0), - }, - .wakeup_src_reg = 0x1b, - .wakeup_src_status_mask = BIT(3), - .wakeup_src_z_mask = BIT(0), - .wakeup_src_y_mask = BIT(1), - .wakeup_src_x_mask = BIT(2), - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM6DSV_ID, - .name = ST_LSM6DSV_DEV_NAME, - .wai = 0x70, - }, { - .hw_id = ST_LSM6DSV16X_ID, - .name = ST_LSM6DSV16X_DEV_NAME, - .wai = 0x70, - }, - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6dsx_acc_channels, - .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .drdy_mask = { - .addr = 0x13, - .mask = BIT(3), - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 0), - }, - .odr_avl[0] = { 7500, 0x02 }, - .odr_avl[1] = { 15000, 0x03 }, - .odr_avl[2] = { 30000, 0x04 }, - .odr_avl[3] = { 60000, 0x05 }, - .odr_avl[4] = { 120000, 0x06 }, - .odr_avl[5] = { 240000, 0x07 }, - .odr_avl[6] = { 480000, 0x08 }, - .odr_avl[7] = { 960000, 0x09 }, - .odr_len = 8, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 0), - }, - .odr_avl[0] = { 7500, 0x02 }, - .odr_avl[1] = { 15000, 0x03 }, - .odr_avl[2] = { 30000, 0x04 }, - .odr_avl[3] = { 60000, 0x05 }, - .odr_avl[4] = { 120000, 0x06 }, - .odr_avl[5] = { 240000, 0x07 }, - .odr_avl[6] = { 480000, 0x08 }, - .odr_avl[7] = { 960000, 0x09 }, - .odr_len = 8, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x17, - .mask = GENMASK(1, 0), - }, - .fs_avl[0] = { IIO_G_TO_M_S_2(61000), 0x0 }, - .fs_avl[1] = { IIO_G_TO_M_S_2(122000), 0x1 }, - .fs_avl[2] = { IIO_G_TO_M_S_2(244000), 0x2 }, - .fs_avl[3] = { IIO_G_TO_M_S_2(488000), 0x3 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x15, - .mask = GENMASK(3, 0), - }, - .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750000), 0x1 }, - .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500000), 0x2 }, - .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000000), 0x3 }, - .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000000), 0x4 }, - .fs_len = 4, - }, - }, - .irq_config = { - .irq1 = { - .addr = 0x0d, - .mask = BIT(3), - }, - .irq2 = { - .addr = 0x0e, - .mask = BIT(3), - }, - .lir = { - .addr = 0x56, - .mask = BIT(0), - }, - .irq1_func = { - .addr = 0x5e, - .mask = BIT(5), - }, - .irq2_func = { - .addr = 0x5f, - .mask = BIT(5), - }, - .hla = { - .addr = 0x03, - .mask = BIT(4), - }, - .od = { - .addr = 0x03, - .mask = BIT(3), - }, - }, - .batch = { - [ST_LSM6DSX_ID_ACC] = { - .addr = 0x09, - .mask = GENMASK(3, 0), - }, - [ST_LSM6DSX_ID_GYRO] = { - .addr = 0x09, - .mask = GENMASK(7, 4), - }, - }, - .fifo_ops = { - .update_fifo = st_lsm6dsx_update_fifo, - .read_fifo = st_lsm6dsx_read_tagged_fifo, - .fifo_th = { - .addr = 0x07, - .mask = GENMASK(7, 0), - }, - .fifo_diff = { - .addr = 0x1b, - .mask = GENMASK(8, 0), - }, - .max_size = 512, - .th_wl = 1, - }, - .ts_settings = { - .timer_en = { - .addr = 0x50, - .mask = BIT(6), - }, - .decimator = { - .addr = 0x0a, - .mask = GENMASK(7, 6), - }, - .freq_fine = 0x4f, - .ts_sensitivity = 21701, - .ts_trim_coeff = 28212, - }, - .shub_settings = { - .page_mux = { - .addr = 0x01, - .mask = BIT(6), - }, - .master_en = { - .sec_page = true, - .addr = 0x14, - .mask = BIT(2), - }, - .pullup_en = { - .addr = 0x03, - .mask = BIT(6), - }, - .aux_sens = { - .addr = 0x14, - .mask = GENMASK(1, 0), - }, - .wr_once = { - .addr = 0x14, - .mask = BIT(6), - }, - .num_ext_dev = 3, - .shub_out = { - .sec_page = true, - .addr = 0x02, - }, - .slv0_addr = 0x15, - .dw_slv0_addr = 0x21, - .batch_en = BIT(3), - }, - .event_settings = { - .enable_reg = { - .addr = 0x50, - .mask = BIT(7), - }, - .wakeup_reg = { - .addr = 0x5b, - .mask = GENMASK(5, 0), - }, - .wakeup_src_reg = 0x45, - .wakeup_src_status_mask = BIT(3), - .wakeup_src_z_mask = BIT(0), - .wakeup_src_y_mask = BIT(1), - .wakeup_src_x_mask = BIT(2), - }, - }, - { - .reset = { - .addr = 0x12, - .mask = BIT(0), - }, - .boot = { - .addr = 0x12, - .mask = BIT(7), - }, - .bdu = { - .addr = 0x12, - .mask = BIT(6), - }, - .id = { - { - .hw_id = ST_LSM6DSO16IS_ID, - .name = ST_LSM6DSO16IS_DEV_NAME, - .wai = 0x22, - }, { - .hw_id = ST_ISM330IS_ID, - .name = ST_ISM330IS_DEV_NAME, - .wai = 0x22, - } - }, - .channels = { - [ST_LSM6DSX_ID_ACC] = { - .chan = st_lsm6ds0_acc_channels, - .len = ARRAY_SIZE(st_lsm6ds0_acc_channels), - }, - [ST_LSM6DSX_ID_GYRO] = { - .chan = st_lsm6dsx_gyro_channels, - .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), - }, - }, - .odr_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(7, 4), - }, - .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 }, - .odr_len = 7, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(7, 4), - }, - .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 }, - .odr_len = 7, - }, - }, - .fs_table = { - [ST_LSM6DSX_ID_ACC] = { - .reg = { - .addr = 0x10, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - [ST_LSM6DSX_ID_GYRO] = { - .reg = { - .addr = 0x11, - .mask = GENMASK(3, 2), - }, - .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 }, - .fs_len = 4, - }, - }, - .irq_config = { - .hla = { - .addr = 0x12, - .mask = BIT(5), - }, - .od = { - .addr = 0x12, - .mask = BIT(4), - }, - }, - .shub_settings = { - .page_mux = { - .addr = 0x01, - .mask = BIT(6), - }, - .master_en = { - .sec_page = true, - .addr = 0x14, - .mask = BIT(2), - }, - .pullup_en = { - .sec_page = true, - .addr = 0x14, - .mask = BIT(3), - }, - .aux_sens = { - .addr = 0x14, - .mask = GENMASK(1, 0), - }, - .wr_once = { - .addr = 0x14, - .mask = BIT(6), - }, - .num_ext_dev = 3, - .shub_out = { - .sec_page = true, - .addr = 0x02, - }, - .slv0_addr = 0x15, - .dw_slv0_addr = 0x21, - }, - }, -}; - -int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - unsigned int data; - int err; - - hub_settings = &hw->settings->shub_settings; - data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask); - err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr, - hub_settings->page_mux.mask, data); - usleep_range(100, 150); - - return err; -} - -static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, - const char **name) -{ - int err, i, j, data; - - for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) { - for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) { - if (st_lsm6dsx_sensor_settings[i].id[j].name && - id == st_lsm6dsx_sensor_settings[i].id[j].hw_id) - break; - } - if (j < ST_LSM6DSX_MAX_ID) - break; - } - - if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) { - dev_err(hw->dev, "unsupported hw id [%02x]\n", id); - return -ENODEV; - } - - err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data); - if (err < 0) { - dev_err(hw->dev, "failed to read whoami register\n"); - return err; - } - - if (data != st_lsm6dsx_sensor_settings[i].id[j].wai) { - dev_err(hw->dev, "unsupported whoami [%02x]\n", data); - return -ENODEV; - } - - *name = st_lsm6dsx_sensor_settings[i].id[j].name; - hw->settings = &st_lsm6dsx_sensor_settings[i]; - - return 0; -} - -static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, - u32 gain) -{ - const struct st_lsm6dsx_fs_table_entry *fs_table; - unsigned int data; - int i, err; - - fs_table = &sensor->hw->settings->fs_table[sensor->id]; - for (i = 0; i < fs_table->fs_len; i++) { - if (fs_table->fs_avl[i].gain == gain) - break; - } - - if (i == fs_table->fs_len) - return -EINVAL; - - data = ST_LSM6DSX_SHIFT_VAL(fs_table->fs_avl[i].val, - fs_table->reg.mask); - err = st_lsm6dsx_update_bits_locked(sensor->hw, fs_table->reg.addr, - fs_table->reg.mask, data); - if (err < 0) - return err; - - sensor->gain = gain; - - return 0; -} - -int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u32 odr, u8 *val) -{ - const struct st_lsm6dsx_odr_table_entry *odr_table; - int i; - - odr_table = &sensor->hw->settings->odr_table[sensor->id]; - for (i = 0; i < odr_table->odr_len; i++) { - /* - * ext devices can run at different odr respect to - * accel sensor - */ - if (odr_table->odr_avl[i].milli_hz >= odr) - break; - } - - if (i == odr_table->odr_len) - return -EINVAL; - - *val = odr_table->odr_avl[i].val; - return odr_table->odr_avl[i].milli_hz; -} - -static int -st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u32 odr, - enum st_lsm6dsx_sensor_id id) -{ - struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]); - - if (odr > 0) { - if (hw->enable_mask & BIT(id)) - return max_t(u32, ref->odr, odr); - else - return odr; - } else { - return (hw->enable_mask & BIT(id)) ? ref->odr : 0; - } -} - -static int -st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) -{ - struct st_lsm6dsx_sensor *ref_sensor = sensor; - struct st_lsm6dsx_hw *hw = sensor->hw; - const struct st_lsm6dsx_reg *reg; - unsigned int data; - u8 val = 0; - int err; - - switch (sensor->id) { - case ST_LSM6DSX_ID_GYRO: - break; - case ST_LSM6DSX_ID_EXT0: - case ST_LSM6DSX_ID_EXT1: - case ST_LSM6DSX_ID_EXT2: - case ST_LSM6DSX_ID_ACC: { - u32 odr; - int i; - - /* - * i2c embedded controller relies on the accelerometer sensor as - * bus read/write trigger so we need to enable accel device - * at odr = max(accel_odr, ext_odr) in order to properly - * communicate with i2c slave devices - */ - ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i] || i == sensor->id) - continue; - - odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i); - if (odr != req_odr) - /* device already configured */ - return 0; - } - break; - } - default: /* should never occur */ - return -EINVAL; - } - - if (req_odr > 0) { - err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val); - if (err < 0) - return err; - } - - reg = &hw->settings->odr_table[ref_sensor->id].reg; - data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); - return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data); -} - -static int -__st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, - bool enable) -{ - struct st_lsm6dsx_hw *hw = sensor->hw; - u32 odr = enable ? sensor->odr : 0; - int err; - - err = st_lsm6dsx_set_odr(sensor, odr); - if (err < 0) - return err; - - if (enable) - hw->enable_mask |= BIT(sensor->id); - else - hw->enable_mask &= ~BIT(sensor->id); - - return 0; -} - -static int -st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor, bool enable) -{ - struct st_lsm6dsx_hw *hw = sensor->hw; - - if (sensor->id == ST_LSM6DSX_ID_GYRO || enable) - return 0; - - return hw->enable_event; -} - -int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, - bool enable) -{ - if (st_lsm6dsx_check_events(sensor, enable)) - return 0; - - return __st_lsm6dsx_sensor_set_enable(sensor, enable); -} - -static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, - u8 addr, int *val) -{ - struct st_lsm6dsx_hw *hw = sensor->hw; - int err, delay; - __le16 data; - - err = st_lsm6dsx_sensor_set_enable(sensor, true); - if (err < 0) - return err; - - /* - * we need to wait for sensor settling time before - * reading data in order to avoid corrupted samples - */ - delay = 1000000000 / sensor->odr; - usleep_range(3 * delay, 4 * delay); - - err = st_lsm6dsx_read_locked(hw, addr, &data, sizeof(data)); - if (err < 0) - return err; - - if (!hw->enable_event) { - err = st_lsm6dsx_sensor_set_enable(sensor, false); - if (err < 0) - return err; - } - - *val = (s16)le16_to_cpu(data); - - return IIO_VAL_INT; -} - -static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *ch, - int *val, int *val2, long mask) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (!iio_device_claim_direct(iio_dev)) - return -EBUSY; - - ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val); - iio_device_release_direct(iio_dev); - break; - case IIO_CHAN_INFO_SAMP_FREQ: - *val = sensor->odr / 1000; - *val2 = (sensor->odr % 1000) * 1000; - ret = IIO_VAL_INT_PLUS_MICRO; - break; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = sensor->gain; - ret = IIO_VAL_INT_PLUS_NANO; - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int err = 0; - - if (!iio_device_claim_direct(iio_dev)) - return -EBUSY; - - switch (mask) { - case IIO_CHAN_INFO_SCALE: - err = st_lsm6dsx_set_full_scale(sensor, val2); - break; - case IIO_CHAN_INFO_SAMP_FREQ: { - u8 data; - - val = val * 1000 + val2 / 1000; - val = st_lsm6dsx_check_odr(sensor, val, &data); - if (val < 0) { - err = val; - } else { - sensor->odr = val; - sensor->hwfifo_odr_mHz = val; - } - break; - } - default: - err = -EINVAL; - break; - } - - iio_device_release_direct(iio_dev); - - return err; -} - -static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, bool state) -{ - const struct st_lsm6dsx_reg *reg; - unsigned int data; - int err; - - if (!hw->settings->irq_config.irq1_func.addr) - return -ENOTSUPP; - - reg = &hw->settings->event_settings.enable_reg; - if (reg->addr) { - data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask); - err = st_lsm6dsx_update_bits_locked(hw, reg->addr, - reg->mask, data); - if (err < 0) - return err; - } - - /* Enable wakeup interrupt */ - data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask); - return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr, - hw->irq_routing->mask, data); -} - -static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - - if (type != IIO_EV_TYPE_THRESH) - return -EINVAL; - - *val2 = 0; - *val = hw->event_threshold; - - return IIO_VAL_INT; -} - -static int -st_lsm6dsx_write_event(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - const struct st_lsm6dsx_reg *reg; - unsigned int data; - int err; - - if (type != IIO_EV_TYPE_THRESH) - return -EINVAL; - - if (val < 0 || val > 31) - return -EINVAL; - - reg = &hw->settings->event_settings.wakeup_reg; - data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); - err = st_lsm6dsx_update_bits_locked(hw, reg->addr, - reg->mask, data); - if (err < 0) - return -EINVAL; - - hw->event_threshold = val; - - return 0; -} - -static int -st_lsm6dsx_read_event_config(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - - if (type != IIO_EV_TYPE_THRESH) - return -EINVAL; - - return !!(hw->enable_event & BIT(chan->channel2)); -} - -static int -st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, bool state) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - u8 enable_event; - int err; - - if (type != IIO_EV_TYPE_THRESH) - return -EINVAL; - - if (state) { - enable_event = hw->enable_event | BIT(chan->channel2); - - /* do not enable events if they are already enabled */ - if (hw->enable_event) - goto out; - } else { - enable_event = hw->enable_event & ~BIT(chan->channel2); - - /* only turn off sensor if no events is enabled */ - if (enable_event) - goto out; - } - - /* stop here if no changes have been made */ - if (hw->enable_event == enable_event) - return 0; - - err = st_lsm6dsx_event_setup(hw, state); - if (err < 0) - return err; - - mutex_lock(&hw->conf_lock); - if (enable_event || !(hw->fifo_mask & BIT(sensor->id))) - err = __st_lsm6dsx_sensor_set_enable(sensor, state); - mutex_unlock(&hw->conf_lock); - if (err < 0) - return err; - -out: - hw->enable_event = enable_event; - - return 0; -} - -int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - int err; - - val = clamp_val(val, 1, hw->settings->fifo_ops.max_size); - - mutex_lock(&hw->conf_lock); - - err = st_lsm6dsx_update_watermark(sensor, val); - - mutex_unlock(&hw->conf_lock); - - if (err < 0) - return err; - - sensor->watermark = val; - - return 0; -} - -static ssize_t -st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); - const struct st_lsm6dsx_odr_table_entry *odr_table; - int i, len = 0; - - odr_table = &sensor->hw->settings->odr_table[sensor->id]; - for (i = 0; i < odr_table->odr_len; i++) - len += sysfs_emit_at(buf, len, "%d.%03d%c", - odr_table->odr_avl[i].milli_hz / 1000, - odr_table->odr_avl[i].milli_hz % 1000, - (i == odr_table->odr_len - 1) ? '\n' : ' '); - - return len; -} - -static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); - const struct st_lsm6dsx_fs_table_entry *fs_table; - struct st_lsm6dsx_hw *hw = sensor->hw; - int i, len = 0; - - fs_table = &hw->settings->fs_table[sensor->id]; - for (i = 0; i < fs_table->fs_len; i++) - len += sysfs_emit_at(buf, len, "0.%09u%c", - fs_table->fs_avl[i].gain, - (i == fs_table->fs_len - 1) ? '\n' : ' '); - - return len; -} - -static int st_lsm6dsx_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; - default: - return IIO_VAL_INT_PLUS_MICRO; - } - default: - return IIO_VAL_INT_PLUS_MICRO; - } -} - -static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail); -static IIO_DEVICE_ATTR(in_accel_scale_available, 0444, - st_lsm6dsx_sysfs_scale_avail, NULL, 0); -static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444, - st_lsm6dsx_sysfs_scale_avail, NULL, 0); - -static struct attribute *st_lsm6dsx_acc_attributes[] = { - &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_accel_scale_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group st_lsm6dsx_acc_attribute_group = { - .attrs = st_lsm6dsx_acc_attributes, -}; - -static const struct iio_info st_lsm6dsx_acc_info = { - .attrs = &st_lsm6dsx_acc_attribute_group, - .read_raw = st_lsm6dsx_read_raw, - .write_raw = st_lsm6dsx_write_raw, - .read_event_value = st_lsm6dsx_read_event, - .write_event_value = st_lsm6dsx_write_event, - .read_event_config = st_lsm6dsx_read_event_config, - .write_event_config = st_lsm6dsx_write_event_config, - .hwfifo_set_watermark = st_lsm6dsx_set_watermark, - .write_raw_get_fmt = st_lsm6dsx_write_raw_get_fmt, -}; - -static struct attribute *st_lsm6dsx_gyro_attributes[] = { - &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group st_lsm6dsx_gyro_attribute_group = { - .attrs = st_lsm6dsx_gyro_attributes, -}; - -static const struct iio_info st_lsm6dsx_gyro_info = { - .attrs = &st_lsm6dsx_gyro_attribute_group, - .read_raw = st_lsm6dsx_read_raw, - .write_raw = st_lsm6dsx_write_raw, - .hwfifo_set_watermark = st_lsm6dsx_set_watermark, - .write_raw_get_fmt = st_lsm6dsx_write_raw_get_fmt, -}; - -static int -st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, - const struct st_lsm6dsx_reg **drdy_reg) -{ - struct device *dev = hw->dev; - const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); - int err = 0, drdy_pin; - - if (device_property_read_u32(dev, "st,drdy-int-pin", &drdy_pin) < 0) - drdy_pin = pdata ? pdata->drdy_int_pin : 1; - - switch (drdy_pin) { - case 1: - hw->irq_routing = &hw->settings->irq_config.irq1_func; - *drdy_reg = &hw->settings->irq_config.irq1; - break; - case 2: - hw->irq_routing = &hw->settings->irq_config.irq2_func; - *drdy_reg = &hw->settings->irq_config.irq2; - break; - default: - dev_err(hw->dev, "unsupported data ready pin\n"); - err = -EINVAL; - break; - } - - return err; -} - -static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - struct device *dev = hw->dev; - const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); - unsigned int data; - int err = 0; - - hub_settings = &hw->settings->shub_settings; - - if (device_property_read_bool(dev, "st,pullups") || - (pdata && pdata->pullups)) { - if (hub_settings->pullup_en.sec_page) { - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - return err; - } - - data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask); - err = regmap_update_bits(hw->regmap, - hub_settings->pullup_en.addr, - hub_settings->pullup_en.mask, data); - - if (hub_settings->pullup_en.sec_page) - st_lsm6dsx_set_page(hw, false); - - if (err < 0) - return err; - } - - if (hub_settings->aux_sens.addr) { - /* configure aux sensors */ - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - return err; - - data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask); - err = regmap_update_bits(hw->regmap, - hub_settings->aux_sens.addr, - hub_settings->aux_sens.mask, data); - - st_lsm6dsx_set_page(hw, false); - - if (err < 0) - return err; - } - - if (hub_settings->emb_func.addr) { - data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->emb_func.mask); - err = regmap_update_bits(hw->regmap, - hub_settings->emb_func.addr, - hub_settings->emb_func.mask, data); - } - - return err; -} - -static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_hw_ts_settings *ts_settings; - int err, val; - - ts_settings = &hw->settings->ts_settings; - /* enable hw timestamp generation if necessary */ - if (ts_settings->timer_en.addr) { - val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask); - err = regmap_update_bits(hw->regmap, - ts_settings->timer_en.addr, - ts_settings->timer_en.mask, val); - if (err < 0) - return err; - } - - /* enable high resolution for hw ts timer if necessary */ - if (ts_settings->hr_timer.addr) { - val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask); - err = regmap_update_bits(hw->regmap, - ts_settings->hr_timer.addr, - ts_settings->hr_timer.mask, val); - if (err < 0) - return err; - } - - /* enable ts queueing in FIFO if necessary */ - if (ts_settings->fifo_en.addr) { - val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask); - err = regmap_update_bits(hw->regmap, - ts_settings->fifo_en.addr, - ts_settings->fifo_en.mask, val); - if (err < 0) - return err; - } - - /* calibrate timestamp sensitivity */ - hw->ts_gain = ts_settings->ts_sensitivity; - if (ts_settings->freq_fine) { - err = regmap_read(hw->regmap, ts_settings->freq_fine, &val); - if (err < 0) - return err; - - hw->ts_gain -= ((s8)val * ts_settings->ts_trim_coeff) / 1000; - } - - return 0; -} - -static int st_lsm6dsx_reset_device(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_reg *reg; - int err; - - /* - * flush hw FIFO before device reset in order to avoid - * possible races on interrupt line 1. If the first interrupt - * line is asserted during hw reset the device will work in - * I3C-only mode (if it is supported) - */ - err = st_lsm6dsx_flush_fifo(hw); - if (err < 0 && err != -ENOTSUPP) - return err; - - /* device sw reset */ - reg = &hw->settings->reset; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - - msleep(50); - - /* reload trimming parameter */ - reg = &hw->settings->boot; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - - msleep(50); - - return 0; -} - -static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_reg *reg; - int err; - - err = st_lsm6dsx_reset_device(hw); - if (err < 0) - return err; - - /* enable Block Data Update */ - reg = &hw->settings->bdu; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - - /* enable FIFO watermak interrupt */ - err = st_lsm6dsx_get_drdy_reg(hw, ®); - if (err < 0) - return err; - - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - - /* enable Latched interrupts for device events */ - if (hw->settings->irq_config.lir.addr) { - reg = &hw->settings->irq_config.lir; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - - /* enable clear on read for latched interrupts */ - if (hw->settings->irq_config.clear_on_read.addr) { - reg = &hw->settings->irq_config.clear_on_read; - err = regmap_update_bits(hw->regmap, - reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - } - } - - /* enable drdy-mas if available */ - if (hw->settings->drdy_mask.addr) { - reg = &hw->settings->drdy_mask; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - } - - err = st_lsm6dsx_init_shub(hw); - if (err < 0) - return err; - - return st_lsm6dsx_init_hw_timer(hw); -} - -static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, - enum st_lsm6dsx_sensor_id id, - const char *name) -{ - struct st_lsm6dsx_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->available_scan_masks = st_lsm6dsx_available_scan_masks; - iio_dev->channels = hw->settings->channels[id].chan; - iio_dev->num_channels = hw->settings->channels[id].len; - - sensor = iio_priv(iio_dev); - sensor->id = id; - sensor->hw = hw; - sensor->odr = hw->settings->odr_table[id].odr_avl[0].milli_hz; - sensor->hwfifo_odr_mHz = sensor->odr; - sensor->gain = hw->settings->fs_table[id].fs_avl[0].gain; - sensor->watermark = 1; - - switch (id) { - case ST_LSM6DSX_ID_ACC: - iio_dev->info = &st_lsm6dsx_acc_info; - scnprintf(sensor->name, sizeof(sensor->name), "%s_accel", - name); - break; - case ST_LSM6DSX_ID_GYRO: - iio_dev->info = &st_lsm6dsx_gyro_info; - scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro", - name); - break; - default: - return NULL; - } - iio_dev->name = sensor->name; - - return iio_dev; -} - -static bool -st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_event_settings *event_settings; - int err, data; - s64 timestamp; - - if (!hw->enable_event) - return false; - - event_settings = &hw->settings->event_settings; - err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg, - &data, sizeof(data)); - if (err < 0) - return false; - - timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - if ((data & hw->settings->event_settings.wakeup_src_z_mask) && - (hw->enable_event & BIT(IIO_MOD_Z))) - iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], - IIO_MOD_EVENT_CODE(IIO_ACCEL, - 0, - IIO_MOD_Z, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - - if ((data & hw->settings->event_settings.wakeup_src_y_mask) && - (hw->enable_event & BIT(IIO_MOD_Y))) - iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], - IIO_MOD_EVENT_CODE(IIO_ACCEL, - 0, - IIO_MOD_Y, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - - if ((data & hw->settings->event_settings.wakeup_src_x_mask) && - (hw->enable_event & BIT(IIO_MOD_X))) - iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], - IIO_MOD_EVENT_CODE(IIO_ACCEL, - 0, - IIO_MOD_X, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - - return data & event_settings->wakeup_src_status_mask; -} - -static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) -{ - struct st_lsm6dsx_hw *hw = private; - int fifo_len = 0, len; - bool event; - - event = st_lsm6dsx_report_motion_event(hw); - - if (!hw->settings->fifo_ops.read_fifo) - return event ? IRQ_HANDLED : IRQ_NONE; - - /* - * If we are using edge IRQs, new samples can arrive while - * processing current interrupt since there are no hw - * guarantees the irq line stays "low" long enough to properly - * detect the new interrupt. In this case the new sample will - * be missed. - * Polling FIFO status register allow us to read new - * samples even if the interrupt arrives while processing - * previous data and the timeslot where the line is "low" is - * too short to be properly detected. - */ - do { - mutex_lock(&hw->fifo_lock); - len = hw->settings->fifo_ops.read_fifo(hw); - mutex_unlock(&hw->fifo_lock); - - if (len > 0) - fifo_len += len; - } while (len > 0); - - return fifo_len || event ? IRQ_HANDLED : IRQ_NONE; -} - -static irqreturn_t st_lsm6dsx_sw_trigger_handler_thread(int irq, - void *private) -{ - struct iio_poll_func *pf = private; - struct iio_dev *iio_dev = pf->indio_dev; - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - struct st_lsm6dsx_hw *hw = sensor->hw; - - if (sensor->id == ST_LSM6DSX_ID_EXT0 || - sensor->id == ST_LSM6DSX_ID_EXT1 || - sensor->id == ST_LSM6DSX_ID_EXT2) - st_lsm6dsx_shub_read_output(hw, - (u8 *)hw->scan[sensor->id].channels, - sizeof(hw->scan[sensor->id].channels)); - else - st_lsm6dsx_read_locked(hw, iio_dev->channels[0].address, - hw->scan[sensor->id].channels, - sizeof(hw->scan[sensor->id].channels)); - - iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan[sensor->id], - iio_get_time_ns(iio_dev)); - iio_trigger_notify_done(iio_dev->trig); - - return IRQ_HANDLED; -} - -static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) -{ - const struct st_lsm6dsx_reg *reg; - struct device *dev = hw->dev; - const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); - unsigned long irq_type; - bool irq_active_low; - int err; - - irq_type = irq_get_trigger_type(hw->irq); - switch (irq_type) { - case IRQF_TRIGGER_HIGH: - case IRQF_TRIGGER_RISING: - irq_active_low = false; - break; - case IRQF_TRIGGER_LOW: - case IRQF_TRIGGER_FALLING: - irq_active_low = true; - break; - default: - dev_info(hw->dev, "mode %lx unsupported\n", irq_type); - return -EINVAL; - } - - reg = &hw->settings->irq_config.hla; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(irq_active_low, - reg->mask)); - if (err < 0) - return err; - - if (device_property_read_bool(dev, "drive-open-drain") || - (pdata && pdata->open_drain)) { - reg = &hw->settings->irq_config.od; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); - if (err < 0) - return err; - - irq_type |= IRQF_SHARED; - } - - err = devm_request_threaded_irq(hw->dev, hw->irq, - NULL, - st_lsm6dsx_handler_thread, - irq_type | IRQF_ONESHOT, - "lsm6dsx", hw); - if (err) { - dev_err(hw->dev, "failed to request trigger irq %d\n", - hw->irq); - return err; - } - - return 0; -} - -static int st_lsm6dsx_sw_buffer_preenable(struct iio_dev *iio_dev) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - - return st_lsm6dsx_device_set_enable(sensor, true); -} - -static int st_lsm6dsx_sw_buffer_postdisable(struct iio_dev *iio_dev) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - - return st_lsm6dsx_device_set_enable(sensor, false); -} - -static const struct iio_buffer_setup_ops st_lsm6dsx_sw_buffer_ops = { - .preenable = st_lsm6dsx_sw_buffer_preenable, - .postdisable = st_lsm6dsx_sw_buffer_postdisable, -}; - -static int st_lsm6dsx_sw_buffers_setup(struct st_lsm6dsx_hw *hw) -{ - int i; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - int err; - - if (!hw->iio_devs[i]) - continue; - - err = devm_iio_triggered_buffer_setup(hw->dev, - hw->iio_devs[i], NULL, - st_lsm6dsx_sw_trigger_handler_thread, - &st_lsm6dsx_sw_buffer_ops); - if (err) - return err; - } - - return 0; -} - -static int st_lsm6dsx_init_regulators(struct device *dev) -{ - /* vdd-vddio power regulators */ - static const char * const regulators[] = { "vdd", "vddio" }; - int err; - - err = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), - regulators); - if (err) - return dev_err_probe(dev, err, "failed to enable regulators\n"); - - msleep(50); - - return 0; -} - -int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, - struct regmap *regmap) -{ - const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); - const struct st_lsm6dsx_shub_settings *hub_settings; - struct st_lsm6dsx_hw *hw; - const char *name = NULL; - int i, err; - - hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); - if (!hw) - return -ENOMEM; - - dev_set_drvdata(dev, hw); - - mutex_init(&hw->fifo_lock); - mutex_init(&hw->conf_lock); - mutex_init(&hw->page_lock); - - err = st_lsm6dsx_init_regulators(dev); - if (err) - return err; - - hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL); - if (!hw->buff) - return -ENOMEM; - - hw->dev = dev; - hw->irq = irq; - hw->regmap = regmap; - - err = st_lsm6dsx_check_whoami(hw, hw_id, &name); - if (err < 0) - return err; - - for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) { - hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name); - if (!hw->iio_devs[i]) - return -ENOMEM; - } - - err = st_lsm6dsx_init_device(hw); - if (err < 0) - return err; - - hub_settings = &hw->settings->shub_settings; - if (hub_settings->master_en.addr && - !device_property_read_bool(dev, "st,disable-sensor-hub")) { - err = st_lsm6dsx_shub_probe(hw, name); - if (err < 0) - return err; - } - - if (hw->irq > 0) { - err = st_lsm6dsx_irq_setup(hw); - if (err < 0) - return err; - - err = st_lsm6dsx_fifo_setup(hw); - if (err < 0) - return err; - } - - if (!hw->irq || !hw->settings->fifo_ops.read_fifo) { - /* - * Rely on sw triggers (e.g. hr-timers) if irq pin is not - * connected of if the device does not support HW FIFO - */ - err = st_lsm6dsx_sw_buffers_setup(hw); - if (err) - return err; - } - - if (!iio_read_acpi_mount_matrix(hw->dev, &hw->orientation, "ROTM")) { - err = iio_read_mount_matrix(hw->dev, &hw->orientation); - if (err) - return err; - } - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - err = devm_iio_device_register(hw->dev, hw->iio_devs[i]); - if (err) - return err; - } - - if (device_property_read_bool(dev, "wakeup-source") || - (pdata && pdata->wakeup_source)) { - err = devm_device_init_wakeup(dev); - if (err) - return dev_err_probe(dev, err, "Failed to init wakeup\n"); - } - - return 0; -} -EXPORT_SYMBOL_NS(st_lsm6dsx_probe, "IIO_LSM6DSX"); - -static int st_lsm6dsx_suspend(struct device *dev) -{ - struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev); - struct st_lsm6dsx_sensor *sensor; - int i, err = 0; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - sensor = iio_priv(hw->iio_devs[i]); - if (!(hw->enable_mask & BIT(sensor->id))) - continue; - - if (device_may_wakeup(dev) && - sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) { - /* Enable wake from IRQ */ - enable_irq_wake(hw->irq); - continue; - } - - err = st_lsm6dsx_device_set_enable(sensor, false); - if (err < 0) - return err; - - hw->suspend_mask |= BIT(sensor->id); - } - - if (hw->fifo_mask) - err = st_lsm6dsx_flush_fifo(hw); - - return err; -} - -static int st_lsm6dsx_resume(struct device *dev) -{ - struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev); - struct st_lsm6dsx_sensor *sensor; - int i, err = 0; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - if (!hw->iio_devs[i]) - continue; - - sensor = iio_priv(hw->iio_devs[i]); - if (device_may_wakeup(dev) && - sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) - disable_irq_wake(hw->irq); - - if (!(hw->suspend_mask & BIT(sensor->id))) - continue; - - err = st_lsm6dsx_device_set_enable(sensor, true); - if (err < 0) - return err; - - hw->suspend_mask &= ~BIT(sensor->id); - } - - if (hw->fifo_mask) - err = st_lsm6dsx_resume_fifo(hw); - - return err; -} - -EXPORT_NS_SIMPLE_DEV_PM_OPS(st_lsm6dsx_pm_ops, st_lsm6dsx_suspend, - st_lsm6dsx_resume, IIO_LSM6DSX); - -MODULE_AUTHOR("Lorenzo Bianconi "); -MODULE_AUTHOR("Denis Ciocca "); -MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver"); -MODULE_LICENSE("GPL v2"); diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_i2c.c b/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_i2c.c deleted file mode 100644 index b2a7c2e..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_i2c.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * STMicroelectronics st_lsm6dsx i2c driver - * - * Copyright 2016 STMicroelectronics Inc. - * - * Lorenzo Bianconi - * Denis Ciocca - */ - -#include -#include -#include -#include -#include - -#include "st_lsm6dsx.h" - -static const struct regmap_config st_lsm6dsx_i2c_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - -static int st_lsm6dsx_i2c_probe(struct i2c_client *client) -{ - int hw_id; - struct regmap *regmap; - - hw_id = (kernel_ulong_t)device_get_match_data(&client->dev); - if (!hw_id) - hw_id = i2c_client_get_device_id(client)->driver_data; - if (!hw_id) - return -EINVAL; - - regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config); - if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); - return PTR_ERR(regmap); - } - - return st_lsm6dsx_probe(&client->dev, client->irq, hw_id, regmap); -} - -static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { - { - .compatible = "st,lsm6ds3", - .data = (void *)ST_LSM6DS3_ID, - }, - { - .compatible = "st,lsm6ds3h", - .data = (void *)ST_LSM6DS3H_ID, - }, - { - .compatible = "st,lsm6dsl", - .data = (void *)ST_LSM6DSL_ID, - }, - { - .compatible = "st,lsm6dsm", - .data = (void *)ST_LSM6DSM_ID, - }, - { - .compatible = "st,ism330dlc", - .data = (void *)ST_ISM330DLC_ID, - }, - { - .compatible = "st,lsm6dso", - .data = (void *)ST_LSM6DSO_ID, - }, - { - .compatible = "st,asm330lhh", - .data = (void *)ST_ASM330LHH_ID, - }, - { - .compatible = "st,lsm6dsox", - .data = (void *)ST_LSM6DSOX_ID, - }, - { - .compatible = "st,lsm6dsr", - .data = (void *)ST_LSM6DSR_ID, - }, - { - .compatible = "st,lsm6ds3tr-c", - .data = (void *)ST_LSM6DS3TRC_ID, - }, - { - .compatible = "st,ism330dhcx", - .data = (void *)ST_ISM330DHCX_ID, - }, - { - .compatible = "st,lsm9ds1-imu", - .data = (void *)ST_LSM9DS1_ID, - }, - { - .compatible = "st,lsm6ds0", - .data = (void *)ST_LSM6DS0_ID, - }, - { - .compatible = "st,lsm6dsrx", - .data = (void *)ST_LSM6DSRX_ID, - }, - { - .compatible = "st,lsm6dst", - .data = (void *)ST_LSM6DST_ID, - }, - { - .compatible = "st,lsm6dsop", - .data = (void *)ST_LSM6DSOP_ID, - }, - { - .compatible = "st,asm330lhhx", - .data = (void *)ST_ASM330LHHX_ID, - }, - { - .compatible = "st,lsm6dstx", - .data = (void *)ST_LSM6DSTX_ID, - }, - { - .compatible = "st,lsm6dsv", - .data = (void *)ST_LSM6DSV_ID, - }, - { - .compatible = "st,lsm6dsv16x", - .data = (void *)ST_LSM6DSV16X_ID, - }, - { - .compatible = "st,lsm6dso16is", - .data = (void *)ST_LSM6DSO16IS_ID, - }, - { - .compatible = "st,ism330is", - .data = (void *)ST_ISM330IS_ID, - }, - { - .compatible = "st,asm330lhb", - .data = (void *)ST_ASM330LHB_ID, - }, - { - .compatible = "st,asm330lhhxg1", - .data = (void *)ST_ASM330LHHXG1_ID, - }, - { } -}; -MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); - -static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { - { "SMO8B30", ST_LSM6DS3TRC_ID, }, - { "SMOCF00", ST_LSM6DSO_ID, }, - { } -}; -MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); - -static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { - { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, - { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, - { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, - { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, - { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, - { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, - { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID }, - { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID }, - { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID }, - { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, - { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, - { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, - { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, - { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, - { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, - { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, - { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, - { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, - { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID }, - { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, - { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, - { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, - { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, - { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, - { } -}; -MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); - -static struct i2c_driver st_lsm6dsx_driver = { - .driver = { - .name = "st_lsm6dsx_i2c", - .pm = pm_sleep_ptr(&st_lsm6dsx_pm_ops), - .of_match_table = st_lsm6dsx_i2c_of_match, - .acpi_match_table = st_lsm6dsx_i2c_acpi_match, - }, - .probe = st_lsm6dsx_i2c_probe, - .id_table = st_lsm6dsx_i2c_id_table, -}; -module_i2c_driver(st_lsm6dsx_driver); - -MODULE_AUTHOR("Lorenzo Bianconi "); -MODULE_AUTHOR("Denis Ciocca "); -MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver"); -MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS("IIO_LSM6DSX"); diff --git a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_shub.c b/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_shub.c deleted file mode 100644 index d6a1eeb..0000000 --- a/usr/src/st-lsm6dsx-shift13mi-0.1/st_lsm6dsx_shub.c +++ /dev/null @@ -1,922 +0,0 @@ -/* - * STMicroelectronics st_lsm6dsx i2c controller driver - * - * i2c controller embedded in lsm6dx series can connect up to four - * slave devices using accelerometer sensor as trigger for i2c - * read/write operations. Current implementation relies on SLV0 channel - * for slave configuration and SLV{1,2,3} to read data and push them into - * the hw FIFO - * - * Copyright (C) 2018 Lorenzo Bianconi - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ -#include -#include -#include -#include -#include - -#include "st_lsm6dsx.h" - -#define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3) -#define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3) -#define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3) - -#define ST_LS6DSX_READ_OP_MASK GENMASK(2, 0) - -static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { - /* LIS2MDL */ - { - .i2c_addr = { 0x1e }, - .wai = { - .addr = 0x4f, - .val = 0x40, - }, - .id = ST_LSM6DSX_ID_MAGN, - .odr_table = { - .reg = { - .addr = 0x60, - .mask = GENMASK(3, 2), - }, - .odr_avl[0] = { 10000, 0x0 }, - .odr_avl[1] = { 20000, 0x1 }, - .odr_avl[2] = { 50000, 0x2 }, - .odr_avl[3] = { 100000, 0x3 }, - .odr_len = 4, - }, - .fs_table = { - .fs_avl[0] = { - .gain = 1500, - .val = 0x0, - }, /* 1500 uG/LSB */ - .fs_len = 1, - }, - .temp_comp = { - .addr = 0x60, - .mask = BIT(7), - }, - .pwr_table = { - .reg = { - .addr = 0x60, - .mask = GENMASK(1, 0), - }, - .off_val = 0x2, - .on_val = 0x0, - }, - .off_canc = { - .addr = 0x61, - .mask = BIT(1), - }, - .bdu = { - .addr = 0x62, - .mask = BIT(4), - }, - .out = { - .addr = 0x68, - .len = 6, - }, - }, - /* LIS3MDL */ - { - .i2c_addr = { 0x1e }, - .wai = { - .addr = 0x0f, - .val = 0x3d, - }, - .id = ST_LSM6DSX_ID_MAGN, - .odr_table = { - .reg = { - .addr = 0x20, - .mask = GENMASK(4, 2), - }, - .odr_avl[0] = { 1000, 0x0 }, - .odr_avl[1] = { 2000, 0x1 }, - .odr_avl[2] = { 3000, 0x2 }, - .odr_avl[3] = { 5000, 0x3 }, - .odr_avl[4] = { 10000, 0x4 }, - .odr_avl[5] = { 20000, 0x5 }, - .odr_avl[6] = { 40000, 0x6 }, - .odr_avl[7] = { 80000, 0x7 }, - .odr_len = 8, - }, - .fs_table = { - .reg = { - .addr = 0x21, - .mask = GENMASK(6, 5), - }, - .fs_avl[0] = { - .gain = 146, - .val = 0x00, - }, /* 4000 uG/LSB */ - .fs_avl[1] = { - .gain = 292, - .val = 0x01, - }, /* 8000 uG/LSB */ - .fs_avl[2] = { - .gain = 438, - .val = 0x02, - }, /* 12000 uG/LSB */ - .fs_avl[3] = { - .gain = 584, - .val = 0x03, - }, /* 16000 uG/LSB */ - .fs_len = 4, - }, - .pwr_table = { - .reg = { - .addr = 0x22, - .mask = GENMASK(1, 0), - }, - .off_val = 0x2, - .on_val = 0x0, - }, - .bdu = { - .addr = 0x24, - .mask = BIT(6), - }, - .out = { - .addr = 0x28, - .len = 6, - }, - }, -}; - -static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) -{ - struct st_lsm6dsx_sensor *sensor; - u32 odr, timeout; - - sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - odr = (hw->enable_mask & BIT(ST_LSM6DSX_ID_ACC)) ? sensor->odr : 12500; - /* set 10ms as minimum timeout for i2c slave configuration */ - timeout = max_t(u32, 2000000U / odr + 1, 10); - msleep(timeout); -} - -/* - * st_lsm6dsx_shub_read_output - read i2c controller register - * - * Read st_lsm6dsx i2c controller register - */ -int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - int err; - - mutex_lock(&hw->page_lock); - - hub_settings = &hw->settings->shub_settings; - if (hub_settings->shub_out.sec_page) { - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; - } - - err = regmap_bulk_read(hw->regmap, hub_settings->shub_out.addr, - data, len); - - if (hub_settings->shub_out.sec_page) - st_lsm6dsx_set_page(hw, false); -out: - mutex_unlock(&hw->page_lock); - - return err; -} - -/* - * st_lsm6dsx_shub_write_reg - write i2c controller register - * - * Write st_lsm6dsx i2c controller register - */ -static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr, - u8 *data, int len) -{ - int err; - - mutex_lock(&hw->page_lock); - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; - - err = regmap_bulk_write(hw->regmap, addr, data, len); - - st_lsm6dsx_set_page(hw, false); -out: - mutex_unlock(&hw->page_lock); - - return err; -} - -static int -st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, - u8 mask, u8 val) -{ - int err; - - mutex_lock(&hw->page_lock); - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; - - err = regmap_update_bits(hw->regmap, addr, mask, val); - - st_lsm6dsx_set_page(hw, false); -out: - mutex_unlock(&hw->page_lock); - - return err; -} - -static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor, - bool enable) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - struct st_lsm6dsx_hw *hw = sensor->hw; - unsigned int data; - int err; - - /* enable acc sensor as trigger */ - err = st_lsm6dsx_sensor_set_enable(sensor, enable); - if (err < 0) - return err; - - mutex_lock(&hw->page_lock); - - hub_settings = &hw->settings->shub_settings; - if (hub_settings->master_en.sec_page) { - err = st_lsm6dsx_set_page(hw, true); - if (err < 0) - goto out; - } - - data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask); - err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr, - hub_settings->master_en.mask, data); - - if (hub_settings->master_en.sec_page) - st_lsm6dsx_set_page(hw, false); -out: - mutex_unlock(&hw->page_lock); - - return err; -} - -/* - * st_lsm6dsx_shub_read - read data from slave device register - * - * Read data from slave device register. SLV0 is used for - * one-shot read operation - */ -static int -st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, - u8 *data, int len) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - u8 config[3], slv_addr, slv_config = 0; - struct st_lsm6dsx_hw *hw = sensor->hw; - const struct st_lsm6dsx_reg *aux_sens; - int err; - - hub_settings = &hw->settings->shub_settings; - slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); - aux_sens = &hw->settings->shub_settings.aux_sens; - /* do not overwrite aux_sens */ - if (slv_addr + 2 == aux_sens->addr) - slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); - - config[0] = (sensor->ext_info.addr << 1) | 1; - config[1] = addr; - config[2] = (len & ST_LS6DSX_READ_OP_MASK) | slv_config; - - err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, - sizeof(config)); - if (err < 0) - return err; - - err = st_lsm6dsx_shub_master_enable(sensor, true); - if (err < 0) - return err; - - st_lsm6dsx_shub_wait_complete(hw); - - err = st_lsm6dsx_shub_read_output(hw, data, - len & ST_LS6DSX_READ_OP_MASK); - if (err < 0) - return err; - - st_lsm6dsx_shub_master_enable(sensor, false); - - config[0] = hub_settings->pause; - config[1] = 0; - config[2] = slv_config; - return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, - sizeof(config)); -} - -/* - * st_lsm6dsx_shub_write - write data to slave device register - * - * Write data from slave device register. SLV0 is used for - * one-shot write operation - */ -static int -st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr, - u8 *data, int len) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - struct st_lsm6dsx_hw *hw = sensor->hw; - u8 config[2], slv_addr; - int err, i; - - hub_settings = &hw->settings->shub_settings; - if (hub_settings->wr_once.addr) { - unsigned int data; - - data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask); - err = st_lsm6dsx_shub_write_reg_with_mask(hw, - hub_settings->wr_once.addr, - hub_settings->wr_once.mask, - data); - if (err < 0) - return err; - } - - slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); - config[0] = sensor->ext_info.addr << 1; - for (i = 0 ; i < len; i++) { - config[1] = addr + i; - - err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, - sizeof(config)); - if (err < 0) - return err; - - err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr, - &data[i], 1); - if (err < 0) - return err; - - err = st_lsm6dsx_shub_master_enable(sensor, true); - if (err < 0) - return err; - - st_lsm6dsx_shub_wait_complete(hw); - - st_lsm6dsx_shub_master_enable(sensor, false); - } - - config[0] = hub_settings->pause; - config[1] = 0; - return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); -} - -static int -st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor, - u8 addr, u8 mask, u8 val) -{ - int err; - u8 data; - - err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data)); - if (err < 0) - return err; - - data = ((data & ~mask) | (val << __ffs(mask) & mask)); - - return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data)); -} - -static int -st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor, - u32 odr, u16 *val) -{ - const struct st_lsm6dsx_ext_dev_settings *settings; - int i; - - settings = sensor->ext_info.settings; - for (i = 0; i < settings->odr_table.odr_len; i++) { - if (settings->odr_table.odr_avl[i].milli_hz == odr) - break; - } - - if (i == settings->odr_table.odr_len) - return -EINVAL; - - *val = settings->odr_table.odr_avl[i].val; - return 0; -} - -static int -st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u32 odr) -{ - const struct st_lsm6dsx_ext_dev_settings *settings; - u16 val; - int err; - - err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val); - if (err < 0) - return err; - - settings = sensor->ext_info.settings; - return st_lsm6dsx_shub_write_with_mask(sensor, - settings->odr_table.reg.addr, - settings->odr_table.reg.mask, - val); -} - -/* use SLV{1,2,3} for FIFO read operations */ -static int -st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor, - bool enable) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - const struct st_lsm6dsx_ext_dev_settings *settings; - u8 config[9] = {}, enable_mask, slv_addr; - struct st_lsm6dsx_hw *hw = sensor->hw; - struct st_lsm6dsx_sensor *cur_sensor; - int i, j = 0; - - hub_settings = &hw->settings->shub_settings; - if (enable) - enable_mask = hw->enable_mask | BIT(sensor->id); - else - enable_mask = hw->enable_mask & ~BIT(sensor->id); - - for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) { - if (!hw->iio_devs[i]) - continue; - - cur_sensor = iio_priv(hw->iio_devs[i]); - if (!(enable_mask & BIT(cur_sensor->id))) - continue; - - settings = cur_sensor->ext_info.settings; - config[j] = (sensor->ext_info.addr << 1) | 1; - config[j + 1] = settings->out.addr; - config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) | - hub_settings->batch_en; - j += 3; - } - - slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr); - return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, - sizeof(config)); -} - -int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) -{ - const struct st_lsm6dsx_ext_dev_settings *settings; - int err; - - err = st_lsm6dsx_shub_config_channels(sensor, enable); - if (err < 0) - return err; - - settings = sensor->ext_info.settings; - if (enable) { - err = st_lsm6dsx_shub_set_odr(sensor, - sensor->ext_info.slv_odr); - if (err < 0) - return err; - } else { - err = st_lsm6dsx_shub_write_with_mask(sensor, - settings->odr_table.reg.addr, - settings->odr_table.reg.mask, 0); - if (err < 0) - return err; - } - - if (settings->pwr_table.reg.addr) { - u8 val; - - val = enable ? settings->pwr_table.on_val - : settings->pwr_table.off_val; - err = st_lsm6dsx_shub_write_with_mask(sensor, - settings->pwr_table.reg.addr, - settings->pwr_table.reg.mask, val); - if (err < 0) - return err; - } - - return st_lsm6dsx_shub_master_enable(sensor, enable); -} - -static int -st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor, - struct iio_chan_spec const *ch, - int *val) -{ - int err, delay, len; - u8 data[4]; - - err = st_lsm6dsx_shub_set_enable(sensor, true); - if (err < 0) - return err; - - delay = 1000000000 / sensor->ext_info.slv_odr; - usleep_range(delay, 2 * delay); - - len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3); - err = st_lsm6dsx_shub_read(sensor, ch->address, data, len); - if (err < 0) - return err; - - err = st_lsm6dsx_shub_set_enable(sensor, false); - if (err < 0) - return err; - - switch (len) { - case 2: - *val = (s16)le16_to_cpu(*((__le16 *)data)); - break; - default: - return -EINVAL; - } - - return IIO_VAL_INT; -} - -static int -st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *ch, - int *val, int *val2, long mask) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (!iio_device_claim_direct(iio_dev)) - return -EBUSY; - - ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val); - iio_device_release_direct(iio_dev); - break; - case IIO_CHAN_INFO_SAMP_FREQ: - *val = sensor->ext_info.slv_odr / 1000; - *val2 = (sensor->ext_info.slv_odr % 1000) * 1000; - ret = IIO_VAL_INT_PLUS_MICRO; - break; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = sensor->gain; - ret = IIO_VAL_INT_PLUS_MICRO; - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int -st_lsm6dsx_shub_set_full_scale(struct st_lsm6dsx_sensor *sensor, - u32 gain) -{ - const struct st_lsm6dsx_fs_table_entry *fs_table; - int i, err; - - fs_table = &sensor->ext_info.settings->fs_table; - if (!fs_table->reg.addr) - return -ENOTSUPP; - - for (i = 0; i < fs_table->fs_len; i++) { - if (fs_table->fs_avl[i].gain == gain) - break; - } - - if (i == fs_table->fs_len) - return -EINVAL; - - err = st_lsm6dsx_shub_write_with_mask(sensor, fs_table->reg.addr, - fs_table->reg.mask, - fs_table->fs_avl[i].val); - if (err < 0) - return err; - - sensor->gain = gain; - - return 0; -} - -static int -__st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int err; - - switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: { - struct st_lsm6dsx_hw *hw = sensor->hw; - struct st_lsm6dsx_sensor *ref_sensor; - u8 odr_val; - u16 data; - int odr; - - val = val * 1000 + val2 / 1000; - err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data); - if (err) - return err; - - ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - odr = st_lsm6dsx_check_odr(ref_sensor, val, &odr_val); - if (odr < 0) - return odr; - - sensor->ext_info.slv_odr = val; - sensor->odr = odr; - sensor->hwfifo_odr_mHz = odr; - return 0; - } - case IIO_CHAN_INFO_SCALE: - return st_lsm6dsx_shub_set_full_scale(sensor, val2); - default: - return -EINVAL; - } -} - -static int -st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - int ret; - - if (!iio_device_claim_direct(iio_dev)) - return -EBUSY; - - ret = __st_lsm6dsx_shub_write_raw(iio_dev, chan, val, val2, mask); - - iio_device_release_direct(iio_dev); - - return ret; -} - -static ssize_t -st_lsm6dsx_shub_sampling_freq_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); - const struct st_lsm6dsx_ext_dev_settings *settings; - int i, len = 0; - - settings = sensor->ext_info.settings; - for (i = 0; i < settings->odr_table.odr_len; i++) { - u32 val = settings->odr_table.odr_avl[i].milli_hz; - - len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ", - val / 1000, val % 1000); - } - buf[len - 1] = '\n'; - - return len; -} - -static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); - const struct st_lsm6dsx_ext_dev_settings *settings; - int i, len = 0; - - settings = sensor->ext_info.settings; - for (i = 0; i < settings->fs_table.fs_len; i++) - len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", - settings->fs_table.fs_avl[i].gain); - buf[len - 1] = '\n'; - - return len; -} - -static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail); -static IIO_DEVICE_ATTR(in_scale_available, 0444, - st_lsm6dsx_shub_scale_avail, NULL, 0); -static struct attribute *st_lsm6dsx_shub_attributes[] = { - &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_scale_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group st_lsm6dsx_shub_attribute_group = { - .attrs = st_lsm6dsx_shub_attributes, -}; - -static const struct iio_info st_lsm6dsx_shub_info = { - .attrs = &st_lsm6dsx_shub_attribute_group, - .read_raw = st_lsm6dsx_shub_read_raw, - .write_raw = st_lsm6dsx_shub_write_raw, - .hwfifo_set_watermark = st_lsm6dsx_set_watermark, -}; - -static struct iio_dev * -st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, - enum st_lsm6dsx_sensor_id id, - const struct st_lsm6dsx_ext_dev_settings *info, - u8 i2c_addr, const char *name) -{ - enum st_lsm6dsx_sensor_id ref_id = ST_LSM6DSX_ID_ACC; - struct iio_chan_spec *ext_channels; - struct st_lsm6dsx_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->info = &st_lsm6dsx_shub_info; - - sensor = iio_priv(iio_dev); - sensor->id = id; - sensor->hw = hw; - sensor->odr = hw->settings->odr_table[ref_id].odr_avl[0].milli_hz; - sensor->hwfifo_odr_mHz = sensor->odr; - sensor->ext_info.slv_odr = info->odr_table.odr_avl[0].milli_hz; - sensor->gain = info->fs_table.fs_avl[0].gain; - sensor->ext_info.settings = info; - sensor->ext_info.addr = i2c_addr; - sensor->watermark = 1; - - switch (info->id) { - case ST_LSM6DSX_ID_MAGN: { - const struct iio_chan_spec magn_channels[] = { - ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr, - IIO_MOD_X, 0), - ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2, - IIO_MOD_Y, 1), - ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4, - IIO_MOD_Z, 2), - IIO_CHAN_SOFT_TIMESTAMP(3), - }; - - ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels), - GFP_KERNEL); - if (!ext_channels) - return NULL; - - memcpy(ext_channels, magn_channels, sizeof(magn_channels)); - iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks; - iio_dev->channels = ext_channels; - iio_dev->num_channels = ARRAY_SIZE(magn_channels); - - scnprintf(sensor->name, sizeof(sensor->name), "%s_magn", - name); - break; - } - default: - return NULL; - } - iio_dev->name = sensor->name; - - return iio_dev; -} - -static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor) -{ - const struct st_lsm6dsx_ext_dev_settings *settings; - int err; - - settings = sensor->ext_info.settings; - if (settings->bdu.addr) { - err = st_lsm6dsx_shub_write_with_mask(sensor, - settings->bdu.addr, - settings->bdu.mask, 1); - if (err < 0) - return err; - } - - if (settings->temp_comp.addr) { - err = st_lsm6dsx_shub_write_with_mask(sensor, - settings->temp_comp.addr, - settings->temp_comp.mask, 1); - if (err < 0) - return err; - } - - if (settings->off_canc.addr) { - err = st_lsm6dsx_shub_write_with_mask(sensor, - settings->off_canc.addr, - settings->off_canc.mask, 1); - if (err < 0) - return err; - } - - return 0; -} - -static int -st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, - const struct st_lsm6dsx_ext_dev_settings *settings) -{ - const struct st_lsm6dsx_shub_settings *hub_settings; - u8 config[3], data, slv_addr, slv_config = 0; - const struct st_lsm6dsx_reg *aux_sens; - struct st_lsm6dsx_sensor *sensor; - bool found = false; - int i, err; - - sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - hub_settings = &hw->settings->shub_settings; - aux_sens = &hw->settings->shub_settings.aux_sens; - slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); - /* do not overwrite aux_sens */ - if (slv_addr + 2 == aux_sens->addr) - slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); - - for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) { - if (!settings->i2c_addr[i]) - continue; - - /* read wai slave register */ - config[0] = (settings->i2c_addr[i] << 1) | 0x1; - config[1] = settings->wai.addr; - config[2] = 0x1 | slv_config; - - err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, - sizeof(config)); - if (err < 0) - return err; - - err = st_lsm6dsx_shub_master_enable(sensor, true); - if (err < 0) - return err; - - st_lsm6dsx_shub_wait_complete(hw); - - err = st_lsm6dsx_shub_read_output(hw, &data, sizeof(data)); - - st_lsm6dsx_shub_master_enable(sensor, false); - - if (err < 0) - return err; - - if (data != settings->wai.val) - continue; - - *i2c_addr = settings->i2c_addr[i]; - found = true; - break; - } - - /* reset SLV0 channel */ - config[0] = hub_settings->pause; - config[1] = 0; - config[2] = slv_config; - err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, - sizeof(config)); - if (err < 0) - return err; - - return found ? 0 : -ENODEV; -} - -int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name) -{ - enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0; - struct st_lsm6dsx_sensor *sensor; - int err, i, num_ext_dev = 0; - u8 i2c_addr = 0; - - for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table); i++) { - err = st_lsm6dsx_shub_check_wai(hw, &i2c_addr, - &st_lsm6dsx_ext_dev_table[i]); - if (err == -ENODEV) - continue; - else if (err < 0) - return err; - - hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw, id, - &st_lsm6dsx_ext_dev_table[i], - i2c_addr, name); - if (!hw->iio_devs[id]) - return -ENOMEM; - - sensor = iio_priv(hw->iio_devs[id]); - err = st_lsm6dsx_shub_init_device(sensor); - if (err < 0) - return err; - - if (++num_ext_dev >= hw->settings->shub_settings.num_ext_dev) - break; - id++; - } - - return 0; -} diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/Makefile b/usr/src/st-lsm6dsx-shift13mi-1.1/Makefile new file mode 100644 index 0000000..787d987 --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/Makefile @@ -0,0 +1,2 @@ +obj-m += st_lsm6dsx.o st_lsm6dsx_i2c.o +st_lsm6dsx-objs := st_lsm6dsx_core.o st_lsm6dsx_buffer.o st_lsm6dsx_shub.o diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/dkms.conf b/usr/src/st-lsm6dsx-shift13mi-1.1/dkms.conf new file mode 100644 index 0000000..caabebd --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/dkms.conf @@ -0,0 +1,14 @@ +PACKAGE_NAME="st-lsm6dsx-shift13mi" +PACKAGE_VERSION="1.1" + +BUILT_MODULE_NAME[0]="st_lsm6dsx_i2c" + +BUILT_MODULE_LOCATION[0]="" + +DEST_MODULE_LOCATION[0]="/kernel/drivers/iio/imu/st_lsm6dsx/" + +AUTOINSTALL="yes" + +MAKE="make -C /usr/lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build" +CLEAN="make -C /usr/lib/modules/${kernelver}/build M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean" + diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx.h b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx.h new file mode 100644 index 0000000..6405a53 --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx.h @@ -0,0 +1,572 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * STMicroelectronics st_lsm6dsx sensor driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + */ + +#ifndef ST_LSM6DSX_H +#define ST_LSM6DSX_H + +#include +#include +#include + +#define ST_LSM6DS3_DEV_NAME "lsm6ds3" +#define ST_LSM6DS3H_DEV_NAME "lsm6ds3h" +#define ST_LSM6DSL_DEV_NAME "lsm6dsl" +#define ST_LSM6DSM_DEV_NAME "lsm6dsm" +#define ST_ISM330DLC_DEV_NAME "ism330dlc" +#define ST_LSM6DSO_DEV_NAME "lsm6dso" +#define ST_ASM330LHH_DEV_NAME "asm330lhh" +#define ST_LSM6DSOX_DEV_NAME "lsm6dsox" +#define ST_LSM6DSR_DEV_NAME "lsm6dsr" +#define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c" +#define ST_ISM330DHCX_DEV_NAME "ism330dhcx" +#define ST_LSM9DS1_DEV_NAME "lsm9ds1-imu" +#define ST_LSM6DS0_DEV_NAME "lsm6ds0" +#define ST_LSM6DSRX_DEV_NAME "lsm6dsrx" +#define ST_LSM6DST_DEV_NAME "lsm6dst" +#define ST_LSM6DSOP_DEV_NAME "lsm6dsop" +#define ST_ASM330LHHX_DEV_NAME "asm330lhhx" +#define ST_LSM6DSTX_DEV_NAME "lsm6dstx" +#define ST_LSM6DSV_DEV_NAME "lsm6dsv" +#define ST_LSM6DSV16X_DEV_NAME "lsm6dsv16x" +#define ST_LSM6DSO16IS_DEV_NAME "lsm6dso16is" +#define ST_ISM330IS_DEV_NAME "ism330is" +#define ST_ASM330LHB_DEV_NAME "asm330lhb" +#define ST_ASM330LHHXG1_DEV_NAME "asm330lhhxg1" + +enum st_lsm6dsx_hw_id { + ST_LSM6DS3_ID = 1, + ST_LSM6DS3H_ID, + ST_LSM6DSL_ID, + ST_LSM6DSM_ID, + ST_ISM330DLC_ID, + ST_LSM6DSO_ID, + ST_ASM330LHH_ID, + ST_LSM6DSOX_ID, + ST_LSM6DSR_ID, + ST_LSM6DS3TRC_ID, + ST_ISM330DHCX_ID, + ST_LSM9DS1_ID, + ST_LSM6DS0_ID, + ST_LSM6DSRX_ID, + ST_LSM6DST_ID, + ST_LSM6DSOP_ID, + ST_ASM330LHHX_ID, + ST_LSM6DSTX_ID, + ST_LSM6DSV_ID, + ST_LSM6DSV16X_ID, + ST_LSM6DSO16IS_ID, + ST_ISM330IS_ID, + ST_ASM330LHB_ID, + ST_ASM330LHHXG1_ID, + ST_LSM6DSX_MAX_ID, +}; + +#define ST_LSM6DSX_BUFF_SIZE 512 +#define ST_LSM6DSX_CHAN_SIZE 2 +#define ST_LSM6DSX_SAMPLE_SIZE 6 +#define ST_LSM6DSX_TAG_SIZE 1 +#define ST_LSM6DSX_TAGGED_SAMPLE_SIZE (ST_LSM6DSX_SAMPLE_SIZE + \ + ST_LSM6DSX_TAG_SIZE) +#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \ + ST_LSM6DSX_SAMPLE_SIZE) +#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \ + * ST_LSM6DSX_TAGGED_SAMPLE_SIZE) +#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) + +#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .event_spec = &st_lsm6dsx_event, \ + .ext_info = st_lsm6dsx_ext_info, \ + .num_event_specs = 1, \ +} + +#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .ext_info = st_lsm6dsx_ext_info, \ +} + +struct st_lsm6dsx_reg { + u8 addr; + u8 mask; +}; + +struct st_lsm6dsx_sensor; +struct st_lsm6dsx_hw; + +struct st_lsm6dsx_odr { + u32 milli_hz; + u8 val; +}; + +#define ST_LSM6DSX_ODR_LIST_SIZE 8 +struct st_lsm6dsx_odr_table_entry { + struct st_lsm6dsx_reg reg; + + struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE]; + int odr_len; +}; + +struct st_lsm6dsx_samples_to_discard { + struct { + u32 milli_hz; + u16 samples; + } val[ST_LSM6DSX_ODR_LIST_SIZE]; +}; + +struct st_lsm6dsx_fs { + u32 gain; + u8 val; +}; + +#define ST_LSM6DSX_FS_LIST_SIZE 4 +struct st_lsm6dsx_fs_table_entry { + struct st_lsm6dsx_reg reg; + + struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; + int fs_len; +}; + +/** + * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings + * @update_fifo: Update FIFO configuration callback. + * @read_fifo: Read FIFO callback. + * @fifo_th: FIFO threshold register info (addr + mask). + * @fifo_diff: FIFO diff status register info (addr + mask). + * @max_size: Sensor max fifo length in FIFO words. + * @th_wl: FIFO threshold word length. + */ +struct st_lsm6dsx_fifo_ops { + int (*update_fifo)(struct st_lsm6dsx_sensor *sensor, bool enable); + int (*read_fifo)(struct st_lsm6dsx_hw *hw); + struct { + u8 addr; + u16 mask; + } fifo_th; + struct { + u8 addr; + u16 mask; + } fifo_diff; + u16 max_size; + u8 th_wl; +}; + +/** + * struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings + * @timer_en: Hw timer enable register info (addr + mask). + * @hr_timer: Hw timer resolution register info (addr + mask). + * @fifo_en: Hw timer FIFO enable register info (addr + mask). + * @decimator: Hw timer FIFO decimator register info (addr + mask). + * @freq_fine: Difference in % of ODR with respect to the typical. + * @ts_sensitivity: Nominal timestamp sensitivity. + * @ts_trim_coeff: Coefficient for calculating the calibrated timestamp gain. + * This coefficient comes into play when linearizing the formula + * used to calculate the calibrated timestamp (please see the + * relevant formula in the AN for the specific IMU). + * For example, in the case of LSM6DSO we have: + * + * 1 / (1 + x) ~= 1 - x (Taylor’s Series) + * ttrim[s] = 1 / (40000 * (1 + 0.0015 * val)) (from AN5192) + * ttrim[ns] ~= 25000 - 37.5 * val + * ttrim[ns] ~= 25000 - (37500 * val) / 1000 + * + * so, replacing ts_sensitivity = 25000 and + * ts_trim_coeff = 37500 + * + * ttrim[ns] ~= ts_sensitivity - (ts_trim_coeff * val) / 1000 + */ +struct st_lsm6dsx_hw_ts_settings { + struct st_lsm6dsx_reg timer_en; + struct st_lsm6dsx_reg hr_timer; + struct st_lsm6dsx_reg fifo_en; + struct st_lsm6dsx_reg decimator; + u8 freq_fine; + u16 ts_sensitivity; + u16 ts_trim_coeff; +}; + +/** + * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings + * @page_mux: register page mux info (addr + mask). + * @master_en: master config register info (addr + mask). + * @pullup_en: i2c controller pull-up register info (addr + mask). + * @aux_sens: aux sensor register info (addr + mask). + * @wr_once: write_once register info (addr + mask). + * @emb_func: embedded function register info (addr + mask). + * @num_ext_dev: max number of slave devices. + * @shub_out: sensor hub first output register info. + * @slv0_addr: slave0 address in secondary page. + * @dw_slv0_addr: slave0 write register address in secondary page. + * @batch_en: Enable/disable FIFO batching. + * @pause: controller pause value. + */ +struct st_lsm6dsx_shub_settings { + struct st_lsm6dsx_reg page_mux; + struct { + bool sec_page; + u8 addr; + u8 mask; + } master_en; + struct { + bool sec_page; + u8 addr; + u8 mask; + } pullup_en; + struct st_lsm6dsx_reg aux_sens; + struct st_lsm6dsx_reg wr_once; + struct st_lsm6dsx_reg emb_func; + u8 num_ext_dev; + struct { + bool sec_page; + u8 addr; + } shub_out; + u8 slv0_addr; + u8 dw_slv0_addr; + u8 batch_en; + u8 pause; +}; + +struct st_lsm6dsx_event_settings { + struct st_lsm6dsx_reg enable_reg; + struct st_lsm6dsx_reg wakeup_reg; + u8 wakeup_src_reg; + u8 wakeup_src_status_mask; + u8 wakeup_src_z_mask; + u8 wakeup_src_y_mask; + u8 wakeup_src_x_mask; +}; + +enum st_lsm6dsx_sensor_id { + ST_LSM6DSX_ID_GYRO, + ST_LSM6DSX_ID_ACC, + ST_LSM6DSX_ID_EXT0, + ST_LSM6DSX_ID_EXT1, + ST_LSM6DSX_ID_EXT2, + ST_LSM6DSX_ID_MAX +}; + +enum st_lsm6dsx_ext_sensor_id { + ST_LSM6DSX_ID_MAGN, +}; + +/** + * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings + * @i2c_addr: I2c slave address list. + * @wai: Wai address info. + * @id: external sensor id. + * @odr_table: Output data rate of the sensor [Hz]. + * @fs_table: Configured sensor sensitivity table depending on full scale. + * @temp_comp: Temperature compensation register info (addr + mask). + * @pwr_table: Power on register info (addr + mask). + * @off_canc: Offset cancellation register info (addr + mask). + * @bdu: Block data update register info (addr + mask). + * @out: Output register info. + */ +struct st_lsm6dsx_ext_dev_settings { + u8 i2c_addr[2]; + struct { + u8 addr; + u8 val; + } wai; + enum st_lsm6dsx_ext_sensor_id id; + struct st_lsm6dsx_odr_table_entry odr_table; + struct st_lsm6dsx_fs_table_entry fs_table; + struct st_lsm6dsx_reg temp_comp; + struct { + struct st_lsm6dsx_reg reg; + u8 off_val; + u8 on_val; + } pwr_table; + struct st_lsm6dsx_reg off_canc; + struct st_lsm6dsx_reg bdu; + struct { + u8 addr; + u8 len; + } out; +}; + +/** + * struct st_lsm6dsx_settings - ST IMU sensor settings + * @reset: register address for reset. + * @boot: register address for boot. + * @bdu: register address for Block Data Update. + * @id: List of hw id/device name supported by the driver configuration. + * @channels: IIO channels supported by the device. + * @irq_config: interrupts related registers. + * @drdy_mask: register info for data-ready mask (addr + mask). + * @odr_table: Hw sensors odr table (Hz + val). + * @samples_to_discard: Number of samples to discard for filters settling time. + * @fs_table: Hw sensors gain table (gain + val). + * @decimator: List of decimator register info (addr + mask). + * @batch: List of FIFO batching register info (addr + mask). + * @fifo_ops: Sensor hw FIFO parameters. + * @ts_settings: Hw timer related settings. + * @shub_settings: i2c controller related settings. + */ +struct st_lsm6dsx_settings { + struct st_lsm6dsx_reg reset; + struct st_lsm6dsx_reg boot; + struct st_lsm6dsx_reg bdu; + struct { + enum st_lsm6dsx_hw_id hw_id; + const char *name; + u8 wai; + } id[ST_LSM6DSX_MAX_ID]; + struct { + const struct iio_chan_spec *chan; + int len; + } channels[2]; + struct { + struct st_lsm6dsx_reg irq1; + struct st_lsm6dsx_reg irq2; + struct st_lsm6dsx_reg irq1_func; + struct st_lsm6dsx_reg irq2_func; + struct st_lsm6dsx_reg lir; + struct st_lsm6dsx_reg clear_on_read; + struct st_lsm6dsx_reg hla; + struct st_lsm6dsx_reg od; + } irq_config; + struct st_lsm6dsx_reg drdy_mask; + struct st_lsm6dsx_odr_table_entry odr_table[2]; + struct st_lsm6dsx_samples_to_discard samples_to_discard[2]; + struct st_lsm6dsx_fs_table_entry fs_table[2]; + struct st_lsm6dsx_reg decimator[ST_LSM6DSX_ID_MAX]; + struct st_lsm6dsx_reg batch[2]; + struct st_lsm6dsx_fifo_ops fifo_ops; + struct st_lsm6dsx_hw_ts_settings ts_settings; + struct st_lsm6dsx_shub_settings shub_settings; + struct st_lsm6dsx_event_settings event_settings; +}; + +enum st_lsm6dsx_fifo_mode { + ST_LSM6DSX_FIFO_BYPASS = 0x0, + ST_LSM6DSX_FIFO_CONT = 0x6, +}; + +/** + * struct st_lsm6dsx_sensor - ST IMU sensor instance + * @name: Sensor name. + * @id: Sensor identifier. + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * @gain: Configured sensor sensitivity. + * @odr: Output data rate of the sensor [mHz]. + * hwfifo_odr_mHz: Batch data rate for hardware FIFO [mHz] + * @samples_to_discard: Number of samples to discard for filters settling time. + * @watermark: Sensor watermark level. + * @decimator: Sensor decimation factor. + * @sip: Number of samples in a given pattern. + * @ts_ref: Sensor timestamp reference for hw one. + * @ext_info: Sensor settings if it is connected to i2c controller + */ +struct st_lsm6dsx_sensor { + char name[32]; + enum st_lsm6dsx_sensor_id id; + struct st_lsm6dsx_hw *hw; + + u32 gain; + u32 odr; + u32 hwfifo_odr_mHz; + + u16 samples_to_discard; + u16 watermark; + u8 decimator; + u8 sip; + s64 ts_ref; + + struct { + const struct st_lsm6dsx_ext_dev_settings *settings; + u32 slv_odr; + u8 addr; + } ext_info; +}; + +/** + * struct st_lsm6dsx_hw - ST IMU MEMS hw instance + * @dev: Pointer to instance of struct device (I2C or SPI). + * @regmap: Register map of the device. + * @irq: Device interrupt line (I2C or SPI). + * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO. + * @conf_lock: Mutex to prevent concurrent FIFO configuration update. + * @page_lock: Mutex to prevent concurrent memory page configuration. + * @suspend_mask: Suspended sensor bitmask. + * @enable_mask: Enabled sensor bitmask. + * @fifo_mask: Enabled hw FIFO bitmask. + * @ts_gain: Hw timestamp rate after internal calibration. + * @ts_sip: Total number of timestamp samples in a given pattern. + * @sip: Total number of samples (acc/gyro/ts) in a given pattern. + * @buff: Device read buffer. + * @irq_routing: pointer to interrupt routing configuration. + * @event_threshold: wakeup event threshold. + * @enable_event: enabled event bitmask. + * @iio_devs: Pointers to acc/gyro iio_dev instances. + * @settings: Pointer to the specific sensor settings in use. + * @orientation: sensor chip orientation relative to main hardware. + * @scan: Temporary buffers used to align data before iio_push_to_buffers() + */ +struct st_lsm6dsx_hw { + struct device *dev; + struct regmap *regmap; + int irq; + + struct mutex fifo_lock; + struct mutex conf_lock; + struct mutex page_lock; + + u8 suspend_mask; + u8 enable_mask; + u8 fifo_mask; + s64 ts_gain; + u8 ts_sip; + u8 sip; + + const struct st_lsm6dsx_reg *irq_routing; + u8 event_threshold; + u8 enable_event; + + u8 *buff; + + struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX]; + + const struct st_lsm6dsx_settings *settings; + + struct iio_mount_matrix orientation; + /* Ensure natural alignment of buffer elements */ + struct { + __le16 channels[3]; + aligned_s64 ts; + } scan[ST_LSM6DSX_ID_MAX]; +}; + +static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) +}; + +static __maybe_unused const unsigned long st_lsm6dsx_available_scan_masks[] = { + 0x7, 0x0, +}; + +extern const struct dev_pm_ops st_lsm6dsx_pm_ops; + +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, + struct regmap *regmap); +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable); +int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val); +int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, + u16 watermark); +int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable); +int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_resume_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u32 odr, u8 *val); +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name); +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable); +int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len); +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable); + +static inline int +st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr, + unsigned int mask, unsigned int val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = regmap_update_bits(hw->regmap, addr, mask, val); + mutex_unlock(&hw->page_lock); + + return err; +} + +static inline int +st_lsm6dsx_read_locked(struct st_lsm6dsx_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_lsm6dsx_write_locked(struct st_lsm6dsx_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 const struct iio_mount_matrix * +st_lsm6dsx_get_mount_matrix(const struct iio_dev *iio_dev, + const struct iio_chan_spec *chan) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + return &hw->orientation; +} + +static inline int +st_lsm6dsx_device_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + if (sensor->id == ST_LSM6DSX_ID_EXT0 || + sensor->id == ST_LSM6DSX_ID_EXT1 || + sensor->id == ST_LSM6DSX_ID_EXT2) + return st_lsm6dsx_shub_set_enable(sensor, enable); + + return st_lsm6dsx_sensor_set_enable(sensor, enable); +} + +static const +struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_lsm6dsx_get_mount_matrix), + { } +}; + +#endif /* ST_LSM6DSX_H */ diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_buffer.c b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_buffer.c new file mode 100644 index 0000000..55d8777 --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_buffer.c @@ -0,0 +1,872 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_lsm6dsx FIFO buffer library driver + * + * Pattern FIFO: + * The FIFO buffer can be configured to store data from gyroscope and + * accelerometer. Samples are queued without any tag according to a + * specific pattern based on 'FIFO data sets' (6 bytes each): + * - 1st data set is reserved for gyroscope data + * - 2nd data set is reserved for accelerometer data + * The FIFO pattern changes depending on the ODRs and decimation factors + * assigned to the FIFO data sets. The first sequence of data stored in FIFO + * buffer contains the data of all the enabled FIFO data sets + * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the + * value of the decimation factor and ODR set for each FIFO data set. + * + * Supported devices: + * - ISM330DLC + * - LSM6DS3 + * - LSM6DS3H + * - LSM6DS3TR-C + * - LSM6DSL + * - LSM6DSM + * + * Tagged FIFO: + * The FIFO buffer can be configured to store data from gyroscope and + * accelerometer. Each sample is queued with a tag (1B) indicating data + * source (gyroscope, accelerometer, hw timer). + * + * Supported devices: + * - ASM330LHB + * - ASM330LHH + * - ASM330LHHX + * - ASM330LHHXG1 + * - ISM330DHCX + * - LSM6DSO + * - LSM6DSOP + * - LSM6DSOX + * - LSM6DSR + * - LSM6DSRX + * - LSM6DST + * - LSM6DSTX + * - LSM6DSV + * + * FIFO supported modes: + * - BYPASS: FIFO disabled + * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index + * restarts from the beginning and the oldest sample is overwritten + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a +#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0) +#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) +#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) +#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e +#define ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR 0x78 +#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42 + +#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 + +#define ST_LSM6DSX_TS_RESET_VAL 0xaa + +struct st_lsm6dsx_decimator_entry { + u8 decimator; + u8 val; +}; + +enum st_lsm6dsx_fifo_tag { + ST_LSM6DSX_GYRO_TAG = 0x01, + ST_LSM6DSX_ACC_TAG = 0x02, + ST_LSM6DSX_TS_TAG = 0x04, + ST_LSM6DSX_EXT0_TAG = 0x0f, + ST_LSM6DSX_EXT1_TAG = 0x10, + ST_LSM6DSX_EXT2_TAG = 0x11, +}; + +static const +struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, + { 4, 0x4 }, + { 8, 0x5 }, + { 16, 0x6 }, + { 32, 0x7 }, +}; + +static int +st_lsm6dsx_get_decimator_val(struct st_lsm6dsx_sensor *sensor, u32 max_odr) +{ + const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table); + u32 decimator = max_odr / sensor->hwfifo_odr_mHz; + int i; + + if (decimator > 1) + decimator = round_down(decimator, 2); + + for (i = 0; i < max_size; i++) { + if (st_lsm6dsx_decimator_table[i].decimator == decimator) + break; + } + + sensor->decimator = decimator; + return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val; +} + +static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, + u32 *max_odr, u32 *min_odr) +{ + struct st_lsm6dsx_sensor *sensor; + int i; + + *max_odr = 0, *min_odr = ~0; + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + *max_odr = max(*max_odr, sensor->hwfifo_odr_mHz); + *min_odr = min(*min_odr, sensor->hwfifo_odr_mHz); + } +} + +static u8 st_lsm6dsx_get_sip(struct st_lsm6dsx_sensor *sensor, u32 min_odr) +{ + u8 sip = sensor->hwfifo_odr_mHz / min_odr; + + return sip > 1 ? round_down(sip, 2) : sip; +} + +static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_reg *ts_dec_reg; + struct st_lsm6dsx_sensor *sensor; + u16 sip = 0, ts_sip = 0; + u32 max_odr, min_odr; + int err = 0, i; + u8 data; + + st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr); + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + const struct st_lsm6dsx_reg *dec_reg; + + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + /* update fifo decimators and sample in pattern */ + if (hw->enable_mask & BIT(sensor->id)) { + sensor->sip = st_lsm6dsx_get_sip(sensor, min_odr); + data = st_lsm6dsx_get_decimator_val(sensor, max_odr); + } else { + sensor->sip = 0; + data = 0; + } + ts_sip = max_t(u16, ts_sip, sensor->sip); + + dec_reg = &hw->settings->decimator[sensor->id]; + if (dec_reg->addr) { + int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask); + + err = st_lsm6dsx_update_bits_locked(hw, dec_reg->addr, + dec_reg->mask, + val); + if (err < 0) + return err; + } + sip += sensor->sip; + } + hw->sip = sip + ts_sip; + hw->ts_sip = ts_sip; + + /* + * update hw ts decimator if necessary. Decimator for hw timestamp + * is always 1 or 0 in order to have a ts sample for each data + * sample in FIFO + */ + ts_dec_reg = &hw->settings->ts_settings.decimator; + if (ts_dec_reg->addr) { + int val, ts_dec = !!hw->ts_sip; + + val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, ts_dec_reg->addr, + ts_dec_reg->mask, val); + } + return err; +} + +static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_fifo_mode fifo_mode) +{ + unsigned int data; + + data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode); + return st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_MODE_MASK, data); +} + +static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *batch_reg; + u8 data; + + batch_reg = &hw->settings->batch[sensor->id]; + if (batch_reg->addr) { + int val; + + if (enable) { + int err; + + err = st_lsm6dsx_check_odr(sensor, sensor->hwfifo_odr_mHz, + &data); + if (err < 0) + return err; + } else { + data = 0; + } + val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask); + return st_lsm6dsx_update_bits_locked(hw, batch_reg->addr, + batch_reg->mask, val); + } else { + data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; + return st_lsm6dsx_update_bits_locked(hw, + ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_ODR_MASK, + FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, + data)); + } +} + +int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) +{ + u16 fifo_watermark = ~0, cur_watermark, fifo_th_mask; + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *cur_sensor; + int i, err, data; + __le16 wdata; + + if (!hw->sip) + return 0; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + cur_sensor = iio_priv(hw->iio_devs[i]); + + if (!(hw->enable_mask & BIT(cur_sensor->id))) + continue; + + cur_watermark = (cur_sensor == sensor) ? watermark + : cur_sensor->watermark; + + fifo_watermark = min_t(u16, fifo_watermark, cur_watermark); + } + + fifo_watermark = max_t(u16, fifo_watermark, hw->sip); + fifo_watermark = (fifo_watermark / hw->sip) * hw->sip; + fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl; + + mutex_lock(&hw->page_lock); + err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1, + &data); + if (err < 0) + goto out; + + fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask; + fifo_watermark = ((data << 8) & ~fifo_th_mask) | + (fifo_watermark & fifo_th_mask); + + wdata = cpu_to_le16(fifo_watermark); + err = regmap_bulk_write(hw->regmap, + hw->settings->fifo_ops.fifo_th.addr, + &wdata, sizeof(wdata)); +out: + mutex_unlock(&hw->page_lock); + return err; +} + +static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *sensor; + int i, err; + + /* reset hw ts counter */ + err = st_lsm6dsx_write_locked(hw, ST_LSM6DSX_REG_TS_RESET_ADDR, + ST_LSM6DSX_TS_RESET_VAL); + if (err < 0) + return err; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + /* + * store enable buffer timestamp as reference for + * hw timestamp + */ + sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]); + } + return 0; +} + +int st_lsm6dsx_resume_fifo(struct st_lsm6dsx_hw *hw) +{ + int err; + + /* reset hw ts counter */ + err = st_lsm6dsx_reset_hw_ts(hw); + if (err < 0) + return err; + + return st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); +} + +/* + * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN + * in order to avoid a kmalloc for each bus access + */ +static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, + u8 *data, unsigned int data_len, + unsigned int max_word_len) +{ + unsigned int word_len, read_len = 0; + int err; + + while (read_len < data_len) { + word_len = min_t(unsigned int, data_len - read_len, + max_word_len); + err = st_lsm6dsx_read_locked(hw, addr, data + read_len, + word_len); + if (err < 0) + return err; + read_len += word_len; + } + return 0; +} + +#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ + sizeof(s64)) + sizeof(s64)) +/** + * st_lsm6dsx_read_fifo() - hw FIFO read routine + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * + * Read samples from the hw FIFO and push them to IIO buffers. + * + * Return: Number of bytes read from the FIFO + */ +int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor, *ext_sensor = NULL; + int err, sip, acc_sip, gyro_sip, ts_sip, ext_sip, read_len, offset; + u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; + u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; + bool reset_ts = false; + __le16 fifo_status; + s64 ts = 0; + + err = st_lsm6dsx_read_locked(hw, + hw->settings->fifo_ops.fifo_diff.addr, + &fifo_status, sizeof(fifo_status)); + if (err < 0) { + dev_err(hw->dev, "failed to read fifo status (err=%d)\n", + err); + return err; + } + + if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK)) + return 0; + + if (!pattern_len) + pattern_len = ST_LSM6DSX_SAMPLE_SIZE; + + fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * + ST_LSM6DSX_CHAN_SIZE; + fifo_len = (fifo_len / pattern_len) * pattern_len; + + acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); + if (hw->iio_devs[ST_LSM6DSX_ID_EXT0]) + ext_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_EXT0]); + + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { + err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, + hw->buff, pattern_len, + ST_LSM6DSX_MAX_WORD_LEN); + if (err < 0) { + dev_err(hw->dev, + "failed to read pattern from fifo (err=%d)\n", + err); + return err; + } + + /* + * Data are written to the FIFO with a specific pattern + * depending on the configured ODRs. The first sequence of data + * stored in FIFO contains the data of all enabled sensors + * (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated + * depending on the value of the decimation factor set for each + * sensor. + * + * Supposing the FIFO is storing data from gyroscope and + * accelerometer at different ODRs: + * - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz + * Since the gyroscope ODR is twice the accelerometer one, the + * following pattern is repeated every 9 samples: + * - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, .. + */ + ext_sip = ext_sensor ? ext_sensor->sip : 0; + gyro_sip = gyro_sensor->sip; + acc_sip = acc_sensor->sip; + ts_sip = hw->ts_sip; + offset = 0; + sip = 0; + + while (acc_sip > 0 || gyro_sip > 0 || ext_sip > 0) { + if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { + memcpy(hw->scan[ST_LSM6DSX_ID_GYRO].channels, + &hw->buff[offset], + sizeof(hw->scan[ST_LSM6DSX_ID_GYRO].channels)); + offset += sizeof(hw->scan[ST_LSM6DSX_ID_GYRO].channels); + } + if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { + memcpy(hw->scan[ST_LSM6DSX_ID_ACC].channels, + &hw->buff[offset], + sizeof(hw->scan[ST_LSM6DSX_ID_ACC].channels)); + offset += sizeof(hw->scan[ST_LSM6DSX_ID_ACC].channels); + } + if (ext_sip > 0 && !(sip % ext_sensor->decimator)) { + memcpy(hw->scan[ST_LSM6DSX_ID_EXT0].channels, + &hw->buff[offset], + sizeof(hw->scan[ST_LSM6DSX_ID_EXT0].channels)); + offset += sizeof(hw->scan[ST_LSM6DSX_ID_EXT0].channels); + } + + if (ts_sip-- > 0) { + u8 data[ST_LSM6DSX_SAMPLE_SIZE]; + + memcpy(data, &hw->buff[offset], sizeof(data)); + /* + * hw timestamp is 3B long and it is stored + * in FIFO using 6B as 4th FIFO data set + * according to this schema: + * B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0] + */ + ts = data[1] << 16 | data[0] << 8 | data[3]; + /* + * check if hw timestamp engine is going to + * reset (the sensor generates an interrupt + * to signal the hw timestamp will reset in + * 1.638s) + */ + if (!reset_ts && ts >= 0xff0000) + reset_ts = true; + ts *= hw->ts_gain; + + offset += ST_LSM6DSX_SAMPLE_SIZE; + } + + if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) { + /* + * We need to discards gyro samples during + * filters settling time + */ + if (gyro_sensor->samples_to_discard > 0) + gyro_sensor->samples_to_discard--; + else + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + &hw->scan[ST_LSM6DSX_ID_GYRO], + gyro_sensor->ts_ref + ts); + gyro_sip--; + } + if (acc_sip > 0 && !(sip % acc_sensor->decimator)) { + /* + * We need to discards accel samples during + * filters settling time + */ + if (acc_sensor->samples_to_discard > 0) + acc_sensor->samples_to_discard--; + else + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + &hw->scan[ST_LSM6DSX_ID_ACC], + acc_sensor->ts_ref + ts); + acc_sip--; + } + if (ext_sip > 0 && !(sip % ext_sensor->decimator)) { + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_EXT0], + &hw->scan[ST_LSM6DSX_ID_EXT0], + ext_sensor->ts_ref + ts); + ext_sip--; + } + sip++; + } + } + + if (unlikely(reset_ts)) { + err = st_lsm6dsx_reset_hw_ts(hw); + if (err < 0) { + dev_err(hw->dev, "failed to reset hw ts (err=%d)\n", + err); + return err; + } + } + return read_len; +} + +#define ST_LSM6DSX_INVALID_SAMPLE 0x7ffd +static int +st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, + u8 *data, s64 ts) +{ + s16 val = le16_to_cpu(*(__le16 *)data); + struct st_lsm6dsx_sensor *sensor; + struct iio_dev *iio_dev; + + /* invalid sample during bootstrap phase */ + if (val >= ST_LSM6DSX_INVALID_SAMPLE) + return -EINVAL; + + /* + * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG + * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG + * to the second one and ST_LSM6DSX_EXT2_TAG to the last enabled + * channel + */ + switch (tag) { + case ST_LSM6DSX_GYRO_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO]; + break; + case ST_LSM6DSX_ACC_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC]; + break; + case ST_LSM6DSX_EXT0_TAG: + if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0]; + else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1)) + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; + else + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; + break; + case ST_LSM6DSX_EXT1_TAG: + if ((hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) && + (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))) + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; + else + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; + break; + case ST_LSM6DSX_EXT2_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; + break; + default: + return -EINVAL; + } + + sensor = iio_priv(iio_dev); + iio_push_to_buffers_with_timestamp(iio_dev, data, + ts + sensor->ts_ref); + + return 0; +} + +/** + * st_lsm6dsx_read_tagged_fifo() - tagged hw FIFO read routine + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * + * Read samples from the hw FIFO and push them to IIO buffers. + * + * Return: Number of bytes read from the FIFO + */ +int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) +{ + u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + u16 fifo_len, fifo_diff_mask; + /* + * Alignment needed as this can ultimately be passed to a + * call to iio_push_to_buffers_with_timestamp() which + * must be passed a buffer that is aligned to 8 bytes so + * as to allow insertion of a naturally aligned timestamp. + */ + u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8); + u8 tag; + bool reset_ts = false; + int i, err, read_len; + __le16 fifo_status; + s64 ts = 0; + + err = st_lsm6dsx_read_locked(hw, + hw->settings->fifo_ops.fifo_diff.addr, + &fifo_status, sizeof(fifo_status)); + if (err < 0) { + dev_err(hw->dev, "failed to read fifo status (err=%d)\n", + err); + return err; + } + + fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; + fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * + ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + if (!fifo_len) + return 0; + + if (!pattern_len) + pattern_len = ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { + err = st_lsm6dsx_read_block(hw, + ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR, + hw->buff, pattern_len, + ST_LSM6DSX_MAX_TAGGED_WORD_LEN); + if (err < 0) { + dev_err(hw->dev, + "failed to read pattern from fifo (err=%d)\n", + err); + return err; + } + + for (i = 0; i < pattern_len; + i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) { + memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE], + ST_LSM6DSX_SAMPLE_SIZE); + + tag = hw->buff[i] >> 3; + if (tag == ST_LSM6DSX_TS_TAG) { + /* + * hw timestamp is 4B long and it is stored + * in FIFO according to this schema: + * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16], + * B3 = ts[31:24] + */ + ts = le32_to_cpu(*((__le32 *)iio_buff)); + /* + * check if hw timestamp engine is going to + * reset (the sensor generates an interrupt + * to signal the hw timestamp will reset in + * 1.638s) + */ + if (!reset_ts && ts >= 0xffff0000) + reset_ts = true; + ts *= hw->ts_gain; + } else { + st_lsm6dsx_push_tagged_data(hw, tag, iio_buff, + ts); + } + } + } + + if (unlikely(reset_ts)) { + err = st_lsm6dsx_reset_hw_ts(hw); + if (err < 0) + return err; + } + return read_len; +} + +int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) +{ + int err; + + if (!hw->settings->fifo_ops.read_fifo) + return -ENOTSUPP; + + mutex_lock(&hw->fifo_lock); + + hw->settings->fifo_ops.read_fifo(hw); + err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS); + + mutex_unlock(&hw->fifo_lock); + + return err; +} + +static void +st_lsm6dsx_update_samples_to_discard(struct st_lsm6dsx_sensor *sensor) +{ + const struct st_lsm6dsx_samples_to_discard *data; + struct st_lsm6dsx_hw *hw = sensor->hw; + int i; + + if (sensor->id != ST_LSM6DSX_ID_GYRO && + sensor->id != ST_LSM6DSX_ID_ACC) + return; + + /* check if drdy mask is supported in hw */ + if (hw->settings->drdy_mask.addr) + return; + + data = &hw->settings->samples_to_discard[sensor->id]; + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { + if (data->val[i].milli_hz == sensor->hwfifo_odr_mHz) { + sensor->samples_to_discard = data->val[i].samples; + return; + } + } +} + +int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + u8 fifo_mask; + int err; + + mutex_lock(&hw->conf_lock); + + if (enable) + fifo_mask = hw->fifo_mask | BIT(sensor->id); + else + fifo_mask = hw->fifo_mask & ~BIT(sensor->id); + + if (hw->fifo_mask) { + err = st_lsm6dsx_flush_fifo(hw); + if (err < 0) + goto out; + } + + if (enable) + st_lsm6dsx_update_samples_to_discard(sensor); + + err = st_lsm6dsx_device_set_enable(sensor, enable); + if (err < 0) + goto out; + + err = st_lsm6dsx_set_fifo_odr(sensor, enable); + if (err < 0) + goto out; + + err = st_lsm6dsx_update_decimators(hw); + if (err < 0) + goto out; + + err = st_lsm6dsx_update_watermark(sensor, sensor->watermark); + if (err < 0) + goto out; + + if (fifo_mask) { + err = st_lsm6dsx_resume_fifo(hw); + if (err < 0) + goto out; + } + + hw->fifo_mask = fifo_mask; + +out: + mutex_unlock(&hw->conf_lock); + + return err; +} + +static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (!hw->settings->fifo_ops.update_fifo) + return -ENOTSUPP; + + return hw->settings->fifo_ops.update_fifo(sensor, true); +} + +static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (!hw->settings->fifo_ops.update_fifo) + return -ENOTSUPP; + + return hw->settings->fifo_ops.update_fifo(sensor, false); +} + +static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { + .preenable = st_lsm6dsx_buffer_preenable, + .postdisable = st_lsm6dsx_buffer_postdisable, +}; + +static ssize_t st_lsm6dsx_hwfifo_odr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + + return sysfs_emit(buf, "%d.%03d\n", sensor->hwfifo_odr_mHz / 1000, + sensor->hwfifo_odr_mHz % 1000); +} + +static ssize_t st_lsm6dsx_hwfifo_odr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *iio_dev = dev_to_iio_dev(dev); + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int integer, milli; + int ret; + u32 hwfifo_odr; + u8 data; + + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + ret = iio_str_to_fixpoint(buf, 100, &integer, &milli); + if (ret) + goto out; + + hwfifo_odr = integer * 1000 + milli; + ret = st_lsm6dsx_check_odr(sensor, hwfifo_odr, &data); + if (ret < 0) + goto out; + + hwfifo_odr = ret; + + /* the batch data rate must not exceed the sensor output data rate */ + if (hwfifo_odr <= sensor->odr) + sensor->hwfifo_odr_mHz = hwfifo_odr; + else + ret = -EINVAL; + +out: + iio_device_release_direct(iio_dev); + + return ret < 0 ? ret : len; +} + +static IIO_DEV_ATTR_SAMP_FREQ(0664, st_lsm6dsx_hwfifo_odr_show, st_lsm6dsx_hwfifo_odr_store); + +static const struct iio_dev_attr *st_lsm6dsx_buffer_attrs[] = { + &iio_dev_attr_sampling_frequency, + NULL +}; + +int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) +{ + int i, ret; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + ret = devm_iio_kfifo_buffer_setup_ext(hw->dev, hw->iio_devs[i], + &st_lsm6dsx_buffer_ops, + st_lsm6dsx_buffer_attrs); + if (ret) + return ret; + } + + return 0; +} diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_core.c b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_core.c new file mode 100644 index 0000000..dc78227 --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_core.c @@ -0,0 +1,2808 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_lsm6dsx sensor driver + * + * The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer + * and 3D digital gyroscope system-in-package with a digital I2C/SPI serial + * interface standard output. + * LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale + * acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of + * +-125/+-245/+-500/+-1000/+-2000 dps + * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer + * allowing dynamic batching of sensor data. + * LSM9DSx series is similar but includes an additional magnetometer, handled + * by a different driver. + * + * Supported sensors: + * + * - LSM6DS3 + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 8KB + * + * - ISM330DLC + * - LSM6DS3H + * - LSM6DS3TR-C + * - LSM6DSL + * - LSM6DSM + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 4KB + * + * - ASM330LHH + * - ASM330LHHX + * - ASM330LHHXG1 + * - ISM330DHCX + * - ISM330IS + * - LSM6DSO + * - LSM6DSO16IS + * - LSM6DSOP + * - LSM6DSOX + * - LSM6DSR + * - LSM6DST + * - LSM6DSTX + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, + * 833 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 3KB + * + * - LSM6DSV + * - LSM6DSV16X + * - Accelerometer/Gyroscope supported ODR [Hz]: 7.5, 15, 30, 60, 120, 240, + * 480, 960 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-250/+-500/+-1000/+-2000 + * - FIFO size: 3KB + * + * - LSM6DS0 + * - LSM9DS1 + * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952 + * - Gyroscope supported full-scale [dps]: +-245/+-500/+-2000 + * - FIFO size: 32 + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f + +static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { + ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_lsm6ds0_acc_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x22, IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x24, IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x26, IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x18, IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x1a, IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x1c, IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { + { + .reset = { + .addr = 0x22, + .mask = BIT(0), + }, + .boot = { + .addr = 0x22, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x22, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM9DS1_ID, + .name = ST_LSM9DS1_DEV_NAME, + .wai = 0x68, + }, { + .hw_id = ST_LSM6DS0_ID, + .name = ST_LSM6DS0_DEV_NAME, + .wai = 0x68, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6ds0_acc_channels, + .len = ARRAY_SIZE(st_lsm6ds0_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6ds0_gyro_channels, + .len = ARRAY_SIZE(st_lsm6ds0_gyro_channels), + }, + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x20, + .mask = GENMASK(7, 5), + }, + .odr_avl[0] = { 10000, 0x01 }, + .odr_avl[1] = { 50000, 0x02 }, + .odr_avl[2] = { 119000, 0x03 }, + .odr_avl[3] = { 238000, 0x04 }, + .odr_avl[4] = { 476000, 0x05 }, + .odr_avl[5] = { 952000, 0x06 }, + .odr_len = 6, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 5), + }, + .odr_avl[0] = { 14900, 0x01 }, + .odr_avl[1] = { 59500, 0x02 }, + .odr_avl[2] = { 119000, 0x03 }, + .odr_avl[3] = { 238000, 0x04 }, + .odr_avl[4] = { 476000, 0x05 }, + .odr_avl[5] = { 952000, 0x06 }, + .odr_len = 6, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x20, + .mask = GENMASK(4, 3), + }, + .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(732000), 0x1 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(4, 3), + }, + + .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(70000000), 0x3 }, + .fs_len = 3, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0c, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .hla = { + .addr = 0x22, + .mask = BIT(5), + }, + .od = { + .addr = 0x22, + .mask = BIT(4), + }, + }, + .fifo_ops = { + .max_size = 32, + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM6DS3_ID, + .name = ST_LSM6DS3_DEV_NAME, + .wai = 0x69, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6dsx_acc_channels, + .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 4), + }, + .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_len = 6, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(7, 4), + }, + .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_len = 6, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, + .decimator = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x08, + .mask = GENMASK(2, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x08, + .mask = GENMASK(5, 3), + }, + }, + .fifo_ops = { + .update_fifo = st_lsm6dsx_update_fifo, + .read_fifo = st_lsm6dsx_read_fifo, + .fifo_th = { + .addr = 0x06, + .mask = GENMASK(11, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(11, 0), + }, + .max_size = 1365, + .th_wl = 3, /* 1LSB = 2B */ + }, + .ts_settings = { + .timer_en = { + .addr = 0x58, + .mask = BIT(7), + }, + .hr_timer = { + .addr = 0x5c, + .mask = BIT(4), + }, + .fifo_en = { + .addr = 0x07, + .mask = BIT(7), + }, + .decimator = { + .addr = 0x09, + .mask = GENMASK(5, 3), + }, + }, + .event_settings = { + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM6DS3H_ID, + .name = ST_LSM6DS3H_DEV_NAME, + .wai = 0x69, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6dsx_acc_channels, + .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 4), + }, + .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_len = 6, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(7, 4), + }, + .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_len = 6, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, + .decimator = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x08, + .mask = GENMASK(2, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x08, + .mask = GENMASK(5, 3), + }, + }, + .fifo_ops = { + .update_fifo = st_lsm6dsx_update_fifo, + .read_fifo = st_lsm6dsx_read_fifo, + .fifo_th = { + .addr = 0x06, + .mask = GENMASK(11, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(11, 0), + }, + .max_size = 682, + .th_wl = 3, /* 1LSB = 2B */ + }, + .ts_settings = { + .timer_en = { + .addr = 0x58, + .mask = BIT(7), + }, + .hr_timer = { + .addr = 0x5c, + .mask = BIT(4), + }, + .fifo_en = { + .addr = 0x07, + .mask = BIT(7), + }, + .decimator = { + .addr = 0x09, + .mask = GENMASK(5, 3), + }, + }, + .event_settings = { + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM6DSL_ID, + .name = ST_LSM6DSL_DEV_NAME, + .wai = 0x6a, + }, { + .hw_id = ST_LSM6DSM_ID, + .name = ST_LSM6DSM_DEV_NAME, + .wai = 0x6a, + }, { + .hw_id = ST_ISM330DLC_ID, + .name = ST_ISM330DLC_DEV_NAME, + .wai = 0x6a, + }, { + .hw_id = ST_LSM6DS3TRC_ID, + .name = ST_LSM6DS3TRC_DEV_NAME, + .wai = 0x6a, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6dsx_acc_channels, + .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 4), + }, + .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_len = 6, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(7, 4), + }, + .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_len = 6, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + }, + .samples_to_discard = { + [ST_LSM6DSX_ID_ACC] = { + .val[0] = { 12500, 1 }, + .val[1] = { 26000, 1 }, + .val[2] = { 52000, 1 }, + .val[3] = { 104000, 2 }, + .val[4] = { 208000, 2 }, + .val[5] = { 416000, 2 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .val[0] = { 12500, 2 }, + .val[1] = { 26000, 5 }, + .val[2] = { 52000, 7 }, + .val[3] = { 104000, 12 }, + .val[4] = { 208000, 20 }, + .val[5] = { 416000, 36 }, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x58, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, + .decimator = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x08, + .mask = GENMASK(2, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x08, + .mask = GENMASK(5, 3), + }, + [ST_LSM6DSX_ID_EXT0] = { + .addr = 0x09, + .mask = GENMASK(2, 0), + }, + }, + .fifo_ops = { + .update_fifo = st_lsm6dsx_update_fifo, + .read_fifo = st_lsm6dsx_read_fifo, + .fifo_th = { + .addr = 0x06, + .mask = GENMASK(10, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(10, 0), + }, + .max_size = 682, + .th_wl = 3, /* 1LSB = 2B */ + }, + .ts_settings = { + .timer_en = { + .addr = 0x19, + .mask = BIT(5), + }, + .hr_timer = { + .addr = 0x5c, + .mask = BIT(4), + }, + .fifo_en = { + .addr = 0x07, + .mask = BIT(7), + }, + .decimator = { + .addr = 0x09, + .mask = GENMASK(5, 3), + }, + }, + .shub_settings = { + .page_mux = { + .addr = 0x01, + .mask = BIT(7), + }, + .master_en = { + .addr = 0x1a, + .mask = BIT(0), + }, + .pullup_en = { + .addr = 0x1a, + .mask = BIT(3), + }, + .aux_sens = { + .addr = 0x04, + .mask = GENMASK(5, 4), + }, + .wr_once = { + .addr = 0x07, + .mask = BIT(5), + }, + .emb_func = { + .addr = 0x19, + .mask = BIT(2), + }, + .num_ext_dev = 1, + .shub_out = { + .addr = 0x2e, + }, + .slv0_addr = 0x02, + .dw_slv0_addr = 0x0e, + .pause = 0x7, + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM6DSR_ID, + .name = ST_LSM6DSR_DEV_NAME, + .wai = 0x6b, + }, { + .hw_id = ST_ISM330DHCX_ID, + .name = ST_ISM330DHCX_DEV_NAME, + .wai = 0x6b, + }, { + .hw_id = ST_LSM6DSRX_ID, + .name = ST_LSM6DSRX_DEV_NAME, + .wai = 0x6b, + }, { + .hw_id = ST_LSM6DSO_ID, + .name = ST_LSM6DSO_DEV_NAME, + .wai = 0x6c, + }, { + .hw_id = ST_LSM6DSOX_ID, + .name = ST_LSM6DSOX_DEV_NAME, + .wai = 0x6c, + }, { + .hw_id = ST_LSM6DST_ID, + .name = ST_LSM6DST_DEV_NAME, + .wai = 0x6d, + }, { + .hw_id = ST_ASM330LHHX_ID, + .name = ST_ASM330LHHX_DEV_NAME, + .wai = 0x6b, + }, { + .hw_id = ST_ASM330LHHXG1_ID, + .name = ST_ASM330LHHXG1_DEV_NAME, + .wai = 0x6b, + }, { + .hw_id = ST_LSM6DSTX_ID, + .name = ST_LSM6DSTX_DEV_NAME, + .wai = 0x6d, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6dsx_acc_channels, + .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 4), + }, + .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 }, + .odr_len = 7, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(7, 4), + }, + .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 }, + .odr_len = 7, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, + .batch = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x09, + .mask = GENMASK(3, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x09, + .mask = GENMASK(7, 4), + }, + }, + .fifo_ops = { + .update_fifo = st_lsm6dsx_update_fifo, + .read_fifo = st_lsm6dsx_read_tagged_fifo, + .fifo_th = { + .addr = 0x07, + .mask = GENMASK(8, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(9, 0), + }, + .max_size = 512, + .th_wl = 1, + }, + .ts_settings = { + .timer_en = { + .addr = 0x19, + .mask = BIT(5), + }, + .decimator = { + .addr = 0x0a, + .mask = GENMASK(7, 6), + }, + .freq_fine = 0x63, + .ts_sensitivity = 25000, + .ts_trim_coeff = 37500, + }, + .shub_settings = { + .page_mux = { + .addr = 0x01, + .mask = BIT(6), + }, + .master_en = { + .sec_page = true, + .addr = 0x14, + .mask = BIT(2), + }, + .pullup_en = { + .sec_page = true, + .addr = 0x14, + .mask = BIT(3), + }, + .aux_sens = { + .addr = 0x14, + .mask = GENMASK(1, 0), + }, + .wr_once = { + .addr = 0x14, + .mask = BIT(6), + }, + .num_ext_dev = 3, + .shub_out = { + .sec_page = true, + .addr = 0x02, + }, + .slv0_addr = 0x15, + .dw_slv0_addr = 0x21, + .batch_en = BIT(3), + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5b, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_ASM330LHH_ID, + .name = ST_ASM330LHH_DEV_NAME, + .wai = 0x6b, + }, { + .hw_id = ST_LSM6DSOP_ID, + .name = ST_LSM6DSOP_DEV_NAME, + .wai = 0x6c, + }, { + .hw_id = ST_ASM330LHB_ID, + .name = ST_ASM330LHB_DEV_NAME, + .wai = 0x6b, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6dsx_acc_channels, + .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 4), + }, + .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 }, + .odr_len = 7, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(7, 4), + }, + .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 }, + .odr_len = 7, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .clear_on_read = { + .addr = 0x56, + .mask = BIT(6), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, + .batch = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x09, + .mask = GENMASK(3, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x09, + .mask = GENMASK(7, 4), + }, + }, + .fifo_ops = { + .update_fifo = st_lsm6dsx_update_fifo, + .read_fifo = st_lsm6dsx_read_tagged_fifo, + .fifo_th = { + .addr = 0x07, + .mask = GENMASK(8, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(9, 0), + }, + .max_size = 512, + .th_wl = 1, + }, + .ts_settings = { + .timer_en = { + .addr = 0x19, + .mask = BIT(5), + }, + .decimator = { + .addr = 0x0a, + .mask = GENMASK(7, 6), + }, + .freq_fine = 0x63, + .ts_sensitivity = 25000, + .ts_trim_coeff = 37500, + }, + .event_settings = { + .enable_reg = { + .addr = 0x58, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5B, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x1b, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM6DSV_ID, + .name = ST_LSM6DSV_DEV_NAME, + .wai = 0x70, + }, { + .hw_id = ST_LSM6DSV16X_ID, + .name = ST_LSM6DSV16X_DEV_NAME, + .wai = 0x70, + }, + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6dsx_acc_channels, + .len = ARRAY_SIZE(st_lsm6dsx_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .drdy_mask = { + .addr = 0x13, + .mask = BIT(3), + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 0), + }, + .odr_avl[0] = { 7500, 0x02 }, + .odr_avl[1] = { 15000, 0x03 }, + .odr_avl[2] = { 30000, 0x04 }, + .odr_avl[3] = { 60000, 0x05 }, + .odr_avl[4] = { 120000, 0x06 }, + .odr_avl[5] = { 240000, 0x07 }, + .odr_avl[6] = { 480000, 0x08 }, + .odr_avl[7] = { 960000, 0x09 }, + .odr_len = 8, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 0), + }, + .odr_avl[0] = { 7500, 0x02 }, + .odr_avl[1] = { 15000, 0x03 }, + .odr_avl[2] = { 30000, 0x04 }, + .odr_avl[3] = { 60000, 0x05 }, + .odr_avl[4] = { 120000, 0x06 }, + .odr_avl[5] = { 240000, 0x07 }, + .odr_avl[6] = { 480000, 0x08 }, + .odr_avl[7] = { 960000, 0x09 }, + .odr_len = 8, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x17, + .mask = GENMASK(1, 0), + }, + .fs_avl[0] = { IIO_G_TO_M_S_2(61000), 0x0 }, + .fs_avl[1] = { IIO_G_TO_M_S_2(122000), 0x1 }, + .fs_avl[2] = { IIO_G_TO_M_S_2(244000), 0x2 }, + .fs_avl[3] = { IIO_G_TO_M_S_2(488000), 0x3 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x15, + .mask = GENMASK(3, 0), + }, + .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750000), 0x1 }, + .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500000), 0x2 }, + .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000000), 0x3 }, + .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000000), 0x4 }, + .fs_len = 4, + }, + }, + .irq_config = { + .irq1 = { + .addr = 0x0d, + .mask = BIT(3), + }, + .irq2 = { + .addr = 0x0e, + .mask = BIT(3), + }, + .lir = { + .addr = 0x56, + .mask = BIT(0), + }, + .irq1_func = { + .addr = 0x5e, + .mask = BIT(5), + }, + .irq2_func = { + .addr = 0x5f, + .mask = BIT(5), + }, + .hla = { + .addr = 0x03, + .mask = BIT(4), + }, + .od = { + .addr = 0x03, + .mask = BIT(3), + }, + }, + .batch = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x09, + .mask = GENMASK(3, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x09, + .mask = GENMASK(7, 4), + }, + }, + .fifo_ops = { + .update_fifo = st_lsm6dsx_update_fifo, + .read_fifo = st_lsm6dsx_read_tagged_fifo, + .fifo_th = { + .addr = 0x07, + .mask = GENMASK(7, 0), + }, + .fifo_diff = { + .addr = 0x1b, + .mask = GENMASK(8, 0), + }, + .max_size = 512, + .th_wl = 1, + }, + .ts_settings = { + .timer_en = { + .addr = 0x50, + .mask = BIT(6), + }, + .decimator = { + .addr = 0x0a, + .mask = GENMASK(7, 6), + }, + .freq_fine = 0x4f, + .ts_sensitivity = 21701, + .ts_trim_coeff = 28212, + }, + .shub_settings = { + .page_mux = { + .addr = 0x01, + .mask = BIT(6), + }, + .master_en = { + .sec_page = true, + .addr = 0x14, + .mask = BIT(2), + }, + .pullup_en = { + .addr = 0x03, + .mask = BIT(6), + }, + .aux_sens = { + .addr = 0x14, + .mask = GENMASK(1, 0), + }, + .wr_once = { + .addr = 0x14, + .mask = BIT(6), + }, + .num_ext_dev = 3, + .shub_out = { + .sec_page = true, + .addr = 0x02, + }, + .slv0_addr = 0x15, + .dw_slv0_addr = 0x21, + .batch_en = BIT(3), + }, + .event_settings = { + .enable_reg = { + .addr = 0x50, + .mask = BIT(7), + }, + .wakeup_reg = { + .addr = 0x5b, + .mask = GENMASK(5, 0), + }, + .wakeup_src_reg = 0x45, + .wakeup_src_status_mask = BIT(3), + .wakeup_src_z_mask = BIT(0), + .wakeup_src_y_mask = BIT(1), + .wakeup_src_x_mask = BIT(2), + }, + }, + { + .reset = { + .addr = 0x12, + .mask = BIT(0), + }, + .boot = { + .addr = 0x12, + .mask = BIT(7), + }, + .bdu = { + .addr = 0x12, + .mask = BIT(6), + }, + .id = { + { + .hw_id = ST_LSM6DSO16IS_ID, + .name = ST_LSM6DSO16IS_DEV_NAME, + .wai = 0x22, + }, { + .hw_id = ST_ISM330IS_ID, + .name = ST_ISM330IS_DEV_NAME, + .wai = 0x22, + } + }, + .channels = { + [ST_LSM6DSX_ID_ACC] = { + .chan = st_lsm6ds0_acc_channels, + .len = ARRAY_SIZE(st_lsm6ds0_acc_channels), + }, + [ST_LSM6DSX_ID_GYRO] = { + .chan = st_lsm6dsx_gyro_channels, + .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels), + }, + }, + .odr_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(7, 4), + }, + .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 }, + .odr_len = 7, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(7, 4), + }, + .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 }, + .odr_len = 7, + }, + }, + .fs_table = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = 0x10, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = 0x11, + .mask = GENMASK(3, 2), + }, + .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 }, + .fs_len = 4, + }, + }, + .irq_config = { + .hla = { + .addr = 0x12, + .mask = BIT(5), + }, + .od = { + .addr = 0x12, + .mask = BIT(4), + }, + }, + .shub_settings = { + .page_mux = { + .addr = 0x01, + .mask = BIT(6), + }, + .master_en = { + .sec_page = true, + .addr = 0x14, + .mask = BIT(2), + }, + .pullup_en = { + .sec_page = true, + .addr = 0x14, + .mask = BIT(3), + }, + .aux_sens = { + .addr = 0x14, + .mask = GENMASK(1, 0), + }, + .wr_once = { + .addr = 0x14, + .mask = BIT(6), + }, + .num_ext_dev = 3, + .shub_out = { + .sec_page = true, + .addr = 0x02, + }, + .slv0_addr = 0x15, + .dw_slv0_addr = 0x21, + }, + }, +}; + +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + unsigned int data; + int err; + + hub_settings = &hw->settings->shub_settings; + data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask); + err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr, + hub_settings->page_mux.mask, data); + usleep_range(100, 150); + + return err; +} + +static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, + const char **name) +{ + int err, i, j, data; + + for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) { + for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) { + if (st_lsm6dsx_sensor_settings[i].id[j].name && + id == st_lsm6dsx_sensor_settings[i].id[j].hw_id) + break; + } + if (j < ST_LSM6DSX_MAX_ID) + break; + } + + if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) { + dev_err(hw->dev, "unsupported hw id [%02x]\n", id); + return -ENODEV; + } + + err = regmap_read(hw->regmap, ST_LSM6DSX_REG_WHOAMI_ADDR, &data); + if (err < 0) { + dev_err(hw->dev, "failed to read whoami register\n"); + return err; + } + + if (data != st_lsm6dsx_sensor_settings[i].id[j].wai) { + dev_err(hw->dev, "unsupported whoami [%02x]\n", data); + return -ENODEV; + } + + *name = st_lsm6dsx_sensor_settings[i].id[j].name; + hw->settings = &st_lsm6dsx_sensor_settings[i]; + + return 0; +} + +static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, + u32 gain) +{ + const struct st_lsm6dsx_fs_table_entry *fs_table; + unsigned int data; + int i, err; + + fs_table = &sensor->hw->settings->fs_table[sensor->id]; + for (i = 0; i < fs_table->fs_len; i++) { + if (fs_table->fs_avl[i].gain == gain) + break; + } + + if (i == fs_table->fs_len) + return -EINVAL; + + data = ST_LSM6DSX_SHIFT_VAL(fs_table->fs_avl[i].val, + fs_table->reg.mask); + err = st_lsm6dsx_update_bits_locked(sensor->hw, fs_table->reg.addr, + fs_table->reg.mask, data); + if (err < 0) + return err; + + sensor->gain = gain; + + return 0; +} + +int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u32 odr, u8 *val) +{ + const struct st_lsm6dsx_odr_table_entry *odr_table; + int i; + + odr_table = &sensor->hw->settings->odr_table[sensor->id]; + for (i = 0; i < odr_table->odr_len; i++) { + /* + * ext devices can run at different odr respect to + * accel sensor + */ + if (odr_table->odr_avl[i].milli_hz >= odr) + break; + } + + if (i == odr_table->odr_len) + return -EINVAL; + + *val = odr_table->odr_avl[i].val; + return odr_table->odr_avl[i].milli_hz; +} + +static int +st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u32 odr, + enum st_lsm6dsx_sensor_id id) +{ + struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]); + + if (odr > 0) { + if (hw->enable_mask & BIT(id)) + return max_t(u32, ref->odr, odr); + else + return odr; + } else { + return (hw->enable_mask & BIT(id)) ? ref->odr : 0; + } +} + +static int +st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u32 req_odr) +{ + struct st_lsm6dsx_sensor *ref_sensor = sensor; + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *reg; + unsigned int data; + u8 val = 0; + int err; + + switch (sensor->id) { + case ST_LSM6DSX_ID_GYRO: + break; + case ST_LSM6DSX_ID_EXT0: + case ST_LSM6DSX_ID_EXT1: + case ST_LSM6DSX_ID_EXT2: + case ST_LSM6DSX_ID_ACC: { + u32 odr; + int i; + + /* + * i2c embedded controller relies on the accelerometer sensor as + * bus read/write trigger so we need to enable accel device + * at odr = max(accel_odr, ext_odr) in order to properly + * communicate with i2c slave devices + */ + ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i] || i == sensor->id) + continue; + + odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i); + if (odr != req_odr) + /* device already configured */ + return 0; + } + break; + } + default: /* should never occur */ + return -EINVAL; + } + + if (req_odr > 0) { + err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val); + if (err < 0) + return err; + } + + reg = &hw->settings->odr_table[ref_sensor->id].reg; + data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); + return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data); +} + +static int +__st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + u32 odr = enable ? sensor->odr : 0; + int err; + + err = st_lsm6dsx_set_odr(sensor, odr); + if (err < 0) + return err; + + if (enable) + hw->enable_mask |= BIT(sensor->id); + else + hw->enable_mask &= ~BIT(sensor->id); + + return 0; +} + +static int +st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (sensor->id == ST_LSM6DSX_ID_GYRO || enable) + return 0; + + return hw->enable_event; +} + +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + if (st_lsm6dsx_check_events(sensor, enable)) + return 0; + + return __st_lsm6dsx_sensor_set_enable(sensor, enable); +} + +static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, + u8 addr, int *val) +{ + struct st_lsm6dsx_hw *hw = sensor->hw; + int err, delay; + __le16 data; + + err = st_lsm6dsx_sensor_set_enable(sensor, true); + if (err < 0) + return err; + + /* + * we need to wait for sensor settling time before + * reading data in order to avoid corrupted samples + */ + delay = 1000000000 / sensor->odr; + usleep_range(3 * delay, 4 * delay); + + err = st_lsm6dsx_read_locked(hw, addr, &data, sizeof(data)); + if (err < 0) + return err; + + if (!hw->enable_event) { + err = st_lsm6dsx_sensor_set_enable(sensor, false); + if (err < 0) + return err; + } + + *val = (s16)le16_to_cpu(data); + + return IIO_VAL_INT; +} + +static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val); + iio_device_release_direct(iio_dev); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->odr / 1000; + *val2 = (sensor->odr % 1000) * 1000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_NANO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int err = 0; + + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_lsm6dsx_set_full_scale(sensor, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: { + u8 data; + + val = val * 1000 + val2 / 1000; + val = st_lsm6dsx_check_odr(sensor, val, &data); + if (val < 0) { + err = val; + } else { + sensor->odr = val; + sensor->hwfifo_odr_mHz = val; + } + break; + } + default: + err = -EINVAL; + break; + } + + iio_device_release_direct(iio_dev); + + return err; +} + +static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, bool state) +{ + const struct st_lsm6dsx_reg *reg; + unsigned int data; + int err; + + if (!hw->settings->irq_config.irq1_func.addr) + return -ENOTSUPP; + + reg = &hw->settings->event_settings.enable_reg; + if (reg->addr) { + data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, + reg->mask, data); + if (err < 0) + return err; + } + + /* Enable wakeup interrupt */ + data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask); + return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr, + hw->irq_routing->mask, data); +} + +static int st_lsm6dsx_read_event(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + *val2 = 0; + *val = hw->event_threshold; + + return IIO_VAL_INT; +} + +static int +st_lsm6dsx_write_event(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *reg; + unsigned int data; + int err; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (val < 0 || val > 31) + return -EINVAL; + + reg = &hw->settings->event_settings.wakeup_reg; + data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, + reg->mask, data); + if (err < 0) + return -EINVAL; + + hw->event_threshold = val; + + return 0; +} + +static int +st_lsm6dsx_read_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + return !!(hw->enable_event & BIT(chan->channel2)); +} + +static int +st_lsm6dsx_write_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + u8 enable_event; + int err; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (state) { + enable_event = hw->enable_event | BIT(chan->channel2); + + /* do not enable events if they are already enabled */ + if (hw->enable_event) + goto out; + } else { + enable_event = hw->enable_event & ~BIT(chan->channel2); + + /* only turn off sensor if no events is enabled */ + if (enable_event) + goto out; + } + + /* stop here if no changes have been made */ + if (hw->enable_event == enable_event) + return 0; + + err = st_lsm6dsx_event_setup(hw, state); + if (err < 0) + return err; + + mutex_lock(&hw->conf_lock); + if (enable_event || !(hw->fifo_mask & BIT(sensor->id))) + err = __st_lsm6dsx_sensor_set_enable(sensor, state); + mutex_unlock(&hw->conf_lock); + if (err < 0) + return err; + +out: + hw->enable_event = enable_event; + + return 0; +} + +int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + int err; + + val = clamp_val(val, 1, hw->settings->fifo_ops.max_size); + + mutex_lock(&hw->conf_lock); + + err = st_lsm6dsx_update_watermark(sensor, val); + + mutex_unlock(&hw->conf_lock); + + if (err < 0) + return err; + + sensor->watermark = val; + + return 0; +} + +static ssize_t +st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + const struct st_lsm6dsx_odr_table_entry *odr_table; + int i, len = 0; + + odr_table = &sensor->hw->settings->odr_table[sensor->id]; + for (i = 0; i < odr_table->odr_len; i++) + len += sysfs_emit_at(buf, len, "%d.%03d%c", + odr_table->odr_avl[i].milli_hz / 1000, + odr_table->odr_avl[i].milli_hz % 1000, + (i == odr_table->odr_len - 1) ? '\n' : ' '); + + return len; +} + +static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_to_iio_dev(dev)); + const struct st_lsm6dsx_fs_table_entry *fs_table; + struct st_lsm6dsx_hw *hw = sensor->hw; + int i, len = 0; + + fs_table = &hw->settings->fs_table[sensor->id]; + for (i = 0; i < fs_table->fs_len; i++) + len += sysfs_emit_at(buf, len, "0.%09u%c", + fs_table->fs_avl[i].gain, + (i == fs_table->fs_len - 1) ? '\n' : ' '); + + return len; +} + +static int st_lsm6dsx_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; + default: + return IIO_VAL_INT_PLUS_MICRO; + } + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail); +static IIO_DEVICE_ATTR(in_accel_scale_available, 0444, + st_lsm6dsx_sysfs_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444, + st_lsm6dsx_sysfs_scale_avail, NULL, 0); + +static struct attribute *st_lsm6dsx_acc_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_acc_attribute_group = { + .attrs = st_lsm6dsx_acc_attributes, +}; + +static const struct iio_info st_lsm6dsx_acc_info = { + .attrs = &st_lsm6dsx_acc_attribute_group, + .read_raw = st_lsm6dsx_read_raw, + .write_raw = st_lsm6dsx_write_raw, + .read_event_value = st_lsm6dsx_read_event, + .write_event_value = st_lsm6dsx_write_event, + .read_event_config = st_lsm6dsx_read_event_config, + .write_event_config = st_lsm6dsx_write_event_config, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, + .write_raw_get_fmt = st_lsm6dsx_write_raw_get_fmt, +}; + +static struct attribute *st_lsm6dsx_gyro_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_gyro_attribute_group = { + .attrs = st_lsm6dsx_gyro_attributes, +}; + +static const struct iio_info st_lsm6dsx_gyro_info = { + .attrs = &st_lsm6dsx_gyro_attribute_group, + .read_raw = st_lsm6dsx_read_raw, + .write_raw = st_lsm6dsx_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, + .write_raw_get_fmt = st_lsm6dsx_write_raw_get_fmt, +}; + +static int +st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, + const struct st_lsm6dsx_reg **drdy_reg) +{ + struct device *dev = hw->dev; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); + int err = 0, drdy_pin; + + if (device_property_read_u32(dev, "st,drdy-int-pin", &drdy_pin) < 0) + drdy_pin = pdata ? pdata->drdy_int_pin : 1; + + switch (drdy_pin) { + case 1: + hw->irq_routing = &hw->settings->irq_config.irq1_func; + *drdy_reg = &hw->settings->irq_config.irq1; + break; + case 2: + hw->irq_routing = &hw->settings->irq_config.irq2_func; + *drdy_reg = &hw->settings->irq_config.irq2; + break; + default: + dev_err(hw->dev, "unsupported data ready pin\n"); + err = -EINVAL; + break; + } + + return err; +} + +static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct device *dev = hw->dev; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); + unsigned int data; + int err = 0; + + hub_settings = &hw->settings->shub_settings; + + if (device_property_read_bool(dev, "st,pullups") || + (pdata && pdata->pullups)) { + if (hub_settings->pullup_en.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + return err; + } + + data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask); + err = regmap_update_bits(hw->regmap, + hub_settings->pullup_en.addr, + hub_settings->pullup_en.mask, data); + + if (hub_settings->pullup_en.sec_page) + st_lsm6dsx_set_page(hw, false); + + if (err < 0) + return err; + } + + if (hub_settings->aux_sens.addr) { + /* configure aux sensors */ + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + return err; + + data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask); + err = regmap_update_bits(hw->regmap, + hub_settings->aux_sens.addr, + hub_settings->aux_sens.mask, data); + + st_lsm6dsx_set_page(hw, false); + + if (err < 0) + return err; + } + + if (hub_settings->emb_func.addr) { + data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->emb_func.mask); + err = regmap_update_bits(hw->regmap, + hub_settings->emb_func.addr, + hub_settings->emb_func.mask, data); + } + + return err; +} + +static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_hw_ts_settings *ts_settings; + int err, val; + + ts_settings = &hw->settings->ts_settings; + /* enable hw timestamp generation if necessary */ + if (ts_settings->timer_en.addr) { + val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask); + err = regmap_update_bits(hw->regmap, + ts_settings->timer_en.addr, + ts_settings->timer_en.mask, val); + if (err < 0) + return err; + } + + /* enable high resolution for hw ts timer if necessary */ + if (ts_settings->hr_timer.addr) { + val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask); + err = regmap_update_bits(hw->regmap, + ts_settings->hr_timer.addr, + ts_settings->hr_timer.mask, val); + if (err < 0) + return err; + } + + /* enable ts queueing in FIFO if necessary */ + if (ts_settings->fifo_en.addr) { + val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask); + err = regmap_update_bits(hw->regmap, + ts_settings->fifo_en.addr, + ts_settings->fifo_en.mask, val); + if (err < 0) + return err; + } + + /* calibrate timestamp sensitivity */ + hw->ts_gain = ts_settings->ts_sensitivity; + if (ts_settings->freq_fine) { + err = regmap_read(hw->regmap, ts_settings->freq_fine, &val); + if (err < 0) + return err; + + hw->ts_gain -= ((s8)val * ts_settings->ts_trim_coeff) / 1000; + } + + return 0; +} + +static int st_lsm6dsx_reset_device(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_reg *reg; + int err; + + /* + * flush hw FIFO before device reset in order to avoid + * possible races on interrupt line 1. If the first interrupt + * line is asserted during hw reset the device will work in + * I3C-only mode (if it is supported) + */ + err = st_lsm6dsx_flush_fifo(hw); + if (err < 0 && err != -ENOTSUPP) + return err; + + /* device sw reset */ + reg = &hw->settings->reset; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + msleep(50); + + /* reload trimming parameter */ + reg = &hw->settings->boot; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + msleep(50); + + return 0; +} + +static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_reg *reg; + int err; + + err = st_lsm6dsx_reset_device(hw); + if (err < 0) + return err; + + /* enable Block Data Update */ + reg = &hw->settings->bdu; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + /* enable FIFO watermak interrupt */ + err = st_lsm6dsx_get_drdy_reg(hw, ®); + if (err < 0) + return err; + + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + /* enable Latched interrupts for device events */ + if (hw->settings->irq_config.lir.addr) { + reg = &hw->settings->irq_config.lir; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + /* enable clear on read for latched interrupts */ + if (hw->settings->irq_config.clear_on_read.addr) { + reg = &hw->settings->irq_config.clear_on_read; + err = regmap_update_bits(hw->regmap, + reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + } + } + + /* enable drdy-mas if available */ + if (hw->settings->drdy_mask.addr) { + reg = &hw->settings->drdy_mask; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + } + + err = st_lsm6dsx_init_shub(hw); + if (err < 0) + return err; + + return st_lsm6dsx_init_hw_timer(hw); +} + +static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_sensor_id id, + const char *name) +{ + struct st_lsm6dsx_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->available_scan_masks = st_lsm6dsx_available_scan_masks; + iio_dev->channels = hw->settings->channels[id].chan; + iio_dev->num_channels = hw->settings->channels[id].len; + + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + sensor->odr = hw->settings->odr_table[id].odr_avl[0].milli_hz; + sensor->hwfifo_odr_mHz = sensor->odr; + sensor->gain = hw->settings->fs_table[id].fs_avl[0].gain; + sensor->watermark = 1; + + switch (id) { + case ST_LSM6DSX_ID_ACC: + iio_dev->info = &st_lsm6dsx_acc_info; + scnprintf(sensor->name, sizeof(sensor->name), "%s_accel", + name); + break; + case ST_LSM6DSX_ID_GYRO: + iio_dev->info = &st_lsm6dsx_gyro_info; + scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro", + name); + break; + default: + return NULL; + } + iio_dev->name = sensor->name; + + return iio_dev; +} + +static bool +st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_event_settings *event_settings; + int err, data; + s64 timestamp; + + if (!hw->enable_event) + return false; + + event_settings = &hw->settings->event_settings; + err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg, + &data, sizeof(data)); + if (err < 0) + return false; + + timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + if ((data & hw->settings->event_settings.wakeup_src_z_mask) && + (hw->enable_event & BIT(IIO_MOD_Z))) + iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + + if ((data & hw->settings->event_settings.wakeup_src_y_mask) && + (hw->enable_event & BIT(IIO_MOD_Y))) + iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + + if ((data & hw->settings->event_settings.wakeup_src_x_mask) && + (hw->enable_event & BIT(IIO_MOD_X))) + iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC], + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + + return data & event_settings->wakeup_src_status_mask; +} + +static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) +{ + struct st_lsm6dsx_hw *hw = private; + int fifo_len = 0, len; + bool event; + + event = st_lsm6dsx_report_motion_event(hw); + + if (!hw->settings->fifo_ops.read_fifo) + return event ? IRQ_HANDLED : IRQ_NONE; + + /* + * If we are using edge IRQs, new samples can arrive while + * processing current interrupt since there are no hw + * guarantees the irq line stays "low" long enough to properly + * detect the new interrupt. In this case the new sample will + * be missed. + * Polling FIFO status register allow us to read new + * samples even if the interrupt arrives while processing + * previous data and the timeslot where the line is "low" is + * too short to be properly detected. + */ + do { + mutex_lock(&hw->fifo_lock); + len = hw->settings->fifo_ops.read_fifo(hw); + mutex_unlock(&hw->fifo_lock); + + if (len > 0) + fifo_len += len; + } while (len > 0); + + return fifo_len || event ? IRQ_HANDLED : IRQ_NONE; +} + +static irqreturn_t st_lsm6dsx_sw_trigger_handler_thread(int irq, + void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *iio_dev = pf->indio_dev; + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + + if (sensor->id == ST_LSM6DSX_ID_EXT0 || + sensor->id == ST_LSM6DSX_ID_EXT1 || + sensor->id == ST_LSM6DSX_ID_EXT2) + st_lsm6dsx_shub_read_output(hw, + (u8 *)hw->scan[sensor->id].channels, + sizeof(hw->scan[sensor->id].channels)); + else + st_lsm6dsx_read_locked(hw, iio_dev->channels[0].address, + hw->scan[sensor->id].channels, + sizeof(hw->scan[sensor->id].channels)); + + iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan[sensor->id], + iio_get_time_ns(iio_dev)); + iio_trigger_notify_done(iio_dev->trig); + + return IRQ_HANDLED; +} + +static int st_lsm6dsx_irq_setup(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_reg *reg; + struct device *dev = hw->dev; + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); + unsigned long irq_type; + bool irq_active_low; + int err; + + irq_type = irq_get_trigger_type(hw->irq); + switch (irq_type) { + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + irq_active_low = false; + break; + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_FALLING: + irq_active_low = true; + break; + default: + dev_info(hw->dev, "mode %lx unsupported\n", irq_type); + return -EINVAL; + } + + reg = &hw->settings->irq_config.hla; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(irq_active_low, + reg->mask)); + if (err < 0) + return err; + + if (device_property_read_bool(dev, "drive-open-drain") || + (pdata && pdata->open_drain)) { + reg = &hw->settings->irq_config.od; + err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, + ST_LSM6DSX_SHIFT_VAL(1, reg->mask)); + if (err < 0) + return err; + + irq_type |= IRQF_SHARED; + } + + err = devm_request_threaded_irq(hw->dev, hw->irq, + NULL, + st_lsm6dsx_handler_thread, + irq_type | IRQF_ONESHOT, + "lsm6dsx", hw); + if (err) { + dev_err(hw->dev, "failed to request trigger irq %d\n", + hw->irq); + return err; + } + + return 0; +} + +static int st_lsm6dsx_sw_buffer_preenable(struct iio_dev *iio_dev) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + + return st_lsm6dsx_device_set_enable(sensor, true); +} + +static int st_lsm6dsx_sw_buffer_postdisable(struct iio_dev *iio_dev) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + + return st_lsm6dsx_device_set_enable(sensor, false); +} + +static const struct iio_buffer_setup_ops st_lsm6dsx_sw_buffer_ops = { + .preenable = st_lsm6dsx_sw_buffer_preenable, + .postdisable = st_lsm6dsx_sw_buffer_postdisable, +}; + +static int st_lsm6dsx_sw_buffers_setup(struct st_lsm6dsx_hw *hw) +{ + int i; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + int err; + + if (!hw->iio_devs[i]) + continue; + + err = devm_iio_triggered_buffer_setup(hw->dev, + hw->iio_devs[i], NULL, + st_lsm6dsx_sw_trigger_handler_thread, + &st_lsm6dsx_sw_buffer_ops); + if (err) + return err; + } + + return 0; +} + +static int st_lsm6dsx_init_regulators(struct device *dev) +{ + /* vdd-vddio power regulators */ + static const char * const regulators[] = { "vdd", "vddio" }; + int err; + + err = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), + regulators); + if (err) + return dev_err_probe(dev, err, "failed to enable regulators\n"); + + msleep(50); + + return 0; +} + +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, + struct regmap *regmap) +{ + const struct st_sensors_platform_data *pdata = dev_get_platdata(dev); + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_hw *hw; + const char *name = NULL; + int i, err; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + dev_set_drvdata(dev, hw); + + mutex_init(&hw->fifo_lock); + mutex_init(&hw->conf_lock); + mutex_init(&hw->page_lock); + + err = st_lsm6dsx_init_regulators(dev); + if (err) + return err; + + hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL); + if (!hw->buff) + return -ENOMEM; + + hw->dev = dev; + hw->irq = irq; + hw->regmap = regmap; + + err = st_lsm6dsx_check_whoami(hw, hw_id, &name); + if (err < 0) + return err; + + for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) { + hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name); + if (!hw->iio_devs[i]) + return -ENOMEM; + } + + err = st_lsm6dsx_init_device(hw); + if (err < 0) + return err; + + hub_settings = &hw->settings->shub_settings; + if (hub_settings->master_en.addr && + !device_property_read_bool(dev, "st,disable-sensor-hub")) { + err = st_lsm6dsx_shub_probe(hw, name); + if (err < 0) + return err; + } + + if (hw->irq > 0) { + err = st_lsm6dsx_irq_setup(hw); + if (err < 0) + return err; + + err = st_lsm6dsx_fifo_setup(hw); + if (err < 0) + return err; + } + + if (!hw->irq || !hw->settings->fifo_ops.read_fifo) { + /* + * Rely on sw triggers (e.g. hr-timers) if irq pin is not + * connected of if the device does not support HW FIFO + */ + err = st_lsm6dsx_sw_buffers_setup(hw); + if (err) + return err; + } + + if (!iio_read_acpi_mount_matrix(hw->dev, &hw->orientation, "ROTM")) { + err = iio_read_mount_matrix(hw->dev, &hw->orientation); + if (err) + return err; + } + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + err = devm_iio_device_register(hw->dev, hw->iio_devs[i]); + if (err) + return err; + } + + if (device_property_read_bool(dev, "wakeup-source") || + (pdata && pdata->wakeup_source)) { + err = devm_device_init_wakeup(dev); + if (err) + return dev_err_probe(dev, err, "Failed to init wakeup\n"); + } + + return 0; +} +EXPORT_SYMBOL_NS(st_lsm6dsx_probe, "IIO_LSM6DSX"); + +static int st_lsm6dsx_suspend(struct device *dev) +{ + struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev); + struct st_lsm6dsx_sensor *sensor; + int i, err = 0; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + if (device_may_wakeup(dev) && + sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) { + /* Enable wake from IRQ */ + enable_irq_wake(hw->irq); + continue; + } + + err = st_lsm6dsx_device_set_enable(sensor, false); + if (err < 0) + return err; + + hw->suspend_mask |= BIT(sensor->id); + } + + if (hw->fifo_mask) + err = st_lsm6dsx_flush_fifo(hw); + + return err; +} + +static int st_lsm6dsx_resume(struct device *dev) +{ + struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev); + struct st_lsm6dsx_sensor *sensor; + int i, err = 0; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + + sensor = iio_priv(hw->iio_devs[i]); + if (device_may_wakeup(dev) && + sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) + disable_irq_wake(hw->irq); + + if (!(hw->suspend_mask & BIT(sensor->id))) + continue; + + err = st_lsm6dsx_device_set_enable(sensor, true); + if (err < 0) + return err; + + hw->suspend_mask &= ~BIT(sensor->id); + } + + if (hw->fifo_mask) + err = st_lsm6dsx_resume_fifo(hw); + + return err; +} + +EXPORT_NS_SIMPLE_DEV_PM_OPS(st_lsm6dsx_pm_ops, st_lsm6dsx_suspend, + st_lsm6dsx_resume, IIO_LSM6DSX); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver"); +MODULE_LICENSE("GPL v2"); diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_i2c.c b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_i2c.c new file mode 100644 index 0000000..b2a7c2e --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_i2c.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics st_lsm6dsx i2c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + */ + +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +static const struct regmap_config st_lsm6dsx_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int st_lsm6dsx_i2c_probe(struct i2c_client *client) +{ + int hw_id; + struct regmap *regmap; + + hw_id = (kernel_ulong_t)device_get_match_data(&client->dev); + if (!hw_id) + hw_id = i2c_client_get_device_id(client)->driver_data; + if (!hw_id) + return -EINVAL; + + regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return st_lsm6dsx_probe(&client->dev, client->irq, hw_id, regmap); +} + +static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { + { + .compatible = "st,lsm6ds3", + .data = (void *)ST_LSM6DS3_ID, + }, + { + .compatible = "st,lsm6ds3h", + .data = (void *)ST_LSM6DS3H_ID, + }, + { + .compatible = "st,lsm6dsl", + .data = (void *)ST_LSM6DSL_ID, + }, + { + .compatible = "st,lsm6dsm", + .data = (void *)ST_LSM6DSM_ID, + }, + { + .compatible = "st,ism330dlc", + .data = (void *)ST_ISM330DLC_ID, + }, + { + .compatible = "st,lsm6dso", + .data = (void *)ST_LSM6DSO_ID, + }, + { + .compatible = "st,asm330lhh", + .data = (void *)ST_ASM330LHH_ID, + }, + { + .compatible = "st,lsm6dsox", + .data = (void *)ST_LSM6DSOX_ID, + }, + { + .compatible = "st,lsm6dsr", + .data = (void *)ST_LSM6DSR_ID, + }, + { + .compatible = "st,lsm6ds3tr-c", + .data = (void *)ST_LSM6DS3TRC_ID, + }, + { + .compatible = "st,ism330dhcx", + .data = (void *)ST_ISM330DHCX_ID, + }, + { + .compatible = "st,lsm9ds1-imu", + .data = (void *)ST_LSM9DS1_ID, + }, + { + .compatible = "st,lsm6ds0", + .data = (void *)ST_LSM6DS0_ID, + }, + { + .compatible = "st,lsm6dsrx", + .data = (void *)ST_LSM6DSRX_ID, + }, + { + .compatible = "st,lsm6dst", + .data = (void *)ST_LSM6DST_ID, + }, + { + .compatible = "st,lsm6dsop", + .data = (void *)ST_LSM6DSOP_ID, + }, + { + .compatible = "st,asm330lhhx", + .data = (void *)ST_ASM330LHHX_ID, + }, + { + .compatible = "st,lsm6dstx", + .data = (void *)ST_LSM6DSTX_ID, + }, + { + .compatible = "st,lsm6dsv", + .data = (void *)ST_LSM6DSV_ID, + }, + { + .compatible = "st,lsm6dsv16x", + .data = (void *)ST_LSM6DSV16X_ID, + }, + { + .compatible = "st,lsm6dso16is", + .data = (void *)ST_LSM6DSO16IS_ID, + }, + { + .compatible = "st,ism330is", + .data = (void *)ST_ISM330IS_ID, + }, + { + .compatible = "st,asm330lhb", + .data = (void *)ST_ASM330LHB_ID, + }, + { + .compatible = "st,asm330lhhxg1", + .data = (void *)ST_ASM330LHHXG1_ID, + }, + { } +}; +MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); + +static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { + { "SMO8B30", ST_LSM6DS3TRC_ID, }, + { "SMOCF00", ST_LSM6DSO_ID, }, + { } +}; +MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); + +static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { + { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, + { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, + { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, + { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, + { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, + { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID }, + { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID }, + { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID }, + { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID }, + { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID }, + { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID }, + { ST_LSM6DS0_DEV_NAME, ST_LSM6DS0_ID }, + { ST_LSM6DSRX_DEV_NAME, ST_LSM6DSRX_ID }, + { ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID }, + { ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID }, + { ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID }, + { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID }, + { ST_LSM6DSV_DEV_NAME, ST_LSM6DSV_ID }, + { ST_LSM6DSV16X_DEV_NAME, ST_LSM6DSV16X_ID }, + { ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID }, + { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, + { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, + { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, + { } +}; +MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); + +static struct i2c_driver st_lsm6dsx_driver = { + .driver = { + .name = "st_lsm6dsx_i2c", + .pm = pm_sleep_ptr(&st_lsm6dsx_pm_ops), + .of_match_table = st_lsm6dsx_i2c_of_match, + .acpi_match_table = st_lsm6dsx_i2c_acpi_match, + }, + .probe = st_lsm6dsx_i2c_probe, + .id_table = st_lsm6dsx_i2c_id_table, +}; +module_i2c_driver(st_lsm6dsx_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver"); +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_LSM6DSX"); diff --git a/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_shub.c b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_shub.c new file mode 100644 index 0000000..d6a1eeb --- /dev/null +++ b/usr/src/st-lsm6dsx-shift13mi-1.1/st_lsm6dsx_shub.c @@ -0,0 +1,922 @@ +/* + * STMicroelectronics st_lsm6dsx i2c controller driver + * + * i2c controller embedded in lsm6dx series can connect up to four + * slave devices using accelerometer sensor as trigger for i2c + * read/write operations. Current implementation relies on SLV0 channel + * for slave configuration and SLV{1,2,3} to read data and push them into + * the hw FIFO + * + * Copyright (C) 2018 Lorenzo Bianconi + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3) +#define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3) +#define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3) + +#define ST_LS6DSX_READ_OP_MASK GENMASK(2, 0) + +static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { + /* LIS2MDL */ + { + .i2c_addr = { 0x1e }, + .wai = { + .addr = 0x4f, + .val = 0x40, + }, + .id = ST_LSM6DSX_ID_MAGN, + .odr_table = { + .reg = { + .addr = 0x60, + .mask = GENMASK(3, 2), + }, + .odr_avl[0] = { 10000, 0x0 }, + .odr_avl[1] = { 20000, 0x1 }, + .odr_avl[2] = { 50000, 0x2 }, + .odr_avl[3] = { 100000, 0x3 }, + .odr_len = 4, + }, + .fs_table = { + .fs_avl[0] = { + .gain = 1500, + .val = 0x0, + }, /* 1500 uG/LSB */ + .fs_len = 1, + }, + .temp_comp = { + .addr = 0x60, + .mask = BIT(7), + }, + .pwr_table = { + .reg = { + .addr = 0x60, + .mask = GENMASK(1, 0), + }, + .off_val = 0x2, + .on_val = 0x0, + }, + .off_canc = { + .addr = 0x61, + .mask = BIT(1), + }, + .bdu = { + .addr = 0x62, + .mask = BIT(4), + }, + .out = { + .addr = 0x68, + .len = 6, + }, + }, + /* LIS3MDL */ + { + .i2c_addr = { 0x1e }, + .wai = { + .addr = 0x0f, + .val = 0x3d, + }, + .id = ST_LSM6DSX_ID_MAGN, + .odr_table = { + .reg = { + .addr = 0x20, + .mask = GENMASK(4, 2), + }, + .odr_avl[0] = { 1000, 0x0 }, + .odr_avl[1] = { 2000, 0x1 }, + .odr_avl[2] = { 3000, 0x2 }, + .odr_avl[3] = { 5000, 0x3 }, + .odr_avl[4] = { 10000, 0x4 }, + .odr_avl[5] = { 20000, 0x5 }, + .odr_avl[6] = { 40000, 0x6 }, + .odr_avl[7] = { 80000, 0x7 }, + .odr_len = 8, + }, + .fs_table = { + .reg = { + .addr = 0x21, + .mask = GENMASK(6, 5), + }, + .fs_avl[0] = { + .gain = 146, + .val = 0x00, + }, /* 4000 uG/LSB */ + .fs_avl[1] = { + .gain = 292, + .val = 0x01, + }, /* 8000 uG/LSB */ + .fs_avl[2] = { + .gain = 438, + .val = 0x02, + }, /* 12000 uG/LSB */ + .fs_avl[3] = { + .gain = 584, + .val = 0x03, + }, /* 16000 uG/LSB */ + .fs_len = 4, + }, + .pwr_table = { + .reg = { + .addr = 0x22, + .mask = GENMASK(1, 0), + }, + .off_val = 0x2, + .on_val = 0x0, + }, + .bdu = { + .addr = 0x24, + .mask = BIT(6), + }, + .out = { + .addr = 0x28, + .len = 6, + }, + }, +}; + +static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *sensor; + u32 odr, timeout; + + sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + odr = (hw->enable_mask & BIT(ST_LSM6DSX_ID_ACC)) ? sensor->odr : 12500; + /* set 10ms as minimum timeout for i2c slave configuration */ + timeout = max_t(u32, 2000000U / odr + 1, 10); + msleep(timeout); +} + +/* + * st_lsm6dsx_shub_read_output - read i2c controller register + * + * Read st_lsm6dsx i2c controller register + */ +int st_lsm6dsx_shub_read_output(struct st_lsm6dsx_hw *hw, u8 *data, int len) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + int err; + + mutex_lock(&hw->page_lock); + + hub_settings = &hw->settings->shub_settings; + if (hub_settings->shub_out.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + } + + err = regmap_bulk_read(hw->regmap, hub_settings->shub_out.addr, + data, len); + + if (hub_settings->shub_out.sec_page) + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/* + * st_lsm6dsx_shub_write_reg - write i2c controller register + * + * Write st_lsm6dsx i2c controller register + */ +static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr, + u8 *data, int len) +{ + int err; + + mutex_lock(&hw->page_lock); + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + + err = regmap_bulk_write(hw->regmap, addr, data, len); + + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +static int +st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, + u8 mask, u8 val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + + err = regmap_update_bits(hw->regmap, addr, mask, val); + + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_hw *hw = sensor->hw; + unsigned int data; + int err; + + /* enable acc sensor as trigger */ + err = st_lsm6dsx_sensor_set_enable(sensor, enable); + if (err < 0) + return err; + + mutex_lock(&hw->page_lock); + + hub_settings = &hw->settings->shub_settings; + if (hub_settings->master_en.sec_page) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + } + + data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask); + err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr, + hub_settings->master_en.mask, data); + + if (hub_settings->master_en.sec_page) + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/* + * st_lsm6dsx_shub_read - read data from slave device register + * + * Read data from slave device register. SLV0 is used for + * one-shot read operation + */ +static int +st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, + u8 *data, int len) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + u8 config[3], slv_addr, slv_config = 0; + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *aux_sens; + int err; + + hub_settings = &hw->settings->shub_settings; + slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + aux_sens = &hw->settings->shub_settings.aux_sens; + /* do not overwrite aux_sens */ + if (slv_addr + 2 == aux_sens->addr) + slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); + + config[0] = (sensor->ext_info.addr << 1) | 1; + config[1] = addr; + config[2] = (len & ST_LS6DSX_READ_OP_MASK) | slv_config; + + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_lsm6dsx_shub_wait_complete(hw); + + err = st_lsm6dsx_shub_read_output(hw, data, + len & ST_LS6DSX_READ_OP_MASK); + if (err < 0) + return err; + + st_lsm6dsx_shub_master_enable(sensor, false); + + config[0] = hub_settings->pause; + config[1] = 0; + config[2] = slv_config; + return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); +} + +/* + * st_lsm6dsx_shub_write - write data to slave device register + * + * Write data from slave device register. SLV0 is used for + * one-shot write operation + */ +static int +st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr, + u8 *data, int len) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_hw *hw = sensor->hw; + u8 config[2], slv_addr; + int err, i; + + hub_settings = &hw->settings->shub_settings; + if (hub_settings->wr_once.addr) { + unsigned int data; + + data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask); + err = st_lsm6dsx_shub_write_reg_with_mask(hw, + hub_settings->wr_once.addr, + hub_settings->wr_once.mask, + data); + if (err < 0) + return err; + } + + slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + config[0] = sensor->ext_info.addr << 1; + for (i = 0 ; i < len; i++) { + config[1] = addr + i; + + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr, + &data[i], 1); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_lsm6dsx_shub_wait_complete(hw); + + st_lsm6dsx_shub_master_enable(sensor, false); + } + + config[0] = hub_settings->pause; + config[1] = 0; + return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); +} + +static int +st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor, + u8 addr, u8 mask, u8 val) +{ + int err; + u8 data; + + err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data)); + if (err < 0) + return err; + + data = ((data & ~mask) | (val << __ffs(mask) & mask)); + + return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data)); +} + +static int +st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor, + u32 odr, u16 *val) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + int i; + + settings = sensor->ext_info.settings; + for (i = 0; i < settings->odr_table.odr_len; i++) { + if (settings->odr_table.odr_avl[i].milli_hz == odr) + break; + } + + if (i == settings->odr_table.odr_len) + return -EINVAL; + + *val = settings->odr_table.odr_avl[i].val; + return 0; +} + +static int +st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u32 odr) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + u16 val; + int err; + + err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val); + if (err < 0) + return err; + + settings = sensor->ext_info.settings; + return st_lsm6dsx_shub_write_with_mask(sensor, + settings->odr_table.reg.addr, + settings->odr_table.reg.mask, + val); +} + +/* use SLV{1,2,3} for FIFO read operations */ +static int +st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + const struct st_lsm6dsx_ext_dev_settings *settings; + u8 config[9] = {}, enable_mask, slv_addr; + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *cur_sensor; + int i, j = 0; + + hub_settings = &hw->settings->shub_settings; + if (enable) + enable_mask = hw->enable_mask | BIT(sensor->id); + else + enable_mask = hw->enable_mask & ~BIT(sensor->id); + + for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) { + if (!hw->iio_devs[i]) + continue; + + cur_sensor = iio_priv(hw->iio_devs[i]); + if (!(enable_mask & BIT(cur_sensor->id))) + continue; + + settings = cur_sensor->ext_info.settings; + config[j] = (sensor->ext_info.addr << 1) | 1; + config[j + 1] = settings->out.addr; + config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) | + hub_settings->batch_en; + j += 3; + } + + slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr); + return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); +} + +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + int err; + + err = st_lsm6dsx_shub_config_channels(sensor, enable); + if (err < 0) + return err; + + settings = sensor->ext_info.settings; + if (enable) { + err = st_lsm6dsx_shub_set_odr(sensor, + sensor->ext_info.slv_odr); + if (err < 0) + return err; + } else { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->odr_table.reg.addr, + settings->odr_table.reg.mask, 0); + if (err < 0) + return err; + } + + if (settings->pwr_table.reg.addr) { + u8 val; + + val = enable ? settings->pwr_table.on_val + : settings->pwr_table.off_val; + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->pwr_table.reg.addr, + settings->pwr_table.reg.mask, val); + if (err < 0) + return err; + } + + return st_lsm6dsx_shub_master_enable(sensor, enable); +} + +static int +st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor, + struct iio_chan_spec const *ch, + int *val) +{ + int err, delay, len; + u8 data[4]; + + err = st_lsm6dsx_shub_set_enable(sensor, true); + if (err < 0) + return err; + + delay = 1000000000 / sensor->ext_info.slv_odr; + usleep_range(delay, 2 * delay); + + len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3); + err = st_lsm6dsx_shub_read(sensor, ch->address, data, len); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_set_enable(sensor, false); + if (err < 0) + return err; + + switch (len) { + case 2: + *val = (s16)le16_to_cpu(*((__le16 *)data)); + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + +static int +st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val); + iio_device_release_direct(iio_dev); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->ext_info.slv_odr / 1000; + *val2 = (sensor->ext_info.slv_odr % 1000) * 1000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int +st_lsm6dsx_shub_set_full_scale(struct st_lsm6dsx_sensor *sensor, + u32 gain) +{ + const struct st_lsm6dsx_fs_table_entry *fs_table; + int i, err; + + fs_table = &sensor->ext_info.settings->fs_table; + if (!fs_table->reg.addr) + return -ENOTSUPP; + + for (i = 0; i < fs_table->fs_len; i++) { + if (fs_table->fs_avl[i].gain == gain) + break; + } + + if (i == fs_table->fs_len) + return -EINVAL; + + err = st_lsm6dsx_shub_write_with_mask(sensor, fs_table->reg.addr, + fs_table->reg.mask, + fs_table->fs_avl[i].val); + if (err < 0) + return err; + + sensor->gain = gain; + + return 0; +} + +static int +__st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int err; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *ref_sensor; + u8 odr_val; + u16 data; + int odr; + + val = val * 1000 + val2 / 1000; + err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data); + if (err) + return err; + + ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + odr = st_lsm6dsx_check_odr(ref_sensor, val, &odr_val); + if (odr < 0) + return odr; + + sensor->ext_info.slv_odr = val; + sensor->odr = odr; + sensor->hwfifo_odr_mHz = odr; + return 0; + } + case IIO_CHAN_INFO_SCALE: + return st_lsm6dsx_shub_set_full_scale(sensor, val2); + default: + return -EINVAL; + } +} + +static int +st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + ret = __st_lsm6dsx_shub_write_raw(iio_dev, chan, val, val2, mask); + + iio_device_release_direct(iio_dev); + + return ret; +} + +static ssize_t +st_lsm6dsx_shub_sampling_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + const struct st_lsm6dsx_ext_dev_settings *settings; + int i, len = 0; + + settings = sensor->ext_info.settings; + for (i = 0; i < settings->odr_table.odr_len; i++) { + u32 val = settings->odr_table.odr_avl[i].milli_hz; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ", + val / 1000, val % 1000); + } + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + const struct st_lsm6dsx_ext_dev_settings *settings; + int i, len = 0; + + settings = sensor->ext_info.settings; + for (i = 0; i < settings->fs_table.fs_len; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + settings->fs_table.fs_avl[i].gain); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail); +static IIO_DEVICE_ATTR(in_scale_available, 0444, + st_lsm6dsx_shub_scale_avail, NULL, 0); +static struct attribute *st_lsm6dsx_shub_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_shub_attribute_group = { + .attrs = st_lsm6dsx_shub_attributes, +}; + +static const struct iio_info st_lsm6dsx_shub_info = { + .attrs = &st_lsm6dsx_shub_attribute_group, + .read_raw = st_lsm6dsx_shub_read_raw, + .write_raw = st_lsm6dsx_shub_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +static struct iio_dev * +st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_sensor_id id, + const struct st_lsm6dsx_ext_dev_settings *info, + u8 i2c_addr, const char *name) +{ + enum st_lsm6dsx_sensor_id ref_id = ST_LSM6DSX_ID_ACC; + struct iio_chan_spec *ext_channels; + struct st_lsm6dsx_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->info = &st_lsm6dsx_shub_info; + + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + sensor->odr = hw->settings->odr_table[ref_id].odr_avl[0].milli_hz; + sensor->hwfifo_odr_mHz = sensor->odr; + sensor->ext_info.slv_odr = info->odr_table.odr_avl[0].milli_hz; + sensor->gain = info->fs_table.fs_avl[0].gain; + sensor->ext_info.settings = info; + sensor->ext_info.addr = i2c_addr; + sensor->watermark = 1; + + switch (info->id) { + case ST_LSM6DSX_ID_MAGN: { + const struct iio_chan_spec magn_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr, + IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2, + IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4, + IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), + }; + + ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels), + GFP_KERNEL); + if (!ext_channels) + return NULL; + + memcpy(ext_channels, magn_channels, sizeof(magn_channels)); + iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks; + iio_dev->channels = ext_channels; + iio_dev->num_channels = ARRAY_SIZE(magn_channels); + + scnprintf(sensor->name, sizeof(sensor->name), "%s_magn", + name); + break; + } + default: + return NULL; + } + iio_dev->name = sensor->name; + + return iio_dev; +} + +static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + int err; + + settings = sensor->ext_info.settings; + if (settings->bdu.addr) { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->bdu.addr, + settings->bdu.mask, 1); + if (err < 0) + return err; + } + + if (settings->temp_comp.addr) { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->temp_comp.addr, + settings->temp_comp.mask, 1); + if (err < 0) + return err; + } + + if (settings->off_canc.addr) { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->off_canc.addr, + settings->off_canc.mask, 1); + if (err < 0) + return err; + } + + return 0; +} + +static int +st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, + const struct st_lsm6dsx_ext_dev_settings *settings) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + u8 config[3], data, slv_addr, slv_config = 0; + const struct st_lsm6dsx_reg *aux_sens; + struct st_lsm6dsx_sensor *sensor; + bool found = false; + int i, err; + + sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + hub_settings = &hw->settings->shub_settings; + aux_sens = &hw->settings->shub_settings.aux_sens; + slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + /* do not overwrite aux_sens */ + if (slv_addr + 2 == aux_sens->addr) + slv_config = ST_LSM6DSX_SHIFT_VAL(3, aux_sens->mask); + + for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) { + if (!settings->i2c_addr[i]) + continue; + + /* read wai slave register */ + config[0] = (settings->i2c_addr[i] << 1) | 0x1; + config[1] = settings->wai.addr; + config[2] = 0x1 | slv_config; + + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_lsm6dsx_shub_wait_complete(hw); + + err = st_lsm6dsx_shub_read_output(hw, &data, sizeof(data)); + + st_lsm6dsx_shub_master_enable(sensor, false); + + if (err < 0) + return err; + + if (data != settings->wai.val) + continue; + + *i2c_addr = settings->i2c_addr[i]; + found = true; + break; + } + + /* reset SLV0 channel */ + config[0] = hub_settings->pause; + config[1] = 0; + config[2] = slv_config; + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + return found ? 0 : -ENODEV; +} + +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name) +{ + enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0; + struct st_lsm6dsx_sensor *sensor; + int err, i, num_ext_dev = 0; + u8 i2c_addr = 0; + + for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table); i++) { + err = st_lsm6dsx_shub_check_wai(hw, &i2c_addr, + &st_lsm6dsx_ext_dev_table[i]); + if (err == -ENODEV) + continue; + else if (err < 0) + return err; + + hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw, id, + &st_lsm6dsx_ext_dev_table[i], + i2c_addr, name); + if (!hw->iio_devs[id]) + return -ENOMEM; + + sensor = iio_priv(hw->iio_devs[id]); + err = st_lsm6dsx_shub_init_device(sensor); + if (err < 0) + return err; + + if (++num_ext_dev >= hw->settings->shub_settings.num_ext_dev) + break; + id++; + } + + return 0; +} -- cgit v1.2.3