xref: /qemu/tests/qtest/npcm7xx_adc-test.c (revision 76eb88b1)
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 "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(13)
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     qobject_unref(response);
133 }
134 
135 static void adc_write_input(QTestState *qts, const ADC *adc,
136         uint32_t index, uint32_t value)
137 {
138     char name[100];
139 
140     sprintf(name, "adci[%u]", index);
141     adc_qom_set(qts, adc, name, value);
142 }
143 
144 static void adc_write_vref(QTestState *qts, const ADC *adc, uint32_t value)
145 {
146     adc_qom_set(qts, adc, "vref", value);
147 }
148 
149 static uint32_t adc_calculate_output(uint32_t input, uint32_t ref)
150 {
151     uint32_t output;
152 
153     g_assert_cmpuint(input, <=, ref);
154     output = (input * (MAX_RESULT + 1)) / ref;
155     if (output > MAX_RESULT) {
156         output = MAX_RESULT;
157     }
158 
159     return output;
160 }
161 
162 static uint32_t adc_prescaler(QTestState *qts, const ADC *adc)
163 {
164     uint32_t div = extract32(adc_read_con(qts, adc), 1, 8);
165 
166     return 2 * (div + 1);
167 }
168 
169 static int64_t adc_calculate_steps(uint32_t cycles, uint32_t prescale,
170         uint32_t clkdiv)
171 {
172     return (NANOSECONDS_PER_SECOND / (REF_HZ >> clkdiv)) * cycles * prescale;
173 }
174 
175 static void adc_wait_conv_finished(QTestState *qts, const ADC *adc,
176         uint32_t clkdiv)
177 {
178     uint32_t prescaler = adc_prescaler(qts, adc);
179 
180     /*
181      * ADC should takes roughly 20 cycles to convert one sample. So we assert it
182      * should take 10~30 cycles here.
183      */
184     qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES / 2, prescaler,
185                 clkdiv));
186     /* ADC is still converting. */
187     g_assert_true(adc_read_con(qts, adc) & CON_CONV);
188     qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES, prescaler, clkdiv));
189     /* ADC has finished conversion. */
190     g_assert_false(adc_read_con(qts, adc) & CON_CONV);
191 }
192 
193 /* Check ADC can be reset to default value. */
194 static void test_init(gconstpointer adc_p)
195 {
196     const ADC *adc = adc_p;
197 
198     QTestState *qts = qtest_init("-machine quanta-gsj");
199     adc_write_con(qts, adc, CON_REFSEL | CON_INT);
200     g_assert_cmphex(adc_read_con(qts, adc), ==, CON_REFSEL);
201     qtest_quit(qts);
202 }
203 
204 /* Check ADC can convert from an internal reference. */
205 static void test_convert_internal(gconstpointer adc_p)
206 {
207     const ADC *adc = adc_p;
208     uint32_t index, input, output, expected_output;
209     QTestState *qts = qtest_init("-machine quanta-gsj");
210     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
211 
212     for (index = 0; index < NUM_INPUTS; ++index) {
213         for (size_t i = 0; i < ARRAY_SIZE(input_list); ++i) {
214             input = input_list[i];
215             expected_output = adc_calculate_output(input, DEFAULT_IREF);
216 
217             adc_write_input(qts, adc, index, input);
218             adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT |
219                     CON_EN | CON_CONV);
220             adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
221             g_assert_cmphex(adc_read_con(qts, adc), ==, CON_MUX(index) |
222                     CON_REFSEL | CON_EN);
223             g_assert_false(qtest_get_irq(qts, adc->irq));
224             output = adc_read_data(qts, adc);
225             g_assert_cmpuint(output, ==, expected_output);
226         }
227     }
228 
229     qtest_quit(qts);
230 }
231 
232 /* Check ADC can convert from an external reference. */
233 static void test_convert_external(gconstpointer adc_p)
234 {
235     const ADC *adc = adc_p;
236     uint32_t index, input, vref, output, expected_output;
237     QTestState *qts = qtest_init("-machine quanta-gsj");
238     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
239 
240     for (index = 0; index < NUM_INPUTS; ++index) {
241         for (size_t i = 0; i < ARRAY_SIZE(input_list); ++i) {
242             for (size_t j = 0; j < ARRAY_SIZE(vref_list); ++j) {
243                 input = input_list[i];
244                 vref = vref_list[j];
245                 expected_output = adc_calculate_output(input, vref);
246 
247                 adc_write_input(qts, adc, index, input);
248                 adc_write_vref(qts, adc, vref);
249                 adc_write_con(qts, adc, CON_MUX(index) | CON_INT | CON_EN |
250                         CON_CONV);
251                 adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
252                 g_assert_cmphex(adc_read_con(qts, adc), ==,
253                         CON_MUX(index) | CON_EN);
254                 g_assert_false(qtest_get_irq(qts, adc->irq));
255                 output = adc_read_data(qts, adc);
256                 g_assert_cmpuint(output, ==, expected_output);
257             }
258         }
259     }
260 
261     qtest_quit(qts);
262 }
263 
264 /* Check ADC interrupt files if and only if CON_INT_EN is set. */
265 static void test_interrupt(gconstpointer adc_p)
266 {
267     const ADC *adc = adc_p;
268     uint32_t index, input, output, expected_output;
269     QTestState *qts = qtest_init("-machine quanta-gsj");
270 
271     index = 1;
272     input = input_list[1];
273     expected_output = adc_calculate_output(input, DEFAULT_IREF);
274 
275     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
276     adc_write_input(qts, adc, index, input);
277     g_assert_false(qtest_get_irq(qts, adc->irq));
278     adc_write_con(qts, adc, CON_MUX(index) | CON_INT_EN | CON_REFSEL | CON_INT
279             | CON_EN | CON_CONV);
280     adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
281     g_assert_cmphex(adc_read_con(qts, adc), ==, CON_MUX(index) | CON_INT_EN
282             | CON_REFSEL | CON_INT | CON_EN);
283     g_assert_true(qtest_get_irq(qts, adc->irq));
284     output = adc_read_data(qts, adc);
285     g_assert_cmpuint(output, ==, expected_output);
286 
287     qtest_quit(qts);
288 }
289 
290 /* Check ADC is reset after setting ADC_RST for 10 ADC cycles. */
291 static void test_reset(gconstpointer adc_p)
292 {
293     const ADC *adc = adc_p;
294     QTestState *qts = qtest_init("-machine quanta-gsj");
295 
296     for (size_t i = 0; i < ARRAY_SIZE(div_list); ++i) {
297         uint32_t div = div_list[i];
298 
299         adc_write_con(qts, adc, CON_INT | CON_EN | CON_RST | CON_DIV(div));
300         qtest_clock_step(qts, adc_calculate_steps(RESET_CYCLES,
301                     adc_prescaler(qts, adc), DEFAULT_CLKDIV));
302         g_assert_false(adc_read_con(qts, adc) & CON_EN);
303     }
304     qtest_quit(qts);
305 }
306 
307 /* Check ADC Calibration works as desired. */
308 static void test_calibrate(gconstpointer adc_p)
309 {
310     int i, j;
311     const ADC *adc = adc_p;
312 
313     for (j = 0; j < ARRAY_SIZE(iref_list); ++j) {
314         uint32_t iref = iref_list[j];
315         uint32_t expected_rv[] = {
316             adc_calculate_output(R0_INPUT, iref),
317             adc_calculate_output(R1_INPUT, iref),
318         };
319         char buf[100];
320         QTestState *qts;
321 
322         sprintf(buf, "-machine quanta-gsj -global npcm7xx-adc.iref=%u", iref);
323         qts = qtest_init(buf);
324 
325         /* Check the converted value is correct using the calibration value. */
326         for (i = 0; i < ARRAY_SIZE(input_list); ++i) {
327             uint32_t input;
328             uint32_t output;
329             uint32_t expected_output;
330             uint32_t calibrated_voltage;
331             uint32_t index = 0;
332 
333             input = input_list[i];
334             /* Calibration only works for input range 0.1V ~ 1.8V. */
335             if (input < MIN_CALIB_INPUT || input > MAX_CALIB_INPUT) {
336                 continue;
337             }
338             expected_output = adc_calculate_output(input, iref);
339 
340             adc_write_input(qts, adc, index, input);
341             adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT |
342                     CON_EN | CON_CONV);
343             adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
344             g_assert_cmphex(adc_read_con(qts, adc), ==,
345                     CON_REFSEL | CON_MUX(index) | CON_EN);
346             output = adc_read_data(qts, adc);
347             g_assert_cmpuint(output, ==, expected_output);
348 
349             calibrated_voltage = adc_calibrate(output, expected_rv);
350             g_assert_cmpuint(calibrated_voltage, >, input - MAX_ERROR);
351             g_assert_cmpuint(calibrated_voltage, <, input + MAX_ERROR);
352         }
353 
354         qtest_quit(qts);
355     }
356 }
357 
358 static void adc_add_test(const char *name, const ADC* wd,
359         GTestDataFunc fn)
360 {
361     g_autofree char *full_name = g_strdup_printf("npcm7xx_adc/%s",  name);
362     qtest_add_data_func(full_name, wd, fn);
363 }
364 #define add_test(name, td) adc_add_test(#name, td, test_##name)
365 
366 int main(int argc, char **argv)
367 {
368     g_test_init(&argc, &argv, NULL);
369 
370     add_test(init, &adc);
371     add_test(convert_internal, &adc);
372     add_test(convert_external, &adc);
373     add_test(interrupt, &adc);
374     add_test(reset, &adc);
375     add_test(calibrate, &adc);
376 
377     return g_test_run();
378 }
379