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