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 713fc8f74bSXiaojuan Yang static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, 723fc8f74bSXiaojuan Yang unsigned size, MemTxAttrs attrs) 73cbff2db1SXiaojuan Yang { 74cbff2db1SXiaojuan Yang LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); 75cbff2db1SXiaojuan Yang unsigned long offset = addr & 0xffff; 763fc8f74bSXiaojuan 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; 813fc8f74bSXiaojuan 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; 853fc8f74bSXiaojuan 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; 893fc8f74bSXiaojuan 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; 933fc8f74bSXiaojuan Yang *data = s->bounce[index]; 94cbff2db1SXiaojuan Yang break; 95cbff2db1SXiaojuan Yang case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1: 96a649fffcSXiaojuan Yang index = (offset - EXTIOI_COREISR_START) >> 2; 97a649fffcSXiaojuan Yang /* using attrs to get current cpu index */ 98a649fffcSXiaojuan Yang cpu = attrs.requester_id; 993fc8f74bSXiaojuan Yang *data = s->coreisr[cpu][index]; 100cbff2db1SXiaojuan Yang break; 101cbff2db1SXiaojuan Yang case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: 102cbff2db1SXiaojuan Yang index = (offset - EXTIOI_COREMAP_START) >> 2; 1033fc8f74bSXiaojuan Yang *data = s->coremap[index]; 104cbff2db1SXiaojuan Yang break; 105cbff2db1SXiaojuan Yang default: 106cbff2db1SXiaojuan Yang break; 107cbff2db1SXiaojuan Yang } 108cbff2db1SXiaojuan Yang 1093fc8f74bSXiaojuan Yang trace_loongarch_extioi_readw(addr, *data); 1103fc8f74bSXiaojuan Yang return MEMTX_OK; 111cbff2db1SXiaojuan Yang } 112cbff2db1SXiaojuan Yang 113cbff2db1SXiaojuan Yang static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ 114cbff2db1SXiaojuan Yang uint32_t mask, int level) 115cbff2db1SXiaojuan Yang { 116cbff2db1SXiaojuan Yang uint32_t val; 117cbff2db1SXiaojuan Yang int irq; 118cbff2db1SXiaojuan Yang 119cbff2db1SXiaojuan Yang val = mask & s->isr[index]; 120cbff2db1SXiaojuan Yang irq = ctz32(val); 121cbff2db1SXiaojuan Yang while (irq != 32) { 122cbff2db1SXiaojuan Yang /* 123cbff2db1SXiaojuan Yang * enable bit change from 0 to 1, 124cbff2db1SXiaojuan Yang * need to update irq by pending bits 125cbff2db1SXiaojuan Yang */ 126cbff2db1SXiaojuan Yang extioi_update_irq(s, irq + index * 32, level); 127cbff2db1SXiaojuan Yang val &= ~(1 << irq); 128cbff2db1SXiaojuan Yang irq = ctz32(val); 129cbff2db1SXiaojuan Yang } 130cbff2db1SXiaojuan Yang } 131cbff2db1SXiaojuan Yang 1323fc8f74bSXiaojuan Yang static MemTxResult extioi_writew(void *opaque, hwaddr addr, 1333fc8f74bSXiaojuan Yang uint64_t val, unsigned size, 1343fc8f74bSXiaojuan Yang MemTxAttrs attrs) 135cbff2db1SXiaojuan Yang { 136cbff2db1SXiaojuan Yang LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); 137cbff2db1SXiaojuan Yang int i, cpu, index, old_data, irq; 138cbff2db1SXiaojuan Yang uint32_t offset; 139cbff2db1SXiaojuan Yang 140cbff2db1SXiaojuan Yang trace_loongarch_extioi_writew(addr, val); 141cbff2db1SXiaojuan Yang offset = addr & 0xffff; 142cbff2db1SXiaojuan Yang 143cbff2db1SXiaojuan Yang switch (offset) { 144cbff2db1SXiaojuan Yang case EXTIOI_NODETYPE_START ... EXTIOI_NODETYPE_END - 1: 145cbff2db1SXiaojuan Yang index = (offset - EXTIOI_NODETYPE_START) >> 2; 146cbff2db1SXiaojuan Yang s->nodetype[index] = val; 147cbff2db1SXiaojuan Yang break; 148cbff2db1SXiaojuan Yang case EXTIOI_IPMAP_START ... EXTIOI_IPMAP_END - 1: 149cbff2db1SXiaojuan Yang /* 150cbff2db1SXiaojuan Yang * ipmap cannot be set at runtime, can be set only at the beginning 151cbff2db1SXiaojuan Yang * of intr driver, need not update upper irq level 152cbff2db1SXiaojuan Yang */ 153cbff2db1SXiaojuan Yang index = (offset - EXTIOI_IPMAP_START) >> 2; 154cbff2db1SXiaojuan Yang s->ipmap[index] = val; 155cbff2db1SXiaojuan Yang /* 156cbff2db1SXiaojuan Yang * loongarch only support little endian, 157cbff2db1SXiaojuan Yang * so we paresd the value with little endian. 158cbff2db1SXiaojuan Yang */ 159cbff2db1SXiaojuan Yang val = cpu_to_le64(val); 160cbff2db1SXiaojuan Yang for (i = 0; i < 4; i++) { 161cbff2db1SXiaojuan Yang uint8_t ipnum; 162cbff2db1SXiaojuan Yang ipnum = val & 0xff; 163cbff2db1SXiaojuan Yang ipnum = ctz32(ipnum); 164cbff2db1SXiaojuan Yang ipnum = (ipnum >= 4) ? 0 : ipnum; 165cbff2db1SXiaojuan Yang s->sw_ipmap[index * 4 + i] = ipnum; 166cbff2db1SXiaojuan Yang val = val >> 8; 167cbff2db1SXiaojuan Yang } 168cbff2db1SXiaojuan Yang 169cbff2db1SXiaojuan Yang break; 170cbff2db1SXiaojuan Yang case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1: 171cbff2db1SXiaojuan Yang index = (offset - EXTIOI_ENABLE_START) >> 2; 172cbff2db1SXiaojuan Yang old_data = s->enable[index]; 173cbff2db1SXiaojuan Yang s->enable[index] = val; 174cbff2db1SXiaojuan Yang 175cbff2db1SXiaojuan Yang /* unmask irq */ 176cbff2db1SXiaojuan Yang val = s->enable[index] & ~old_data; 177cbff2db1SXiaojuan Yang extioi_enable_irq(s, index, val, 1); 178cbff2db1SXiaojuan Yang 179cbff2db1SXiaojuan Yang /* mask irq */ 180cbff2db1SXiaojuan Yang val = ~s->enable[index] & old_data; 181cbff2db1SXiaojuan Yang extioi_enable_irq(s, index, val, 0); 182cbff2db1SXiaojuan Yang break; 183cbff2db1SXiaojuan Yang case EXTIOI_BOUNCE_START ... EXTIOI_BOUNCE_END - 1: 184cbff2db1SXiaojuan Yang /* do not emulate hw bounced irq routing */ 185cbff2db1SXiaojuan Yang index = (offset - EXTIOI_BOUNCE_START) >> 2; 186cbff2db1SXiaojuan Yang s->bounce[index] = val; 187cbff2db1SXiaojuan Yang break; 188cbff2db1SXiaojuan Yang case EXTIOI_COREISR_START ... EXTIOI_COREISR_END - 1: 189a649fffcSXiaojuan Yang index = (offset - EXTIOI_COREISR_START) >> 2; 190a649fffcSXiaojuan Yang /* using attrs to get current cpu index */ 191a649fffcSXiaojuan Yang cpu = attrs.requester_id; 192cbff2db1SXiaojuan Yang old_data = s->coreisr[cpu][index]; 193cbff2db1SXiaojuan Yang s->coreisr[cpu][index] = old_data & ~val; 194*9b4b4e51SMichael Tokarev /* write 1 to clear interrupt */ 195cbff2db1SXiaojuan Yang old_data &= val; 196cbff2db1SXiaojuan Yang irq = ctz32(old_data); 197cbff2db1SXiaojuan Yang while (irq != 32) { 198cbff2db1SXiaojuan Yang extioi_update_irq(s, irq + index * 32, 0); 199cbff2db1SXiaojuan Yang old_data &= ~(1 << irq); 200cbff2db1SXiaojuan Yang irq = ctz32(old_data); 201cbff2db1SXiaojuan Yang } 202cbff2db1SXiaojuan Yang break; 203cbff2db1SXiaojuan Yang case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: 204cbff2db1SXiaojuan Yang irq = offset - EXTIOI_COREMAP_START; 205cbff2db1SXiaojuan Yang index = irq / 4; 206cbff2db1SXiaojuan Yang s->coremap[index] = val; 207cbff2db1SXiaojuan Yang /* 208cbff2db1SXiaojuan Yang * loongarch only support little endian, 209cbff2db1SXiaojuan Yang * so we paresd the value with little endian. 210cbff2db1SXiaojuan Yang */ 211cbff2db1SXiaojuan Yang val = cpu_to_le64(val); 212cbff2db1SXiaojuan Yang 213cbff2db1SXiaojuan Yang for (i = 0; i < 4; i++) { 214cbff2db1SXiaojuan Yang cpu = val & 0xff; 215cbff2db1SXiaojuan Yang cpu = ctz32(cpu); 216cbff2db1SXiaojuan Yang cpu = (cpu >= 4) ? 0 : cpu; 217cbff2db1SXiaojuan Yang val = val >> 8; 218cbff2db1SXiaojuan Yang 219cbff2db1SXiaojuan Yang if (s->sw_coremap[irq + i] == cpu) { 220cbff2db1SXiaojuan Yang continue; 221cbff2db1SXiaojuan Yang } 222cbff2db1SXiaojuan Yang 223cbff2db1SXiaojuan Yang if (test_bit(irq, (unsigned long *)s->isr)) { 224cbff2db1SXiaojuan Yang /* 225cbff2db1SXiaojuan Yang * lower irq at old cpu and raise irq at new cpu 226cbff2db1SXiaojuan Yang */ 227cbff2db1SXiaojuan Yang extioi_update_irq(s, irq + i, 0); 228cbff2db1SXiaojuan Yang s->sw_coremap[irq + i] = cpu; 229cbff2db1SXiaojuan Yang extioi_update_irq(s, irq + i, 1); 230cbff2db1SXiaojuan Yang } else { 231cbff2db1SXiaojuan Yang s->sw_coremap[irq + i] = cpu; 232cbff2db1SXiaojuan Yang } 233cbff2db1SXiaojuan Yang } 234cbff2db1SXiaojuan Yang break; 235cbff2db1SXiaojuan Yang default: 236cbff2db1SXiaojuan Yang break; 237cbff2db1SXiaojuan Yang } 2383fc8f74bSXiaojuan Yang return MEMTX_OK; 239cbff2db1SXiaojuan Yang } 240cbff2db1SXiaojuan Yang 241cbff2db1SXiaojuan Yang static const MemoryRegionOps extioi_ops = { 2423fc8f74bSXiaojuan Yang .read_with_attrs = extioi_readw, 2433fc8f74bSXiaojuan Yang .write_with_attrs = extioi_writew, 244cbff2db1SXiaojuan Yang .impl.min_access_size = 4, 245cbff2db1SXiaojuan Yang .impl.max_access_size = 4, 246cbff2db1SXiaojuan Yang .valid.min_access_size = 4, 247cbff2db1SXiaojuan Yang .valid.max_access_size = 8, 248cbff2db1SXiaojuan Yang .endianness = DEVICE_LITTLE_ENDIAN, 249cbff2db1SXiaojuan Yang }; 250cbff2db1SXiaojuan Yang 251cbff2db1SXiaojuan Yang static const VMStateDescription vmstate_loongarch_extioi = { 252cbff2db1SXiaojuan Yang .name = TYPE_LOONGARCH_EXTIOI, 253cbff2db1SXiaojuan Yang .version_id = 1, 254cbff2db1SXiaojuan Yang .minimum_version_id = 1, 255cbff2db1SXiaojuan Yang .fields = (VMStateField[]) { 256cbff2db1SXiaojuan Yang VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), 257646c39b2SSong Gao VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, EXTIOI_CPUS, 258cbff2db1SXiaojuan Yang EXTIOI_IRQS_GROUP_COUNT), 259cbff2db1SXiaojuan Yang VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI, 260cbff2db1SXiaojuan Yang EXTIOI_IRQS_NODETYPE_COUNT / 2), 261cbff2db1SXiaojuan Yang VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32), 262cbff2db1SXiaojuan Yang VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32), 263cbff2db1SXiaojuan Yang VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4), 264cbff2db1SXiaojuan Yang VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4), 265cbff2db1SXiaojuan Yang VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE), 266cbff2db1SXiaojuan Yang VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS), 267cbff2db1SXiaojuan Yang 268cbff2db1SXiaojuan Yang VMSTATE_END_OF_LIST() 269cbff2db1SXiaojuan Yang } 270cbff2db1SXiaojuan Yang }; 271cbff2db1SXiaojuan Yang 272cbff2db1SXiaojuan Yang static void loongarch_extioi_instance_init(Object *obj) 273cbff2db1SXiaojuan Yang { 274cbff2db1SXiaojuan Yang SysBusDevice *dev = SYS_BUS_DEVICE(obj); 275cbff2db1SXiaojuan Yang LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); 276cbff2db1SXiaojuan Yang int i, cpu, pin; 277cbff2db1SXiaojuan Yang 278cbff2db1SXiaojuan Yang for (i = 0; i < EXTIOI_IRQS; i++) { 2797d5b0d68SPhilippe Mathieu-Daudé sysbus_init_irq(dev, &s->irq[i]); 280cbff2db1SXiaojuan Yang } 281cbff2db1SXiaojuan Yang 282cbff2db1SXiaojuan Yang qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS); 283cbff2db1SXiaojuan Yang 284646c39b2SSong Gao for (cpu = 0; cpu < EXTIOI_CPUS; cpu++) { 285cbff2db1SXiaojuan Yang memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops, 286cbff2db1SXiaojuan Yang s, "extioi_iocsr", 0x900); 2877d5b0d68SPhilippe Mathieu-Daudé sysbus_init_mmio(dev, &s->extioi_iocsr_mem[cpu]); 288cbff2db1SXiaojuan Yang for (pin = 0; pin < LS3A_INTC_IP; pin++) { 289cbff2db1SXiaojuan Yang qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1); 290cbff2db1SXiaojuan Yang } 291cbff2db1SXiaojuan Yang } 292cbff2db1SXiaojuan Yang memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, 293cbff2db1SXiaojuan Yang s, "extioi_system_mem", 0x900); 2947d5b0d68SPhilippe Mathieu-Daudé sysbus_init_mmio(dev, &s->extioi_system_mem); 295cbff2db1SXiaojuan Yang } 296cbff2db1SXiaojuan Yang 297cbff2db1SXiaojuan Yang static void loongarch_extioi_class_init(ObjectClass *klass, void *data) 298cbff2db1SXiaojuan Yang { 299cbff2db1SXiaojuan Yang DeviceClass *dc = DEVICE_CLASS(klass); 300cbff2db1SXiaojuan Yang 301cbff2db1SXiaojuan Yang dc->vmsd = &vmstate_loongarch_extioi; 302cbff2db1SXiaojuan Yang } 303cbff2db1SXiaojuan Yang 304cbff2db1SXiaojuan Yang static const TypeInfo loongarch_extioi_info = { 305cbff2db1SXiaojuan Yang .name = TYPE_LOONGARCH_EXTIOI, 306cbff2db1SXiaojuan Yang .parent = TYPE_SYS_BUS_DEVICE, 307cbff2db1SXiaojuan Yang .instance_init = loongarch_extioi_instance_init, 308cbff2db1SXiaojuan Yang .instance_size = sizeof(struct LoongArchExtIOI), 309cbff2db1SXiaojuan Yang .class_init = loongarch_extioi_class_init, 310cbff2db1SXiaojuan Yang }; 311cbff2db1SXiaojuan Yang 312cbff2db1SXiaojuan Yang static void loongarch_extioi_register_types(void) 313cbff2db1SXiaojuan Yang { 314cbff2db1SXiaojuan Yang type_register_static(&loongarch_extioi_info); 315cbff2db1SXiaojuan Yang } 316cbff2db1SXiaojuan Yang 317cbff2db1SXiaojuan Yang type_init(loongarch_extioi_register_types) 318