]>
git.proxmox.com Git - mirror_qemu.git/blob - tests/qtest/npcm7xx_adc-test.c
2 * QTests for Nuvoton NPCM7xx ADCModules.
4 * Copyright 2020 Google LLC
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.
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
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"
23 #define REF_HZ (25000000)
25 #define CON_OFFSET 0x0
26 #define DATA_OFFSET 0x4
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
36 #define DEFAULT_CLKDIV 5
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
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)
56 #define FST_RDST BIT(1)
57 #define FDATA_MASK 0xff
59 #define MAX_ERROR 10000
60 #define MIN_CALIB_INPUT 100000
61 #define MAX_CALIB_INPUT 1800000
63 static const uint32_t input_list
[] = {
72 static const uint32_t vref_list
[] = {
78 static const uint32_t iref_list
[] = {
86 static const uint32_t div_list
[] = {0, 1, 3, 7, 15};
95 .base_addr
= 0xf000c000
98 static uint32_t adc_read_con(QTestState
*qts
, const ADC
*adc
)
100 return qtest_readl(qts
, adc
->base_addr
+ CON_OFFSET
);
103 static void adc_write_con(QTestState
*qts
, const ADC
*adc
, uint32_t value
)
105 qtest_writel(qts
, adc
->base_addr
+ CON_OFFSET
, value
);
108 static uint32_t adc_read_data(QTestState
*qts
, const ADC
*adc
)
110 return qtest_readl(qts
, adc
->base_addr
+ DATA_OFFSET
);
113 static uint32_t adc_calibrate(uint32_t measured
, uint32_t *rv
)
115 return R0_INPUT
+ (R1_INPUT
- R0_INPUT
) * (int32_t)(measured
- rv
[0])
116 / (int32_t)(rv
[1] - rv
[0]);
119 static void adc_qom_set(QTestState
*qts
, const ADC
*adc
,
120 const char *name
, uint32_t value
)
123 const char *path
= "/machine/soc/adc";
125 g_test_message("Setting properties %s of %s with value %u",
127 response
= qtest_qmp(qts
, "{ 'execute': 'qom-set',"
128 " 'arguments': { 'path': %s, 'property': %s, 'value': %u}}",
130 /* The qom set message returns successfully. */
131 g_assert_true(qdict_haskey(response
, "return"));
134 static void adc_write_input(QTestState
*qts
, const ADC
*adc
,
135 uint32_t index
, uint32_t value
)
139 sprintf(name
, "adci[%u]", index
);
140 adc_qom_set(qts
, adc
, name
, value
);
143 static void adc_write_vref(QTestState
*qts
, const ADC
*adc
, uint32_t value
)
145 adc_qom_set(qts
, adc
, "vref", value
);
148 static uint32_t adc_calculate_output(uint32_t input
, uint32_t ref
)
152 g_assert_cmpuint(input
, <=, ref
);
153 output
= (input
* (MAX_RESULT
+ 1)) / ref
;
154 if (output
> MAX_RESULT
) {
161 static uint32_t adc_prescaler(QTestState
*qts
, const ADC
*adc
)
163 uint32_t div
= extract32(adc_read_con(qts
, adc
), 1, 8);
165 return 2 * (div
+ 1);
168 static int64_t adc_calculate_steps(uint32_t cycles
, uint32_t prescale
,
171 return (NANOSECONDS_PER_SECOND
/ (REF_HZ
>> clkdiv
)) * cycles
* prescale
;
174 static void adc_wait_conv_finished(QTestState
*qts
, const ADC
*adc
,
177 uint32_t prescaler
= adc_prescaler(qts
, adc
);
180 * ADC should takes roughly 20 cycles to convert one sample. So we assert it
181 * should take 10~30 cycles here.
183 qtest_clock_step(qts
, adc_calculate_steps(CONV_CYCLES
/ 2, prescaler
,
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
);
192 /* Check ADC can be reset to default value. */
193 static void test_init(gconstpointer adc_p
)
195 const ADC
*adc
= adc_p
;
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
);
203 /* Check ADC can convert from an internal reference. */
204 static void test_convert_internal(gconstpointer adc_p
)
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");
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
);
216 adc_write_input(qts
, adc
, index
, input
);
217 adc_write_con(qts
, adc
, CON_MUX(index
) | CON_REFSEL
| CON_INT
|
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
);
231 /* Check ADC can convert from an external reference. */
232 static void test_convert_external(gconstpointer adc_p
)
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");
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
];
244 expected_output
= adc_calculate_output(input
, vref
);
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
|
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
);
263 /* Check ADC interrupt files if and only if CON_INT_EN is set. */
264 static void test_interrupt(gconstpointer adc_p
)
266 const ADC
*adc
= adc_p
;
267 uint32_t index
, input
, output
, expected_output
;
268 QTestState
*qts
= qtest_init("-machine quanta-gsj");
271 input
= input_list
[1];
272 expected_output
= adc_calculate_output(input
, DEFAULT_IREF
);
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
);
289 /* Check ADC is reset after setting ADC_RST for 10 ADC cycles. */
290 static void test_reset(gconstpointer adc_p
)
292 const ADC
*adc
= adc_p
;
293 QTestState
*qts
= qtest_init("-machine quanta-gsj");
295 for (size_t i
= 0; i
< ARRAY_SIZE(div_list
); ++i
) {
296 uint32_t div
= div_list
[i
];
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
);
306 /* Check ADC Calibration works as desired. */
307 static void test_calibrate(gconstpointer adc_p
)
310 const ADC
*adc
= adc_p
;
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
),
321 sprintf(buf
, "-machine quanta-gsj -global npcm7xx-adc.iref=%u", iref
);
322 qts
= qtest_init(buf
);
324 /* Check the converted value is correct using the calibration value. */
325 for (i
= 0; i
< ARRAY_SIZE(input_list
); ++i
) {
328 uint32_t expected_output
;
329 uint32_t calibrated_voltage
;
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
) {
337 expected_output
= adc_calculate_output(input
, iref
);
339 adc_write_input(qts
, adc
, index
, input
);
340 adc_write_con(qts
, adc
, CON_MUX(index
) | CON_REFSEL
| CON_INT
|
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
);
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
);
357 static void adc_add_test(const char *name
, const ADC
* wd
,
360 g_autofree
char *full_name
= g_strdup_printf("npcm7xx_adc/%s", name
);
361 qtest_add_data_func(full_name
, wd
, fn
);
363 #define add_test(name, td) adc_add_test(#name, td, test_##name)
365 int main(int argc
, char **argv
)
367 g_test_init(&argc
, &argv
, NULL
);
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
);