qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

aspeed_vic.c (10661B)


      1 /*
      2  * ASPEED Interrupt Controller (New)
      3  *
      4  * Andrew Jeffery <andrew@aj.id.au>
      5  *
      6  * Copyright 2015, 2016 IBM Corp.
      7  *
      8  * This code is licensed under the GPL version 2 or later.  See
      9  * the COPYING file in the top-level directory.
     10  */
     11 
     12 /* The hardware exposes two register sets, a legacy set and a 'new' set. The
     13  * model implements the 'new' register set, and logs warnings on accesses to
     14  * the legacy IO space.
     15  *
     16  * The hardware uses 32bit registers to manage 51 IRQs, with low and high
     17  * registers for each conceptual register. The device model's implementation
     18  * uses 64bit data types to store both low and high register values (in the one
     19  * member), but must cope with access offset values in multiples of 4 passed to
     20  * the callbacks. As such the read() and write() implementations process the
     21  * provided offset to understand whether the access is requesting the lower or
     22  * upper 32 bits of the 64bit member.
     23  *
     24  * Additionally, the "Interrupt Enable", "Edge Status" and "Software Interrupt"
     25  * fields have separate "enable"/"status" and "clear" registers, where set bits
     26  * are written to one or the other to change state (avoiding a
     27  * read-modify-write sequence).
     28  */
     29 
     30 #include "qemu/osdep.h"
     31 #include "hw/intc/aspeed_vic.h"
     32 #include "hw/irq.h"
     33 #include "migration/vmstate.h"
     34 #include "qemu/bitops.h"
     35 #include "qemu/log.h"
     36 #include "qemu/module.h"
     37 #include "trace.h"
     38 
     39 #define AVIC_NEW_BASE_OFFSET 0x80
     40 
     41 #define AVIC_L_MASK 0xFFFFFFFFU
     42 #define AVIC_H_MASK 0x0007FFFFU
     43 #define AVIC_EVENT_W_MASK (0x78000ULL << 32)
     44 
     45 static void aspeed_vic_update(AspeedVICState *s)
     46 {
     47     uint64_t new = (s->raw & s->enable);
     48     uint64_t flags;
     49 
     50     flags = new & s->select;
     51     trace_aspeed_vic_update_fiq(!!flags);
     52     qemu_set_irq(s->fiq, !!flags);
     53 
     54     flags = new & ~s->select;
     55     trace_aspeed_vic_update_irq(!!flags);
     56     qemu_set_irq(s->irq, !!flags);
     57 }
     58 
     59 static void aspeed_vic_set_irq(void *opaque, int irq, int level)
     60 {
     61     uint64_t irq_mask;
     62     bool raise;
     63     AspeedVICState *s = (AspeedVICState *)opaque;
     64 
     65     if (irq > ASPEED_VIC_NR_IRQS) {
     66         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
     67                       __func__, irq);
     68         return;
     69     }
     70 
     71     trace_aspeed_vic_set_irq(irq, level);
     72 
     73     irq_mask = BIT(irq);
     74     if (s->sense & irq_mask) {
     75         /* level-triggered */
     76         if (s->event & irq_mask) {
     77             /* high-sensitive */
     78             raise = level;
     79         } else {
     80             /* low-sensitive */
     81             raise = !level;
     82         }
     83         s->raw = deposit64(s->raw, irq, 1, raise);
     84     } else {
     85         uint64_t old_level = s->level & irq_mask;
     86 
     87         /* edge-triggered */
     88         if (s->dual_edge & irq_mask) {
     89             raise = (!!old_level) != (!!level);
     90         } else {
     91             if (s->event & irq_mask) {
     92                 /* rising-sensitive */
     93                 raise = !old_level && level;
     94             } else {
     95                 /* falling-sensitive */
     96                 raise = old_level && !level;
     97             }
     98         }
     99         if (raise) {
    100             s->raw = deposit64(s->raw, irq, 1, raise);
    101         }
    102     }
    103     s->level = deposit64(s->level, irq, 1, level);
    104     aspeed_vic_update(s);
    105 }
    106 
    107 static uint64_t aspeed_vic_read(void *opaque, hwaddr offset, unsigned size)
    108 {
    109     AspeedVICState *s = (AspeedVICState *)opaque;
    110     hwaddr n_offset;
    111     uint64_t val;
    112     bool high;
    113 
    114     if (offset < AVIC_NEW_BASE_OFFSET) {
    115         high = false;
    116         n_offset = offset;
    117     } else {
    118         high = !!(offset & 0x4);
    119         n_offset = (offset & ~0x4);
    120     }
    121 
    122     switch (n_offset) {
    123     case 0x80: /* IRQ Status */
    124     case 0x00:
    125         val = s->raw & ~s->select & s->enable;
    126         break;
    127     case 0x88: /* FIQ Status */
    128     case 0x04:
    129         val = s->raw & s->select & s->enable;
    130         break;
    131     case 0x90: /* Raw Interrupt Status */
    132     case 0x08:
    133         val = s->raw;
    134         break;
    135     case 0x98: /* Interrupt Selection */
    136     case 0x0c:
    137         val = s->select;
    138         break;
    139     case 0xa0: /* Interrupt Enable */
    140     case 0x10:
    141         val = s->enable;
    142         break;
    143     case 0xb0: /* Software Interrupt */
    144     case 0x18:
    145         val = s->trigger;
    146         break;
    147     case 0xc0: /* Interrupt Sensitivity */
    148     case 0x24:
    149         val = s->sense;
    150         break;
    151     case 0xc8: /* Interrupt Both Edge Trigger Control */
    152     case 0x28:
    153         val = s->dual_edge;
    154         break;
    155     case 0xd0: /* Interrupt Event */
    156     case 0x2c:
    157         val = s->event;
    158         break;
    159     case 0xe0: /* Edge Triggered Interrupt Status */
    160         val = s->raw & ~s->sense;
    161         break;
    162         /* Illegal */
    163     case 0xa8: /* Interrupt Enable Clear */
    164     case 0xb8: /* Software Interrupt Clear */
    165     case 0xd8: /* Edge Triggered Interrupt Clear */
    166         qemu_log_mask(LOG_GUEST_ERROR,
    167                       "%s: Read of write-only register with offset 0x%"
    168                       HWADDR_PRIx "\n", __func__, offset);
    169         val = 0;
    170         break;
    171     default:
    172         qemu_log_mask(LOG_GUEST_ERROR,
    173                       "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
    174                       __func__, offset);
    175         val = 0;
    176         break;
    177     }
    178     if (high) {
    179         val = extract64(val, 32, 19);
    180     } else {
    181         val = extract64(val, 0, 32);
    182     }
    183     trace_aspeed_vic_read(offset, size, val);
    184     return val;
    185 }
    186 
    187 static void aspeed_vic_write(void *opaque, hwaddr offset, uint64_t data,
    188                              unsigned size)
    189 {
    190     AspeedVICState *s = (AspeedVICState *)opaque;
    191     hwaddr n_offset;
    192     bool high;
    193 
    194     if (offset < AVIC_NEW_BASE_OFFSET) {
    195         high = false;
    196         n_offset = offset;
    197     } else {
    198         high = !!(offset & 0x4);
    199         n_offset = (offset & ~0x4);
    200     }
    201 
    202     trace_aspeed_vic_write(offset, size, data);
    203 
    204     /* Given we have members using separate enable/clear registers, deposit64()
    205      * isn't quite the tool for the job. Instead, relocate the incoming bits to
    206      * the required bit offset based on the provided access address
    207      */
    208     if (high) {
    209         data &= AVIC_H_MASK;
    210         data <<= 32;
    211     } else {
    212         data &= AVIC_L_MASK;
    213     }
    214 
    215     switch (n_offset) {
    216     case 0x98: /* Interrupt Selection */
    217     case 0x0c:
    218         /* Register has deposit64() semantics - overwrite requested 32 bits */
    219         if (high) {
    220             s->select &= AVIC_L_MASK;
    221         } else {
    222             s->select &= ((uint64_t) AVIC_H_MASK) << 32;
    223         }
    224         s->select |= data;
    225         break;
    226     case 0xa0: /* Interrupt Enable */
    227     case 0x10:
    228         s->enable |= data;
    229         break;
    230     case 0xa8: /* Interrupt Enable Clear */
    231     case 0x14:
    232         s->enable &= ~data;
    233         break;
    234     case 0xb0: /* Software Interrupt */
    235     case 0x18:
    236         qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
    237                       "IRQs requested: 0x%016" PRIx64 "\n", __func__, data);
    238         break;
    239     case 0xb8: /* Software Interrupt Clear */
    240     case 0x1c:
    241         qemu_log_mask(LOG_UNIMP, "%s: Software interrupts unavailable. "
    242                       "IRQs to be cleared: 0x%016" PRIx64 "\n", __func__, data);
    243         break;
    244     case 0xd0: /* Interrupt Event */
    245         /* Register has deposit64() semantics - overwrite the top four valid
    246          * IRQ bits, as only the top four IRQs (GPIOs) can change their event
    247          * type */
    248         if (high) {
    249             s->event &= ~AVIC_EVENT_W_MASK;
    250             s->event |= (data & AVIC_EVENT_W_MASK);
    251         } else {
    252             qemu_log_mask(LOG_GUEST_ERROR,
    253                           "Ignoring invalid write to interrupt event register");
    254         }
    255         break;
    256     case 0xd8: /* Edge Triggered Interrupt Clear */
    257     case 0x38:
    258         s->raw &= ~(data & ~s->sense);
    259         break;
    260     case 0x80: /* IRQ Status */
    261     case 0x00:
    262     case 0x88: /* FIQ Status */
    263     case 0x04:
    264     case 0x90: /* Raw Interrupt Status */
    265     case 0x08:
    266     case 0xc0: /* Interrupt Sensitivity */
    267     case 0x24:
    268     case 0xc8: /* Interrupt Both Edge Trigger Control */
    269     case 0x28:
    270     case 0xe0: /* Edge Triggered Interrupt Status */
    271         qemu_log_mask(LOG_GUEST_ERROR,
    272                       "%s: Write of read-only register with offset 0x%"
    273                       HWADDR_PRIx "\n", __func__, offset);
    274         break;
    275 
    276     default:
    277         qemu_log_mask(LOG_GUEST_ERROR,
    278                       "%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
    279                       __func__, offset);
    280         break;
    281     }
    282     aspeed_vic_update(s);
    283 }
    284 
    285 static const MemoryRegionOps aspeed_vic_ops = {
    286     .read = aspeed_vic_read,
    287     .write = aspeed_vic_write,
    288     .endianness = DEVICE_LITTLE_ENDIAN,
    289     .valid.min_access_size = 4,
    290     .valid.max_access_size = 4,
    291     .valid.unaligned = false,
    292 };
    293 
    294 static void aspeed_vic_reset(DeviceState *dev)
    295 {
    296     AspeedVICState *s = ASPEED_VIC(dev);
    297 
    298     s->level = 0;
    299     s->raw = 0;
    300     s->select = 0;
    301     s->enable = 0;
    302     s->trigger = 0;
    303     s->sense = 0x1F07FFF8FFFFULL;
    304     s->dual_edge = 0xF800070000ULL;
    305     s->event = 0x5F07FFF8FFFFULL;
    306 }
    307 
    308 #define AVIC_IO_REGION_SIZE 0x20000
    309 
    310 static void aspeed_vic_realize(DeviceState *dev, Error **errp)
    311 {
    312     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    313     AspeedVICState *s = ASPEED_VIC(dev);
    314 
    315     memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_vic_ops, s,
    316                           TYPE_ASPEED_VIC, AVIC_IO_REGION_SIZE);
    317 
    318     sysbus_init_mmio(sbd, &s->iomem);
    319 
    320     qdev_init_gpio_in(dev, aspeed_vic_set_irq, ASPEED_VIC_NR_IRQS);
    321     sysbus_init_irq(sbd, &s->irq);
    322     sysbus_init_irq(sbd, &s->fiq);
    323 }
    324 
    325 static const VMStateDescription vmstate_aspeed_vic = {
    326     .name = "aspeed.new-vic",
    327     .version_id = 1,
    328     .minimum_version_id = 1,
    329     .fields = (VMStateField[]) {
    330         VMSTATE_UINT64(level, AspeedVICState),
    331         VMSTATE_UINT64(raw, AspeedVICState),
    332         VMSTATE_UINT64(select, AspeedVICState),
    333         VMSTATE_UINT64(enable, AspeedVICState),
    334         VMSTATE_UINT64(trigger, AspeedVICState),
    335         VMSTATE_UINT64(sense, AspeedVICState),
    336         VMSTATE_UINT64(dual_edge, AspeedVICState),
    337         VMSTATE_UINT64(event, AspeedVICState),
    338         VMSTATE_END_OF_LIST()
    339     }
    340 };
    341 
    342 static void aspeed_vic_class_init(ObjectClass *klass, void *data)
    343 {
    344     DeviceClass *dc = DEVICE_CLASS(klass);
    345     dc->realize = aspeed_vic_realize;
    346     dc->reset = aspeed_vic_reset;
    347     dc->desc = "ASPEED Interrupt Controller (New)";
    348     dc->vmsd = &vmstate_aspeed_vic;
    349 }
    350 
    351 static const TypeInfo aspeed_vic_info = {
    352     .name = TYPE_ASPEED_VIC,
    353     .parent = TYPE_SYS_BUS_DEVICE,
    354     .instance_size = sizeof(AspeedVICState),
    355     .class_init = aspeed_vic_class_init,
    356 };
    357 
    358 static void aspeed_vic_register_types(void)
    359 {
    360     type_register_static(&aspeed_vic_info);
    361 }
    362 
    363 type_init(aspeed_vic_register_types);