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