xref: /openbmc/qemu/hw/timer/hpet.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
149ab747fSPaolo Bonzini /*
297c61fb7SStefan Hajnoczi  *  High Precision Event Timer emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  *  Copyright (c) 2007 Alexander Graf
549ab747fSPaolo Bonzini  *  Copyright (c) 2008 IBM Corporation
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  *  Authors: Beth Kon <bkon@us.ibm.com>
849ab747fSPaolo Bonzini  *
949ab747fSPaolo Bonzini  * This library is free software; you can redistribute it and/or
1049ab747fSPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
1149ab747fSPaolo Bonzini  * License as published by the Free Software Foundation; either
1261f3c91aSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
1349ab747fSPaolo Bonzini  *
1449ab747fSPaolo Bonzini  * This library is distributed in the hope that it will be useful,
1549ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1649ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1749ab747fSPaolo Bonzini  * Lesser General Public License for more details.
1849ab747fSPaolo Bonzini  *
1949ab747fSPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
2049ab747fSPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
2149ab747fSPaolo Bonzini  *
2249ab747fSPaolo Bonzini  * *****************************************************************
2349ab747fSPaolo Bonzini  *
2449ab747fSPaolo Bonzini  * This driver attempts to emulate an HPET device in software.
2549ab747fSPaolo Bonzini  */
2649ab747fSPaolo Bonzini 
27b6a0aa05SPeter Maydell #include "qemu/osdep.h"
2864552b6bSMarkus Armbruster #include "hw/irq.h"
29da34e65cSMarkus Armbruster #include "qapi/error.h"
30d49b6836SMarkus Armbruster #include "qemu/error-report.h"
3149ab747fSPaolo Bonzini #include "qemu/timer.h"
322dbf9dd8SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
3349ab747fSPaolo Bonzini #include "hw/timer/hpet.h"
3449ab747fSPaolo Bonzini #include "hw/sysbus.h"
35bcdb9064SPhilippe Mathieu-Daudé #include "hw/rtc/mc146818rtc.h"
367ffcb73dSPhilippe Mathieu-Daudé #include "hw/rtc/mc146818rtc_regs.h"
37d6454270SMarkus Armbruster #include "migration/vmstate.h"
3849ab747fSPaolo Bonzini #include "hw/timer/i8254.h"
39858be923SPhilippe Mathieu-Daudé #include "exec/address-spaces.h"
40db1015e9SEduardo Habkost #include "qom/object.h"
41c0d0b716SDaniel Hoffman #include "trace.h"
4249ab747fSPaolo Bonzini 
4349ab747fSPaolo Bonzini #define HPET_MSI_SUPPORT        0
4449ab747fSPaolo Bonzini 
458063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(HPETState, HPET)
4602f9a6f5SHu Tao 
4749ab747fSPaolo Bonzini struct HPETState;
4849ab747fSPaolo Bonzini typedef struct HPETTimer {  /* timers */
4949ab747fSPaolo Bonzini     uint8_t tn;             /*timer number*/
5049ab747fSPaolo Bonzini     QEMUTimer *qemu_timer;
5149ab747fSPaolo Bonzini     struct HPETState *state;
5249ab747fSPaolo Bonzini     /* Memory-mapped, software visible timer registers */
5349ab747fSPaolo Bonzini     uint64_t config;        /* configuration/cap */
5449ab747fSPaolo Bonzini     uint64_t cmp;           /* comparator */
5549ab747fSPaolo Bonzini     uint64_t fsb;           /* FSB route */
5649ab747fSPaolo Bonzini     /* Hidden register state */
57242d6653SPaolo Bonzini     uint64_t cmp64;         /* comparator (extended to counter width) */
5849ab747fSPaolo Bonzini     uint64_t period;        /* Last value written to comparator */
5949ab747fSPaolo Bonzini     uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
6049ab747fSPaolo Bonzini                              * mode. Next pop will be actual timer expiration.
6149ab747fSPaolo Bonzini                              */
627c912ffbSPaolo Bonzini     uint64_t last;          /* last value armed, to avoid timer storms */
6349ab747fSPaolo Bonzini } HPETTimer;
6449ab747fSPaolo Bonzini 
65db1015e9SEduardo Habkost struct HPETState {
6602f9a6f5SHu Tao     /*< private >*/
6702f9a6f5SHu Tao     SysBusDevice parent_obj;
6802f9a6f5SHu Tao     /*< public >*/
6902f9a6f5SHu Tao 
7049ab747fSPaolo Bonzini     MemoryRegion iomem;
7149ab747fSPaolo Bonzini     uint64_t hpet_offset;
72829600a5SPavel Dovgalyuk     bool hpet_offset_saved;
7349ab747fSPaolo Bonzini     qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
7449ab747fSPaolo Bonzini     uint32_t flags;
7549ab747fSPaolo Bonzini     uint8_t rtc_irq_level;
7649ab747fSPaolo Bonzini     qemu_irq pit_enabled;
7749ab747fSPaolo Bonzini     uint8_t num_timers;
787a10ef51SLiu Ping Fan     uint32_t intcap;
7949ab747fSPaolo Bonzini     HPETTimer timer[HPET_MAX_TIMERS];
8049ab747fSPaolo Bonzini 
8149ab747fSPaolo Bonzini     /* Memory-mapped, software visible registers */
8249ab747fSPaolo Bonzini     uint64_t capability;        /* capabilities */
8349ab747fSPaolo Bonzini     uint64_t config;            /* configuration */
8449ab747fSPaolo Bonzini     uint64_t isr;               /* interrupt status reg */
8549ab747fSPaolo Bonzini     uint64_t hpet_counter;      /* main counter */
8649ab747fSPaolo Bonzini     uint8_t  hpet_id;           /* instance id */
87db1015e9SEduardo Habkost };
8849ab747fSPaolo Bonzini 
hpet_in_legacy_mode(HPETState * s)8949ab747fSPaolo Bonzini static uint32_t hpet_in_legacy_mode(HPETState *s)
9049ab747fSPaolo Bonzini {
9149ab747fSPaolo Bonzini     return s->config & HPET_CFG_LEGACY;
9249ab747fSPaolo Bonzini }
9349ab747fSPaolo Bonzini 
timer_int_route(struct HPETTimer * timer)9449ab747fSPaolo Bonzini static uint32_t timer_int_route(struct HPETTimer *timer)
9549ab747fSPaolo Bonzini {
9649ab747fSPaolo Bonzini     return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
9749ab747fSPaolo Bonzini }
9849ab747fSPaolo Bonzini 
timer_fsb_route(HPETTimer * t)9949ab747fSPaolo Bonzini static uint32_t timer_fsb_route(HPETTimer *t)
10049ab747fSPaolo Bonzini {
10149ab747fSPaolo Bonzini     return t->config & HPET_TN_FSB_ENABLE;
10249ab747fSPaolo Bonzini }
10349ab747fSPaolo Bonzini 
hpet_enabled(HPETState * s)10449ab747fSPaolo Bonzini static uint32_t hpet_enabled(HPETState *s)
10549ab747fSPaolo Bonzini {
10649ab747fSPaolo Bonzini     return s->config & HPET_CFG_ENABLE;
10749ab747fSPaolo Bonzini }
10849ab747fSPaolo Bonzini 
timer_is_periodic(HPETTimer * t)10949ab747fSPaolo Bonzini static uint32_t timer_is_periodic(HPETTimer *t)
11049ab747fSPaolo Bonzini {
11149ab747fSPaolo Bonzini     return t->config & HPET_TN_PERIODIC;
11249ab747fSPaolo Bonzini }
11349ab747fSPaolo Bonzini 
timer_enabled(HPETTimer * t)11449ab747fSPaolo Bonzini static uint32_t timer_enabled(HPETTimer *t)
11549ab747fSPaolo Bonzini {
11649ab747fSPaolo Bonzini     return t->config & HPET_TN_ENABLE;
11749ab747fSPaolo Bonzini }
11849ab747fSPaolo Bonzini 
hpet_time_after(uint64_t a,uint64_t b)11949ab747fSPaolo Bonzini static uint32_t hpet_time_after(uint64_t a, uint64_t b)
12049ab747fSPaolo Bonzini {
121d17008bcSPeter Maydell     return ((int64_t)(b - a) < 0);
12249ab747fSPaolo Bonzini }
12349ab747fSPaolo Bonzini 
ticks_to_ns(uint64_t value)12449ab747fSPaolo Bonzini static uint64_t ticks_to_ns(uint64_t value)
12549ab747fSPaolo Bonzini {
1260a4f9240SLaurent Vivier     return value * HPET_CLK_PERIOD;
12749ab747fSPaolo Bonzini }
12849ab747fSPaolo Bonzini 
ns_to_ticks(uint64_t value)12949ab747fSPaolo Bonzini static uint64_t ns_to_ticks(uint64_t value)
13049ab747fSPaolo Bonzini {
1310a4f9240SLaurent Vivier     return value / HPET_CLK_PERIOD;
13249ab747fSPaolo Bonzini }
13349ab747fSPaolo Bonzini 
hpet_fixup_reg(uint64_t new,uint64_t old,uint64_t mask)13449ab747fSPaolo Bonzini static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
13549ab747fSPaolo Bonzini {
13649ab747fSPaolo Bonzini     new &= mask;
13749ab747fSPaolo Bonzini     new |= old & ~mask;
13849ab747fSPaolo Bonzini     return new;
13949ab747fSPaolo Bonzini }
14049ab747fSPaolo Bonzini 
activating_bit(uint64_t old,uint64_t new,uint64_t mask)14149ab747fSPaolo Bonzini static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
14249ab747fSPaolo Bonzini {
14349ab747fSPaolo Bonzini     return (!(old & mask) && (new & mask));
14449ab747fSPaolo Bonzini }
14549ab747fSPaolo Bonzini 
deactivating_bit(uint64_t old,uint64_t new,uint64_t mask)14649ab747fSPaolo Bonzini static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
14749ab747fSPaolo Bonzini {
14849ab747fSPaolo Bonzini     return ((old & mask) && !(new & mask));
14949ab747fSPaolo Bonzini }
15049ab747fSPaolo Bonzini 
hpet_get_ticks(HPETState * s)15149ab747fSPaolo Bonzini static uint64_t hpet_get_ticks(HPETState *s)
15249ab747fSPaolo Bonzini {
153bc72ad67SAlex Bligh     return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset);
15449ab747fSPaolo Bonzini }
15549ab747fSPaolo Bonzini 
hpet_get_ns(HPETState * s,uint64_t tick)156242d6653SPaolo Bonzini static uint64_t hpet_get_ns(HPETState *s, uint64_t tick)
15749ab747fSPaolo Bonzini {
158242d6653SPaolo Bonzini     return ticks_to_ns(tick) - s->hpet_offset;
15949ab747fSPaolo Bonzini }
160242d6653SPaolo Bonzini 
161242d6653SPaolo Bonzini /*
162242d6653SPaolo Bonzini  * calculate next value of the general counter that matches the
163242d6653SPaolo Bonzini  * target (either entirely, or the low 32-bit only depending on
164242d6653SPaolo Bonzini  * the timer mode).
165242d6653SPaolo Bonzini  */
hpet_calculate_cmp64(HPETTimer * t,uint64_t cur_tick,uint64_t target)166242d6653SPaolo Bonzini static uint64_t hpet_calculate_cmp64(HPETTimer *t, uint64_t cur_tick, uint64_t target)
167242d6653SPaolo Bonzini {
168242d6653SPaolo Bonzini     if (t->config & HPET_TN_32BIT) {
169242d6653SPaolo Bonzini         uint64_t result = deposit64(cur_tick, 0, 32, target);
170242d6653SPaolo Bonzini         if (result < cur_tick) {
171242d6653SPaolo Bonzini             result += 0x100000000ULL;
172242d6653SPaolo Bonzini         }
173242d6653SPaolo Bonzini         return result;
174242d6653SPaolo Bonzini     } else {
175242d6653SPaolo Bonzini         return target;
176242d6653SPaolo Bonzini     }
177242d6653SPaolo Bonzini }
178242d6653SPaolo Bonzini 
hpet_next_wrap(uint64_t cur_tick)179242d6653SPaolo Bonzini static uint64_t hpet_next_wrap(uint64_t cur_tick)
180242d6653SPaolo Bonzini {
181242d6653SPaolo Bonzini     return (cur_tick | 0xffffffffU) + 1;
18249ab747fSPaolo Bonzini }
18349ab747fSPaolo Bonzini 
update_irq(struct HPETTimer * timer,int set)18449ab747fSPaolo Bonzini static void update_irq(struct HPETTimer *timer, int set)
18549ab747fSPaolo Bonzini {
18649ab747fSPaolo Bonzini     uint64_t mask;
18749ab747fSPaolo Bonzini     HPETState *s;
18849ab747fSPaolo Bonzini     int route;
18949ab747fSPaolo Bonzini 
19049ab747fSPaolo Bonzini     if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
19149ab747fSPaolo Bonzini         /* if LegacyReplacementRoute bit is set, HPET specification requires
19249ab747fSPaolo Bonzini          * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
19349ab747fSPaolo Bonzini          * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
19449ab747fSPaolo Bonzini          */
19549ab747fSPaolo Bonzini         route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
19649ab747fSPaolo Bonzini     } else {
19749ab747fSPaolo Bonzini         route = timer_int_route(timer);
19849ab747fSPaolo Bonzini     }
19949ab747fSPaolo Bonzini     s = timer->state;
20049ab747fSPaolo Bonzini     mask = 1 << timer->tn;
201f0ccf770SPaolo Bonzini 
202f0ccf770SPaolo Bonzini     if (set && (timer->config & HPET_TN_TYPE_LEVEL)) {
203f0ccf770SPaolo Bonzini         /*
204f0ccf770SPaolo Bonzini          * If HPET_TN_ENABLE bit is 0, "the timer will still operate and
205f0ccf770SPaolo Bonzini          * generate appropriate status bits, but will not cause an interrupt"
206f0ccf770SPaolo Bonzini          */
207f0ccf770SPaolo Bonzini         s->isr |= mask;
208f0ccf770SPaolo Bonzini     } else {
20949ab747fSPaolo Bonzini         s->isr &= ~mask;
21049ab747fSPaolo Bonzini     }
211f0ccf770SPaolo Bonzini 
212f0ccf770SPaolo Bonzini     if (set && timer_enabled(timer) && hpet_enabled(s)) {
213f0ccf770SPaolo Bonzini         if (timer_fsb_route(timer)) {
21442874d3aSPeter Maydell             address_space_stl_le(&address_space_memory, timer->fsb >> 32,
21542874d3aSPeter Maydell                                  timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED,
21642874d3aSPeter Maydell                                  NULL);
21749ab747fSPaolo Bonzini         } else if (timer->config & HPET_TN_TYPE_LEVEL) {
21849ab747fSPaolo Bonzini             qemu_irq_raise(s->irqs[route]);
21949ab747fSPaolo Bonzini         } else {
22049ab747fSPaolo Bonzini             qemu_irq_pulse(s->irqs[route]);
22149ab747fSPaolo Bonzini         }
222f0ccf770SPaolo Bonzini     } else {
223f0ccf770SPaolo Bonzini         if (!timer_fsb_route(timer)) {
224f0ccf770SPaolo Bonzini             qemu_irq_lower(s->irqs[route]);
225f0ccf770SPaolo Bonzini         }
226f0ccf770SPaolo Bonzini     }
22749ab747fSPaolo Bonzini }
22849ab747fSPaolo Bonzini 
hpet_pre_save(void * opaque)22944b1ff31SDr. David Alan Gilbert static int hpet_pre_save(void *opaque)
23049ab747fSPaolo Bonzini {
23149ab747fSPaolo Bonzini     HPETState *s = opaque;
23249ab747fSPaolo Bonzini 
23349ab747fSPaolo Bonzini     /* save current counter value */
234829600a5SPavel Dovgalyuk     if (hpet_enabled(s)) {
23549ab747fSPaolo Bonzini         s->hpet_counter = hpet_get_ticks(s);
236829600a5SPavel Dovgalyuk     }
23744b1ff31SDr. David Alan Gilbert 
23844b1ff31SDr. David Alan Gilbert     return 0;
23949ab747fSPaolo Bonzini }
24049ab747fSPaolo Bonzini 
hpet_pre_load(void * opaque)24149ab747fSPaolo Bonzini static int hpet_pre_load(void *opaque)
24249ab747fSPaolo Bonzini {
24349ab747fSPaolo Bonzini     HPETState *s = opaque;
24449ab747fSPaolo Bonzini 
24549ab747fSPaolo Bonzini     /* version 1 only supports 3, later versions will load the actual value */
24649ab747fSPaolo Bonzini     s->num_timers = HPET_MIN_TIMERS;
24749ab747fSPaolo Bonzini     return 0;
24849ab747fSPaolo Bonzini }
24949ab747fSPaolo Bonzini 
hpet_validate_num_timers(void * opaque,int version_id)2503f1c49e2SMichael S. Tsirkin static bool hpet_validate_num_timers(void *opaque, int version_id)
2513f1c49e2SMichael S. Tsirkin {
2523f1c49e2SMichael S. Tsirkin     HPETState *s = opaque;
2533f1c49e2SMichael S. Tsirkin 
2543f1c49e2SMichael S. Tsirkin     if (s->num_timers < HPET_MIN_TIMERS) {
2553f1c49e2SMichael S. Tsirkin         return false;
2563f1c49e2SMichael S. Tsirkin     } else if (s->num_timers > HPET_MAX_TIMERS) {
2573f1c49e2SMichael S. Tsirkin         return false;
2583f1c49e2SMichael S. Tsirkin     }
2593f1c49e2SMichael S. Tsirkin     return true;
2603f1c49e2SMichael S. Tsirkin }
2613f1c49e2SMichael S. Tsirkin 
hpet_post_load(void * opaque,int version_id)26249ab747fSPaolo Bonzini static int hpet_post_load(void *opaque, int version_id)
26349ab747fSPaolo Bonzini {
26449ab747fSPaolo Bonzini     HPETState *s = opaque;
265242d6653SPaolo Bonzini     int i;
26649ab747fSPaolo Bonzini 
267242d6653SPaolo Bonzini     for (i = 0; i < s->num_timers; i++) {
268242d6653SPaolo Bonzini         HPETTimer *t = &s->timer[i];
269242d6653SPaolo Bonzini         t->cmp64 = hpet_calculate_cmp64(t, s->hpet_counter, t->cmp);
2707c912ffbSPaolo Bonzini         t->last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - NANOSECONDS_PER_SECOND;
271242d6653SPaolo Bonzini     }
27249ab747fSPaolo Bonzini     /* Recalculate the offset between the main counter and guest time */
273829600a5SPavel Dovgalyuk     if (!s->hpet_offset_saved) {
274829600a5SPavel Dovgalyuk         s->hpet_offset = ticks_to_ns(s->hpet_counter)
275829600a5SPavel Dovgalyuk                         - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
276829600a5SPavel Dovgalyuk     }
27749ab747fSPaolo Bonzini 
27849ab747fSPaolo Bonzini     /* Push number of timers into capability returned via HPET_ID */
27949ab747fSPaolo Bonzini     s->capability &= ~HPET_ID_NUM_TIM_MASK;
28049ab747fSPaolo Bonzini     s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
28149ab747fSPaolo Bonzini     hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
28249ab747fSPaolo Bonzini 
28349ab747fSPaolo Bonzini     /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
28449ab747fSPaolo Bonzini     s->flags &= ~(1 << HPET_MSI_SUPPORT);
28549ab747fSPaolo Bonzini     if (s->timer[0].config & HPET_TN_FSB_CAP) {
28649ab747fSPaolo Bonzini         s->flags |= 1 << HPET_MSI_SUPPORT;
28749ab747fSPaolo Bonzini     }
28849ab747fSPaolo Bonzini     return 0;
28949ab747fSPaolo Bonzini }
29049ab747fSPaolo Bonzini 
hpet_offset_needed(void * opaque)291829600a5SPavel Dovgalyuk static bool hpet_offset_needed(void *opaque)
292829600a5SPavel Dovgalyuk {
293829600a5SPavel Dovgalyuk     HPETState *s = opaque;
294829600a5SPavel Dovgalyuk 
295829600a5SPavel Dovgalyuk     return hpet_enabled(s) && s->hpet_offset_saved;
296829600a5SPavel Dovgalyuk }
297829600a5SPavel Dovgalyuk 
hpet_rtc_irq_level_needed(void * opaque)29849ab747fSPaolo Bonzini static bool hpet_rtc_irq_level_needed(void *opaque)
29949ab747fSPaolo Bonzini {
30049ab747fSPaolo Bonzini     HPETState *s = opaque;
30149ab747fSPaolo Bonzini 
30249ab747fSPaolo Bonzini     return s->rtc_irq_level != 0;
30349ab747fSPaolo Bonzini }
30449ab747fSPaolo Bonzini 
30549ab747fSPaolo Bonzini static const VMStateDescription vmstate_hpet_rtc_irq_level = {
30649ab747fSPaolo Bonzini     .name = "hpet/rtc_irq_level",
30749ab747fSPaolo Bonzini     .version_id = 1,
30849ab747fSPaolo Bonzini     .minimum_version_id = 1,
3095cd8cadaSJuan Quintela     .needed = hpet_rtc_irq_level_needed,
310ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
31149ab747fSPaolo Bonzini         VMSTATE_UINT8(rtc_irq_level, HPETState),
31249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
31349ab747fSPaolo Bonzini     }
31449ab747fSPaolo Bonzini };
31549ab747fSPaolo Bonzini 
316829600a5SPavel Dovgalyuk static const VMStateDescription vmstate_hpet_offset = {
317829600a5SPavel Dovgalyuk     .name = "hpet/offset",
318829600a5SPavel Dovgalyuk     .version_id = 1,
319829600a5SPavel Dovgalyuk     .minimum_version_id = 1,
320829600a5SPavel Dovgalyuk     .needed = hpet_offset_needed,
321ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
322829600a5SPavel Dovgalyuk         VMSTATE_UINT64(hpet_offset, HPETState),
323829600a5SPavel Dovgalyuk         VMSTATE_END_OF_LIST()
324829600a5SPavel Dovgalyuk     }
325829600a5SPavel Dovgalyuk };
326829600a5SPavel Dovgalyuk 
32749ab747fSPaolo Bonzini static const VMStateDescription vmstate_hpet_timer = {
32849ab747fSPaolo Bonzini     .name = "hpet_timer",
32949ab747fSPaolo Bonzini     .version_id = 1,
33049ab747fSPaolo Bonzini     .minimum_version_id = 1,
331ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
33249ab747fSPaolo Bonzini         VMSTATE_UINT8(tn, HPETTimer),
33349ab747fSPaolo Bonzini         VMSTATE_UINT64(config, HPETTimer),
33449ab747fSPaolo Bonzini         VMSTATE_UINT64(cmp, HPETTimer),
33549ab747fSPaolo Bonzini         VMSTATE_UINT64(fsb, HPETTimer),
33649ab747fSPaolo Bonzini         VMSTATE_UINT64(period, HPETTimer),
33749ab747fSPaolo Bonzini         VMSTATE_UINT8(wrap_flag, HPETTimer),
338e720677eSPaolo Bonzini         VMSTATE_TIMER_PTR(qemu_timer, HPETTimer),
33949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
34049ab747fSPaolo Bonzini     }
34149ab747fSPaolo Bonzini };
34249ab747fSPaolo Bonzini 
34349ab747fSPaolo Bonzini static const VMStateDescription vmstate_hpet = {
34449ab747fSPaolo Bonzini     .name = "hpet",
34549ab747fSPaolo Bonzini     .version_id = 2,
34649ab747fSPaolo Bonzini     .minimum_version_id = 1,
34749ab747fSPaolo Bonzini     .pre_save = hpet_pre_save,
34849ab747fSPaolo Bonzini     .pre_load = hpet_pre_load,
34949ab747fSPaolo Bonzini     .post_load = hpet_post_load,
350ba324b3fSRichard Henderson     .fields = (const VMStateField[]) {
35149ab747fSPaolo Bonzini         VMSTATE_UINT64(config, HPETState),
35249ab747fSPaolo Bonzini         VMSTATE_UINT64(isr, HPETState),
35349ab747fSPaolo Bonzini         VMSTATE_UINT64(hpet_counter, HPETState),
35449ab747fSPaolo Bonzini         VMSTATE_UINT8_V(num_timers, HPETState, 2),
3553f1c49e2SMichael S. Tsirkin         VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers),
35649ab747fSPaolo Bonzini         VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
35749ab747fSPaolo Bonzini                                     vmstate_hpet_timer, HPETTimer),
35849ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
35949ab747fSPaolo Bonzini     },
360ba324b3fSRichard Henderson     .subsections = (const VMStateDescription * const []) {
3615cd8cadaSJuan Quintela         &vmstate_hpet_rtc_irq_level,
362829600a5SPavel Dovgalyuk         &vmstate_hpet_offset,
3635cd8cadaSJuan Quintela         NULL
36449ab747fSPaolo Bonzini     }
36549ab747fSPaolo Bonzini };
36649ab747fSPaolo Bonzini 
hpet_arm(HPETTimer * t,uint64_t tick)367242d6653SPaolo Bonzini static void hpet_arm(HPETTimer *t, uint64_t tick)
36837d2bcbcSAkihiko Odaki {
3697c912ffbSPaolo Bonzini     uint64_t ns = hpet_get_ns(t->state, tick);
3707c912ffbSPaolo Bonzini 
3717c912ffbSPaolo Bonzini     /* Clamp period to reasonable min value (1 us) */
3727c912ffbSPaolo Bonzini     if (timer_is_periodic(t) && ns - t->last < 1000) {
3737c912ffbSPaolo Bonzini         ns = t->last + 1000;
3747c912ffbSPaolo Bonzini     }
3757c912ffbSPaolo Bonzini 
3767c912ffbSPaolo Bonzini     t->last = ns;
3777c912ffbSPaolo Bonzini     timer_mod(t->qemu_timer, ns);
37837d2bcbcSAkihiko Odaki }
37937d2bcbcSAkihiko Odaki 
38049ab747fSPaolo Bonzini /*
38149ab747fSPaolo Bonzini  * timer expiration callback
38249ab747fSPaolo Bonzini  */
hpet_timer(void * opaque)38349ab747fSPaolo Bonzini static void hpet_timer(void *opaque)
38449ab747fSPaolo Bonzini {
38549ab747fSPaolo Bonzini     HPETTimer *t = opaque;
38649ab747fSPaolo Bonzini     uint64_t period = t->period;
38749ab747fSPaolo Bonzini     uint64_t cur_tick = hpet_get_ticks(t->state);
38849ab747fSPaolo Bonzini 
38949ab747fSPaolo Bonzini     if (timer_is_periodic(t) && period != 0) {
390242d6653SPaolo Bonzini         while (hpet_time_after(cur_tick, t->cmp64)) {
391242d6653SPaolo Bonzini             t->cmp64 += period;
392242d6653SPaolo Bonzini         }
39349ab747fSPaolo Bonzini         if (t->config & HPET_TN_32BIT) {
394242d6653SPaolo Bonzini             t->cmp = (uint32_t)t->cmp64;
39549ab747fSPaolo Bonzini         } else {
396242d6653SPaolo Bonzini             t->cmp = t->cmp64;
39749ab747fSPaolo Bonzini         }
398242d6653SPaolo Bonzini         hpet_arm(t, t->cmp64);
399242d6653SPaolo Bonzini     } else if (t->wrap_flag) {
40049ab747fSPaolo Bonzini         t->wrap_flag = 0;
401242d6653SPaolo Bonzini         hpet_arm(t, t->cmp64);
40249ab747fSPaolo Bonzini     }
40349ab747fSPaolo Bonzini     update_irq(t, 1);
40449ab747fSPaolo Bonzini }
40549ab747fSPaolo Bonzini 
hpet_set_timer(HPETTimer * t)40649ab747fSPaolo Bonzini static void hpet_set_timer(HPETTimer *t)
40749ab747fSPaolo Bonzini {
40849ab747fSPaolo Bonzini     uint64_t cur_tick = hpet_get_ticks(t->state);
40949ab747fSPaolo Bonzini 
41049ab747fSPaolo Bonzini     t->wrap_flag = 0;
411242d6653SPaolo Bonzini     t->cmp64 = hpet_calculate_cmp64(t, cur_tick, t->cmp);
412242d6653SPaolo Bonzini     if (t->config & HPET_TN_32BIT) {
41349ab747fSPaolo Bonzini 
41449ab747fSPaolo Bonzini         /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
41549ab747fSPaolo Bonzini          * counter wraps in addition to an interrupt with comparator match.
41649ab747fSPaolo Bonzini          */
417242d6653SPaolo Bonzini         if (!timer_is_periodic(t) && t->cmp64 > hpet_next_wrap(cur_tick)) {
41849ab747fSPaolo Bonzini             t->wrap_flag = 1;
419242d6653SPaolo Bonzini             hpet_arm(t, hpet_next_wrap(cur_tick));
420242d6653SPaolo Bonzini             return;
42149ab747fSPaolo Bonzini         }
42249ab747fSPaolo Bonzini     }
423242d6653SPaolo Bonzini     hpet_arm(t, t->cmp64);
42449ab747fSPaolo Bonzini }
42549ab747fSPaolo Bonzini 
hpet_del_timer(HPETTimer * t)42649ab747fSPaolo Bonzini static void hpet_del_timer(HPETTimer *t)
42749ab747fSPaolo Bonzini {
428f0ccf770SPaolo Bonzini     HPETState *s = t->state;
429bc72ad67SAlex Bligh     timer_del(t->qemu_timer);
430f0ccf770SPaolo Bonzini 
431f0ccf770SPaolo Bonzini     if (s->isr & (1 << t->tn)) {
432f0ccf770SPaolo Bonzini         /* For level-triggered interrupt, this leaves ISR set but lowers irq.  */
433f0ccf770SPaolo Bonzini         update_irq(t, 1);
434f0ccf770SPaolo Bonzini     }
43549ab747fSPaolo Bonzini }
43649ab747fSPaolo Bonzini 
hpet_ram_read(void * opaque,hwaddr addr,unsigned size)43749ab747fSPaolo Bonzini static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
43849ab747fSPaolo Bonzini                               unsigned size)
43949ab747fSPaolo Bonzini {
44049ab747fSPaolo Bonzini     HPETState *s = opaque;
441c2366567SPaolo Bonzini     int shift = (addr & 4) * 8;
4425895879aSPaolo Bonzini     uint64_t cur_tick;
44349ab747fSPaolo Bonzini 
444c0d0b716SDaniel Hoffman     trace_hpet_ram_read(addr);
4455895879aSPaolo Bonzini 
44649ab747fSPaolo Bonzini     /*address range of all TN regs*/
4475895879aSPaolo Bonzini     if (addr >= 0x100 && addr <= 0x3ff) {
44849ab747fSPaolo Bonzini         uint8_t timer_id = (addr - 0x100) / 0x20;
44949ab747fSPaolo Bonzini         HPETTimer *timer = &s->timer[timer_id];
45049ab747fSPaolo Bonzini 
45149ab747fSPaolo Bonzini         if (timer_id > s->num_timers) {
452c0d0b716SDaniel Hoffman             trace_hpet_timer_id_out_of_range(timer_id);
45349ab747fSPaolo Bonzini             return 0;
45449ab747fSPaolo Bonzini         }
45549ab747fSPaolo Bonzini 
456c2366567SPaolo Bonzini         switch (addr & 0x18) {
457c2366567SPaolo Bonzini         case HPET_TN_CFG: // including interrupt capabilities
458c2366567SPaolo Bonzini             return timer->config >> shift;
45949ab747fSPaolo Bonzini         case HPET_TN_CMP: // comparator register
460c2366567SPaolo Bonzini             return timer->cmp >> shift;
46149ab747fSPaolo Bonzini         case HPET_TN_ROUTE:
462c2366567SPaolo Bonzini             return timer->fsb >> shift;
46349ab747fSPaolo Bonzini         default:
464c0d0b716SDaniel Hoffman             trace_hpet_ram_read_invalid();
46549ab747fSPaolo Bonzini             break;
46649ab747fSPaolo Bonzini         }
46749ab747fSPaolo Bonzini     } else {
468c2366567SPaolo Bonzini         switch (addr & ~4) {
469c2366567SPaolo Bonzini         case HPET_ID: // including HPET_PERIOD
470c2366567SPaolo Bonzini             return s->capability >> shift;
47149ab747fSPaolo Bonzini         case HPET_CFG:
472c2366567SPaolo Bonzini             return s->config >> shift;
47349ab747fSPaolo Bonzini         case HPET_COUNTER:
47449ab747fSPaolo Bonzini             if (hpet_enabled(s)) {
47549ab747fSPaolo Bonzini                 cur_tick = hpet_get_ticks(s);
47649ab747fSPaolo Bonzini             } else {
47749ab747fSPaolo Bonzini                 cur_tick = s->hpet_counter;
47849ab747fSPaolo Bonzini             }
479c2366567SPaolo Bonzini             trace_hpet_ram_read_reading_counter(addr & 4, cur_tick);
480c2366567SPaolo Bonzini             return cur_tick >> shift;
48149ab747fSPaolo Bonzini         case HPET_STATUS:
482c2366567SPaolo Bonzini             return s->isr >> shift;
48349ab747fSPaolo Bonzini         default:
484c0d0b716SDaniel Hoffman             trace_hpet_ram_read_invalid();
48549ab747fSPaolo Bonzini             break;
48649ab747fSPaolo Bonzini         }
48749ab747fSPaolo Bonzini     }
48849ab747fSPaolo Bonzini     return 0;
48949ab747fSPaolo Bonzini }
49049ab747fSPaolo Bonzini 
hpet_ram_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)49149ab747fSPaolo Bonzini static void hpet_ram_write(void *opaque, hwaddr addr,
49249ab747fSPaolo Bonzini                            uint64_t value, unsigned size)
49349ab747fSPaolo Bonzini {
49449ab747fSPaolo Bonzini     int i;
49549ab747fSPaolo Bonzini     HPETState *s = opaque;
496c2366567SPaolo Bonzini     int shift = (addr & 4) * 8;
497c2366567SPaolo Bonzini     int len = MIN(size * 8, 64 - shift);
498ba88935bSPaolo Bonzini     uint64_t old_val, new_val, cleared;
49949ab747fSPaolo Bonzini 
500c0d0b716SDaniel Hoffman     trace_hpet_ram_write(addr, value);
50149ab747fSPaolo Bonzini 
50249ab747fSPaolo Bonzini     /*address range of all TN regs*/
5035895879aSPaolo Bonzini     if (addr >= 0x100 && addr <= 0x3ff) {
50449ab747fSPaolo Bonzini         uint8_t timer_id = (addr - 0x100) / 0x20;
50549ab747fSPaolo Bonzini         HPETTimer *timer = &s->timer[timer_id];
50649ab747fSPaolo Bonzini 
507c0d0b716SDaniel Hoffman         trace_hpet_ram_write_timer_id(timer_id);
50849ab747fSPaolo Bonzini         if (timer_id > s->num_timers) {
509c0d0b716SDaniel Hoffman             trace_hpet_timer_id_out_of_range(timer_id);
51049ab747fSPaolo Bonzini             return;
51149ab747fSPaolo Bonzini         }
512c2366567SPaolo Bonzini         switch (addr & 0x18) {
51349ab747fSPaolo Bonzini         case HPET_TN_CFG:
514c2366567SPaolo Bonzini             trace_hpet_ram_write_tn_cfg(addr & 4);
515c2366567SPaolo Bonzini             old_val = timer->config;
516c2366567SPaolo Bonzini             new_val = deposit64(old_val, shift, len, value);
517c2366567SPaolo Bonzini             new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
518f0ccf770SPaolo Bonzini             if (deactivating_bit(old_val, new_val, HPET_TN_TYPE_LEVEL)) {
519f0ccf770SPaolo Bonzini                 /*
520f0ccf770SPaolo Bonzini                  * Do this before changing timer->config; otherwise, if
521f0ccf770SPaolo Bonzini                  * HPET_TN_FSB is set, update_irq will not lower the qemu_irq.
522f0ccf770SPaolo Bonzini                  */
52349ab747fSPaolo Bonzini                 update_irq(timer, 0);
52449ab747fSPaolo Bonzini             }
525c2366567SPaolo Bonzini             timer->config = new_val;
526f0ccf770SPaolo Bonzini             if (activating_bit(old_val, new_val, HPET_TN_ENABLE)
527f0ccf770SPaolo Bonzini                 && (s->isr & (1 << timer_id))) {
528f0ccf770SPaolo Bonzini                 update_irq(timer, 1);
529f0ccf770SPaolo Bonzini             }
53049ab747fSPaolo Bonzini             if (new_val & HPET_TN_32BIT) {
53149ab747fSPaolo Bonzini                 timer->cmp = (uint32_t)timer->cmp;
53249ab747fSPaolo Bonzini                 timer->period = (uint32_t)timer->period;
53349ab747fSPaolo Bonzini             }
534f0ccf770SPaolo Bonzini             if (hpet_enabled(s)) {
53549ab747fSPaolo Bonzini                 hpet_set_timer(timer);
53649ab747fSPaolo Bonzini             }
53749ab747fSPaolo Bonzini             break;
53849ab747fSPaolo Bonzini         case HPET_TN_CMP: // comparator register
53949ab747fSPaolo Bonzini             if (timer->config & HPET_TN_32BIT) {
540c2366567SPaolo Bonzini                 /* High 32-bits are zero, leave them untouched.  */
541c2366567SPaolo Bonzini                 if (shift) {
5429eb7fad3SPaolo Bonzini                     trace_hpet_ram_write_invalid_tn_cmp();
5439eb7fad3SPaolo Bonzini                     break;
5449eb7fad3SPaolo Bonzini                 }
545c2366567SPaolo Bonzini                 len = 64;
546c2366567SPaolo Bonzini                 value = (uint32_t) value;
547c2366567SPaolo Bonzini             }
548c2366567SPaolo Bonzini             trace_hpet_ram_write_tn_cmp(addr & 4);
54949ab747fSPaolo Bonzini             if (!timer_is_periodic(timer)
55049ab747fSPaolo Bonzini                 || (timer->config & HPET_TN_SETVAL)) {
551c2366567SPaolo Bonzini                 timer->cmp = deposit64(timer->cmp, shift, len, value);
552340627ecSPaolo Bonzini             }
553340627ecSPaolo Bonzini             if (timer_is_periodic(timer)) {
554242d6653SPaolo Bonzini                 timer->period = deposit64(timer->period, shift, len, value);
55549ab747fSPaolo Bonzini             }
55649ab747fSPaolo Bonzini             timer->config &= ~HPET_TN_SETVAL;
55749ab747fSPaolo Bonzini             if (hpet_enabled(s)) {
55849ab747fSPaolo Bonzini                 hpet_set_timer(timer);
55949ab747fSPaolo Bonzini             }
56049ab747fSPaolo Bonzini             break;
56149ab747fSPaolo Bonzini         case HPET_TN_ROUTE:
562c2366567SPaolo Bonzini             timer->fsb = deposit64(timer->fsb, shift, len, value);
56349ab747fSPaolo Bonzini             break;
56449ab747fSPaolo Bonzini         default:
565c0d0b716SDaniel Hoffman             trace_hpet_ram_write_invalid();
56649ab747fSPaolo Bonzini             break;
56749ab747fSPaolo Bonzini         }
56849ab747fSPaolo Bonzini         return;
56949ab747fSPaolo Bonzini     } else {
570c2366567SPaolo Bonzini         switch (addr & ~4) {
57149ab747fSPaolo Bonzini         case HPET_ID:
57249ab747fSPaolo Bonzini             return;
57349ab747fSPaolo Bonzini         case HPET_CFG:
574c2366567SPaolo Bonzini             old_val = s->config;
575c2366567SPaolo Bonzini             new_val = deposit64(old_val, shift, len, value);
576ba88935bSPaolo Bonzini             new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
577c2366567SPaolo Bonzini             s->config = new_val;
57849ab747fSPaolo Bonzini             if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
57949ab747fSPaolo Bonzini                 /* Enable main counter and interrupt generation. */
58049ab747fSPaolo Bonzini                 s->hpet_offset =
581bc72ad67SAlex Bligh                     ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
58249ab747fSPaolo Bonzini                 for (i = 0; i < s->num_timers; i++) {
583f0ccf770SPaolo Bonzini                     if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) {
584f0ccf770SPaolo Bonzini                         update_irq(&s->timer[i], 1);
58549ab747fSPaolo Bonzini                     }
586f0ccf770SPaolo Bonzini                     hpet_set_timer(&s->timer[i]);
58749ab747fSPaolo Bonzini                 }
58849ab747fSPaolo Bonzini             } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
58949ab747fSPaolo Bonzini                 /* Halt main counter and disable interrupt generation. */
59049ab747fSPaolo Bonzini                 s->hpet_counter = hpet_get_ticks(s);
59149ab747fSPaolo Bonzini                 for (i = 0; i < s->num_timers; i++) {
59249ab747fSPaolo Bonzini                     hpet_del_timer(&s->timer[i]);
59349ab747fSPaolo Bonzini                 }
59449ab747fSPaolo Bonzini             }
59549ab747fSPaolo Bonzini             /* i8254 and RTC output pins are disabled
59649ab747fSPaolo Bonzini              * when HPET is in legacy mode */
59749ab747fSPaolo Bonzini             if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
59849ab747fSPaolo Bonzini                 qemu_set_irq(s->pit_enabled, 0);
59949ab747fSPaolo Bonzini                 qemu_irq_lower(s->irqs[0]);
60049ab747fSPaolo Bonzini                 qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
60149ab747fSPaolo Bonzini             } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
60249ab747fSPaolo Bonzini                 qemu_irq_lower(s->irqs[0]);
60349ab747fSPaolo Bonzini                 qemu_set_irq(s->pit_enabled, 1);
60449ab747fSPaolo Bonzini                 qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
60549ab747fSPaolo Bonzini             }
60649ab747fSPaolo Bonzini             break;
60749ab747fSPaolo Bonzini         case HPET_STATUS:
608c2366567SPaolo Bonzini             new_val = value << shift;
609ba88935bSPaolo Bonzini             cleared = new_val & s->isr;
61049ab747fSPaolo Bonzini             for (i = 0; i < s->num_timers; i++) {
611ba88935bSPaolo Bonzini                 if (cleared & (1 << i)) {
61249ab747fSPaolo Bonzini                     update_irq(&s->timer[i], 0);
61349ab747fSPaolo Bonzini                 }
61449ab747fSPaolo Bonzini             }
61549ab747fSPaolo Bonzini             break;
61649ab747fSPaolo Bonzini         case HPET_COUNTER:
61749ab747fSPaolo Bonzini             if (hpet_enabled(s)) {
618c0d0b716SDaniel Hoffman                 trace_hpet_ram_write_counter_write_while_enabled();
61949ab747fSPaolo Bonzini             }
620c2366567SPaolo Bonzini             s->hpet_counter = deposit64(s->hpet_counter, shift, len, value);
62149ab747fSPaolo Bonzini             break;
62249ab747fSPaolo Bonzini         default:
623c0d0b716SDaniel Hoffman             trace_hpet_ram_write_invalid();
62449ab747fSPaolo Bonzini             break;
62549ab747fSPaolo Bonzini         }
62649ab747fSPaolo Bonzini     }
62749ab747fSPaolo Bonzini }
62849ab747fSPaolo Bonzini 
62949ab747fSPaolo Bonzini static const MemoryRegionOps hpet_ram_ops = {
63049ab747fSPaolo Bonzini     .read = hpet_ram_read,
63149ab747fSPaolo Bonzini     .write = hpet_ram_write,
63249ab747fSPaolo Bonzini     .valid = {
63349ab747fSPaolo Bonzini         .min_access_size = 4,
634c2366567SPaolo Bonzini         .max_access_size = 8,
635c2366567SPaolo Bonzini     },
636c2366567SPaolo Bonzini     .impl = {
637c2366567SPaolo Bonzini         .min_access_size = 4,
638c2366567SPaolo Bonzini         .max_access_size = 8,
63949ab747fSPaolo Bonzini     },
64049ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
64149ab747fSPaolo Bonzini };
64249ab747fSPaolo Bonzini 
hpet_reset(DeviceState * d)64349ab747fSPaolo Bonzini static void hpet_reset(DeviceState *d)
64449ab747fSPaolo Bonzini {
64502f9a6f5SHu Tao     HPETState *s = HPET(d);
64602f9a6f5SHu Tao     SysBusDevice *sbd = SYS_BUS_DEVICE(d);
64749ab747fSPaolo Bonzini     int i;
64849ab747fSPaolo Bonzini 
64949ab747fSPaolo Bonzini     for (i = 0; i < s->num_timers; i++) {
65049ab747fSPaolo Bonzini         HPETTimer *timer = &s->timer[i];
65149ab747fSPaolo Bonzini 
65249ab747fSPaolo Bonzini         hpet_del_timer(timer);
65349ab747fSPaolo Bonzini         timer->cmp = ~0ULL;
65449ab747fSPaolo Bonzini         timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
65549ab747fSPaolo Bonzini         if (s->flags & (1 << HPET_MSI_SUPPORT)) {
65649ab747fSPaolo Bonzini             timer->config |= HPET_TN_FSB_CAP;
65749ab747fSPaolo Bonzini         }
6587a10ef51SLiu Ping Fan         /* advertise availability of ioapic int */
6597a10ef51SLiu Ping Fan         timer->config |=  (uint64_t)s->intcap << 32;
66049ab747fSPaolo Bonzini         timer->period = 0ULL;
66149ab747fSPaolo Bonzini         timer->wrap_flag = 0;
66249ab747fSPaolo Bonzini     }
66349ab747fSPaolo Bonzini 
66449ab747fSPaolo Bonzini     qemu_set_irq(s->pit_enabled, 1);
66549ab747fSPaolo Bonzini     s->hpet_counter = 0ULL;
66649ab747fSPaolo Bonzini     s->hpet_offset = 0ULL;
66749ab747fSPaolo Bonzini     s->config = 0ULL;
66849ab747fSPaolo Bonzini     hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
66902f9a6f5SHu Tao     hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr;
67049ab747fSPaolo Bonzini 
67149ab747fSPaolo Bonzini     /* to document that the RTC lowers its output on reset as well */
67249ab747fSPaolo Bonzini     s->rtc_irq_level = 0;
67349ab747fSPaolo Bonzini }
67449ab747fSPaolo Bonzini 
hpet_handle_legacy_irq(void * opaque,int n,int level)67549ab747fSPaolo Bonzini static void hpet_handle_legacy_irq(void *opaque, int n, int level)
67649ab747fSPaolo Bonzini {
67702f9a6f5SHu Tao     HPETState *s = HPET(opaque);
67849ab747fSPaolo Bonzini 
67949ab747fSPaolo Bonzini     if (n == HPET_LEGACY_PIT_INT) {
68049ab747fSPaolo Bonzini         if (!hpet_in_legacy_mode(s)) {
68149ab747fSPaolo Bonzini             qemu_set_irq(s->irqs[0], level);
68249ab747fSPaolo Bonzini         }
68349ab747fSPaolo Bonzini     } else {
68449ab747fSPaolo Bonzini         s->rtc_irq_level = level;
68549ab747fSPaolo Bonzini         if (!hpet_in_legacy_mode(s)) {
68649ab747fSPaolo Bonzini             qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
68749ab747fSPaolo Bonzini         }
68849ab747fSPaolo Bonzini     }
68949ab747fSPaolo Bonzini }
69049ab747fSPaolo Bonzini 
hpet_init(Object * obj)691726887efSHu Tao static void hpet_init(Object *obj)
69249ab747fSPaolo Bonzini {
693726887efSHu Tao     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
694726887efSHu Tao     HPETState *s = HPET(obj);
695726887efSHu Tao 
696726887efSHu Tao     /* HPET Area */
697a57d708dSIgor Mammedov     memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN);
698726887efSHu Tao     sysbus_init_mmio(sbd, &s->iomem);
699726887efSHu Tao }
700726887efSHu Tao 
hpet_realize(DeviceState * dev,Error ** errp)701726887efSHu Tao static void hpet_realize(DeviceState *dev, Error **errp)
702726887efSHu Tao {
703726887efSHu Tao     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
70402f9a6f5SHu Tao     HPETState *s = HPET(dev);
70549ab747fSPaolo Bonzini     int i;
70649ab747fSPaolo Bonzini     HPETTimer *timer;
70749ab747fSPaolo Bonzini 
7087a10ef51SLiu Ping Fan     if (!s->intcap) {
709d081ceddSMarkus Armbruster         warn_report("Hpet's intcap not initialized");
7107a10ef51SLiu Ping Fan     }
71149ab747fSPaolo Bonzini     if (hpet_cfg.count == UINT8_MAX) {
71249ab747fSPaolo Bonzini         /* first instance */
71349ab747fSPaolo Bonzini         hpet_cfg.count = 0;
71449ab747fSPaolo Bonzini     }
71549ab747fSPaolo Bonzini 
71649ab747fSPaolo Bonzini     if (hpet_cfg.count == 8) {
717726887efSHu Tao         error_setg(errp, "Only 8 instances of HPET is allowed");
718726887efSHu Tao         return;
71949ab747fSPaolo Bonzini     }
72049ab747fSPaolo Bonzini 
72149ab747fSPaolo Bonzini     s->hpet_id = hpet_cfg.count++;
72249ab747fSPaolo Bonzini 
72349ab747fSPaolo Bonzini     for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
724726887efSHu Tao         sysbus_init_irq(sbd, &s->irqs[i]);
72549ab747fSPaolo Bonzini     }
72649ab747fSPaolo Bonzini 
72749ab747fSPaolo Bonzini     if (s->num_timers < HPET_MIN_TIMERS) {
72849ab747fSPaolo Bonzini         s->num_timers = HPET_MIN_TIMERS;
72949ab747fSPaolo Bonzini     } else if (s->num_timers > HPET_MAX_TIMERS) {
73049ab747fSPaolo Bonzini         s->num_timers = HPET_MAX_TIMERS;
73149ab747fSPaolo Bonzini     }
73249ab747fSPaolo Bonzini     for (i = 0; i < HPET_MAX_TIMERS; i++) {
73349ab747fSPaolo Bonzini         timer = &s->timer[i];
734bc72ad67SAlex Bligh         timer->qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hpet_timer, timer);
73549ab747fSPaolo Bonzini         timer->tn = i;
73649ab747fSPaolo Bonzini         timer->state = s;
73749ab747fSPaolo Bonzini     }
73849ab747fSPaolo Bonzini 
73949ab747fSPaolo Bonzini     /* 64-bit main counter; LegacyReplacementRoute. */
74049ab747fSPaolo Bonzini     s->capability = 0x8086a001ULL;
74149ab747fSPaolo Bonzini     s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
7420a4f9240SLaurent Vivier     s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32);
74349ab747fSPaolo Bonzini 
744726887efSHu Tao     qdev_init_gpio_in(dev, hpet_handle_legacy_irq, 2);
745726887efSHu Tao     qdev_init_gpio_out(dev, &s->pit_enabled, 1);
74649ab747fSPaolo Bonzini }
74749ab747fSPaolo Bonzini 
74849ab747fSPaolo Bonzini static Property hpet_device_properties[] = {
74949ab747fSPaolo Bonzini     DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
75049ab747fSPaolo Bonzini     DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
7517a10ef51SLiu Ping Fan     DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0),
752829600a5SPavel Dovgalyuk     DEFINE_PROP_BOOL("hpet-offset-saved", HPETState, hpet_offset_saved, true),
75349ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
75449ab747fSPaolo Bonzini };
75549ab747fSPaolo Bonzini 
hpet_device_class_init(ObjectClass * klass,void * data)75649ab747fSPaolo Bonzini static void hpet_device_class_init(ObjectClass *klass, void *data)
75749ab747fSPaolo Bonzini {
75849ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
75949ab747fSPaolo Bonzini 
760726887efSHu Tao     dc->realize = hpet_realize;
761*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, hpet_reset);
76249ab747fSPaolo Bonzini     dc->vmsd = &vmstate_hpet;
7634f67d30bSMarc-André Lureau     device_class_set_props(dc, hpet_device_properties);
76449ab747fSPaolo Bonzini }
76549ab747fSPaolo Bonzini 
76649ab747fSPaolo Bonzini static const TypeInfo hpet_device_info = {
76702f9a6f5SHu Tao     .name          = TYPE_HPET,
76849ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
76949ab747fSPaolo Bonzini     .instance_size = sizeof(HPETState),
770726887efSHu Tao     .instance_init = hpet_init,
77149ab747fSPaolo Bonzini     .class_init    = hpet_device_class_init,
77249ab747fSPaolo Bonzini };
77349ab747fSPaolo Bonzini 
hpet_register_types(void)77449ab747fSPaolo Bonzini static void hpet_register_types(void)
77549ab747fSPaolo Bonzini {
77649ab747fSPaolo Bonzini     type_register_static(&hpet_device_info);
77749ab747fSPaolo Bonzini }
77849ab747fSPaolo Bonzini 
77949ab747fSPaolo Bonzini type_init(hpet_register_types)
780