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