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