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