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