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