xref: /linux/drivers/iio/test/iio-test-gts.c (revision 6de2f3a1f4bb6abb01c0922c753544ec7f349dd4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Unit tests for IIO light sensor gain-time-scale helpers
3  *
4  * Copyright (c) 2023 Matti Vaittinen <mazziesaccount@gmail.com>
5  */
6 
7 #include <kunit/device.h>
8 #include <kunit/test.h>
9 #include <linux/device.h>
10 #include <linux/iio/iio-gts-helper.h>
11 #include <linux/iio/types.h>
12 
13 /*
14  * Please, read the "rant" from the top of the lib/test_linear_ranges.c if
15  * you see a line of helper code which is not being tested.
16  *
17  * Then, please look at the line which is not being tested. Is this line
18  * somehow unusually complex? If answer is "no", then chances are that the
19  * "development inertia" caused by adding a test exceeds the benefits.
20  *
21  * If yes, then adding a test is probably a good idea but please stop for a
22  * moment and consider the effort of changing all the tests when code gets
23  * refactored. Eventually it neeeds to be.
24  */
25 
26 #define TEST_TSEL_50		1
27 #define TEST_TSEL_X_MIN		TEST_TSEL_50
28 #define TEST_TSEL_100		0
29 #define TEST_TSEL_200		2
30 #define TEST_TSEL_400		4
31 #define TEST_TSEL_X_MAX		TEST_TSEL_400
32 
33 #define TEST_GSEL_1		0x00
34 #define TEST_GSEL_X_MIN		TEST_GSEL_1
35 #define TEST_GSEL_4		0x08
36 #define TEST_GSEL_16		0x0a
37 #define TEST_GSEL_32		0x0b
38 #define TEST_GSEL_64		0x0c
39 #define TEST_GSEL_256		0x18
40 #define TEST_GSEL_512		0x19
41 #define TEST_GSEL_1024		0x1a
42 #define TEST_GSEL_2048		0x1b
43 #define TEST_GSEL_4096		0x1c
44 #define TEST_GSEL_X_MAX		TEST_GSEL_4096
45 
46 #define TEST_SCALE_1X		64
47 #define TEST_SCALE_MIN_X	TEST_SCALE_1X
48 #define TEST_SCALE_2X		32
49 #define TEST_SCALE_4X		16
50 #define TEST_SCALE_8X		8
51 #define TEST_SCALE_16X		4
52 #define TEST_SCALE_32X		2
53 #define TEST_SCALE_64X		1
54 
55 #define TEST_SCALE_NANO_128X	500000000
56 #define TEST_SCALE_NANO_256X	250000000
57 #define TEST_SCALE_NANO_512X	125000000
58 #define TEST_SCALE_NANO_1024X	62500000
59 #define TEST_SCALE_NANO_2048X	31250000
60 #define TEST_SCALE_NANO_4096X	15625000
61 #define TEST_SCALE_NANO_4096X2	7812500
62 #define TEST_SCALE_NANO_4096X4	3906250
63 #define TEST_SCALE_NANO_4096X8	1953125
64 
65 #define TEST_SCALE_NANO_MAX_X TEST_SCALE_NANO_4096X8
66 
67 /*
68  * Can't have this allocated from stack because the kunit clean-up will
69  * happen only after the test function has already gone
70  */
71 static struct iio_gts gts;
72 
73 /* Keep the gain and time tables unsorted to test the sorting */
74 static const struct iio_gain_sel_pair gts_test_gains[] = {
75 	GAIN_SCALE_GAIN(1, TEST_GSEL_1),
76 	GAIN_SCALE_GAIN(4, TEST_GSEL_4),
77 	GAIN_SCALE_GAIN(16, TEST_GSEL_16),
78 	GAIN_SCALE_GAIN(32, TEST_GSEL_32),
79 	GAIN_SCALE_GAIN(64, TEST_GSEL_64),
80 	GAIN_SCALE_GAIN(256, TEST_GSEL_256),
81 	GAIN_SCALE_GAIN(512, TEST_GSEL_512),
82 	GAIN_SCALE_GAIN(1024, TEST_GSEL_1024),
83 	GAIN_SCALE_GAIN(4096, TEST_GSEL_4096),
84 	GAIN_SCALE_GAIN(2048, TEST_GSEL_2048),
85 #define HWGAIN_MAX 4096
86 };
87 
88 static const struct iio_itime_sel_mul gts_test_itimes[] = {
89 	GAIN_SCALE_ITIME_US(100 * 1000, TEST_TSEL_100, 2),
90 	GAIN_SCALE_ITIME_US(400 * 1000, TEST_TSEL_400, 8),
91 	GAIN_SCALE_ITIME_US(400 * 1000, TEST_TSEL_400, 8),
92 	GAIN_SCALE_ITIME_US(50 * 1000, TEST_TSEL_50, 1),
93 	GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4),
94 #define TIMEGAIN_MAX 8
95 };
96 #define TOTAL_GAIN_MAX	(HWGAIN_MAX * TIMEGAIN_MAX)
97 #define IIO_GTS_TEST_DEV "iio-gts-test-dev"
98 
99 static struct device *__test_init_iio_gain_scale(struct kunit *test,
100 		struct iio_gts *gts, const struct iio_gain_sel_pair *g_table,
101 		int num_g, const struct iio_itime_sel_mul *i_table, int num_i)
102 {
103 	struct device *dev;
104 	int ret;
105 
106 	dev = kunit_device_register(test, IIO_GTS_TEST_DEV);
107 
108 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
109 	if (IS_ERR_OR_NULL(dev))
110 		return NULL;
111 
112 	ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, g_table, num_g,
113 				    i_table, num_i, gts);
114 	KUNIT_EXPECT_EQ(test, 0, ret);
115 	if (ret)
116 		return NULL;
117 
118 	return dev;
119 }
120 
121 #define test_init_iio_gain_scale(test, gts)	\
122 	__test_init_iio_gain_scale(test, gts, gts_test_gains, \
123 				   ARRAY_SIZE(gts_test_gains), gts_test_itimes, \
124 				   ARRAY_SIZE(gts_test_itimes))
125 
126 static void test_init_iio_gts_invalid(struct kunit *test)
127 {
128 	struct device *dev;
129 	int ret;
130 	const struct iio_itime_sel_mul itimes_neg[] = {
131 		GAIN_SCALE_ITIME_US(-10, TEST_TSEL_400, 8),
132 		GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4),
133 	};
134 	const struct iio_gain_sel_pair gains_neg[] = {
135 		GAIN_SCALE_GAIN(1, TEST_GSEL_1),
136 		GAIN_SCALE_GAIN(2, TEST_GSEL_4),
137 		GAIN_SCALE_GAIN(-2, TEST_GSEL_16),
138 	};
139 	/* 55555 * 38656 = 2147534080 => overflows 32bit int */
140 	const struct iio_itime_sel_mul itimes_overflow[] = {
141 		GAIN_SCALE_ITIME_US(400 * 1000, TEST_TSEL_400, 55555),
142 		GAIN_SCALE_ITIME_US(200 * 1000, TEST_TSEL_200, 4),
143 	};
144 	const struct iio_gain_sel_pair gains_overflow[] = {
145 		GAIN_SCALE_GAIN(1, TEST_GSEL_1),
146 		GAIN_SCALE_GAIN(2, TEST_GSEL_4),
147 		GAIN_SCALE_GAIN(38656, TEST_GSEL_16),
148 	};
149 
150 	dev = kunit_device_register(test, IIO_GTS_TEST_DEV);
151 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
152 	if (!dev)
153 		return;
154 
155 	/* Ok gains, negative time */
156 	ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, gts_test_gains,
157 				    ARRAY_SIZE(gts_test_gains), itimes_neg,
158 				    ARRAY_SIZE(itimes_neg), &gts);
159 	KUNIT_EXPECT_EQ(test, -EINVAL, ret);
160 
161 	/* Ok times, negative gain */
162 	ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, gains_neg,
163 				    ARRAY_SIZE(gains_neg), gts_test_itimes,
164 				    ARRAY_SIZE(gts_test_itimes), &gts);
165 	KUNIT_EXPECT_EQ(test, -EINVAL, ret);
166 
167 	/* gain * time overflow int */
168 	ret = devm_iio_init_iio_gts(dev, TEST_SCALE_1X, 0, gains_overflow,
169 				    ARRAY_SIZE(gains_overflow), itimes_overflow,
170 				    ARRAY_SIZE(itimes_overflow), &gts);
171 	KUNIT_EXPECT_EQ(test, -EOVERFLOW, ret);
172 }
173 
174 static void test_iio_gts_find_gain_for_scale_using_time(struct kunit *test)
175 {
176 	struct device *dev;
177 	int ret, gain_sel;
178 
179 	dev = test_init_iio_gain_scale(test, &gts);
180 	if (!dev)
181 		return;
182 
183 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_100,
184 						TEST_SCALE_8X, 0, &gain_sel);
185 	/*
186 	 * Meas time 100 => gain by time 2x
187 	 * TEST_SCALE_8X matches total gain 8x
188 	 * => required HWGAIN 4x
189 	 */
190 	KUNIT_EXPECT_EQ(test, 0, ret);
191 	KUNIT_EXPECT_EQ(test, TEST_GSEL_4, gain_sel);
192 
193 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_200, 0,
194 						TEST_SCALE_NANO_256X, &gain_sel);
195 	/*
196 	 * Meas time 200 => gain by time 4x
197 	 * TEST_SCALE_256X matches total gain 256x
198 	 * => required HWGAIN 256/4 => 64x
199 	 */
200 	KUNIT_EXPECT_EQ(test, 0, ret);
201 	KUNIT_EXPECT_EQ(test, TEST_GSEL_64, gain_sel);
202 
203 	/* Min time, Min gain */
204 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_X_MIN,
205 						TEST_SCALE_MIN_X, 0, &gain_sel);
206 	KUNIT_EXPECT_EQ(test, 0, ret);
207 	KUNIT_EXPECT_EQ(test, TEST_GSEL_1, gain_sel);
208 
209 	/* Max time, Max gain */
210 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_X_MAX,
211 					0, TEST_SCALE_NANO_MAX_X, &gain_sel);
212 	KUNIT_EXPECT_EQ(test, 0, ret);
213 	KUNIT_EXPECT_EQ(test, TEST_GSEL_4096, gain_sel);
214 
215 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_100, 0,
216 						TEST_SCALE_NANO_256X, &gain_sel);
217 	/*
218 	 * Meas time 100 => gain by time 2x
219 	 * TEST_SCALE_256X matches total gain 256x
220 	 * => required HWGAIN 256/2 => 128x (not in gain-table - unsupported)
221 	 */
222 	KUNIT_EXPECT_NE(test, 0, ret);
223 
224 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_200, 0,
225 						TEST_SCALE_NANO_MAX_X, &gain_sel);
226 	/* We can't reach the max gain with integration time smaller than MAX */
227 	KUNIT_EXPECT_NE(test, 0, ret);
228 
229 	ret = iio_gts_find_gain_sel_for_scale_using_time(&gts, TEST_TSEL_50, 0,
230 						TEST_SCALE_NANO_MAX_X, &gain_sel);
231 	/* We can't reach the max gain with integration time smaller than MAX */
232 	KUNIT_EXPECT_NE(test, 0, ret);
233 }
234 
235 static void test_iio_gts_find_new_gain_sel_by_old_gain_time(struct kunit *test)
236 {
237 	struct device *dev;
238 	int ret, old_gain, new_gain, old_time_sel, new_time_sel;
239 
240 	dev = test_init_iio_gain_scale(test, &gts);
241 	if (!dev)
242 		return;
243 
244 	old_gain = 32;
245 	old_time_sel = TEST_TSEL_200;
246 	new_time_sel = TEST_TSEL_400;
247 
248 	ret = iio_gts_find_new_gain_sel_by_old_gain_time(&gts, old_gain,
249 					old_time_sel, new_time_sel, &new_gain);
250 	KUNIT_EXPECT_EQ(test, 0, ret);
251 	/*
252 	 * Doubling the integration time doubles the total gain - so old
253 	 * (hw)gain must be divided by two to compensate. => 32 / 2 => 16
254 	 */
255 	KUNIT_EXPECT_EQ(test, 16, new_gain);
256 
257 	old_gain = 4;
258 	old_time_sel = TEST_TSEL_50;
259 	new_time_sel = TEST_TSEL_200;
260 	ret = iio_gts_find_new_gain_sel_by_old_gain_time(&gts, old_gain,
261 					old_time_sel, new_time_sel, &new_gain);
262 	KUNIT_EXPECT_EQ(test, 0, ret);
263 	/*
264 	 * gain by time 1x => 4x - (hw)gain 4x => 1x
265 	 */
266 	KUNIT_EXPECT_EQ(test, 1, new_gain);
267 
268 	old_gain = 512;
269 	old_time_sel = TEST_TSEL_400;
270 	new_time_sel = TEST_TSEL_50;
271 	ret = iio_gts_find_new_gain_sel_by_old_gain_time(&gts, old_gain,
272 					old_time_sel, new_time_sel, &new_gain);
273 	KUNIT_EXPECT_EQ(test, 0, ret);
274 	/*
275 	 * gain by time 8x => 1x - (hw)gain 512x => 4096x)
276 	 */
277 	KUNIT_EXPECT_EQ(test, 4096, new_gain);
278 
279 	/* Unsupported gain 2x */
280 	old_gain = 4;
281 	old_time_sel = TEST_TSEL_200;
282 	new_time_sel = TEST_TSEL_400;
283 	ret = iio_gts_find_new_gain_sel_by_old_gain_time(&gts, old_gain,
284 					old_time_sel, new_time_sel, &new_gain);
285 	KUNIT_EXPECT_NE(test, 0, ret);
286 
287 	/* Too small gain */
288 	old_gain = 4;
289 	old_time_sel = TEST_TSEL_50;
290 	new_time_sel = TEST_TSEL_400;
291 	ret = iio_gts_find_new_gain_sel_by_old_gain_time(&gts, old_gain,
292 					old_time_sel, new_time_sel, &new_gain);
293 	KUNIT_EXPECT_NE(test, 0, ret);
294 
295 	/* Too big gain */
296 	old_gain = 1024;
297 	old_time_sel = TEST_TSEL_400;
298 	new_time_sel = TEST_TSEL_50;
299 	ret = iio_gts_find_new_gain_sel_by_old_gain_time(&gts, old_gain,
300 					old_time_sel, new_time_sel, &new_gain);
301 	KUNIT_EXPECT_NE(test, 0, ret);
302 
303 }
304 
305 static void test_iio_find_closest_gain_low(struct kunit *test)
306 {
307 	struct device *dev;
308 	bool in_range;
309 	int ret;
310 
311 	const struct iio_gain_sel_pair gts_test_gains_gain_low[] = {
312 		GAIN_SCALE_GAIN(4, TEST_GSEL_4),
313 		GAIN_SCALE_GAIN(16, TEST_GSEL_16),
314 		GAIN_SCALE_GAIN(32, TEST_GSEL_32),
315 	};
316 
317 	dev = test_init_iio_gain_scale(test, &gts);
318 	if (!dev)
319 		return;
320 
321 	ret = iio_find_closest_gain_low(&gts, 2, &in_range);
322 	KUNIT_EXPECT_EQ(test, 1, ret);
323 	KUNIT_EXPECT_EQ(test, true, in_range);
324 
325 	ret = iio_find_closest_gain_low(&gts, 1, &in_range);
326 	KUNIT_EXPECT_EQ(test, 1, ret);
327 	KUNIT_EXPECT_EQ(test, true, in_range);
328 
329 	ret = iio_find_closest_gain_low(&gts, 4095, &in_range);
330 	KUNIT_EXPECT_EQ(test, 2048, ret);
331 	KUNIT_EXPECT_EQ(test, true, in_range);
332 
333 	ret = iio_find_closest_gain_low(&gts, 4097, &in_range);
334 	KUNIT_EXPECT_EQ(test, 4096, ret);
335 	KUNIT_EXPECT_EQ(test, false, in_range);
336 
337 	kunit_device_unregister(test, dev);
338 
339 	dev = __test_init_iio_gain_scale(test, &gts, gts_test_gains_gain_low,
340 				ARRAY_SIZE(gts_test_gains_gain_low),
341 				gts_test_itimes, ARRAY_SIZE(gts_test_itimes));
342 	if (!dev)
343 		return;
344 
345 	ret = iio_find_closest_gain_low(&gts, 3, &in_range);
346 	KUNIT_EXPECT_EQ(test, -EINVAL, ret);
347 	KUNIT_EXPECT_EQ(test, false, in_range);
348 }
349 
350 static void test_iio_gts_total_gain_to_scale(struct kunit *test)
351 {
352 	struct device *dev;
353 	int ret, scale_int, scale_nano;
354 
355 	dev = test_init_iio_gain_scale(test, &gts);
356 	if (!dev)
357 		return;
358 
359 	ret = iio_gts_total_gain_to_scale(&gts, 1, &scale_int, &scale_nano);
360 	KUNIT_EXPECT_EQ(test, 0, ret);
361 	KUNIT_EXPECT_EQ(test, TEST_SCALE_1X, scale_int);
362 	KUNIT_EXPECT_EQ(test, 0, scale_nano);
363 
364 	ret = iio_gts_total_gain_to_scale(&gts, 1, &scale_int, &scale_nano);
365 	KUNIT_EXPECT_EQ(test, 0, ret);
366 	KUNIT_EXPECT_EQ(test, TEST_SCALE_1X, scale_int);
367 	KUNIT_EXPECT_EQ(test, 0, scale_nano);
368 
369 	ret = iio_gts_total_gain_to_scale(&gts, 4096 * 8, &scale_int,
370 					  &scale_nano);
371 	KUNIT_EXPECT_EQ(test, 0, ret);
372 	KUNIT_EXPECT_EQ(test, 0, scale_int);
373 	KUNIT_EXPECT_EQ(test, TEST_SCALE_NANO_4096X8, scale_nano);
374 }
375 
376 static void test_iio_gts_chk_times(struct kunit *test, const int *vals)
377 {
378 	static const int expected[] = {0, 50000, 0, 100000, 0, 200000, 0, 400000};
379 	int i;
380 
381 	for (i = 0; i < ARRAY_SIZE(expected); i++)
382 		KUNIT_EXPECT_EQ(test, expected[i], vals[i]);
383 }
384 
385 static void test_iio_gts_chk_scales_all(struct kunit *test, struct iio_gts *gts,
386 					const int *vals, int len)
387 {
388 	static const int gains[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512,
389 				    1024, 2048, 4096, 4096 * 2, 4096 * 4,
390 				    4096 * 8};
391 	int expected[ARRAY_SIZE(gains) * 2];
392 	int i, ret;
393 	int exp_len = ARRAY_SIZE(gains) * 2;
394 
395 	KUNIT_EXPECT_EQ(test, exp_len, len);
396 	if (len != exp_len)
397 		return;
398 
399 	for (i = 0; i < ARRAY_SIZE(gains); i++) {
400 		ret = iio_gts_total_gain_to_scale(gts, gains[i],
401 						  &expected[2 * i],
402 						  &expected[2 * i + 1]);
403 		KUNIT_EXPECT_EQ(test, 0, ret);
404 		if (ret)
405 			return;
406 	}
407 
408 	for (i = 0; i < ARRAY_SIZE(expected); i++)
409 		KUNIT_EXPECT_EQ(test, expected[i], vals[i]);
410 }
411 
412 static void test_iio_gts_chk_scales_t200(struct kunit *test, struct iio_gts *gts,
413 					 const int *vals, int len)
414 {
415 	/* The gain caused by time 200 is 4x */
416 	static const int gains[] = {
417 		1 * 4,
418 		4 * 4,
419 		16 * 4,
420 		32 * 4,
421 		64 * 4,
422 		256 * 4,
423 		512 * 4,
424 		1024 * 4,
425 		2048 * 4,
426 		4096 * 4
427 	};
428 	int expected[ARRAY_SIZE(gains) * 2];
429 	int i, ret;
430 
431 	KUNIT_EXPECT_EQ(test, 2 * ARRAY_SIZE(gains), len);
432 	if (len < 2 * ARRAY_SIZE(gains))
433 		return;
434 
435 	for (i = 0; i < ARRAY_SIZE(gains); i++) {
436 		ret = iio_gts_total_gain_to_scale(gts, gains[i],
437 						  &expected[2 * i],
438 						  &expected[2 * i + 1]);
439 		KUNIT_EXPECT_EQ(test, 0, ret);
440 		if (ret)
441 			return;
442 	}
443 
444 	for (i = 0; i < ARRAY_SIZE(expected); i++)
445 		KUNIT_EXPECT_EQ(test, expected[i], vals[i]);
446 }
447 
448 static void test_iio_gts_avail_test(struct kunit *test)
449 {
450 	struct device *dev;
451 	int ret;
452 	int type, len;
453 	const int *vals;
454 
455 	dev = test_init_iio_gain_scale(test, &gts);
456 	if (!dev)
457 		return;
458 
459 	/* test table building for times and iio_gts_avail_times() */
460 	ret = iio_gts_avail_times(&gts, &vals, &type, &len);
461 	KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret);
462 	if (ret)
463 		return;
464 
465 	KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_MICRO, type);
466 	KUNIT_EXPECT_EQ(test, 8, len);
467 	if (len < 8)
468 		return;
469 
470 	test_iio_gts_chk_times(test, vals);
471 
472 	/* Test table building for all scales and iio_gts_all_avail_scales() */
473 	ret = iio_gts_all_avail_scales(&gts, &vals, &type, &len);
474 	KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret);
475 	if (ret)
476 		return;
477 
478 	KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_NANO, type);
479 
480 	test_iio_gts_chk_scales_all(test, &gts, vals, len);
481 
482 	/*
483 	 * Test table building for scales/time and
484 	 * iio_gts_avail_scales_for_time()
485 	 */
486 	ret = iio_gts_avail_scales_for_time(&gts, 200000, &vals, &type, &len);
487 	KUNIT_EXPECT_EQ(test, IIO_AVAIL_LIST, ret);
488 	if (ret)
489 		return;
490 
491 	KUNIT_EXPECT_EQ(test, IIO_VAL_INT_PLUS_NANO, type);
492 	test_iio_gts_chk_scales_t200(test, &gts, vals, len);
493 }
494 
495 static struct kunit_case iio_gts_test_cases[] = {
496 	KUNIT_CASE(test_init_iio_gts_invalid),
497 	KUNIT_CASE(test_iio_gts_find_gain_for_scale_using_time),
498 	KUNIT_CASE(test_iio_gts_find_new_gain_sel_by_old_gain_time),
499 	KUNIT_CASE(test_iio_find_closest_gain_low),
500 	KUNIT_CASE(test_iio_gts_total_gain_to_scale),
501 	KUNIT_CASE(test_iio_gts_avail_test),
502 	{}
503 };
504 
505 static struct kunit_suite iio_gts_test_suite = {
506 	.name = "iio-gain-time-scale",
507 	.test_cases = iio_gts_test_cases,
508 };
509 
510 kunit_test_suite(iio_gts_test_suite);
511 
512 MODULE_LICENSE("GPL");
513 MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
514 MODULE_DESCRIPTION("Test IIO light sensor gain-time-scale helpers");
515 MODULE_IMPORT_NS(IIO_GTS_HELPER);
516