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