Skip to content

Commit

Permalink
sensor: lsm6dsv16x: add i3c support
Browse files Browse the repository at this point in the history
Add I3C support to the lsm6dsv16x.

Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
  • Loading branch information
XenuIsWatching authored and kartben committed Dec 18, 2024
1 parent cbfabf8 commit eb93ba0
Show file tree
Hide file tree
Showing 6 changed files with 397 additions and 82 deletions.
15 changes: 9 additions & 6 deletions drivers/sensor/st/lsm6dsv16x/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
# SPDX-License-Identifier: Apache-2.0

menuconfig LSM6DSV16X
bool "LSM6DSV16X I2C/SPI accelerometer and gyroscope Chip"
bool "LSM6DSV16X I3C/I2C/SPI accelerometer and gyroscope Chip"
default y
depends on DT_HAS_ST_LSM6DSV16X_ENABLED
depends on ZEPHYR_HAL_ST_MODULE
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),i2c)
select I3C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),i3c)
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),spi)
select HAS_STMEMSC
select USE_STDC_LSM6DSV16X
Expand All @@ -23,7 +24,7 @@ config LSM6DSV16X_STREAM
bool "Use hardware FIFO to stream data"
select LSM6DSV16X_TRIGGER
default y
depends on I2C_RTIO || SPI_RTIO
depends on I2C_RTIO || SPI_RTIO || I3C_RTIO
depends on SENSOR_ASYNC_API
help
Use this config option to enable streaming sensor data via RTIO subsystem.
Expand All @@ -40,16 +41,18 @@ config LSM6DSV16X_TRIGGER_NONE

config LSM6DSV16X_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
depends on GPIO || I3C
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_ST_LSM6DSV16X),int1-gpios) ||\
$(dt_compat_any_has_prop,$(DT_COMPAT_ST_LSM6DSV16X),int2-gpios)
$(dt_compat_any_has_prop,$(DT_COMPAT_ST_LSM6DSV16X),int2-gpios) ||\
$(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),i3c)
select LSM6DSV16X_TRIGGER

config LSM6DSV16X_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
depends on GPIO || I3C
depends on $(dt_compat_any_has_prop,$(DT_COMPAT_ST_LSM6DSV16X),int1-gpios) ||\
$(dt_compat_any_has_prop,$(DT_COMPAT_ST_LSM6DSV16X),int2-gpios)
$(dt_compat_any_has_prop,$(DT_COMPAT_ST_LSM6DSV16X),int2-gpios) ||\
$(dt_compat_on_bus,$(DT_COMPAT_ST_LSM6DSV16X),i3c)
select LSM6DSV16X_TRIGGER

endchoice
Expand Down
106 changes: 95 additions & 11 deletions drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,20 @@ static int lsm6dsv16x_init_chip(const struct device *dev)
uint8_t chip_id;
uint8_t odr, fs;

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
if (cfg->i3c.bus != NULL) {
/*
* Need to grab the pointer to the I3C device descriptor
* before we can talk to the sensor.
*/
lsm6dsv16x->i3c_dev = i3c_device_find(cfg->i3c.bus, &cfg->i3c.dev_id);
if (lsm6dsv16x->i3c_dev == NULL) {
LOG_ERR("Cannot find I3C device descriptor");
return -ENODEV;
}
}
#endif

/* All registers except 0x01 are different between banks, including the WHO_AM_I
* register and the register used for a SW reset. If the lsm6dsv16x wasn't on the user
* bank when it reset, then both the chip id check and the sw reset will fail unless we
Expand All @@ -973,13 +987,26 @@ static int lsm6dsv16x_init_chip(const struct device *dev)
return -EIO;
}

/* reset device (sw_por) */
if (lsm6dsv16x_reset_set(ctx, LSM6DSV16X_GLOBAL_RST) < 0) {
return -EIO;
}
/* Resetting the whole device while using I3C will also reset the DA, therefore perform
* only a software reset if the bus is I3C. It should be assumed that the device was
* already fully reset by the I3C CCC RSTACT (whole chip) done as apart of the I3C Bus
* initialization.
*/
if (ON_I3C_BUS(cfg)) {
/* Restore default configuration */
lsm6dsv16x_reset_set(ctx, LSM6DSV16X_RESTORE_CAL_PARAM);

/* wait 150us as reported in AN5763 */
k_sleep(K_USEC(150));
} else {
/* reset device (sw_por) */
if (lsm6dsv16x_reset_set(ctx, LSM6DSV16X_GLOBAL_RST) < 0) {
return -EIO;
}

/* wait 30ms as reported in AN5763 */
k_sleep(K_MSEC(30));
/* wait 30ms as reported in AN5763 */
k_sleep(K_MSEC(30));
}

