// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics accelerometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/common/st_sensors.h>
#include "st_accel.h"
#define ST_ACCEL_NUMBER_DATA_CHANNELS 3
/* DEFAULT VALUE FOR SENSORS */
#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0 x28
#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0 x2a
#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0 x2c
/* FULLSCALE */
#define ST_ACCEL_FS_AVL_2G 2
#define ST_ACCEL_FS_AVL_4G 4
#define ST_ACCEL_FS_AVL_6G 6
#define ST_ACCEL_FS_AVL_8G 8
#define ST_ACCEL_FS_AVL_16G 16
#define ST_ACCEL_FS_AVL_100G 100
#define ST_ACCEL_FS_AVL_200G 200
#define ST_ACCEL_FS_AVL_400G 400
static const struct iio_mount_matrix *
st_accel_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
return &adata->mount_matrix;
}
static const struct iio_chan_spec_ext_info st_accel_mount_matrix_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_accel_get_mount_matrix),
{ }
};
static const struct iio_chan_spec st_accel_8bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1 , IIO_MOD_X, 's' , IIO_LE, 8 , 8 ,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1 ,
st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1 , IIO_MOD_Y, 's' , IIO_LE, 8 , 8 ,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1 ,
st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1 , IIO_MOD_Z, 's' , IIO_LE, 8 , 8 ,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1 ,
st_accel_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3 )
};
static const struct iio_chan_spec st_accel_12bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1 , IIO_MOD_X, 's' , IIO_LE, 12 , 16 ,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR,
st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1 , IIO_MOD_Y, 's' , IIO_LE, 12 , 16 ,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR,
st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1 , IIO_MOD_Z, 's' , IIO_LE, 12 , 16 ,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR,
st_accel_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3 )
};
static const struct iio_chan_spec st_accel_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1 , IIO_MOD_X, 's' , IIO_LE, 16 , 16 ,
ST_ACCEL_DEFAULT_OUT_X_L_ADDR,
st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1 , IIO_MOD_Y, 's' , IIO_LE, 16 , 16 ,
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR,
st_accel_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1 , IIO_MOD_Z, 's' , IIO_LE, 16 , 16 ,
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR,
st_accel_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3 )
};
static const struct st_sensor_settings st_accel_sensors_settings[] = {
{
.wai = 0 x33,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS3DH_ACCEL_DEV_NAME,
[1 ] = LSM303DLHC_ACCEL_DEV_NAME,
[2 ] = LSM330D_ACCEL_DEV_NAME,
[3 ] = LSM330DL_ACCEL_DEV_NAME,
[4 ] = LSM330DLC_ACCEL_DEV_NAME,
[5 ] = LSM303AGR_ACCEL_DEV_NAME,
[6 ] = LIS2DH12_ACCEL_DEV_NAME,
[7 ] = LIS3DE_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 1 , .value = 0 x01, },
{ .hz = 10 , .value = 0 x02, },
{ .hz = 25 , .value = 0 x03, },
{ .hz = 50 , .value = 0 x04, },
{ .hz = 100 , .value = 0 x05, },
{ .hz = 200 , .value = 0 x06, },
{ .hz = 400 , .value = 0 x07, },
{ .hz = 1600 , .value = 0 x08, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(1000 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(2000 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(4000 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(12000 ),
},
},
},
.bdu = {
.addr = 0 x23,
.mask = 0 x80,
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x10,
},
.addr_ihl = 0 x25,
.mask_ihl = 0 x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
.wai = 0 x32,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS331DLH_ACCEL_DEV_NAME,
[1 ] = LSM303DL_ACCEL_DEV_NAME,
[2 ] = LSM303DLH_ACCEL_DEV_NAME,
[3 ] = LSM303DLM_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 x18,
.odr_avl = {
{ .hz = 50 , .value = 0 x00, },
{ .hz = 100 , .value = 0 x01, },
{ .hz = 400 , .value = 0 x02, },
{ .hz = 1000 , .value = 0 x03, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xe0,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(1000 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(2000 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(3900 ),
},
},
},
.bdu = {
.addr = 0 x23,
.mask = 0 x80,
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x02,
.addr_od = 0 x22,
.mask_od = 0 x40,
},
.int2 = {
.addr = 0 x22,
.mask = 0 x10,
.addr_od = 0 x22,
.mask_od = 0 x40,
},
.addr_ihl = 0 x22,
.mask_ihl = 0 x80,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
.wai = 0 x40,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LSM330_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 3 , .value = 0 x01, },
{ .hz = 6 , .value = 0 x02, },
{ .hz = 12 , .value = 0 x03, },
{ .hz = 25 , .value = 0 x04, },
{ .hz = 50 , .value = 0 x05, },
{ .hz = 100 , .value = 0 x06, },
{ .hz = 200 , .value = 0 x07, },
{ .hz = 400 , .value = 0 x08, },
{ .hz = 800 , .value = 0 x09, },
{ .hz = 1600 , .value = 0 x0a, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x24,
.mask = 0 x38,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(61 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(122 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_6G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(183 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(244 ),
},
[4 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x04,
.gain = IIO_G_TO_M_S_2(732 ),
},
},
},
.bdu = {
.addr = 0 x20,
.mask = 0 x08,
},
.drdy_irq = {
.int1 = {
.addr = 0 x23,
.mask = 0 x80,
},
.addr_ihl = 0 x23,
.mask_ihl = 0 x40,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
.ig1 = {
.en_addr = 0 x23,
.en_mask = 0 x08,
},
},
.sim = {
.addr = 0 x24,
.value = BIT(0 ),
},
.multi_read_bit = false ,
.bootime = 2 ,
},
{
.wai = 0 x3a,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS3LV02DL_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 x30, /* DF1 and DF0 */
.odr_avl = {
{ .hz = 40 , .value = 0 x00, },
{ .hz = 160 , .value = 0 x01, },
{ .hz = 640 , .value = 0 x02, },
{ .hz = 2560 , .value = 0 x03, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xc0,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x21,
.mask = 0 x80,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(1000 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_6G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(3000 ),
},
},
},
.bdu = {
.addr = 0 x21,
.mask = 0 x40,
},
/*
* Data Alignment Setting - needs to be set to get
* left-justified data like all other sensors.
*/
.das = {
.addr = 0 x21,
.mask = 0 x01,
},
.drdy_irq = {
.int1 = {
.addr = 0 x21,
.mask = 0 x04,
},
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x21,
.value = BIT(1 ),
},
.multi_read_bit = true ,
.bootime = 2 , /* guess */
},
{
.wai = 0 x3b,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS331DL_ACCEL_DEV_NAME,
[1 ] = LIS302DL_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 x80,
.odr_avl = {
{ .hz = 100 , .value = 0 x00, },
{ .hz = 400 , .value = 0 x01, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 x40,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x20,
.mask = 0 x20,
/*
* TODO: check these resulting gain settings, these are
* not in the datsheet
*/
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(18000 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(72000 ),
},
},
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x04,
.addr_od = 0 x22,
.mask_od = 0 x40,
},
.int2 = {
.addr = 0 x22,
.mask = 0 x20,
.addr_od = 0 x22,
.mask_od = 0 x40,
},
.addr_ihl = 0 x22,
.mask_ihl = 0 x80,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x21,
.value = BIT(7 ),
},
.multi_read_bit = false ,
.bootime = 2 , /* guess */
},
{
.wai = 0 x32,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = H3LIS331DL_ACCEL_DEV_NAME,
[1 ] = IIS328DQ_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 x18,
.odr_avl = {
{ .hz = 50 , .value = 0 x00, },
{ .hz = 100 , .value = 0 x01, },
{ .hz = 400 , .value = 0 x02, },
{ .hz = 1000 , .value = 0 x03, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 x20,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_100G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(49000 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_200G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(98000 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_400G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(195000 ),
},
},
},
.bdu = {
.addr = 0 x23,
.mask = 0 x80,
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x02,
},
.int2 = {
.addr = 0 x22,
.mask = 0 x10,
},
.addr_ihl = 0 x22,
.mask_ihl = 0 x80,
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
/* No WAI register present */
.sensors_supported = {
[0 ] = LIS3L02DQ_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 x30,
.odr_avl = {
{ .hz = 280 , .value = 0 x00, },
{ .hz = 560 , .value = 0 x01, },
{ .hz = 1120 , .value = 0 x02, },
{ .hz = 4480 , .value = 0 x03, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xc0,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.gain = IIO_G_TO_M_S_2(488 ),
},
},
},
/*
* The part has a BDU bit but if set the data is never
* updated so don't set it.
*/
.bdu = {
},
.drdy_irq = {
.int1 = {
.addr = 0 x21,
.mask = 0 x04,
},
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x21,
.value = BIT(1 ),
},
.multi_read_bit = false ,
.bootime = 2 ,
},
{
.wai = 0 x33,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LNG2DM_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 1 , .value = 0 x01, },
{ .hz = 10 , .value = 0 x02, },
{ .hz = 25 , .value = 0 x03, },
{ .hz = 50 , .value = 0 x04, },
{ .hz = 100 , .value = 0 x05, },
{ .hz = 200 , .value = 0 x06, },
{ .hz = 400 , .value = 0 x07, },
{ .hz = 1600 , .value = 0 x08, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(15600 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(31200 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(62500 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(187500 ),
},
},
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x10,
},
.addr_ihl = 0 x25,
.mask_ihl = 0 x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
.wai = 0 x44,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS2DW12_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 1 , .value = 0 x01, },
{ .hz = 12 , .value = 0 x02, },
{ .hz = 25 , .value = 0 x03, },
{ .hz = 50 , .value = 0 x04, },
{ .hz = 100 , .value = 0 x05, },
{ .hz = 200 , .value = 0 x06, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.fs = {
.addr = 0 x25,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(976 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(1952 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(3904 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(7808 ),
},
},
},
.bdu = {
.addr = 0 x21,
.mask = 0 x08,
},
.drdy_irq = {
.int1 = {
.addr = 0 x23,
.mask = 0 x01,
.addr_od = 0 x22,
.mask_od = 0 x20,
},
.int2 = {
.addr = 0 x24,
.mask = 0 x01,
.addr_od = 0 x22,
.mask_od = 0 x20,
},
.addr_ihl = 0 x22,
.mask_ihl = 0 x08,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x01,
},
},
.sim = {
.addr = 0 x21,
.value = BIT(0 ),
},
.multi_read_bit = false ,
.bootime = 2 ,
},
{
.wai = 0 x11,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS3DHH_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
/* just ODR = 1100Hz available */
.odr_avl = {
{ .hz = 1100 , .value = 0 x00, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 x80,
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.fs = {
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.gain = IIO_G_TO_M_S_2(76 ),
},
},
},
.bdu = {
.addr = 0 x20,
.mask = 0 x01,
},
.drdy_irq = {
.int1 = {
.addr = 0 x21,
.mask = 0 x80,
.addr_od = 0 x23,
.mask_od = 0 x04,
},
.int2 = {
.addr = 0 x22,
.mask = 0 x80,
.addr_od = 0 x23,
.mask_od = 0 x08,
},
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.multi_read_bit = false ,
.bootime = 2 ,
},
{
.wai = 0 x33,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS2DE12_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 1 , .value = 0 x01, },
{ .hz = 10 , .value = 0 x02, },
{ .hz = 25 , .value = 0 x03, },
{ .hz = 50 , .value = 0 x04, },
{ .hz = 100 , .value = 0 x05, },
{ .hz = 200 , .value = 0 x06, },
{ .hz = 400 , .value = 0 x07, },
{ .hz = 1620 , .value = 0 x08, },
{ .hz = 5376 , .value = 0 x09, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(15600 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(31200 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(62500 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(187500 ),
},
},
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x10,
},
.addr_ihl = 0 x25,
.mask_ihl = 0 x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
.wai = 0 x43,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS2DS12_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 10 , .value = 0 x01, },
{ .hz = 50 , .value = 0 x02, },
{ .hz = 100 , .value = 0 x03, },
{ .hz = 200 , .value = 0 x04, },
{ .hz = 400 , .value = 0 x05, },
{ .hz = 800 , .value = 0 x06, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x20,
.mask = 0 x0c,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(61 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(122 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(244 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(488 ),
},
},
},
.bdu = {
.addr = 0 x20,
.mask = 0 x01,
},
.drdy_irq = {
.int1 = {
.addr = 0 x23,
.mask = 0 x01,
},
.int2 = {
.addr = 0 x24,
.mask = 0 x01,
},
.addr_ihl = 0 x22,
.mask_ihl = 0 x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x01,
},
},
.sim = {
.addr = 0 x21,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
.wai = 0 x41,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LIS2HH12_ACCEL_DEV_NAME,
[1 ] = LSM303C_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 x70,
.odr_avl = {
{ .hz = 10 , .value = 0 x01, },
{ .hz = 50 , .value = 0 x02, },
{ .hz = 100 , .value = 0 x03, },
{ .hz = 200 , .value = 0 x04, },
{ .hz = 400 , .value = 0 x05, },
{ .hz = 800 , .value = 0 x06, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 x70,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(61 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(122 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(244 ),
},
},
},
.bdu = {
.addr = 0 x20,
.mask = 0 x08,
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x01,
},
.int2 = {
.addr = 0 x25,
.mask = 0 x01,
},
.addr_ihl = 0 x24,
.mask_ihl = 0 x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
.wai = 0 x49,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = LSM9DS0_IMU_DEV_NAME,
[1 ] = LSM303D_IMU_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
.odr = {
.addr = 0 x20,
.mask = GENMASK(7 , 4 ),
.odr_avl = {
{ 3 , 0 x01, },
{ 6 , 0 x02, },
{ 12 , 0 x03, },
{ 25 , 0 x04, },
{ 50 , 0 x05, },
{ 100 , 0 x06, },
{ 200 , 0 x07, },
{ 400 , 0 x08, },
{ 800 , 0 x09, },
{ 1600 , 0 x0a, },
},
},
.pw = {
.addr = 0 x20,
.mask = GENMASK(7 , 4 ),
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x21,
.mask = GENMASK(5 , 3 ),
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(61 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(122 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_6G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(183 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(244 ),
},
[4 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x04,
.gain = IIO_G_TO_M_S_2(732 ),
},
},
},
.bdu = {
.addr = 0 x20,
.mask = BIT(3 ),
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = BIT(2 ),
},
.int2 = {
.addr = 0 x23,
.mask = BIT(3 ),
},
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = GENMASK(2 , 0 ),
},
},
.sim = {
.addr = 0 x21,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
{
/*
* Not an ST part. Register-compatible with the LIS2DH, even
* though the WAI value is different.
*/
.wai = 0 x11,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0 ] = SC7A20_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
.addr = 0 x20,
.mask = 0 xf0,
.odr_avl = {
{ .hz = 1 , .value = 0 x01, },
{ .hz = 10 , .value = 0 x02, },
{ .hz = 25 , .value = 0 x03, },
{ .hz = 50 , .value = 0 x04, },
{ .hz = 100 , .value = 0 x05, },
{ .hz = 200 , .value = 0 x06, },
{ .hz = 400 , .value = 0 x07, },
{ .hz = 1600 , .value = 0 x08, },
},
},
.pw = {
.addr = 0 x20,
.mask = 0 xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0 x23,
.mask = 0 x30,
.fs_avl = {
[0 ] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0 x00,
.gain = IIO_G_TO_M_S_2(1000 ),
},
[1 ] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0 x01,
.gain = IIO_G_TO_M_S_2(2000 ),
},
[2 ] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0 x02,
.gain = IIO_G_TO_M_S_2(4000 ),
},
[3 ] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0 x03,
.gain = IIO_G_TO_M_S_2(12000 ),
},
},
},
.bdu = {
.addr = 0 x23,
.mask = 0 x80,
},
.drdy_irq = {
.int1 = {
.addr = 0 x22,
.mask = 0 x10,
},
.addr_ihl = 0 x25,
.mask_ihl = 0 x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0 x07,
},
},
.sim = {
.addr = 0 x23,
.value = BIT(0 ),
},
.multi_read_bit = true ,
.bootime = 2 ,
},
};
/* Default accel DRDY is available on INT1 pin */
static const struct st_sensors_platform_data default_accel_pdata = {
.drdy_int_pin = 1 ,
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch, int *val,
int *val2, long mask)
{
int err;
struct st_sensor_data *adata = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
err = st_sensors_read_info_raw(indio_dev, ch, val);
if (err < 0 )
goto read_error;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = adata->current_fullscale->gain / 1000000 ;
*val2 = adata->current_fullscale->gain % 1000000 ;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = adata->odr;
return IIO_VAL_INT;
default :
return -EINVAL;
}
read_error:
return err;
}
static int st_accel_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE: {
int gain;
gain = val * 1000000 + val2;
return st_sensors_set_fullscale_by_gain(indio_dev, gain);
}
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
return st_sensors_set_odr(indio_dev, val);
default :
return -EINVAL;
}
}
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available);
static struct attribute *st_accel_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_accel_attribute_group = {
.attrs = st_accel_attributes,
};
static const struct iio_info accel_info = {
.attrs = &st_accel_attribute_group,
.read_raw = &st_accel_read_raw,
.write_raw = &st_accel_write_raw,
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
static const struct iio_trigger_ops st_accel_trigger_ops = {
.set_trigger_state = ST_ACCEL_TRIGGER_SET_STATE,
.validate_device = st_sensors_validate_device,
};
#define ST_ACCEL_TRIGGER_OPS (&st_accel_trigger_ops)
#else
#define ST_ACCEL_TRIGGER_OPS NULL
#endif
#ifdef CONFIG_ACPI
/* Read ST-specific _ONT orientation data from ACPI and generate an
* appropriate mount matrix.
*/
static int apply_acpi_orientation(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
struct acpi_device *adev;
union acpi_object *ont;
union acpi_object *elements;
acpi_status status;
struct device *parent = indio_dev->dev.parent;
int ret = -EINVAL;
unsigned int val;
int i, j;
int final_ont[3 ][3 ] = { { 0 }, };
/* For some reason, ST's _ONT translation does not apply directly
* to the data read from the sensor. Another translation must be
* performed first, as described by the matrix below. Perhaps
* ST required this specific translation for the first product
* where the device was mounted?
*/
const int default_ont[3 ][3 ] = {
{ 0 , 1 , 0 },
{ -1 , 0 , 0 },
{ 0 , 0 , -1 },
};
adev = ACPI_COMPANION(parent);
if (!adev)
return -ENXIO;
/* Read _ONT data, which should be a package of 6 integers. */
status = acpi_evaluate_object(adev->handle, "_ONT" , NULL, &buffer);
if (status == AE_NOT_FOUND) {
return -ENXIO;
} else if (ACPI_FAILURE(status)) {
dev_warn(parent, "failed to execute _ONT: %d\n" , status);
return status;
}
ont = buffer.pointer;
if (ont->type != ACPI_TYPE_PACKAGE || ont->package.count != 6 )
goto out;
/* The first 3 integers provide axis order information.
* e.g. 0 1 2 would indicate normal X,Y,Z ordering.
* e.g. 1 0 2 indicates that data arrives in order Y,X,Z.
*/
elements = ont->package.elements;
for (i = 0 ; i < 3 ; i++) {
if (elements[i].type != ACPI_TYPE_INTEGER)
goto out;
val = elements[i].integer.value;
if (val > 2 )
goto out;
/* Avoiding full matrix multiplication, we simply reorder the
* columns in the default_ont matrix according to the
* ordering provided by _ONT.
*/
final_ont[0 ][i] = default_ont[0 ][val];
final_ont[1 ][i] = default_ont[1 ][val];
final_ont[2 ][i] = default_ont[2 ][val];
}
/* The final 3 integers provide sign flip information.
* 0 means no change, 1 means flip.
* e.g. 0 0 1 means that Z data should be sign-flipped.
* This is applied after the axis reordering from above.
*/
elements += 3 ;
for (i = 0 ; i < 3 ; i++) {
if (elements[i].type != ACPI_TYPE_INTEGER)
goto out;
val = elements[i].integer.value;
if (val != 0 && val != 1 )
goto out;
if (!val)
continue ;
/* Flip the values in the indicated column */
final_ont[0 ][i] *= -1 ;
final_ont[1 ][i] *= -1 ;
final_ont[2 ][i] *= -1 ;
}
/* Convert our integer matrix to a string-based iio_mount_matrix */
for (i = 0 ; i < 3 ; i++) {
for (j = 0 ; j < 3 ; j++) {
int matrix_val = final_ont[i][j];
char *str_value;
switch (matrix_val) {
case -1 :
str_value = "-1" ;
break ;
case 0 :
str_value = "0" ;
break ;
case 1 :
str_value = "1" ;
break ;
default :
goto out;
}
adata->mount_matrix.rotation[i * 3 + j] = str_value;
}
}
ret = 0 ;
dev_info(parent, "computed mount matrix from ACPI\n" );
out:
kfree(buffer.pointer);
if (ret)
dev_dbg(parent,
"failed to apply ACPI orientation data: %d\n" , ret);
return ret;
}
#else /* !CONFIG_ACPI */
static int apply_acpi_orientation(struct iio_dev *indio_dev)
{
return -EINVAL;
}
#endif
/*
* st_accel_get_settings() - get sensor settings from device name
* @name: device name buffer reference.
*
* Return: valid reference on success, NULL otherwise.
*/
const struct st_sensor_settings *st_accel_get_settings(const char *name)
{
int index = st_sensors_get_settings_index(name,
st_accel_sensors_settings,
ARRAY_SIZE(st_accel_sensors_settings));
if (index < 0 )
return NULL;
return &st_accel_sensors_settings[index];
}
EXPORT_SYMBOL_NS(st_accel_get_settings, "IIO_ST_SENSORS" );
int st_accel_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
struct device *parent = indio_dev->dev.parent;
struct st_sensors_platform_data *pdata = dev_get_platdata(parent);
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &accel_info;
err = st_sensors_verify_id(indio_dev);
if (err < 0 )
return err;
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
indio_dev->channels = adata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
/*
* First try specific ACPI methods to retrieve orientation then try the
* generic function.
*/
err = apply_acpi_orientation(indio_dev);
if (err) {
err = iio_read_mount_matrix(parent, &adata->mount_matrix);
if (err)
return err;
}
adata->current_fullscale = &adata->sensor_settings->fs.fs_avl[0 ];
adata->odr = adata->sensor_settings->odr.odr_avl[0 ].hz;
if (!pdata)
pdata = (struct st_sensors_platform_data *)&default_accel_pdata;
err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0 )
return err;
err = st_accel_allocate_ring(indio_dev);
if (err < 0 )
return err;
if (adata->irq > 0 ) {
err = st_sensors_allocate_trigger(indio_dev,
ST_ACCEL_TRIGGER_OPS);
if (err < 0 )
return err;
}
return devm_iio_device_register(parent, indio_dev);
}
EXPORT_SYMBOL_NS(st_accel_common_probe, "IIO_ST_SENSORS" );
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>" );
MODULE_DESCRIPTION("STMicroelectronics accelerometers driver" );
MODULE_LICENSE("GPL v2" );
MODULE_IMPORT_NS("IIO_ST_SENSORS" );
Messung V0.5 in Prozent C=90 H=98 G=94
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland