1 /* 2 * QTests for Nuvoton NPCM7xx PWM Modules. 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 "libqos/libqtest.h" 20 #include "qapi/qmp/qdict.h" 21 #include "qapi/qmp/qnum.h" 22 23 #define REF_HZ 25000000 24 25 /* Register field definitions. */ 26 #define CH_EN BIT(0) 27 #define CH_INV BIT(2) 28 #define CH_MOD BIT(3) 29 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 36 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) 52 53 #define MAX_DUTY 1000000 54 55 typedef struct PWMModule { 56 int irq; 57 uint64_t base_addr; 58 } PWMModule; 59 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; 66 67 typedef struct TestData { 68 const PWMModule *module; 69 const PWM *pwm; 70 } TestData; 71 72 static const PWMModule pwm_module_list[] = { 73 { 74 .irq = 93, 75 .base_addr = 0xf0103000 76 }, 77 { 78 .irq = 94, 79 .base_addr = 0xf0104000 80 } 81 }; 82 83 static const PWM pwm_list[] = { 84 { 85 .cnr_offset = 0x0c, 86 .cmr_offset = 0x10, 87 .pdr_offset = 0x14, 88 .pwdr_offset = 0x44, 89 }, 90 { 91 .cnr_offset = 0x18, 92 .cmr_offset = 0x1c, 93 .pdr_offset = 0x20, 94 .pwdr_offset = 0x48, 95 }, 96 { 97 .cnr_offset = 0x24, 98 .cmr_offset = 0x28, 99 .pdr_offset = 0x2c, 100 .pwdr_offset = 0x4c, 101 }, 102 { 103 .cnr_offset = 0x30, 104 .cmr_offset = 0x34, 105 .pdr_offset = 0x38, 106 .pwdr_offset = 0x50, 107 }, 108 }; 109 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 }; 113 114 static const uint32_t ppr_list[] = { 115 0, 116 1, 117 10, 118 100, 119 255, /* Max possible value. */ 120 }; 121 122 static const uint32_t csr_list[] = { 123 0, 124 1, 125 2, 126 3, 127 4, /* Max possible value. */ 128 }; 129 130 static const uint32_t cnr_list[] = { 131 0, 132 1, 133 50, 134 100, 135 150, 136 200, 137 1000, 138 10000, 139 65535, /* Max possible value. */ 140 }; 141 142 static const uint32_t cmr_list[] = { 143 0, 144 1, 145 10, 146 50, 147 100, 148 150, 149 200, 150 1000, 151 10000, 152 65535, /* Max possible value. */ 153 }; 154 155 /* Returns the index of the PWM module. */ 156 static int pwm_module_index(const PWMModule *module) 157 { 158 ptrdiff_t diff = module - pwm_module_list; 159 160 g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list)); 161 162 return diff; 163 } 164 165 /* Returns the index of the PWM entry. */ 166 static int pwm_index(const PWM *pwm) 167 { 168 ptrdiff_t diff = pwm - pwm_list; 169 170 g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list)); 171 172 return diff; 173 } 174 175 static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char *name) 176 { 177 QDict *response; 178 uint64_t val; 179 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; 189 } 190 191 static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_index) 192 { 193 char path[100]; 194 char name[100]; 195 196 sprintf(path, "/machine/soc/pwm[%d]", module_index); 197 sprintf(name, "freq[%d]", pwm_index); 198 199 return pwm_qom_get(qts, path, name); 200 } 201 202 static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_index) 203 { 204 char path[100]; 205 char name[100]; 206 207 sprintf(path, "/machine/soc/pwm[%d]", module_index); 208 sprintf(name, "duty[%d]", pwm_index); 209 210 return pwm_qom_get(qts, path, name); 211 } 212 213 static uint32_t get_pll(uint32_t con) 214 { 215 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con) 216 * PLL_OTDV2(con)); 217 } 218 219 static uint64_t read_pclk(QTestState *qts) 220 { 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); 226 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(); 242 } 243 244 freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + APB3CKDIV(clkdiv2)); 245 246 return freq; 247 } 248 249 static uint32_t pwm_selector(uint32_t csr) 250 { 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(); 264 } 265 } 266 267 static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr, 268 uint32_t cnr) 269 { 270 return read_pclk(qts) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); 271 } 272 273 static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted) 274 { 275 uint64_t duty; 276 277 if (cnr == 0) { 278 /* PWM is stopped. */ 279 duty = 0; 280 } else if (cmr >= cnr) { 281 duty = MAX_DUTY; 282 } else { 283 duty = MAX_DUTY * (cmr + 1) / (cnr + 1); 284 } 285 286 if (inverted) { 287 duty = MAX_DUTY - duty; 288 } 289 290 return duty; 291 } 292 293 static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned offset) 294 { 295 return qtest_readl(qts, td->module->base_addr + offset); 296 } 297 298 static void pwm_write(QTestState *qts, const TestData *td, unsigned offset, 299 uint32_t value) 300 { 301 qtest_writel(qts, td->module->base_addr + offset, value); 302 } 303 304 static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td) 305 { 306 return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)], 8); 307 } 308 309 static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t value) 310 { 311 pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]); 312 } 313 314 static uint32_t pwm_read_csr(QTestState *qts, const TestData *td) 315 { 316 return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)], 3); 317 } 318 319 static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t value) 320 { 321 pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]); 322 } 323 324 static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td) 325 { 326 return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)], 4); 327 } 328 329 static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t value) 330 { 331 pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]); 332 } 333 334 static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td) 335 { 336 return pwm_read(qts, td, td->pwm->cnr_offset); 337 } 338 339 static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t value) 340 { 341 pwm_write(qts, td, td->pwm->cnr_offset, value); 342 } 343 344 static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td) 345 { 346 return pwm_read(qts, td, td->pwm->cmr_offset); 347 } 348 349 static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t value) 350 { 351 pwm_write(qts, td, td->pwm->cmr_offset, value); 352 } 353 354 /* Check pwm registers can be reset to default value */ 355 static void test_init(gconstpointer test_data) 356 { 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); 361 362 g_assert_cmpuint(pwm_get_freq(qts, module, pwm), ==, 0); 363 g_assert_cmpuint(pwm_get_duty(qts, module, pwm), ==, 0); 364 365 qtest_quit(qts); 366 } 367 368 /* One-shot mode should not change frequency and duty cycle. */ 369 static void test_oneshot(gconstpointer test_data) 370 { 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; 377 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); 382 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); 387 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); 393 } 394 } 395 396 qtest_quit(qts); 397 } 398 399 /* In toggle mode, the PWM generates correct outputs. */ 400 static void test_toggle(gconstpointer test_data) 401 { 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; 409 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); 414 415 for (j = 0; j < ARRAY_SIZE(csr_list); ++j) { 416 csr = csr_list[j]; 417 pwm_write_csr(qts, td, csr); 418 419 for (k = 0; k < ARRAY_SIZE(cnr_list); ++k) { 420 cnr = cnr_list[k]; 421 pwm_write_cnr(qts, td, cnr); 422 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); 428 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); 441 } 442 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); 453 } 454 455 } 456 } 457 } 458 } 459 460 qtest_quit(qts); 461 } 462 463 static void pwm_add_test(const char *name, const TestData* td, 464 GTestDataFunc fn) 465 { 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); 470 } 471 #define add_test(name, td) pwm_add_test(#name, td, test_##name) 472 473 int main(int argc, char **argv) 474 { 475 TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_list)]; 476 477 g_test_init(&argc, &argv, NULL); 478 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]; 482 483 td->module = &pwm_module_list[i]; 484 td->pwm = &pwm_list[j]; 485 486 add_test(init, td); 487 add_test(oneshot, td); 488 add_test(toggle, td); 489 } 490 } 491 492 return g_test_run(); 493 } 494