fs = cfg->accel_range;
LOG_DBG("accel range is %d", fs);
Expand Down Expand Up @@ -1012,6 +1039,23 @@ static int lsm6dsv16x_init_chip(const struct device *dev)
return -EIO;
}

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM) && (ON_I3C_BUS(cfg))) {
/*
* Set MRL to the Max Size of the FIFO so the entire FIFO can be read
* out at once
*/
struct i3c_ccc_mrl setmrl = {
.len = 0x0700,
.ibi_len = lsm6dsv16x->i3c_dev->data_length.max_ibi,
};
if (i3c_ccc_do_setmrl(lsm6dsv16x->i3c_dev, &setmrl) < 0) {
LOG_ERR("failed to set mrl");
return -EIO;
}
}
#endif

if (lsm6dsv16x_block_data_update_set(ctx, 1) < 0) {
LOG_DBG("failed to set BDU mode");
return -EIO;
Expand Down Expand Up @@ -1164,15 +1208,55 @@ static int lsm6dsv16x_init(const struct device *dev)
static const struct lsm6dsv16x_config lsm6dsv16x_config_##inst = \
LSM6DSV16X_CONFIG_I2C(inst); \

/*
* Instantiation macros used when a device is on an I3C bus.
*/

#define LSM6DSV16X_I3C_RTIO_DEFINE(inst) \
I3C_DT_IODEV_DEFINE(lsm6dsv16x_i3c_iodev_##inst, DT_DRV_INST(inst)); \
RTIO_DEFINE(lsm6dsv16x_rtio_ctx_##inst, 4, 4);

#define LSM6DSV16X_CONFIG_I3C(inst) \
{ \
STMEMSC_CTX_I3C(&lsm6dsv16x_config_##inst.stmemsc_cfg), \
.stmemsc_cfg = { \
.i3c = &lsm6dsv16x_data_##inst.i3c_dev, \
}, \
.i3c.bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \
.i3c.dev_id = I3C_DEVICE_ID_DT_INST(inst), \
IF_ENABLED(CONFIG_LSM6DSV16X_TRIGGER, \
(.int_en_i3c = DT_INST_PROP(inst, int_en_i3c), \
.bus_act_sel = DT_INST_ENUM_IDX(inst, bus_act_sel_us),)) \
LSM6DSV16X_CONFIG_COMMON(inst) \
}

#define LSM6DSV16X_DEFINE_I3C(inst) \
IF_ENABLED(CONFIG_LSM6DSV16X_STREAM, (LSM6DSV16X_I3C_RTIO_DEFINE(inst))); \
static struct lsm6dsv16x_data lsm6dsv16x_data_##inst = { \
IF_ENABLED(CONFIG_LSM6DSV16X_STREAM, \
(.rtio_ctx = &lsm6dsv16x_rtio_ctx_##inst, \
.iodev = &lsm6dsv16x_i3c_iodev_##inst, \
.bus_type = BUS_I3C,)) \
}; \
static const struct lsm6dsv16x_config lsm6dsv16x_config_##inst = \
LSM6DSV16X_CONFIG_I3C(inst); \

#define LSM6DSV16X_DEFINE_I3C_OR_I2C(inst) \
COND_CODE_0(DT_INST_PROP_BY_IDX(inst, reg, 1), \
(LSM6DSV16X_DEFINE_I2C(inst)), \
(LSM6DSV16X_DEFINE_I3C(inst)))

/*
* Main instantiation macro. Use of COND_CODE_1() selects the right
* bus-specific macro at preprocessor time.
*/

#define LSM6DSV16X_DEFINE(inst) \
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
(LSM6DSV16X_DEFINE_SPI(inst)), \
(LSM6DSV16X_DEFINE_I2C(inst))); \
LSM6DSV16X_DEVICE_INIT(inst)
#define LSM6DSV16X_DEFINE(inst) \
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
(LSM6DSV16X_DEFINE_SPI(inst)), \
(COND_CODE_1(DT_INST_ON_BUS(inst, i3c), \
(LSM6DSV16X_DEFINE_I3C_OR_I2C(inst)), \
(LSM6DSV16X_DEFINE_I2C(inst))))); \
LSM6DSV16X_DEVICE_INIT(inst)

DT_INST_FOREACH_STATUS_OKAY(LSM6DSV16X_DEFINE)
51 changes: 48 additions & 3 deletions drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@
#include <zephyr/drivers/i2c.h>
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
#include <zephyr/drivers/i3c.h>
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) */

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
#define ON_I3C_BUS(cfg) (cfg->i3c.bus != NULL)
#define I3C_INT_PIN(cfg) (cfg->int_en_i3c)
#else
#define ON_I3C_BUS(cfg) (false)
#define I3C_INT_PIN(cfg) (false)
#endif

