2 * Nuvoton NPCM7xx Timer Controller
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"
20 #include "hw/misc/npcm7xx_clk.h"
21 #include "hw/timer/npcm7xx_timer.h"
22 #include "migration/vmstate.h"
23 #include "qemu/bitops.h"
24 #include "qemu/error-report.h"
26 #include "qemu/module.h"
27 #include "qemu/timer.h"
28 #include "qemu/units.h"
31 /* 32-bit register indices. */
32 enum NPCM7xxTimerRegisters
{
47 NPCM7XX_TIMER_TCSR4
= 0x0040 / sizeof(uint32_t),
48 NPCM7XX_TIMER_TICR4
= 0x0048 / sizeof(uint32_t),
49 NPCM7XX_TIMER_TDR4
= 0x0050 / sizeof(uint32_t),
50 NPCM7XX_TIMER_REGS_END
,
53 /* Register field definitions. */
54 #define NPCM7XX_TCSR_CEN BIT(30)
55 #define NPCM7XX_TCSR_IE BIT(29)
56 #define NPCM7XX_TCSR_PERIODIC BIT(27)
57 #define NPCM7XX_TCSR_CRST BIT(26)
58 #define NPCM7XX_TCSR_CACT BIT(25)
59 #define NPCM7XX_TCSR_RSVD 0x01ffff00
60 #define NPCM7XX_TCSR_PRESCALE_START 0
61 #define NPCM7XX_TCSR_PRESCALE_LEN 8
64 * Returns the index of timer in the tc->timer array. This can be used to
65 * locate the registers that belong to this timer.
67 static int npcm7xx_timer_index(NPCM7xxTimerCtrlState
*tc
, NPCM7xxTimer
*timer
)
69 int index
= timer
- tc
->timer
;
71 g_assert(index
>= 0 && index
< NPCM7XX_TIMERS_PER_CTRL
);
76 /* Return the value by which to divide the reference clock rate. */
77 static uint32_t npcm7xx_tcsr_prescaler(uint32_t tcsr
)
79 return extract32(tcsr
, NPCM7XX_TCSR_PRESCALE_START
,
80 NPCM7XX_TCSR_PRESCALE_LEN
) + 1;
83 /* Convert a timer cycle count to a time interval in nanoseconds. */
84 static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer
*t
, uint32_t count
)
88 ns
*= NANOSECONDS_PER_SECOND
/ NPCM7XX_TIMER_REF_HZ
;
89 ns
*= npcm7xx_tcsr_prescaler(t
->tcsr
);
94 /* Convert a time interval in nanoseconds to a timer cycle count. */
95 static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer
*t
, int64_t ns
)
99 count
= ns
/ (NANOSECONDS_PER_SECOND
/ NPCM7XX_TIMER_REF_HZ
);
100 count
/= npcm7xx_tcsr_prescaler(t
->tcsr
);
106 * Raise the interrupt line if there's a pending interrupt and interrupts are
107 * enabled for this timer. If not, lower it.
109 static void npcm7xx_timer_check_interrupt(NPCM7xxTimer
*t
)
111 NPCM7xxTimerCtrlState
*tc
= t
->ctrl
;
112 int index
= npcm7xx_timer_index(tc
, t
);
113 bool pending
= (t
->tcsr
& NPCM7XX_TCSR_IE
) && (tc
->tisr
& BIT(index
));
115 qemu_set_irq(t
->irq
, pending
);
116 trace_npcm7xx_timer_irq(DEVICE(tc
)->canonical_path
, index
, pending
);
119 /* Start or resume the timer. */
120 static void npcm7xx_timer_start(NPCM7xxTimer
*t
)
124 now
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
125 t
->expires_ns
= now
+ t
->remaining_ns
;
126 timer_mod(&t
->qtimer
, t
->expires_ns
);
130 * Called when the counter reaches zero. Sets the interrupt flag, and either
131 * restarts or disables the timer.
133 static void npcm7xx_timer_reached_zero(NPCM7xxTimer
*t
)
135 NPCM7xxTimerCtrlState
*tc
= t
->ctrl
;
136 int index
= npcm7xx_timer_index(tc
, t
);
138 tc
->tisr
|= BIT(index
);
140 if (t
->tcsr
& NPCM7XX_TCSR_PERIODIC
) {
141 t
->remaining_ns
= npcm7xx_timer_count_to_ns(t
, t
->ticr
);
142 if (t
->tcsr
& NPCM7XX_TCSR_CEN
) {
143 npcm7xx_timer_start(t
);
146 t
->tcsr
&= ~(NPCM7XX_TCSR_CEN
| NPCM7XX_TCSR_CACT
);
149 npcm7xx_timer_check_interrupt(t
);
152 /* Stop counting. Record the time remaining so we can continue later. */
153 static void npcm7xx_timer_pause(NPCM7xxTimer
*t
)
157 timer_del(&t
->qtimer
);
158 now
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
159 t
->remaining_ns
= t
->expires_ns
- now
;
163 * Restart the timer from its initial value. If the timer was enabled and stays
164 * enabled, adjust the QEMU timer according to the new count. If the timer is
165 * transitioning from disabled to enabled, the caller is expected to start the
168 static void npcm7xx_timer_restart(NPCM7xxTimer
*t
, uint32_t old_tcsr
)
170 t
->remaining_ns
= npcm7xx_timer_count_to_ns(t
, t
->ticr
);
172 if (old_tcsr
& t
->tcsr
& NPCM7XX_TCSR_CEN
) {
173 npcm7xx_timer_start(t
);
177 /* Register read and write handlers */
179 static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer
*t
)
181 if (t
->tcsr
& NPCM7XX_TCSR_CEN
) {
182 int64_t now
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
);
184 return npcm7xx_timer_ns_to_count(t
, t
->expires_ns
- now
);
187 return npcm7xx_timer_ns_to_count(t
, t
->remaining_ns
);
190 static void npcm7xx_timer_write_tcsr(NPCM7xxTimer
*t
, uint32_t new_tcsr
)
192 uint32_t old_tcsr
= t
->tcsr
;
195 if (new_tcsr
& NPCM7XX_TCSR_RSVD
) {
196 qemu_log_mask(LOG_GUEST_ERROR
, "%s: reserved bits in 0x%08x ignored\n",
198 new_tcsr
&= ~NPCM7XX_TCSR_RSVD
;
200 if (new_tcsr
& NPCM7XX_TCSR_CACT
) {
201 qemu_log_mask(LOG_GUEST_ERROR
, "%s: read-only bits in 0x%08x ignored\n",
203 new_tcsr
&= ~NPCM7XX_TCSR_CACT
;
205 if ((new_tcsr
& NPCM7XX_TCSR_CRST
) && (new_tcsr
& NPCM7XX_TCSR_CEN
)) {
206 qemu_log_mask(LOG_GUEST_ERROR
,
207 "%s: both CRST and CEN set; ignoring CEN.\n",
209 new_tcsr
&= ~NPCM7XX_TCSR_CEN
;
212 /* Calculate the value of TDR before potentially changing the prescaler. */
213 tdr
= npcm7xx_timer_read_tdr(t
);
215 t
->tcsr
= (t
->tcsr
& NPCM7XX_TCSR_CACT
) | new_tcsr
;
217 if (npcm7xx_tcsr_prescaler(old_tcsr
) != npcm7xx_tcsr_prescaler(new_tcsr
)) {
218 /* Recalculate time remaining based on the current TDR value. */
219 t
->remaining_ns
= npcm7xx_timer_count_to_ns(t
, tdr
);
220 if (old_tcsr
& t
->tcsr
& NPCM7XX_TCSR_CEN
) {
221 npcm7xx_timer_start(t
);
225 if ((old_tcsr
^ new_tcsr
) & NPCM7XX_TCSR_IE
) {
226 npcm7xx_timer_check_interrupt(t
);
228 if (new_tcsr
& NPCM7XX_TCSR_CRST
) {
229 npcm7xx_timer_restart(t
, old_tcsr
);
230 t
->tcsr
&= ~NPCM7XX_TCSR_CRST
;
232 if ((old_tcsr
^ new_tcsr
) & NPCM7XX_TCSR_CEN
) {
233 if (new_tcsr
& NPCM7XX_TCSR_CEN
) {
234 t
->tcsr
|= NPCM7XX_TCSR_CACT
;
235 npcm7xx_timer_start(t
);
237 t
->tcsr
&= ~NPCM7XX_TCSR_CACT
;
238 npcm7xx_timer_pause(t
);
239 if (t
->remaining_ns
<= 0) {
240 npcm7xx_timer_reached_zero(t
);
246 static void npcm7xx_timer_write_ticr(NPCM7xxTimer
*t
, uint32_t new_ticr
)
250 npcm7xx_timer_restart(t
, t
->tcsr
);
253 static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState
*s
, uint32_t value
)
258 for (i
= 0; i
< ARRAY_SIZE(s
->timer
); i
++) {
259 if (value
& (1U << i
)) {
260 npcm7xx_timer_check_interrupt(&s
->timer
[i
]);
265 static hwaddr
npcm7xx_tcsr_index(hwaddr reg
)
268 case NPCM7XX_TIMER_TCSR0
:
270 case NPCM7XX_TIMER_TCSR1
:
272 case NPCM7XX_TIMER_TCSR2
:
274 case NPCM7XX_TIMER_TCSR3
:
276 case NPCM7XX_TIMER_TCSR4
:
279 g_assert_not_reached();
283 static hwaddr
npcm7xx_ticr_index(hwaddr reg
)
286 case NPCM7XX_TIMER_TICR0
:
288 case NPCM7XX_TIMER_TICR1
:
290 case NPCM7XX_TIMER_TICR2
:
292 case NPCM7XX_TIMER_TICR3
:
294 case NPCM7XX_TIMER_TICR4
:
297 g_assert_not_reached();
301 static hwaddr
npcm7xx_tdr_index(hwaddr reg
)
304 case NPCM7XX_TIMER_TDR0
:
306 case NPCM7XX_TIMER_TDR1
:
308 case NPCM7XX_TIMER_TDR2
:
310 case NPCM7XX_TIMER_TDR3
:
312 case NPCM7XX_TIMER_TDR4
:
315 g_assert_not_reached();
319 static uint64_t npcm7xx_timer_read(void *opaque
, hwaddr offset
, unsigned size
)
321 NPCM7xxTimerCtrlState
*s
= opaque
;
325 reg
= offset
/ sizeof(uint32_t);
327 case NPCM7XX_TIMER_TCSR0
:
328 case NPCM7XX_TIMER_TCSR1
:
329 case NPCM7XX_TIMER_TCSR2
:
330 case NPCM7XX_TIMER_TCSR3
:
331 case NPCM7XX_TIMER_TCSR4
:
332 value
= s
->timer
[npcm7xx_tcsr_index(reg
)].tcsr
;
335 case NPCM7XX_TIMER_TICR0
:
336 case NPCM7XX_TIMER_TICR1
:
337 case NPCM7XX_TIMER_TICR2
:
338 case NPCM7XX_TIMER_TICR3
:
339 case NPCM7XX_TIMER_TICR4
:
340 value
= s
->timer
[npcm7xx_ticr_index(reg
)].ticr
;
343 case NPCM7XX_TIMER_TDR0
:
344 case NPCM7XX_TIMER_TDR1
:
345 case NPCM7XX_TIMER_TDR2
:
346 case NPCM7XX_TIMER_TDR3
:
347 case NPCM7XX_TIMER_TDR4
:
348 value
= npcm7xx_timer_read_tdr(&s
->timer
[npcm7xx_tdr_index(reg
)]);
351 case NPCM7XX_TIMER_TISR
:
355 case NPCM7XX_TIMER_WTCR
:
360 qemu_log_mask(LOG_GUEST_ERROR
,
361 "%s: invalid offset 0x%04" HWADDR_PRIx
"\n",
366 trace_npcm7xx_timer_read(DEVICE(s
)->canonical_path
, offset
, value
);
371 static void npcm7xx_timer_write(void *opaque
, hwaddr offset
,
372 uint64_t v
, unsigned size
)
374 uint32_t reg
= offset
/ sizeof(uint32_t);
375 NPCM7xxTimerCtrlState
*s
= opaque
;
378 trace_npcm7xx_timer_write(DEVICE(s
)->canonical_path
, offset
, value
);
381 case NPCM7XX_TIMER_TCSR0
:
382 case NPCM7XX_TIMER_TCSR1
:
383 case NPCM7XX_TIMER_TCSR2
:
384 case NPCM7XX_TIMER_TCSR3
:
385 case NPCM7XX_TIMER_TCSR4
:
386 npcm7xx_timer_write_tcsr(&s
->timer
[npcm7xx_tcsr_index(reg
)], value
);
389 case NPCM7XX_TIMER_TICR0
:
390 case NPCM7XX_TIMER_TICR1
:
391 case NPCM7XX_TIMER_TICR2
:
392 case NPCM7XX_TIMER_TICR3
:
393 case NPCM7XX_TIMER_TICR4
:
394 npcm7xx_timer_write_ticr(&s
->timer
[npcm7xx_ticr_index(reg
)], value
);
397 case NPCM7XX_TIMER_TDR0
:
398 case NPCM7XX_TIMER_TDR1
:
399 case NPCM7XX_TIMER_TDR2
:
400 case NPCM7XX_TIMER_TDR3
:
401 case NPCM7XX_TIMER_TDR4
:
402 qemu_log_mask(LOG_GUEST_ERROR
,
403 "%s: register @ 0x%04" HWADDR_PRIx
" is read-only\n",
407 case NPCM7XX_TIMER_TISR
:
408 npcm7xx_timer_write_tisr(s
, value
);
411 case NPCM7XX_TIMER_WTCR
:
412 qemu_log_mask(LOG_UNIMP
, "%s: WTCR write not implemented: 0x%08x\n",
417 qemu_log_mask(LOG_GUEST_ERROR
,
418 "%s: invalid offset 0x%04" HWADDR_PRIx
"\n",
422 static const struct MemoryRegionOps npcm7xx_timer_ops
= {
423 .read
= npcm7xx_timer_read
,
424 .write
= npcm7xx_timer_write
,
425 .endianness
= DEVICE_LITTLE_ENDIAN
,
427 .min_access_size
= 4,
428 .max_access_size
= 4,
433 /* Called when the QEMU timer expires. */
434 static void npcm7xx_timer_expired(void *opaque
)
436 NPCM7xxTimer
*t
= opaque
;
438 if (t
->tcsr
& NPCM7XX_TCSR_CEN
) {
439 npcm7xx_timer_reached_zero(t
);
443 static void npcm7xx_timer_enter_reset(Object
*obj
, ResetType type
)
445 NPCM7xxTimerCtrlState
*s
= NPCM7XX_TIMER(obj
);
448 for (i
= 0; i
< NPCM7XX_TIMERS_PER_CTRL
; i
++) {
449 NPCM7xxTimer
*t
= &s
->timer
[i
];
451 timer_del(&t
->qtimer
);
454 t
->tcsr
= 0x00000005;
455 t
->ticr
= 0x00000000;
458 s
->tisr
= 0x00000000;
459 s
->wtcr
= 0x00000400;
462 static void npcm7xx_timer_hold_reset(Object
*obj
)
464 NPCM7xxTimerCtrlState
*s
= NPCM7XX_TIMER(obj
);
467 for (i
= 0; i
< NPCM7XX_TIMERS_PER_CTRL
; i
++) {
468 qemu_irq_lower(s
->timer
[i
].irq
);
472 static void npcm7xx_timer_realize(DeviceState
*dev
, Error
**errp
)
474 NPCM7xxTimerCtrlState
*s
= NPCM7XX_TIMER(dev
);
475 SysBusDevice
*sbd
= &s
->parent
;
478 for (i
= 0; i
< NPCM7XX_TIMERS_PER_CTRL
; i
++) {
479 NPCM7xxTimer
*t
= &s
->timer
[i
];
481 timer_init_ns(&t
->qtimer
, QEMU_CLOCK_VIRTUAL
, npcm7xx_timer_expired
, t
);
482 sysbus_init_irq(sbd
, &t
->irq
);
485 memory_region_init_io(&s
->iomem
, OBJECT(s
), &npcm7xx_timer_ops
, s
,
486 TYPE_NPCM7XX_TIMER
, 4 * KiB
);
487 sysbus_init_mmio(sbd
, &s
->iomem
);
490 static const VMStateDescription vmstate_npcm7xx_timer
= {
491 .name
= "npcm7xx-timer",
493 .minimum_version_id
= 0,
494 .fields
= (VMStateField
[]) {
495 VMSTATE_TIMER(qtimer
, NPCM7xxTimer
),
496 VMSTATE_INT64(expires_ns
, NPCM7xxTimer
),
497 VMSTATE_INT64(remaining_ns
, NPCM7xxTimer
),
498 VMSTATE_UINT32(tcsr
, NPCM7xxTimer
),
499 VMSTATE_UINT32(ticr
, NPCM7xxTimer
),
500 VMSTATE_END_OF_LIST(),
504 static const VMStateDescription vmstate_npcm7xx_timer_ctrl
= {
505 .name
= "npcm7xx-timer-ctrl",
507 .minimum_version_id
= 0,
508 .fields
= (VMStateField
[]) {
509 VMSTATE_UINT32(tisr
, NPCM7xxTimerCtrlState
),
510 VMSTATE_UINT32(wtcr
, NPCM7xxTimerCtrlState
),
511 VMSTATE_STRUCT_ARRAY(timer
, NPCM7xxTimerCtrlState
,
512 NPCM7XX_TIMERS_PER_CTRL
, 0, vmstate_npcm7xx_timer
,
514 VMSTATE_END_OF_LIST(),
518 static void npcm7xx_timer_class_init(ObjectClass
*klass
, void *data
)
520 ResettableClass
*rc
= RESETTABLE_CLASS(klass
);
521 DeviceClass
*dc
= DEVICE_CLASS(klass
);
523 QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END
> NPCM7XX_TIMER_NR_REGS
);
525 dc
->desc
= "NPCM7xx Timer Controller";
526 dc
->realize
= npcm7xx_timer_realize
;
527 dc
->vmsd
= &vmstate_npcm7xx_timer_ctrl
;
528 rc
->phases
.enter
= npcm7xx_timer_enter_reset
;
529 rc
->phases
.hold
= npcm7xx_timer_hold_reset
;
532 static const TypeInfo npcm7xx_timer_info
= {
533 .name
= TYPE_NPCM7XX_TIMER
,
534 .parent
= TYPE_SYS_BUS_DEVICE
,
535 .instance_size
= sizeof(NPCM7xxTimerCtrlState
),
536 .class_init
= npcm7xx_timer_class_init
,
539 static void npcm7xx_timer_register_type(void)
541 type_register_static(&npcm7xx_timer_info
);
543 type_init(npcm7xx_timer_register_type
);