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