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