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" 1110a8f7d2SBibo Mao #include "qapi/error.h" 12cbff2db1SXiaojuan Yang #include "hw/irq.h" 13cbff2db1SXiaojuan Yang #include "hw/loongarch/virt.h" 14cbff2db1SXiaojuan Yang #include "exec/address-spaces.h" 15cbff2db1SXiaojuan Yang #include "hw/intc/loongarch_extioi.h" 16cbff2db1SXiaojuan Yang #include "trace.h" 17cbff2db1SXiaojuan Yang 18c3afa714SBibo Mao static int extioi_get_index_from_archid(LoongArchExtIOICommonState *s, 19c3afa714SBibo Mao uint64_t arch_id) 20c3afa714SBibo Mao { 21c3afa714SBibo Mao int i; 22c3afa714SBibo Mao 23c3afa714SBibo Mao for (i = 0; i < s->num_cpu; i++) { 24c3afa714SBibo Mao if (s->cpu[i].arch_id == arch_id) { 25c3afa714SBibo Mao break; 26c3afa714SBibo Mao } 27c3afa714SBibo Mao } 28c3afa714SBibo Mao 29c3afa714SBibo Mao if ((i < s->num_cpu) && s->cpu[i].cpu) { 30c3afa714SBibo Mao return i; 31c3afa714SBibo Mao } 32c3afa714SBibo Mao 33c3afa714SBibo Mao return -1; 34c3afa714SBibo Mao } 35cbff2db1SXiaojuan Yang 366f6006adSBibo Mao static void extioi_update_irq(LoongArchExtIOICommonState *s, int irq, int level) 37cbff2db1SXiaojuan Yang { 38cbff2db1SXiaojuan Yang int ipnum, cpu, found, irq_index, irq_mask; 39cbff2db1SXiaojuan Yang 40cbff2db1SXiaojuan Yang ipnum = s->sw_ipmap[irq / 32]; 41cbff2db1SXiaojuan Yang cpu = s->sw_coremap[irq]; 42cbff2db1SXiaojuan Yang irq_index = irq / 32; 43cbff2db1SXiaojuan Yang irq_mask = 1 << (irq & 0x1f); 44cbff2db1SXiaojuan Yang 45cbff2db1SXiaojuan Yang if (level) { 46cbff2db1SXiaojuan Yang /* if not enable return false */ 47cbff2db1SXiaojuan Yang if (((s->enable[irq_index]) & irq_mask) == 0) { 48cbff2db1SXiaojuan Yang return; 49cbff2db1SXiaojuan Yang } 5010a8f7d2SBibo Mao s->cpu[cpu].coreisr[irq_index] |= irq_mask; 5110a8f7d2SBibo Mao found = find_first_bit(s->cpu[cpu].sw_isr[ipnum], EXTIOI_IRQS); 5210a8f7d2SBibo Mao set_bit(irq, s->cpu[cpu].sw_isr[ipnum]); 53cbff2db1SXiaojuan Yang if (found < EXTIOI_IRQS) { 54cbff2db1SXiaojuan Yang /* other irq is handling, need not update parent irq level */ 55cbff2db1SXiaojuan Yang return; 56cbff2db1SXiaojuan Yang } 57cbff2db1SXiaojuan Yang } else { 5810a8f7d2SBibo Mao s->cpu[cpu].coreisr[irq_index] &= ~irq_mask; 5910a8f7d2SBibo Mao clear_bit(irq, s->cpu[cpu].sw_isr[ipnum]); 6010a8f7d2SBibo Mao found = find_first_bit(s->cpu[cpu].sw_isr[ipnum], EXTIOI_IRQS); 61cbff2db1SXiaojuan Yang if (found < EXTIOI_IRQS) { 62cbff2db1SXiaojuan Yang /* other irq is handling, need not update parent irq level */ 63cbff2db1SXiaojuan Yang return; 64cbff2db1SXiaojuan Yang } 65cbff2db1SXiaojuan Yang } 6610a8f7d2SBibo Mao qemu_set_irq(s->cpu[cpu].parent_irq[ipnum], level); 67cbff2db1SXiaojuan Yang } 68cbff2db1SXiaojuan Yang 69cbff2db1SXiaojuan Yang static void extioi_setirq(void *opaque, int irq, int level) 70cbff2db1SXiaojuan Yang { 716f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); 72cbff2db1SXiaojuan Yang trace_loongarch_extioi_setirq(irq, level); 73cbff2db1SXiaojuan Yang if (level) { 74335be5bcSPeter Maydell set_bit32(irq, s->isr); 75cbff2db1SXiaojuan Yang } else { 76335be5bcSPeter Maydell clear_bit32(irq, s->isr); 77cbff2db1SXiaojuan Yang } 78cbff2db1SXiaojuan Yang extioi_update_irq(s, irq, level); 79cbff2db1SXiaojuan Yang } 80cbff2db1SXiaojuan Yang 813fc8f74bSXiaojuan Yang static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, 823fc8f74bSXiaojuan Yang unsigned size, MemTxAttrs attrs) 83cbff2db1SXiaojuan Yang { 846f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); 85cbff2db1SXiaojuan Yang unsigned long offset = addr & 0xffff; 863fc8f74bSXiaojuan Yang uint32_t index, cpu; 87cbff2db1SXiaojuan Yang 88cbff2db1SXiaojuan Yang switch (offset) { 89cbff2db1SXiaojuan Yang case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1: 90cbff2db1SXiaojuan Yang index = (offset - EXTIOI_NODETYPE_START) >> 2; 913fc8f74bSXiaojuan Yang *data = s->nodetype[index]; 92cbff2db1SXiaojuan Yang break; 93cbff2db1SXiaojuan Yang case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1: 94cbff2db1SXiaojuan Yang index = (offset - EXTIOI_IPMAP_START) >> 2; 953fc8f74bSXiaojuan Yang *data = s->ipmap[index]; 96cbff2db1SXiaojuan Yang break; 97cbff2db1SXiaojuan Yang case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1: 98cbff2db1SXiaojuan Yang index = (offset - EXTIOI_ENABLE_START) >> 2; 993fc8f74bSXiaojuan Yang *data = s->enable[index]; 100cbff2db1SXiaojuan Yang break; 101cbff2db1SXiaojuan Yang case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1: 102cbff2db1SXiaojuan Yang index = (offset - EXTIOI_BOUNCE_START) >> 2; 1033fc8f74bSXiaojuan Yang *data = s->bounce[index]; 104cbff2db1SXiaojuan Yang break; 105cbff2db1SXiaojuan Yang case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1: 106a649fffcSXiaojuan Yang index = (offset - EXTIOI_COREISR_START) >> 2; 107a649fffcSXiaojuan Yang /* using attrs to get current cpu index */ 108a649fffcSXiaojuan Yang cpu = attrs.requester_id; 10910a8f7d2SBibo Mao *data = s->cpu[cpu].coreisr[index]; 110cbff2db1SXiaojuan Yang break; 111cbff2db1SXiaojuan Yang case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: 112cbff2db1SXiaojuan Yang index = (offset - EXTIOI_COREMAP_START) >> 2; 1133fc8f74bSXiaojuan Yang *data = s->coremap[index]; 114cbff2db1SXiaojuan Yang break; 115cbff2db1SXiaojuan Yang default: 116cbff2db1SXiaojuan Yang break; 117cbff2db1SXiaojuan Yang } 118cbff2db1SXiaojuan Yang 1193fc8f74bSXiaojuan Yang trace_loongarch_extioi_readw(addr, *data); 1203fc8f74bSXiaojuan Yang return MEMTX_OK; 121cbff2db1SXiaojuan Yang } 122cbff2db1SXiaojuan Yang 1236f6006adSBibo Mao static inline void extioi_enable_irq(LoongArchExtIOICommonState *s, int index,\ 124cbff2db1SXiaojuan Yang uint32_t mask, int level) 125cbff2db1SXiaojuan Yang { 126cbff2db1SXiaojuan Yang uint32_t val; 127cbff2db1SXiaojuan Yang int irq; 128cbff2db1SXiaojuan Yang 129cbff2db1SXiaojuan Yang val = mask & s->isr[index]; 130cbff2db1SXiaojuan Yang irq = ctz32(val); 131cbff2db1SXiaojuan Yang while (irq != 32) { 132cbff2db1SXiaojuan Yang /* 133cbff2db1SXiaojuan Yang * enable bit change from 0 to 1, 134cbff2db1SXiaojuan Yang * need to update irq by pending bits 135cbff2db1SXiaojuan Yang */ 136cbff2db1SXiaojuan Yang extioi_update_irq(s, irq + index * 32, level); 137cbff2db1SXiaojuan Yang val &= ~(1 << irq); 138cbff2db1SXiaojuan Yang irq = ctz32(val); 139cbff2db1SXiaojuan Yang } 140cbff2db1SXiaojuan Yang } 141cbff2db1SXiaojuan Yang 1426f6006adSBibo Mao static inline void extioi_update_sw_coremap(LoongArchExtIOICommonState *s, 1436f6006adSBibo Mao int irq, uint64_t val, bool notify) 144428a6ef4SBibo Mao { 145c3afa714SBibo Mao int i, cpu, cpuid; 146428a6ef4SBibo Mao 147428a6ef4SBibo Mao /* 148428a6ef4SBibo Mao * loongarch only support little endian, 149428a6ef4SBibo Mao * so we paresd the value with little endian. 150428a6ef4SBibo Mao */ 151428a6ef4SBibo Mao val = cpu_to_le64(val); 152428a6ef4SBibo Mao 153428a6ef4SBibo Mao for (i = 0; i < 4; i++) { 154c3afa714SBibo Mao cpuid = val & 0xff; 155dc6f37ebSSong Gao val = val >> 8; 156dc6f37ebSSong Gao 157dc6f37ebSSong Gao if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { 158c3afa714SBibo Mao cpuid = ctz32(cpuid); 159c3afa714SBibo Mao cpuid = (cpuid >= 4) ? 0 : cpuid; 160c3afa714SBibo Mao } 161c3afa714SBibo Mao 162c3afa714SBibo Mao cpu = extioi_get_index_from_archid(s, cpuid); 163c3afa714SBibo Mao if (cpu < 0) { 164c3afa714SBibo Mao continue; 165dc6f37ebSSong Gao } 166428a6ef4SBibo Mao 167428a6ef4SBibo Mao if (s->sw_coremap[irq + i] == cpu) { 168428a6ef4SBibo Mao continue; 169428a6ef4SBibo Mao } 170428a6ef4SBibo Mao 171335be5bcSPeter Maydell if (notify && test_bit32(irq + i, s->isr)) { 172428a6ef4SBibo Mao /* 173428a6ef4SBibo Mao * lower irq at old cpu and raise irq at new cpu 174428a6ef4SBibo Mao */ 175428a6ef4SBibo Mao extioi_update_irq(s, irq + i, 0); 176428a6ef4SBibo Mao s->sw_coremap[irq + i] = cpu; 177428a6ef4SBibo Mao extioi_update_irq(s, irq + i, 1); 178428a6ef4SBibo Mao } else { 179428a6ef4SBibo Mao s->sw_coremap[irq + i] = cpu; 180428a6ef4SBibo Mao } 181428a6ef4SBibo Mao } 182428a6ef4SBibo Mao } 183428a6ef4SBibo Mao 1846f6006adSBibo Mao static inline void extioi_update_sw_ipmap(LoongArchExtIOICommonState *s, 1856f6006adSBibo Mao int index, uint64_t val) 186428a6ef4SBibo Mao { 187428a6ef4SBibo Mao int i; 188428a6ef4SBibo Mao uint8_t ipnum; 189428a6ef4SBibo Mao 190428a6ef4SBibo Mao /* 191428a6ef4SBibo Mao * loongarch only support little endian, 192428a6ef4SBibo Mao * so we paresd the value with little endian. 193428a6ef4SBibo Mao */ 194428a6ef4SBibo Mao val = cpu_to_le64(val); 195428a6ef4SBibo Mao for (i = 0; i < 4; i++) { 196428a6ef4SBibo Mao ipnum = val & 0xff; 197428a6ef4SBibo Mao ipnum = ctz32(ipnum); 198428a6ef4SBibo Mao ipnum = (ipnum >= 4) ? 0 : ipnum; 199428a6ef4SBibo Mao s->sw_ipmap[index * 4 + i] = ipnum; 200428a6ef4SBibo Mao val = val >> 8; 201428a6ef4SBibo Mao } 202428a6ef4SBibo Mao } 203428a6ef4SBibo Mao 2043fc8f74bSXiaojuan Yang static MemTxResult extioi_writew(void *opaque, hwaddr addr, 2053fc8f74bSXiaojuan Yang uint64_t val, unsigned size, 2063fc8f74bSXiaojuan Yang MemTxAttrs attrs) 207cbff2db1SXiaojuan Yang { 2086f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); 209428a6ef4SBibo Mao int cpu, index, old_data, irq; 210cbff2db1SXiaojuan Yang uint32_t offset; 211cbff2db1SXiaojuan Yang 212cbff2db1SXiaojuan Yang trace_loongarch_extioi_writew(addr, val); 213cbff2db1SXiaojuan Yang offset = addr & 0xffff; 214cbff2db1SXiaojuan Yang 215cbff2db1SXiaojuan Yang switch (offset) { 216cbff2db1SXiaojuan Yang case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1: 217cbff2db1SXiaojuan Yang index = (offset - EXTIOI_NODETYPE_START) >> 2; 218cbff2db1SXiaojuan Yang s->nodetype[index] = val; 219cbff2db1SXiaojuan Yang break; 220cbff2db1SXiaojuan Yang case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1: 221cbff2db1SXiaojuan Yang /* 222cbff2db1SXiaojuan Yang * ipmap cannot be set at runtime, can be set only at the beginning 223cbff2db1SXiaojuan Yang * of intr driver, need not update upper irq level 224cbff2db1SXiaojuan Yang */ 225cbff2db1SXiaojuan Yang index = (offset - EXTIOI_IPMAP_START) >> 2; 226cbff2db1SXiaojuan Yang s->ipmap[index] = val; 227428a6ef4SBibo Mao extioi_update_sw_ipmap(s, index, val); 228cbff2db1SXiaojuan Yang break; 229cbff2db1SXiaojuan Yang case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1: 230cbff2db1SXiaojuan Yang index = (offset - EXTIOI_ENABLE_START) >> 2; 231cbff2db1SXiaojuan Yang old_data = s->enable[index]; 232cbff2db1SXiaojuan Yang s->enable[index] = val; 233cbff2db1SXiaojuan Yang 234cbff2db1SXiaojuan Yang /* unmask irq */ 235cbff2db1SXiaojuan Yang val = s->enable[index] & ~old_data; 236cbff2db1SXiaojuan Yang extioi_enable_irq(s, index, val, 1); 237cbff2db1SXiaojuan Yang 238cbff2db1SXiaojuan Yang /* mask irq */ 239cbff2db1SXiaojuan Yang val = ~s->enable[index] & old_data; 240cbff2db1SXiaojuan Yang extioi_enable_irq(s, index, val, 0); 241cbff2db1SXiaojuan Yang break; 242cbff2db1SXiaojuan Yang case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1: 243cbff2db1SXiaojuan Yang /* do not emulate hw bounced irq routing */ 244cbff2db1SXiaojuan Yang index = (offset - EXTIOI_BOUNCE_START) >> 2; 245cbff2db1SXiaojuan Yang s->bounce[index] = val; 246cbff2db1SXiaojuan Yang break; 247cbff2db1SXiaojuan Yang case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1: 248a649fffcSXiaojuan Yang index = (offset - EXTIOI_COREISR_START) >> 2; 249a649fffcSXiaojuan Yang /* using attrs to get current cpu index */ 250a649fffcSXiaojuan Yang cpu = attrs.requester_id; 25110a8f7d2SBibo Mao old_data = s->cpu[cpu].coreisr[index]; 25210a8f7d2SBibo Mao s->cpu[cpu].coreisr[index] = old_data & ~val; 2539b4b4e51SMichael Tokarev /* write 1 to clear interrupt */ 254cbff2db1SXiaojuan Yang old_data &= val; 255cbff2db1SXiaojuan Yang irq = ctz32(old_data); 256cbff2db1SXiaojuan Yang while (irq != 32) { 257cbff2db1SXiaojuan Yang extioi_update_irq(s, irq + index * 32, 0); 258cbff2db1SXiaojuan Yang old_data &= ~(1 << irq); 259cbff2db1SXiaojuan Yang irq = ctz32(old_data); 260cbff2db1SXiaojuan Yang } 261cbff2db1SXiaojuan Yang break; 262cbff2db1SXiaojuan Yang case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: 263cbff2db1SXiaojuan Yang irq = offset - EXTIOI_COREMAP_START; 264cbff2db1SXiaojuan Yang index = irq / 4; 265cbff2db1SXiaojuan Yang s->coremap[index] = val; 266cbff2db1SXiaojuan Yang 267428a6ef4SBibo Mao extioi_update_sw_coremap(s, irq, val, true); 268cbff2db1SXiaojuan Yang break; 269cbff2db1SXiaojuan Yang default: 270cbff2db1SXiaojuan Yang break; 271cbff2db1SXiaojuan Yang } 2723fc8f74bSXiaojuan Yang return MEMTX_OK; 273cbff2db1SXiaojuan Yang } 274cbff2db1SXiaojuan Yang 275cbff2db1SXiaojuan Yang static const MemoryRegionOps extioi_ops = { 2763fc8f74bSXiaojuan Yang .read_with_attrs = extioi_readw, 2773fc8f74bSXiaojuan Yang .write_with_attrs = extioi_writew, 278cbff2db1SXiaojuan Yang .impl.min_access_size = 4, 279cbff2db1SXiaojuan Yang .impl.max_access_size = 4, 280cbff2db1SXiaojuan Yang .valid.min_access_size = 4, 281cbff2db1SXiaojuan Yang .valid.max_access_size = 8, 282cbff2db1SXiaojuan Yang .endianness = DEVICE_LITTLE_ENDIAN, 283cbff2db1SXiaojuan Yang }; 284cbff2db1SXiaojuan Yang 285dc6f37ebSSong Gao static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data, 286dc6f37ebSSong Gao unsigned size, MemTxAttrs attrs) 287dc6f37ebSSong Gao { 2886f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); 289dc6f37ebSSong Gao 290dc6f37ebSSong Gao switch (addr) { 291dc6f37ebSSong Gao case EXTIOI_VIRT_FEATURES: 292dc6f37ebSSong Gao *data = s->features; 293dc6f37ebSSong Gao break; 294dc6f37ebSSong Gao case EXTIOI_VIRT_CONFIG: 295dc6f37ebSSong Gao *data = s->status; 296dc6f37ebSSong Gao break; 297dc6f37ebSSong Gao default: 298dc6f37ebSSong Gao g_assert_not_reached(); 299dc6f37ebSSong Gao } 300dc6f37ebSSong Gao 301dc6f37ebSSong Gao return MEMTX_OK; 302dc6f37ebSSong Gao } 303dc6f37ebSSong Gao 304dc6f37ebSSong Gao static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr, 305dc6f37ebSSong Gao uint64_t val, unsigned size, 306dc6f37ebSSong Gao MemTxAttrs attrs) 307dc6f37ebSSong Gao { 3086f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); 309dc6f37ebSSong Gao 310dc6f37ebSSong Gao switch (addr) { 311dc6f37ebSSong Gao case EXTIOI_VIRT_FEATURES: 312dc6f37ebSSong Gao return MEMTX_ACCESS_ERROR; 313dc6f37ebSSong Gao 314dc6f37ebSSong Gao case EXTIOI_VIRT_CONFIG: 315dc6f37ebSSong Gao /* 316dc6f37ebSSong Gao * extioi features can only be set at disabled status 317dc6f37ebSSong Gao */ 318dc6f37ebSSong Gao if ((s->status & BIT(EXTIOI_ENABLE)) && val) { 319dc6f37ebSSong Gao return MEMTX_ACCESS_ERROR; 320dc6f37ebSSong Gao } 321dc6f37ebSSong Gao 322dc6f37ebSSong Gao s->status = val & s->features; 323dc6f37ebSSong Gao break; 324dc6f37ebSSong Gao default: 325dc6f37ebSSong Gao g_assert_not_reached(); 326dc6f37ebSSong Gao } 327dc6f37ebSSong Gao return MEMTX_OK; 328dc6f37ebSSong Gao } 329dc6f37ebSSong Gao 330dc6f37ebSSong Gao static const MemoryRegionOps extioi_virt_ops = { 331dc6f37ebSSong Gao .read_with_attrs = extioi_virt_readw, 332dc6f37ebSSong Gao .write_with_attrs = extioi_virt_writew, 333dc6f37ebSSong Gao .impl.min_access_size = 4, 334dc6f37ebSSong Gao .impl.max_access_size = 4, 335dc6f37ebSSong Gao .valid.min_access_size = 4, 336dc6f37ebSSong Gao .valid.max_access_size = 8, 337dc6f37ebSSong Gao .endianness = DEVICE_LITTLE_ENDIAN, 338dc6f37ebSSong Gao }; 339dc6f37ebSSong Gao 34010a8f7d2SBibo Mao static void loongarch_extioi_realize(DeviceState *dev, Error **errp) 34110a8f7d2SBibo Mao { 342272c467aSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); 343272c467aSBibo Mao LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(dev); 34410a8f7d2SBibo Mao SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 345aa6330d5SBibo Mao Error *local_err = NULL; 346*8b4b668fSBibo Mao int i; 34710a8f7d2SBibo Mao 348272c467aSBibo Mao lec->parent_realize(dev, &local_err); 349aa6330d5SBibo Mao if (local_err) { 350aa6330d5SBibo Mao error_propagate(errp, local_err); 35110a8f7d2SBibo Mao return; 35210a8f7d2SBibo Mao } 35310a8f7d2SBibo Mao 35410a8f7d2SBibo Mao for (i = 0; i < EXTIOI_IRQS; i++) { 35510a8f7d2SBibo Mao sysbus_init_irq(sbd, &s->irq[i]); 35610a8f7d2SBibo Mao } 35710a8f7d2SBibo Mao 35810a8f7d2SBibo Mao qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS); 35910a8f7d2SBibo Mao memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, 36010a8f7d2SBibo Mao s, "extioi_system_mem", 0x900); 36110a8f7d2SBibo Mao sysbus_init_mmio(sbd, &s->extioi_system_mem); 362dc6f37ebSSong Gao 363dc6f37ebSSong Gao if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { 364dc6f37ebSSong Gao memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops, 365dc6f37ebSSong Gao s, "extioi_virt", EXTIOI_VIRT_SIZE); 366dc6f37ebSSong Gao sysbus_init_mmio(sbd, &s->virt_extend); 367dc6f37ebSSong Gao s->features |= EXTIOI_VIRT_HAS_FEATURES; 368dc6f37ebSSong Gao } else { 369dc6f37ebSSong Gao s->status |= BIT(EXTIOI_ENABLE); 370dc6f37ebSSong Gao } 37110a8f7d2SBibo Mao } 37210a8f7d2SBibo Mao 3734abf4712SBibo Mao static void loongarch_extioi_unrealize(DeviceState *dev) 37410a8f7d2SBibo Mao { 3756f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); 37610a8f7d2SBibo Mao 37710a8f7d2SBibo Mao g_free(s->cpu); 37810a8f7d2SBibo Mao } 37910a8f7d2SBibo Mao 380dc6f37ebSSong Gao static void loongarch_extioi_reset(DeviceState *d) 381dc6f37ebSSong Gao { 3826f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(d); 383dc6f37ebSSong Gao 384dc6f37ebSSong Gao s->status = 0; 385dc6f37ebSSong Gao } 386dc6f37ebSSong Gao 387428a6ef4SBibo Mao static int vmstate_extioi_post_load(void *opaque, int version_id) 388428a6ef4SBibo Mao { 3896f6006adSBibo Mao LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); 390428a6ef4SBibo Mao int i, start_irq; 391428a6ef4SBibo Mao 392428a6ef4SBibo Mao for (i = 0; i < (EXTIOI_IRQS / 4); i++) { 393428a6ef4SBibo Mao start_irq = i * 4; 394428a6ef4SBibo Mao extioi_update_sw_coremap(s, start_irq, s->coremap[i], false); 395428a6ef4SBibo Mao } 396428a6ef4SBibo Mao 397428a6ef4SBibo Mao for (i = 0; i < (EXTIOI_IRQS_IPMAP_SIZE / 4); i++) { 398428a6ef4SBibo Mao extioi_update_sw_ipmap(s, i, s->ipmap[i]); 399428a6ef4SBibo Mao } 400428a6ef4SBibo Mao 401428a6ef4SBibo Mao return 0; 402428a6ef4SBibo Mao } 403428a6ef4SBibo Mao 404cbff2db1SXiaojuan Yang static void loongarch_extioi_class_init(ObjectClass *klass, void *data) 405cbff2db1SXiaojuan Yang { 406cbff2db1SXiaojuan Yang DeviceClass *dc = DEVICE_CLASS(klass); 407272c467aSBibo Mao LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_CLASS(klass); 408272c467aSBibo Mao LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); 409cbff2db1SXiaojuan Yang 410272c467aSBibo Mao device_class_set_parent_realize(dc, loongarch_extioi_realize, 411272c467aSBibo Mao &lec->parent_realize); 412272c467aSBibo Mao device_class_set_parent_unrealize(dc, loongarch_extioi_unrealize, 413272c467aSBibo Mao &lec->parent_unrealize); 414e3d08143SPeter Maydell device_class_set_legacy_reset(dc, loongarch_extioi_reset); 415272c467aSBibo Mao lecc->post_load = vmstate_extioi_post_load; 416cbff2db1SXiaojuan Yang } 417cbff2db1SXiaojuan Yang 418272c467aSBibo Mao static const TypeInfo loongarch_extioi_types[] = { 419272c467aSBibo Mao { 420cbff2db1SXiaojuan Yang .name = TYPE_LOONGARCH_EXTIOI, 421272c467aSBibo Mao .parent = TYPE_LOONGARCH_EXTIOI_COMMON, 422272c467aSBibo Mao .instance_size = sizeof(LoongArchExtIOIState), 423272c467aSBibo Mao .class_size = sizeof(LoongArchExtIOIClass), 424cbff2db1SXiaojuan Yang .class_init = loongarch_extioi_class_init, 425272c467aSBibo Mao } 426cbff2db1SXiaojuan Yang }; 427cbff2db1SXiaojuan Yang 428272c467aSBibo Mao DEFINE_TYPES(loongarch_extioi_types) 429