4 * Copyright (c) 2020 Western Digital
6 * Author: Alistair Francis <alistair.francis@wdc.com>
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #include "qemu/osdep.h"
30 #include "hw/timer/sifive_pwm.h"
31 #include "hw/qdev-properties.h"
32 #include "hw/registerfields.h"
33 #include "migration/vmstate.h"
35 #include "qemu/module.h"
37 #define HAS_PWM_EN_BITS(cfg) ((cfg & R_CONFIG_ENONESHOT_MASK) || \
38 (cfg & R_CONFIG_ENALWAYS_MASK))
40 #define PWMCMP_MASK 0xFFFF
41 #define PWMCOUNT_MASK 0x7FFFFFFF
44 FIELD(CONFIG
, SCALE
, 0, 4)
45 FIELD(CONFIG
, STICKY
, 8, 1)
46 FIELD(CONFIG
, ZEROCMP
, 9, 1)
47 FIELD(CONFIG
, DEGLITCH
, 10, 1)
48 FIELD(CONFIG
, ENALWAYS
, 12, 1)
49 FIELD(CONFIG
, ENONESHOT
, 13, 1)
50 FIELD(CONFIG
, CMP0CENTER
, 16, 1)
51 FIELD(CONFIG
, CMP1CENTER
, 17, 1)
52 FIELD(CONFIG
, CMP2CENTER
, 18, 1)
53 FIELD(CONFIG
, CMP3CENTER
, 19, 1)
54 FIELD(CONFIG
, CMP0GANG
, 24, 1)
55 FIELD(CONFIG
, CMP1GANG
, 25, 1)
56 FIELD(CONFIG
, CMP2GANG
, 26, 1)
57 FIELD(CONFIG
, CMP3GANG
, 27, 1)
58 FIELD(CONFIG
, CMP0IP
, 28, 1)
59 FIELD(CONFIG
, CMP1IP
, 29, 1)
60 FIELD(CONFIG
, CMP2IP
, 30, 1)
61 FIELD(CONFIG
, CMP3IP
, 31, 1)
69 static inline uint64_t sifive_pwm_ns_to_ticks(SiFivePwmState
*s
,
72 return muldiv64(time
, s
->freq_hz
, NANOSECONDS_PER_SECOND
);
75 static inline uint64_t sifive_pwm_ticks_to_ns(SiFivePwmState
*s
,
78 return muldiv64(ticks
, NANOSECONDS_PER_SECOND
, s
->freq_hz
);
81 static inline uint64_t sifive_pwm_compute_scale(SiFivePwmState
*s
)
83 return s
->pwmcfg
& R_CONFIG_SCALE_MASK
;
86 static void sifive_pwm_set_alarms(SiFivePwmState
*s
)
88 uint64_t now_ns
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
90 if (HAS_PWM_EN_BITS(s
->pwmcfg
)) {
92 * Subtract ticks from number of ticks when the timer was zero
93 * and mask to the register width.
95 uint64_t pwmcount
= (sifive_pwm_ns_to_ticks(s
, now_ns
) -
96 s
->tick_offset
) & PWMCOUNT_MASK
;
97 uint64_t scale
= sifive_pwm_compute_scale(s
);
98 /* PWMs only contains PWMCMP_MASK bits starting at scale */
99 uint64_t pwms
= (pwmcount
& (PWMCMP_MASK
<< scale
)) >> scale
;
101 for (int i
= 0; i
< SIFIVE_PWM_CHANS
; i
++) {
102 uint64_t pwmcmp
= s
->pwmcmp
[i
] & PWMCMP_MASK
;
103 uint64_t pwmcmp_ticks
= pwmcmp
<< scale
;
106 * Per circuit diagram and spec, both cases raises corresponding
107 * IP bit one clock cycle after time expires.
110 uint64_t offset
= pwmcmp_ticks
- pwmcount
+ 1;
111 uint64_t when_to_fire
= now_ns
+
112 sifive_pwm_ticks_to_ns(s
, offset
);
114 trace_sifive_pwm_set_alarm(when_to_fire
, now_ns
);
115 timer_mod(&s
->timer
[i
], when_to_fire
);
117 /* Schedule interrupt for next cycle */
118 trace_sifive_pwm_set_alarm(now_ns
+ 1, now_ns
);
119 timer_mod(&s
->timer
[i
], now_ns
+ 1);
125 * If timer incrementing disabled, just do pwms > pwmcmp check since
126 * a write may have happened to PWMs.
128 uint64_t pwmcount
= (s
->tick_offset
) & PWMCOUNT_MASK
;
129 uint64_t scale
= sifive_pwm_compute_scale(s
);
130 uint64_t pwms
= (pwmcount
& (PWMCMP_MASK
<< scale
)) >> scale
;
132 for (int i
= 0; i
< SIFIVE_PWM_CHANS
; i
++) {
133 uint64_t pwmcmp
= s
->pwmcmp
[i
] & PWMCMP_MASK
;
135 if (pwms
>= pwmcmp
) {
136 trace_sifive_pwm_set_alarm(now_ns
+ 1, now_ns
);
137 timer_mod(&s
->timer
[i
], now_ns
+ 1);
139 /* Effectively disable timer by scheduling far in future. */
140 trace_sifive_pwm_set_alarm(0xFFFFFFFFFFFFFF, now_ns
);
141 timer_mod(&s
->timer
[i
], 0xFFFFFFFFFFFFFF);
147 static void sifive_pwm_interrupt(SiFivePwmState
*s
, int num
)
149 uint64_t now
= sifive_pwm_ns_to_ticks(s
,
150 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
));
151 bool was_incrementing
= HAS_PWM_EN_BITS(s
->pwmcfg
);
153 trace_sifive_pwm_interrupt(num
);
155 s
->pwmcfg
|= R_CONFIG_CMP0IP_MASK
<< num
;
156 qemu_irq_raise(s
->irqs
[num
]);
159 * If the zerocmp is set and pwmcmp0 raised the interrupt
160 * reset the zero ticks.
162 if ((s
->pwmcfg
& R_CONFIG_ZEROCMP_MASK
) && (num
== 0)) {
163 /* If reset signal conditions, disable ENONESHOT. */
164 s
->pwmcfg
&= ~R_CONFIG_ENONESHOT_MASK
;
166 if (was_incrementing
) {
167 /* If incrementing, time in ticks is when pwmcount is zero */
168 s
->tick_offset
= now
;
170 /* If not incrementing, pwmcount = 0 */
176 * If carryout bit set, which we discern via looking for overflow,
177 * also reset ENONESHOT.
179 if (was_incrementing
&&
180 ((now
& PWMCOUNT_MASK
) < (s
->tick_offset
& PWMCOUNT_MASK
))) {
181 s
->pwmcfg
&= ~R_CONFIG_ENONESHOT_MASK
;
184 /* Schedule or disable interrupts */
185 sifive_pwm_set_alarms(s
);
187 /* If was enabled, and now not enabled, switch tick rep */
188 if (was_incrementing
&& !HAS_PWM_EN_BITS(s
->pwmcfg
)) {
189 s
->tick_offset
= (now
- s
->tick_offset
) & PWMCOUNT_MASK
;
193 static void sifive_pwm_interrupt_0(void *opaque
)
195 SiFivePwmState
*s
= opaque
;
197 sifive_pwm_interrupt(s
, 0);
200 static void sifive_pwm_interrupt_1(void *opaque
)
202 SiFivePwmState
*s
= opaque
;
204 sifive_pwm_interrupt(s
, 1);
207 static void sifive_pwm_interrupt_2(void *opaque
)
209 SiFivePwmState
*s
= opaque
;
211 sifive_pwm_interrupt(s
, 2);
214 static void sifive_pwm_interrupt_3(void *opaque
)
216 SiFivePwmState
*s
= opaque
;
218 sifive_pwm_interrupt(s
, 3);
221 static uint64_t sifive_pwm_read(void *opaque
, hwaddr addr
,
224 SiFivePwmState
*s
= opaque
;
225 uint64_t cur_time
, scale
;
226 uint64_t now
= sifive_pwm_ns_to_ticks(s
,
227 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
));
229 trace_sifive_pwm_read(addr
);
235 cur_time
= s
->tick_offset
;
237 if (HAS_PWM_EN_BITS(s
->pwmcfg
)) {
238 cur_time
= now
- cur_time
;
242 * Return the value in the counter with bit 31 always 0
243 * This is allowed to wrap around so we don't need to check that.
245 return cur_time
& PWMCOUNT_MASK
;
247 cur_time
= s
->tick_offset
;
248 scale
= sifive_pwm_compute_scale(s
);
250 if (HAS_PWM_EN_BITS(s
->pwmcfg
)) {
251 cur_time
= now
- cur_time
;
254 return ((cur_time
& PWMCOUNT_MASK
) >> scale
) & PWMCMP_MASK
;
256 return s
->pwmcmp
[0] & PWMCMP_MASK
;
258 return s
->pwmcmp
[1] & PWMCMP_MASK
;
260 return s
->pwmcmp
[2] & PWMCMP_MASK
;
262 return s
->pwmcmp
[3] & PWMCMP_MASK
;
264 qemu_log_mask(LOG_GUEST_ERROR
,
265 "%s: Bad offset 0x%"HWADDR_PRIx
"\n", __func__
, addr
);
272 static void sifive_pwm_write(void *opaque
, hwaddr addr
,
273 uint64_t val64
, unsigned int size
)
275 SiFivePwmState
*s
= opaque
;
276 uint32_t value
= val64
;
277 uint64_t new_offset
, scale
;
278 uint64_t now
= sifive_pwm_ns_to_ticks(s
,
279 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
));
281 trace_sifive_pwm_write(value
, addr
);
285 if (value
& (R_CONFIG_CMP0CENTER_MASK
| R_CONFIG_CMP1CENTER_MASK
|
286 R_CONFIG_CMP2CENTER_MASK
| R_CONFIG_CMP3CENTER_MASK
)) {
287 qemu_log_mask(LOG_UNIMP
, "%s: CMPxCENTER is not supported\n",
291 if (value
& (R_CONFIG_CMP0GANG_MASK
| R_CONFIG_CMP1GANG_MASK
|
292 R_CONFIG_CMP2GANG_MASK
| R_CONFIG_CMP3GANG_MASK
)) {
293 qemu_log_mask(LOG_UNIMP
, "%s: CMPxGANG is not supported\n",
297 if (value
& (R_CONFIG_CMP0IP_MASK
| R_CONFIG_CMP1IP_MASK
|
298 R_CONFIG_CMP2IP_MASK
| R_CONFIG_CMP3IP_MASK
)) {
299 qemu_log_mask(LOG_UNIMP
, "%s: CMPxIP is not supported\n",
303 if (!(value
& R_CONFIG_CMP0IP_MASK
)) {
304 qemu_irq_lower(s
->irqs
[0]);
307 if (!(value
& R_CONFIG_CMP1IP_MASK
)) {
308 qemu_irq_lower(s
->irqs
[1]);
311 if (!(value
& R_CONFIG_CMP2IP_MASK
)) {
312 qemu_irq_lower(s
->irqs
[2]);
315 if (!(value
& R_CONFIG_CMP3IP_MASK
)) {
316 qemu_irq_lower(s
->irqs
[3]);
320 * If this write enables the timer increment
321 * set the time when pwmcount was zero to be cur_time - pwmcount.
322 * If this write disables the timer increment
323 * convert back from pwmcount to the time in ticks
324 * when pwmcount was zero.
326 if ((!HAS_PWM_EN_BITS(s
->pwmcfg
) && HAS_PWM_EN_BITS(value
)) ||
327 (HAS_PWM_EN_BITS(s
->pwmcfg
) && !HAS_PWM_EN_BITS(value
))) {
328 s
->tick_offset
= (now
- s
->tick_offset
) & PWMCOUNT_MASK
;
334 /* The guest changed the counter, updated the offset value. */
337 if (HAS_PWM_EN_BITS(s
->pwmcfg
)) {
338 new_offset
= now
- new_offset
;
341 s
->tick_offset
= new_offset
;
344 scale
= sifive_pwm_compute_scale(s
);
345 new_offset
= (((value
& PWMCMP_MASK
) << scale
) & PWMCOUNT_MASK
);
347 if (HAS_PWM_EN_BITS(s
->pwmcfg
)) {
348 new_offset
= now
- new_offset
;
351 s
->tick_offset
= new_offset
;
354 s
->pwmcmp
[0] = value
& PWMCMP_MASK
;
357 s
->pwmcmp
[1] = value
& PWMCMP_MASK
;
360 s
->pwmcmp
[2] = value
& PWMCMP_MASK
;
363 s
->pwmcmp
[3] = value
& PWMCMP_MASK
;
366 qemu_log_mask(LOG_GUEST_ERROR
,
367 "%s: Bad offset 0x%"HWADDR_PRIx
"\n", __func__
, addr
);
370 /* Update the alarms to reflect possible updated values */
371 sifive_pwm_set_alarms(s
);
374 static void sifive_pwm_reset(DeviceState
*dev
)
376 SiFivePwmState
*s
= SIFIVE_PWM(dev
);
377 uint64_t now
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
379 s
->pwmcfg
= 0x00000000;
380 s
->pwmcmp
[0] = 0x00000000;
381 s
->pwmcmp
[1] = 0x00000000;
382 s
->pwmcmp
[2] = 0x00000000;
383 s
->pwmcmp
[3] = 0x00000000;
385 s
->tick_offset
= sifive_pwm_ns_to_ticks(s
, now
);
388 static const MemoryRegionOps sifive_pwm_ops
= {
389 .read
= sifive_pwm_read
,
390 .write
= sifive_pwm_write
,
391 .endianness
= DEVICE_NATIVE_ENDIAN
,
394 static const VMStateDescription vmstate_sifive_pwm
= {
395 .name
= TYPE_SIFIVE_PWM
,
397 .minimum_version_id
= 1,
398 .fields
= (const VMStateField
[]) {
399 VMSTATE_TIMER_ARRAY(timer
, SiFivePwmState
, 4),
400 VMSTATE_UINT64(tick_offset
, SiFivePwmState
),
401 VMSTATE_UINT32(pwmcfg
, SiFivePwmState
),
402 VMSTATE_UINT32_ARRAY(pwmcmp
, SiFivePwmState
, 4),
403 VMSTATE_END_OF_LIST()
407 static Property sifive_pwm_properties
[] = {
408 /* 0.5Ghz per spec after FSBL */
409 DEFINE_PROP_UINT64("clock-frequency", struct SiFivePwmState
,
410 freq_hz
, 500000000ULL),
411 DEFINE_PROP_END_OF_LIST(),
414 static void sifive_pwm_init(Object
*obj
)
416 SiFivePwmState
*s
= SIFIVE_PWM(obj
);
419 for (i
= 0; i
< SIFIVE_PWM_IRQS
; i
++) {
420 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &s
->irqs
[i
]);
423 memory_region_init_io(&s
->mmio
, obj
, &sifive_pwm_ops
, s
,
424 TYPE_SIFIVE_PWM
, 0x100);
425 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->mmio
);
428 static void sifive_pwm_realize(DeviceState
*dev
, Error
**errp
)
430 SiFivePwmState
*s
= SIFIVE_PWM(dev
);
432 timer_init_ns(&s
->timer
[0], QEMU_CLOCK_VIRTUAL
,
433 sifive_pwm_interrupt_0
, s
);
435 timer_init_ns(&s
->timer
[1], QEMU_CLOCK_VIRTUAL
,
436 sifive_pwm_interrupt_1
, s
);
438 timer_init_ns(&s
->timer
[2], QEMU_CLOCK_VIRTUAL
,
439 sifive_pwm_interrupt_2
, s
);
441 timer_init_ns(&s
->timer
[3], QEMU_CLOCK_VIRTUAL
,
442 sifive_pwm_interrupt_3
, s
);
445 static void sifive_pwm_class_init(ObjectClass
*klass
, void *data
)
447 DeviceClass
*dc
= DEVICE_CLASS(klass
);
449 device_class_set_legacy_reset(dc
, sifive_pwm_reset
);
450 device_class_set_props(dc
, sifive_pwm_properties
);
451 dc
->vmsd
= &vmstate_sifive_pwm
;
452 dc
->realize
= sifive_pwm_realize
;
455 static const TypeInfo sifive_pwm_info
= {
456 .name
= TYPE_SIFIVE_PWM
,
457 .parent
= TYPE_SYS_BUS_DEVICE
,
458 .instance_size
= sizeof(SiFivePwmState
),
459 .instance_init
= sifive_pwm_init
,
460 .class_init
= sifive_pwm_class_init
,
463 static void sifive_pwm_register_types(void)
465 type_register_static(&sifive_pwm_info
);
468 type_init(sifive_pwm_register_types
)