1 /* ST Microelectronics LSM6DSV16X 6-axis IMU sensor driver
2  *
3  * Copyright (c) 2023 Google LLC
4  * Copyright (c) 2024 STMicroelectronics
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr/drivers/sensor.h>
10 #include "lsm6dsv16x.h"
11 #include "lsm6dsv16x_rtio.h"
12 #include "lsm6dsv16x_decoder.h"
13 #include <zephyr/rtio/work.h>
14 #include <zephyr/drivers/sensor_clock.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(LSM6DSV16X_RTIO, CONFIG_SENSOR_LOG_LEVEL);
18 
lsm6dsv16x_submit_sample(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)19 static void lsm6dsv16x_submit_sample(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
20 {
21 	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
22 	const struct sensor_chan_spec *const channels = cfg->channels;
23 	const size_t num_channels = cfg->count;
24 	uint32_t min_buf_len = sizeof(struct lsm6dsv16x_rtio_data);
25 	uint64_t cycles;
26 	int rc = 0;
27 	uint8_t *buf;
28 	uint32_t buf_len;
29 	struct lsm6dsv16x_rtio_data *edata;
30 	struct lsm6dsv16x_data *data = dev->data;
31 
32 	const struct lsm6dsv16x_config *config = dev->config;
33 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx;
34 
35 	/* Get the buffer for the frame, it may be allocated dynamically by the rtio context */
36 	rc = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
37 	if (rc != 0) {
38 		LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
39 		goto err;
40 	}
41 
42 	edata = (struct lsm6dsv16x_rtio_data *)buf;
43 
44 	edata->has_accel = 0;
45 	edata->has_gyro = 0;
46 	edata->has_temp = 0;
47 
48 	for (int i = 0; i < num_channels; i++) {
49 		switch (channels[i].chan_type) {
50 		case SENSOR_CHAN_ACCEL_X:
51 		case SENSOR_CHAN_ACCEL_Y:
52 		case SENSOR_CHAN_ACCEL_Z:
53 		case SENSOR_CHAN_ACCEL_XYZ:
54 			edata->has_accel = 1;
55 
56 			rc = lsm6dsv16x_acceleration_raw_get(ctx, edata->acc);
57 			if (rc  < 0) {
58 				LOG_DBG("Failed to read accel sample");
59 				goto err;
60 			}
61 			break;
62 		case SENSOR_CHAN_GYRO_X:
63 		case SENSOR_CHAN_GYRO_Y:
64 		case SENSOR_CHAN_GYRO_Z:
65 		case SENSOR_CHAN_GYRO_XYZ:
66 			edata->has_gyro = 1;
67 
68 			rc = lsm6dsv16x_angular_rate_raw_get(ctx, edata->gyro);
69 			if (rc  < 0) {
70 				LOG_DBG("Failed to read gyro sample");
71 				goto err;
72 			}
73 			break;
74 #if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP)
75 		case SENSOR_CHAN_DIE_TEMP:
76 			edata->has_temp = 1;
77 
78 			rc = lsm6dsv16x_temperature_raw_get(ctx, &edata->temp);
79 			if (rc < 0) {
80 				LOG_DBG("Failed to read temp sample");
81 				goto err;
82 			}
83 			break;
84 #endif
85 		case SENSOR_CHAN_ALL:
86 			edata->has_accel = 1;
87 
88 			rc = lsm6dsv16x_acceleration_raw_get(ctx, edata->acc);
89 			if (rc  < 0) {
90 				LOG_DBG("Failed to read accel sample");
91 				goto err;
92 			}
93 
94 			edata->has_gyro = 1;
95 
96 			rc = lsm6dsv16x_angular_rate_raw_get(ctx, edata->gyro);
97 			if (rc  < 0) {
98 				LOG_DBG("Failed to read gyro sample");
99 				goto err;
100 			}
101 
102 #if defined(CONFIG_LSM6DSV16X_ENABLE_TEMP)
103 			edata->has_temp = 1;
104 
105 			rc = lsm6dsv16x_temperature_raw_get(ctx, &edata->temp);
106 			if (rc < 0) {
107 				LOG_DBG("Failed to read temp sample");
108 				goto err;
109 			}
110 #endif
111 			break;
112 		default:
113 			continue;
114 		}
115 	}
116 
117 	rc = sensor_clock_get_cycles(&cycles);
118 	if (rc != 0) {
119 		LOG_ERR("Failed to get sensor clock cycles");
120 		rtio_iodev_sqe_err(iodev_sqe, rc);
121 		goto err;
122 	}
123 
124 	edata->header.is_fifo = false;
125 	edata->header.accel_fs = data->accel_fs;
126 	edata->header.gyro_fs = data->gyro_fs;
127 	edata->header.timestamp = sensor_clock_cycles_to_ns(cycles);
128 
129 	rtio_iodev_sqe_ok(iodev_sqe, 0);
130 
131 err:
132 	/* Check that the fetch succeeded */
133 	if (rc != 0) {
134 		LOG_ERR("Failed to fetch samples");
135 		rtio_iodev_sqe_err(iodev_sqe, rc);
136 		return;
137 	}
138 }
139 
lsm6dsv16x_submit_sync(struct rtio_iodev_sqe * iodev_sqe)140 void lsm6dsv16x_submit_sync(struct rtio_iodev_sqe *iodev_sqe)
141 {
142 	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
143 	const struct device *dev = cfg->sensor;
144 
145 	if (!cfg->is_streaming) {
146 		lsm6dsv16x_submit_sample(dev, iodev_sqe);
147 	} else if (IS_ENABLED(CONFIG_LSM6DSV16X_STREAM)) {
148 		lsm6dsv16x_submit_stream(dev, iodev_sqe);
149 	} else {
150 		rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
151 	}
152 }
153 
lsm6dsv16x_submit(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)154 void lsm6dsv16x_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
155 {
156 	if (!lsm6dsv16x_is_active(dev)) {
157 		return;
158 	}
159 
160 	struct rtio_work_req *req = rtio_work_req_alloc();
161 
162 	if (req == NULL) {
163 		LOG_ERR("RTIO work item allocation failed. Consider to increase "
164 			"CONFIG_RTIO_WORKQ_POOL_ITEMS.");
165 		rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
166 		return;
167 	}
168 
169 	rtio_work_req_submit(req, iodev_sqe, lsm6dsv16x_submit_sync);
170 }
171