1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/i2c.h>
9 #include <zephyr/rtio/rtio.h>
10 #include <zephyr/drivers/i2c/rtio.h>
11 
12 #include <string.h>
13 
14 #define I2C_CONTROLLER_NODE              DT_ALIAS(i2c_controller)
15 #define I2C_CONTROLLER_TARGET_NODE       DT_ALIAS(i2c_controller_target)
16 #define I2C_CONTROLLER_DEVICE_GET        DEVICE_DT_GET(I2C_CONTROLLER_NODE)
17 #define I2C_CONTROLLER_TARGET_DEVICE_GET DEVICE_DT_GET(I2C_CONTROLLER_TARGET_NODE)
18 #define I2C_TARGET_ADDR                  0x0A
19 #define SAMPLE_TIMEOUT                   K_SECONDS(1)
20 
21 static const struct device *sample_i2c_controller = I2C_CONTROLLER_DEVICE_GET;
22 static const struct device *sample_i2c_controller_target = I2C_CONTROLLER_TARGET_DEVICE_GET;
23 
24 /* Data to write and buffer to store write in */
25 static uint8_t sample_write_data[CONFIG_I2C_RTIO_LOOPBACK_DATA_WRITE_MAX_SIZE];
26 static uint8_t sample_write_buf[sizeof(sample_write_data)];
27 static uint32_t sample_write_buf_pos;
28 
29 /* Data to read and buffer to store read in */
30 static uint8_t sample_read_data[CONFIG_I2C_RTIO_LOOPBACK_DATA_READ_MAX_SIZE];
31 static uint32_t sample_read_data_pos;
32 static uint8_t sample_read_buf[sizeof(sample_read_data)];
33 
34 /*
35  * The user defines an RTIO context to which actions like writes and reads will be
36  * submitted, and the results of said actions will be retrieved.
37  *
38  * We will be using 3 submission queue events (SQEs); i2c write, i2c read,
39  * done callback, and 2 completion queue events (CQEs); i2c write result,
40  * i2c read result.
41  */
42 RTIO_DEFINE(sample_rtio, 3, 2);
43 
44 /*
45  * The user defines an RTIO IODEV which wraps the device which will perform the
46  * actions submitted to the RTIO context. In this sample, we are using an I2C
47  * controller device, so we use the I2C specific helper to define the RTIO IODEV.
48  */
49 I2C_IODEV_DEFINE(sample_rtio_iodev, I2C_CONTROLLER_NODE, I2C_TARGET_ADDR);
50 
51 /*
52  * For async write read operation we will be waiting for a callback from RTIO.
53  * We will wait on this sem which we will give from the callback.
54  */
55 static K_SEM_DEFINE(sample_write_read_sem, 0, 1);
56 
57 /*
58  * We register a simple I2C target which we will be targeting using RTIO. We
59  * store written data, and return sample_read_data when read.
60  */
sample_target_write_requested(struct i2c_target_config * target_config)61 static int sample_target_write_requested(struct i2c_target_config *target_config)
62 {
63 	sample_write_buf_pos = 0;
64 	return 0;
65 }
66 
sample_target_read_requested(struct i2c_target_config * target_config,uint8_t * val)67 static int sample_target_read_requested(struct i2c_target_config *target_config, uint8_t *val)
68 {
69 	sample_read_data_pos = 0;
70 	*val = sample_read_data[sample_read_data_pos];
71 	return 0;
72 }
73 
sample_target_write_received(struct i2c_target_config * target_config,uint8_t val)74 static int sample_target_write_received(struct i2c_target_config *target_config, uint8_t val)
75 {
76 	if (sample_write_buf_pos == sizeof(sample_write_buf)) {
77 		return -ENOMEM;
78 	}
79 
80 	sample_write_buf[sample_write_buf_pos] = val;
81 	sample_write_buf_pos++;
82 	return 0;
83 }
84 
sample_target_read_processed(struct i2c_target_config * target_config,uint8_t * val)85 static int sample_target_read_processed(struct i2c_target_config *target_config, uint8_t *val)
86 {
87 	sample_read_data_pos++;
88 
89 	if (sample_read_data_pos == sizeof(sample_read_data)) {
90 		return -ENOMEM;
91 	}
92 
93 	*val = sample_read_data[sample_read_data_pos];
94 	return 0;
95 }
96 
97 #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
sample_target_buf_write_received(struct i2c_target_config * target_config,uint8_t * data,uint32_t size)98 static void sample_target_buf_write_received(struct i2c_target_config *target_config,
99 					     uint8_t *data,
100 					     uint32_t size)
101 {
102 	sample_write_buf_pos = MIN(size, ARRAY_SIZE(sample_write_buf));
103 	memcpy(sample_write_buf, data, sample_write_buf_pos);
104 }
105 
sample_target_buf_read_requested(struct i2c_target_config * target_config,uint8_t ** data,uint32_t * size)106 static int sample_target_buf_read_requested(struct i2c_target_config *target_config,
107 					    uint8_t **data,
108 					    uint32_t *size)
109 {
110 	*data = sample_read_data;
111 	*size = sizeof(sample_read_data);
112 	return 0;
113 }
114 #endif /* CONFIG_I2C_TARGET_BUFFER_MODE */
115 
sample_target_stop(struct i2c_target_config * config)116 static int sample_target_stop(struct i2c_target_config *config)
117 {
118 	ARG_UNUSED(config);
119 	return 0;
120 }
121 
122 static const struct i2c_target_callbacks sample_target_callbacks = {
123 	.write_requested = sample_target_write_requested,
124 	.read_requested = sample_target_read_requested,
125 	.write_received = sample_target_write_received,
126 	.read_processed = sample_target_read_processed,
127 #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
128 	.buf_write_received = sample_target_buf_write_received,
129 	.buf_read_requested = sample_target_buf_read_requested,
130 #endif
131 	.stop = sample_target_stop,
132 };
133 
134 static struct i2c_target_config sample_target_config = {
135 	.address = I2C_TARGET_ADDR,
136 	.callbacks = &sample_target_callbacks,
137 };
138 
sample_init_i2c_target(void)139 static int sample_init_i2c_target(void)
140 {
141 	return i2c_target_register(sample_i2c_controller_target, &sample_target_config);
142 }
143 
sample_reset_buffers(void)144 static void sample_reset_buffers(void)
145 {
146 	memset(sample_write_buf, 0, sizeof(sample_write_buf));
147 	memset(sample_read_buf, 0, sizeof(sample_read_buf));
148 }
149 
sample_standard_write_read(void)150 static int sample_standard_write_read(void)
151 {
152 	int ret;
153 	struct i2c_msg msgs[2];
154 
155 	msgs[0].buf = sample_write_data;
156 	msgs[0].len = sizeof(sample_write_data);
157 	msgs[0].flags = I2C_MSG_WRITE;
158 
159 	msgs[1].buf = sample_read_buf;
160 	msgs[1].len = sizeof(sample_read_buf);
161 	msgs[1].flags = I2C_MSG_RESTART | I2C_MSG_READ | I2C_MSG_STOP;
162 
163 	ret = i2c_transfer(sample_i2c_controller,
164 			   msgs,
165 			   ARRAY_SIZE(msgs),
166 			   I2C_TARGET_ADDR);
167 	if (ret) {
168 		return -EIO;
169 	}
170 
171 	return 0;
172 }
173 
sample_validate_write_read(void)174 static int sample_validate_write_read(void)
175 {
176 	int ret;
177 
178 	if (sample_write_buf_pos != sizeof(sample_write_data)) {
179 		printk("Posittion error: %zu != %zu\n",
180 		       sample_write_buf_pos, sizeof(sample_write_data));
181 		return -EIO;
182 	}
183 
184 	ret = memcmp(sample_write_buf, sample_write_data, sizeof(sample_write_data));
185 	if (ret) {
186 		for (int n = 0; n < sizeof(sample_write_data); n++) {
187 			if (sample_write_buf[n] != sample_write_data[n]) {
188 				printk("Write at offset %u: %02x != %02x\n",
189 				       n, sample_write_buf[n], sample_write_data[n]);
190 			}
191 		}
192 		return -EIO;
193 	}
194 
195 	ret = memcmp(sample_read_buf, sample_read_data, sizeof(sample_read_data));
196 	if (ret) {
197 		for (int n = 0; n < sizeof(sample_read_data); n++) {
198 			if (sample_read_buf[n] != sample_read_data[n]) {
199 				printk("Read at offset %u: %02x != %02x\n",
200 				       n, sample_read_buf[n], sample_read_data[n]);
201 			}
202 		}
203 		return -EIO;
204 	}
205 
206 	return 0;
207 }
208 
209 /* This is functionally identical to sample_standard_write_read() but uses RTIO */
sample_rtio_write_read(void)210 static int sample_rtio_write_read(void)
211 {
212 	struct rtio_sqe *wr_sqe, *rd_sqe;
213 	struct rtio_cqe *wr_rd_cqe;
214 	int ret;
215 
216 	/*
217 	 * We allocate one of the 3 submission queue events (SQEs) as defined by
218 	 * RTIO_DEFINE() and configure it to write sample_write_data to
219 	 * sample_rtio_iodev.
220 	 */
221 	wr_sqe = rtio_sqe_acquire(&sample_rtio);
222 	rtio_sqe_prep_write(wr_sqe,
223 			    &sample_rtio_iodev,
224 			    0,
225 			    sample_write_data,
226 			    sizeof(sample_write_data),
227 			    NULL);
228 
229 	/*
230 	 * This write SQE is followed by a read SQE, which is part of a single
231 	 * transaction. We configure this by setting the RTIO_SQE_TRANSACTION.
232 	 */
233 	wr_sqe->flags |= RTIO_SQE_TRANSACTION;
234 
235 	/*
236 	 * We then allocate an SQE and configure it to read
237 	 * sizeof(sample_read_buf) into sample_read_buf.
238 	 */
239 	rd_sqe = rtio_sqe_acquire(&sample_rtio);
240 	rtio_sqe_prep_read(rd_sqe,
241 			    &sample_rtio_iodev,
242 			    0,
243 			    sample_read_buf,
244 			    sizeof(sample_read_buf), NULL);
245 
246 	/*
247 	 * Since we are working with I2C messages, we need to specify the I2C
248 	 * message options. The I2C_READ and I2C_WRITE are implicit since we
249 	 * are preparing read and write SQEs, I2C_STOP and I2C_RESTART are not,
250 	 * so we add them to the read SQE.
251 	 */
252 	rd_sqe->iodev_flags = RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
253 
254 	/*
255 	 * With the two SQEs of the sample_rtio context prepared, we call
256 	 * rtio_submit() to have them executed. This call will execute all
257 	 * prepared SQEs.
258 	 *
259 	 * In this case, we wait for the two SQEs to be completed before
260 	 * continuing, similar to calling i2c_transfer().
261 	 */
262 	ret = rtio_submit(&sample_rtio, 2);
263 	if (ret) {
264 		return -EIO;
265 	}
266 
267 	/*
268 	 * Since the RTIO SQEs are executed in the background, we need to
269 	 * get the CQE after and check its result. Since we configured the
270 	 * write and read SQEs as a single transaction, only one CQE is
271 	 * generated which includes both of them. If we had chained them
272 	 * instead, one CQE would be created for each of them.
273 	 */
274 	wr_rd_cqe = rtio_cqe_consume(&sample_rtio);
275 	if (wr_rd_cqe->result) {
276 		return -EIO;
277 	}
278 
279 	/* Release the CQE after having checked its result. */
280 	rtio_cqe_release(&sample_rtio, wr_rd_cqe);
281 	return 0;
282 }
283 
rtio_write_read_done_callback(struct rtio * r,const struct rtio_sqe * sqe,int result,void * arg0)284 static void rtio_write_read_done_callback(struct rtio *r, const struct rtio_sqe *sqe,
285 					  int result, void *arg0)
286 {
287 	struct k_sem *sem = arg0;
288 	struct rtio_cqe *wr_rd_cqe;
289 
290 	/* See sample_rtio_write_read() */
291 	wr_rd_cqe = rtio_cqe_consume(&sample_rtio);
292 	if (wr_rd_cqe->result) {
293 		/* Signal write and read SQEs completed with error */
294 		k_sem_reset(sem);
295 	}
296 
297 	/* See sample_rtio_write_read() */
298 	rtio_cqe_release(&sample_rtio, wr_rd_cqe);
299 
300 	/* Signal write and read SQEs completed with success */
301 	k_sem_give(sem);
302 }
303 
304 /*
305  * Aside from the blocking wait for the sample_write_read_sem, async RTIO
306  * can be performed entirely from within ISRs.
307  */
sample_rtio_write_read_async(void)308 static int sample_rtio_write_read_async(void)
309 {
310 	struct rtio_sqe *wr_sqe, *rd_sqe, *cb_sqe;
311 	int ret;
312 
313 	/* See sample_rtio_write_read() */
314 	wr_sqe = rtio_sqe_acquire(&sample_rtio);
315 	rtio_sqe_prep_write(wr_sqe,
316 			    &sample_rtio_iodev,
317 			    0,
318 			    sample_write_data,
319 			    sizeof(sample_write_data), NULL);
320 	wr_sqe->flags |= RTIO_SQE_TRANSACTION;
321 
322 	/* See sample_rtio_write_read() */
323 	rd_sqe = rtio_sqe_acquire(&sample_rtio);
324 	rtio_sqe_prep_read(rd_sqe,
325 			    &sample_rtio_iodev,
326 			    0,
327 			    sample_read_buf,
328 			    sizeof(sample_read_buf), NULL);
329 	rd_sqe->iodev_flags = RTIO_IODEV_I2C_STOP | RTIO_IODEV_I2C_RESTART;
330 
331 	/*
332 	 * The next SQE is a callback which we will use to signal that the
333 	 * write and read SQEs have completed. It has to be executed only
334 	 * after the write and read SQEs, which we configure by setting the
335 	 * RTIO_SQE_CHAINED flag.
336 	 */
337 	rd_sqe->flags |= RTIO_SQE_CHAINED;
338 
339 	/*
340 	 * Prepare the callback SQE. The SQE allows us to pass an optional
341 	 * argument to the handler, which we will use to store a pointer to
342 	 * the binary semaphore we will be waiting on.
343 	 */
344 	cb_sqe = rtio_sqe_acquire(&sample_rtio);
345 	rtio_sqe_prep_callback_no_cqe(cb_sqe,
346 				      rtio_write_read_done_callback,
347 				      &sample_write_read_sem,
348 				      NULL);
349 
350 	/*
351 	 * Submit the SQEs for execution, without waiting for any of them
352 	 * to be completed. We use the callback to signal completion of all
353 	 * of them.
354 	 */
355 	ret = rtio_submit(&sample_rtio, 0);
356 	if (ret) {
357 		return -EIO;
358 	}
359 
360 	/*
361 	 * We wait for the callback which signals RTIO transfer has completed.
362 	 *
363 	 * We will be checking the CQE result from within the callback, which
364 	 * is entirely safe given RTIO is designed to work from ISR context.
365 	 * If the result is ok, we give the sem, if its not ok, we reset the
366 	 * sem (which makes k_sem_take() return an error).
367 	 */
368 	ret = k_sem_take(&sample_write_read_sem, SAMPLE_TIMEOUT);
369 	if (ret) {
370 		return -EIO;
371 	}
372 
373 	return 0;
374 }
375 
main(void)376 int main(void)
377 {
378 	int ret, n;
379 
380 	for (n = 0; n < sizeof(sample_write_data); n++) {
381 		sample_write_data[n] = (0xFF - n) % 0xFF;
382 	}
383 
384 	for (n = 0; n < sizeof(sample_read_data); n++) {
385 		sample_read_data[n] = n % 0xFF;
386 	}
387 
388 	printk("%s %s\n", "init_i2c_target", "running");
389 	ret = sample_init_i2c_target();
390 	if (ret) {
391 		printk("%s %s\n", "init_i2c_target", "failed");
392 		return 0;
393 	}
394 
395 	sample_reset_buffers();
396 
397 	printk("%s %s\n", "standard_write_read", "running");
398 	ret = sample_standard_write_read();
399 	if (ret) {
400 		printk("%s %s\n", "standard_write_read", "failed");
401 		return 0;
402 	}
403 
404 	ret = sample_validate_write_read();
405 	if (ret) {
406 		printk("%s %s\n", "standard_write_read", "corrupted");
407 		return 0;
408 	}
409 
410 	sample_reset_buffers();
411 
412 	printk("%s %s\n", "rtio_write_read", "running");
413 	ret = sample_rtio_write_read();
414 	if (ret) {
415 		printk("%s %s\n", "rtio_write_read", "failed");
416 		return 0;
417 	}
418 
419 	ret = sample_validate_write_read();
420 	if (ret) {
421 		printk("%s %s\n", "rtio_write_read", "corrupted");
422 		return 0;
423 	}
424 
425 	sample_reset_buffers();
426 
427 	printk("%s %s\n", "rtio_write_read_async", "running");
428 	ret = sample_rtio_write_read_async();
429 	if (ret) {
430 		printk("%s %s\n", "rtio_write_read_async", "failed");
431 		return 0;
432 	}
433 
434 	ret = sample_validate_write_read();
435 	if (ret) {
436 		printk("%s %s\n", "rtio_write_read_async", "corrupted");
437 		return 0;
438 	}
439 
440 	printk("sample complete\n");
441 	return 0;
442 }
443