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
);
142 qemu_set_irq(p
->module
->duty_gpio_out
[p
->index
], p
->duty
);
146 static void npcm7xx_pwm_update_output(NPCM7xxPWM
*p
)
148 npcm7xx_pwm_update_freq(p
);
149 npcm7xx_pwm_update_duty(p
);
152 static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState
*s
, uint32_t new_ppr
)
155 uint32_t old_ppr
= s
->ppr
;
157 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ppr_base
) != NPCM7XX_PWM_PER_MODULE
);
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
]);
166 static void npcm7xx_pwm_write_csr(NPCM7xxPWMState
*s
, uint32_t new_csr
)
169 uint32_t old_csr
= s
->csr
;
171 QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_csr_base
) != NPCM7XX_PWM_PER_MODULE
);
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
]);
180 static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState
*s
, uint32_t 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
) {
191 pcr
= NPCM7XX_CH(new_pcr
, i
);
192 inverted
= pcr
& NPCM7XX_CH_INV
;
195 * We only run a PWM channel with toggle mode. Single-shot mode does not
196 * generate frequency and duty-cycle values.
198 if ((pcr
& NPCM7XX_CH_EN
) && (pcr
& NPCM7XX_CH_MOD
)) {
200 /* Re-run this PWM channel if inverted changed. */
201 if (p
->inverted
^ inverted
) {
202 p
->inverted
= inverted
;
203 npcm7xx_pwm_update_duty(p
);
206 /* Run this PWM channel. */
208 p
->inverted
= inverted
;
209 npcm7xx_pwm_update_output(p
);
212 /* Clear this PWM channel. */
214 p
->inverted
= inverted
;
215 npcm7xx_pwm_update_output(p
);
221 static hwaddr
npcm7xx_cnr_index(hwaddr offset
)
224 case A_NPCM7XX_PWM_CNR0
:
226 case A_NPCM7XX_PWM_CNR1
:
228 case A_NPCM7XX_PWM_CNR2
:
230 case A_NPCM7XX_PWM_CNR3
:
233 g_assert_not_reached();
237 static hwaddr
npcm7xx_cmr_index(hwaddr offset
)
240 case A_NPCM7XX_PWM_CMR0
:
242 case A_NPCM7XX_PWM_CMR1
:
244 case A_NPCM7XX_PWM_CMR2
:
246 case A_NPCM7XX_PWM_CMR3
:
249 g_assert_not_reached();
253 static hwaddr
npcm7xx_pdr_index(hwaddr offset
)
256 case A_NPCM7XX_PWM_PDR0
:
258 case A_NPCM7XX_PWM_PDR1
:
260 case A_NPCM7XX_PWM_PDR2
:
262 case A_NPCM7XX_PWM_PDR3
:
265 g_assert_not_reached();
269 static hwaddr
npcm7xx_pwdr_index(hwaddr offset
)
272 case A_NPCM7XX_PWM_PWDR0
:
274 case A_NPCM7XX_PWM_PWDR1
:
276 case A_NPCM7XX_PWM_PWDR2
:
278 case A_NPCM7XX_PWM_PWDR3
:
281 g_assert_not_reached();
285 static uint64_t npcm7xx_pwm_read(void *opaque
, hwaddr offset
, unsigned size
)
287 NPCM7xxPWMState
*s
= opaque
;
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
;
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
;
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
;
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
;
319 case A_NPCM7XX_PWM_PPR
:
323 case A_NPCM7XX_PWM_CSR
:
327 case A_NPCM7XX_PWM_PCR
:
331 case A_NPCM7XX_PWM_PIER
:
335 case A_NPCM7XX_PWM_PIIR
:
340 qemu_log_mask(LOG_GUEST_ERROR
,
341 "%s: invalid offset 0x%04" HWADDR_PRIx
"\n",
346 trace_npcm7xx_pwm_read(DEVICE(s
)->canonical_path
, offset
, value
);
350 static void npcm7xx_pwm_write(void *opaque
, hwaddr offset
,
351 uint64_t v
, unsigned size
)
353 NPCM7xxPWMState
*s
= opaque
;
357 trace_npcm7xx_pwm_write(DEVICE(s
)->canonical_path
, offset
, value
);
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
;
371 npcm7xx_pwm_update_output(p
);
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
;
386 npcm7xx_pwm_update_output(p
);
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",
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",
407 case A_NPCM7XX_PWM_PPR
:
408 npcm7xx_pwm_write_ppr(s
, value
);
411 case A_NPCM7XX_PWM_CSR
:
412 npcm7xx_pwm_write_csr(s
, value
);
415 case A_NPCM7XX_PWM_PCR
:
416 npcm7xx_pwm_write_pcr(s
, value
);
419 case A_NPCM7XX_PWM_PIER
:
420 qemu_log_mask(LOG_UNIMP
,
421 "%s: register @ 0x%04" HWADDR_PRIx
" is not implemented\n",
425 case A_NPCM7XX_PWM_PIIR
:
426 qemu_log_mask(LOG_UNIMP
,
427 "%s: register @ 0x%04" HWADDR_PRIx
" is not implemented\n",
432 qemu_log_mask(LOG_GUEST_ERROR
,
433 "%s: invalid offset 0x%04" HWADDR_PRIx
"\n",
439 static const struct MemoryRegionOps npcm7xx_pwm_ops
= {
440 .read
= npcm7xx_pwm_read
,
441 .write
= npcm7xx_pwm_write
,
442 .endianness
= DEVICE_LITTLE_ENDIAN
,
444 .min_access_size
= 4,
445 .max_access_size
= 4,
450 static void npcm7xx_pwm_enter_reset(Object
*obj
, ResetType type
)
452 NPCM7xxPWMState
*s
= NPCM7XX_PWM(obj
);
455 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; i
++) {
456 NPCM7xxPWM
*p
= &s
->pwm
[i
];
461 p
->pwdr
= 0x00000000;
467 s
->pier
= 0x00000000;
468 s
->piir
= 0x00000000;
471 static void npcm7xx_pwm_hold_reset(Object
*obj
)
473 NPCM7xxPWMState
*s
= NPCM7XX_PWM(obj
);
476 for (i
= 0; i
< NPCM7XX_PWM_PER_MODULE
; i
++) {
477 qemu_irq_lower(s
->pwm
[i
].irq
);
481 static void npcm7xx_pwm_init(Object
*obj
)
483 NPCM7xxPWMState
*s
= NPCM7XX_PWM(obj
);
484 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
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
];
492 sysbus_init_irq(sbd
, &p
->irq
);
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);
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
);
506 qdev_init_gpio_out_named(DEVICE(s
), s
->duty_gpio_out
,
507 "duty-gpio-out", NPCM7XX_PWM_PER_MODULE
);
510 static const VMStateDescription vmstate_npcm7xx_pwm
= {
511 .name
= "npcm7xx-pwm",
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(),
528 static const VMStateDescription vmstate_npcm7xx_pwm_module
= {
529 .name
= "npcm7xx-pwm-module",
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
,
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(),
546 static void npcm7xx_pwm_class_init(ObjectClass
*klass
, void *data
)
548 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
549 DeviceClass
*dc
= DEVICE_CLASS(klass
);
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
;
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
,
565 static void npcm7xx_pwm_register_type(void)
567 type_register_static(&npcm7xx_pwm_info
);
569 type_init(npcm7xx_pwm_register_type
);