1 /* 2 * Nuvoton NPCM7xx PWM Module 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 "hw/irq.h" 19 #include "hw/qdev-clock.h" 20 #include "hw/qdev-properties.h" 21 #include "hw/misc/npcm7xx_pwm.h" 22 #include "hw/registerfields.h" 23 #include "migration/vmstate.h" 24 #include "qemu/bitops.h" 25 #include "qemu/error-report.h" 26 #include "qemu/log.h" 27 #include "qemu/module.h" 28 #include "qemu/units.h" 29 #include "trace.h" 30 31 REG32(NPCM7XX_PWM_PPR, 0x00); 32 REG32(NPCM7XX_PWM_CSR, 0x04); 33 REG32(NPCM7XX_PWM_PCR, 0x08); 34 REG32(NPCM7XX_PWM_CNR0, 0x0c); 35 REG32(NPCM7XX_PWM_CMR0, 0x10); 36 REG32(NPCM7XX_PWM_PDR0, 0x14); 37 REG32(NPCM7XX_PWM_CNR1, 0x18); 38 REG32(NPCM7XX_PWM_CMR1, 0x1c); 39 REG32(NPCM7XX_PWM_PDR1, 0x20); 40 REG32(NPCM7XX_PWM_CNR2, 0x24); 41 REG32(NPCM7XX_PWM_CMR2, 0x28); 42 REG32(NPCM7XX_PWM_PDR2, 0x2c); 43 REG32(NPCM7XX_PWM_CNR3, 0x30); 44 REG32(NPCM7XX_PWM_CMR3, 0x34); 45 REG32(NPCM7XX_PWM_PDR3, 0x38); 46 REG32(NPCM7XX_PWM_PIER, 0x3c); 47 REG32(NPCM7XX_PWM_PIIR, 0x40); 48 REG32(NPCM7XX_PWM_PWDR0, 0x44); 49 REG32(NPCM7XX_PWM_PWDR1, 0x48); 50 REG32(NPCM7XX_PWM_PWDR2, 0x4c); 51 REG32(NPCM7XX_PWM_PWDR3, 0x50); 52 53 /* Register field definitions. */ 54 #define NPCM7XX_PPR(rv, index) extract32((rv), npcm7xx_ppr_base[index], 8) 55 #define NPCM7XX_CSR(rv, index) extract32((rv), npcm7xx_csr_base[index], 3) 56 #define NPCM7XX_CH(rv, index) extract32((rv), npcm7xx_ch_base[index], 4) 57 #define NPCM7XX_CH_EN BIT(0) 58 #define NPCM7XX_CH_INV BIT(2) 59 #define NPCM7XX_CH_MOD BIT(3) 60 61 #define NPCM7XX_MAX_CMR 65535 62 #define NPCM7XX_MAX_CNR 65535 63 64 /* Offset of each PWM channel's prescaler in the PPR register. */ 65 static const int npcm7xx_ppr_base[] = { 0, 0, 8, 8 }; 66 /* Offset of each PWM channel's clock selector in the CSR register. */ 67 static const int npcm7xx_csr_base[] = { 0, 4, 8, 12 }; 68 /* Offset of each PWM channel's control variable in the PCR register. */ 69 static const int npcm7xx_ch_base[] = { 0, 8, 12, 16 }; 70 71 static uint32_t npcm7xx_pwm_calculate_freq(NPCM7xxPWM *p) 72 { 73 uint32_t ppr; 74 uint32_t csr; 75 uint32_t freq; 76 77 if (!p->running) { 78 return 0; 79 } 80 81 csr = NPCM7XX_CSR(p->module->csr, p->index); 82 ppr = NPCM7XX_PPR(p->module->ppr, p->index); 83 freq = clock_get_hz(p->module->clock); 84 freq /= ppr + 1; 85 /* csr can only be 0~4 */ 86 if (csr > 4) { 87 qemu_log_mask(LOG_GUEST_ERROR, 88 "%s: invalid csr value %u\n", 89 __func__, csr); 90 csr = 4; 91 } 92 /* freq won't be changed if csr == 4. */ 93 if (csr < 4) { 94 freq >>= csr + 1; 95 } 96 97 return freq / (p->cnr + 1); 98 } 99 100 static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM *p) 101 { 102 uint32_t duty; 103 104 if (p->running) { 105 if (p->cnr == 0) { 106 duty = 0; 107 } else if (p->cmr >= p->cnr) { 108 duty = NPCM7XX_PWM_MAX_DUTY; 109 } else { 110 duty = (uint64_t)NPCM7XX_PWM_MAX_DUTY * (p->cmr + 1) / (p->cnr + 1); 111 } 112 } else { 113 duty = 0; 114 } 115 116 if (p->inverted) { 117 duty = NPCM7XX_PWM_MAX_DUTY - duty; 118 } 119 120 return duty; 121 } 122 123 static void npcm7xx_pwm_update_freq(NPCM7xxPWM *p) 124 { 125 uint32_t freq = npcm7xx_pwm_calculate_freq(p); 126 127 if (freq != p->freq) { 128 trace_npcm7xx_pwm_update_freq(DEVICE(p->module)->canonical_path, 129 p->index, p->freq, freq); 130 p->freq = freq; 131 } 132 } 133 134 static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p) 135 { 136 uint32_t duty = npcm7xx_pwm_calculate_duty(p); 137 138 if (duty != p->duty) { 139 trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path, 140 p->index, p->duty, duty); 141 p->duty = duty; 142 } 143 } 144 145 static void npcm7xx_pwm_update_output(NPCM7xxPWM *p) 146 { 147 npcm7xx_pwm_update_freq(p); 148 npcm7xx_pwm_update_duty(p); 149 } 150 151 static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState *s, uint32_t new_ppr) 152 { 153 int i; 154 uint32_t old_ppr = s->ppr; 155 156 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ppr_base) != NPCM7XX_PWM_PER_MODULE); 157 s->ppr = new_ppr; 158 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 159 if (NPCM7XX_PPR(old_ppr, i) != NPCM7XX_PPR(new_ppr, i)) { 160 npcm7xx_pwm_update_freq(&s->pwm[i]); 161 } 162 } 163 } 164 165 static void npcm7xx_pwm_write_csr(NPCM7xxPWMState *s, uint32_t new_csr) 166 { 167 int i; 168 uint32_t old_csr = s->csr; 169 170 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_csr_base) != NPCM7XX_PWM_PER_MODULE); 171 s->csr = new_csr; 172 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 173 if (NPCM7XX_CSR(old_csr, i) != NPCM7XX_CSR(new_csr, i)) { 174 npcm7xx_pwm_update_freq(&s->pwm[i]); 175 } 176 } 177 } 178 179 static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState *s, uint32_t new_pcr) 180 { 181 int i; 182 bool inverted; 183 uint32_t pcr; 184 NPCM7xxPWM *p; 185 186 s->pcr = new_pcr; 187 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ch_base) != NPCM7XX_PWM_PER_MODULE); 188 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 189 p = &s->pwm[i]; 190 pcr = NPCM7XX_CH(new_pcr, i); 191 inverted = pcr & NPCM7XX_CH_INV; 192 193 /* 194 * We only run a PWM channel with toggle mode. Single-shot mode does not 195 * generate frequency and duty-cycle values. 196 */ 197 if ((pcr & NPCM7XX_CH_EN) && (pcr & NPCM7XX_CH_MOD)) { 198 if (p->running) { 199 /* Re-run this PWM channel if inverted changed. */ 200 if (p->inverted ^ inverted) { 201 p->inverted = inverted; 202 npcm7xx_pwm_update_duty(p); 203 } 204 } else { 205 /* Run this PWM channel. */ 206 p->running = true; 207 p->inverted = inverted; 208 npcm7xx_pwm_update_output(p); 209 } 210 } else { 211 /* Clear this PWM channel. */ 212 p->running = false; 213 p->inverted = inverted; 214 npcm7xx_pwm_update_output(p); 215 } 216 } 217 218 } 219 220 static hwaddr npcm7xx_cnr_index(hwaddr offset) 221 { 222 switch (offset) { 223 case A_NPCM7XX_PWM_CNR0: 224 return 0; 225 case A_NPCM7XX_PWM_CNR1: 226 return 1; 227 case A_NPCM7XX_PWM_CNR2: 228 return 2; 229 case A_NPCM7XX_PWM_CNR3: 230 return 3; 231 default: 232 g_assert_not_reached(); 233 } 234 } 235 236 static hwaddr npcm7xx_cmr_index(hwaddr offset) 237 { 238 switch (offset) { 239 case A_NPCM7XX_PWM_CMR0: 240 return 0; 241 case A_NPCM7XX_PWM_CMR1: 242 return 1; 243 case A_NPCM7XX_PWM_CMR2: 244 return 2; 245 case A_NPCM7XX_PWM_CMR3: 246 return 3; 247 default: 248 g_assert_not_reached(); 249 } 250 } 251 252 static hwaddr npcm7xx_pdr_index(hwaddr offset) 253 { 254 switch (offset) { 255 case A_NPCM7XX_PWM_PDR0: 256 return 0; 257 case A_NPCM7XX_PWM_PDR1: 258 return 1; 259 case A_NPCM7XX_PWM_PDR2: 260 return 2; 261 case A_NPCM7XX_PWM_PDR3: 262 return 3; 263 default: 264 g_assert_not_reached(); 265 } 266 } 267 268 static hwaddr npcm7xx_pwdr_index(hwaddr offset) 269 { 270 switch (offset) { 271 case A_NPCM7XX_PWM_PWDR0: 272 return 0; 273 case A_NPCM7XX_PWM_PWDR1: 274 return 1; 275 case A_NPCM7XX_PWM_PWDR2: 276 return 2; 277 case A_NPCM7XX_PWM_PWDR3: 278 return 3; 279 default: 280 g_assert_not_reached(); 281 } 282 } 283 284 static uint64_t npcm7xx_pwm_read(void *opaque, hwaddr offset, unsigned size) 285 { 286 NPCM7xxPWMState *s = opaque; 287 uint64_t value = 0; 288 289 switch (offset) { 290 case A_NPCM7XX_PWM_CNR0: 291 case A_NPCM7XX_PWM_CNR1: 292 case A_NPCM7XX_PWM_CNR2: 293 case A_NPCM7XX_PWM_CNR3: 294 value = s->pwm[npcm7xx_cnr_index(offset)].cnr; 295 break; 296 297 case A_NPCM7XX_PWM_CMR0: 298 case A_NPCM7XX_PWM_CMR1: 299 case A_NPCM7XX_PWM_CMR2: 300 case A_NPCM7XX_PWM_CMR3: 301 value = s->pwm[npcm7xx_cmr_index(offset)].cmr; 302 break; 303 304 case A_NPCM7XX_PWM_PDR0: 305 case A_NPCM7XX_PWM_PDR1: 306 case A_NPCM7XX_PWM_PDR2: 307 case A_NPCM7XX_PWM_PDR3: 308 value = s->pwm[npcm7xx_pdr_index(offset)].pdr; 309 break; 310 311 case A_NPCM7XX_PWM_PWDR0: 312 case A_NPCM7XX_PWM_PWDR1: 313 case A_NPCM7XX_PWM_PWDR2: 314 case A_NPCM7XX_PWM_PWDR3: 315 value = s->pwm[npcm7xx_pwdr_index(offset)].pwdr; 316 break; 317 318 case A_NPCM7XX_PWM_PPR: 319 value = s->ppr; 320 break; 321 322 case A_NPCM7XX_PWM_CSR: 323 value = s->csr; 324 break; 325 326 case A_NPCM7XX_PWM_PCR: 327 value = s->pcr; 328 break; 329 330 case A_NPCM7XX_PWM_PIER: 331 value = s->pier; 332 break; 333 334 case A_NPCM7XX_PWM_PIIR: 335 value = s->piir; 336 break; 337 338 default: 339 qemu_log_mask(LOG_GUEST_ERROR, 340 "%s: invalid offset 0x%04" HWADDR_PRIx "\n", 341 __func__, offset); 342 break; 343 } 344 345 trace_npcm7xx_pwm_read(DEVICE(s)->canonical_path, offset, value); 346 return value; 347 } 348 349 static void npcm7xx_pwm_write(void *opaque, hwaddr offset, 350 uint64_t v, unsigned size) 351 { 352 NPCM7xxPWMState *s = opaque; 353 NPCM7xxPWM *p; 354 uint32_t value = v; 355 356 trace_npcm7xx_pwm_write(DEVICE(s)->canonical_path, offset, value); 357 switch (offset) { 358 case A_NPCM7XX_PWM_CNR0: 359 case A_NPCM7XX_PWM_CNR1: 360 case A_NPCM7XX_PWM_CNR2: 361 case A_NPCM7XX_PWM_CNR3: 362 p = &s->pwm[npcm7xx_cnr_index(offset)]; 363 if (value > NPCM7XX_MAX_CNR) { 364 qemu_log_mask(LOG_GUEST_ERROR, 365 "%s: invalid cnr value: %u", __func__, value); 366 p->cnr = NPCM7XX_MAX_CNR; 367 } else { 368 p->cnr = value; 369 } 370 npcm7xx_pwm_update_output(p); 371 break; 372 373 case A_NPCM7XX_PWM_CMR0: 374 case A_NPCM7XX_PWM_CMR1: 375 case A_NPCM7XX_PWM_CMR2: 376 case A_NPCM7XX_PWM_CMR3: 377 p = &s->pwm[npcm7xx_cmr_index(offset)]; 378 if (value > NPCM7XX_MAX_CMR) { 379 qemu_log_mask(LOG_GUEST_ERROR, 380 "%s: invalid cmr value: %u", __func__, value); 381 p->cmr = NPCM7XX_MAX_CMR; 382 } else { 383 p->cmr = value; 384 } 385 npcm7xx_pwm_update_output(p); 386 break; 387 388 case A_NPCM7XX_PWM_PDR0: 389 case A_NPCM7XX_PWM_PDR1: 390 case A_NPCM7XX_PWM_PDR2: 391 case A_NPCM7XX_PWM_PDR3: 392 qemu_log_mask(LOG_GUEST_ERROR, 393 "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", 394 __func__, offset); 395 break; 396 397 case A_NPCM7XX_PWM_PWDR0: 398 case A_NPCM7XX_PWM_PWDR1: 399 case A_NPCM7XX_PWM_PWDR2: 400 case A_NPCM7XX_PWM_PWDR3: 401 qemu_log_mask(LOG_UNIMP, 402 "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n", 403 __func__, offset); 404 break; 405 406 case A_NPCM7XX_PWM_PPR: 407 npcm7xx_pwm_write_ppr(s, value); 408 break; 409 410 case A_NPCM7XX_PWM_CSR: 411 npcm7xx_pwm_write_csr(s, value); 412 break; 413 414 case A_NPCM7XX_PWM_PCR: 415 npcm7xx_pwm_write_pcr(s, value); 416 break; 417 418 case A_NPCM7XX_PWM_PIER: 419 qemu_log_mask(LOG_UNIMP, 420 "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n", 421 __func__, offset); 422 break; 423 424 case A_NPCM7XX_PWM_PIIR: 425 qemu_log_mask(LOG_UNIMP, 426 "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n", 427 __func__, offset); 428 break; 429 430 default: 431 qemu_log_mask(LOG_GUEST_ERROR, 432 "%s: invalid offset 0x%04" HWADDR_PRIx "\n", 433 __func__, offset); 434 break; 435 } 436 } 437 438 static const struct MemoryRegionOps npcm7xx_pwm_ops = { 439 .read = npcm7xx_pwm_read, 440 .write = npcm7xx_pwm_write, 441 .endianness = DEVICE_LITTLE_ENDIAN, 442 .valid = { 443 .min_access_size = 4, 444 .max_access_size = 4, 445 .unaligned = false, 446 }, 447 }; 448 449 static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type) 450 { 451 NPCM7xxPWMState *s = NPCM7XX_PWM(obj); 452 int i; 453 454 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) { 455 NPCM7xxPWM *p = &s->pwm[i]; 456 457 p->cnr = 0x00000000; 458 p->cmr = 0x00000000; 459 p->pdr = 0x00000000; 460 p->pwdr = 0x00000000; 461 } 462 463 s->ppr = 0x00000000; 464 s->csr = 0x00000000; 465 s->pcr = 0x00000000; 466 s->pier = 0x00000000; 467 s->piir = 0x00000000; 468 } 469 470 static void npcm7xx_pwm_hold_reset(Object *obj) 471 { 472 NPCM7xxPWMState *s = NPCM7XX_PWM(obj); 473 int i; 474 475 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) { 476 qemu_irq_lower(s->pwm[i].irq); 477 } 478 } 479 480 static void npcm7xx_pwm_init(Object *obj) 481 { 482 NPCM7xxPWMState *s = NPCM7XX_PWM(obj); 483 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 484 int i; 485 486 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) { 487 NPCM7xxPWM *p = &s->pwm[i]; 488 p->module = s; 489 p->index = i; 490 sysbus_init_irq(sbd, &p->irq); 491 } 492 493 memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s, 494 TYPE_NPCM7XX_PWM, 4 * KiB); 495 sysbus_init_mmio(sbd, &s->iomem); 496 s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL); 497 498 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { 499 object_property_add_uint32_ptr(obj, "freq[*]", 500 &s->pwm[i].freq, OBJ_PROP_FLAG_READ); 501 object_property_add_uint32_ptr(obj, "duty[*]", 502 &s->pwm[i].duty, OBJ_PROP_FLAG_READ); 503 } 504 } 505 506 static const VMStateDescription vmstate_npcm7xx_pwm = { 507 .name = "npcm7xx-pwm", 508 .version_id = 0, 509 .minimum_version_id = 0, 510 .fields = (VMStateField[]) { 511 VMSTATE_BOOL(running, NPCM7xxPWM), 512 VMSTATE_BOOL(inverted, NPCM7xxPWM), 513 VMSTATE_UINT8(index, NPCM7xxPWM), 514 VMSTATE_UINT32(cnr, NPCM7xxPWM), 515 VMSTATE_UINT32(cmr, NPCM7xxPWM), 516 VMSTATE_UINT32(pdr, NPCM7xxPWM), 517 VMSTATE_UINT32(pwdr, NPCM7xxPWM), 518 VMSTATE_UINT32(freq, NPCM7xxPWM), 519 VMSTATE_UINT32(duty, NPCM7xxPWM), 520 VMSTATE_END_OF_LIST(), 521 }, 522 }; 523 524 static const VMStateDescription vmstate_npcm7xx_pwm_module = { 525 .name = "npcm7xx-pwm-module", 526 .version_id = 0, 527 .minimum_version_id = 0, 528 .fields = (VMStateField[]) { 529 VMSTATE_CLOCK(clock, NPCM7xxPWMState), 530 VMSTATE_STRUCT_ARRAY(pwm, NPCM7xxPWMState, 531 NPCM7XX_PWM_PER_MODULE, 0, vmstate_npcm7xx_pwm, 532 NPCM7xxPWM), 533 VMSTATE_UINT32(ppr, NPCM7xxPWMState), 534 VMSTATE_UINT32(csr, NPCM7xxPWMState), 535 VMSTATE_UINT32(pcr, NPCM7xxPWMState), 536 VMSTATE_UINT32(pier, NPCM7xxPWMState), 537 VMSTATE_UINT32(piir, NPCM7xxPWMState), 538 VMSTATE_END_OF_LIST(), 539 }, 540 }; 541 542 static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data) 543 { 544 ResettableClass *rc = RESETTABLE_CLASS(klass); 545 DeviceClass *dc = DEVICE_CLASS(klass); 546 547 dc->desc = "NPCM7xx PWM Controller"; 548 dc->vmsd = &vmstate_npcm7xx_pwm_module; 549 rc->phases.enter = npcm7xx_pwm_enter_reset; 550 rc->phases.hold = npcm7xx_pwm_hold_reset; 551 } 552 553 static const TypeInfo npcm7xx_pwm_info = { 554 .name = TYPE_NPCM7XX_PWM, 555 .parent = TYPE_SYS_BUS_DEVICE, 556 .instance_size = sizeof(NPCM7xxPWMState), 557 .class_init = npcm7xx_pwm_class_init, 558 .instance_init = npcm7xx_pwm_init, 559 }; 560 561 static void npcm7xx_pwm_register_type(void) 562 { 563 type_register_static(&npcm7xx_pwm_info); 564 } 565 type_init(npcm7xx_pwm_register_type); 566