xref: /openbmc/qemu/hw/misc/npcm7xx_pwm.c (revision a10b9d93)
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