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
17 #include "qemu/osdep.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"
27 #include "qemu/module.h"
28 #include "qemu/units.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
)
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
);
85 /* csr can only be 0~4 */
87 qemu_log_mask(LOG_GUEST_ERROR
,
88 "%s: invalid csr value %u\n",
92 /* freq won't be changed if csr == 4. */
97 return freq
/ (p
->cnr
+ 1);
100 static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM
*p
)
107 } else if (p
->cmr
>= p
->cnr
) {
108 duty
= NPCM7XX_PWM_MAX_DUTY
;
110 duty
= (uint64_t)NPCM7XX_PWM_MAX_DUTY
* (p
->cmr
+ 1) / (p
->cnr
+ 1);
117 duty
= NPCM7XX_PWM_MAX_DUTY
- 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
);
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
);
145 static void npcm7xx_pwm_update_output(NPCM7xxPWM
*p
)
147 npcm7xx_pwm_update_freq(p
);
148 npcm7xx_pwm_update_duty(p
);
151 static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState
*s
, uint32_t new_ppr
)
154 uint32_t old_ppr
= s
->ppr
;
156 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ppr_base
) != NPCM7XX_PWM_PER_MODULE
);
158 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; ++i
) {
159 if (NPCM7XX_PPR(old_ppr
, i
) != NPCM7XX_PPR(new_ppr
, i
)) {
160 npcm7xx_pwm_update_freq(&s
->pwm
[i
]);
165 static void npcm7xx_pwm_write_csr(NPCM7xxPWMState
*s
, uint32_t new_csr
)
168 uint32_t old_csr
= s
->csr
;
170 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_csr_base
) != NPCM7XX_PWM_PER_MODULE
);
172 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; ++i
) {
173 if (NPCM7XX_CSR(old_csr
, i
) != NPCM7XX_CSR(new_csr
, i
)) {
174 npcm7xx_pwm_update_freq(&s
->pwm
[i
]);
179 static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState
*s
, uint32_t new_pcr
)
187 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ch_base
) != NPCM7XX_PWM_PER_MODULE
);
188 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; ++i
) {
190 pcr
= NPCM7XX_CH(new_pcr
, i
);
191 inverted
= pcr
& NPCM7XX_CH_INV
;
194 * We only run a PWM channel with toggle mode. Single-shot mode does not
195 * generate frequency and duty-cycle values.
197 if ((pcr
& NPCM7XX_CH_EN
) && (pcr
& NPCM7XX_CH_MOD
)) {
199 /* Re-run this PWM channel if inverted changed. */
200 if (p
->inverted
^ inverted
) {
201 p
->inverted
= inverted
;
202 npcm7xx_pwm_update_duty(p
);
205 /* Run this PWM channel. */
207 p
->inverted
= inverted
;
208 npcm7xx_pwm_update_output(p
);
211 /* Clear this PWM channel. */
213 p
->inverted
= inverted
;
214 npcm7xx_pwm_update_output(p
);
220 static hwaddr
npcm7xx_cnr_index(hwaddr offset
)
223 case A_NPCM7XX_PWM_CNR0
:
225 case A_NPCM7XX_PWM_CNR1
:
227 case A_NPCM7XX_PWM_CNR2
:
229 case A_NPCM7XX_PWM_CNR3
:
232 g_assert_not_reached();
236 static hwaddr
npcm7xx_cmr_index(hwaddr offset
)
239 case A_NPCM7XX_PWM_CMR0
:
241 case A_NPCM7XX_PWM_CMR1
:
243 case A_NPCM7XX_PWM_CMR2
:
245 case A_NPCM7XX_PWM_CMR3
:
248 g_assert_not_reached();
252 static hwaddr
npcm7xx_pdr_index(hwaddr offset
)
255 case A_NPCM7XX_PWM_PDR0
:
257 case A_NPCM7XX_PWM_PDR1
:
259 case A_NPCM7XX_PWM_PDR2
:
261 case A_NPCM7XX_PWM_PDR3
:
264 g_assert_not_reached();
268 static hwaddr
npcm7xx_pwdr_index(hwaddr offset
)
271 case A_NPCM7XX_PWM_PWDR0
:
273 case A_NPCM7XX_PWM_PWDR1
:
275 case A_NPCM7XX_PWM_PWDR2
:
277 case A_NPCM7XX_PWM_PWDR3
:
280 g_assert_not_reached();
284 static uint64_t npcm7xx_pwm_read(void *opaque
, hwaddr offset
, unsigned size
)
286 NPCM7xxPWMState
*s
= opaque
;
290 case A_NPCM7XX_PWM_CNR0
:
291 case A_NPCM7XX_PWM_CNR1
:
292 case A_NPCM7XX_PWM_CNR2
:
293 case A_NPCM7XX_PWM_CNR3
:
294 value
= s
->pwm
[npcm7xx_cnr_index(offset
)].cnr
;
297 case A_NPCM7XX_PWM_CMR0
:
298 case A_NPCM7XX_PWM_CMR1
:
299 case A_NPCM7XX_PWM_CMR2
:
300 case A_NPCM7XX_PWM_CMR3
:
301 value
= s
->pwm
[npcm7xx_cmr_index(offset
)].cmr
;
304 case A_NPCM7XX_PWM_PDR0
:
305 case A_NPCM7XX_PWM_PDR1
:
306 case A_NPCM7XX_PWM_PDR2
:
307 case A_NPCM7XX_PWM_PDR3
:
308 value
= s
->pwm
[npcm7xx_pdr_index(offset
)].pdr
;
311 case A_NPCM7XX_PWM_PWDR0
:
312 case A_NPCM7XX_PWM_PWDR1
:
313 case A_NPCM7XX_PWM_PWDR2
:
314 case A_NPCM7XX_PWM_PWDR3
:
315 value
= s
->pwm
[npcm7xx_pwdr_index(offset
)].pwdr
;
318 case A_NPCM7XX_PWM_PPR
:
322 case A_NPCM7XX_PWM_CSR
:
326 case A_NPCM7XX_PWM_PCR
:
330 case A_NPCM7XX_PWM_PIER
:
334 case A_NPCM7XX_PWM_PIIR
:
339 qemu_log_mask(LOG_GUEST_ERROR
,
340 "%s: invalid offset 0x%04" HWADDR_PRIx
"\n",
345 trace_npcm7xx_pwm_read(DEVICE(s
)->canonical_path
, offset
, value
);
349 static void npcm7xx_pwm_write(void *opaque
, hwaddr offset
,
350 uint64_t v
, unsigned size
)
352 NPCM7xxPWMState
*s
= opaque
;
356 trace_npcm7xx_pwm_write(DEVICE(s
)->canonical_path
, offset
, value
);
358 case A_NPCM7XX_PWM_CNR0
:
359 case A_NPCM7XX_PWM_CNR1
:
360 case A_NPCM7XX_PWM_CNR2
:
361 case A_NPCM7XX_PWM_CNR3
:
362 p
= &s
->pwm
[npcm7xx_cnr_index(offset
)];
363 if (value
> NPCM7XX_MAX_CNR
) {
364 qemu_log_mask(LOG_GUEST_ERROR
,
365 "%s: invalid cnr value: %u", __func__
, value
);
366 p
->cnr
= NPCM7XX_MAX_CNR
;
370 npcm7xx_pwm_update_output(p
);
373 case A_NPCM7XX_PWM_CMR0
:
374 case A_NPCM7XX_PWM_CMR1
:
375 case A_NPCM7XX_PWM_CMR2
:
376 case A_NPCM7XX_PWM_CMR3
:
377 p
= &s
->pwm
[npcm7xx_cmr_index(offset
)];
378 if (value
> NPCM7XX_MAX_CMR
) {
379 qemu_log_mask(LOG_GUEST_ERROR
,
380 "%s: invalid cmr value: %u", __func__
, value
);
381 p
->cmr
= NPCM7XX_MAX_CMR
;
385 npcm7xx_pwm_update_output(p
);
388 case A_NPCM7XX_PWM_PDR0
:
389 case A_NPCM7XX_PWM_PDR1
:
390 case A_NPCM7XX_PWM_PDR2
:
391 case A_NPCM7XX_PWM_PDR3
:
392 qemu_log_mask(LOG_GUEST_ERROR
,
393 "%s: register @ 0x%04" HWADDR_PRIx
" is read-only\n",
397 case A_NPCM7XX_PWM_PWDR0
:
398 case A_NPCM7XX_PWM_PWDR1
:
399 case A_NPCM7XX_PWM_PWDR2
:
400 case A_NPCM7XX_PWM_PWDR3
:
401 qemu_log_mask(LOG_UNIMP
,
402 "%s: register @ 0x%04" HWADDR_PRIx
" is not implemented\n",
406 case A_NPCM7XX_PWM_PPR
:
407 npcm7xx_pwm_write_ppr(s
, value
);
410 case A_NPCM7XX_PWM_CSR
:
411 npcm7xx_pwm_write_csr(s
, value
);
414 case A_NPCM7XX_PWM_PCR
:
415 npcm7xx_pwm_write_pcr(s
, value
);
418 case A_NPCM7XX_PWM_PIER
:
419 qemu_log_mask(LOG_UNIMP
,
420 "%s: register @ 0x%04" HWADDR_PRIx
" is not implemented\n",
424 case A_NPCM7XX_PWM_PIIR
:
425 qemu_log_mask(LOG_UNIMP
,
426 "%s: register @ 0x%04" HWADDR_PRIx
" is not implemented\n",
431 qemu_log_mask(LOG_GUEST_ERROR
,
432 "%s: invalid offset 0x%04" HWADDR_PRIx
"\n",
438 static const struct MemoryRegionOps npcm7xx_pwm_ops
= {
439 .read
= npcm7xx_pwm_read
,
440 .write
= npcm7xx_pwm_write
,
441 .endianness
= DEVICE_LITTLE_ENDIAN
,
443 .min_access_size
= 4,
444 .max_access_size
= 4,
449 static void npcm7xx_pwm_enter_reset(Object
*obj
, ResetType type
)
451 NPCM7xxPWMState
*s
= NPCM7XX_PWM(obj
);
454 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; i
++) {
455 NPCM7xxPWM
*p
= &s
->pwm
[i
];
460 p
->pwdr
= 0x00000000;
466 s
->pier
= 0x00000000;
467 s
->piir
= 0x00000000;
470 static void npcm7xx_pwm_hold_reset(Object
*obj
)
472 NPCM7xxPWMState
*s
= NPCM7XX_PWM(obj
);
475 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; i
++) {
476 qemu_irq_lower(s
->pwm
[i
].irq
);
480 static void npcm7xx_pwm_init(Object
*obj
)
482 NPCM7xxPWMState
*s
= NPCM7XX_PWM(obj
);
483 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
486 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; i
++) {
487 NPCM7xxPWM
*p
= &s
->pwm
[i
];
490 sysbus_init_irq(sbd
, &p
->irq
);
493 memory_region_init_io(&s
->iomem
, obj
, &npcm7xx_pwm_ops
, s
,
494 TYPE_NPCM7XX_PWM
, 4 * KiB
);
495 sysbus_init_mmio(sbd
, &s
->iomem
);
496 s
->clock
= qdev_init_clock_in(DEVICE(s
), "clock", NULL
, NULL
);
498 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; ++i
) {
499 object_property_add_uint32_ptr(obj
, "freq[*]",
500 &s
->pwm
[i
].freq
, OBJ_PROP_FLAG_READ
);
501 object_property_add_uint32_ptr(obj
, "duty[*]",
502 &s
->pwm
[i
].duty
, OBJ_PROP_FLAG_READ
);
506 static const VMStateDescription vmstate_npcm7xx_pwm
= {
507 .name
= "npcm7xx-pwm",
509 .minimum_version_id
= 0,
510 .fields
= (VMStateField
[]) {
511 VMSTATE_BOOL(running
, NPCM7xxPWM
),
512 VMSTATE_BOOL(inverted
, NPCM7xxPWM
),
513 VMSTATE_UINT8(index
, NPCM7xxPWM
),
514 VMSTATE_UINT32(cnr
, NPCM7xxPWM
),
515 VMSTATE_UINT32(cmr
, NPCM7xxPWM
),
516 VMSTATE_UINT32(pdr
, NPCM7xxPWM
),
517 VMSTATE_UINT32(pwdr
, NPCM7xxPWM
),
518 VMSTATE_UINT32(freq
, NPCM7xxPWM
),
519 VMSTATE_UINT32(duty
, NPCM7xxPWM
),
520 VMSTATE_END_OF_LIST(),
524 static const VMStateDescription vmstate_npcm7xx_pwm_module
= {
525 .name
= "npcm7xx-pwm-module",
527 .minimum_version_id
= 0,
528 .fields
= (VMStateField
[]) {
529 VMSTATE_CLOCK(clock
, NPCM7xxPWMState
),
530 VMSTATE_STRUCT_ARRAY(pwm
, NPCM7xxPWMState
,
531 NPCM7XX_PWM_PER_MODULE
, 0, vmstate_npcm7xx_pwm
,
533 VMSTATE_UINT32(ppr
, NPCM7xxPWMState
),
534 VMSTATE_UINT32(csr
, NPCM7xxPWMState
),
535 VMSTATE_UINT32(pcr
, NPCM7xxPWMState
),
536 VMSTATE_UINT32(pier
, NPCM7xxPWMState
),
537 VMSTATE_UINT32(piir
, NPCM7xxPWMState
),
538 VMSTATE_END_OF_LIST(),
542 static void npcm7xx_pwm_class_init(ObjectClass
*klass
, void *data
)
544 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
545 DeviceClass
*dc
= DEVICE_CLASS(klass
);
547 dc
->desc
= "NPCM7xx PWM Controller";
548 dc
->vmsd
= &vmstate_npcm7xx_pwm_module
;
549 rc
->phases
.enter
= npcm7xx_pwm_enter_reset
;
550 rc
->phases
.hold
= npcm7xx_pwm_hold_reset
;
553 static const TypeInfo npcm7xx_pwm_info
= {
554 .name
= TYPE_NPCM7XX_PWM
,
555 .parent
= TYPE_SYS_BUS_DEVICE
,
556 .instance_size
= sizeof(NPCM7xxPWMState
),
557 .class_init
= npcm7xx_pwm_class_init
,
558 .instance_init
= npcm7xx_pwm_init
,
561 static void npcm7xx_pwm_register_type(void)
563 type_register_static(&npcm7xx_pwm_info
);
565 type_init(npcm7xx_pwm_register_type
);