qemu/hw/i8254.c
<<
>>
Prefs
   1/*
   2 * QEMU 8253/8254 interval timer emulation
   3 *
   4 * Copyright (c) 2003-2004 Fabrice Bellard
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "hw.h"
  25#include "pc.h"
  26#include "isa.h"
  27#include "qemu/timer.h"
  28#include "i8254.h"
  29#include "i8254_internal.h"
  30
  31//#define DEBUG_PIT
  32
  33#define RW_STATE_LSB 1
  34#define RW_STATE_MSB 2
  35#define RW_STATE_WORD0 3
  36#define RW_STATE_WORD1 4
  37
  38static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
  39
  40static int pit_get_count(PITChannelState *s)
  41{
  42    uint64_t d;
  43    int counter;
  44
  45    d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
  46                 get_ticks_per_sec());
  47    switch(s->mode) {
  48    case 0:
  49    case 1:
  50    case 4:
  51    case 5:
  52        counter = (s->count - d) & 0xffff;
  53        break;
  54    case 3:
  55        /* XXX: may be incorrect for odd counts */
  56        counter = s->count - ((2 * d) % s->count);
  57        break;
  58    default:
  59        counter = s->count - (d % s->count);
  60        break;
  61    }
  62    return counter;
  63}
  64
  65/* val must be 0 or 1 */
  66static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc,
  67                                 int val)
  68{
  69    switch (sc->mode) {
  70    default:
  71    case 0:
  72    case 4:
  73        /* XXX: just disable/enable counting */
  74        break;
  75    case 1:
  76    case 5:
  77        if (sc->gate < val) {
  78            /* restart counting on rising edge */
  79            sc->count_load_time = qemu_get_clock_ns(vm_clock);
  80            pit_irq_timer_update(sc, sc->count_load_time);
  81        }
  82        break;
  83    case 2:
  84    case 3:
  85        if (sc->gate < val) {
  86            /* restart counting on rising edge */
  87            sc->count_load_time = qemu_get_clock_ns(vm_clock);
  88            pit_irq_timer_update(sc, sc->count_load_time);
  89        }
  90        /* XXX: disable/enable counting */
  91        break;
  92    }
  93    sc->gate = val;
  94}
  95
  96static inline void pit_load_count(PITChannelState *s, int val)
  97{
  98    if (val == 0)
  99        val = 0x10000;
 100    s->count_load_time = qemu_get_clock_ns(vm_clock);
 101    s->count = val;
 102    pit_irq_timer_update(s, s->count_load_time);
 103}
 104
 105/* if already latched, do not latch again */
 106static void pit_latch_count(PITChannelState *s)
 107{
 108    if (!s->count_latched) {
 109        s->latched_count = pit_get_count(s);
 110        s->count_latched = s->rw_mode;
 111    }
 112}
 113
 114static void pit_ioport_write(void *opaque, hwaddr addr,
 115                             uint64_t val, unsigned size)
 116{
 117    PITCommonState *pit = opaque;
 118    int channel, access;
 119    PITChannelState *s;
 120
 121    addr &= 3;
 122    if (addr == 3) {
 123        channel = val >> 6;
 124        if (channel == 3) {
 125            /* read back command */
 126            for(channel = 0; channel < 3; channel++) {
 127                s = &pit->channels[channel];
 128                if (val & (2 << channel)) {
 129                    if (!(val & 0x20)) {
 130                        pit_latch_count(s);
 131                    }
 132                    if (!(val & 0x10) && !s->status_latched) {
 133                        /* status latch */
 134                        /* XXX: add BCD and null count */
 135                        s->status =
 136                            (pit_get_out(s,
 137                                         qemu_get_clock_ns(vm_clock)) << 7) |
 138                            (s->rw_mode << 4) |
 139                            (s->mode << 1) |
 140                            s->bcd;
 141                        s->status_latched = 1;
 142                    }
 143                }
 144            }
 145        } else {
 146            s = &pit->channels[channel];
 147            access = (val >> 4) & 3;
 148            if (access == 0) {
 149                pit_latch_count(s);
 150            } else {
 151                s->rw_mode = access;
 152                s->read_state = access;
 153                s->write_state = access;
 154
 155                s->mode = (val >> 1) & 7;
 156                s->bcd = val & 1;
 157                /* XXX: update irq timer ? */
 158            }
 159        }
 160    } else {
 161        s = &pit->channels[addr];
 162        switch(s->write_state) {
 163        default:
 164        case RW_STATE_LSB:
 165            pit_load_count(s, val);
 166            break;
 167        case RW_STATE_MSB:
 168            pit_load_count(s, val << 8);
 169            break;
 170        case RW_STATE_WORD0:
 171            s->write_latch = val;
 172            s->write_state = RW_STATE_WORD1;
 173            break;
 174        case RW_STATE_WORD1:
 175            pit_load_count(s, s->write_latch | (val << 8));
 176            s->write_state = RW_STATE_WORD0;
 177            break;
 178        }
 179    }
 180}
 181
 182static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
 183                                unsigned size)
 184{
 185    PITCommonState *pit = opaque;
 186    int ret, count;
 187    PITChannelState *s;
 188
 189    addr &= 3;
 190    s = &pit->channels[addr];
 191    if (s->status_latched) {
 192        s->status_latched = 0;
 193        ret = s->status;
 194    } else if (s->count_latched) {
 195        switch(s->count_latched) {
 196        default:
 197        case RW_STATE_LSB:
 198            ret = s->latched_count & 0xff;
 199            s->count_latched = 0;
 200            break;
 201        case RW_STATE_MSB:
 202            ret = s->latched_count >> 8;
 203            s->count_latched = 0;
 204            break;
 205        case RW_STATE_WORD0:
 206            ret = s->latched_count & 0xff;
 207            s->count_latched = RW_STATE_MSB;
 208            break;
 209        }
 210    } else {
 211        switch(s->read_state) {
 212        default:
 213        case RW_STATE_LSB:
 214            count = pit_get_count(s);
 215            ret = count & 0xff;
 216            break;
 217        case RW_STATE_MSB:
 218            count = pit_get_count(s);
 219            ret = (count >> 8) & 0xff;
 220            break;
 221        case RW_STATE_WORD0:
 222            count = pit_get_count(s);
 223            ret = count & 0xff;
 224            s->read_state = RW_STATE_WORD1;
 225            break;
 226        case RW_STATE_WORD1:
 227            count = pit_get_count(s);
 228            ret = (count >> 8) & 0xff;
 229            s->read_state = RW_STATE_WORD0;
 230            break;
 231        }
 232    }
 233    return ret;
 234}
 235
 236static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
 237{
 238    int64_t expire_time;
 239    int irq_level;
 240
 241    if (!s->irq_timer || s->irq_disabled) {
 242        return;
 243    }
 244    expire_time = pit_get_next_transition_time(s, current_time);
 245    irq_level = pit_get_out(s, current_time);
 246    qemu_set_irq(s->irq, irq_level);
 247#ifdef DEBUG_PIT
 248    printf("irq_level=%d next_delay=%f\n",
 249           irq_level,
 250           (double)(expire_time - current_time) / get_ticks_per_sec());
 251#endif
 252    s->next_transition_time = expire_time;
 253    if (expire_time != -1)
 254        qemu_mod_timer(s->irq_timer, expire_time);
 255    else
 256        qemu_del_timer(s->irq_timer);
 257}
 258
 259static void pit_irq_timer(void *opaque)
 260{
 261    PITChannelState *s = opaque;
 262
 263    pit_irq_timer_update(s, s->next_transition_time);
 264}
 265
 266static void pit_reset(DeviceState *dev)
 267{
 268    PITCommonState *pit = DO_UPCAST(PITCommonState, dev.qdev, dev);
 269    PITChannelState *s;
 270
 271    pit_reset_common(pit);
 272
 273    s = &pit->channels[0];
 274    if (!s->irq_disabled) {
 275        qemu_mod_timer(s->irq_timer, s->next_transition_time);
 276    }
 277}
 278
 279/* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
 280 * reenable it when legacy mode is left again. */
 281static void pit_irq_control(void *opaque, int n, int enable)
 282{
 283    PITCommonState *pit = opaque;
 284    PITChannelState *s = &pit->channels[0];
 285
 286    if (enable) {
 287        s->irq_disabled = 0;
 288        pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock));
 289    } else {
 290        s->irq_disabled = 1;
 291        qemu_del_timer(s->irq_timer);
 292    }
 293}
 294
 295static const MemoryRegionOps pit_ioport_ops = {
 296    .read = pit_ioport_read,
 297    .write = pit_ioport_write,
 298    .impl = {
 299        .min_access_size = 1,
 300        .max_access_size = 1,
 301    },
 302    .endianness = DEVICE_LITTLE_ENDIAN,
 303};
 304
 305static void pit_post_load(PITCommonState *s)
 306{
 307    PITChannelState *sc = &s->channels[0];
 308
 309    if (sc->next_transition_time != -1) {
 310        qemu_mod_timer(sc->irq_timer, sc->next_transition_time);
 311    } else {
 312        qemu_del_timer(sc->irq_timer);
 313    }
 314}
 315
 316static int pit_initfn(PITCommonState *pit)
 317{
 318    PITChannelState *s;
 319
 320    s = &pit->channels[0];
 321    /* the timer 0 is connected to an IRQ */
 322    s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
 323    qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1);
 324
 325    memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
 326
 327    qdev_init_gpio_in(&pit->dev.qdev, pit_irq_control, 1);
 328
 329    return 0;
 330}
 331
 332static Property pit_properties[] = {
 333    DEFINE_PROP_HEX32("iobase", PITCommonState, iobase,  -1),
 334    DEFINE_PROP_END_OF_LIST(),
 335};
 336
 337static void pit_class_initfn(ObjectClass *klass, void *data)
 338{
 339    PITCommonClass *k = PIT_COMMON_CLASS(klass);
 340    DeviceClass *dc = DEVICE_CLASS(klass);
 341
 342    k->init = pit_initfn;
 343    k->set_channel_gate = pit_set_channel_gate;
 344    k->get_channel_info = pit_get_channel_info_common;
 345    k->post_load = pit_post_load;
 346    dc->reset = pit_reset;
 347    dc->props = pit_properties;
 348}
 349
 350static const TypeInfo pit_info = {
 351    .name          = "isa-pit",
 352    .parent        = TYPE_PIT_COMMON,
 353    .instance_size = sizeof(PITCommonState),
 354    .class_init    = pit_class_initfn,
 355};
 356
 357static void pit_register_types(void)
 358{
 359    type_register_static(&pit_info);
 360}
 361
 362type_init(pit_register_types)
 363