qemu

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

loongarch_extioi.c (10299B)


      1 /* SPDX-License-Identifier: GPL-2.0-or-later */
      2 /*
      3  * Loongson 3A5000 ext interrupt controller emulation
      4  *
      5  * Copyright (C) 2021 Loongson Technology Corporation Limited
      6  */
      7 
      8 #include "qemu/osdep.h"
      9 #include "qemu/module.h"
     10 #include "qemu/log.h"
     11 #include "hw/irq.h"
     12 #include "hw/sysbus.h"
     13 #include "hw/loongarch/virt.h"
     14 #include "hw/qdev-properties.h"
     15 #include "exec/address-spaces.h"
     16 #include "hw/intc/loongarch_extioi.h"
     17 #include "migration/vmstate.h"
     18 #include "trace.h"
     19 
     20 
     21 static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level)
     22 {
     23     int ipnum, cpu, found, irq_index, irq_mask;
     24 
     25     ipnum = s->sw_ipmap[irq / 32];
     26     cpu = s->sw_coremap[irq];
     27     irq_index = irq / 32;
     28     irq_mask = 1 << (irq & 0x1f);
     29 
     30     if (level) {
     31         /* if not enable return false */
     32         if (((s->enable[irq_index]) & irq_mask) == 0) {
     33             return;
     34         }
     35         s->coreisr[cpu][irq_index] |= irq_mask;
     36         found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS);
     37         set_bit(irq, s->sw_isr[cpu][ipnum]);
     38         if (found < EXTIOI_IRQS) {
     39             /* other irq is handling, need not update parent irq level */
     40             return;
     41         }
     42     } else {
     43         s->coreisr[cpu][irq_index] &= ~irq_mask;
     44         clear_bit(irq, s->sw_isr[cpu][ipnum]);
     45         found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS);
     46         if (found < EXTIOI_IRQS) {
     47             /* other irq is handling, need not update parent irq level */
     48             return;
     49         }
     50     }
     51     qemu_set_irq(s->parent_irq[cpu][ipnum], level);
     52 }
     53 
     54 static void extioi_setirq(void *opaque, int irq, int level)
     55 {
     56     LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
     57     trace_loongarch_extioi_setirq(irq, level);
     58     if (level) {
     59         /*
     60          * s->isr should be used in vmstate structure,
     61          * but it not support 'unsigned long',
     62          * so we have to switch it.
     63          */
     64         set_bit(irq, (unsigned long *)s->isr);
     65     } else {
     66         clear_bit(irq, (unsigned long *)s->isr);
     67     }
     68     extioi_update_irq(s, irq, level);
     69 }
     70 
     71 static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data,
     72                                 unsigned size, MemTxAttrs attrs)
     73 {
     74     LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
     75     unsigned long offset = addr & 0xffff;
     76     uint32_t index, cpu;
     77 
     78     switch (offset) {
     79     case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1:
     80         index = (offset - EXTIOI_NODETYPE_START) >> 2;
     81         *data = s->nodetype[index];
     82         break;
     83     case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1:
     84         index = (offset - EXTIOI_IPMAP_START) >> 2;
     85         *data = s->ipmap[index];
     86         break;
     87     case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1:
     88         index = (offset - EXTIOI_ENABLE_START) >> 2;
     89         *data = s->enable[index];
     90         break;
     91     case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1:
     92         index = (offset - EXTIOI_BOUNCE_START) >> 2;
     93         *data = s->bounce[index];
     94         break;
     95     case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1:
     96         index = (offset - EXTIOI_COREISR_START) >> 2;
     97         /* using attrs to get current cpu index */
     98         cpu = attrs.requester_id;
     99         *data = s->coreisr[cpu][index];
    100         break;
    101     case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1:
    102         index = (offset - EXTIOI_COREMAP_START) >> 2;
    103         *data = s->coremap[index];
    104         break;
    105     default:
    106         break;
    107     }
    108 
    109     trace_loongarch_extioi_readw(addr, *data);
    110     return MEMTX_OK;
    111 }
    112 
    113 static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\
    114                                      uint32_t mask, int level)
    115 {
    116     uint32_t val;
    117     int irq;
    118 
    119     val = mask & s->isr[index];
    120     irq = ctz32(val);
    121     while (irq != 32) {
    122         /*
    123          * enable bit change from 0 to 1,
    124          * need to update irq by pending bits
    125          */
    126         extioi_update_irq(s, irq + index * 32, level);
    127         val &= ~(1 << irq);
    128         irq = ctz32(val);
    129     }
    130 }
    131 
    132 static MemTxResult extioi_writew(void *opaque, hwaddr addr,
    133                           uint64_t val, unsigned size,
    134                           MemTxAttrs attrs)
    135 {
    136     LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque);
    137     int i, cpu, index, old_data, irq;
    138     uint32_t offset;
    139 
    140     trace_loongarch_extioi_writew(addr, val);
    141     offset = addr & 0xffff;
    142 
    143     switch (offset) {
    144     case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1:
    145         index = (offset - EXTIOI_NODETYPE_START) >> 2;
    146         s->nodetype[index] = val;
    147         break;
    148     case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1:
    149         /*
    150          * ipmap cannot be set at runtime, can be set only at the beginning
    151          * of intr driver, need not update upper irq level
    152          */
    153         index = (offset - EXTIOI_IPMAP_START) >> 2;
    154         s->ipmap[index] = val;
    155         /*
    156          * loongarch only support little endian,
    157          * so we paresd the value with little endian.
    158          */
    159         val = cpu_to_le64(val);
    160         for (i = 0; i < 4; i++) {
    161             uint8_t ipnum;
    162             ipnum = val & 0xff;
    163             ipnum = ctz32(ipnum);
    164             ipnum = (ipnum >= 4) ? 0 : ipnum;
    165             s->sw_ipmap[index * 4 + i] = ipnum;
    166             val = val >> 8;
    167         }
    168 
    169         break;
    170     case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1:
    171         index = (offset - EXTIOI_ENABLE_START) >> 2;
    172         old_data = s->enable[index];
    173         s->enable[index] = val;
    174 
    175         /* unmask irq */
    176         val = s->enable[index] & ~old_data;
    177         extioi_enable_irq(s, index, val, 1);
    178 
    179         /* mask irq */
    180         val = ~s->enable[index] & old_data;
    181         extioi_enable_irq(s, index, val, 0);
    182         break;
    183     case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1:
    184         /* do not emulate hw bounced irq routing */
    185         index = (offset - EXTIOI_BOUNCE_START) >> 2;
    186         s->bounce[index] = val;
    187         break;
    188     case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1:
    189         index = (offset - EXTIOI_COREISR_START) >> 2;
    190         /* using attrs to get current cpu index */
    191         cpu = attrs.requester_id;
    192         old_data = s->coreisr[cpu][index];
    193         s->coreisr[cpu][index] = old_data & ~val;
    194         /* write 1 to clear interrrupt */
    195         old_data &= val;
    196         irq = ctz32(old_data);
    197         while (irq != 32) {
    198             extioi_update_irq(s, irq + index * 32, 0);
    199             old_data &= ~(1 << irq);
    200             irq = ctz32(old_data);
    201         }
    202         break;
    203     case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1:
    204         irq = offset - EXTIOI_COREMAP_START;
    205         index = irq / 4;
    206         s->coremap[index] = val;
    207         /*
    208          * loongarch only support little endian,
    209          * so we paresd the value with little endian.
    210          */
    211         val = cpu_to_le64(val);
    212 
    213         for (i = 0; i < 4; i++) {
    214             cpu = val & 0xff;
    215             cpu = ctz32(cpu);
    216             cpu = (cpu >= 4) ? 0 : cpu;
    217             val = val >> 8;
    218 
    219             if (s->sw_coremap[irq + i] == cpu) {
    220                 continue;
    221             }
    222 
    223             if (test_bit(irq, (unsigned long *)s->isr)) {
    224                 /*
    225                  * lower irq at old cpu and raise irq at new cpu
    226                  */
    227                 extioi_update_irq(s, irq + i, 0);
    228                 s->sw_coremap[irq + i] = cpu;
    229                 extioi_update_irq(s, irq + i, 1);
    230             } else {
    231                 s->sw_coremap[irq + i] = cpu;
    232             }
    233         }
    234         break;
    235     default:
    236         break;
    237     }
    238     return MEMTX_OK;
    239 }
    240 
    241 static const MemoryRegionOps extioi_ops = {
    242     .read_with_attrs = extioi_readw,
    243     .write_with_attrs = extioi_writew,
    244     .impl.min_access_size = 4,
    245     .impl.max_access_size = 4,
    246     .valid.min_access_size = 4,
    247     .valid.max_access_size = 8,
    248     .endianness = DEVICE_LITTLE_ENDIAN,
    249 };
    250 
    251 static const VMStateDescription vmstate_loongarch_extioi = {
    252     .name = TYPE_LOONGARCH_EXTIOI,
    253     .version_id = 1,
    254     .minimum_version_id = 1,
    255     .fields = (VMStateField[]) {
    256         VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT),
    257         VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, LOONGARCH_MAX_VCPUS,
    258                                EXTIOI_IRQS_GROUP_COUNT),
    259         VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI,
    260                              EXTIOI_IRQS_NODETYPE_COUNT / 2),
    261         VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32),
    262         VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32),
    263         VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4),
    264         VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4),
    265         VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE),
    266         VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS),
    267 
    268         VMSTATE_END_OF_LIST()
    269     }
    270 };
    271 
    272 static void loongarch_extioi_instance_init(Object *obj)
    273 {
    274     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
    275     LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj);
    276     int i, cpu, pin;
    277 
    278     for (i = 0; i < EXTIOI_IRQS; i++) {
    279         sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
    280     }
    281 
    282     qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS);
    283 
    284     for (cpu = 0; cpu < LOONGARCH_MAX_VCPUS; cpu++) {
    285         memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops,
    286                               s, "extioi_iocsr", 0x900);
    287         sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_iocsr_mem[cpu]);
    288         for (pin = 0; pin < LS3A_INTC_IP; pin++) {
    289             qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1);
    290         }
    291     }
    292     memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops,
    293                           s, "extioi_system_mem", 0x900);
    294     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->extioi_system_mem);
    295 }
    296 
    297 static void loongarch_extioi_class_init(ObjectClass *klass, void *data)
    298 {
    299     DeviceClass *dc = DEVICE_CLASS(klass);
    300 
    301     dc->vmsd = &vmstate_loongarch_extioi;
    302 }
    303 
    304 static const TypeInfo loongarch_extioi_info = {
    305     .name          = TYPE_LOONGARCH_EXTIOI,
    306     .parent        = TYPE_SYS_BUS_DEVICE,
    307     .instance_init = loongarch_extioi_instance_init,
    308     .instance_size = sizeof(struct LoongArchExtIOI),
    309     .class_init    = loongarch_extioi_class_init,
    310 };
    311 
    312 static void loongarch_extioi_register_types(void)
    313 {
    314     type_register_static(&loongarch_extioi_info);
    315 }
    316 
    317 type_init(loongarch_extioi_register_types)