xref: /openbmc/qemu/hw/timer/hpet.c (revision 14a650ec)
1 /*
2  *  High Precisition Event Timer emulation
3  *
4  *  Copyright (c) 2007 Alexander Graf
5  *  Copyright (c) 2008 IBM Corporation
6  *
7  *  Authors: Beth Kon <bkon@us.ibm.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21  *
22  * *****************************************************************
23  *
24  * This driver attempts to emulate an HPET device in software.
25  */
26 
27 #include "hw/hw.h"
28 #include "hw/i386/pc.h"
29 #include "ui/console.h"
30 #include "qemu/timer.h"
31 #include "hw/timer/hpet.h"
32 #include "hw/sysbus.h"
33 #include "hw/timer/mc146818rtc.h"
34 #include "hw/timer/i8254.h"
35 
36 //#define HPET_DEBUG
37 #ifdef HPET_DEBUG
38 #define DPRINTF printf
39 #else
40 #define DPRINTF(...)
41 #endif
42 
43 #define HPET_MSI_SUPPORT        0
44 
45 #define TYPE_HPET "hpet"
46 #define HPET(obj) OBJECT_CHECK(HPETState, (obj), TYPE_HPET)
47 
48 struct HPETState;
49 typedef struct HPETTimer {  /* timers */
50     uint8_t tn;             /*timer number*/
51     QEMUTimer *qemu_timer;
52     struct HPETState *state;
53     /* Memory-mapped, software visible timer registers */
54     uint64_t config;        /* configuration/cap */
55     uint64_t cmp;           /* comparator */
56     uint64_t fsb;           /* FSB route */
57     /* Hidden register state */
58     uint64_t period;        /* Last value written to comparator */
59     uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
60                              * mode. Next pop will be actual timer expiration.
61                              */
62 } HPETTimer;
63 
64 typedef struct HPETState {
65     /*< private >*/
66     SysBusDevice parent_obj;
67     /*< public >*/
68 
69     MemoryRegion iomem;
70     uint64_t hpet_offset;
71     qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
72     uint32_t flags;
73     uint8_t rtc_irq_level;
74     qemu_irq pit_enabled;
75     uint8_t num_timers;
76     HPETTimer timer[HPET_MAX_TIMERS];
77 
78     /* Memory-mapped, software visible registers */
79     uint64_t capability;        /* capabilities */
80     uint64_t config;            /* configuration */
81     uint64_t isr;               /* interrupt status reg */
82     uint64_t hpet_counter;      /* main counter */
83     uint8_t  hpet_id;           /* instance id */
84 } HPETState;
85 
86 static uint32_t hpet_in_legacy_mode(HPETState *s)
87 {
88     return s->config & HPET_CFG_LEGACY;
89 }
90 
91 static uint32_t timer_int_route(struct HPETTimer *timer)
92 {
93     return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
94 }
95 
96 static uint32_t timer_fsb_route(HPETTimer *t)
97 {
98     return t->config & HPET_TN_FSB_ENABLE;
99 }
100 
101 static uint32_t hpet_enabled(HPETState *s)
102 {
103     return s->config & HPET_CFG_ENABLE;
104 }
105 
106 static uint32_t timer_is_periodic(HPETTimer *t)
107 {
108     return t->config & HPET_TN_PERIODIC;
109 }
110 
111 static uint32_t timer_enabled(HPETTimer *t)
112 {
113     return t->config & HPET_TN_ENABLE;
114 }
115 
116 static uint32_t hpet_time_after(uint64_t a, uint64_t b)
117 {
118     return ((int32_t)(b) - (int32_t)(a) < 0);
119 }
120 
121 static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
122 {
123     return ((int64_t)(b) - (int64_t)(a) < 0);
124 }
125 
126 static uint64_t ticks_to_ns(uint64_t value)
127 {
128     return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
129 }
130 
131 static uint64_t ns_to_ticks(uint64_t value)
132 {
133     return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
134 }
135 
136 static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
137 {
138     new &= mask;
139     new |= old & ~mask;
140     return new;
141 }
142 
143 static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
144 {
145     return (!(old & mask) && (new & mask));
146 }
147 
148 static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
149 {
150     return ((old & mask) && !(new & mask));
151 }
152 
153 static uint64_t hpet_get_ticks(HPETState *s)
154 {
155     return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset);
156 }
157 
158 /*
159  * calculate diff between comparator value and current ticks
160  */
161 static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
162 {
163 
164     if (t->config & HPET_TN_32BIT) {
165         uint32_t diff, cmp;
166 
167         cmp = (uint32_t)t->cmp;
168         diff = cmp - (uint32_t)current;
169         diff = (int32_t)diff > 0 ? diff : (uint32_t)1;
170         return (uint64_t)diff;
171     } else {
172         uint64_t diff, cmp;
173 
174         cmp = t->cmp;
175         diff = cmp - current;
176         diff = (int64_t)diff > 0 ? diff : (uint64_t)1;
177         return diff;
178     }
179 }
180 
181 static void update_irq(struct HPETTimer *timer, int set)
182 {
183     uint64_t mask;
184     HPETState *s;
185     int route;
186 
187     if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
188         /* if LegacyReplacementRoute bit is set, HPET specification requires
189          * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
190          * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
191          */
192         route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
193     } else {
194         route = timer_int_route(timer);
195     }
196     s = timer->state;
197     mask = 1 << timer->tn;
198     if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
199         s->isr &= ~mask;
200         if (!timer_fsb_route(timer)) {
201             qemu_irq_lower(s->irqs[route]);
202         }
203     } else if (timer_fsb_route(timer)) {
204         stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
205     } else if (timer->config & HPET_TN_TYPE_LEVEL) {
206         s->isr |= mask;
207         qemu_irq_raise(s->irqs[route]);
208     } else {
209         s->isr &= ~mask;
210         qemu_irq_pulse(s->irqs[route]);
211     }
212 }
213 
214 static void hpet_pre_save(void *opaque)
215 {
216     HPETState *s = opaque;
217 
218     /* save current counter value */
219     s->hpet_counter = hpet_get_ticks(s);
220 }
221 
222 static int hpet_pre_load(void *opaque)
223 {
224     HPETState *s = opaque;
225 
226     /* version 1 only supports 3, later versions will load the actual value */
227     s->num_timers = HPET_MIN_TIMERS;
228     return 0;
229 }
230 
231 static int hpet_post_load(void *opaque, int version_id)
232 {
233     HPETState *s = opaque;
234 
235     /* Recalculate the offset between the main counter and guest time */
236     s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
237 
238     /* Push number of timers into capability returned via HPET_ID */
239     s->capability &= ~HPET_ID_NUM_TIM_MASK;
240     s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
241     hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
242 
243     /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
244     s->flags &= ~(1 << HPET_MSI_SUPPORT);
245     if (s->timer[0].config & HPET_TN_FSB_CAP) {
246         s->flags |= 1 << HPET_MSI_SUPPORT;
247     }
248     return 0;
249 }
250 
251 static bool hpet_rtc_irq_level_needed(void *opaque)
252 {
253     HPETState *s = opaque;
254 
255     return s->rtc_irq_level != 0;
256 }
257 
258 static const VMStateDescription vmstate_hpet_rtc_irq_level = {
259     .name = "hpet/rtc_irq_level",
260     .version_id = 1,
261     .minimum_version_id = 1,
262     .minimum_version_id_old = 1,
263     .fields      = (VMStateField[]) {
264         VMSTATE_UINT8(rtc_irq_level, HPETState),
265         VMSTATE_END_OF_LIST()
266     }
267 };
268 
269 static const VMStateDescription vmstate_hpet_timer = {
270     .name = "hpet_timer",
271     .version_id = 1,
272     .minimum_version_id = 1,
273     .minimum_version_id_old = 1,
274     .fields      = (VMStateField []) {
275         VMSTATE_UINT8(tn, HPETTimer),
276         VMSTATE_UINT64(config, HPETTimer),
277         VMSTATE_UINT64(cmp, HPETTimer),
278         VMSTATE_UINT64(fsb, HPETTimer),
279         VMSTATE_UINT64(period, HPETTimer),
280         VMSTATE_UINT8(wrap_flag, HPETTimer),
281         VMSTATE_TIMER(qemu_timer, HPETTimer),
282         VMSTATE_END_OF_LIST()
283     }
284 };
285 
286 static const VMStateDescription vmstate_hpet = {
287     .name = "hpet",
288     .version_id = 2,
289     .minimum_version_id = 1,
290     .minimum_version_id_old = 1,
291     .pre_save = hpet_pre_save,
292     .pre_load = hpet_pre_load,
293     .post_load = hpet_post_load,
294     .fields      = (VMStateField []) {
295         VMSTATE_UINT64(config, HPETState),
296         VMSTATE_UINT64(isr, HPETState),
297         VMSTATE_UINT64(hpet_counter, HPETState),
298         VMSTATE_UINT8_V(num_timers, HPETState, 2),
299         VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
300                                     vmstate_hpet_timer, HPETTimer),
301         VMSTATE_END_OF_LIST()
302     },
303     .subsections = (VMStateSubsection[]) {
304         {
305             .vmsd = &vmstate_hpet_rtc_irq_level,
306             .needed = hpet_rtc_irq_level_needed,
307         }, {
308             /* empty */
309         }
310     }
311 };
312 
313 /*
314  * timer expiration callback
315  */
316 static void hpet_timer(void *opaque)
317 {
318     HPETTimer *t = opaque;
319     uint64_t diff;
320 
321     uint64_t period = t->period;
322     uint64_t cur_tick = hpet_get_ticks(t->state);
323 
324     if (timer_is_periodic(t) && period != 0) {
325         if (t->config & HPET_TN_32BIT) {
326             while (hpet_time_after(cur_tick, t->cmp)) {
327                 t->cmp = (uint32_t)(t->cmp + t->period);
328             }
329         } else {
330             while (hpet_time_after64(cur_tick, t->cmp)) {
331                 t->cmp += period;
332             }
333         }
334         diff = hpet_calculate_diff(t, cur_tick);
335         timer_mod(t->qemu_timer,
336                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff));
337     } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
338         if (t->wrap_flag) {
339             diff = hpet_calculate_diff(t, cur_tick);
340             timer_mod(t->qemu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
341                            (int64_t)ticks_to_ns(diff));
342             t->wrap_flag = 0;
343         }
344     }
345     update_irq(t, 1);
346 }
347 
348 static void hpet_set_timer(HPETTimer *t)
349 {
350     uint64_t diff;
351     uint32_t wrap_diff;  /* how many ticks until we wrap? */
352     uint64_t cur_tick = hpet_get_ticks(t->state);
353 
354     /* whenever new timer is being set up, make sure wrap_flag is 0 */
355     t->wrap_flag = 0;
356     diff = hpet_calculate_diff(t, cur_tick);
357 
358     /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
359      * counter wraps in addition to an interrupt with comparator match.
360      */
361     if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
362         wrap_diff = 0xffffffff - (uint32_t)cur_tick;
363         if (wrap_diff < (uint32_t)diff) {
364             diff = wrap_diff;
365             t->wrap_flag = 1;
366         }
367     }
368     timer_mod(t->qemu_timer,
369                    qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff));
370 }
371 
372 static void hpet_del_timer(HPETTimer *t)
373 {
374     timer_del(t->qemu_timer);
375     update_irq(t, 0);
376 }
377 
378 #ifdef HPET_DEBUG
379 static uint32_t hpet_ram_readb(void *opaque, hwaddr addr)
380 {
381     printf("qemu: hpet_read b at %" PRIx64 "\n", addr);
382     return 0;
383 }
384 
385 static uint32_t hpet_ram_readw(void *opaque, hwaddr addr)
386 {
387     printf("qemu: hpet_read w at %" PRIx64 "\n", addr);
388     return 0;
389 }
390 #endif
391 
392 static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
393                               unsigned size)
394 {
395     HPETState *s = opaque;
396     uint64_t cur_tick, index;
397 
398     DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr);
399     index = addr;
400     /*address range of all TN regs*/
401     if (index >= 0x100 && index <= 0x3ff) {
402         uint8_t timer_id = (addr - 0x100) / 0x20;
403         HPETTimer *timer = &s->timer[timer_id];
404 
405         if (timer_id > s->num_timers) {
406             DPRINTF("qemu: timer id out of range\n");
407             return 0;
408         }
409 
410         switch ((addr - 0x100) % 0x20) {
411         case HPET_TN_CFG:
412             return timer->config;
413         case HPET_TN_CFG + 4: // Interrupt capabilities
414             return timer->config >> 32;
415         case HPET_TN_CMP: // comparator register
416             return timer->cmp;
417         case HPET_TN_CMP + 4:
418             return timer->cmp >> 32;
419         case HPET_TN_ROUTE:
420             return timer->fsb;
421         case HPET_TN_ROUTE + 4:
422             return timer->fsb >> 32;
423         default:
424             DPRINTF("qemu: invalid hpet_ram_readl\n");
425             break;
426         }
427     } else {
428         switch (index) {
429         case HPET_ID:
430             return s->capability;
431         case HPET_PERIOD:
432             return s->capability >> 32;
433         case HPET_CFG:
434             return s->config;
435         case HPET_CFG + 4:
436             DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n");
437             return 0;
438         case HPET_COUNTER:
439             if (hpet_enabled(s)) {
440                 cur_tick = hpet_get_ticks(s);
441             } else {
442                 cur_tick = s->hpet_counter;
443             }
444             DPRINTF("qemu: reading counter  = %" PRIx64 "\n", cur_tick);
445             return cur_tick;
446         case HPET_COUNTER + 4:
447             if (hpet_enabled(s)) {
448                 cur_tick = hpet_get_ticks(s);
449             } else {
450                 cur_tick = s->hpet_counter;
451             }
452             DPRINTF("qemu: reading counter + 4  = %" PRIx64 "\n", cur_tick);
453             return cur_tick >> 32;
454         case HPET_STATUS:
455             return s->isr;
456         default:
457             DPRINTF("qemu: invalid hpet_ram_readl\n");
458             break;
459         }
460     }
461     return 0;
462 }
463 
464 static void hpet_ram_write(void *opaque, hwaddr addr,
465                            uint64_t value, unsigned size)
466 {
467     int i;
468     HPETState *s = opaque;
469     uint64_t old_val, new_val, val, index;
470 
471     DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value);
472     index = addr;
473     old_val = hpet_ram_read(opaque, addr, 4);
474     new_val = value;
475 
476     /*address range of all TN regs*/
477     if (index >= 0x100 && index <= 0x3ff) {
478         uint8_t timer_id = (addr - 0x100) / 0x20;
479         HPETTimer *timer = &s->timer[timer_id];
480 
481         DPRINTF("qemu: hpet_ram_writel timer_id = %#x\n", timer_id);
482         if (timer_id > s->num_timers) {
483             DPRINTF("qemu: timer id out of range\n");
484             return;
485         }
486         switch ((addr - 0x100) % 0x20) {
487         case HPET_TN_CFG:
488             DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n");
489             if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
490                 update_irq(timer, 0);
491             }
492             val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
493             timer->config = (timer->config & 0xffffffff00000000ULL) | val;
494             if (new_val & HPET_TN_32BIT) {
495                 timer->cmp = (uint32_t)timer->cmp;
496                 timer->period = (uint32_t)timer->period;
497             }
498             if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
499                 hpet_set_timer(timer);
500             } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
501                 hpet_del_timer(timer);
502             }
503             break;
504         case HPET_TN_CFG + 4: // Interrupt capabilities
505             DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n");
506             break;
507         case HPET_TN_CMP: // comparator register
508             DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n");
509             if (timer->config & HPET_TN_32BIT) {
510                 new_val = (uint32_t)new_val;
511             }
512             if (!timer_is_periodic(timer)
513                 || (timer->config & HPET_TN_SETVAL)) {
514                 timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
515             }
516             if (timer_is_periodic(timer)) {
517                 /*
518                  * FIXME: Clamp period to reasonable min value?
519                  * Clamp period to reasonable max value
520                  */
521                 new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
522                 timer->period =
523                     (timer->period & 0xffffffff00000000ULL) | new_val;
524             }
525             timer->config &= ~HPET_TN_SETVAL;
526             if (hpet_enabled(s)) {
527                 hpet_set_timer(timer);
528             }
529             break;
530         case HPET_TN_CMP + 4: // comparator register high order
531             DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n");
532             if (!timer_is_periodic(timer)
533                 || (timer->config & HPET_TN_SETVAL)) {
534                 timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32;
535             } else {
536                 /*
537                  * FIXME: Clamp period to reasonable min value?
538                  * Clamp period to reasonable max value
539                  */
540                 new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
541                 timer->period =
542                     (timer->period & 0xffffffffULL) | new_val << 32;
543                 }
544                 timer->config &= ~HPET_TN_SETVAL;
545                 if (hpet_enabled(s)) {
546                     hpet_set_timer(timer);
547                 }
548                 break;
549         case HPET_TN_ROUTE:
550             timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
551             break;
552         case HPET_TN_ROUTE + 4:
553             timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
554             break;
555         default:
556             DPRINTF("qemu: invalid hpet_ram_writel\n");
557             break;
558         }
559         return;
560     } else {
561         switch (index) {
562         case HPET_ID:
563             return;
564         case HPET_CFG:
565             val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
566             s->config = (s->config & 0xffffffff00000000ULL) | val;
567             if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
568                 /* Enable main counter and interrupt generation. */
569                 s->hpet_offset =
570                     ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
571                 for (i = 0; i < s->num_timers; i++) {
572                     if ((&s->timer[i])->cmp != ~0ULL) {
573                         hpet_set_timer(&s->timer[i]);
574                     }
575                 }
576             } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
577                 /* Halt main counter and disable interrupt generation. */
578                 s->hpet_counter = hpet_get_ticks(s);
579                 for (i = 0; i < s->num_timers; i++) {
580                     hpet_del_timer(&s->timer[i]);
581                 }
582             }
583             /* i8254 and RTC output pins are disabled
584              * when HPET is in legacy mode */
585             if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
586                 qemu_set_irq(s->pit_enabled, 0);
587                 qemu_irq_lower(s->irqs[0]);
588                 qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
589             } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
590                 qemu_irq_lower(s->irqs[0]);
591                 qemu_set_irq(s->pit_enabled, 1);
592                 qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
593             }
594             break;
595         case HPET_CFG + 4:
596             DPRINTF("qemu: invalid HPET_CFG+4 write\n");
597             break;
598         case HPET_STATUS:
599             val = new_val & s->isr;
600             for (i = 0; i < s->num_timers; i++) {
601                 if (val & (1 << i)) {
602                     update_irq(&s->timer[i], 0);
603                 }
604             }
605             break;
606         case HPET_COUNTER:
607             if (hpet_enabled(s)) {
608                 DPRINTF("qemu: Writing counter while HPET enabled!\n");
609             }
610             s->hpet_counter =
611                 (s->hpet_counter & 0xffffffff00000000ULL) | value;
612             DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n",
613                     value, s->hpet_counter);
614             break;
615         case HPET_COUNTER + 4:
616             if (hpet_enabled(s)) {
617                 DPRINTF("qemu: Writing counter while HPET enabled!\n");
618             }
619             s->hpet_counter =
620                 (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32);
621             DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n",
622                     value, s->hpet_counter);
623             break;
624         default:
625             DPRINTF("qemu: invalid hpet_ram_writel\n");
626             break;
627         }
628     }
629 }
630 
631 static const MemoryRegionOps hpet_ram_ops = {
632     .read = hpet_ram_read,
633     .write = hpet_ram_write,
634     .valid = {
635         .min_access_size = 4,
636         .max_access_size = 4,
637     },
638     .endianness = DEVICE_NATIVE_ENDIAN,
639 };
640 
641 static void hpet_reset(DeviceState *d)
642 {
643     HPETState *s = HPET(d);
644     SysBusDevice *sbd = SYS_BUS_DEVICE(d);
645     int i;
646 
647     for (i = 0; i < s->num_timers; i++) {
648         HPETTimer *timer = &s->timer[i];
649 
650         hpet_del_timer(timer);
651         timer->cmp = ~0ULL;
652         timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
653         if (s->flags & (1 << HPET_MSI_SUPPORT)) {
654             timer->config |= HPET_TN_FSB_CAP;
655         }
656         /* advertise availability of ioapic inti2 */
657         timer->config |=  0x00000004ULL << 32;
658         timer->period = 0ULL;
659         timer->wrap_flag = 0;
660     }
661 
662     qemu_set_irq(s->pit_enabled, 1);
663     s->hpet_counter = 0ULL;
664     s->hpet_offset = 0ULL;
665     s->config = 0ULL;
666     hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
667     hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr;
668 
669     /* to document that the RTC lowers its output on reset as well */
670     s->rtc_irq_level = 0;
671 }
672 
673 static void hpet_handle_legacy_irq(void *opaque, int n, int level)
674 {
675     HPETState *s = HPET(opaque);
676 
677     if (n == HPET_LEGACY_PIT_INT) {
678         if (!hpet_in_legacy_mode(s)) {
679             qemu_set_irq(s->irqs[0], level);
680         }
681     } else {
682         s->rtc_irq_level = level;
683         if (!hpet_in_legacy_mode(s)) {
684             qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
685         }
686     }
687 }
688 
689 static void hpet_init(Object *obj)
690 {
691     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
692     HPETState *s = HPET(obj);
693 
694     /* HPET Area */
695     memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", 0x400);
696     sysbus_init_mmio(sbd, &s->iomem);
697 }
698 
699 static void hpet_realize(DeviceState *dev, Error **errp)
700 {
701     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
702     HPETState *s = HPET(dev);
703     int i;
704     HPETTimer *timer;
705 
706     if (hpet_cfg.count == UINT8_MAX) {
707         /* first instance */
708         hpet_cfg.count = 0;
709     }
710 
711     if (hpet_cfg.count == 8) {
712         error_setg(errp, "Only 8 instances of HPET is allowed");
713         return;
714     }
715 
716     s->hpet_id = hpet_cfg.count++;
717 
718     for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
719         sysbus_init_irq(sbd, &s->irqs[i]);
720     }
721 
722     if (s->num_timers < HPET_MIN_TIMERS) {
723         s->num_timers = HPET_MIN_TIMERS;
724     } else if (s->num_timers > HPET_MAX_TIMERS) {
725         s->num_timers = HPET_MAX_TIMERS;
726     }
727     for (i = 0; i < HPET_MAX_TIMERS; i++) {
728         timer = &s->timer[i];
729         timer->qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hpet_timer, timer);
730         timer->tn = i;
731         timer->state = s;
732     }
733 
734     /* 64-bit main counter; LegacyReplacementRoute. */
735     s->capability = 0x8086a001ULL;
736     s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
737     s->capability |= ((HPET_CLK_PERIOD) << 32);
738 
739     qdev_init_gpio_in(dev, hpet_handle_legacy_irq, 2);
740     qdev_init_gpio_out(dev, &s->pit_enabled, 1);
741 }
742 
743 static Property hpet_device_properties[] = {
744     DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
745     DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
746     DEFINE_PROP_END_OF_LIST(),
747 };
748 
749 static void hpet_device_class_init(ObjectClass *klass, void *data)
750 {
751     DeviceClass *dc = DEVICE_CLASS(klass);
752 
753     dc->realize = hpet_realize;
754     dc->no_user = 1;
755     dc->reset = hpet_reset;
756     dc->vmsd = &vmstate_hpet;
757     dc->props = hpet_device_properties;
758 }
759 
760 bool hpet_find(void)
761 {
762     return object_resolve_path_type("", TYPE_HPET, NULL);
763 }
764 
765 static const TypeInfo hpet_device_info = {
766     .name          = TYPE_HPET,
767     .parent        = TYPE_SYS_BUS_DEVICE,
768     .instance_size = sizeof(HPETState),
769     .instance_init = hpet_init,
770     .class_init    = hpet_device_class_init,
771 };
772 
773 static void hpet_register_types(void)
774 {
775     type_register_static(&hpet_device_info);
776 }
777 
778 type_init(hpet_register_types)
779