1 /*
2  * Copyright (c) 2025 Croxel Inc.
3  * Copyright (c) 2025 CogniPilot Foundation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT pixart_paa3905
9 
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/drivers/spi.h>
12 #include <zephyr/rtio/rtio.h>
13 
14 #include "paa3905.h"
15 #include "paa3905_bus.h"
16 #include "paa3905_stream.h"
17 #include "paa3905_decoder.h"
18 
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(PAA3905, CONFIG_SENSOR_LOG_LEVEL);
21 
22 /* Helper struct used to set reg-val sequences for op-modes */
23 struct reg_val_pair {
24 	uint8_t reg;
25 	uint8_t val;
26 };
27 
paa3905_complete_result(struct rtio * ctx,const struct rtio_sqe * sqe,void * arg)28 static void paa3905_complete_result(struct rtio *ctx,
29 				    const struct rtio_sqe *sqe,
30 				    void *arg)
31 {
32 	struct rtio_iodev_sqe *iodev_sqe = (struct rtio_iodev_sqe *)sqe->userdata;
33 	struct rtio_cqe *cqe;
34 	int err = 0;
35 
36 	do {
37 		cqe = rtio_cqe_consume(ctx);
38 		if (cqe != NULL) {
39 			err = cqe->result;
40 			rtio_cqe_release(ctx, cqe);
41 		}
42 	} while (cqe != NULL);
43 
44 	if (err) {
45 		rtio_iodev_sqe_err(iodev_sqe, err);
46 	} else {
47 		rtio_iodev_sqe_ok(iodev_sqe, 0);
48 	}
49 
50 	LOG_DBG("One-shot fetch completed");
51 }
52 
paa3905_submit_one_shot(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)53 static void paa3905_submit_one_shot(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
54 {
55 	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
56 	const struct sensor_chan_spec *const channels = cfg->channels;
57 	struct paa3905_data *data = dev->data;
58 	const size_t num_channels = cfg->count;
59 	uint32_t min_buf_len = sizeof(struct paa3905_encoded_data);
60 	int err;
61 	uint8_t *buf;
62 	uint32_t buf_len;
63 	struct paa3905_encoded_data *edata;
64 
65 	err = rtio_sqe_rx_buf(iodev_sqe, min_buf_len, min_buf_len, &buf, &buf_len);
66 	if (err) {
67 		LOG_ERR("Failed to get a read buffer of size %u bytes", min_buf_len);
68 		rtio_iodev_sqe_err(iodev_sqe, err);
69 		return;
70 	}
71 
72 	edata = (struct paa3905_encoded_data *)buf;
73 
74 	err = paa3905_encode(dev, channels, num_channels, buf);
75 	if (err != 0) {
76 		LOG_ERR("Failed to encode sensor data");
77 		rtio_iodev_sqe_err(iodev_sqe, err);
78 		return;
79 	}
80 
81 	struct rtio_sqe *write_sqe = rtio_sqe_acquire(data->rtio.ctx);
82 	struct rtio_sqe *read_sqe = rtio_sqe_acquire(data->rtio.ctx);
83 	struct rtio_sqe *complete_sqe = rtio_sqe_acquire(data->rtio.ctx);
84 
85 	if (!write_sqe || !read_sqe | !complete_sqe) {
86 		LOG_ERR("Failed to acquire RTIO SQEs");
87 		rtio_iodev_sqe_err(iodev_sqe, -ENOMEM);
88 		return;
89 	}
90 
91 	uint8_t val = REG_BURST_READ | REG_SPI_READ_BIT;
92 
93 	rtio_sqe_prep_tiny_write(write_sqe,
94 				 data->rtio.iodev,
95 				 RTIO_PRIO_HIGH,
96 				 &val,
97 				 1,
98 				NULL);
99 	write_sqe->flags |= RTIO_SQE_TRANSACTION;
100 
101 	rtio_sqe_prep_read(read_sqe,
102 			   data->rtio.iodev,
103 			   RTIO_PRIO_HIGH,
104 			   edata->buf,
105 			   sizeof(edata->buf),
106 			   NULL);
107 	read_sqe->flags |= RTIO_SQE_CHAINED;
108 
109 	rtio_sqe_prep_callback_no_cqe(complete_sqe,
110 				      paa3905_complete_result,
111 				      (void *)dev,
112 				      iodev_sqe);
113 
114 	rtio_submit(data->rtio.ctx, 0);
115 }
116 
paa3905_submit(const struct device * dev,struct rtio_iodev_sqe * iodev_sqe)117 static void paa3905_submit(const struct device *dev, struct rtio_iodev_sqe *iodev_sqe)
118 {
119 	const struct sensor_read_config *cfg = iodev_sqe->sqe.iodev->data;
120 
121 	if (!cfg->is_streaming) {
122 		paa3905_submit_one_shot(dev, iodev_sqe);
123 	} else if (IS_ENABLED(CONFIG_PAA3905_STREAM)) {
124 		paa3905_stream_submit(dev, iodev_sqe);
125 	} else {
126 		LOG_ERR("Streaming not supported");
127 		rtio_iodev_sqe_err(iodev_sqe, -ENOTSUP);
128 	}
129 }
130 
131 static DEVICE_API(sensor, paa3905_driver_api) = {
132 	.submit = paa3905_submit,
133 	.get_decoder = paa3905_get_decoder,
134 };
135 
detection_mode_standard(const struct device * dev)136 static int detection_mode_standard(const struct device *dev)
137 {
138 	int err;
139 	/** As per datasheet, set of values configure the sensor in Standard mode. */
140 	const static struct reg_val_pair paa3905_detection_mode_std[] = {
141 		{.reg = 0x7F, .val = 0x00}, {.reg = 0x51, .val = 0xFF}, {.reg = 0x4E, .val = 0x2A},
142 		{.reg = 0x66, .val = 0x3E}, {.reg = 0x7F, .val = 0x14}, {.reg = 0x7E, .val = 0x71},
143 		{.reg = 0x55, .val = 0x00}, {.reg = 0x59, .val = 0x00}, {.reg = 0x6F, .val = 0x2C},
144 		{.reg = 0x7F, .val = 0x05}, {.reg = 0x4D, .val = 0xAC}, {.reg = 0x4E, .val = 0x32},
145 		{.reg = 0x7F, .val = 0x09}, {.reg = 0x5C, .val = 0xAF}, {.reg = 0x5F, .val = 0xAF},
146 		{.reg = 0x70, .val = 0x08}, {.reg = 0x71, .val = 0x04}, {.reg = 0x72, .val = 0x06},
147 		{.reg = 0x74, .val = 0x3C}, {.reg = 0x75, .val = 0x28}, {.reg = 0x76, .val = 0x20},
148 		{.reg = 0x4E, .val = 0xBF}, {.reg = 0x7F, .val = 0x03}, {.reg = 0x64, .val = 0x14},
149 		{.reg = 0x65, .val = 0x0A}, {.reg = 0x66, .val = 0x10}, {.reg = 0x55, .val = 0x3C},
150 		{.reg = 0x56, .val = 0x28}, {.reg = 0x57, .val = 0x20}, {.reg = 0x4A, .val = 0x2D},
151 		{.reg = 0x4B, .val = 0x2D}, {.reg = 0x4E, .val = 0x4B}, {.reg = 0x69, .val = 0xFA},
152 		{.reg = 0x7F, .val = 0x05}, {.reg = 0x69, .val = 0x1F}, {.reg = 0x47, .val = 0x1F},
153 		{.reg = 0x48, .val = 0x0C}, {.reg = 0x5A, .val = 0x20}, {.reg = 0x75, .val = 0x0F},
154 		{.reg = 0x4A, .val = 0x0F}, {.reg = 0x42, .val = 0x02}, {.reg = 0x45, .val = 0x03},
155 		{.reg = 0x65, .val = 0x00}, {.reg = 0x67, .val = 0x76}, {.reg = 0x68, .val = 0x76},
156 		{.reg = 0x6A, .val = 0xC5}, {.reg = 0x43, .val = 0x00}, {.reg = 0x7F, .val = 0x06},
157 		{.reg = 0x4A, .val = 0x18}, {.reg = 0x4B, .val = 0x0C}, {.reg = 0x4C, .val = 0x0C},
158 		{.reg = 0x4D, .val = 0x0C}, {.reg = 0x46, .val = 0x0A}, {.reg = 0x59, .val = 0xCD},
159 		{.reg = 0x7F, .val = 0x0A}, {.reg = 0x4A, .val = 0x2A}, {.reg = 0x48, .val = 0x96},
160 		{.reg = 0x52, .val = 0xB4}, {.reg = 0x7F, .val = 0x00}, {.reg = 0x5B, .val = 0xA0},
161 	};
162 
163 	for (size_t i = 0 ; i < ARRAY_SIZE(paa3905_detection_mode_std) ; i++) {
164 		err = paa3905_bus_write(dev,
165 					paa3905_detection_mode_std[i].reg,
166 					&paa3905_detection_mode_std[i].val,
167 					1);
168 		if (err) {
169 			LOG_ERR("Failed to write detection mode standard");
170 			return err;
171 		}
172 	}
173 
174 	return 0;
175 }
176 
paa3905_configure(const struct device * dev)177 static int paa3905_configure(const struct device *dev)
178 {
179 	const struct paa3905_config *cfg = dev->config;
180 	uint8_t val;
181 	int err;
182 
183 	/* Configure registers for Standard detection mode */
184 	err = detection_mode_standard(dev);
185 	if (err) {
186 		LOG_ERR("Failed to configure detection mode");
187 		return err;
188 	}
189 
190 	val = cfg->resolution;
191 	err = paa3905_bus_write(dev, REG_RESOLUTION, &val, 1);
192 	if (err) {
193 		LOG_ERR("Failed to configure resolution");
194 		return err;
195 	}
196 
197 	if (cfg->led_control) {
198 		struct reg_val_pair led_control_regs[] = {
199 			{.reg = 0x7F, .val = 0x14},
200 			{.reg = 0x6F, .val = 0x0C},
201 			{.reg = 0x7F, .val = 0x00},
202 		};
203 
204 		for (size_t i = 0 ; i < ARRAY_SIZE(led_control_regs) ; i++) {
205 			err = paa3905_bus_write(dev,
206 						led_control_regs[i].reg,
207 						&led_control_regs[i].val,
208 						1);
209 			if (err) {
210 				LOG_ERR("Failed to write LED control reg");
211 				return err;
212 			}
213 		}
214 	}
215 
216 	return 0;
217 }
218 
paa3905_recover(const struct device * dev)219 int paa3905_recover(const struct device *dev)
220 {
221 	int err;
222 	uint8_t val;
223 
224 	/* Write 0x5A to Power up reset reg */
225 	val = POWER_UP_RESET_VAL;
226 	err = paa3905_bus_write(dev, REG_POWER_UP_RESET, &val, 1);
227 	if (err) {
228 		LOG_ERR("Failed to write Power up reset reg");
229 		return err;
230 	}
231 	/* As per datasheet, writing power-up reset requires 1-ms afterwards. */
232 	k_sleep(K_MSEC(1));
233 
234 	/* Configure registers for Standard detection mode */
235 	err = paa3905_configure(dev);
236 	if (err) {
237 		LOG_ERR("Failed to configure");
238 		return err;
239 	}
240 
241 	return err;
242 }
243 
paa3905_init(const struct device * dev)244 static int paa3905_init(const struct device *dev)
245 {
246 	int err;
247 	uint8_t val;
248 	uint8_t motion_data[6];
249 
250 	/* Power-up sequence delay */
251 	k_sleep(K_MSEC(140));
252 
253 	/* Read Product ID */
254 	err = paa3905_bus_read(dev, REG_PRODUCT_ID, &val, 1);
255 	if (err) {
256 		LOG_ERR("Failed to read Product ID");
257 		return err;
258 	} else if (val != PRODUCT_ID) {
259 		LOG_ERR("Invalid Product ID: 0x%02X", val);
260 		return -EIO;
261 	}
262 
263 	/* Write 0x5A to Power up reset reg */
264 	val = POWER_UP_RESET_VAL;
265 	err = paa3905_bus_write(dev, REG_POWER_UP_RESET, &val, 1);
266 	if (err) {
267 		LOG_ERR("Failed to write Power up reset reg");
268 		return err;
269 	}
270 	/* As per datasheet, writing power-up reset requires 1-ms afterwards. */
271 	k_sleep(K_MSEC(1));
272 
273 	/* Read reg's 0x02-0x06 to clear motion data. */
274 	err = paa3905_bus_read(dev, REG_MOTION, motion_data, sizeof(motion_data));
275 	if (err) {
276 		LOG_ERR("Failed to read motion data");
277 		return err;
278 	}
279 
280 	if (IS_ENABLED(CONFIG_PAA3905_STREAM)) {
281 		err = paa3905_stream_init(dev);
282 		if (err) {
283 			LOG_ERR("Failed to initialize streaming");
284 			return err;
285 		}
286 	}
287 
288 	err = paa3905_configure(dev);
289 	if (err) {
290 		LOG_ERR("Failed to configure");
291 		return err;
292 	}
293 
294 	return 0;
295 }
296 
297 #define PAA3905_INIT(inst)									   \
298 												   \
299 	BUILD_ASSERT(DT_PROP(DT_DRV_INST(inst), resolution) > 0 &&				   \
300 		     DT_PROP(DT_DRV_INST(inst), resolution) <= 0xFF,				   \
301 		     "Resolution must be in range 1-255");					   \
302 												   \
303 	RTIO_DEFINE(paa3905_rtio_ctx_##inst, 8, 8);						   \
304 	SPI_DT_IODEV_DEFINE(paa3905_bus_##inst,							   \
305 			    DT_DRV_INST(inst),							   \
306 			    SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB,		   \
307 			    0U);								   \
308 												   \
309 	static const struct paa3905_config paa3905_cfg_##inst = {				   \
310 		.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}),			   \
311 		.backup_timer_period = DT_PROP(DT_DRV_INST(inst), backup_timer_ms),		   \
312 		.resolution = DT_PROP(DT_DRV_INST(inst), resolution),				   \
313 		.led_control = DT_PROP_OR(DT_DRV_INST(inst), led_control, false),		   \
314 	};											   \
315 												   \
316 	static struct paa3905_data paa3905_data_##inst = {					   \
317 		.rtio = {									   \
318 			.iodev = &paa3905_bus_##inst,						   \
319 			.ctx = &paa3905_rtio_ctx_##inst,					   \
320 		},										   \
321 	};											   \
322 												   \
323 	SENSOR_DEVICE_DT_INST_DEFINE(inst,							   \
324 				     paa3905_init,						   \
325 				     NULL,							   \
326 				     &paa3905_data_##inst,					   \
327 				     &paa3905_cfg_##inst,					   \
328 				     POST_KERNEL,						   \
329 				     CONFIG_SENSOR_INIT_PRIORITY,				   \
330 				     &paa3905_driver_api);
331 
332 DT_INST_FOREACH_STATUS_OKAY(PAA3905_INIT)
333