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