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