diff --git a/drivers/sensor/akm09918c/CMakeLists.txt b/drivers/sensor/akm09918c/CMakeLists.txt index 356b78dc8eb..7bfe5c3187f 100644 --- a/drivers/sensor/akm09918c/CMakeLists.txt +++ b/drivers/sensor/akm09918c/CMakeLists.txt @@ -4,5 +4,6 @@ zephyr_library() zephyr_library_sources(akm09918c.c) -zephyr_library_sources_ifdef(CONFIG_EMUL_AKM09918C akm09918c_emul.c) +zephyr_library_sources_ifdef(CONFIG_EMUL_AKM09918C akm09918c_emul.c) +zephyr_library_sources_ifdef(CONFIG_SENSOR_ASYNC_API akm09918c_async.c akm09918c_decoder.c) zephyr_include_directories_ifdef(CONFIG_EMUL_AKM09918C .) diff --git a/drivers/sensor/akm09918c/akm09918c.c b/drivers/sensor/akm09918c/akm09918c.c index 1225d69a3d2..50664bcdbc5 100644 --- a/drivers/sensor/akm09918c/akm09918c.c +++ b/drivers/sensor/akm09918c/akm09918c.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,18 @@ LOG_MODULE_REGISTER(AKM09918C, CONFIG_SENSOR_LOG_LEVEL); -static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel chan) +/** + * @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_sample_fetch_helper(const struct device *dev, enum sensor_channel chan, int16_t *x, + int16_t *y, int16_t *z) { struct akm09918c_data *data = dev->data; const struct akm09918c_config *cfg = dev->config; @@ -54,13 +66,21 @@ static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel return -EBUSY; } - data->x_sample = sys_le16_to_cpu(buf[1] | (buf[2] << 8)); - data->y_sample = sys_le16_to_cpu(buf[3] | (buf[4] << 8)); - data->z_sample = sys_le16_to_cpu(buf[5] | (buf[6] << 8)); + *x = sys_le16_to_cpu(buf[1] | (buf[2] << 8)); + *y = sys_le16_to_cpu(buf[3] | (buf[4] << 8)); + *z = sys_le16_to_cpu(buf[5] | (buf[6] << 8)); return 0; } +static int akm09918c_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + struct akm09918c_data *data = dev->data; + + return akm09918c_sample_fetch_helper(dev, chan, &data->x_sample, &data->y_sample, + &data->z_sample); +} + static void akm09918c_convert(struct sensor_value *val, int16_t sample) { int64_t conv_val = sample * AKM09918C_MICRO_GAUSS_PER_BIT; @@ -149,13 +169,6 @@ static int akm09918c_attr_set(const struct device *dev, enum sensor_channel chan return 0; } -static const struct sensor_driver_api akm09918c_driver_api = { - .sample_fetch = akm09918c_sample_fetch, - .channel_get = akm09918c_channel_get, - .attr_get = akm09918c_attr_get, - .attr_set = akm09918c_attr_set, -}; - static inline int akm09918c_check_who_am_i(const struct i2c_dt_spec *i2c) { uint8_t buffer[2]; @@ -204,6 +217,17 @@ static int akm09918c_init(const struct device *dev) return 0; } +static const struct sensor_driver_api akm09918c_driver_api = { + .sample_fetch = akm09918c_sample_fetch, + .channel_get = akm09918c_channel_get, + .attr_get = akm09918c_attr_get, + .attr_set = akm09918c_attr_set, +#ifdef CONFIG_SENSOR_ASYNC_API + .submit = akm09918c_submit, + .get_decoder = akm09918c_get_decoder, +#endif +}; + #define AKM09918C_DEFINE(inst) \ static struct akm09918c_data akm09918c_data_##inst; \ \ diff --git a/drivers/sensor/akm09918c/akm09918c.h b/drivers/sensor/akm09918c/akm09918c.h index 1745715287e..742c7d3be38 100644 --- a/drivers/sensor/akm09918c/akm09918c.h +++ b/drivers/sensor/akm09918c/akm09918c.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "akm09918c_reg.h" @@ -74,4 +75,24 @@ static inline void akm09918c_reg_to_hz(uint8_t reg, struct sensor_value *val) } } +/* + * RTIO types + */ + +struct akm09918c_decoder_header { + uint64_t timestamp; +} __attribute__((__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); + +int akm09918c_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder); + +int akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe); + #endif /* ZEPHYR_DRIVERS_SENSOR_AKM09918C_AKM09918C_H_ */ diff --git a/drivers/sensor/akm09918c/akm09918c_async.c b/drivers/sensor/akm09918c/akm09918c_async.c new file mode 100644 index 00000000000..438ca24780d --- /dev/null +++ b/drivers/sensor/akm09918c/akm09918c_async.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "akm09918c.h" + +LOG_MODULE_DECLARE(AKM09918C, CONFIG_SENSOR_LOG_LEVEL); + +int akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe) +{ + uint32_t min_buf_len = sizeof(struct akm09918c_encoded_data); + 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 rc; + } + + 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]); + if (rc != 0) { + LOG_ERR("Failed to fetch samples"); + rtio_iodev_sqe_err(iodev_sqe, rc); + return rc; + } + + rtio_iodev_sqe_ok(iodev_sqe, 0); + + return 0; +} diff --git a/drivers/sensor/akm09918c/akm09918c_decoder.c b/drivers/sensor/akm09918c/akm09918c_decoder.c new file mode 100644 index 00000000000..9bd6b312b7a --- /dev/null +++ b/drivers/sensor/akm09918c/akm09918c_decoder.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "akm09918c.h" + +static int akm09918c_decoder_get_frame_count(const uint8_t *buffer, enum sensor_channel channel, + size_t channel_idx, uint16_t *frame_count) +{ + ARG_UNUSED(buffer); + ARG_UNUSED(channel); + ARG_UNUSED(channel_idx); + + /* This sensor lacks a FIFO; there will always only be one frame at a time. */ + *frame_count = 1; + return 0; +} + +static int akm09918c_decoder_get_size_info(enum sensor_channel channel, size_t *base_size, + size_t *frame_size) +{ + switch (channel) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: + *base_size = sizeof(struct sensor_three_axis_data); + *frame_size = sizeof(struct sensor_three_axis_sample_data); + return 0; + default: + return -ENOTSUP; + } +} + +/** Fixed shift value to use. All channels (MAGN_X, _Y, and _Z) have the same fixed range of + * +/- 49.12 Gauss. + */ +#define AKM09918C_SHIFT (6) + +static int akm09918c_convert_raw_to_q31(int16_t reading, q31_t *out) +{ + int64_t intermediate = ((int64_t)reading * AKM09918C_MICRO_GAUSS_PER_BIT) * + ((int64_t)INT32_MAX + 1) / + ((1 << AKM09918C_SHIFT) * INT64_C(1000000)); + + *out = CLAMP(intermediate, INT32_MIN, INT32_MAX); + return 0; +} + +static int akm09918c_decoder_decode(const uint8_t *buffer, enum sensor_channel channel, + size_t channel_idx, uint32_t *fit, + uint16_t max_count, void *data_out) +{ + const struct akm09918c_encoded_data *edata = (const struct akm09918c_encoded_data *)buffer; + + if (*fit != 0) { + return 0; + } + + switch (channel) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: { + struct sensor_three_axis_data *out = data_out; + + out->header.base_timestamp_ns = edata->header.timestamp; + 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); + *fit = 1; + + return 1; + } + default: + return -EINVAL; + } +} + +SENSOR_DECODER_API_DT_DEFINE() = { + .get_frame_count = akm09918c_decoder_get_frame_count, + .get_size_info = akm09918c_decoder_get_size_info, + .decode = akm09918c_decoder_decode, +}; + +int akm09918c_get_decoder(const struct device *dev, const struct sensor_decoder_api **decoder) +{ + ARG_UNUSED(dev); + *decoder = &SENSOR_DECODER_NAME(); + + return 0; +}