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