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