qemu

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

loongarch_pch_pic.c (12524B)


      1 /* SPDX-License-Identifier: GPL-2.0-or-later */
      2 /*
      3  * QEMU Loongson 7A1000 I/O interrupt controller.
      4  *
      5  * Copyright (C) 2021 Loongson Technology Corporation Limited
      6  */
      7 
      8 #include "qemu/osdep.h"
      9 #include "hw/sysbus.h"
     10 #include "hw/loongarch/virt.h"
     11 #include "hw/irq.h"
     12 #include "hw/intc/loongarch_pch_pic.h"
     13 #include "migration/vmstate.h"
     14 #include "trace.h"
     15 
     16 static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
     17 {
     18     uint64_t val;
     19     int irq;
     20 
     21     if (level) {
     22         val = mask & s->intirr & ~s->int_mask;
     23         if (val) {
     24             irq = ctz64(val);
     25             s->intisr |= MAKE_64BIT_MASK(irq, 1);
     26             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
     27         }
     28     } else {
     29         val = mask & s->intisr;
     30         if (val) {
     31             irq = ctz64(val);
     32             s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
     33             qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
     34         }
     35     }
     36 }
     37 
     38 static void pch_pic_irq_handler(void *opaque, int irq, int level)
     39 {
     40     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
     41     uint64_t mask = 1ULL << irq;
     42 
     43     assert(irq < PCH_PIC_IRQ_NUM);
     44     trace_loongarch_pch_pic_irq_handler(irq, level);
     45 
     46     if (s->intedge & mask) {
     47         /* Edge triggered */
     48         if (level) {
     49             if ((s->last_intirr & mask) == 0) {
     50                 s->intirr |= mask;
     51             }
     52             s->last_intirr |= mask;
     53         } else {
     54             s->last_intirr &= ~mask;
     55         }
     56     } else {
     57         /* Level triggered */
     58         if (level) {
     59             s->intirr |= mask;
     60             s->last_intirr |= mask;
     61         } else {
     62             s->intirr &= ~mask;
     63             s->last_intirr &= ~mask;
     64         }
     65     }
     66     pch_pic_update_irq(s, mask, level);
     67 }
     68 
     69 static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
     70                                             unsigned size)
     71 {
     72     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
     73     uint64_t val = 0;
     74     uint32_t offset = addr & 0xfff;
     75 
     76     switch (offset) {
     77     case PCH_PIC_INT_ID_LO:
     78         val = PCH_PIC_INT_ID_VAL;
     79         break;
     80     case PCH_PIC_INT_ID_HI:
     81         val = PCH_PIC_INT_ID_NUM;
     82         break;
     83     case PCH_PIC_INT_MASK_LO:
     84         val = (uint32_t)s->int_mask;
     85         break;
     86     case PCH_PIC_INT_MASK_HI:
     87         val = s->int_mask >> 32;
     88         break;
     89     case PCH_PIC_INT_EDGE_LO:
     90         val = (uint32_t)s->intedge;
     91         break;
     92     case PCH_PIC_INT_EDGE_HI:
     93         val = s->intedge >> 32;
     94         break;
     95     case PCH_PIC_HTMSI_EN_LO:
     96         val = (uint32_t)s->htmsi_en;
     97         break;
     98     case PCH_PIC_HTMSI_EN_HI:
     99         val = s->htmsi_en >> 32;
    100         break;
    101     case PCH_PIC_AUTO_CTRL0_LO:
    102     case PCH_PIC_AUTO_CTRL0_HI:
    103     case PCH_PIC_AUTO_CTRL1_LO:
    104     case PCH_PIC_AUTO_CTRL1_HI:
    105         break;
    106     default:
    107         break;
    108     }
    109 
    110     trace_loongarch_pch_pic_low_readw(size, addr, val);
    111     return val;
    112 }
    113 
    114 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
    115 {
    116     uint64_t mask = 0xffffffff00000000;
    117     uint64_t data = target;
    118 
    119     return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
    120 }
    121 
    122 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
    123                                          uint64_t value, unsigned size)
    124 {
    125     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
    126     uint32_t offset, old_valid, data = (uint32_t)value;
    127     uint64_t old, int_mask;
    128     offset = addr & 0xfff;
    129 
    130     trace_loongarch_pch_pic_low_writew(size, addr, data);
    131 
    132     switch (offset) {
    133     case PCH_PIC_INT_MASK_LO:
    134         old = s->int_mask;
    135         s->int_mask = get_writew_val(old, data, 0);
    136         old_valid = (uint32_t)old;
    137         if (old_valid & ~data) {
    138             pch_pic_update_irq(s, (old_valid & ~data), 1);
    139         }
    140         if (~old_valid & data) {
    141             pch_pic_update_irq(s, (~old_valid & data), 0);
    142         }
    143         break;
    144     case PCH_PIC_INT_MASK_HI:
    145         old = s->int_mask;
    146         s->int_mask = get_writew_val(old, data, 1);
    147         old_valid = (uint32_t)(old >> 32);
    148         int_mask = old_valid & ~data;
    149         if (int_mask) {
    150             pch_pic_update_irq(s, int_mask << 32, 1);
    151         }
    152         int_mask = ~old_valid & data;
    153         if (int_mask) {
    154             pch_pic_update_irq(s, int_mask << 32, 0);
    155         }
    156         break;
    157     case PCH_PIC_INT_EDGE_LO:
    158         s->intedge = get_writew_val(s->intedge, data, 0);
    159         break;
    160     case PCH_PIC_INT_EDGE_HI:
    161         s->intedge = get_writew_val(s->intedge, data, 1);
    162         break;
    163     case PCH_PIC_INT_CLEAR_LO:
    164         if (s->intedge & data) {
    165             s->intirr &= (~data);
    166             pch_pic_update_irq(s, data, 0);
    167             s->intisr &= (~data);
    168         }
    169         break;
    170     case PCH_PIC_INT_CLEAR_HI:
    171         value <<= 32;
    172         if (s->intedge & value) {
    173             s->intirr &= (~value);
    174             pch_pic_update_irq(s, value, 0);
    175             s->intisr &= (~value);
    176         }
    177         break;
    178     case PCH_PIC_HTMSI_EN_LO:
    179         s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
    180         break;
    181     case PCH_PIC_HTMSI_EN_HI:
    182         s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
    183         break;
    184     case PCH_PIC_AUTO_CTRL0_LO:
    185     case PCH_PIC_AUTO_CTRL0_HI:
    186     case PCH_PIC_AUTO_CTRL1_LO:
    187     case PCH_PIC_AUTO_CTRL1_HI:
    188         break;
    189     default:
    190         break;
    191     }
    192 }
    193 
    194 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
    195                                         unsigned size)
    196 {
    197     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
    198     uint64_t val = 0;
    199     uint32_t offset = addr & 0xfff;
    200 
    201     switch (offset) {
    202     case STATUS_LO_START:
    203         val = (uint32_t)(s->intisr & (~s->int_mask));
    204         break;
    205     case STATUS_HI_START:
    206         val = (s->intisr & (~s->int_mask)) >> 32;
    207         break;
    208     case POL_LO_START:
    209         val = (uint32_t)s->int_polarity;
    210         break;
    211     case POL_HI_START:
    212         val = s->int_polarity >> 32;
    213         break;
    214     default:
    215         break;
    216     }
    217 
    218     trace_loongarch_pch_pic_high_readw(size, addr, val);
    219     return val;
    220 }
    221 
    222 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
    223                                      uint64_t value, unsigned size)
    224 {
    225     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
    226     uint32_t offset, data = (uint32_t)value;
    227     offset = addr & 0xfff;
    228 
    229     trace_loongarch_pch_pic_high_writew(size, addr, data);
    230 
    231     switch (offset) {
    232     case STATUS_LO_START:
    233         s->intisr = get_writew_val(s->intisr, data, 0);
    234         break;
    235     case STATUS_HI_START:
    236         s->intisr = get_writew_val(s->intisr, data, 1);
    237         break;
    238     case POL_LO_START:
    239         s->int_polarity = get_writew_val(s->int_polarity, data, 0);
    240         break;
    241     case POL_HI_START:
    242         s->int_polarity = get_writew_val(s->int_polarity, data, 1);
    243         break;
    244     default:
    245         break;
    246     }
    247 }
    248 
    249 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
    250                                         unsigned size)
    251 {
    252     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
    253     uint64_t val = 0;
    254     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
    255     int64_t offset_tmp;
    256 
    257     switch (offset) {
    258     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
    259         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
    260         if (offset_tmp >= 0 && offset_tmp < 64) {
    261             val = s->htmsi_vector[offset_tmp];
    262         }
    263         break;
    264     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
    265         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
    266         if (offset_tmp >= 0 && offset_tmp < 64) {
    267             val = s->route_entry[offset_tmp];
    268         }
    269         break;
    270     default:
    271         break;
    272     }
    273 
    274     trace_loongarch_pch_pic_readb(size, addr, val);
    275     return val;
    276 }
    277 
    278 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
    279                                      uint64_t data, unsigned size)
    280 {
    281     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
    282     int32_t offset_tmp;
    283     uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
    284 
    285     trace_loongarch_pch_pic_writeb(size, addr, data);
    286 
    287     switch (offset) {
    288     case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
    289         offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
    290         if (offset_tmp >= 0 && offset_tmp < 64) {
    291             s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
    292         }
    293         break;
    294     case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
    295         offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
    296         if (offset_tmp >= 0 && offset_tmp < 64) {
    297             s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
    298         }
    299         break;
    300     default:
    301         break;
    302     }
    303 }
    304 
    305 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
    306     .read = loongarch_pch_pic_low_readw,
    307     .write = loongarch_pch_pic_low_writew,
    308     .valid = {
    309         .min_access_size = 4,
    310         .max_access_size = 8,
    311     },
    312     .impl = {
    313         .min_access_size = 4,
    314         .max_access_size = 4,
    315     },
    316     .endianness = DEVICE_LITTLE_ENDIAN,
    317 };
    318 
    319 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
    320     .read = loongarch_pch_pic_high_readw,
    321     .write = loongarch_pch_pic_high_writew,
    322     .valid = {
    323         .min_access_size = 4,
    324         .max_access_size = 8,
    325     },
    326     .impl = {
    327         .min_access_size = 4,
    328         .max_access_size = 4,
    329     },
    330     .endianness = DEVICE_LITTLE_ENDIAN,
    331 };
    332 
    333 static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
    334     .read = loongarch_pch_pic_readb,
    335     .write = loongarch_pch_pic_writeb,
    336     .valid = {
    337         .min_access_size = 1,
    338         .max_access_size = 1,
    339     },
    340     .impl = {
    341         .min_access_size = 1,
    342         .max_access_size = 1,
    343     },
    344     .endianness = DEVICE_LITTLE_ENDIAN,
    345 };
    346 
    347 static void loongarch_pch_pic_reset(DeviceState *d)
    348 {
    349     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d);
    350     int i;
    351 
    352     s->int_mask = -1;
    353     s->htmsi_en = 0x0;
    354     s->intedge  = 0x0;
    355     s->intclr   = 0x0;
    356     s->auto_crtl0 = 0x0;
    357     s->auto_crtl1 = 0x0;
    358     for (i = 0; i < 64; i++) {
    359         s->route_entry[i] = 0x1;
    360         s->htmsi_vector[i] = 0x0;
    361     }
    362     s->intirr = 0x0;
    363     s->intisr = 0x0;
    364     s->last_intirr = 0x0;
    365     s->int_polarity = 0x0;
    366 }
    367 
    368 static void loongarch_pch_pic_init(Object *obj)
    369 {
    370     LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj);
    371     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    372 
    373     memory_region_init_io(&s->iomem32_low, obj,
    374                           &loongarch_pch_pic_reg32_low_ops,
    375                           s, PCH_PIC_NAME(.reg32_part1), 0x100);
    376     memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops,
    377                           s, PCH_PIC_NAME(.reg8), 0x2a0);
    378     memory_region_init_io(&s->iomem32_high, obj,
    379                           &loongarch_pch_pic_reg32_high_ops,
    380                           s, PCH_PIC_NAME(.reg32_part2), 0xc60);
    381     sysbus_init_mmio(sbd, &s->iomem32_low);
    382     sysbus_init_mmio(sbd, &s->iomem8);
    383     sysbus_init_mmio(sbd, &s->iomem32_high);
    384 
    385     qdev_init_gpio_out(DEVICE(obj), s->parent_irq, PCH_PIC_IRQ_NUM);
    386     qdev_init_gpio_in(DEVICE(obj), pch_pic_irq_handler, PCH_PIC_IRQ_NUM);
    387 }
    388 
    389 static const VMStateDescription vmstate_loongarch_pch_pic = {
    390     .name = TYPE_LOONGARCH_PCH_PIC,
    391     .version_id = 1,
    392     .minimum_version_id = 1,
    393     .fields = (VMStateField[]) {
    394         VMSTATE_UINT64(int_mask, LoongArchPCHPIC),
    395         VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC),
    396         VMSTATE_UINT64(intedge, LoongArchPCHPIC),
    397         VMSTATE_UINT64(intclr, LoongArchPCHPIC),
    398         VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC),
    399         VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC),
    400         VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64),
    401         VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64),
    402         VMSTATE_UINT64(last_intirr, LoongArchPCHPIC),
    403         VMSTATE_UINT64(intirr, LoongArchPCHPIC),
    404         VMSTATE_UINT64(intisr, LoongArchPCHPIC),
    405         VMSTATE_UINT64(int_polarity, LoongArchPCHPIC),
    406         VMSTATE_END_OF_LIST()
    407     }
    408 };
    409 
    410 static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
    411 {
    412     DeviceClass *dc = DEVICE_CLASS(klass);
    413 
    414     dc->reset = loongarch_pch_pic_reset;
    415     dc->vmsd = &vmstate_loongarch_pch_pic;
    416 }
    417 
    418 static const TypeInfo loongarch_pch_pic_info = {
    419     .name          = TYPE_LOONGARCH_PCH_PIC,
    420     .parent        = TYPE_SYS_BUS_DEVICE,
    421     .instance_size = sizeof(LoongArchPCHPIC),
    422     .instance_init = loongarch_pch_pic_init,
    423     .class_init    = loongarch_pch_pic_class_init,
    424 };
    425 
    426 static void loongarch_pch_pic_register_types(void)
    427 {
    428     type_register_static(&loongarch_pch_pic_info);
    429 }
    430 
    431 type_init(loongarch_pch_pic_register_types)