Merge remote-tracking branch 'remotes/dgilbert-gitlab/tags/pull-migration-20210726a...
[qemu/armbru.git] / hw / misc / npcm7xx_pwm.c
blob2be5bd25c6c1750bdf16543800020d8105d2ef1d
1 /*
2 * Nuvoton NPCM7xx PWM Module
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 "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"
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);
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)
61 #define NPCM7XX_MAX_CMR 65535
62 #define NPCM7XX_MAX_CNR 65535
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 };
71 static uint32_t npcm7xx_pwm_calculate_freq(NPCM7xxPWM *p)
73 uint32_t ppr;
74 uint32_t csr;
75 uint32_t freq;
77 if (!p->running) {
78 return 0;
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;
92 /* freq won't be changed if csr == 4. */
93 if (csr < 4) {
94 freq >>= csr + 1;
97 return freq / (p->cnr + 1);
100 static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM *p)
102 uint32_t duty;
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);
112 } else {
113 duty = 0;
116 if (p->inverted) {
117 duty = NPCM7XX_PWM_MAX_DUTY - duty;
120 return duty;
123 static void npcm7xx_pwm_update_freq(NPCM7xxPWM *p)
125 uint32_t freq = npcm7xx_pwm_calculate_freq(p);
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;
134 static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p)
136 uint32_t duty = npcm7xx_pwm_calculate_duty(p);
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 qemu_set_irq(p->module->duty_gpio_out[p->index], p->duty);
146 static void npcm7xx_pwm_update_output(NPCM7xxPWM *p)
148 npcm7xx_pwm_update_freq(p);
149 npcm7xx_pwm_update_duty(p);
152 static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState *s, uint32_t new_ppr)
154 int i;
155 uint32_t old_ppr = s->ppr;
157 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ppr_base) != NPCM7XX_PWM_PER_MODULE);
158 s->ppr = new_ppr;
159 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
160 if (NPCM7XX_PPR(old_ppr, i) != NPCM7XX_PPR(new_ppr, i)) {
161 npcm7xx_pwm_update_freq(&s->pwm[i]);
166 static void npcm7xx_pwm_write_csr(NPCM7xxPWMState *s, uint32_t new_csr)
168 int i;
169 uint32_t old_csr = s->csr;
171 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_csr_base) != NPCM7XX_PWM_PER_MODULE);
172 s->csr = new_csr;
173 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
174 if (NPCM7XX_CSR(old_csr, i) != NPCM7XX_CSR(new_csr, i)) {
175 npcm7xx_pwm_update_freq(&s->pwm[i]);
180 static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState *s, uint32_t new_pcr)
182 int i;
183 bool inverted;
184 uint32_t pcr;
185 NPCM7xxPWM *p;
187 s->pcr = new_pcr;
188 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ch_base) != NPCM7XX_PWM_PER_MODULE);
189 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
190 p = &s->pwm[i];
191 pcr = NPCM7XX_CH(new_pcr, i);
192 inverted = pcr & NPCM7XX_CH_INV;
195 * We only run a PWM channel with toggle mode. Single-shot mode does not
196 * generate frequency and duty-cycle values.
198 if ((pcr & NPCM7XX_CH_EN) && (pcr & NPCM7XX_CH_MOD)) {
199 if (p->running) {
200 /* Re-run this PWM channel if inverted changed. */
201 if (p->inverted ^ inverted) {
202 p->inverted = inverted;
203 npcm7xx_pwm_update_duty(p);
205 } else {
206 /* Run this PWM channel. */
207 p->running = true;
208 p->inverted = inverted;
209 npcm7xx_pwm_update_output(p);
211 } else {
212 /* Clear this PWM channel. */
213 p->running = false;
214 p->inverted = inverted;
215 npcm7xx_pwm_update_output(p);
221 static hwaddr npcm7xx_cnr_index(hwaddr offset)
223 switch (offset) {
224 case A_NPCM7XX_PWM_CNR0:
225 return 0;
226 case A_NPCM7XX_PWM_CNR1:
227 return 1;
228 case A_NPCM7XX_PWM_CNR2:
229 return 2;
230 case A_NPCM7XX_PWM_CNR3:
231 return 3;
232 default:
233 g_assert_not_reached();
237 static hwaddr npcm7xx_cmr_index(hwaddr offset)
239 switch (offset) {
240 case A_NPCM7XX_PWM_CMR0:
241 return 0;
242 case A_NPCM7XX_PWM_CMR1:
243 return 1;
244 case A_NPCM7XX_PWM_CMR2:
245 return 2;
246 case A_NPCM7XX_PWM_CMR3:
247 return 3;
248 default:
249 g_assert_not_reached();
253 static hwaddr npcm7xx_pdr_index(hwaddr offset)
255 switch (offset) {
256 case A_NPCM7XX_PWM_PDR0:
257 return 0;
258 case A_NPCM7XX_PWM_PDR1:
259 return 1;
260 case A_NPCM7XX_PWM_PDR2:
261 return 2;
262 case A_NPCM7XX_PWM_PDR3:
263 return 3;
264 default:
265 g_assert_not_reached();
269 static hwaddr npcm7xx_pwdr_index(hwaddr offset)
271 switch (offset) {
272 case A_NPCM7XX_PWM_PWDR0:
273 return 0;
274 case A_NPCM7XX_PWM_PWDR1:
275 return 1;
276 case A_NPCM7XX_PWM_PWDR2:
277 return 2;
278 case A_NPCM7XX_PWM_PWDR3:
279 return 3;
280 default:
281 g_assert_not_reached();
285 static uint64_t npcm7xx_pwm_read(void *opaque, hwaddr offset, unsigned size)
287 NPCM7xxPWMState *s = opaque;
288 uint64_t value = 0;
290 switch (offset) {
291 case A_NPCM7XX_PWM_CNR0:
292 case A_NPCM7XX_PWM_CNR1:
293 case A_NPCM7XX_PWM_CNR2:
294 case A_NPCM7XX_PWM_CNR3:
295 value = s->pwm[npcm7xx_cnr_index(offset)].cnr;
296 break;
298 case A_NPCM7XX_PWM_CMR0:
299 case A_NPCM7XX_PWM_CMR1:
300 case A_NPCM7XX_PWM_CMR2:
301 case A_NPCM7XX_PWM_CMR3:
302 value = s->pwm[npcm7xx_cmr_index(offset)].cmr;
303 break;
305 case A_NPCM7XX_PWM_PDR0:
306 case A_NPCM7XX_PWM_PDR1:
307 case A_NPCM7XX_PWM_PDR2:
308 case A_NPCM7XX_PWM_PDR3:
309 value = s->pwm[npcm7xx_pdr_index(offset)].pdr;
310 break;
312 case A_NPCM7XX_PWM_PWDR0:
313 case A_NPCM7XX_PWM_PWDR1:
314 case A_NPCM7XX_PWM_PWDR2:
315 case A_NPCM7XX_PWM_PWDR3:
316 value = s->pwm[npcm7xx_pwdr_index(offset)].pwdr;
317 break;
319 case A_NPCM7XX_PWM_PPR:
320 value = s->ppr;
321 break;
323 case A_NPCM7XX_PWM_CSR:
324 value = s->csr;
325 break;
327 case A_NPCM7XX_PWM_PCR:
328 value = s->pcr;
329 break;
331 case A_NPCM7XX_PWM_PIER:
332 value = s->pier;
333 break;
335 case A_NPCM7XX_PWM_PIIR:
336 value = s->piir;
337 break;
339 default:
340 qemu_log_mask(LOG_GUEST_ERROR,
341 "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
342 __func__, offset);
343 break;
346 trace_npcm7xx_pwm_read(DEVICE(s)->canonical_path, offset, value);
347 return value;
350 static void npcm7xx_pwm_write(void *opaque, hwaddr offset,
351 uint64_t v, unsigned size)
353 NPCM7xxPWMState *s = opaque;
354 NPCM7xxPWM *p;
355 uint32_t value = v;
357 trace_npcm7xx_pwm_write(DEVICE(s)->canonical_path, offset, value);
358 switch (offset) {
359 case A_NPCM7XX_PWM_CNR0:
360 case A_NPCM7XX_PWM_CNR1:
361 case A_NPCM7XX_PWM_CNR2:
362 case A_NPCM7XX_PWM_CNR3:
363 p = &s->pwm[npcm7xx_cnr_index(offset)];
364 if (value > NPCM7XX_MAX_CNR) {
365 qemu_log_mask(LOG_GUEST_ERROR,
366 "%s: invalid cnr value: %u", __func__, value);
367 p->cnr = NPCM7XX_MAX_CNR;
368 } else {
369 p->cnr = value;
371 npcm7xx_pwm_update_output(p);
372 break;
374 case A_NPCM7XX_PWM_CMR0:
375 case A_NPCM7XX_PWM_CMR1:
376 case A_NPCM7XX_PWM_CMR2:
377 case A_NPCM7XX_PWM_CMR3:
378 p = &s->pwm[npcm7xx_cmr_index(offset)];
379 if (value > NPCM7XX_MAX_CMR) {
380 qemu_log_mask(LOG_GUEST_ERROR,
381 "%s: invalid cmr value: %u", __func__, value);
382 p->cmr = NPCM7XX_MAX_CMR;
383 } else {
384 p->cmr = value;
386 npcm7xx_pwm_update_output(p);
387 break;
389 case A_NPCM7XX_PWM_PDR0:
390 case A_NPCM7XX_PWM_PDR1:
391 case A_NPCM7XX_PWM_PDR2:
392 case A_NPCM7XX_PWM_PDR3:
393 qemu_log_mask(LOG_GUEST_ERROR,
394 "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
395 __func__, offset);
396 break;
398 case A_NPCM7XX_PWM_PWDR0:
399 case A_NPCM7XX_PWM_PWDR1:
400 case A_NPCM7XX_PWM_PWDR2:
401 case A_NPCM7XX_PWM_PWDR3:
402 qemu_log_mask(LOG_UNIMP,
403 "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n",
404 __func__, offset);
405 break;
407 case A_NPCM7XX_PWM_PPR:
408 npcm7xx_pwm_write_ppr(s, value);
409 break;
411 case A_NPCM7XX_PWM_CSR:
412 npcm7xx_pwm_write_csr(s, value);
413 break;
415 case A_NPCM7XX_PWM_PCR:
416 npcm7xx_pwm_write_pcr(s, value);
417 break;
419 case A_NPCM7XX_PWM_PIER:
420 qemu_log_mask(LOG_UNIMP,
421 "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n",
422 __func__, offset);
423 break;
425 case A_NPCM7XX_PWM_PIIR:
426 qemu_log_mask(LOG_UNIMP,
427 "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n",
428 __func__, offset);
429 break;
431 default:
432 qemu_log_mask(LOG_GUEST_ERROR,
433 "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
434 __func__, offset);
435 break;
439 static const struct MemoryRegionOps npcm7xx_pwm_ops = {
440 .read = npcm7xx_pwm_read,
441 .write = npcm7xx_pwm_write,
442 .endianness = DEVICE_LITTLE_ENDIAN,
443 .valid = {
444 .min_access_size = 4,
445 .max_access_size = 4,
446 .unaligned = false,
450 static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type)
452 NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
453 int i;
455 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
456 NPCM7xxPWM *p = &s->pwm[i];
458 p->cnr = 0x00000000;
459 p->cmr = 0x00000000;
460 p->pdr = 0x00000000;
461 p->pwdr = 0x00000000;
464 s->ppr = 0x00000000;
465 s->csr = 0x00000000;
466 s->pcr = 0x00000000;
467 s->pier = 0x00000000;
468 s->piir = 0x00000000;
471 static void npcm7xx_pwm_hold_reset(Object *obj)
473 NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
474 int i;
476 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
477 qemu_irq_lower(s->pwm[i].irq);
481 static void npcm7xx_pwm_init(Object *obj)
483 NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
484 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
485 int i;
487 QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->pwm) != NPCM7XX_PWM_PER_MODULE);
488 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
489 NPCM7xxPWM *p = &s->pwm[i];
490 p->module = s;
491 p->index = i;
492 sysbus_init_irq(sbd, &p->irq);
495 memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s,
496 TYPE_NPCM7XX_PWM, 4 * KiB);
497 sysbus_init_mmio(sbd, &s->iomem);
498 s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
500 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
501 object_property_add_uint32_ptr(obj, "freq[*]",
502 &s->pwm[i].freq, OBJ_PROP_FLAG_READ);
503 object_property_add_uint32_ptr(obj, "duty[*]",
504 &s->pwm[i].duty, OBJ_PROP_FLAG_READ);
506 qdev_init_gpio_out_named(DEVICE(s), s->duty_gpio_out,
507 "duty-gpio-out", NPCM7XX_PWM_PER_MODULE);
510 static const VMStateDescription vmstate_npcm7xx_pwm = {
511 .name = "npcm7xx-pwm",
512 .version_id = 0,
513 .minimum_version_id = 0,
514 .fields = (VMStateField[]) {
515 VMSTATE_BOOL(running, NPCM7xxPWM),
516 VMSTATE_BOOL(inverted, NPCM7xxPWM),
517 VMSTATE_UINT8(index, NPCM7xxPWM),
518 VMSTATE_UINT32(cnr, NPCM7xxPWM),
519 VMSTATE_UINT32(cmr, NPCM7xxPWM),
520 VMSTATE_UINT32(pdr, NPCM7xxPWM),
521 VMSTATE_UINT32(pwdr, NPCM7xxPWM),
522 VMSTATE_UINT32(freq, NPCM7xxPWM),
523 VMSTATE_UINT32(duty, NPCM7xxPWM),
524 VMSTATE_END_OF_LIST(),
528 static const VMStateDescription vmstate_npcm7xx_pwm_module = {
529 .name = "npcm7xx-pwm-module",
530 .version_id = 0,
531 .minimum_version_id = 0,
532 .fields = (VMStateField[]) {
533 VMSTATE_CLOCK(clock, NPCM7xxPWMState),
534 VMSTATE_STRUCT_ARRAY(pwm, NPCM7xxPWMState,
535 NPCM7XX_PWM_PER_MODULE, 0, vmstate_npcm7xx_pwm,
536 NPCM7xxPWM),
537 VMSTATE_UINT32(ppr, NPCM7xxPWMState),
538 VMSTATE_UINT32(csr, NPCM7xxPWMState),
539 VMSTATE_UINT32(pcr, NPCM7xxPWMState),
540 VMSTATE_UINT32(pier, NPCM7xxPWMState),
541 VMSTATE_UINT32(piir, NPCM7xxPWMState),
542 VMSTATE_END_OF_LIST(),
546 static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data)
548 ResettableClass *rc = RESETTABLE_CLASS(klass);
549 DeviceClass *dc = DEVICE_CLASS(klass);
551 dc->desc = "NPCM7xx PWM Controller";
552 dc->vmsd = &vmstate_npcm7xx_pwm_module;
553 rc->phases.enter = npcm7xx_pwm_enter_reset;
554 rc->phases.hold = npcm7xx_pwm_hold_reset;
557 static const TypeInfo npcm7xx_pwm_info = {
558 .name = TYPE_NPCM7XX_PWM,
559 .parent = TYPE_SYS_BUS_DEVICE,
560 .instance_size = sizeof(NPCM7xxPWMState),
561 .class_init = npcm7xx_pwm_class_init,
562 .instance_init = npcm7xx_pwm_init,
565 static void npcm7xx_pwm_register_type(void)
567 type_register_static(&npcm7xx_pwm_info);
569 type_init(npcm7xx_pwm_register_type);