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