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