drivers:iio:stm:imu:st_ism330is: add ST MEMS IMU ISM330IS sensor support
Added support tu ISM330IS ST MEMS IMU sensor with following features: - Sensor Hub 1) Maximum of 2 device supported to be chosen from the following part numbers: - LIS2MDL 3-axis magnetometer - LPS22HH pressure 2) Possibility to configure pull up on I2C master interface through device tree 3) Self scan on I2C master interface of supported device, including multi address devices 4) Configure SHUB sensor ODR - Self-test verifying the functionality of the device without moving it. The Self Test procedure can be performed on the Accelerometer and also on Gyro sensor separately using the selftest entry of the sysfs. - Acc, gyro and temperature data reading (with sensitivity and raw data) - External trigger Signed-off-by: Mario Tesi <mario.tesi@st.com> Change-Id: I6de3f216b54244dae7127ae48e38b7939009e248
This commit is contained in:
parent
2dae89e66f
commit
a64c7c7b2d
@ -0,0 +1,55 @@
|
|||||||
|
* st_ism330is driver for imu MEMS sensors
|
||||||
|
|
||||||
|
Required properties for all bus drivers:
|
||||||
|
- compatible: must be one of:
|
||||||
|
"st,ism330is"
|
||||||
|
|
||||||
|
Required properties for the i2c bindings:
|
||||||
|
- reg: i2c slave address
|
||||||
|
|
||||||
|
Required properties for the spi bindings:
|
||||||
|
- reg: the chipselect index
|
||||||
|
|
||||||
|
- spi-max-frequency: maximal bus speed, should be set to 1000000 unless
|
||||||
|
constrained by external circuitry.
|
||||||
|
|
||||||
|
Optional properties for all bus drivers:
|
||||||
|
- vdd-supply: an optional regulator that needs to be on to provide VDD
|
||||||
|
power to the sensor.
|
||||||
|
|
||||||
|
- vddio-supply: an optional regulator that needs to be on to provide the
|
||||||
|
VDD IO power to the sensor.
|
||||||
|
|
||||||
|
- mount-matrix: mount rotation matrix.
|
||||||
|
Refer to iio/mount-matrix.txt for details.
|
||||||
|
|
||||||
|
- enable-sensor-hub: enable i2c master interface. Default is disabled.
|
||||||
|
|
||||||
|
- drive-pullup-shub: enable pullup on master i2c line.
|
||||||
|
|
||||||
|
Example for an spi device node on CS0:
|
||||||
|
|
||||||
|
ism330is-imu@0 {
|
||||||
|
compatible = "st,ism330is";
|
||||||
|
reg = <0x0>;
|
||||||
|
spi-max-frequency = <1000000>;
|
||||||
|
vddio-supply = <&sensors_vddio>;
|
||||||
|
vdd-supply = <&sensors_vdd>;
|
||||||
|
mount-matrix = "1", "0", "0",
|
||||||
|
"0", "1", "0",
|
||||||
|
"0", "0", "1";
|
||||||
|
drive-pullup-shub;
|
||||||
|
};
|
||||||
|
|
||||||
|
Example for an i2c device node when SA0 tied to ground:
|
||||||
|
|
||||||
|
ism330is-imu@6a {
|
||||||
|
compatible = "st,ism330is";
|
||||||
|
reg = <0x6a>;
|
||||||
|
vddio-supply = <&sensors_vddio>;
|
||||||
|
vdd-supply = <&sensors_vdd>;
|
||||||
|
mount-matrix = "1", "0", "0",
|
||||||
|
"0", "1", "0",
|
||||||
|
"0", "0", "1";
|
||||||
|
drive-pullup-shub;
|
||||||
|
};
|
@ -18,5 +18,6 @@ source "drivers/iio/stm/imu/st_lsm6dsm/Kconfig"
|
|||||||
source "drivers/iio/stm/imu/st_lsm6dsvx/Kconfig"
|
source "drivers/iio/stm/imu/st_lsm6dsvx/Kconfig"
|
||||||
source "drivers/iio/stm/imu/st_lsm6dso16is/Kconfig"
|
source "drivers/iio/stm/imu/st_lsm6dso16is/Kconfig"
|
||||||
source "drivers/iio/stm/imu/st_lsm6dsv16bx/Kconfig"
|
source "drivers/iio/stm/imu/st_lsm6dsv16bx/Kconfig"
|
||||||
|
source "drivers/iio/stm/imu/st_ism330is/Kconfig"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -16,3 +16,4 @@ obj-y += st_lsm6dsm/
|
|||||||
obj-y += st_lsm6dsvx/
|
obj-y += st_lsm6dsvx/
|
||||||
obj-y += st_lsm6dso16is/
|
obj-y += st_lsm6dso16is/
|
||||||
obj-y += st_lsm6dsv16bx/
|
obj-y += st_lsm6dsv16bx/
|
||||||
|
obj-y += st_ism330is/
|
||||||
|
26
drivers/iio/stm/imu/st_ism330is/Kconfig
Normal file
26
drivers/iio/stm/imu/st_ism330is/Kconfig
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
config IIO_ST_ISM330IS
|
||||||
|
tristate "STMicroelectronics ISM330IS sensor"
|
||||||
|
depends on (I2C || SPI)
|
||||||
|
select IIO_BUFFER
|
||||||
|
select IIO_TRIGGERED_BUFFER
|
||||||
|
select IIO_ST_ISM330IS_I2C if (I2C)
|
||||||
|
select IIO_ST_ISM330IS_SPI if (SPI_MASTER)
|
||||||
|
help
|
||||||
|
Say yes here to build support for STMicroelectronics
|
||||||
|
ISM330IS imu sensors.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called st_ism330is.
|
||||||
|
|
||||||
|
config IIO_ST_ISM330IS_I2C
|
||||||
|
tristate
|
||||||
|
select REGMAP_I2C
|
||||||
|
depends on IIO_ST_ISM330IS
|
||||||
|
|
||||||
|
config IIO_ST_ISM330IS_SPI
|
||||||
|
tristate
|
||||||
|
select REGMAP_SPI
|
||||||
|
depends on IIO_ST_ISM330IS
|
||||||
|
|
8
drivers/iio/stm/imu/st_ism330is/Makefile
Normal file
8
drivers/iio/stm/imu/st_ism330is/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
st_ism330is-y := st_ism330is_core.o \
|
||||||
|
st_ism330is_shub.o \
|
||||||
|
st_ism330is_triggers.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_IIO_ST_ISM330IS) += st_ism330is.o
|
||||||
|
obj-$(CONFIG_IIO_ST_ISM330IS_I2C) += st_ism330is_i2c.o
|
||||||
|
obj-$(CONFIG_IIO_ST_ISM330IS_SPI) += st_ism330is_spi.o
|
344
drivers/iio/stm/imu/st_ism330is/st_ism330is.h
Normal file
344
drivers/iio/stm/imu/st_ism330is/st_ism330is.h
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_ism330is sensor driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2023 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ST_ISM330IS_H
|
||||||
|
#define ST_ISM330IS_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
|
||||||
|
#define ST_ISM330IS_DEV_NAME "ism330is"
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_FUNC_CFG_ACCESS_ADDR 0x01
|
||||||
|
#define ST_ISM330IS_SHUB_REG_MASK BIT(6)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_PIN_CTRL_ADDR 0x02
|
||||||
|
#define ST_ISM330IS_SDO_PU_EN_MASK BIT(6)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_WHOAMI_ADDR 0x0f
|
||||||
|
#define ST_ISM330IS_WHOAMI_VAL 0x22
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_CTRL1_XL_ADDR 0x10
|
||||||
|
#define ST_ISM330IS_ODR_XL_MASK GENMASK(7, 4)
|
||||||
|
#define ST_ISM330IS_FS_XL_MASK GENMASK(3, 2)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_CTRL2_G_ADDR 0x11
|
||||||
|
#define ST_ISM330IS_ODR_G_MASK GENMASK(7, 4)
|
||||||
|
#define ST_ISM330IS_FS_G_MASK GENMASK(3, 1)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_CTRL3_C_ADDR 0x12
|
||||||
|
#define ST_ISM330IS_BOOT_MASK BIT(7)
|
||||||
|
#define ST_ISM330IS_BDU_MASK BIT(6)
|
||||||
|
#define ST_ISM330IS_H_LACTIVE_MASK BIT(5)
|
||||||
|
#define ST_ISM330IS_PP_OD_MASK BIT(4)
|
||||||
|
#define ST_ISM330IS_SW_RESET_MASK BIT(0)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_CTRL5_C_ADDR 0x14
|
||||||
|
#define ST_ISM330IS_ST_G_MASK GENMASK(3, 2)
|
||||||
|
#define ST_ISM330IS_ST_XL_MASK GENMASK(1, 0)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_STATUS_ADDR 0x1e
|
||||||
|
#define ST_ISM330IS_STATUS_TDA BIT(2)
|
||||||
|
#define ST_ISM330IS_STATUS_XLDA BIT(0)
|
||||||
|
#define ST_ISM330IS_STATUS_GDA BIT(1)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_OUT_TEMP_L_ADDR 0x20
|
||||||
|
#define ST_ISM330IS_REG_OUTX_L_G_ADDR 0x22
|
||||||
|
#define ST_ISM330IS_REG_OUTY_L_G_ADDR 0x24
|
||||||
|
#define ST_ISM330IS_REG_OUTZ_L_G_ADDR 0x26
|
||||||
|
#define ST_ISM330IS_REG_OUTX_L_A_ADDR 0x28
|
||||||
|
#define ST_ISM330IS_REG_OUTY_L_A_ADDR 0x2a
|
||||||
|
#define ST_ISM330IS_REG_OUTZ_L_A_ADDR 0x2c
|
||||||
|
|
||||||
|
#define ST_ISM330IS_ST_ACCEL_MIN 737
|
||||||
|
#define ST_ISM330IS_ST_ACCEL_MAX 13934
|
||||||
|
#define ST_ISM330IS_ST_GYRO_MIN 2142
|
||||||
|
#define ST_ISM330IS_ST_GYRO_MAX 10000
|
||||||
|
|
||||||
|
#define ST_ISM330IS_ST_DISABLED_VAL 0
|
||||||
|
#define ST_ISM330IS_ST_POS_SIGN_VAL 1
|
||||||
|
#define ST_ISM330IS_ST_NEG_ACCEL_SIGN_VAL 2
|
||||||
|
#define ST_ISM330IS_ST_NEG_GYRO_SIGN_VAL 3
|
||||||
|
|
||||||
|
/* shub registers */
|
||||||
|
#define ST_ISM330IS_REG_SENSOR_HUB_1_ADDR 0x02
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_MASTER_CONFIG_ADDR 0x14
|
||||||
|
#define ST_ISM330IS_WRITE_ONCE_MASK BIT(6)
|
||||||
|
#define ST_ISM330IS_SHUB_PU_EN_MASK BIT(3)
|
||||||
|
#define ST_ISM330IS_MASTER_ON_MASK BIT(2)
|
||||||
|
#define ST_ISM330IS_AUX_SENS_ON_MASK GENMASK(1, 0)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_SLV0_ADDR 0x15
|
||||||
|
#define ST_ISM330IS_REG_SLV0_CFG 0x17
|
||||||
|
#define ST_ISM330IS_REG_SLV1_ADDR 0x18
|
||||||
|
#define ST_ISM330IS_REG_SLV2_ADDR 0x1b
|
||||||
|
#define ST_ISM330IS_REG_SLV3_ADDR 0x1e
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_DATAWRITE_SLV0_ADDR 0x21
|
||||||
|
#define ST_ISM330IS_SLAVE_NUMOP_MASK GENMASK(2, 0)
|
||||||
|
|
||||||
|
#define ST_ISM330IS_REG_STATUS_MASTER_ADDR 0x22
|
||||||
|
#define ST_ISM330IS_SENS_HUB_ENDOP_MASK BIT(0)
|
||||||
|
|
||||||
|
/* Timestamp Tick 25us/LSB */
|
||||||
|
#define ST_ISM330IS_TS_DELTA_NS 25000ULL
|
||||||
|
|
||||||
|
/* Temperature in uC */
|
||||||
|
#define ST_ISM330IS_TEMP_GAIN 256
|
||||||
|
#define ST_ISM330IS_TEMP_OFFSET 6400
|
||||||
|
|
||||||
|
#define ST_ISM330IS_DATA_CHANNEL(chan_type, addr, mod, ch2, scan_idx, \
|
||||||
|
rb, sb, sg, ext_inf) \
|
||||||
|
{ \
|
||||||
|
.type = chan_type, \
|
||||||
|
.address = addr, \
|
||||||
|
.modified = mod, \
|
||||||
|
.channel2 = ch2, \
|
||||||
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE), \
|
||||||
|
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||||
|
.scan_index = scan_idx, \
|
||||||
|
.scan_type = { \
|
||||||
|
.sign = sg, \
|
||||||
|
.realbits = rb, \
|
||||||
|
.storagebits = sb, \
|
||||||
|
.endianness = IIO_LE, \
|
||||||
|
}, \
|
||||||
|
.ext_info = ext_inf, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ST_ISM330IS_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
|
||||||
|
|
||||||
|
extern const struct dev_pm_ops st_ism330is_pm_ops;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_reg - Generic sensor register
|
||||||
|
* description (addr + mask)
|
||||||
|
*
|
||||||
|
* @addr: Address of register.
|
||||||
|
* @mask: Bitmask register for proper usage.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_reg {
|
||||||
|
u8 addr;
|
||||||
|
u8 mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_odr - Single ODR entry
|
||||||
|
* @mhz: Sensor ODR (milli Hz).
|
||||||
|
* @val: ODR register value.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_odr {
|
||||||
|
u32 mhz;
|
||||||
|
u8 val;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_odr_table_entry - Sensor ODR table
|
||||||
|
* @size: Size of ODR table.
|
||||||
|
* @reg: ODR register.
|
||||||
|
* @odr_avl: Array of supported ODR value.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_odr_table_entry {
|
||||||
|
u8 size;
|
||||||
|
struct st_ism330is_reg reg;
|
||||||
|
struct st_ism330is_odr odr_avl[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_fs
|
||||||
|
* brief Full scale entry
|
||||||
|
*
|
||||||
|
* @gain: The gain to obtain data value from raw data (LSB).
|
||||||
|
* @val: Register value.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_fs {
|
||||||
|
u32 gain;
|
||||||
|
u8 val;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_fs_table_entry - Full Scale sensor table
|
||||||
|
* @reg: st_ism330is_reg struct.
|
||||||
|
* @fs_avl: Full Scale list entries.
|
||||||
|
* @fs_len: Real size of fs_avl array.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_fs_table_entry {
|
||||||
|
int fs_len;
|
||||||
|
struct st_ism330is_reg reg;
|
||||||
|
struct st_ism330is_fs fs_avl[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum st_ism330is_sensor_id {
|
||||||
|
ST_ISM330IS_ID_GYRO = 0,
|
||||||
|
ST_ISM330IS_ID_ACC,
|
||||||
|
ST_ISM330IS_ID_TEMP,
|
||||||
|
ST_ISM330IS_ID_EXT0,
|
||||||
|
ST_ISM330IS_ID_EXT1,
|
||||||
|
ST_ISM330IS_ID_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum st_ism330is_sensor_id
|
||||||
|
* @brief Sensor Table Identifier
|
||||||
|
*/
|
||||||
|
static const enum st_ism330is_sensor_id st_ism330is_main_sensor_list[] = {
|
||||||
|
[0] = ST_ISM330IS_ID_GYRO,
|
||||||
|
[1] = ST_ISM330IS_ID_ACC,
|
||||||
|
[2] = ST_ISM330IS_ID_TEMP,
|
||||||
|
[3] = ST_ISM330IS_ID_EXT0,
|
||||||
|
[4] = ST_ISM330IS_ID_EXT1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const enum st_ism330is_sensor_id
|
||||||
|
st_ism330is_triggered_main_sensor_list[] = {
|
||||||
|
[0] = ST_ISM330IS_ID_GYRO,
|
||||||
|
[1] = ST_ISM330IS_ID_ACC,
|
||||||
|
[2] = ST_ISM330IS_ID_TEMP,
|
||||||
|
[3] = ST_ISM330IS_ID_EXT0,
|
||||||
|
[4] = ST_ISM330IS_ID_EXT1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct st_ism330is_ext_dev_info {
|
||||||
|
const struct st_ism330is_ext_dev_settings *ext_dev_settings;
|
||||||
|
u8 ext_dev_i2c_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_sensor - ST IMU sensor instance
|
||||||
|
* @ext_dev_info: For sensor hub indicate device info struct.
|
||||||
|
* @id: Sensor identifier.
|
||||||
|
* @hw: Pointer to instance of struct st_ism330is_hw.
|
||||||
|
* @name: Sensor name.
|
||||||
|
* @offset: Sensor data offset.
|
||||||
|
* @gain: Configured sensor sensitivity.
|
||||||
|
* @mhz: Output data rate of the sensor [milli Hz].
|
||||||
|
* @selftest_status: Report last self test status.
|
||||||
|
* @min_st: Min self test raw data value.
|
||||||
|
* @max_st: Max self test raw data value.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_sensor {
|
||||||
|
struct st_ism330is_ext_dev_info ext_dev_info;
|
||||||
|
enum st_ism330is_sensor_id id;
|
||||||
|
struct st_ism330is_hw *hw;
|
||||||
|
char name[32];
|
||||||
|
|
||||||
|
u32 offset;
|
||||||
|
u32 gain;
|
||||||
|
u32 mhz;
|
||||||
|
|
||||||
|
/* self test */
|
||||||
|
int8_t selftest_status;
|
||||||
|
int min_st;
|
||||||
|
int max_st;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct st_ism330is_hw - ST IMU MEMS hw instance
|
||||||
|
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||||
|
* @orientation: Sensor orientation matrix.
|
||||||
|
* @vddio_supply: Voltage regulator for VDDIIO.
|
||||||
|
* @vdd_supply: Voltage regulator for VDD.
|
||||||
|
* @page_lock: Mutex to prevent concurrent access to the page selector.
|
||||||
|
* @regmap: Register map of the device.
|
||||||
|
* @dev: Pointer to instance of struct device (I2C or SPI).
|
||||||
|
* @i2c_master_pu: I2C master line Pull Up configuration.
|
||||||
|
* @enable_mask: Enabled sensor bitmask.
|
||||||
|
* @ext_data_len: Number of i2c slave devices connected to I2C master.
|
||||||
|
* @irq: Device interrupt line (I2C or SPI).
|
||||||
|
*/
|
||||||
|
struct st_ism330is_hw {
|
||||||
|
struct iio_dev *iio_devs[ST_ISM330IS_ID_MAX];
|
||||||
|
struct iio_mount_matrix orientation;
|
||||||
|
struct regulator *vddio_supply;
|
||||||
|
struct regulator *vdd_supply;
|
||||||
|
struct mutex page_lock;
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct device *dev;
|
||||||
|
u8 i2c_master_pu;
|
||||||
|
u32 enable_mask;
|
||||||
|
u8 ext_data_len;
|
||||||
|
int irq;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
__st_ism330is_write_with_mask(struct st_ism330is_hw *hw,
|
||||||
|
unsigned int addr,
|
||||||
|
unsigned int mask,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
return regmap_update_bits(hw->regmap, addr, mask,
|
||||||
|
ST_ISM330IS_SHIFT_VAL(val, mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
st_ism330is_update_bits_locked(struct st_ism330is_hw *hw,
|
||||||
|
unsigned int addr,
|
||||||
|
unsigned int mask,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->page_lock);
|
||||||
|
err = __st_ism330is_write_with_mask(hw, addr, mask, val);
|
||||||
|
mutex_unlock(&hw->page_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
st_ism330is_read_locked(struct st_ism330is_hw *hw, unsigned int addr,
|
||||||
|
void *val, unsigned int len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->page_lock);
|
||||||
|
err = regmap_bulk_read(hw->regmap, addr, val, len);
|
||||||
|
mutex_unlock(&hw->page_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
st_ism330is_write_locked(struct st_ism330is_hw *hw, unsigned int addr,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->page_lock);
|
||||||
|
err = regmap_write(hw->regmap, addr, val);
|
||||||
|
mutex_unlock(&hw->page_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
st_ism330is_set_page_access(struct st_ism330is_hw *hw, unsigned int mask,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
return __st_ism330is_write_with_mask(hw,
|
||||||
|
ST_ISM330IS_REG_FUNC_CFG_ACCESS_ADDR,
|
||||||
|
mask, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
int st_ism330is_probe(struct device *dev, int irq, struct regmap *regmap);
|
||||||
|
int st_ism330is_sensor_set_enable(struct st_ism330is_sensor *sensor,
|
||||||
|
bool enable);
|
||||||
|
int st_ism330is_shub_probe(struct st_ism330is_hw *hw);
|
||||||
|
int st_ism330is_shub_set_enable(struct st_ism330is_sensor *sensor,
|
||||||
|
bool enable);
|
||||||
|
int st_ism330is_shub_read(struct st_ism330is_sensor *sensor,
|
||||||
|
u8 addr, u8 *data, int len);
|
||||||
|
int st_ism330is_allocate_buffers(struct st_ism330is_hw *hw);
|
||||||
|
#endif /* ST_ISM330IS_H */
|
1210
drivers/iio/stm/imu/st_ism330is/st_ism330is_core.c
Normal file
1210
drivers/iio/stm/imu/st_ism330is/st_ism330is_core.c
Normal file
File diff suppressed because it is too large
Load Diff
64
drivers/iio/stm/imu/st_ism330is/st_ism330is_i2c.c
Normal file
64
drivers/iio/stm/imu/st_ism330is/st_ism330is_i2c.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_ism330is i2c driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2023 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
#include "st_ism330is.h"
|
||||||
|
|
||||||
|
static const struct regmap_config st_ism330is_i2c_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int st_ism330is_i2c_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_i2c(client, &st_ism330is_i2c_regmap_config);
|
||||||
|
if (IS_ERR(regmap)) {
|
||||||
|
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||||
|
(int)PTR_ERR(regmap));
|
||||||
|
|
||||||
|
return PTR_ERR(regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return st_ism330is_probe(&client->dev, client->irq, regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id st_ism330is_i2c_of_match[] = {
|
||||||
|
{ .compatible = "st,ism330is" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, st_ism330is_i2c_of_match);
|
||||||
|
|
||||||
|
static const struct i2c_device_id st_ism330is_i2c_id_table[] = {
|
||||||
|
{ ST_ISM330IS_DEV_NAME },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, st_ism330is_i2c_id_table);
|
||||||
|
|
||||||
|
static struct i2c_driver st_ism330is_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "st_" ST_ISM330IS_DEV_NAME "_i2c",
|
||||||
|
.pm = &st_ism330is_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(st_ism330is_i2c_of_match),
|
||||||
|
},
|
||||||
|
.probe = st_ism330is_i2c_probe,
|
||||||
|
.id_table = st_ism330is_i2c_id_table,
|
||||||
|
};
|
||||||
|
module_i2c_driver(st_ism330is_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics st_ism330is i2c driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
952
drivers/iio/stm/imu/st_ism330is/st_ism330is_shub.c
Normal file
952
drivers/iio/stm/imu/st_ism330is/st_ism330is_shub.c
Normal file
@ -0,0 +1,952 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_ism330is sensor hub library driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2023 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
|
#include "st_ism330is.h"
|
||||||
|
|
||||||
|
#define ST_ISM330IS_MAX_SLV_NUM 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct st_ism330is_ext_pwr
|
||||||
|
* @brief External device Power Management description
|
||||||
|
* reg: Generic sensor register description.
|
||||||
|
* off_val: Value to write into register to power off external sensor.
|
||||||
|
* on_val: Value to write into register for power on external sensor.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_ext_pwr {
|
||||||
|
struct st_ism330is_reg reg;
|
||||||
|
u8 off_val;
|
||||||
|
u8 on_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct st_ism330is_ext_dev_settings
|
||||||
|
* @brief External sensor descritor entry
|
||||||
|
* i2c_addr: External I2C device address (max two).
|
||||||
|
* wai_addr: Device ID address.
|
||||||
|
* wai_val: Device ID value.
|
||||||
|
* odr_table: ODR sensor table.
|
||||||
|
* fs_table: Full scale table.
|
||||||
|
* temp_comp_reg: Temperature compensation registers.
|
||||||
|
* pwr_table: External device Power Management description.
|
||||||
|
* off_canc_reg: Offset cancellation registers.
|
||||||
|
* bdu_reg: Block Data Update registers.
|
||||||
|
* ext_channels:IIO device channel specifications.
|
||||||
|
* ext_chan_depth: Max number of IIO device channel specifications.
|
||||||
|
* data_len: Sensor output data len.
|
||||||
|
*/
|
||||||
|
struct st_ism330is_ext_dev_settings {
|
||||||
|
u8 i2c_addr[2];
|
||||||
|
u8 wai_addr;
|
||||||
|
u8 wai_val;
|
||||||
|
struct st_ism330is_odr_table_entry odr_table;
|
||||||
|
struct st_ism330is_fs_table_entry fs_table;
|
||||||
|
struct st_ism330is_reg temp_comp_reg;
|
||||||
|
struct st_ism330is_ext_pwr pwr_table;
|
||||||
|
struct st_ism330is_reg off_canc_reg;
|
||||||
|
struct st_ism330is_reg bdu_reg;
|
||||||
|
const struct iio_chan_spec ext_channels[5];
|
||||||
|
u8 ext_chan_depth;
|
||||||
|
u8 data_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct st_ism330is_ext_dev_settings st_ism330is_ext_dev_table[] = {
|
||||||
|
{
|
||||||
|
/* LIS2MDL */
|
||||||
|
.i2c_addr = { 0x1e },
|
||||||
|
.wai_addr = 0x4f,
|
||||||
|
.wai_val = 0x40,
|
||||||
|
.odr_table = {
|
||||||
|
.size = 5,
|
||||||
|
.reg = {
|
||||||
|
.addr = 0x60,
|
||||||
|
.mask = GENMASK(3, 2),
|
||||||
|
},
|
||||||
|
.odr_avl[0] = { 5000, 0x0 },
|
||||||
|
.odr_avl[1] = { 10000, 0x0 },
|
||||||
|
.odr_avl[2] = { 20000, 0x1 },
|
||||||
|
.odr_avl[3] = { 50000, 0x2 },
|
||||||
|
.odr_avl[4] = { 100000, 0x3 },
|
||||||
|
},
|
||||||
|
.fs_table = {
|
||||||
|
.fs_len = 1,
|
||||||
|
.fs_avl[0] = {
|
||||||
|
.gain = 1500,
|
||||||
|
.val = 0x0,
|
||||||
|
}, /* 1500 uG/LSB */
|
||||||
|
},
|
||||||
|
.temp_comp_reg = {
|
||||||
|
.addr = 0x60,
|
||||||
|
.mask = BIT(7),
|
||||||
|
},
|
||||||
|
.pwr_table = {
|
||||||
|
.reg = {
|
||||||
|
.addr = 0x60,
|
||||||
|
.mask = GENMASK(1, 0),
|
||||||
|
},
|
||||||
|
.off_val = 0x2,
|
||||||
|
.on_val = 0x0,
|
||||||
|
},
|
||||||
|
.off_canc_reg = {
|
||||||
|
.addr = 0x61,
|
||||||
|
.mask = BIT(1),
|
||||||
|
},
|
||||||
|
.bdu_reg = {
|
||||||
|
.addr = 0x62,
|
||||||
|
.mask = BIT(4),
|
||||||
|
},
|
||||||
|
.ext_channels[0] = ST_ISM330IS_DATA_CHANNEL(IIO_MAGN, 0x68,
|
||||||
|
1, IIO_MOD_X, 0,
|
||||||
|
16, 16, 's', NULL),
|
||||||
|
.ext_channels[1] = ST_ISM330IS_DATA_CHANNEL(IIO_MAGN, 0x6a,
|
||||||
|
1, IIO_MOD_Y, 1,
|
||||||
|
16, 16, 's', NULL),
|
||||||
|
.ext_channels[2] = ST_ISM330IS_DATA_CHANNEL(IIO_MAGN, 0x6c,
|
||||||
|
1, IIO_MOD_Z, 2,
|
||||||
|
16, 16, 's', NULL),
|
||||||
|
.ext_channels[3] = IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||||
|
.ext_chan_depth = 4,
|
||||||
|
.data_len = 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
/* LPS22HH */
|
||||||
|
.i2c_addr = { 0x5c, 0x5d },
|
||||||
|
.wai_addr = 0x0f,
|
||||||
|
.wai_val = 0xb3,
|
||||||
|
.odr_table = {
|
||||||
|
.size = 5,
|
||||||
|
.reg = {
|
||||||
|
.addr = 0x10,
|
||||||
|
.mask = GENMASK(6, 4),
|
||||||
|
},
|
||||||
|
.odr_avl[0] = { 1000, 0x1 },
|
||||||
|
.odr_avl[1] = { 10000, 0x2 },
|
||||||
|
.odr_avl[2] = { 25000, 0x3 },
|
||||||
|
.odr_avl[3] = { 50000, 0x4 },
|
||||||
|
.odr_avl[4] = { 100000, 0x6 },
|
||||||
|
},
|
||||||
|
.fs_table = {
|
||||||
|
.fs_len = 1,
|
||||||
|
/* hPa miscro scale */
|
||||||
|
.fs_avl[0] = {
|
||||||
|
.gain = 1000000UL / 4096UL,
|
||||||
|
.val = 0x0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.bdu_reg = {
|
||||||
|
.addr = 0x10,
|
||||||
|
.mask = BIT(1),
|
||||||
|
},
|
||||||
|
.ext_channels[0] = ST_ISM330IS_DATA_CHANNEL(IIO_PRESSURE, 0x28,
|
||||||
|
0, IIO_NO_MOD, 0,
|
||||||
|
24, 32, 'u', NULL),
|
||||||
|
.ext_channels[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||||
|
.ext_chan_depth = 2,
|
||||||
|
.data_len = 3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait write trigger [SHUB]
|
||||||
|
*
|
||||||
|
* In write on external device register, each operation is triggered
|
||||||
|
* by accel/gyro data ready, this means that wait time depends on ODR
|
||||||
|
* plus i2c time
|
||||||
|
* NOTE: Be sure to enable Acc or Gyro before this operation
|
||||||
|
*
|
||||||
|
* @param hw: ST IMU MEMS hw instance.
|
||||||
|
*/
|
||||||
|
static inline void st_ism330is_shub_wait_complete(struct st_ism330is_hw *hw)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor;
|
||||||
|
u32 odr;
|
||||||
|
|
||||||
|
sensor = iio_priv(hw->iio_devs[ST_ISM330IS_ID_ACC]);
|
||||||
|
|
||||||
|
/* check if acc is enabled (it should be) */
|
||||||
|
if (hw->enable_mask & BIT(ST_ISM330IS_ID_ACC))
|
||||||
|
odr = sensor->mhz;
|
||||||
|
else
|
||||||
|
odr = 12500;
|
||||||
|
|
||||||
|
/* odr tollerance is 10 % */
|
||||||
|
msleep(1100000 / odr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from sensor hub bank register [SHUB]
|
||||||
|
*
|
||||||
|
* NOTE: uses page_lock
|
||||||
|
*
|
||||||
|
* @param hw: ST IMU MEMS hw instance.
|
||||||
|
* @param addr: Remote address register.
|
||||||
|
* @param data: Data buffer.
|
||||||
|
* @param len: Data read len.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_read_reg(struct st_ism330is_hw *hw, u8 addr,
|
||||||
|
u8 *data, int len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->page_lock);
|
||||||
|
err = st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK,
|
||||||
|
true);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = regmap_bulk_read(hw->regmap, (unsigned int)addr,
|
||||||
|
(unsigned int *)data, len);
|
||||||
|
st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, false);
|
||||||
|
out:
|
||||||
|
mutex_unlock(&hw->page_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to sensor hub bank register [SHUB]
|
||||||
|
*
|
||||||
|
* NOTE: uses page_lock
|
||||||
|
*
|
||||||
|
* @param hw: ST IMU MEMS hw instance.
|
||||||
|
* @param addr: Remote address register.
|
||||||
|
* @param data: Data buffer.
|
||||||
|
* @param len: Data read len.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_write_reg(struct st_ism330is_hw *hw, u8 addr,
|
||||||
|
u8 *data, int len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->page_lock);
|
||||||
|
err = st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, true);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = regmap_bulk_write(hw->regmap, (unsigned int)addr,
|
||||||
|
(unsigned int *)data, len);
|
||||||
|
st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, false);
|
||||||
|
out:
|
||||||
|
mutex_unlock(&hw->page_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable sensor hub interface [SHUB]
|
||||||
|
*
|
||||||
|
* NOTE: uses page_lock
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param enable: Master Enable/Disable.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
st_ism330is_shub_master_enable(struct st_ism330is_sensor *sensor, bool enable)
|
||||||
|
{
|
||||||
|
struct st_ism330is_hw *hw = sensor->hw;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* enable main sensor as trigger */
|
||||||
|
err = st_ism330is_sensor_set_enable(sensor, enable);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
mutex_lock(&hw->page_lock);
|
||||||
|
err = st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, true);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = __st_ism330is_write_with_mask(hw,
|
||||||
|
ST_ISM330IS_REG_MASTER_CONFIG_ADDR,
|
||||||
|
ST_ISM330IS_MASTER_ON_MASK,
|
||||||
|
enable);
|
||||||
|
|
||||||
|
st_ism330is_set_page_access(hw, ST_ISM330IS_SHUB_REG_MASK, false);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&hw->page_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read sensor data register from shub interface
|
||||||
|
*
|
||||||
|
* NOTE: use SLV3 i2c slave for one-shot read operation
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param addr: Remote address register.
|
||||||
|
* @param data: Data buffer.
|
||||||
|
* @param len: Data read len.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
int st_ism330is_shub_read(struct st_ism330is_sensor *sensor,
|
||||||
|
u8 addr, u8 *data, int len)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
struct st_ism330is_hw *hw = sensor->hw;
|
||||||
|
u8 out_addr = ST_ISM330IS_REG_SENSOR_HUB_1_ADDR + hw->ext_data_len;
|
||||||
|
u8 config[3];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
config[0] = (ext_info->ext_dev_i2c_addr << 1) | 1;
|
||||||
|
config[1] = addr;
|
||||||
|
config[2] = len & 0x7;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV3_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_master_enable(sensor, true);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
st_ism330is_shub_wait_complete(hw);
|
||||||
|
|
||||||
|
err = st_ism330is_shub_read_reg(hw, out_addr, data, len & 0x7);
|
||||||
|
|
||||||
|
st_ism330is_shub_master_enable(sensor, false);
|
||||||
|
|
||||||
|
memset(config, 0, sizeof(config));
|
||||||
|
|
||||||
|
return st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV3_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write sensor data register from shub interface
|
||||||
|
*
|
||||||
|
* NOTE: use SLV0 i2c slave for write operation
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param addr: Remote address register.
|
||||||
|
* @param data: Data buffer.
|
||||||
|
* @param len: Data read len.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_write(struct st_ism330is_sensor *sensor,
|
||||||
|
u8 addr, u8 *data, int len)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
struct st_ism330is_hw *hw = sensor->hw;
|
||||||
|
u8 mconfig = ST_ISM330IS_WRITE_ONCE_MASK | 3 | hw->i2c_master_pu;
|
||||||
|
u8 config[3] = {};
|
||||||
|
int err, i;
|
||||||
|
|
||||||
|
/* AuxSens = 3 + wr once + pull up configuration */
|
||||||
|
err = st_ism330is_shub_write_reg(hw,
|
||||||
|
ST_ISM330IS_REG_MASTER_CONFIG_ADDR,
|
||||||
|
&mconfig, sizeof(mconfig));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
config[0] = ext_info->ext_dev_i2c_addr << 1;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
config[1] = addr + i;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_write_reg(hw,
|
||||||
|
ST_ISM330IS_REG_SLV0_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_write_reg(hw,
|
||||||
|
ST_ISM330IS_REG_DATAWRITE_SLV0_ADDR,
|
||||||
|
&data[i], 1);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_master_enable(sensor, true);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
st_ism330is_shub_wait_complete(hw);
|
||||||
|
|
||||||
|
st_ism330is_shub_master_enable(sensor, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV0_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write sensor data register from shub interface using register bitmask
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param addr: Remote address register.
|
||||||
|
* @param mask: Register bitmask.
|
||||||
|
* @param val: Data buffer.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
st_ism330is_shub_write_with_mask(struct st_ism330is_sensor *sensor,
|
||||||
|
u8 addr, u8 mask, u8 val)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_read(sensor, addr, &data, sizeof(data));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
data = (data & ~mask) | ST_ISM330IS_SHIFT_VAL(val, mask);
|
||||||
|
|
||||||
|
return st_ism330is_shub_write(sensor, addr, &data, sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure external sensor connected on master I2C interface
|
||||||
|
*
|
||||||
|
* NOTE: use SLV1/SLV2 i2c slave for FIFO read operation
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param enable: Enable/Disable sensor.
|
||||||
|
* @return 0 if OK, < 0 if ERROR
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
st_ism330is_shub_config_channels(struct st_ism330is_sensor *sensor, bool enable)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info;
|
||||||
|
struct st_ism330is_hw *hw = sensor->hw;
|
||||||
|
struct st_ism330is_sensor *cur_sensor;
|
||||||
|
u8 config[6] = {}, enable_mask;
|
||||||
|
int i, j = 0;
|
||||||
|
|
||||||
|
enable_mask = enable ? hw->enable_mask | BIT(sensor->id)
|
||||||
|
: hw->enable_mask & ~BIT(sensor->id);
|
||||||
|
|
||||||
|
for (i = ST_ISM330IS_ID_EXT0; i <= ST_ISM330IS_ID_EXT1; i++) {
|
||||||
|
if (!hw->iio_devs[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cur_sensor = iio_priv(hw->iio_devs[i]);
|
||||||
|
if (!(enable_mask & BIT(cur_sensor->id)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ext_info = &cur_sensor->ext_dev_info;
|
||||||
|
config[j] = (ext_info->ext_dev_i2c_addr << 1) | 1;
|
||||||
|
config[j + 1] =
|
||||||
|
ext_info->ext_dev_settings->ext_channels[0].address;
|
||||||
|
config[j + 2] = (ext_info->ext_dev_settings->data_len &
|
||||||
|
ST_ISM330IS_SLAVE_NUMOP_MASK);
|
||||||
|
j += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV1_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a valid ODR [SHUB]
|
||||||
|
*
|
||||||
|
* Check a valid ODR closest to the passed value
|
||||||
|
*
|
||||||
|
* @param sensor: SST IMU sensor instance.
|
||||||
|
* @param odr: ODR value (in Hz).
|
||||||
|
* @param val: ODR register value data pointer.
|
||||||
|
* @return 0 if OK, negative value for ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_get_odr_val(struct st_ism330is_sensor *sensor,
|
||||||
|
u32 odr, u8 *val)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ext_info->ext_dev_settings->odr_table.size; i++)
|
||||||
|
if (ext_info->ext_dev_settings->odr_table.odr_avl[i].mhz >= odr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i == ext_info->ext_dev_settings->odr_table.size)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*val = ext_info->ext_dev_settings->odr_table.odr_avl[i].val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set new ODR to sensor [SHUB]
|
||||||
|
*
|
||||||
|
* Set a valid ODR closest to the passed value
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param odr: ODR value (in Hz).
|
||||||
|
* @return 0 if OK, negative value for ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_set_odr(struct st_ism330is_sensor *sensor, u32 odr)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
struct st_ism330is_hw *hw = sensor->hw;
|
||||||
|
u8 odr_val;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_get_odr_val(sensor, odr, &odr_val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (sensor->mhz == odr && (hw->enable_mask & BIT(sensor->id)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return st_ism330is_shub_write_with_mask(sensor,
|
||||||
|
ext_info->ext_dev_settings->odr_table.reg.addr,
|
||||||
|
ext_info->ext_dev_settings->odr_table.reg.mask,
|
||||||
|
odr_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or Disable sensor [SHUB]
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param enable: Enable or disable the sensor [true,false].
|
||||||
|
* @return 0 if OK, negative value for ERROR
|
||||||
|
*/
|
||||||
|
int st_ism330is_shub_set_enable(struct st_ism330is_sensor *sensor, bool enable)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_config_channels(sensor, enable);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
err = st_ism330is_shub_set_odr(sensor, sensor->mhz);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
|
err = st_ism330is_shub_write_with_mask(sensor,
|
||||||
|
ext_info->ext_dev_settings->odr_table.reg.addr,
|
||||||
|
ext_info->ext_dev_settings->odr_table.reg.mask,
|
||||||
|
0);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext_info->ext_dev_settings->pwr_table.reg.addr) {
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
val = enable ? ext_info->ext_dev_settings->pwr_table.on_val
|
||||||
|
: ext_info->ext_dev_settings->pwr_table.off_val;
|
||||||
|
err = st_ism330is_shub_write_with_mask(sensor,
|
||||||
|
ext_info->ext_dev_settings->pwr_table.reg.addr,
|
||||||
|
ext_info->ext_dev_settings->pwr_table.reg.mask,
|
||||||
|
val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return st_ism330is_shub_master_enable(sensor, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 st_ism330is_get_unaligned_le24(const u8 *p)
|
||||||
|
{
|
||||||
|
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single sensor read operation [SHUB]
|
||||||
|
*
|
||||||
|
* @param sensor: ST IMU sensor instance
|
||||||
|
* @param ch: IIO Channel.
|
||||||
|
* @param val: Output data register value.
|
||||||
|
* @return IIO_VAL_INT if OK, negative value for ERROR
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
st_ism330is_shub_read_oneshot(struct st_ism330is_sensor *sensor,
|
||||||
|
struct iio_chan_spec const *ch, int *val)
|
||||||
|
{
|
||||||
|
int err, delay, len = ch->scan_type.realbits >> 3;
|
||||||
|
u8 data[4];
|
||||||
|
|
||||||
|
err = st_ism330is_shub_set_enable(sensor, true);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
delay = 1100000000 / sensor->mhz;
|
||||||
|
usleep_range(delay, delay + (delay >> 1));
|
||||||
|
|
||||||
|
err = st_ism330is_shub_read(sensor, ch->address, data, len);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
st_ism330is_shub_set_enable(sensor, false);
|
||||||
|
|
||||||
|
switch (len) {
|
||||||
|
case 3:
|
||||||
|
*val = (s32)st_ism330is_get_unaligned_le24(data);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*val = (s16)get_unaligned_le16(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read Sensor data configuration [SHUB]
|
||||||
|
*
|
||||||
|
* @param iio_dev: IIO Device.
|
||||||
|
* @param ch: IIO Channel.
|
||||||
|
* @param val: Data Buffer (MSB).
|
||||||
|
* @param val2: Data Buffer (LSB).
|
||||||
|
* @param mask: Data Mask.
|
||||||
|
* @return 0 if OK, -EINVAL value for ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_read_raw(struct iio_dev *iio_dev,
|
||||||
|
struct iio_chan_spec const *ch,
|
||||||
|
int *val, int *val2, long mask)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor = iio_priv(iio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_RAW:
|
||||||
|
ret = iio_device_claim_direct_mode(iio_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = st_ism330is_shub_read_oneshot(sensor, ch, val);
|
||||||
|
iio_device_release_direct_mode(iio_dev);
|
||||||
|
break;
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
*val = sensor->mhz;
|
||||||
|
ret = IIO_VAL_INT;
|
||||||
|
break;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
*val = 0;
|
||||||
|
*val2 = sensor->gain;
|
||||||
|
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write Sensor data configuration [SHUB]
|
||||||
|
*
|
||||||
|
* @param iio_dev: IIO Device.
|
||||||
|
* @param chan: IIO Channel.
|
||||||
|
* @param val: Data Buffer (MSB).
|
||||||
|
* @param val2: Data Buffer (LSB).
|
||||||
|
* @param mask: Data Mask.
|
||||||
|
* @return 0 if OK, -EINVAL value for ERROR
|
||||||
|
*/
|
||||||
|
static int st_ism330is_shub_write_raw(struct iio_dev *iio_dev,
|
||||||
|
struct iio_chan_spec const *chan,
|
||||||
|
int val, int val2, long mask)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor = iio_priv(iio_dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&iio_dev->mlock);
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ: {
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
val = val * 1000 + val2 / 1000;
|
||||||
|
err = st_ism330is_shub_get_odr_val(sensor, val, &data);
|
||||||
|
if (!err)
|
||||||
|
sensor->mhz = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
err = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&iio_dev->mlock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of available sensor ODR [SHUB]
|
||||||
|
*
|
||||||
|
* List of available ODR returned separated by commas
|
||||||
|
*
|
||||||
|
* @param dev: IIO Device.
|
||||||
|
* @param attr: IIO Channel attribute.
|
||||||
|
* @param buf: User buffer.
|
||||||
|
* @return buffer len
|
||||||
|
*/
|
||||||
|
static ssize_t
|
||||||
|
st_ism330is_sysfs_shub_sampling_freq_avail(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev));
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ext_info->ext_dev_settings->odr_table.size; i++) {
|
||||||
|
u16 val = ext_info->ext_dev_settings->odr_table.odr_avl[i].mhz;
|
||||||
|
|
||||||
|
if (val > 0)
|
||||||
|
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%03d ",
|
||||||
|
val / 1000, val % 1000);
|
||||||
|
}
|
||||||
|
buf[len - 1] = '\n';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of available sensor Full Scale [SHUB]
|
||||||
|
*
|
||||||
|
* List of available Full Scale returned separated by commas
|
||||||
|
*
|
||||||
|
* @param dev: IIO Device.
|
||||||
|
* @param attr: IIO Channel attribute.
|
||||||
|
* @param buf: User buffer.
|
||||||
|
* @return buffer len
|
||||||
|
*/
|
||||||
|
static ssize_t st_ism330is_sysfs_shub_scale_avail(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor = iio_priv(dev_to_iio_dev(dev));
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ext_info->ext_dev_settings->fs_table.fs_len; i++) {
|
||||||
|
u16 val = ext_info->ext_dev_settings->fs_table.fs_avl[i].gain;
|
||||||
|
|
||||||
|
if (val > 0)
|
||||||
|
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
buf[len - 1] = '\n';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_ism330is_sysfs_shub_sampling_freq_avail);
|
||||||
|
static IIO_DEVICE_ATTR(in_ext_scale_available, 0444,
|
||||||
|
st_ism330is_sysfs_shub_scale_avail, NULL, 0);
|
||||||
|
|
||||||
|
static struct attribute *st_ism330is_ext_attributes[] = {
|
||||||
|
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||||
|
&iio_dev_attr_in_ext_scale_available.dev_attr.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group st_ism330is_ext_attribute_group = {
|
||||||
|
.attrs = st_ism330is_ext_attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct iio_info st_ism330is_ext_info = {
|
||||||
|
.attrs = &st_ism330is_ext_attribute_group,
|
||||||
|
.read_raw = st_ism330is_shub_read_raw,
|
||||||
|
.write_raw = st_ism330is_shub_write_raw,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate IIO device [SHUB]
|
||||||
|
*
|
||||||
|
* @param hw: ST IMU MEMS hw instance.
|
||||||
|
* @param ext_settings: xternal sensor descritor entry.
|
||||||
|
* @param id: Sensor Identifier.
|
||||||
|
* @param i2c_addr: external I2C address on master bus.
|
||||||
|
* @return struct iio_dev *, NULL if ERROR
|
||||||
|
*/
|
||||||
|
static struct iio_dev *st_ism330is_shub_alloc_iio_dev(struct st_ism330is_hw *hw,
|
||||||
|
const struct st_ism330is_ext_dev_settings *ext_settings,
|
||||||
|
enum st_ism330is_sensor_id id, u8 i2c_addr)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor;
|
||||||
|
struct iio_dev *iio_dev;
|
||||||
|
|
||||||
|
iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
|
||||||
|
if (!iio_dev)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
|
iio_dev->dev.parent = hw->dev;
|
||||||
|
iio_dev->info = &st_ism330is_ext_info;
|
||||||
|
iio_dev->channels = ext_settings->ext_channels;
|
||||||
|
iio_dev->num_channels = ext_settings->ext_chan_depth;
|
||||||
|
sensor = iio_priv(iio_dev);
|
||||||
|
sensor->id = id;
|
||||||
|
sensor->hw = hw;
|
||||||
|
sensor->mhz = ext_settings->odr_table.odr_avl[0].mhz;
|
||||||
|
sensor->gain = ext_settings->fs_table.fs_avl[0].gain;
|
||||||
|
sensor->ext_dev_info.ext_dev_i2c_addr = i2c_addr;
|
||||||
|
sensor->ext_dev_info.ext_dev_settings = ext_settings;
|
||||||
|
|
||||||
|
switch (iio_dev->channels[0].type) {
|
||||||
|
case IIO_MAGN:
|
||||||
|
scnprintf(sensor->name, sizeof(sensor->name), "%s_magn",
|
||||||
|
ST_ISM330IS_DEV_NAME);
|
||||||
|
break;
|
||||||
|
case IIO_PRESSURE:
|
||||||
|
scnprintf(sensor->name, sizeof(sensor->name), "%s_press",
|
||||||
|
ST_ISM330IS_DEV_NAME);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scnprintf(sensor->name, sizeof(sensor->name), "%s_ext",
|
||||||
|
ST_ISM330IS_DEV_NAME);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iio_dev->name = sensor->name;
|
||||||
|
|
||||||
|
return iio_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
st_ism330is_shub_init_remote_sensor(struct st_ism330is_sensor *sensor)
|
||||||
|
{
|
||||||
|
struct st_ism330is_ext_dev_info *ext_info = &sensor->ext_dev_info;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (ext_info->ext_dev_settings->bdu_reg.addr)
|
||||||
|
err = st_ism330is_shub_write_with_mask(sensor,
|
||||||
|
ext_info->ext_dev_settings->bdu_reg.addr,
|
||||||
|
ext_info->ext_dev_settings->bdu_reg.mask, 1);
|
||||||
|
|
||||||
|
if (ext_info->ext_dev_settings->temp_comp_reg.addr)
|
||||||
|
err = st_ism330is_shub_write_with_mask(sensor,
|
||||||
|
ext_info->ext_dev_settings->temp_comp_reg.addr,
|
||||||
|
ext_info->ext_dev_settings->temp_comp_reg.mask, 1);
|
||||||
|
|
||||||
|
if (ext_info->ext_dev_settings->off_canc_reg.addr)
|
||||||
|
err = st_ism330is_shub_write_with_mask(sensor,
|
||||||
|
ext_info->ext_dev_settings->off_canc_reg.addr,
|
||||||
|
ext_info->ext_dev_settings->off_canc_reg.mask, 1);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Probe device function [SHUB]
|
||||||
|
*
|
||||||
|
* @param hw: ST IMU MEMS hw instance.
|
||||||
|
* @return 0 if OK, negative for ERROR
|
||||||
|
*/
|
||||||
|
int st_ism330is_shub_probe(struct st_ism330is_hw *hw)
|
||||||
|
{
|
||||||
|
const struct st_ism330is_ext_dev_settings *settings;
|
||||||
|
struct st_ism330is_sensor *acc_sensor, *sensor;
|
||||||
|
struct device_node *np = hw->dev->of_node;
|
||||||
|
u8 config[3], data, num_ext_dev = 0;
|
||||||
|
enum st_ism330is_sensor_id id;
|
||||||
|
int err, i = 0, j;
|
||||||
|
|
||||||
|
if (np && of_property_read_bool(np, "drive-pullup-shub")) {
|
||||||
|
dev_info(hw->dev, "enabling pull up on i2c master\n");
|
||||||
|
err = st_ism330is_shub_read_reg(hw,
|
||||||
|
ST_ISM330IS_REG_MASTER_CONFIG_ADDR,
|
||||||
|
&data, sizeof(data));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
data |= ST_ISM330IS_SHUB_PU_EN_MASK;
|
||||||
|
err = st_ism330is_shub_write_reg(hw,
|
||||||
|
ST_ISM330IS_REG_MASTER_CONFIG_ADDR,
|
||||||
|
&data, sizeof(data));
|
||||||
|
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
hw->i2c_master_pu = ST_ISM330IS_SHUB_PU_EN_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
acc_sensor = iio_priv(hw->iio_devs[ST_ISM330IS_ID_ACC]);
|
||||||
|
while (i < ARRAY_SIZE(st_ism330is_ext_dev_table) &&
|
||||||
|
num_ext_dev < ST_ISM330IS_MAX_SLV_NUM) {
|
||||||
|
settings = &st_ism330is_ext_dev_table[i];
|
||||||
|
|
||||||
|
for (j = 0; j < ARRAY_SIZE(settings->i2c_addr); j++) {
|
||||||
|
if (!settings->i2c_addr[j])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* read wai slave register */
|
||||||
|
config[0] = (settings->i2c_addr[j] << 1) | 1;
|
||||||
|
config[1] = settings->wai_addr;
|
||||||
|
config[2] = 1;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_write_reg(hw,
|
||||||
|
ST_ISM330IS_REG_SLV0_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = st_ism330is_shub_master_enable(acc_sensor, true);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
st_ism330is_shub_wait_complete(hw);
|
||||||
|
|
||||||
|
err = st_ism330is_shub_read_reg(hw,
|
||||||
|
ST_ISM330IS_REG_SENSOR_HUB_1_ADDR,
|
||||||
|
&data, sizeof(data));
|
||||||
|
|
||||||
|
st_ism330is_shub_master_enable(acc_sensor, false);
|
||||||
|
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (data != settings->wai_val)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
id = ST_ISM330IS_ID_EXT0 + num_ext_dev;
|
||||||
|
hw->iio_devs[id] = st_ism330is_shub_alloc_iio_dev(hw,
|
||||||
|
settings, id,
|
||||||
|
settings->i2c_addr[j]);
|
||||||
|
if (!hw->iio_devs[id])
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sensor = iio_priv(hw->iio_devs[id]);
|
||||||
|
err = st_ism330is_shub_init_remote_sensor(sensor);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
num_ext_dev++;
|
||||||
|
hw->ext_data_len += settings->data_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!num_ext_dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(config, 0, sizeof(config));
|
||||||
|
err = st_ism330is_shub_write_reg(hw, ST_ISM330IS_REG_SLV0_ADDR,
|
||||||
|
config, sizeof(config));
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* AuxSens = 3 + wr once */
|
||||||
|
data = ST_ISM330IS_WRITE_ONCE_MASK | 3 | hw->i2c_master_pu;
|
||||||
|
return st_ism330is_shub_write_reg(hw,
|
||||||
|
ST_ISM330IS_REG_MASTER_CONFIG_ADDR,
|
||||||
|
&data, sizeof(data));
|
||||||
|
}
|
63
drivers/iio/stm/imu/st_ism330is/st_ism330is_spi.c
Normal file
63
drivers/iio/stm/imu/st_ism330is/st_ism330is_spi.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_ism330is spi driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2023 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
#include "st_ism330is.h"
|
||||||
|
|
||||||
|
static const struct regmap_config st_ism330is_spi_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int st_ism330is_spi_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
regmap = devm_regmap_init_spi(spi, &st_ism330is_spi_regmap_config);
|
||||||
|
if (IS_ERR(regmap)) {
|
||||||
|
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||||
|
(int)PTR_ERR(regmap));
|
||||||
|
|
||||||
|
return PTR_ERR(regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return st_ism330is_probe(&spi->dev, spi->irq, regmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id st_ism330is_spi_of_match[] = {
|
||||||
|
{ .compatible = "st,ism330is" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, st_ism330is_spi_of_match);
|
||||||
|
|
||||||
|
static const struct spi_device_id st_ism330is_spi_id_table[] = {
|
||||||
|
{ ST_ISM330IS_DEV_NAME },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, st_ism330is_spi_id_table);
|
||||||
|
|
||||||
|
static struct spi_driver st_ism330is_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "st_" ST_ISM330IS_DEV_NAME "_spi",
|
||||||
|
.pm = &st_ism330is_pm_ops,
|
||||||
|
.of_match_table = of_match_ptr(st_ism330is_spi_of_match),
|
||||||
|
},
|
||||||
|
.probe = st_ism330is_spi_probe,
|
||||||
|
.id_table = st_ism330is_spi_id_table,
|
||||||
|
};
|
||||||
|
module_spi_driver(st_ism330is_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("MEMS Software Solutions Team");
|
||||||
|
MODULE_DESCRIPTION("STMicroelectronics st_ism330is spi driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
132
drivers/iio/stm/imu/st_ism330is/st_ism330is_triggers.c
Normal file
132
drivers/iio/stm/imu/st_ism330is/st_ism330is_triggers.c
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* STMicroelectronics st_ism330is trigger buffer library driver
|
||||||
|
*
|
||||||
|
* MEMS Software Solutions Team
|
||||||
|
*
|
||||||
|
* Copyright 2023 STMicroelectronics Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
#include <linux/iio/iio.h>
|
||||||
|
#include <linux/iio/buffer.h>
|
||||||
|
#include <linux/iio/sw_trigger.h>
|
||||||
|
#include <linux/iio/trigger.h>
|
||||||
|
#include <linux/iio/trigger_consumer.h>
|
||||||
|
#include <linux/iio/triggered_buffer.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "st_ism330is.h"
|
||||||
|
|
||||||
|
#define ST_ISM330IS_AG_SAMPLE_SIZE 6
|
||||||
|
#define ST_ISM330IS_PT_SAMPLE_SIZE 2
|
||||||
|
|
||||||
|
static int st_ism330is_buffer_enable(struct iio_dev *iio_dev, bool enable)
|
||||||
|
{
|
||||||
|
struct st_ism330is_sensor *sensor = iio_priv(iio_dev);
|
||||||
|
|
||||||
|
if (sensor->id == ST_ISM330IS_ID_EXT0 ||
|
||||||
|
sensor->id == ST_ISM330IS_ID_EXT1)
|
||||||
|
return st_ism330is_shub_set_enable(sensor, enable);
|
||||||
|
|
||||||
|
return st_ism330is_sensor_set_enable(sensor, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_ism330is_fifo_preenable(struct iio_dev *iio_dev)
|
||||||
|
{
|
||||||
|
return st_ism330is_buffer_enable(iio_dev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_ism330is_fifo_postdisable(struct iio_dev *iio_dev)
|
||||||
|
{
|
||||||
|
return st_ism330is_buffer_enable(iio_dev, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_buffer_setup_ops st_ism330is_buffer_setup_ops = {
|
||||||
|
.preenable = st_ism330is_fifo_preenable,
|
||||||
|
|
||||||
|
#if KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE
|
||||||
|
.postenable = iio_triggered_buffer_postenable,
|
||||||
|
.predisable = iio_triggered_buffer_predisable,
|
||||||
|
#endif /* LINUX_VERSION_CODE */
|
||||||
|
|
||||||
|
.postdisable = st_ism330is_fifo_postdisable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t st_ism330is_buffer_pollfunc(int irq, void *private)
|
||||||
|
{
|
||||||
|
u8 iio_buf[ALIGN(ST_ISM330IS_AG_SAMPLE_SIZE, sizeof(s64)) +
|
||||||
|
sizeof(s64) + sizeof(s64)];
|
||||||
|
struct iio_poll_func *pf = private;
|
||||||
|
struct iio_dev *indio_dev = pf->indio_dev;
|
||||||
|
struct st_ism330is_sensor *sensor = iio_priv(indio_dev);
|
||||||
|
struct st_ism330is_hw *hw = sensor->hw;
|
||||||
|
int addr = indio_dev->channels[0].address;
|
||||||
|
|
||||||
|
switch (indio_dev->channels[0].type) {
|
||||||
|
case IIO_ACCEL:
|
||||||
|
case IIO_ANGL_VEL:
|
||||||
|
st_ism330is_read_locked(hw, addr, &iio_buf,
|
||||||
|
ST_ISM330IS_AG_SAMPLE_SIZE);
|
||||||
|
break;
|
||||||
|
case IIO_TEMP:
|
||||||
|
st_ism330is_read_locked(hw, addr, &iio_buf,
|
||||||
|
ST_ISM330IS_PT_SAMPLE_SIZE);
|
||||||
|
break;
|
||||||
|
case IIO_PRESSURE:
|
||||||
|
st_ism330is_shub_read(sensor, addr, (u8 *)&iio_buf,
|
||||||
|
ST_ISM330IS_PT_SAMPLE_SIZE);
|
||||||
|
break;
|
||||||
|
case IIO_MAGN:
|
||||||
|
st_ism330is_shub_read(sensor, addr, (u8 *)&iio_buf,
|
||||||
|
ST_ISM330IS_AG_SAMPLE_SIZE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iio_push_to_buffers_with_timestamp(indio_dev, iio_buf,
|
||||||
|
iio_get_time_ns(indio_dev));
|
||||||
|
iio_trigger_notify_done(indio_dev->trig);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int st_ism330is_trig_set_state(struct iio_trigger *trig, bool state)
|
||||||
|
{
|
||||||
|
struct st_ism330is_hw *hw = iio_trigger_get_drvdata(trig);
|
||||||
|
|
||||||
|
dev_dbg(hw->dev, "trigger set %d\n", state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_trigger_ops st_ism330is_trigger_ops = {
|
||||||
|
.set_trigger_state = st_ism330is_trig_set_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
int st_ism330is_allocate_buffers(struct st_ism330is_hw *hw)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0;
|
||||||
|
i < ARRAY_SIZE(st_ism330is_triggered_main_sensor_list);
|
||||||
|
i++) {
|
||||||
|
enum st_ism330is_sensor_id id;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
id = st_ism330is_triggered_main_sensor_list[i];
|
||||||
|
if (!hw->iio_devs[id])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = devm_iio_triggered_buffer_setup(hw->dev,
|
||||||
|
hw->iio_devs[id], NULL,
|
||||||
|
st_ism330is_buffer_pollfunc,
|
||||||
|
&st_ism330is_buffer_setup_ops);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
3
stm_iio_configs/ism330is_defconfig
Normal file
3
stm_iio_configs/ism330is_defconfig
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_IIO_ST_ISM330IS=m
|
||||||
|
CONFIG_IIO_ST_ISM330IS_I2C=m
|
||||||
|
CONFIG_IIO_ST_ISM330IS_SPI=m
|
Loading…
Reference in New Issue
Block a user