diff --git a/drivers/i2c/i2c_rtio.c b/drivers/i2c/i2c_rtio.c index d921f5604efe44e..a44e2e61fee8788 100644 --- a/drivers/i2c/i2c_rtio.c +++ b/drivers/i2c/i2c_rtio.c @@ -52,6 +52,47 @@ struct rtio_sqe *i2c_rtio_copy(struct rtio *r, struct rtio_iodev *iodev, const s return sqe; } +struct rtio_sqe *i2c_rtio_copy_reg_write_byte(struct rtio *r, struct rtio_iodev *iodev, + uint8_t reg_addr, uint8_t data) +{ + uint8_t msg[2]; + + struct rtio_sqe *sqe = rtio_sqe_acquire(r); + + if (sqe == NULL) { + rtio_sqe_drop_all(r); + return NULL; + } + msg[0] = reg_addr; + msg[1] = data; + rtio_sqe_prep_tiny_write(sqe, iodev, RTIO_PRIO_NORM, msg, sizeof(msg), NULL); + sqe->iodev_flags = RTIO_IODEV_I2C_STOP; + return sqe; +} + +struct rtio_sqe *i2c_rtio_copy_reg_burst_read(struct rtio *r, struct rtio_iodev *iodev, + uint8_t start_addr, void *buf, size_t num_bytes) +{ + struct rtio_sqe *sqe = rtio_sqe_acquire(r); + + if (sqe == NULL) { + rtio_sqe_drop_all(r); + return NULL; + } + rtio_sqe_prep_tiny_write(sqe, iodev, RTIO_PRIO_NORM, &start_addr, 1, NULL); + sqe->flags |= RTIO_SQE_TRANSACTION; + + sqe = rtio_sqe_acquire(r); + if (sqe == NULL) { + rtio_sqe_drop_all(r); + return NULL; + } + rtio_sqe_prep_read(sqe, iodev, RTIO_PRIO_NORM, buf, num_bytes, NULL); + sqe->iodev_flags |= RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART; + + return sqe; +} + void i2c_rtio_init(struct i2c_rtio *ctx, const struct device *dev) { k_sem_init(&ctx->lock, 1, 1); diff --git a/drivers/sensor/asahi_kasei/akm09918c/Kconfig b/drivers/sensor/asahi_kasei/akm09918c/Kconfig index d7dac11f424dcf2..301165106aa48e7 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/Kconfig +++ b/drivers/sensor/asahi_kasei/akm09918c/Kconfig @@ -7,6 +7,7 @@ config AKM09918C default y depends on DT_HAS_ASAHI_KASEI_AKM09918C_ENABLED select I2C + select I2C_RTIO if SENSOR_ASYNC_API select RTIO_WORKQ if SENSOR_ASYNC_API help Enable driver for AK8975 magnetometer. diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c index 9bc9efaa2234e94..178ac33c95eb30b 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c @@ -28,7 +28,7 @@ LOG_MODULE_REGISTER(AKM09918C, CONFIG_SENSOR_LOG_LEVEL); * @param chan Channel ID for starting the measurement * @return int 0 if successful or error code */ -int akm09918c_start_measurement(const struct device *dev, enum sensor_channel chan) +int akm09918c_start_measurement_blocking(const struct device *dev, enum sensor_channel chan) { struct akm09918c_data *data = dev->data; const struct akm09918c_config *cfg = dev->config; @@ -41,12 +41,13 @@ int akm09918c_start_measurement(const struct device *dev, enum sensor_channel ch if (data->mode == AKM09918C_CNTL2_PWR_DOWN) { if (i2c_reg_write_byte_dt(&cfg->i2c, AKM09918C_REG_CNTL2, - AKM09918C_CNTL2_SINGLE_MEASURE) != 0) { - LOG_ERR("Failed to start measurement."); - return -EIO; + AKM09918C_CNTL2_SINGLE_MEASURE) == 0) { + return 0; } } - return 0; + + LOG_ERR("Failed to start measurement."); + return -EIO; } /** @@ -59,7 +60,8 @@ int akm09918c_start_measurement(const struct device *dev, enum sensor_channel ch * @param z Location to write Z channel sample. * @return int 0 if successful or error code */ -int akm09918c_fetch_measurement(const struct device *dev, int16_t *x, int16_t *y, int16_t *z) +int akm09918c_fetch_measurement_blocking(const struct device *dev, int16_t *x, int16_t *y, + int16_t *z) { const struct akm09918c_config *cfg = dev->config; uint8_t buf[9] = {0}; @@ -86,7 +88,7 @@ static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel { struct akm09918c_data *data = dev->data; - int ret = akm09918c_start_measurement(dev, chan); + int ret = akm09918c_start_measurement_blocking(dev, chan); if (ret) { return ret; @@ -95,7 +97,8 @@ static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel LOG_DBG("Waiting for sample..."); k_usleep(AKM09918C_MEASURE_TIME_US); - return akm09918c_fetch_measurement(dev, &data->x_sample, &data->y_sample, &data->z_sample); + return akm09918c_fetch_measurement_blocking(dev, &data->x_sample, &data->y_sample, + &data->z_sample); } static void akm09918c_convert(struct sensor_value *val, int16_t sample) @@ -249,8 +252,13 @@ static DEVICE_API(sensor, akm09918c_driver_api) = { }; #define AKM09918C_DEFINE(inst) \ - static struct akm09918c_data akm09918c_data_##inst; \ - \ + IF_ENABLED(CONFIG_I2C_RTIO, \ + (I2C_DT_IODEV_DEFINE(akm09918c_iodev_##inst, DT_DRV_INST(inst));)) \ + IF_ENABLED(CONFIG_I2C_RTIO, (RTIO_DEFINE( \ + akm09918c_rtio_ctx_##inst, CONFIG_I2C_RTIO_SQ_SIZE, CONFIG_I2C_RTIO_CQ_SIZE);)) \ + static struct akm09918c_data akm09918c_data_##inst = { \ + IF_ENABLED(CONFIG_I2C_RTIO, (.rtio_ctx = \ + &akm09918c_rtio_ctx_##inst, .iodev = &akm09918c_iodev_##inst)) }; \ static const struct akm09918c_config akm09918c_config_##inst = { \ .i2c = I2C_DT_SPEC_INST_GET(inst), \ }; \ diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h index f8a4f5c7c4395ec..2f291bea9c7f28a 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h @@ -41,6 +41,9 @@ struct akm09918c_data { uint64_t timestamp; struct k_work_delayable async_fetch_work; } work_ctx; + /* for communication to the bus controller */ + struct rtio *rtio_ctx; + struct rtio_iodev *iodev; #endif }; @@ -82,9 +85,10 @@ static inline void akm09918c_reg_to_hz(uint8_t reg, struct sensor_value *val) break; } } -int akm09918c_start_measurement(const struct device *dev, enum sensor_channel chan); +int akm09918c_start_measurement_blocking(const struct device *dev, enum sensor_channel chan); -int akm09918c_fetch_measurement(const struct device *dev, int16_t *x, int16_t *y, int16_t *z); +int akm09918c_fetch_measurement_blocking(const struct device *dev, int16_t *x, int16_t *y, + int16_t *z); /* * RTIO types */ @@ -95,13 +99,18 @@ struct akm09918c_decoder_header { struct akm09918c_encoded_data { struct akm09918c_decoder_header header; - int16_t readings[3]; -}; + struct { + int16_t data[3]; /* direkt zum speichern nach dem umsortieren*/ + uint8_t tmp[9]; /* dirtekt zum einlesen */ + } reading; +} __packed __aligned(1); void akm09918_async_fetch(struct k_work *work); int akm09918c_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); +void akm09918_after_start_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, void *arg0); +void akm09918_complete_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, void *arg0); #endif /* ZEPHYR_DRIVERS_SENSOR_AKM09918C_AKM09918C_H_ */ diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c index 832d046bdd7ab6a..41e67a9a2387b0b 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c @@ -7,20 +7,38 @@ */ #include -#include +#include +#include #include "akm09918c.h" LOG_MODULE_DECLARE(AKM09918C, CONFIG_SENSOR_LOG_LEVEL); -void akm09918c_submit_sync(struct rtio_iodev_sqe *iodev_sqe) +static int akm09918c_flush_cqes(struct rtio *rtio_ctx) +{ + /* Flush completions */ + struct rtio_cqe *cqe; + int res = 0; + + do { + cqe = rtio_cqe_consume(rtio_ctx); + if (cqe != NULL) { + if ((cqe->result < 0 && res == 0)) { + LOG_ERR("Bus error: %d", cqe->result); + res = cqe->result; + } + rtio_cqe_release(rtio_ctx, cqe); + } + } while (cqe != NULL); + return res; +} + +void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) { const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; - const struct device *dev = cfg->sensor; struct akm09918c_data *data = dev->data; const struct sensor_chan_spec *const channels = cfg->channels; const size_t num_channels = cfg->count; - int rc; /* Check if the requested channels are supported */ for (size_t i = 0; i < num_channels; i++) { @@ -37,42 +55,47 @@ void akm09918c_submit_sync(struct rtio_iodev_sqe *iodev_sqe) return; } } + struct rtio_sqe *writeByte_sqe = i2c_rtio_copy_reg_write_byte( + data->rtio_ctx, data->iodev, AKM09918C_REG_CNTL2, AKM09918C_CNTL2_SINGLE_MEASURE); + struct rtio_sqe *cb_sqe = rtio_sqe_acquire(data->rtio_ctx); - /* start the measurement in the sensor */ - rc = akm09918c_start_measurement(dev, SENSOR_CHAN_MAGN_XYZ); - if (rc != 0) { - LOG_ERR("Failed to fetch samples."); - rtio_iodev_sqe_err(iodev_sqe, rc); - return; + writeByte_sqe->flags |= RTIO_SQE_CHAINED; + rtio_sqe_prep_callback_no_cqe(cb_sqe, akm09918_after_start_cb, (void *)iodev_sqe, NULL); + + if (writeByte_sqe != NULL && cb_sqe != NULL) { + rtio_submit(data->rtio_ctx, 0); + } else { + rtio_sqe_drop_all(data->rtio_ctx); + rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); } +} +void akm09918_after_start_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, void *arg0) +{ + const struct rtio_iodev_sqe *parent_iodev_sqe = (struct rtio_iodev_sqe *)arg0; + const struct sensor_read_config *cfg = parent_iodev_sqe->sqe.iodev->data; + const struct device *dev = cfg->sensor; + struct akm09918c_data *data = dev->data; + int rc; /* save information for the work item */ data->work_ctx.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); - data->work_ctx.iodev_sqe = iodev_sqe; + data->work_ctx.iodev_sqe = (struct rtio_iodev_sqe *)arg0; + + rc = akm09918c_flush_cqes(data->rtio_ctx); + if (rc != 0) { + rtio_iodev_sqe_err((struct rtio_iodev_sqe *)arg0, rc); + return; + } rc = k_work_schedule(&data->work_ctx.async_fetch_work, K_USEC(AKM09918C_MEASURE_TIME_US)); if (rc == 0) { LOG_ERR("The last fetch has not finished yet. " "Try again later when the last sensor read operation has finished."); - rtio_iodev_sqe_err(iodev_sqe, -EBUSY); + rtio_iodev_sqe_err((struct rtio_iodev_sqe *)arg0, -EBUSY); } return; } -void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) -{ - struct rtio_work_req *req = rtio_work_req_alloc(); - - if (req == NULL) { - LOG_ERR("RTIO work item allocation failed. Consider to increase " - "CONFIG_RTIO_WORKQ_POOL_ITEMS."); - rtio_iodev_sqe_err(iodev_sqe, -ENOMEM); - return; - } - - rtio_work_req_submit(req, iodev_sqe, akm09918c_submit_sync); -} - void akm09918_async_fetch(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); @@ -80,6 +103,7 @@ void akm09918_async_fetch(struct k_work *work) CONTAINER_OF(dwork, struct akm09918c_async_fetch_ctx, async_fetch_work); const struct sensor_read_config *cfg = ctx->iodev_sqe->sqe.iodev->data; const struct device *dev = cfg->sensor; + struct akm09918c_data *data = dev->data; uint32_t req_buf_len = sizeof(struct akm09918c_encoded_data); uint32_t buf_len; uint8_t *buf; @@ -94,11 +118,49 @@ void akm09918_async_fetch(struct k_work *work) return; } edata = (struct akm09918c_encoded_data *)buf; - rc = akm09918c_fetch_measurement(dev, &edata->readings[0], &edata->readings[1], - &edata->readings[2]); + + struct rtio_sqe *burstRead_sqe = + i2c_rtio_copy_reg_burst_read(data->rtio_ctx, data->iodev, AKM09918C_REG_ST1, + &(edata->reading.tmp), sizeof(edata->reading.tmp)); + + edata->header.timestamp = ctx->timestamp; + + struct rtio_sqe *cb_sqe = rtio_sqe_acquire(data->rtio_ctx); + + rtio_sqe_prep_callback_no_cqe(cb_sqe, akm09918_complete_cb, (void *)ctx->iodev_sqe, NULL); + + if (burstRead_sqe != NULL && cb_sqe != NULL) { + burstRead_sqe->flags |= RTIO_SQE_CHAINED; + rtio_submit(data->rtio_ctx, 0); + } else { + rtio_sqe_drop_all(data->rtio_ctx); + rtio_iodev_sqe_err(ctx->iodev_sqe, -ENOMEM); + } +} + +void akm09918_complete_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, void *arg0) +{ + struct rtio_iodev_sqe *parent_iodev_sqe = (struct rtio_iodev_sqe *)arg0; + struct rtio_sqe *parent_sqe = &parent_iodev_sqe->sqe; + struct akm09918c_encoded_data *edata = + (struct akm09918c_encoded_data *)(parent_sqe->rx.buf); + int rc; + + rc = akm09918c_flush_cqes(rtio_ctx); if (rc != 0) { - rtio_iodev_sqe_err(ctx->iodev_sqe, rc); + rtio_iodev_sqe_err(parent_iodev_sqe, rc); + return; + } + + if (FIELD_GET(AKM09918C_ST1_DRDY, edata->reading.tmp[0]) == 0) { + LOG_ERR("Data not ready, st1=0x%02x", edata->reading.tmp[0]); + rtio_iodev_sqe_err(parent_iodev_sqe, -EBUSY); return; } - rtio_iodev_sqe_ok(ctx->iodev_sqe, 0); + + edata->reading.data[0] = edata->reading.tmp[1] | (edata->reading.tmp[2] << 8); + edata->reading.data[1] = edata->reading.tmp[3] | (edata->reading.tmp[4] << 8); + edata->reading.data[2] = edata->reading.tmp[5] | (edata->reading.tmp[6] << 8); + + rtio_iodev_sqe_ok(parent_iodev_sqe, 0); } diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_decoder.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_decoder.c index 7a17e959dc4dd37..b282e9f834770d3 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_decoder.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_decoder.c @@ -70,9 +70,9 @@ static int akm09918c_decoder_decode(const uint8_t *buffer, struct sensor_chan_sp out->header.reading_count = 1; out->shift = AKM09918C_SHIFT; - akm09918c_convert_raw_to_q31(edata->readings[0], &out->readings[0].x); - akm09918c_convert_raw_to_q31(edata->readings[1], &out->readings[0].y); - akm09918c_convert_raw_to_q31(edata->readings[2], &out->readings[0].z); + akm09918c_convert_raw_to_q31(edata->reading.data[0], &out->readings[0].x); + akm09918c_convert_raw_to_q31(edata->reading.data[1], &out->readings[0].y); + akm09918c_convert_raw_to_q31(edata->reading.data[2], &out->readings[0].z); *fit = 1; return 1; diff --git a/include/zephyr/drivers/i2c.h b/include/zephyr/drivers/i2c.h index 5f1c3fbe8ae4d3e..f3c19a02792bd52 100644 --- a/include/zephyr/drivers/i2c.h +++ b/include/zephyr/drivers/i2c.h @@ -1092,22 +1092,51 @@ struct rtio_sqe *i2c_rtio_copy(struct rtio *r, const struct i2c_msg *msgs, uint8_t num_msgs); -#endif /* CONFIG_I2C_RTIO */ - /** - * @brief Perform data transfer to another I2C device in controller mode. + * @brief Copy the register address and data to a SQE * - * This is equivalent to: + * @param r RTIO context + * @param iodev RTIO IODev to target for the submissions + * @param reg_addr target register address + * @param data data to be written * - * i2c_transfer(spec->bus, msgs, num_msgs, spec->addr); + * @retval sqe Last submission in the queue added + * @retval NULL Not enough memory in the context to copy the requests + */ +struct rtio_sqe *i2c_rtio_copy_reg_write_byte(struct rtio *r, struct rtio_iodev *iodev, + uint8_t reg_addr, uint8_t data); + +/** + * @brief acquire and configure a i2c burst read transmission * - * @param spec I2C specification from devicetree. - * @param msgs Array of messages to transfer. - * @param num_msgs Number of messages to transfer. + * @param r RTIO context + * @param iodev RTIO IODev to target for the submissions + * @param start_addr target register address + * @param buf Memory pool that stores the retrieved data. + * @param num_bytes Number of bytes to read. * - * @return a value from i2c_transfer() + * @retval sqe Last submission in the queue added + * @retval NULL Not enough memory in the context to copy the requests */ -static inline int i2c_transfer_dt(const struct i2c_dt_spec *spec, +struct rtio_sqe *i2c_rtio_copy_reg_burst_read(struct rtio *r, struct rtio_iodev *iodev, + uint8_t start_addr, void *buf, size_t num_bytes); + +#endif /* CONFIG_I2C_RTIO */ + + /** + * @brief Perform data transfer to another I2C device in controller mode. + * + * This is equivalent to: + * + * i2c_transfer(spec->bus, msgs, num_msgs, spec->addr); + * + * @param spec I2C specification from devicetree. + * @param msgs Array of messages to transfer. + * @param num_msgs Number of messages to transfer. + * + * @return a value from i2c_transfer() + */ + static inline int i2c_transfer_dt(const struct i2c_dt_spec *spec, struct i2c_msg *msgs, uint8_t num_msgs) { return i2c_transfer(spec->bus, msgs, num_msgs, spec->addr);