exec/memory: Use struct Object typedef
[qemu/ar7.git] / tests / qtest / npcm7xx_pwm-test.c
blob3d82654b81a221cba021dac24423d67bcca75ac9
1 /*
2 * QTests for Nuvoton NPCM7xx PWM Modules.
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
14 * for more details.
17 #include "qemu/osdep.h"
18 #include "qemu/bitops.h"
19 #include "libqos/libqtest.h"
20 #include "qapi/qmp/qdict.h"
21 #include "qapi/qmp/qnum.h"
23 #define REF_HZ 25000000
25 /* Register field definitions. */
26 #define CH_EN BIT(0)
27 #define CH_INV BIT(2)
28 #define CH_MOD BIT(3)
30 /* Registers shared between all PWMs in a module */
31 #define PPR 0x00
32 #define CSR 0x04
33 #define PCR 0x08
34 #define PIER 0x3c
35 #define PIIR 0x40
37 /* CLK module related */
38 #define CLK_BA 0xf0801000
39 #define CLKSEL 0x04
40 #define CLKDIV1 0x08
41 #define CLKDIV2 0x2c
42 #define PLLCON0 0x0c
43 #define PLLCON1 0x10
44 #define PLL_INDV(rv) extract32((rv), 0, 6)
45 #define PLL_FBDV(rv) extract32((rv), 16, 12)
46 #define PLL_OTDV1(rv) extract32((rv), 8, 3)
47 #define PLL_OTDV2(rv) extract32((rv), 13, 3)
48 #define APB3CKDIV(rv) extract32((rv), 28, 2)
49 #define CLK2CKDIV(rv) extract32((rv), 0, 1)
50 #define CLK4CKDIV(rv) extract32((rv), 26, 2)
51 #define CPUCKSEL(rv) extract32((rv), 0, 2)
53 #define MAX_DUTY 1000000
55 typedef struct PWMModule {
56 int irq;
57 uint64_t base_addr;
58 } PWMModule;
60 typedef struct PWM {
61 uint32_t cnr_offset;
62 uint32_t cmr_offset;
63 uint32_t pdr_offset;
64 uint32_t pwdr_offset;
65 } PWM;
67 typedef struct TestData {
68 const PWMModule *module;
69 const PWM *pwm;
70 } TestData;
72 static const PWMModule pwm_module_list[] = {
74 .irq = 93,
75 .base_addr = 0xf0103000
78 .irq = 94,
79 .base_addr = 0xf0104000
83 static const PWM pwm_list[] = {
85 .cnr_offset = 0x0c,
86 .cmr_offset = 0x10,
87 .pdr_offset = 0x14,
88 .pwdr_offset = 0x44,
91 .cnr_offset = 0x18,
92 .cmr_offset = 0x1c,
93 .pdr_offset = 0x20,
94 .pwdr_offset = 0x48,
97 .cnr_offset = 0x24,
98 .cmr_offset = 0x28,
99 .pdr_offset = 0x2c,
100 .pwdr_offset = 0x4c,
103 .cnr_offset = 0x30,
104 .cmr_offset = 0x34,
105 .pdr_offset = 0x38,
106 .pwdr_offset = 0x50,
110 static const int ppr_base[] = { 0, 0, 8, 8 };
111 static const int csr_base[] = { 0, 4, 8, 12 };
112 static const int pcr_base[] = { 0, 8, 12, 16 };
114 static const uint32_t ppr_list[] = {
118 100,
119 255, /* Max possible value. */
122 static const uint32_t csr_list[] = {
127 4, /* Max possible value. */
130 static const uint32_t cnr_list[] = {
134 100,
135 150,
136 200,
137 1000,
138 10000,
139 65535, /* Max possible value. */
142 static const uint32_t cmr_list[] = {
147 100,
148 150,
149 200,
150 1000,
151 10000,
152 65535, /* Max possible value. */
155 /* Returns the index of the PWM module. */
156 static int pwm_module_index(const PWMModule *module)
158 ptrdiff_t diff = module - pwm_module_list;
160 g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
162 return diff;
165 /* Returns the index of the PWM entry. */
166 static int pwm_index(const PWM *pwm)
168 ptrdiff_t diff = pwm - pwm_list;
170 g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
172 return diff;
175 static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name)
177 QDict *response;
178 uint64_t val;
180 g_test_message("Getting properties %s from %s", name, path);
181 response = qtest_qmp(qts, "{ 'execute': 'qom-get',"
182 " 'arguments': { 'path': %s, 'property': %s}}",
183 path, name);
184 /* The qom set message returns successfully. */
185 g_assert_true(qdict_haskey(response, "return"));
186 val = qnum_get_uint(qobject_to(QNum, qdict_get(response, "return")));
187 qobject_unref(response);
188 return val;
191 static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_index)
193 char path[100];
194 char name[100];
196 sprintf(path, "/machine/soc/pwm[%d]", module_index);
197 sprintf(name, "freq[%d]", pwm_index);
199 return pwm_qom_get(qts, path, name);
202 static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index)
204 char path[100];
205 char name[100];
207 sprintf(path, "/machine/soc/pwm[%d]", module_index);
208 sprintf(name, "duty[%d]", pwm_index);
210 return pwm_qom_get(qts, path, name);
213 static uint32_t get_pll(uint32_t con)
215 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
216 * PLL_OTDV2(con));
219 static uint64_t read_pclk(QTestState *qts)
221 uint64_t freq = REF_HZ;
222 uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
223 uint32_t pllcon;
224 uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
225 uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
227 switch (CPUCKSEL(clksel)) {
228 case 0:
229 pllcon = qtest_readl(qts, CLK_BA + PLLCON0);
230 freq = get_pll(pllcon);
231 break;
232 case 1:
233 pllcon = qtest_readl(qts, CLK_BA + PLLCON1);
234 freq = get_pll(pllcon);
235 break;
236 case 2:
237 break;
238 case 3:
239 break;
240 default:
241 g_assert_not_reached();
244 freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + APB3CKDIV(clkdiv2));
246 return freq;
249 static uint32_t pwm_selector(uint32_t csr)
251 switch (csr) {
252 case 0:
253 return 2;
254 case 1:
255 return 4;
256 case 2:
257 return 8;
258 case 3:
259 return 16;
260 case 4:
261 return 1;
262 default:
263 g_assert_not_reached();
267 static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
268 uint32_t cnr)
270 return read_pclk(qts) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
273 static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
275 uint32_t duty;
277 if (cnr == 0) {
278 /* PWM is stopped. */
279 duty = 0;
280 } else if (cmr >= cnr) {
281 duty = MAX_DUTY;
282 } else {
283 duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1);
286 if (inverted) {
287 duty = MAX_DUTY - duty;
290 return duty;
293 static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset)
295 return qtest_readl(qts, td->module->base_addr + offset);
298 static void pwm_write(QTestState *qts, const TestData *td, unsigned offset,
299 uint32_t value)
301 qtest_writel(qts, td->module->base_addr + offset, value);
304 static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td)
306 return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8);
309 static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value)
311 pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]);
314 static uint32_t pwm_read_csr(QTestState *qts, const TestData *td)
316 return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3);
319 static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value)
321 pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]);
324 static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td)
326 return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4);
329 static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value)
331 pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]);
334 static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td)
336 return pwm_read(qts, td, td->pwm->cnr_offset);
339 static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value)
341 pwm_write(qts, td, td->pwm->cnr_offset, value);
344 static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td)
346 return pwm_read(qts, td, td->pwm->cmr_offset);
349 static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value)
351 pwm_write(qts, td, td->pwm->cmr_offset, value);
354 /* Check pwm registers can be reset to default value */
355 static void test_init(gconstpointer test_data)
357 const TestData *td = test_data;
358 QTestState *qts = qtest_init("-machine quanta-gsj");
359 int module = pwm_module_index(td->module);
360 int pwm = pwm_index(td->pwm);
362 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
363 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
365 qtest_quit(qts);
368 /* One-shot mode should not change frequency and duty cycle. */
369 static void test_oneshot(gconstpointer test_data)
371 const TestData *td = test_data;
372 QTestState *qts = qtest_init("-machine quanta-gsj");
373 int module = pwm_module_index(td->module);
374 int pwm = pwm_index(td->pwm);
375 uint32_t ppr, csr, pcr;
376 int i, j;
378 pcr = CH_EN;
379 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
380 ppr = ppr_list[i];
381 pwm_write_ppr(qts, td, ppr);
383 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
384 csr = csr_list[j];
385 pwm_write_csr(qts, td, csr);
386 pwm_write_pcr(qts, td, pcr);
388 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
389 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
390 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
391 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0);
392 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0);
396 qtest_quit(qts);
399 /* In toggle mode, the PWM generates correct outputs. */
400 static void test_toggle(gconstpointer test_data)
402 const TestData *td = test_data;
403 QTestState *qts = qtest_init("-machine quanta-gsj");
404 int module = pwm_module_index(td->module);
405 int pwm = pwm_index(td->pwm);
406 uint32_t ppr, csr, pcr, cnr, cmr;
407 int i, j, k, l;
408 uint64_t expected_freq, expected_duty;
410 pcr = CH_EN | CH_MOD;
411 for (i = 0; i < ARRAY_SIZE(ppr_list); ++i) {
412 ppr = ppr_list[i];
413 pwm_write_ppr(qts, td, ppr);
415 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) {
416 csr = csr_list[j];
417 pwm_write_csr(qts, td, csr);
419 for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) {
420 cnr = cnr_list[k];
421 pwm_write_cnr(qts, td, cnr);
423 for (l = 0; l < ARRAY_SIZE(cmr_list); ++l) {
424 cmr = cmr_list[l];
425 pwm_write_cmr(qts, td, cmr);
426 expected_freq = pwm_compute_freq(qts, ppr, csr, cnr);
427 expected_duty = pwm_compute_duty(cnr, cmr, false);
429 pwm_write_pcr(qts, td, pcr);
430 g_assert_cmpuint(pwm_read_ppr(qts, td), ==, ppr);
431 g_assert_cmpuint(pwm_read_csr(qts, td), ==, csr);
432 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr);
433 g_assert_cmpuint(pwm_read_cnr(qts, td), ==, cnr);
434 g_assert_cmpuint(pwm_read_cmr(qts, td), ==, cmr);
435 g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
436 ==, expected_duty);
437 if (expected_duty != 0 && expected_duty != 100) {
438 /* Duty cycle with 0 or 100 doesn't need frequency. */
439 g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
440 ==, expected_freq);
443 /* Test inverted mode */
444 expected_duty = pwm_compute_duty(cnr, cmr, true);
445 pwm_write_pcr(qts, td, pcr | CH_INV);
446 g_assert_cmpuint(pwm_read_pcr(qts, td), ==, pcr | CH_INV);
447 g_assert_cmpuint(pwm_get_duty(qts, module, pwm),
448 ==, expected_duty);
449 if (expected_duty != 0 && expected_duty != 100) {
450 /* Duty cycle with 0 or 100 doesn't need frequency. */
451 g_assert_cmpuint(pwm_get_freq(qts, module, pwm),
452 ==, expected_freq);
460 qtest_quit(qts);
463 static void pwm_add_test(const char *name, const TestData* td,
464 GTestDataFunc fn)
466 g_autofree char *full_name = g_strdup_printf(
467 "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->module),
468 pwm_index(td->pwm), name);
469 qtest_add_data_func(full_name, td, fn);
471 #define add_test(name, td) pwm_add_test(#name, td, test_##name)
473 int main(int argc, char **argv)
475 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)];
477 g_test_init(&argc, &argv, NULL);
479 for (int i = 0; i < ARRAY_SIZE(pwm_module_list); ++i) {
480 for (int j = 0; j < ARRAY_SIZE(pwm_list); ++j) {
481 TestData *td = &test_data_list[i * ARRAY_SIZE(pwm_list) + j];
483 td->module = &pwm_module_list[i];
484 td->pwm = &pwm_list[j];
486 add_test(init, td);
487 add_test(oneshot, td);
488 add_test(toggle, td);
492 return g_test_run();