]> git.proxmox.com Git - mirror_qemu.git/blob - tests/qtest/npcm7xx_adc-test.c
Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging
[mirror_qemu.git] / tests / qtest / npcm7xx_adc-test.c
1 /*
2 * QTests for Nuvoton NPCM7xx ADCModules.
3 *
4 * Copyright 2020 Google LLC
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "qemu/osdep.h"
18 #include "qemu/bitops.h"
19 #include "qemu/timer.h"
20 #include "libqos/libqtest.h"
21 #include "qapi/qmp/qdict.h"
22
23 #define REF_HZ (25000000)
24
25 #define CON_OFFSET 0x0
26 #define DATA_OFFSET 0x4
27
28 #define NUM_INPUTS 8
29 #define DEFAULT_IREF 2000000
30 #define CONV_CYCLES 20
31 #define RESET_CYCLES 10
32 #define R0_INPUT 500000
33 #define R1_INPUT 1500000
34 #define MAX_RESULT 1023
35
36 #define DEFAULT_CLKDIV 5
37
38 #define FUSE_ARRAY_BA 0xf018a000
39 #define FCTL_OFFSET 0x14
40 #define FST_OFFSET 0x0
41 #define FADDR_OFFSET 0x4
42 #define FDATA_OFFSET 0x8
43 #define ADC_CALIB_ADDR 24
44 #define FUSE_READ 0x2
45
46 /* Register field definitions. */
47 #define CON_MUX(rv) ((rv) << 24)
48 #define CON_INT_EN BIT(21)
49 #define CON_REFSEL BIT(19)
50 #define CON_INT BIT(18)
51 #define CON_EN BIT(17)
52 #define CON_RST BIT(16)
53 #define CON_CONV BIT(14)
54 #define CON_DIV(rv) extract32(rv, 1, 8)
55
56 #define FST_RDST BIT(1)
57 #define FDATA_MASK 0xff
58
59 #define MAX_ERROR 10000
60 #define MIN_CALIB_INPUT 100000
61 #define MAX_CALIB_INPUT 1800000
62
63 static const uint32_t input_list[] = {
64 100000,
65 500000,
66 1000000,
67 1500000,
68 1800000,
69 2000000,
70 };
71
72 static const uint32_t vref_list[] = {
73 2000000,
74 2200000,
75 2500000,
76 };
77
78 static const uint32_t iref_list[] = {
79 1800000,
80 1900000,
81 2000000,
82 2100000,
83 2200000,
84 };
85
86 static const uint32_t div_list[] = {0, 1, 3, 7, 15};
87
88 typedef struct ADC {
89 int irq;
90 uint64_t base_addr;
91 } ADC;
92
93 ADC adc = {
94 .irq = 0,
95 .base_addr = 0xf000c000
96 };
97
98 static uint32_t adc_read_con(QTestState *qts, const ADC *adc)
99 {
100 return qtest_readl(qts, adc->base_addr + CON_OFFSET);
101 }
102
103 static void adc_write_con(QTestState *qts, const ADC *adc, uint32_t value)
104 {
105 qtest_writel(qts, adc->base_addr + CON_OFFSET, value);
106 }
107
108 static uint32_t adc_read_data(QTestState *qts, const ADC *adc)
109 {
110 return qtest_readl(qts, adc->base_addr + DATA_OFFSET);
111 }
112
113 static uint32_t adc_calibrate(uint32_t measured, uint32_t *rv)
114 {
115 return R0_INPUT + (R1_INPUT - R0_INPUT) * (int32_t)(measured - rv[0])
116 / (int32_t)(rv[1] - rv[0]);
117 }
118
119 static void adc_qom_set(QTestState *qts, const ADC *adc,
120 const char *name, uint32_t value)
121 {
122 QDict *response;
123 const char *path = "/machine/soc/adc";
124
125 g_test_message("Setting properties %s of %s with value %u",
126 name, path, value);
127 response = qtest_qmp(qts, "{ 'execute': 'qom-set',"
128 " 'arguments': { 'path': %s, 'property': %s, 'value': %u}}",
129 path, name, value);
130 /* The qom set message returns successfully. */
131 g_assert_true(qdict_haskey(response, "return"));
132 }
133
134 static void adc_write_input(QTestState *qts, const ADC *adc,
135 uint32_t index, uint32_t value)
136 {
137 char name[100];
138
139 sprintf(name, "adci[%u]", index);
140 adc_qom_set(qts, adc, name, value);
141 }
142
143 static void adc_write_vref(QTestState *qts, const ADC *adc, uint32_t value)
144 {
145 adc_qom_set(qts, adc, "vref", value);
146 }
147
148 static uint32_t adc_calculate_output(uint32_t input, uint32_t ref)
149 {
150 uint32_t output;
151
152 g_assert_cmpuint(input, <=, ref);
153 output = (input * (MAX_RESULT + 1)) / ref;
154 if (output > MAX_RESULT) {
155 output = MAX_RESULT;
156 }
157
158 return output;
159 }
160
161 static uint32_t adc_prescaler(QTestState *qts, const ADC *adc)
162 {
163 uint32_t div = extract32(adc_read_con(qts, adc), 1, 8);
164
165 return 2 * (div + 1);
166 }
167
168 static int64_t adc_calculate_steps(uint32_t cycles, uint32_t prescale,
169 uint32_t clkdiv)
170 {
171 return (NANOSECONDS_PER_SECOND / (REF_HZ >> clkdiv)) * cycles * prescale;
172 }
173
174 static void adc_wait_conv_finished(QTestState *qts, const ADC *adc,
175 uint32_t clkdiv)
176 {
177 uint32_t prescaler = adc_prescaler(qts, adc);
178
179 /*
180 * ADC should takes roughly 20 cycles to convert one sample. So we assert it
181 * should take 10~30 cycles here.
182 */
183 qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES / 2, prescaler,
184 clkdiv));
185 /* ADC is still converting. */
186 g_assert_true(adc_read_con(qts, adc) & CON_CONV);
187 qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES, prescaler, clkdiv));
188 /* ADC has finished conversion. */
189 g_assert_false(adc_read_con(qts, adc) & CON_CONV);
190 }
191
192 /* Check ADC can be reset to default value. */
193 static void test_init(gconstpointer adc_p)
194 {
195 const ADC *adc = adc_p;
196
197 QTestState *qts = qtest_init("-machine quanta-gsj");
198 adc_write_con(qts, adc, CON_REFSEL | CON_INT);
199 g_assert_cmphex(adc_read_con(qts, adc), ==, CON_REFSEL);
200 qtest_quit(qts);
201 }
202
203 /* Check ADC can convert from an internal reference. */
204 static void test_convert_internal(gconstpointer adc_p)
205 {
206 const ADC *adc = adc_p;
207 uint32_t index, input, output, expected_output;
208 QTestState *qts = qtest_init("-machine quanta-gsj");
209 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
210
211 for (index = 0; index < NUM_INPUTS; ++index) {
212 for (size_t i = 0; i < ARRAY_SIZE(input_list); ++i) {
213 input = input_list[i];
214 expected_output = adc_calculate_output(input, DEFAULT_IREF);
215
216 adc_write_input(qts, adc, index, input);
217 adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT |
218 CON_EN | CON_CONV);
219 adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
220 g_assert_cmphex(adc_read_con(qts, adc), ==, CON_MUX(index) |
221 CON_REFSEL | CON_EN);
222 g_assert_false(qtest_get_irq(qts, adc->irq));
223 output = adc_read_data(qts, adc);
224 g_assert_cmpuint(output, ==, expected_output);
225 }
226 }
227
228 qtest_quit(qts);
229 }
230
231 /* Check ADC can convert from an external reference. */
232 static void test_convert_external(gconstpointer adc_p)
233 {
234 const ADC *adc = adc_p;
235 uint32_t index, input, vref, output, expected_output;
236 QTestState *qts = qtest_init("-machine quanta-gsj");
237 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
238
239 for (index = 0; index < NUM_INPUTS; ++index) {
240 for (size_t i = 0; i < ARRAY_SIZE(input_list); ++i) {
241 for (size_t j = 0; j < ARRAY_SIZE(vref_list); ++j) {
242 input = input_list[i];
243 vref = vref_list[j];
244 expected_output = adc_calculate_output(input, vref);
245
246 adc_write_input(qts, adc, index, input);
247 adc_write_vref(qts, adc, vref);
248 adc_write_con(qts, adc, CON_MUX(index) | CON_INT | CON_EN |
249 CON_CONV);
250 adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
251 g_assert_cmphex(adc_read_con(qts, adc), ==,
252 CON_MUX(index) | CON_EN);
253 g_assert_false(qtest_get_irq(qts, adc->irq));
254 output = adc_read_data(qts, adc);
255 g_assert_cmpuint(output, ==, expected_output);
256 }
257 }
258 }
259
260 qtest_quit(qts);
261 }
262
263 /* Check ADC interrupt files if and only if CON_INT_EN is set. */
264 static void test_interrupt(gconstpointer adc_p)
265 {
266 const ADC *adc = adc_p;
267 uint32_t index, input, output, expected_output;
268 QTestState *qts = qtest_init("-machine quanta-gsj");
269
270 index = 1;
271 input = input_list[1];
272 expected_output = adc_calculate_output(input, DEFAULT_IREF);
273
274 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
275 adc_write_input(qts, adc, index, input);
276 g_assert_false(qtest_get_irq(qts, adc->irq));
277 adc_write_con(qts, adc, CON_MUX(index) | CON_INT_EN | CON_REFSEL | CON_INT
278 | CON_EN | CON_CONV);
279 adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
280 g_assert_cmphex(adc_read_con(qts, adc), ==, CON_MUX(index) | CON_INT_EN
281 | CON_REFSEL | CON_INT | CON_EN);
282 g_assert_true(qtest_get_irq(qts, adc->irq));
283 output = adc_read_data(qts, adc);
284 g_assert_cmpuint(output, ==, expected_output);
285
286 qtest_quit(qts);
287 }
288
289 /* Check ADC is reset after setting ADC_RST for 10 ADC cycles. */
290 static void test_reset(gconstpointer adc_p)
291 {
292 const ADC *adc = adc_p;
293 QTestState *qts = qtest_init("-machine quanta-gsj");
294
295 for (size_t i = 0; i < ARRAY_SIZE(div_list); ++i) {
296 uint32_t div = div_list[i];
297
298 adc_write_con(qts, adc, CON_INT | CON_EN | CON_RST | CON_DIV(div));
299 qtest_clock_step(qts, adc_calculate_steps(RESET_CYCLES,
300 adc_prescaler(qts, adc), DEFAULT_CLKDIV));
301 g_assert_false(adc_read_con(qts, adc) & CON_EN);
302 }
303 qtest_quit(qts);
304 }
305
306 /* Check ADC Calibration works as desired. */
307 static void test_calibrate(gconstpointer adc_p)
308 {
309 int i, j;
310 const ADC *adc = adc_p;
311
312 for (j = 0; j < ARRAY_SIZE(iref_list); ++j) {
313 uint32_t iref = iref_list[j];
314 uint32_t expected_rv[] = {
315 adc_calculate_output(R0_INPUT, iref),
316 adc_calculate_output(R1_INPUT, iref),
317 };
318 char buf[100];
319 QTestState *qts;
320
321 sprintf(buf, "-machine quanta-gsj -global npcm7xx-adc.iref=%u", iref);
322 qts = qtest_init(buf);
323
324 /* Check the converted value is correct using the calibration value. */
325 for (i = 0; i < ARRAY_SIZE(input_list); ++i) {
326 uint32_t input;
327 uint32_t output;
328 uint32_t expected_output;
329 uint32_t calibrated_voltage;
330 uint32_t index = 0;
331
332 input = input_list[i];
333 /* Calibration only works for input range 0.1V ~ 1.8V. */
334 if (input < MIN_CALIB_INPUT || input > MAX_CALIB_INPUT) {
335 continue;
336 }
337 expected_output = adc_calculate_output(input, iref);
338
339 adc_write_input(qts, adc, index, input);
340 adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT |
341 CON_EN | CON_CONV);
342 adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
343 g_assert_cmphex(adc_read_con(qts, adc), ==,
344 CON_REFSEL | CON_MUX(index) | CON_EN);
345 output = adc_read_data(qts, adc);
346 g_assert_cmpuint(output, ==, expected_output);
347
348 calibrated_voltage = adc_calibrate(output, expected_rv);
349 g_assert_cmpuint(calibrated_voltage, >, input - MAX_ERROR);
350 g_assert_cmpuint(calibrated_voltage, <, input + MAX_ERROR);
351 }
352
353 qtest_quit(qts);
354 }
355 }
356
357 static void adc_add_test(const char *name, const ADC* wd,
358 GTestDataFunc fn)
359 {
360 g_autofree char *full_name = g_strdup_printf("npcm7xx_adc/%s", name);
361 qtest_add_data_func(full_name, wd, fn);
362 }
363 #define add_test(name, td) adc_add_test(#name, td, test_##name)
364
365 int main(int argc, char **argv)
366 {
367 g_test_init(&argc, &argv, NULL);
368
369 add_test(init, &adc);
370 add_test(convert_internal, &adc);
371 add_test(convert_external, &adc);
372 add_test(interrupt, &adc);
373 add_test(reset, &adc);
374 add_test(calibrate, &adc);
375
376 return g_test_run();
377 }