1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
31REG32(NPCM7XX_PWM_PPR, 0x00);
32REG32(NPCM7XX_PWM_CSR, 0x04);
33REG32(NPCM7XX_PWM_PCR, 0x08);
34REG32(NPCM7XX_PWM_CNR0, 0x0c);
35REG32(NPCM7XX_PWM_CMR0, 0x10);
36REG32(NPCM7XX_PWM_PDR0, 0x14);
37REG32(NPCM7XX_PWM_CNR1, 0x18);
38REG32(NPCM7XX_PWM_CMR1, 0x1c);
39REG32(NPCM7XX_PWM_PDR1, 0x20);
40REG32(NPCM7XX_PWM_CNR2, 0x24);
41REG32(NPCM7XX_PWM_CMR2, 0x28);
42REG32(NPCM7XX_PWM_PDR2, 0x2c);
43REG32(NPCM7XX_PWM_CNR3, 0x30);
44REG32(NPCM7XX_PWM_CMR3, 0x34);
45REG32(NPCM7XX_PWM_PDR3, 0x38);
46REG32(NPCM7XX_PWM_PIER, 0x3c);
47REG32(NPCM7XX_PWM_PIIR, 0x40);
48REG32(NPCM7XX_PWM_PWDR0, 0x44);
49REG32(NPCM7XX_PWM_PWDR1, 0x48);
50REG32(NPCM7XX_PWM_PWDR2, 0x4c);
51REG32(NPCM7XX_PWM_PWDR3, 0x50);
52
53
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
65static const int npcm7xx_ppr_base[] = { 0, 0, 8, 8 };
66
67static const int npcm7xx_csr_base[] = { 0, 4, 8, 12 };
68
69static const int npcm7xx_ch_base[] = { 0, 8, 12, 16 };
70
71static 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
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
93 if (csr < 4) {
94 freq >>= csr + 1;
95 }
96
97 return freq / (p->cnr + 1);
98}
99
100static 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
123static 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
134static 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 qemu_set_irq(p->module->duty_gpio_out[p->index], p->duty);
143 }
144}
145
146static void npcm7xx_pwm_update_output(NPCM7xxPWM *p)
147{
148 npcm7xx_pwm_update_freq(p);
149 npcm7xx_pwm_update_duty(p);
150}
151
152static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState *s, uint32_t new_ppr)
153{
154 int i;
155 uint32_t old_ppr = s->ppr;
156
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]);
162 }
163 }
164}
165
166static void npcm7xx_pwm_write_csr(NPCM7xxPWMState *s, uint32_t new_csr)
167{
168 int i;
169 uint32_t old_csr = s->csr;
170
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]);
176 }
177 }
178}
179
180static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState *s, uint32_t new_pcr)
181{
182 int i;
183 bool inverted;
184 uint32_t pcr;
185 NPCM7xxPWM *p;
186
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;
193
194
195
196
197
198 if ((pcr & NPCM7XX_CH_EN) && (pcr & NPCM7XX_CH_MOD)) {
199 if (p->running) {
200
201 if (p->inverted ^ inverted) {
202 p->inverted = inverted;
203 npcm7xx_pwm_update_duty(p);
204 }
205 } else {
206
207 p->running = true;
208 p->inverted = inverted;
209 npcm7xx_pwm_update_output(p);
210 }
211 } else {
212
213 p->running = false;
214 p->inverted = inverted;
215 npcm7xx_pwm_update_output(p);
216 }
217 }
218
219}
220
221static hwaddr npcm7xx_cnr_index(hwaddr offset)
222{
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();
234 }
235}
236
237static hwaddr npcm7xx_cmr_index(hwaddr offset)
238{
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();
250 }
251}
252
253static hwaddr npcm7xx_pdr_index(hwaddr offset)
254{
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();
266 }
267}
268
269static hwaddr npcm7xx_pwdr_index(hwaddr offset)
270{
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();
282 }
283}
284
285static uint64_t npcm7xx_pwm_read(void *opaque, hwaddr offset, unsigned size)
286{
287 NPCM7xxPWMState *s = opaque;
288 uint64_t value = 0;
289
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;
297
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;
304
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;
311
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;
318
319 case A_NPCM7XX_PWM_PPR:
320 value = s->ppr;
321 break;
322
323 case A_NPCM7XX_PWM_CSR:
324 value = s->csr;
325 break;
326
327 case A_NPCM7XX_PWM_PCR:
328 value = s->pcr;
329 break;
330
331 case A_NPCM7XX_PWM_PIER:
332 value = s->pier;
333 break;
334
335 case A_NPCM7XX_PWM_PIIR:
336 value = s->piir;
337 break;
338
339 default:
340 qemu_log_mask(LOG_GUEST_ERROR,
341 "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
342 __func__, offset);
343 break;
344 }
345
346 trace_npcm7xx_pwm_read(DEVICE(s)->canonical_path, offset, value);
347 return value;
348}
349
350static void npcm7xx_pwm_write(void *opaque, hwaddr offset,
351 uint64_t v, unsigned size)
352{
353 NPCM7xxPWMState *s = opaque;
354 NPCM7xxPWM *p;
355 uint32_t value = v;
356
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;
370 }
371 npcm7xx_pwm_update_output(p);
372 break;
373
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;
385 }
386 npcm7xx_pwm_update_output(p);
387 break;
388
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;
397
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;
406
407 case A_NPCM7XX_PWM_PPR:
408 npcm7xx_pwm_write_ppr(s, value);
409 break;
410
411 case A_NPCM7XX_PWM_CSR:
412 npcm7xx_pwm_write_csr(s, value);
413 break;
414
415 case A_NPCM7XX_PWM_PCR:
416 npcm7xx_pwm_write_pcr(s, value);
417 break;
418
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;
424
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;
430
431 default:
432 qemu_log_mask(LOG_GUEST_ERROR,
433 "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
434 __func__, offset);
435 break;
436 }
437}
438
439static 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,
447 },
448};
449
450static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type)
451{
452 NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
453 int i;
454
455 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
456 NPCM7xxPWM *p = &s->pwm[i];
457
458 p->cnr = 0x00000000;
459 p->cmr = 0x00000000;
460 p->pdr = 0x00000000;
461 p->pwdr = 0x00000000;
462 }
463
464 s->ppr = 0x00000000;
465 s->csr = 0x00000000;
466 s->pcr = 0x00000000;
467 s->pier = 0x00000000;
468 s->piir = 0x00000000;
469}
470
471static void npcm7xx_pwm_hold_reset(Object *obj)
472{
473 NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
474 int i;
475
476 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
477 qemu_irq_lower(s->pwm[i].irq);
478 }
479}
480
481static void npcm7xx_pwm_init(Object *obj)
482{
483 NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
484 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
485 int i;
486
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);
493 }
494
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);
499
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);
505 }
506 qdev_init_gpio_out_named(DEVICE(s), s->duty_gpio_out,
507 "duty-gpio-out", NPCM7XX_PWM_PER_MODULE);
508}
509
510static 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(),
525 },
526};
527
528static 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(),
543 },
544};
545
546static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data)
547{
548 ResettableClass *rc = RESETTABLE_CLASS(klass);
549 DeviceClass *dc = DEVICE_CLASS(klass);
550
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;
555}
556
557static 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,
563};
564
565static void npcm7xx_pwm_register_type(void)
566{
567 type_register_static(&npcm7xx_pwm_info);
568}
569type_init(npcm7xx_pwm_register_type);
570