1 /*
2 * Copyright (c) 2023 Google LLC
3 * Copyright (c) 2024 Croxel Inc.
4 * Copyright (c) 2024 Florian Weber <Florian.Weber@live.de>
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/logging/log.h>
10 #include <zephyr/rtio/rtio.h>
11 #include <zephyr/drivers/sensor_clock.h>
12 #include <zephyr/sys/byteorder.h>
13
14 #include "akm09918c.h"
15
16 LOG_MODULE_DECLARE(AKM09918C, CONFIG_SENSOR_LOG_LEVEL);
17
akm09918c_flush_cqes(struct rtio * rtio_ctx)18 static int akm09918c_flush_cqes(struct rtio *rtio_ctx)
19 {
20 /* Flush completions */
21 struct rtio_cqe *cqe;
22 int res = 0;
23
24 do {
25 cqe = rtio_cqe_consume(rtio_ctx);
26 if (cqe != NULL) {
27 if ((cqe->result < 0 && res == 0)) {
28 LOG_ERR("Bus error: %d", cqe->result);
29 res = cqe->result;
30 }
31 rtio_cqe_release(rtio_ctx, cqe);
32 }
33 } while (cqe != NULL);
34 return res;
35 }
36
akm09918c_submit(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)37 void akm09918c_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
38 {
39 const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
40 struct akm09918c_data *data = dev->data;
41 const struct sensor_chan_spec *const channels = cfg->channels;
42 const size_t num_channels = cfg->count;
43
44 /* Check if the requested channels are supported */
45 for (size_t i = 0; i < num_channels; i++) {
46 switch (channels[i].chan_type) {
47 case SENSOR_CHAN_MAGN_X:
48 case SENSOR_CHAN_MAGN_Y:
49 case SENSOR_CHAN_MAGN_Z:
50 case SENSOR_CHAN_MAGN_XYZ:
51 case SENSOR_CHAN_ALL:
52 break;
53 default:
54 LOG_ERR("Unsupported channel type %d", channels[i].chan_type);
55 rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
56 return;
57 }
58 }
59 struct rtio_sqe *writeByte_sqe = i2c_rtio_copy_reg_write_byte(
60 data->rtio_ctx, data->iodev, AKM09918C_REG_CNTL2, AKM09918C_CNTL2_SINGLE_MEASURE);
61 struct rtio_sqe *cb_sqe = rtio_sqe_acquire(data->rtio_ctx);
62
63 writeByte_sqe->flags |= RTIO_SQE_CHAINED;
64 rtio_sqe_prep_callback_no_cqe(cb_sqe, akm09918_after_start_cb, (void *)iodev_sqe, NULL);
65
66 if (writeByte_sqe != NULL && cb_sqe != NULL) {
67 rtio_submit(data->rtio_ctx, 0);
68 } else {
69 rtio_sqe_drop_all(data->rtio_ctx);
70 rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
71 }
72 }
73
akm09918_after_start_cb(struct rtio * rtio_ctx,const struct rtio_sqe * sqe,void * arg0)74 void akm09918_after_start_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, void *arg0)
75 {
76 const struct rtio_iodev_sqe *parent_iodev_sqe = (struct rtio_iodev_sqe *)arg0;
77 const struct sensor_read_config *cfg = parent_iodev_sqe->sqe.iodev->data;
78 const struct device *dev = cfg->sensor;
79 struct akm09918c_data *data = dev->data;
80 struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)arg0;
81 uint64_t cycles;
82 int rc;
83
84 rc = sensor_clock_get_cycles(&cycles);
85 if (rc != 0) {
86 LOG_ERR("Failed to get sensor clock cycles");
87 rtio_iodev_sqe_err(iodev_sqe, rc);
88 return;
89 }
90
91 /* save information for the work item */
92 data->work_ctx.timestamp = sensor_clock_cycles_to_ns(cycles);
93 data->work_ctx.iodev_sqe = iodev_sqe;
94
95 rc = akm09918c_flush_cqes(data->rtio_ctx);
96 if (rc != 0) {
97 rtio_iodev_sqe_err(iodev_sqe, rc);
98 return;
99 }
100
101 rc = k_work_schedule(&data->work_ctx.async_fetch_work, K_USEC(AKM09918C_MEASURE_TIME_US));
102 if (rc == 0) {
103 LOG_ERR("The last fetch has not finished yet. "
104 "Try again later when the last sensor read operation has finished.");
105 rtio_iodev_sqe_err(iodev_sqe, -EBUSY);
106 }
107 return;
108 }
109
akm09918_async_fetch(struct k_work * work)110 void akm09918_async_fetch(struct k_work *work)
111 {
112 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
113 struct akm09918c_async_fetch_ctx *ctx =
114 CONTAINER_OF(dwork, struct akm09918c_async_fetch_ctx, async_fetch_work);
115 const struct sensor_read_config *cfg = ctx->iodev_sqe->sqe.iodev->data;
116 const struct device *dev = cfg->sensor;
117 struct akm09918c_data *data = dev->data;
118 uint32_t req_buf_len = sizeof(struct akm09918c_encoded_data);
119 uint32_t buf_len;
120 uint8_t *buf;
121 struct akm09918c_encoded_data *edata;
122 int rc;
123
124 /* Get the buffer for the frame, it may be allocated dynamically by the rtio context */
125 rc = rtio_sqe_rx_buf(ctx->iodev_sqe, req_buf_len, req_buf_len, &buf, &buf_len);
126 if (rc != 0) {
127 LOG_ERR("Failed to get a read buffer of size %u bytes", req_buf_len);
128 rtio_iodev_sqe_err(ctx->iodev_sqe, rc);
129 return;
130 }
131 edata = (struct akm09918c_encoded_data *)buf;
132
133 struct rtio_sqe *burstRead_sqe =
134 i2c_rtio_copy_reg_burst_read(data->rtio_ctx, data->iodev, AKM09918C_REG_ST1,
135 &(edata->reading), sizeof(edata->reading));
136
137 edata->header.timestamp = ctx->timestamp;
138
139 struct rtio_sqe *cb_sqe = rtio_sqe_acquire(data->rtio_ctx);
140
141 rtio_sqe_prep_callback_no_cqe(cb_sqe, akm09918_complete_cb, (void *)ctx->iodev_sqe, NULL);
142
143 if (burstRead_sqe != NULL && cb_sqe != NULL) {
144 burstRead_sqe->flags |= RTIO_SQE_CHAINED;
145 rtio_submit(data->rtio_ctx, 0);
146 } else {
147 rtio_sqe_drop_all(data->rtio_ctx);
148 rtio_iodev_sqe_err(ctx->iodev_sqe, -ENOMEM);
149 }
150 }
151
akm09918_complete_cb(struct rtio * rtio_ctx,const struct rtio_sqe * sqe,void * arg0)152 void akm09918_complete_cb(struct rtio *rtio_ctx, const struct rtio_sqe *sqe, void *arg0)
153 {
154 struct rtio_iodev_sqe *parent_iodev_sqe = (struct rtio_iodev_sqe *)arg0;
155 struct rtio_sqe *parent_sqe = &parent_iodev_sqe->sqe;
156 struct akm09918c_encoded_data *edata =
157 (struct akm09918c_encoded_data *)(parent_sqe->rx.buf);
158 int rc;
159
160 rc = akm09918c_flush_cqes(rtio_ctx);
161 if (rc != 0) {
162 rtio_iodev_sqe_err(parent_iodev_sqe, rc);
163 return;
164 }
165
166 if (FIELD_GET(AKM09918C_ST1_DRDY, edata->reading.st1) == 0) {
167 LOG_ERR("Data not ready, st1=0x%02x", edata->reading.st1);
168 rtio_iodev_sqe_err(parent_iodev_sqe, -EBUSY);
169 return;
170 }
171
172 edata->reading.data[0] = sys_le16_to_cpu(edata->reading.data[0]);
173 edata->reading.data[1] = sys_le16_to_cpu(edata->reading.data[1]);
174 edata->reading.data[2] = sys_le16_to_cpu(edata->reading.data[2]);
175
176 rtio_iodev_sqe_ok(parent_iodev_sqe, 0);
177 }
178