#define LSM6DSV16X_EN_BIT 0x01
#define LSM6DSV16X_DIS_BIT 0x00

Expand All @@ -46,6 +58,9 @@ struct lsm6dsv16x_config {
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
const struct spi_dt_spec spi;
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
struct i3c_device_desc **i3c;
#endif
} stmemsc_cfg;
uint8_t accel_pm;
Expand All @@ -66,7 +81,18 @@ struct lsm6dsv16x_config {
const struct gpio_dt_spec int2_gpio;
uint8_t drdy_pin;
bool trig_enabled;
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
bool int_en_i3c;
lsm6dsv16x_i3c_ibi_time_t bus_act_sel;
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) */
#endif /* CONFIG_LSM6DSV16X_TRIGGER */

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
struct {
const struct device *bus;
const struct i3c_device_id dev_id;
} i3c;
#endif
};

union samples {
Expand All @@ -78,6 +104,19 @@ union samples {

#define LSM6DSV16X_SHUB_MAX_NUM_TARGETS 3

struct lsm6dsv16x_ibi_payload {
uint8_t mdb;
uint8_t fifo_status1;
uint8_t fifo_status2;
uint8_t all_int_src;
uint8_t status_reg;
uint8_t status_reg_ois;
uint8_t status_master_main;
uint8_t emb_func_status;
uint8_t fsm_status;
uint8_t mlc_status;
} __packed;

struct lsm6dsv16x_data {
const struct device *dev;
int16_t acc[3];
Expand Down Expand Up @@ -118,8 +157,8 @@ struct lsm6dsv16x_data {
uint8_t accel_batch_odr : 4;
uint8_t gyro_batch_odr : 4;
uint8_t temp_batch_odr : 2;
uint8_t bus_type : 1; /* I2C is 0, SPI is 1 */
uint8_t reserved : 5;
uint8_t bus_type : 2; /* I2C is 0, SPI is 1, I3C is 2 */
uint8_t reserved : 4;
#endif

#ifdef CONFIG_LSM6DSV16X_TRIGGER
Expand All @@ -136,16 +175,22 @@ struct lsm6dsv16x_data {
#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD)
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_LSM6DSV16X_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem gpio_sem;
struct k_sem intr_sem;
#elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif
#endif /* CONFIG_LSM6DSV16X_TRIGGER */

#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
struct i3c_device_desc *i3c_dev;
struct lsm6dsv16x_ibi_payload ibi_payload;
#endif
};

#ifdef CONFIG_LSM6DSV16X_STREAM
#define BUS_I2C 0
#define BUS_SPI 1
#define BUS_I3C 2

static inline uint8_t lsm6dsv16x_bus_reg(struct lsm6dsv16x_data *data, uint8_t x)
{
Expand Down
Loading

0 comments on commit eb93ba0

Please sign in to comment.