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