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