qemu

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

pl190.c (8168B)


      1 /*
      2  * Arm PrimeCell PL190 Vector Interrupt Controller
      3  *
      4  * Copyright (c) 2006 CodeSourcery.
      5  * Written by Paul Brook
      6  *
      7  * This code is licensed under the GPL.
      8  */
      9 
     10 #include "qemu/osdep.h"
     11 #include "hw/irq.h"
     12 #include "hw/sysbus.h"
     13 #include "migration/vmstate.h"
     14 #include "qemu/log.h"
     15 #include "qemu/module.h"
     16 #include "qom/object.h"
     17 
     18 /* The number of virtual priority levels.  16 user vectors plus the
     19    unvectored IRQ.  Chained interrupts would require an additional level
     20    if implemented.  */
     21 
     22 #define PL190_NUM_PRIO 17
     23 
     24 #define TYPE_PL190 "pl190"
     25 OBJECT_DECLARE_SIMPLE_TYPE(PL190State, PL190)
     26 
     27 struct PL190State {
     28     SysBusDevice parent_obj;
     29 
     30     MemoryRegion iomem;
     31     uint32_t level;
     32     uint32_t soft_level;
     33     uint32_t irq_enable;
     34     uint32_t fiq_select;
     35     uint8_t vect_control[16];
     36     uint32_t vect_addr[PL190_NUM_PRIO];
     37     /* Mask containing interrupts with higher priority than this one.  */
     38     uint32_t prio_mask[PL190_NUM_PRIO + 1];
     39     int protected;
     40     /* Current priority level.  */
     41     int priority;
     42     int prev_prio[PL190_NUM_PRIO];
     43     qemu_irq irq;
     44     qemu_irq fiq;
     45 };
     46 
     47 static const unsigned char pl190_id[] =
     48 { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
     49 
     50 static inline uint32_t pl190_irq_level(PL190State *s)
     51 {
     52     return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
     53 }
     54 
     55 /* Update interrupts.  */
     56 static void pl190_update(PL190State *s)
     57 {
     58     uint32_t level = pl190_irq_level(s);
     59     int set;
     60 
     61     set = (level & s->prio_mask[s->priority]) != 0;
     62     qemu_set_irq(s->irq, set);
     63     set = ((s->level | s->soft_level) & s->fiq_select) != 0;
     64     qemu_set_irq(s->fiq, set);
     65 }
     66 
     67 static void pl190_set_irq(void *opaque, int irq, int level)
     68 {
     69     PL190State *s = (PL190State *)opaque;
     70 
     71     if (level)
     72         s->level |= 1u << irq;
     73     else
     74         s->level &= ~(1u << irq);
     75     pl190_update(s);
     76 }
     77 
     78 static void pl190_update_vectors(PL190State *s)
     79 {
     80     uint32_t mask;
     81     int i;
     82     int n;
     83 
     84     mask = 0;
     85     for (i = 0; i < 16; i++)
     86       {
     87         s->prio_mask[i] = mask;
     88         if (s->vect_control[i] & 0x20)
     89           {
     90             n = s->vect_control[i] & 0x1f;
     91             mask |= 1 << n;
     92           }
     93       }
     94     s->prio_mask[16] = mask;
     95     pl190_update(s);
     96 }
     97 
     98 static uint64_t pl190_read(void *opaque, hwaddr offset,
     99                            unsigned size)
    100 {
    101     PL190State *s = (PL190State *)opaque;
    102     int i;
    103 
    104     if (offset >= 0xfe0 && offset < 0x1000) {
    105         return pl190_id[(offset - 0xfe0) >> 2];
    106     }
    107     if (offset >= 0x100 && offset < 0x140) {
    108         return s->vect_addr[(offset - 0x100) >> 2];
    109     }
    110     if (offset >= 0x200 && offset < 0x240) {
    111         return s->vect_control[(offset - 0x200) >> 2];
    112     }
    113     switch (offset >> 2) {
    114     case 0: /* IRQSTATUS */
    115         return pl190_irq_level(s);
    116     case 1: /* FIQSATUS */
    117         return (s->level | s->soft_level) & s->fiq_select;
    118     case 2: /* RAWINTR */
    119         return s->level | s->soft_level;
    120     case 3: /* INTSELECT */
    121         return s->fiq_select;
    122     case 4: /* INTENABLE */
    123         return s->irq_enable;
    124     case 6: /* SOFTINT */
    125         return s->soft_level;
    126     case 8: /* PROTECTION */
    127         return s->protected;
    128     case 12: /* VECTADDR */
    129         /* Read vector address at the start of an ISR.  Increases the
    130          * current priority level to that of the current interrupt.
    131          *
    132          * Since an enabled interrupt X at priority P causes prio_mask[Y]
    133          * to have bit X set for all Y > P, this loop will stop with
    134          * i == the priority of the highest priority set interrupt.
    135          */
    136         for (i = 0; i < s->priority; i++) {
    137             if ((s->level | s->soft_level) & s->prio_mask[i + 1]) {
    138                 break;
    139             }
    140         }
    141 
    142         /* Reading this value with no pending interrupts is undefined.
    143            We return the default address.  */
    144         if (i == PL190_NUM_PRIO)
    145           return s->vect_addr[16];
    146         if (i < s->priority)
    147           {
    148             s->prev_prio[i] = s->priority;
    149             s->priority = i;
    150             pl190_update(s);
    151           }
    152         return s->vect_addr[s->priority];
    153     case 13: /* DEFVECTADDR */
    154         return s->vect_addr[16];
    155     default:
    156         qemu_log_mask(LOG_GUEST_ERROR,
    157                       "pl190_read: Bad offset %x\n", (int)offset);
    158         return 0;
    159     }
    160 }
    161 
    162 static void pl190_write(void *opaque, hwaddr offset,
    163                         uint64_t val, unsigned size)
    164 {
    165     PL190State *s = (PL190State *)opaque;
    166 
    167     if (offset >= 0x100 && offset < 0x140) {
    168         s->vect_addr[(offset - 0x100) >> 2] = val;
    169         pl190_update_vectors(s);
    170         return;
    171     }
    172     if (offset >= 0x200 && offset < 0x240) {
    173         s->vect_control[(offset - 0x200) >> 2] = val;
    174         pl190_update_vectors(s);
    175         return;
    176     }
    177     switch (offset >> 2) {
    178     case 0: /* SELECT */
    179         /* This is a readonly register, but linux tries to write to it
    180            anyway.  Ignore the write.  */
    181         break;
    182     case 3: /* INTSELECT */
    183         s->fiq_select = val;
    184         break;
    185     case 4: /* INTENABLE */
    186         s->irq_enable |= val;
    187         break;
    188     case 5: /* INTENCLEAR */
    189         s->irq_enable &= ~val;
    190         break;
    191     case 6: /* SOFTINT */
    192         s->soft_level |= val;
    193         break;
    194     case 7: /* SOFTINTCLEAR */
    195         s->soft_level &= ~val;
    196         break;
    197     case 8: /* PROTECTION */
    198         /* TODO: Protection (supervisor only access) is not implemented.  */
    199         s->protected = val & 1;
    200         break;
    201     case 12: /* VECTADDR */
    202         /* Restore the previous priority level.  The value written is
    203            ignored.  */
    204         if (s->priority < PL190_NUM_PRIO)
    205             s->priority = s->prev_prio[s->priority];
    206         break;
    207     case 13: /* DEFVECTADDR */
    208         s->vect_addr[16] = val;
    209         break;
    210     case 0xc0: /* ITCR */
    211         if (val) {
    212             qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n");
    213         }
    214         break;
    215     default:
    216         qemu_log_mask(LOG_GUEST_ERROR,
    217                      "pl190_write: Bad offset %x\n", (int)offset);
    218         return;
    219     }
    220     pl190_update(s);
    221 }
    222 
    223 static const MemoryRegionOps pl190_ops = {
    224     .read = pl190_read,
    225     .write = pl190_write,
    226     .endianness = DEVICE_NATIVE_ENDIAN,
    227 };
    228 
    229 static void pl190_reset(DeviceState *d)
    230 {
    231     PL190State *s = PL190(d);
    232     int i;
    233 
    234     for (i = 0; i < 16; i++) {
    235         s->vect_addr[i] = 0;
    236         s->vect_control[i] = 0;
    237     }
    238     s->vect_addr[16] = 0;
    239     s->prio_mask[17] = 0xffffffff;
    240     s->priority = PL190_NUM_PRIO;
    241     pl190_update_vectors(s);
    242 }
    243 
    244 static void pl190_init(Object *obj)
    245 {
    246     DeviceState *dev = DEVICE(obj);
    247     PL190State *s = PL190(obj);
    248     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    249 
    250     memory_region_init_io(&s->iomem, obj, &pl190_ops, s, "pl190", 0x1000);
    251     sysbus_init_mmio(sbd, &s->iomem);
    252     qdev_init_gpio_in(dev, pl190_set_irq, 32);
    253     sysbus_init_irq(sbd, &s->irq);
    254     sysbus_init_irq(sbd, &s->fiq);
    255 }
    256 
    257 static const VMStateDescription vmstate_pl190 = {
    258     .name = "pl190",
    259     .version_id = 1,
    260     .minimum_version_id = 1,
    261     .fields = (VMStateField[]) {
    262         VMSTATE_UINT32(level, PL190State),
    263         VMSTATE_UINT32(soft_level, PL190State),
    264         VMSTATE_UINT32(irq_enable, PL190State),
    265         VMSTATE_UINT32(fiq_select, PL190State),
    266         VMSTATE_UINT8_ARRAY(vect_control, PL190State, 16),
    267         VMSTATE_UINT32_ARRAY(vect_addr, PL190State, PL190_NUM_PRIO),
    268         VMSTATE_UINT32_ARRAY(prio_mask, PL190State, PL190_NUM_PRIO+1),
    269         VMSTATE_INT32(protected, PL190State),
    270         VMSTATE_INT32(priority, PL190State),
    271         VMSTATE_INT32_ARRAY(prev_prio, PL190State, PL190_NUM_PRIO),
    272         VMSTATE_END_OF_LIST()
    273     }
    274 };
    275 
    276 static void pl190_class_init(ObjectClass *klass, void *data)
    277 {
    278     DeviceClass *dc = DEVICE_CLASS(klass);
    279 
    280     dc->reset = pl190_reset;
    281     dc->vmsd = &vmstate_pl190;
    282 }
    283 
    284 static const TypeInfo pl190_info = {
    285     .name          = TYPE_PL190,
    286     .parent        = TYPE_SYS_BUS_DEVICE,
    287     .instance_size = sizeof(PL190State),
    288     .instance_init = pl190_init,
    289     .class_init    = pl190_class_init,
    290 };
    291 
    292 static void pl190_register_types(void)
    293 {
    294     type_register_static(&pl190_info);
    295 }
    296 
    297 type_init(pl190_register_types)