diff --git a/drivers/sensor/st/lsm6dsv16x/Kconfig b/drivers/sensor/st/lsm6dsv16x/Kconfig index ae02dfd1e2ea..9d41fdda2559 100644 --- a/drivers/sensor/st/lsm6dsv16x/Kconfig +++ b/drivers/sensor/st/lsm6dsv16x/Kconfig @@ -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 @@ -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. @@ -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 diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c index cd89d94b64b0..2cf2fd3acb7d 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.c @@ -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 @@ -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); @@ -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; @@ -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) diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h index 79daeb476b0b..6bfdd710aeed 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x.h @@ -26,6 +26,18 @@ #include #endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) +#include +#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 @@ -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; @@ -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 { @@ -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]; @@ -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 @@ -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) { diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c index 42537fb4f95a..055f56ca1b53 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_rtio_stream.c @@ -70,7 +70,7 @@ static void lsm6dsv16x_config_fifo(const struct device *dev, uint8_t fifo_irq) #endif /* Set pin interrupt (fifo_th could be on or off) */ - if (config->drdy_pin == 1) { + if ((config->drdy_pin == 1) || (ON_I3C_BUS(config) && (!I3C_INT_PIN(config)))) { lsm6dsv16x_pin_int1_route_set(ctx, &pin_int); } else { lsm6dsv16x_pin_int2_route_set(ctx, &pin_int); @@ -80,10 +80,15 @@ static void lsm6dsv16x_config_fifo(const struct device *dev, uint8_t fifo_irq) void lsm6dsv16x_submit_stream(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) { struct lsm6dsv16x_data *lsm6dsv16x = dev->data; +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) + const struct lsm6dsv16x_config *config = dev->config; +#endif const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; uint8_t fifo_irq = 0; - gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_DISABLE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_DISABLE); + } for (size_t i = 0; i < cfg->count; i++) { if (cfg->triggers[i].trigger == SENSOR_TRIG_FIFO_WATERMARK) { @@ -103,12 +108,17 @@ void lsm6dsv16x_submit_stream(const struct device *dev, struct rtio_iodev_sqe *i lsm6dsv16x->streaming_sqe = iodev_sqe; - gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } } static void lsm6dsv16x_complete_op_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) { const struct device *dev = arg; +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) + const struct lsm6dsv16x_config *config = dev->config; +#endif struct lsm6dsv16x_data *lsm6dsv16x = dev->data; /* @@ -116,12 +126,17 @@ static void lsm6dsv16x_complete_op_cb(struct rtio *r, const struct rtio_sqe *sqe */ rtio_iodev_sqe_ok(sqe->userdata, 0); lsm6dsv16x->streaming_sqe = NULL; - gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } } static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, void *arg) { const struct device *dev = arg; +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) + const struct lsm6dsv16x_config *config = dev->config; +#endif struct lsm6dsv16x_data *lsm6dsv16x = dev->data; struct gpio_dt_spec *irq_gpio = lsm6dsv16x->drdy_gpio; struct rtio_iodev *iodev = lsm6dsv16x->iodev; @@ -168,7 +183,9 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, rtio_iodev_sqe_ok(sqe->userdata, 0); lsm6dsv16x->streaming_sqe = NULL; - gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } return; } @@ -215,7 +232,9 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, sizeof(struct lsm6dsv16x_fifo_data), &buf, &buf_len) != 0) { rtio_iodev_sqe_err(lsm6dsv16x->streaming_sqe, -ENOMEM); lsm6dsv16x->streaming_sqe = NULL; - gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } return; } @@ -230,7 +249,9 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, /* complete request with ok */ rtio_iodev_sqe_ok(lsm6dsv16x->streaming_sqe, 0); lsm6dsv16x->streaming_sqe = NULL; - gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } if (data_opt == SENSOR_STREAM_DATA_DROP) { @@ -266,7 +287,9 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, LOG_ERR("Failed to get buffer"); rtio_iodev_sqe_err(lsm6dsv16x->streaming_sqe, -ENOMEM); lsm6dsv16x->streaming_sqe = NULL; - gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(config) || (I3C_INT_PIN(config))) { + gpio_pin_interrupt_configure_dt(irq_gpio, GPIO_INT_EDGE_TO_ACTIVE); + } return; } @@ -316,6 +339,8 @@ static void lsm6dsv16x_read_fifo_cb(struct rtio *r, const struct rtio_sqe *sqe, read_fifo_dout_reg->flags = RTIO_SQE_CHAINED; if (lsm6dsv16x->bus_type == BUS_I2C) { read_fifo_dout_reg->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + } else if (lsm6dsv16x->bus_type == BUS_I3C) { + read_fifo_dout_reg->iodev_flags |= RTIO_IODEV_I3C_STOP | RTIO_IODEV_I3C_RESTART; } rtio_sqe_prep_callback_no_cqe(complete_op, lsm6dsv16x_complete_op_cb, (void *)dev, lsm6dsv16x->streaming_sqe); @@ -327,6 +352,9 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) { struct lsm6dsv16x_data *lsm6dsv16x = dev->data; struct rtio_iodev *iodev = lsm6dsv16x->iodev; +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) + const struct lsm6dsv16x_config *config = dev->config; +#endif if (lsm6dsv16x->streaming_sqe == NULL) { return; @@ -335,32 +363,50 @@ void lsm6dsv16x_stream_irq_handler(const struct device *dev) /* get timestamp as soon as the irq is served */ lsm6dsv16x->fifo_timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); - lsm6dsv16x->fifo_status[0] = lsm6dsv16x->fifo_status[1] = 0; - - /* - * Prepare rtio enabled bus to read LSM6DSV16X_FIFO_STATUS1 and LSM6DSV16X_FIFO_STATUS2 - * registers where FIFO threshold condition and count are reported. - * Then lsm6dsv16x_read_fifo_cb callback will be invoked. - * - * STMEMSC API equivalent code: - * - * lsm6dsv16x_fifo_status_t fifo_status; - * - * lsm6dsv16x_fifo_status_get(&dev_ctx, &fifo_status); - */ - struct rtio_sqe *write_fifo_status_addr = rtio_sqe_acquire(lsm6dsv16x->rtio_ctx); - struct rtio_sqe *read_fifo_status_reg = rtio_sqe_acquire(lsm6dsv16x->rtio_ctx); +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) + if (ON_I3C_BUS(config) && (!I3C_INT_PIN(config))) { + /* + * If we are on an I3C bus, then it should be expected that the fifo status was + * already received in the IBI payload and we don't need to read it again. + */ + lsm6dsv16x->fifo_status[0] = lsm6dsv16x->ibi_payload.fifo_status1; + lsm6dsv16x->fifo_status[1] = lsm6dsv16x->ibi_payload.fifo_status2; + } else +#endif + { + lsm6dsv16x->fifo_status[0] = lsm6dsv16x->fifo_status[1] = 0; + + /* + * Prepare rtio enabled bus to read LSM6DSV16X_FIFO_STATUS1 and + * LSM6DSV16X_FIFO_STATUS2 registers where FIFO threshold condition and count are + * reported. Then lsm6dsv16x_read_fifo_cb callback will be invoked. + * + * STMEMSC API equivalent code: + * + * lsm6dsv16x_fifo_status_t fifo_status; + * + * lsm6dsv16x_fifo_status_get(&dev_ctx, &fifo_status); + */ + struct rtio_sqe *write_fifo_status_addr = rtio_sqe_acquire(lsm6dsv16x->rtio_ctx); + struct rtio_sqe *read_fifo_status_reg = rtio_sqe_acquire(lsm6dsv16x->rtio_ctx); + uint8_t reg = lsm6dsv16x_bus_reg(lsm6dsv16x, LSM6DSV16X_FIFO_STATUS1); + + rtio_sqe_prep_tiny_write(write_fifo_status_addr, iodev, RTIO_PRIO_NORM, ®, 1, + NULL); + write_fifo_status_addr->flags = RTIO_SQE_TRANSACTION; + rtio_sqe_prep_read(read_fifo_status_reg, iodev, RTIO_PRIO_NORM, + lsm6dsv16x->fifo_status, 2, NULL); + read_fifo_status_reg->flags = RTIO_SQE_CHAINED; + if (lsm6dsv16x->bus_type == BUS_I2C) { + read_fifo_status_reg->iodev_flags |= + RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + } else if (lsm6dsv16x->bus_type == BUS_I3C) { + read_fifo_status_reg->iodev_flags |= + RTIO_IODEV_I3C_STOP | RTIO_IODEV_I3C_RESTART; + } + } struct rtio_sqe *check_fifo_status_reg = rtio_sqe_acquire(lsm6dsv16x->rtio_ctx); - uint8_t reg = lsm6dsv16x_bus_reg(lsm6dsv16x, LSM6DSV16X_FIFO_STATUS1); - rtio_sqe_prep_tiny_write(write_fifo_status_addr, iodev, RTIO_PRIO_NORM, ®, 1, NULL); - write_fifo_status_addr->flags = RTIO_SQE_TRANSACTION; - rtio_sqe_prep_read(read_fifo_status_reg, iodev, RTIO_PRIO_NORM, - lsm6dsv16x->fifo_status, 2, NULL); - read_fifo_status_reg->flags = RTIO_SQE_CHAINED; - if (lsm6dsv16x->bus_type == BUS_I2C) { - read_fifo_status_reg->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; - } rtio_sqe_prep_callback_no_cqe(check_fifo_status_reg, lsm6dsv16x_read_fifo_cb, (void *)dev, NULL); rtio_submit(lsm6dsv16x->rtio_ctx, 0); diff --git a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_trigger.c b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_trigger.c index d9e0fc50ad02..f7416c936766 100644 --- a/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_trigger.c +++ b/drivers/sensor/st/lsm6dsv16x/lsm6dsv16x_trigger.c @@ -37,7 +37,7 @@ static int lsm6dsv16x_enable_xl_int(const struct device *dev, int enable) } /* set interrupt */ - if (cfg->drdy_pin == 1) { + if ((cfg->drdy_pin == 1) || (ON_I3C_BUS(cfg) && (!I3C_INT_PIN(cfg)))) { lsm6dsv16x_pin_int_route_t val = {}; ret = lsm6dsv16x_pin_int1_route_get(ctx, &val); @@ -83,7 +83,7 @@ static int lsm6dsv16x_enable_g_int(const struct device *dev, int enable) } /* set interrupt */ - if (cfg->drdy_pin == 1) { + if ((cfg->drdy_pin == 1) || (ON_I3C_BUS(cfg) && (!I3C_INT_PIN(cfg)))) { lsm6dsv16x_pin_int_route_t val = {}; ret = lsm6dsv16x_pin_int1_route_get(ctx, &val); @@ -174,8 +174,13 @@ static void lsm6dsv16x_handle_interrupt(const struct device *dev) const struct lsm6dsv16x_config *cfg = dev->config; stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; lsm6dsv16x_data_ready_t status; + int ret; while (1) { + if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) { + break; + } + if (lsm6dsv16x_flag_data_ready_get(ctx, &status) < 0) { LOG_DBG("failed reading status reg"); return; @@ -195,30 +200,43 @@ static void lsm6dsv16x_handle_interrupt(const struct device *dev) } - gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, - GPIO_INT_EDGE_TO_ACTIVE); + if (!ON_I3C_BUS(cfg) || (I3C_INT_PIN(cfg))) { + ret = gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, + GPIO_INT_EDGE_TO_ACTIVE); + if (ret < 0) { + LOG_ERR("%s: Not able to configure pin_int", dev->name); + } + } } #endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD || CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD */ +static void lsm6dsv16x_intr_callback(struct lsm6dsv16x_data *lsm6dsv16x) +{ +#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) + k_sem_give(&lsm6dsv16x->intr_sem); +#elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD) + k_work_submit(&lsm6dsv16x->work); +#endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */ + if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) { + lsm6dsv16x_stream_irq_handler(lsm6dsv16x->dev); + } +} + static void lsm6dsv16x_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { struct lsm6dsv16x_data *lsm6dsv16x = CONTAINER_OF(cb, struct lsm6dsv16x_data, gpio_cb); + int ret; ARG_UNUSED(pins); - gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_DISABLE); - - if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) { - lsm6dsv16x_stream_irq_handler(lsm6dsv16x->dev); + ret = gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_DISABLE); + if (ret < 0) { + LOG_ERR("%s: Not able to configure pin_int", dev->name); } -#if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) - k_sem_give(&lsm6dsv16x->gpio_sem); -#elif defined(CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD) - k_work_submit(&lsm6dsv16x->work); -#endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */ + lsm6dsv16x_intr_callback(lsm6dsv16x); } #ifdef CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD @@ -230,7 +248,7 @@ static void lsm6dsv16x_thread(void *p1, void *p2, void *p3) struct lsm6dsv16x_data *lsm6dsv16x = p1; while (1) { - k_sem_take(&lsm6dsv16x->gpio_sem, K_FOREVER); + k_sem_take(&lsm6dsv16x->intr_sem, K_FOREVER); lsm6dsv16x_handle_interrupt(lsm6dsv16x->dev); } } @@ -246,6 +264,47 @@ static void lsm6dsv16x_work_cb(struct k_work *work) } #endif /* CONFIG_LSM6DSV16X_TRIGGER_GLOBAL_THREAD */ +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) +static int lsm6dsv16x_ibi_cb(struct i3c_device_desc *target, + struct i3c_ibi_payload *payload) +{ + const struct device *dev = target->dev; + struct lsm6dsv16x_data *lsm6dsv16x = dev->data; + + /* + * The IBI Payload consist of the following 10 bytes : + * 1st byte: MDB + * - MDB[0]: FIFO interrupts (FIFO_WTM_IA, FIFO_OVR_IA, FIFO_FULL_IA, CONTER_BDR_IA) + * - MDB[1]: Physical interrupts (XLDS, GDA, TDA, XLDA_OIS, GDA_OIS) + * - MDB[2]: Basic interrupts (SLEEP_CHANGE_IA, D6D_IA, DOUBLE_TAP, SINGLE_TAP, WU_IA, + * FF_IA) + * - MDB[3]: SHUB DRDY (SENS_HUB_ENDOP) + * - MDB[4]: Advanced Function interrupt group + * - MDB[7:5]: 3'b000: Vendor Definied + * 3'b100: Timing Information + * 2nd byte: FIFO_STATUS1 + * 3rd byte: FIFO_STATUS2 + * 4th byte: ALL_INT_SRC + * 5th byte: STATUS_REG + * 6th byte: STATUS_REG_OIS + * 7th byte: STATUS_MASTER_MAIN + * 8th byte: EMB_FUNC_STATUS + * 9th byte: FSM_STATUS + * 10th byte: MLC_STATUS + */ + if (payload->payload_len != sizeof(lsm6dsv16x->ibi_payload)) { + LOG_ERR("Invalid IBI payload length"); + return -EINVAL; + } + + memcpy(&lsm6dsv16x->ibi_payload, payload->payload, payload->payload_len); + + lsm6dsv16x_intr_callback(lsm6dsv16x); + + return 0; +} +#endif + int lsm6dsv16x_init_interrupt(const struct device *dev) { struct lsm6dsv16x_data *lsm6dsv16x = dev->data; @@ -258,13 +317,17 @@ int lsm6dsv16x_init_interrupt(const struct device *dev) (struct gpio_dt_spec *)&cfg->int2_gpio; /* setup data ready gpio interrupt (INT1 or INT2) */ - if (!gpio_is_ready_dt(lsm6dsv16x->drdy_gpio)) { + if ((!ON_I3C_BUS(cfg) || (I3C_INT_PIN(cfg))) && !gpio_is_ready_dt(lsm6dsv16x->drdy_gpio)) { LOG_ERR("Cannot get pointer to drdy_gpio device"); return -EINVAL; } + if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) { + lsm6dsv16x_stream_irq_handler(lsm6dsv16x->dev); + } + #if defined(CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD) - k_sem_init(&lsm6dsv16x->gpio_sem, 0, K_SEM_MAX_LIMIT); + k_sem_init(&lsm6dsv16x->intr_sem, 0, K_SEM_MAX_LIMIT); k_thread_create(&lsm6dsv16x->thread, lsm6dsv16x->thread_stack, CONFIG_LSM6DSV16X_THREAD_STACK_SIZE, @@ -276,26 +339,29 @@ int lsm6dsv16x_init_interrupt(const struct device *dev) lsm6dsv16x->work.handler = lsm6dsv16x_work_cb; #endif /* CONFIG_LSM6DSV16X_TRIGGER_OWN_THREAD */ - ret = gpio_pin_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INPUT); - if (ret < 0) { - LOG_DBG("Could not configure gpio"); - return ret; - } + if (!ON_I3C_BUS(cfg) || (I3C_INT_PIN(cfg))) { + ret = gpio_pin_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INPUT); + if (ret < 0) { + LOG_DBG("Could not configure gpio"); + return ret; + } - gpio_init_callback(&lsm6dsv16x->gpio_cb, - lsm6dsv16x_gpio_callback, - BIT(lsm6dsv16x->drdy_gpio->pin)); + gpio_init_callback(&lsm6dsv16x->gpio_cb, + lsm6dsv16x_gpio_callback, + BIT(lsm6dsv16x->drdy_gpio->pin)); - if (gpio_add_callback(lsm6dsv16x->drdy_gpio->port, &lsm6dsv16x->gpio_cb) < 0) { - LOG_DBG("Could not set gpio callback"); - return -EIO; - } + if (gpio_add_callback(lsm6dsv16x->drdy_gpio->port, &lsm6dsv16x->gpio_cb) < 0) { + LOG_DBG("Could not set gpio callback"); + return -EIO; + } + } - /* set data ready mode on int1/int2 */ + /* set data ready mode on int1/int2/tir */ LOG_DBG("drdy_pulsed is %d", (int)cfg->drdy_pulsed); lsm6dsv16x_data_ready_mode_t mode = cfg->drdy_pulsed ? LSM6DSV16X_DRDY_PULSED : - LSM6DSV16X_DRDY_LATCHED; + LSM6DSV16X_DRDY_LATCHED; + ret = lsm6dsv16x_data_ready_mode_set(ctx, mode); if (ret < 0) { @@ -303,6 +369,53 @@ int lsm6dsv16x_init_interrupt(const struct device *dev) return ret; } +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c) + if (ON_I3C_BUS(cfg)) { + if (I3C_INT_PIN(cfg)) { + /* Enable INT Pins when using I3C */ + ret = lsm6dsv16x_i3c_int_en_set(ctx, I3C_INT_PIN(cfg)); + if (ret < 0) { + LOG_ERR("failed to enable int pin for I3C %d", ret); + return ret; + } + + ret = gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, + GPIO_INT_EDGE_TO_ACTIVE); + if (ret < 0) { + LOG_ERR("Could not configure gpio interrupt"); + return ret; + } + } else { + /* I3C IBI does not utilize GPIO interrupt. */ + lsm6dsv16x->i3c_dev->ibi_cb = lsm6dsv16x_ibi_cb; + + /* + * Set IBI availability time, this is the time that the sensor + * will wait for inactivity before it is okay to generate an IBI TIR. + * + * NOTE: There is a bug in the API and the Documentation where + * the defines for the values are incorrect. The correct values are: + * 0 = 50us + * 1 = 2us + * 2 = 1ms + * 3 = 25ms + */ + ret = lsm6dsv16x_i3c_ibi_time_set(ctx, cfg->bus_act_sel); + if (ret < 0) { + LOG_ERR("failed to set ibi available time %d", ret); + return -EIO; + } + + if (i3c_ibi_enable(lsm6dsv16x->i3c_dev) != 0) { + LOG_ERR("Could not enable I3C IBI"); + return -EIO; + } + } + + return 0; + } +#endif + return gpio_pin_interrupt_configure_dt(lsm6dsv16x->drdy_gpio, GPIO_INT_EDGE_TO_ACTIVE); } diff --git a/dts/bindings/sensor/st,lsm6dsv16x-i3c.yaml b/dts/bindings/sensor/st,lsm6dsv16x-i3c.yaml new file mode 100644 index 000000000000..416f4bd5ee86 --- /dev/null +++ b/dts/bindings/sensor/st,lsm6dsv16x-i3c.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Meta Platforms +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics LSM6DSV16X 6-axis IMU (Inertial Measurement Unit) sensor + accessed through I3C bus + +compatible: "st,lsm6dsv16x" + +include: ["i3c-device.yaml", "st,lsm6dsv16x-common.yaml"] + +properties: + int-en-i3c: + type: boolean + description: | + Enables INT pin when I3C is enabled + + bus-act-sel-us: + type: int + default: 50 + description: | + Bus available time for I3C IBI in microseconds + + enum: [50, 2, 1000, 25000]