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