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