From 47868961e5dde5e4f6b9bcc96491b33e8d5e77e5 Mon Sep 17 00:00:00 2001 From: Florian Weber Date: Wed, 23 Oct 2024 13:52:52 +0200 Subject: [PATCH] drivers: sensor: akm09918: make submit function more unblocking The driver now does not wait for the completion of a measurement in the submit function. Instead it schedule the fetch and the completion of the submission queue entry as delayed work to the system work queue. Signed-off-by: Florian Weber --- .../sensor/asahi_kasei/akm09918c/akm09918c.c | 50 ++++++++---- .../sensor/asahi_kasei/akm09918c/akm09918c.h | 15 +++- .../asahi_kasei/akm09918c/akm09918c_async.c | 79 +++++++++++++++---- 3 files changed, 109 insertions(+), 35 deletions(-) diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c index 50664bcdbc5ad9..19bb837cc09c9e 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2023 Google LLC + * Copyright (c) 2024 Florian Weber * SPDX-License-Identifier: Apache-2.0 */ @@ -21,21 +22,16 @@ LOG_MODULE_REGISTER(AKM09918C, CONFIG_SENSOR_LOG_LEVEL); /** - * @brief Perform the bus transaction to fetch samples + * @brief Perform the bus transaction to start measurement. * * @param dev Sensor device to operate on - * @param chan Channel ID to fetch - * @param x Location to write X channel sample. - * @param y Location to write Y channel sample. - * @param z Location to write Z channel sample. + * @param chan Channel ID for starting the measurement * @return int 0 if successful or error code */ -int akm09918c_sample_fetch_helper(const struct device *dev, enum sensor_channel chan, int16_t *x, - int16_t *y, int16_t *z) +int akm09918c_start_measurement(const struct device *dev, enum sensor_channel chan) { struct akm09918c_data *data = dev->data; const struct akm09918c_config *cfg = dev->config; - uint8_t buf[9] = {0}; if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_MAGN_X && chan != SENSOR_CHAN_MAGN_Y && chan != SENSOR_CHAN_MAGN_Z && chan != SENSOR_CHAN_MAGN_XYZ) { @@ -49,11 +45,24 @@ int akm09918c_sample_fetch_helper(const struct device *dev, enum sensor_channel LOG_ERR("Failed to start measurement."); return -EIO; } - - /* Wait for sample */ - LOG_DBG("Waiting for sample..."); - k_usleep(AKM09918C_MEASURE_TIME_US); } + return 0; +} + +/** + * @brief Perform the bus transaction to fetch samples. + * + * @param dev Sensor device to operate on + * @param chan Channel ID to fetch + * @param x Location to write X channel sample. + * @param y Location to write Y channel sample. + * @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) +{ + const struct akm09918c_config *cfg = dev->config; + uint8_t buf[9] = {0}; /* We have to read through the TMPS register or the data_ready bit won't clear */ if (i2c_burst_read_dt(&cfg->i2c, AKM09918C_REG_ST1, buf, ARRAY_SIZE(buf)) != 0) { @@ -77,8 +86,16 @@ static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel { struct akm09918c_data *data = dev->data; - return akm09918c_sample_fetch_helper(dev, chan, &data->x_sample, &data->y_sample, - &data->z_sample); + int ret = akm09918c_start_measurement(dev, chan); + + if (ret) { + return ret; + } + /* Wait for sample */ + 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); } static void akm09918c_convert(struct sensor_value *val, int16_t sample) @@ -213,7 +230,10 @@ static int akm09918c_init(const struct device *dev) return rc; } data->mode = AKM09918C_CNTL2_PWR_DOWN; - +#ifdef CONFIG_SENSOR_ASYNC_API + /* init work for fetching after measurement has completed */ + k_work_init_delayable(&data->work_ctx.async_fetch_work, akm09918_async_fetch); +#endif return 0; } diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h index a4b35c64dd8387..f8a4f5c7c4395e 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2023 Google LLC + * Copyright (c) 2024 Florian Weber * SPDX-License-Identifier: Apache-2.0 */ @@ -34,6 +35,13 @@ struct akm09918c_data { int16_t y_sample; int16_t z_sample; uint8_t mode; +#ifdef CONFIG_SENSOR_ASYNC_API + struct akm09918c_async_fetch_ctx { + struct rtio_iodev_sqe *iodev_sqe; + uint64_t timestamp; + struct k_work_delayable async_fetch_work; + } work_ctx; +#endif }; struct akm09918c_config { @@ -74,22 +82,23 @@ 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_fetch_measurement(const struct device *dev, int16_t *x, int16_t *y, int16_t *z); /* * RTIO types */ struct akm09918c_decoder_header { uint64_t timestamp; -} __attribute__((__packed__)); +} __packed; struct akm09918c_encoded_data { struct akm09918c_decoder_header header; int16_t readings[3]; }; -int akm09918c_sample_fetch_helper(const struct device *dev, enum sensor_channel chan, int16_t *x, - int16_t *y, int16_t *z); +void akm09918_async_fetch(struct k_work *work); int akm09918c_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); diff --git a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c index e8ad6beca42ce5..832d046bdd7ab6 100644 --- a/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c +++ b/drivers/sensor/asahi_kasei/akm09918c/akm09918c_async.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2023 Google LLC * Copyright (c) 2024 Croxel Inc. + * Copyright (c) 2024 Florian Weber * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,32 +17,46 @@ void akm09918c_submit_sync(struct rtio_iodev_sqe *iodev_sqe) { const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data; const struct device *dev = cfg->sensor; - uint32_t min_buf_len = sizeof(struct akm09918c_encoded_data); + struct akm09918c_data *data = dev->data; + const struct sensor_chan_spec *const channels = cfg->channels; + const size_t num_channels = cfg->count; int rc; - uint8_t *buf; - uint32_t buf_len; - struct akm09918c_encoded_data *edata; - /* Get the buffer for the frame, it may be allocated dynamically by the rtio context */ - rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len); - if (rc != 0) { - LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len); - rtio_iodev_sqe_err(iodev_sqe, rc); - return; + /* Check if the requested channels are supported */ + for (size_t i = 0; i < num_channels; i++) { + switch (channels[i].chan_type) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: + case SENSOR_CHAN_ALL: + break; + default: + LOG_ERR("Unsupported channel type %d", channels[i].chan_type); + rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP); + return; + } } - edata = (struct akm09918c_encoded_data *)buf; - edata->header.timestamp = k_ticks_to_ns_floor64(k_uptime_ticks()); - - rc = akm09918c_sample_fetch_helper(dev, SENSOR_CHAN_MAGN_XYZ, &edata->readings[0], - &edata->readings[1], &edata->readings[2]); + /* start the measurement in the sensor */ + rc = akm09918c_start_measurement(dev, SENSOR_CHAN_MAGN_XYZ); if (rc != 0) { - LOG_ERR("Failed to fetch samples"); + LOG_ERR("Failed to fetch samples."); rtio_iodev_sqe_err(iodev_sqe, rc); return; } - rtio_iodev_sqe_ok(iodev_sqe, 0); + /* 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; + + 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); + } + return; } void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) @@ -57,3 +72,33 @@ void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe 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); + struct akm09918c_async_fetch_ctx *ctx = + 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; + uint32_t req_buf_len = sizeof(struct akm09918c_encoded_data); + uint32_t buf_len; + uint8_t *buf; + struct akm09918c_encoded_data *edata; + int rc; + + /* Get the buffer for the frame, it may be allocated dynamically by the rtio context */ + rc = rtio_sqe_rx_buf(ctx->iodev_sqe, req_buf_len, req_buf_len, &buf, &buf_len); + if (rc != 0) { + LOG_ERR("Failed to get a read buffer of size %u bytes", req_buf_len); + rtio_iodev_sqe_err(ctx->iodev_sqe, rc); + return; + } + edata = (struct akm09918c_encoded_data *)buf; + rc = akm09918c_fetch_measurement(dev, &edata->readings[0], &edata->readings[1], + &edata->readings[2]); + if (rc != 0) { + rtio_iodev_sqe_err(ctx->iodev_sqe, rc); + return; + } + rtio_iodev_sqe_ok(ctx->iodev_sqe, 0); +}