xref: /openbmc/qemu/hw/misc/npcm7xx_pwm.c (revision c5614ee3)
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         qemu_set_irq(p->module->duty_gpio_out[p->index], p->duty);
143     }
144 }
145 
146 static void npcm7xx_pwm_update_output(NPCM7xxPWM *p)
147 {
148     npcm7xx_pwm_update_freq(p);
149     npcm7xx_pwm_update_duty(p);
150 }
151 
152 static 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 
166 static 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 
180 static 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          * We only run a PWM channel with toggle mode. Single-shot mode does not
196          * generate frequency and duty-cycle values.
197          */
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);
204                 }
205             } else {
206                 /* Run this PWM channel. */
207                 p->running = true;
208                 p->inverted = inverted;
209                 npcm7xx_pwm_update_output(p);
210             }
211         } else {
212             /* Clear this PWM channel. */
213             p->running = false;
214             p->inverted = inverted;
215             npcm7xx_pwm_update_output(p);
216         }
217     }
218 
219 }
220 
221 static 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 
237 static 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 
253 static 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 
269 static 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 
285 static 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 
350 static 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 
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,
447     },
448 };
449 
450 static 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 
471 static void npcm7xx_pwm_hold_reset(Object *obj, ResetType type)
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 
481 static 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 
510 static const VMStateDescription vmstate_npcm7xx_pwm = {
511     .name = "npcm7xx-pwm",
512     .version_id = 0,
513     .minimum_version_id = 0,
514     .fields = (const 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 
528 static const VMStateDescription vmstate_npcm7xx_pwm_module = {
529     .name = "npcm7xx-pwm-module",
530     .version_id = 0,
531     .minimum_version_id = 0,
532     .fields = (const 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 
546 static 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 
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,
563 };
564 
565 static void npcm7xx_pwm_register_type(void)
566 {
567     type_register_static(&npcm7xx_pwm_info);
568 }
569 type_init(npcm7xx_pwm_register_type);
570