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