qemu/hw/intc/i8259.c
<<
>>
Prefs
   1/*
   2 * QEMU 8259 interrupt controller 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
  25#include "qemu/osdep.h"
  26#include "hw/intc/i8259.h"
  27#include "hw/irq.h"
  28#include "hw/isa/isa.h"
  29#include "qemu/timer.h"
  30#include "qemu/log.h"
  31#include "hw/isa/i8259_internal.h"
  32#include "trace.h"
  33#include "qom/object.h"
  34
  35/* debug PIC */
  36//#define DEBUG_PIC
  37
  38//#define DEBUG_IRQ_LATENCY
  39
  40#define TYPE_I8259 "isa-i8259"
  41typedef struct PICClass PICClass;
  42DECLARE_CLASS_CHECKERS(PICClass, PIC,
  43                       TYPE_I8259)
  44
  45/**
  46 * PICClass:
  47 * @parent_realize: The parent's realizefn.
  48 */
  49struct PICClass {
  50    PICCommonClass parent_class;
  51
  52    DeviceRealize parent_realize;
  53};
  54
  55#ifdef DEBUG_IRQ_LATENCY
  56static int64_t irq_time[16];
  57#endif
  58DeviceState *isa_pic;
  59static PICCommonState *slave_pic;
  60
  61/* return the highest priority found in mask (highest = smallest
  62   number). Return 8 if no irq */
  63static int get_priority(PICCommonState *s, int mask)
  64{
  65    int priority;
  66
  67    if (mask == 0) {
  68        return 8;
  69    }
  70    priority = 0;
  71    while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
  72        priority++;
  73    }
  74    return priority;
  75}
  76
  77/* return the pic wanted interrupt. return -1 if none */
  78static int pic_get_irq(PICCommonState *s)
  79{
  80    int mask, cur_priority, priority;
  81
  82    mask = s->irr & ~s->imr;
  83    priority = get_priority(s, mask);
  84    if (priority == 8) {
  85        return -1;
  86    }
  87    /* compute current priority. If special fully nested mode on the
  88       master, the IRQ coming from the slave is not taken into account
  89       for the priority computation. */
  90    mask = s->isr;
  91    if (s->special_mask) {
  92        mask &= ~s->imr;
  93    }
  94    if (s->special_fully_nested_mode && s->master) {
  95        mask &= ~(1 << 2);
  96    }
  97    cur_priority = get_priority(s, mask);
  98    if (priority < cur_priority) {
  99        /* higher priority found: an irq should be generated */
 100        return (priority + s->priority_add) & 7;
 101    } else {
 102        return -1;
 103    }
 104}
 105
 106/* Update INT output. Must be called every time the output may have changed. */
 107static void pic_update_irq(PICCommonState *s)
 108{
 109    int irq;
 110
 111    irq = pic_get_irq(s);
 112    if (irq >= 0) {
 113        trace_pic_update_irq(s->master, s->imr, s->irr, s->priority_add);
 114        qemu_irq_raise(s->int_out[0]);
 115    } else {
 116        qemu_irq_lower(s->int_out[0]);
 117    }
 118}
 119
 120/* set irq level. If an edge is detected, then the IRR is set to 1 */
 121static void pic_set_irq(void *opaque, int irq, int level)
 122{
 123    PICCommonState *s = opaque;
 124    int mask = 1 << irq;
 125    int irq_index = s->master ? irq : irq + 8;
 126
 127    trace_pic_set_irq(s->master, irq, level);
 128    pic_stat_update_irq(irq_index, level);
 129
 130#ifdef DEBUG_IRQ_LATENCY
 131    if (level) {
 132        irq_time[irq_index] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 133    }
 134#endif
 135
 136    if (s->elcr & mask) {
 137        /* level triggered */
 138        if (level) {
 139            s->irr |= mask;
 140            s->last_irr |= mask;
 141        } else {
 142            s->irr &= ~mask;
 143            s->last_irr &= ~mask;
 144        }
 145    } else {
 146        /* edge triggered */
 147        if (level) {
 148            if ((s->last_irr & mask) == 0) {
 149                s->irr |= mask;
 150            }
 151            s->last_irr |= mask;
 152        } else {
 153            s->last_irr &= ~mask;
 154        }
 155    }
 156    pic_update_irq(s);
 157}
 158
 159/* acknowledge interrupt 'irq' */
 160static void pic_intack(PICCommonState *s, int irq)
 161{
 162    if (s->auto_eoi) {
 163        if (s->rotate_on_auto_eoi) {
 164            s->priority_add = (irq + 1) & 7;
 165        }
 166    } else {
 167        s->isr |= (1 << irq);
 168    }
 169    /* We don't clear a level sensitive interrupt here */
 170    if (!(s->elcr & (1 << irq))) {
 171        s->irr &= ~(1 << irq);
 172    }
 173    pic_update_irq(s);
 174}
 175
 176int pic_read_irq(DeviceState *d)
 177{
 178    PICCommonState *s = PIC_COMMON(d);
 179    int irq, intno;
 180
 181    irq = pic_get_irq(s);
 182    if (irq >= 0) {
 183        int irq2;
 184
 185        if (irq == 2) {
 186            irq2 = pic_get_irq(slave_pic);
 187            if (irq2 >= 0) {
 188                pic_intack(slave_pic, irq2);
 189            } else {
 190                /* spurious IRQ on slave controller */
 191                irq2 = 7;
 192            }
 193            intno = slave_pic->irq_base + irq2;
 194            pic_intack(s, irq);
 195            irq = irq2 + 8;
 196        } else {
 197            intno = s->irq_base + irq;
 198            pic_intack(s, irq);
 199        }
 200    } else {
 201        /* spurious IRQ on host controller */
 202        irq = 7;
 203        intno = s->irq_base + irq;
 204    }
 205
 206#ifdef DEBUG_IRQ_LATENCY
 207    printf("IRQ%d latency=%0.3fus\n",
 208           irq,
 209           (double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
 210                    irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND);
 211#endif
 212
 213    trace_pic_interrupt(irq, intno);
 214    return intno;
 215}
 216
 217static void pic_init_reset(PICCommonState *s)
 218{
 219    pic_reset_common(s);
 220    pic_update_irq(s);
 221}
 222
 223static void pic_reset(DeviceState *dev)
 224{
 225    PICCommonState *s = PIC_COMMON(dev);
 226
 227    s->elcr = 0;
 228    pic_init_reset(s);
 229}
 230
 231static void pic_ioport_write(void *opaque, hwaddr addr64,
 232                             uint64_t val64, unsigned size)
 233{
 234    PICCommonState *s = opaque;
 235    uint32_t addr = addr64;
 236    uint32_t val = val64;
 237    int priority, cmd, irq;
 238
 239    trace_pic_ioport_write(s->master, addr, val);
 240
 241    if (addr == 0) {
 242        if (val & 0x10) {
 243            pic_init_reset(s);
 244            s->init_state = 1;
 245            s->init4 = val & 1;
 246            s->single_mode = val & 2;
 247            if (val & 0x08) {
 248                qemu_log_mask(LOG_UNIMP,
 249                              "i8259: level sensitive irq not supported\n");
 250            }
 251        } else if (val & 0x08) {
 252            if (val & 0x04) {
 253                s->poll = 1;
 254            }
 255            if (val & 0x02) {
 256                s->read_reg_select = val & 1;
 257            }
 258            if (val & 0x40) {
 259                s->special_mask = (val >> 5) & 1;
 260            }
 261        } else {
 262            cmd = val >> 5;
 263            switch (cmd) {
 264            case 0:
 265            case 4:
 266                s->rotate_on_auto_eoi = cmd >> 2;
 267                break;
 268            case 1: /* end of interrupt */
 269            case 5:
 270                priority = get_priority(s, s->isr);
 271                if (priority != 8) {
 272                    irq = (priority + s->priority_add) & 7;
 273                    s->isr &= ~(1 << irq);
 274                    if (cmd == 5) {
 275                        s->priority_add = (irq + 1) & 7;
 276                    }
 277                    pic_update_irq(s);
 278                }
 279                break;
 280            case 3:
 281                irq = val & 7;
 282                s->isr &= ~(1 << irq);
 283                pic_update_irq(s);
 284                break;
 285            case 6:
 286                s->priority_add = (val + 1) & 7;
 287                pic_update_irq(s);
 288                break;
 289            case 7:
 290                irq = val & 7;
 291                s->isr &= ~(1 << irq);
 292                s->priority_add = (irq + 1) & 7;
 293                pic_update_irq(s);
 294                break;
 295            default:
 296                /* no operation */
 297                break;
 298            }
 299        }
 300    } else {
 301        switch (s->init_state) {
 302        case 0:
 303            /* normal mode */
 304            s->imr = val;
 305            pic_update_irq(s);
 306            break;
 307        case 1:
 308            s->irq_base = val & 0xf8;
 309            s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
 310            break;
 311        case 2:
 312            if (s->init4) {
 313                s->init_state = 3;
 314            } else {
 315                s->init_state = 0;
 316            }
 317            break;
 318        case 3:
 319            s->special_fully_nested_mode = (val >> 4) & 1;
 320            s->auto_eoi = (val >> 1) & 1;
 321            s->init_state = 0;
 322            break;
 323        }
 324    }
 325}
 326
 327static uint64_t pic_ioport_read(void *opaque, hwaddr addr,
 328                                unsigned size)
 329{
 330    PICCommonState *s = opaque;
 331    int ret;
 332
 333    if (s->poll) {
 334        ret = pic_get_irq(s);
 335        if (ret >= 0) {
 336            pic_intack(s, ret);
 337            ret |= 0x80;
 338        } else {
 339            ret = 0;
 340        }
 341        s->poll = 0;
 342    } else {
 343        if (addr == 0) {
 344            if (s->read_reg_select) {
 345                ret = s->isr;
 346            } else {
 347                ret = s->irr;
 348            }
 349        } else {
 350            ret = s->imr;
 351        }
 352    }
 353    trace_pic_ioport_read(s->master, addr, ret);
 354    return ret;
 355}
 356
 357int pic_get_output(DeviceState *d)
 358{
 359    PICCommonState *s = PIC_COMMON(d);
 360
 361    return (pic_get_irq(s) >= 0);
 362}
 363
 364static void elcr_ioport_write(void *opaque, hwaddr addr,
 365                              uint64_t val, unsigned size)
 366{
 367    PICCommonState *s = opaque;
 368    s->elcr = val & s->elcr_mask;
 369}
 370
 371static uint64_t elcr_ioport_read(void *opaque, hwaddr addr,
 372                                 unsigned size)
 373{
 374    PICCommonState *s = opaque;
 375    return s->elcr;
 376}
 377
 378static const MemoryRegionOps pic_base_ioport_ops = {
 379    .read = pic_ioport_read,
 380    .write = pic_ioport_write,
 381    .impl = {
 382        .min_access_size = 1,
 383        .max_access_size = 1,
 384    },
 385};
 386
 387static const MemoryRegionOps pic_elcr_ioport_ops = {
 388    .read = elcr_ioport_read,
 389    .write = elcr_ioport_write,
 390    .impl = {
 391        .min_access_size = 1,
 392        .max_access_size = 1,
 393    },
 394};
 395
 396static void pic_realize(DeviceState *dev, Error **errp)
 397{
 398    PICCommonState *s = PIC_COMMON(dev);
 399    PICClass *pc = PIC_GET_CLASS(dev);
 400
 401    memory_region_init_io(&s->base_io, OBJECT(s), &pic_base_ioport_ops, s,
 402                          "pic", 2);
 403    memory_region_init_io(&s->elcr_io, OBJECT(s), &pic_elcr_ioport_ops, s,
 404                          "elcr", 1);
 405
 406    qdev_init_gpio_out(dev, s->int_out, ARRAY_SIZE(s->int_out));
 407    qdev_init_gpio_in(dev, pic_set_irq, 8);
 408
 409    pc->parent_realize(dev, errp);
 410}
 411
 412qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
 413{
 414    qemu_irq *irq_set;
 415    DeviceState *dev;
 416    ISADevice *isadev;
 417    int i;
 418
 419    irq_set = g_new0(qemu_irq, ISA_NUM_IRQS);
 420
 421    isadev = i8259_init_chip(TYPE_I8259, bus, true);
 422    dev = DEVICE(isadev);
 423
 424    qdev_connect_gpio_out(dev, 0, parent_irq);
 425    for (i = 0 ; i < 8; i++) {
 426        irq_set[i] = qdev_get_gpio_in(dev, i);
 427    }
 428
 429    isa_pic = dev;
 430
 431    isadev = i8259_init_chip(TYPE_I8259, bus, false);
 432    dev = DEVICE(isadev);
 433
 434    qdev_connect_gpio_out(dev, 0, irq_set[2]);
 435    for (i = 0 ; i < 8; i++) {
 436        irq_set[i + 8] = qdev_get_gpio_in(dev, i);
 437    }
 438
 439    slave_pic = PIC_COMMON(dev);
 440
 441    return irq_set;
 442}
 443
 444static void i8259_class_init(ObjectClass *klass, void *data)
 445{
 446    PICClass *k = PIC_CLASS(klass);
 447    DeviceClass *dc = DEVICE_CLASS(klass);
 448
 449    device_class_set_parent_realize(dc, pic_realize, &k->parent_realize);
 450    dc->reset = pic_reset;
 451}
 452
 453static const TypeInfo i8259_info = {
 454    .name       = TYPE_I8259,
 455    .instance_size = sizeof(PICCommonState),
 456    .parent     = TYPE_PIC_COMMON,
 457    .class_init = i8259_class_init,
 458    .class_size = sizeof(PICClass),
 459};
 460
 461static void pic_register_types(void)
 462{
 463    type_register_static(&i8259_info);
 464}
 465
 466type_init(pic_register_types)
 467