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