1 /*
2  * Copyright (c) 2024 Florian Weber <Florian.Weber@live.de>
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT melexis_mlx90394
7 
8 #include <zephyr/logging/log.h>
9 #include <zephyr/sys/util.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/sensor_clock.h>
13 
14 #include "mlx90394.h"
15 #include "mlx90394_reg.h"
16 
17 #include <stddef.h>
18 
19 LOG_MODULE_DECLARE(MLX90394, CONFIG_SENSOR_LOG_LEVEL);
20 
mlx90394_async_fetch(struct k_work * work)21 void mlx90394_async_fetch(struct k_work *work)
22 {
23 	int rc;
24 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
25 	struct mlx90394_data *data = CONTAINER_OF(dwork, struct mlx90394_data, async_fetch_work);
26 	const struct device *dev = data->dev;
27 	const struct sensor_read_config *cfg =
28 		data->work_ctx.iodev_sqe->sqe.iodev->data;
29 	struct mlx90394_encoded_data *edata;
30 	uint32_t buf_len = sizeof(struct mlx90394_encoded_data);
31 	uint8_t *buf;
32 
33 	rc = mlx90394_sample_fetch_internal(dev, cfg->channels->chan_type);
34 	if (rc != 0) {
35 		LOG_ERR("Failed to fetch samples");
36 		rtio_iodev_sqe_err(data->work_ctx.iodev_sqe, rc);
37 		return;
38 	}
39 	/* Get the buffer for the frame, it may be allocated dynamically by the rtio context */
40 	rc = rtio_sqe_rx_buf(data->work_ctx.iodev_sqe, buf_len, buf_len, &buf, &buf_len);
41 	if (rc != 0) {
42 		LOG_ERR("Failed to get a read buffer of size %u bytes", buf_len);
43 		rtio_iodev_sqe_err(data->work_ctx.iodev_sqe, rc);
44 		return;
45 	}
46 
47 	edata = (struct mlx90394_encoded_data *)buf;
48 
49 	/* buffered from submit */
50 	edata->header.timestamp = data->work_ctx.timestamp;
51 	edata->header.config_val = data->work_ctx.config_val;
52 
53 	switch (cfg->channels->chan_type) {
54 	case SENSOR_CHAN_MAGN_X: {
55 		edata->readings[0] =
56 			(int16_t)((uint16_t)data->sample.x_l | (uint16_t)(data->sample.x_h << 8));
57 	} break;
58 	case SENSOR_CHAN_MAGN_Y: {
59 		edata->readings[1] =
60 			(int16_t)((uint16_t)data->sample.y_l | (uint16_t)(data->sample.y_h << 8));
61 	} break;
62 	case SENSOR_CHAN_MAGN_Z: {
63 		edata->readings[2] =
64 			(int16_t)((uint16_t)data->sample.z_l | (uint16_t)(data->sample.z_h << 8));
65 	} break;
66 	case SENSOR_CHAN_AMBIENT_TEMP: {
67 		edata->readings[3] = (int16_t)((uint16_t)data->sample.temp_l |
68 					       (uint16_t)(data->sample.temp_h << 8));
69 	} break;
70 	case SENSOR_CHAN_MAGN_XYZ: {
71 		edata->readings[0] =
72 			(int16_t)((uint16_t)data->sample.x_l | (uint16_t)(data->sample.x_h << 8));
73 		edata->readings[1] =
74 			(int16_t)((uint16_t)data->sample.y_l | (uint16_t)(data->sample.y_h << 8));
75 		edata->readings[2] =
76 			(int16_t)((uint16_t)data->sample.z_l | (uint16_t)(data->sample.z_h << 8));
77 	} break;
78 	case SENSOR_CHAN_ALL: {
79 		edata->readings[0] =
80 			(int16_t)((uint16_t)data->sample.x_l | (uint16_t)(data->sample.x_h << 8));
81 		edata->readings[1] =
82 			(int16_t)((uint16_t)data->sample.y_l | (uint16_t)(data->sample.y_h << 8));
83 		edata->readings[2] =
84 			(int16_t)((uint16_t)data->sample.z_l | (uint16_t)(data->sample.z_h << 8));
85 		edata->readings[3] = (int16_t)((uint16_t)data->sample.temp_l |
86 					       (uint16_t)(data->sample.temp_h << 8));
87 	} break;
88 	default: {
89 		LOG_DBG("Invalid channel %d", cfg->channels->chan_type);
90 		rtio_iodev_sqe_err(data->work_ctx.iodev_sqe, -ENOTSUP);
91 		return;
92 	}
93 	}
94 	rtio_iodev_sqe_ok(data->work_ctx.iodev_sqe, 0);
95 }
96 
mlx90394_submit(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)97 void mlx90394_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
98 {
99 	int rc;
100 	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
101 	struct mlx90394_data *data = dev->data;
102 	uint64_t cycles;
103 
104 	rc = mlx90394_trigger_measurement_internal(dev, cfg->channels->chan_type);
105 	if (rc != 0) {
106 		LOG_ERR("Failed to trigger measurement");
107 		rtio_iodev_sqe_err(iodev_sqe, rc);
108 		return;
109 	}
110 
111 	rc = sensor_clock_get_cycles(&cycles);
112 	if (rc != 0) {
113 		LOG_ERR("Failed to get sensor clock cycles");
114 		rtio_iodev_sqe_err(iodev_sqe, rc);
115 		return;
116 	}
117 
118 	/* save information for the work item */
119 	data->work_ctx.timestamp = sensor_clock_cycles_to_ns(cycles);
120 	data->work_ctx.iodev_sqe = iodev_sqe;
121 	data->work_ctx.config_val = data->config_val;
122 
123 	/* schedule work to read out sensor and inform the executor about completion with success */
124 	k_work_schedule(&data->async_fetch_work, K_USEC(data->measurement_time_us));
125 }
126