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 "qemu/log.h" 11 #include "hw/irq.h" 12 #include "hw/intc/loongarch_pch_pic.h" 13 #include "trace.h" 14 #include "qapi/error.h" 15 16 static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, 17 int level) 18 { 19 uint64_t val; 20 int irq; 21 22 if (level) { 23 val = mask & s->intirr & ~s->int_mask; 24 if (val) { 25 irq = ctz64(val); 26 s->intisr |= MAKE_64BIT_MASK(irq, 1); 27 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); 28 } 29 } else { 30 /* 31 * intirr means requested pending irq 32 * do not clear pending irq for edge-triggered on lowering edge 33 */ 34 val = mask & s->intisr & ~s->intirr; 35 if (val) { 36 irq = ctz64(val); 37 s->intisr &= ~MAKE_64BIT_MASK(irq, 1); 38 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); 39 } 40 } 41 } 42 43 static void pch_pic_irq_handler(void *opaque, int irq, int level) 44 { 45 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 46 uint64_t mask = 1ULL << irq; 47 48 assert(irq < s->irq_num); 49 trace_loongarch_pch_pic_irq_handler(irq, level); 50 51 if (s->intedge & mask) { 52 /* Edge triggered */ 53 if (level) { 54 if ((s->last_intirr & mask) == 0) { 55 /* marked pending on a rising edge */ 56 s->intirr |= mask; 57 } 58 s->last_intirr |= mask; 59 } else { 60 s->last_intirr &= ~mask; 61 } 62 } else { 63 /* Level triggered */ 64 if (level) { 65 s->intirr |= mask; 66 s->last_intirr |= mask; 67 } else { 68 s->intirr &= ~mask; 69 s->last_intirr &= ~mask; 70 } 71 } 72 pch_pic_update_irq(s, mask, level); 73 } 74 75 static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) 76 { 77 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 78 uint64_t val = 0; 79 uint32_t offset; 80 81 offset = addr & 7; 82 addr -= offset; 83 switch (addr) { 84 case PCH_PIC_INT_ID: 85 val = s->id.data; 86 break; 87 case PCH_PIC_INT_MASK: 88 val = s->int_mask; 89 break; 90 case PCH_PIC_INT_EDGE: 91 val = s->intedge; 92 break; 93 case PCH_PIC_HTMSI_EN: 94 val = s->htmsi_en; 95 break; 96 case PCH_PIC_AUTO_CTRL0: 97 case PCH_PIC_AUTO_CTRL1: 98 /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */ 99 break; 100 default: 101 qemu_log_mask(LOG_GUEST_ERROR, 102 "pch_pic_read: Bad address 0x%"PRIx64"\n", addr); 103 break; 104 } 105 106 return (val >> (offset * 8)) & field_mask; 107 } 108 109 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr, 110 unsigned size) 111 { 112 uint64_t val = 0; 113 114 switch (size) { 115 case 1: 116 val = pch_pic_read(opaque, addr, UCHAR_MAX); 117 break; 118 case 2: 119 val = pch_pic_read(opaque, addr, USHRT_MAX); 120 break; 121 case 4: 122 val = pch_pic_read(opaque, addr, UINT_MAX); 123 break; 124 case 8: 125 val = pch_pic_read(opaque, addr, UINT64_MAX); 126 break; 127 default: 128 qemu_log_mask(LOG_GUEST_ERROR, 129 "loongarch_pch_pic_read: Bad size %d\n", size); 130 break; 131 } 132 133 return val; 134 } 135 136 static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr, 137 unsigned size) 138 { 139 uint64_t val; 140 141 val = loongarch_pch_pic_read(opaque, addr, size); 142 trace_loongarch_pch_pic_low_readw(size, addr, val); 143 return val; 144 } 145 146 static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi) 147 { 148 uint64_t mask = 0xffffffff00000000; 149 uint64_t data = target; 150 151 return hi ? (value & ~mask) | (data << 32) : (value & mask) | data; 152 } 153 154 static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr, 155 uint64_t value, unsigned size) 156 { 157 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 158 uint32_t old_valid, data = (uint32_t)value; 159 uint64_t old, int_mask; 160 161 trace_loongarch_pch_pic_low_writew(size, addr, data); 162 163 switch (addr) { 164 case PCH_PIC_INT_MASK: 165 old = s->int_mask; 166 s->int_mask = get_writew_val(old, data, 0); 167 old_valid = (uint32_t)old; 168 if (old_valid & ~data) { 169 pch_pic_update_irq(s, (old_valid & ~data), 1); 170 } 171 if (~old_valid & data) { 172 pch_pic_update_irq(s, (~old_valid & data), 0); 173 } 174 break; 175 case PCH_PIC_INT_MASK + 4: 176 old = s->int_mask; 177 s->int_mask = get_writew_val(old, data, 1); 178 old_valid = (uint32_t)(old >> 32); 179 int_mask = old_valid & ~data; 180 if (int_mask) { 181 pch_pic_update_irq(s, int_mask << 32, 1); 182 } 183 int_mask = ~old_valid & data; 184 if (int_mask) { 185 pch_pic_update_irq(s, int_mask << 32, 0); 186 } 187 break; 188 case PCH_PIC_INT_EDGE: 189 s->intedge = get_writew_val(s->intedge, data, 0); 190 break; 191 case PCH_PIC_INT_EDGE + 4: 192 s->intedge = get_writew_val(s->intedge, data, 1); 193 break; 194 case PCH_PIC_INT_CLEAR: 195 if (s->intedge & data) { 196 s->intirr &= (~data); 197 pch_pic_update_irq(s, data, 0); 198 s->intisr &= (~data); 199 } 200 break; 201 case PCH_PIC_INT_CLEAR + 4: 202 value <<= 32; 203 if (s->intedge & value) { 204 s->intirr &= (~value); 205 pch_pic_update_irq(s, value, 0); 206 s->intisr &= (~value); 207 } 208 break; 209 case PCH_PIC_HTMSI_EN: 210 s->htmsi_en = get_writew_val(s->htmsi_en, data, 0); 211 break; 212 case PCH_PIC_HTMSI_EN + 4: 213 s->htmsi_en = get_writew_val(s->htmsi_en, data, 1); 214 break; 215 case PCH_PIC_AUTO_CTRL0: 216 case PCH_PIC_AUTO_CTRL0 + 4: 217 case PCH_PIC_AUTO_CTRL1: 218 case PCH_PIC_AUTO_CTRL1 + 4: 219 /* discard auto_ctrl access */ 220 break; 221 default: 222 break; 223 } 224 } 225 226 static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, 227 unsigned size) 228 { 229 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 230 uint64_t val = 0; 231 232 addr += PCH_PIC_INT_STATUS; 233 switch (addr) { 234 case PCH_PIC_INT_STATUS: 235 val = (uint32_t)(s->intisr & (~s->int_mask)); 236 break; 237 case PCH_PIC_INT_STATUS + 4: 238 val = (s->intisr & (~s->int_mask)) >> 32; 239 break; 240 case PCH_PIC_INT_POL: 241 val = (uint32_t)s->int_polarity; 242 break; 243 case PCH_PIC_INT_POL + 4: 244 val = s->int_polarity >> 32; 245 break; 246 default: 247 break; 248 } 249 250 trace_loongarch_pch_pic_high_readw(size, addr, val); 251 return val; 252 } 253 254 static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr, 255 uint64_t value, unsigned size) 256 { 257 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 258 uint32_t data = (uint32_t)value; 259 260 addr += PCH_PIC_INT_STATUS; 261 trace_loongarch_pch_pic_high_writew(size, addr, data); 262 263 switch (addr) { 264 case PCH_PIC_INT_POL: 265 s->int_polarity = get_writew_val(s->int_polarity, data, 0); 266 break; 267 case PCH_PIC_INT_POL + 4: 268 s->int_polarity = get_writew_val(s->int_polarity, data, 1); 269 break; 270 default: 271 break; 272 } 273 } 274 275 static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr, 276 unsigned size) 277 { 278 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 279 uint64_t val = 0; 280 int64_t offset_tmp; 281 282 addr += PCH_PIC_ROUTE_ENTRY; 283 switch (addr) { 284 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: 285 offset_tmp = addr - PCH_PIC_HTMSI_VEC; 286 if (offset_tmp >= 0 && offset_tmp < 64) { 287 val = s->htmsi_vector[offset_tmp]; 288 } 289 break; 290 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: 291 offset_tmp = addr - PCH_PIC_ROUTE_ENTRY; 292 if (offset_tmp >= 0 && offset_tmp < 64) { 293 val = s->route_entry[offset_tmp]; 294 } 295 break; 296 default: 297 break; 298 } 299 300 trace_loongarch_pch_pic_readb(size, addr, val); 301 return val; 302 } 303 304 static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr, 305 uint64_t data, unsigned size) 306 { 307 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 308 int32_t offset_tmp; 309 310 addr += PCH_PIC_ROUTE_ENTRY; 311 trace_loongarch_pch_pic_writeb(size, addr, data); 312 313 switch (addr) { 314 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: 315 offset_tmp = addr - PCH_PIC_HTMSI_VEC; 316 if (offset_tmp >= 0 && offset_tmp < 64) { 317 s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); 318 } 319 break; 320 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: 321 offset_tmp = addr - PCH_PIC_ROUTE_ENTRY; 322 if (offset_tmp >= 0 && offset_tmp < 64) { 323 s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); 324 } 325 break; 326 default: 327 break; 328 } 329 } 330 331 static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = { 332 .read = loongarch_pch_pic_low_readw, 333 .write = loongarch_pch_pic_low_writew, 334 .valid = { 335 .min_access_size = 4, 336 .max_access_size = 8, 337 }, 338 .impl = { 339 .min_access_size = 4, 340 .max_access_size = 4, 341 }, 342 .endianness = DEVICE_LITTLE_ENDIAN, 343 }; 344 345 static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = { 346 .read = loongarch_pch_pic_high_readw, 347 .write = loongarch_pch_pic_high_writew, 348 .valid = { 349 .min_access_size = 4, 350 .max_access_size = 8, 351 }, 352 .impl = { 353 .min_access_size = 4, 354 .max_access_size = 4, 355 }, 356 .endianness = DEVICE_LITTLE_ENDIAN, 357 }; 358 359 static const MemoryRegionOps loongarch_pch_pic_reg8_ops = { 360 .read = loongarch_pch_pic_readb, 361 .write = loongarch_pch_pic_writeb, 362 .valid = { 363 .min_access_size = 1, 364 .max_access_size = 1, 365 }, 366 .impl = { 367 .min_access_size = 1, 368 .max_access_size = 1, 369 }, 370 .endianness = DEVICE_LITTLE_ENDIAN, 371 }; 372 373 static void loongarch_pic_reset_hold(Object *obj, ResetType type) 374 { 375 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj); 376 377 if (lpc->parent_phases.hold) { 378 lpc->parent_phases.hold(obj, type); 379 } 380 } 381 382 static void loongarch_pic_realize(DeviceState *dev, Error **errp) 383 { 384 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); 385 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev); 386 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 387 Error *local_err = NULL; 388 389 lpc->parent_realize(dev, &local_err); 390 if (local_err) { 391 error_propagate(errp, local_err); 392 return; 393 } 394 395 qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); 396 qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); 397 memory_region_init_io(&s->iomem32_low, OBJECT(dev), 398 &loongarch_pch_pic_reg32_low_ops, 399 s, PCH_PIC_NAME(.reg32_part1), 0x100); 400 memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops, 401 s, PCH_PIC_NAME(.reg8), 0x2a0); 402 memory_region_init_io(&s->iomem32_high, OBJECT(dev), 403 &loongarch_pch_pic_reg32_high_ops, 404 s, PCH_PIC_NAME(.reg32_part2), 0xc60); 405 sysbus_init_mmio(sbd, &s->iomem32_low); 406 sysbus_init_mmio(sbd, &s->iomem8); 407 sysbus_init_mmio(sbd, &s->iomem32_high); 408 409 } 410 411 static void loongarch_pic_class_init(ObjectClass *klass, const void *data) 412 { 413 DeviceClass *dc = DEVICE_CLASS(klass); 414 LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); 415 ResettableClass *rc = RESETTABLE_CLASS(klass); 416 417 resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold, 418 NULL, &lpc->parent_phases); 419 device_class_set_parent_realize(dc, loongarch_pic_realize, 420 &lpc->parent_realize); 421 } 422 423 static const TypeInfo loongarch_pic_types[] = { 424 { 425 .name = TYPE_LOONGARCH_PIC, 426 .parent = TYPE_LOONGARCH_PIC_COMMON, 427 .instance_size = sizeof(LoongarchPICState), 428 .class_size = sizeof(LoongarchPICClass), 429 .class_init = loongarch_pic_class_init, 430 } 431 }; 432 433 DEFINE_TYPES(loongarch_pic_types) 434