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