qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

npcm7xx_adc-test.c (11459B)


      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 }