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 "system/kvm.h" 14 #include "trace.h" 15 #include "qapi/error.h" 16 17 static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, 18 int level) 19 { 20 uint64_t val; 21 int irq; 22 23 if (level) { 24 val = mask & s->intirr & ~s->int_mask; 25 if (val) { 26 irq = ctz64(val); 27 s->intisr |= MAKE_64BIT_MASK(irq, 1); 28 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); 29 } 30 } else { 31 /* 32 * intirr means requested pending irq 33 * do not clear pending irq for edge-triggered on lowering edge 34 */ 35 val = mask & s->intisr & ~s->intirr; 36 if (val) { 37 irq = ctz64(val); 38 s->intisr &= ~MAKE_64BIT_MASK(irq, 1); 39 qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); 40 } 41 } 42 } 43 44 static void pch_pic_irq_handler(void *opaque, int irq, int level) 45 { 46 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 47 uint64_t mask = 1ULL << irq; 48 49 assert(irq < s->irq_num); 50 trace_loongarch_pch_pic_irq_handler(irq, level); 51 52 if (kvm_irqchip_in_kernel()) { 53 kvm_set_irq(kvm_state, irq, !!level); 54 return; 55 } 56 57 if (s->intedge & mask) { 58 /* Edge triggered */ 59 if (level) { 60 if ((s->last_intirr & mask) == 0) { 61 /* marked pending on a rising edge */ 62 s->intirr |= mask; 63 } 64 s->last_intirr |= mask; 65 } else { 66 s->last_intirr &= ~mask; 67 } 68 } else { 69 /* Level triggered */ 70 if (level) { 71 s->intirr |= mask; 72 s->last_intirr |= mask; 73 } else { 74 s->intirr &= ~mask; 75 s->last_intirr &= ~mask; 76 } 77 } 78 pch_pic_update_irq(s, mask, level); 79 } 80 81 static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) 82 { 83 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 84 uint64_t val = 0; 85 uint32_t offset; 86 87 offset = addr & 7; 88 addr -= offset; 89 switch (addr) { 90 case PCH_PIC_INT_ID: 91 val = cpu_to_le64(s->id.data); 92 break; 93 case PCH_PIC_INT_MASK: 94 val = s->int_mask; 95 break; 96 case PCH_PIC_INT_EDGE: 97 val = s->intedge; 98 break; 99 case PCH_PIC_HTMSI_EN: 100 val = s->htmsi_en; 101 break; 102 case PCH_PIC_AUTO_CTRL0: 103 case PCH_PIC_AUTO_CTRL1: 104 /* PCH PIC connect to EXTIOI always, discard auto_ctrl access */ 105 break; 106 case PCH_PIC_INT_STATUS: 107 val = s->intisr & (~s->int_mask); 108 break; 109 case PCH_PIC_INT_POL: 110 val = s->int_polarity; 111 break; 112 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: 113 val = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); 114 break; 115 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: 116 val = *(uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); 117 break; 118 default: 119 qemu_log_mask(LOG_GUEST_ERROR, 120 "pch_pic_read: Bad address 0x%"PRIx64"\n", addr); 121 break; 122 } 123 124 return (val >> (offset * 8)) & field_mask; 125 } 126 127 static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, 128 uint64_t field_mask) 129 { 130 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); 131 uint32_t offset; 132 uint64_t old, mask, data, *ptemp; 133 134 offset = addr & 7; 135 addr -= offset; 136 mask = field_mask << (offset * 8); 137 data = (value & field_mask) << (offset * 8); 138 switch (addr) { 139 case PCH_PIC_INT_MASK: 140 old = s->int_mask; 141 s->int_mask = (old & ~mask) | data; 142 if (old & ~data) { 143 pch_pic_update_irq(s, old & ~data, 1); 144 } 145 146 if (~old & data) { 147 pch_pic_update_irq(s, ~old & data, 0); 148 } 149 break; 150 case PCH_PIC_INT_EDGE: 151 s->intedge = (s->intedge & ~mask) | data; 152 break; 153 case PCH_PIC_INT_CLEAR: 154 if (s->intedge & data) { 155 s->intirr &= ~data; 156 pch_pic_update_irq(s, data, 0); 157 s->intisr &= ~data; 158 } 159 break; 160 case PCH_PIC_HTMSI_EN: 161 s->htmsi_en = (s->htmsi_en & ~mask) | data; 162 break; 163 case PCH_PIC_AUTO_CTRL0: 164 case PCH_PIC_AUTO_CTRL1: 165 /* Discard auto_ctrl access */ 166 break; 167 case PCH_PIC_INT_POL: 168 s->int_polarity = (s->int_polarity & ~mask) | data; 169 break; 170 case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: 171 ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); 172 *ptemp = (*ptemp & ~mask) | data; 173 break; 174 case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: 175 ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); 176 *ptemp = (*ptemp & ~mask) | data; 177 break; 178 default: 179 qemu_log_mask(LOG_GUEST_ERROR, 180 "pch_pic_write: Bad address 0x%"PRIx64"\n", addr); 181 break; 182 } 183 } 184 185 static uint64_t loongarch_pch_pic_read(void *opaque, hwaddr addr, 186 unsigned size) 187 { 188 uint64_t val = 0; 189 190 switch (size) { 191 case 1: 192 val = pch_pic_read(opaque, addr, UCHAR_MAX); 193 break; 194 case 2: 195 val = pch_pic_read(opaque, addr, USHRT_MAX); 196 break; 197 case 4: 198 val = pch_pic_read(opaque, addr, UINT_MAX); 199 break; 200 case 8: 201 val = pch_pic_read(opaque, addr, UINT64_MAX); 202 break; 203 default: 204 qemu_log_mask(LOG_GUEST_ERROR, 205 "loongarch_pch_pic_read: Bad size %d\n", size); 206 break; 207 } 208 209 trace_loongarch_pch_pic_read(size, addr, val); 210 return val; 211 } 212 213 static void loongarch_pch_pic_write(void *opaque, hwaddr addr, 214 uint64_t value, unsigned size) 215 { 216 trace_loongarch_pch_pic_write(size, addr, value); 217 218 switch (size) { 219 case 1: 220 pch_pic_write(opaque, addr, value, UCHAR_MAX); 221 break; 222 case 2: 223 pch_pic_write(opaque, addr, value, USHRT_MAX); 224 break; 225 break; 226 case 4: 227 pch_pic_write(opaque, addr, value, UINT_MAX); 228 break; 229 case 8: 230 pch_pic_write(opaque, addr, value, UINT64_MAX); 231 break; 232 default: 233 qemu_log_mask(LOG_GUEST_ERROR, 234 "loongarch_pch_pic_write: Bad size %d\n", size); 235 break; 236 } 237 } 238 239 static const MemoryRegionOps loongarch_pch_pic_ops = { 240 .read = loongarch_pch_pic_read, 241 .write = loongarch_pch_pic_write, 242 .valid = { 243 .min_access_size = 1, 244 .max_access_size = 8, 245 /* 246 * PCH PIC device would not work correctly if the guest was doing 247 * unaligned access. This might not be a limitation on the real 248 * device but in practice there is no reason for a guest to access 249 * this device unaligned. 250 */ 251 .unaligned = false, 252 }, 253 .impl = { 254 .min_access_size = 1, 255 .max_access_size = 8, 256 }, 257 .endianness = DEVICE_LITTLE_ENDIAN, 258 }; 259 260 static void loongarch_pic_reset_hold(Object *obj, ResetType type) 261 { 262 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj); 263 264 if (lpc->parent_phases.hold) { 265 lpc->parent_phases.hold(obj, type); 266 } 267 268 if (kvm_irqchip_in_kernel()) { 269 kvm_pic_put(obj, 0); 270 } 271 } 272 273 static void loongarch_pic_realize(DeviceState *dev, Error **errp) 274 { 275 LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev); 276 LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev); 277 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 278 Error *local_err = NULL; 279 280 lpc->parent_realize(dev, &local_err); 281 if (local_err) { 282 error_propagate(errp, local_err); 283 return; 284 } 285 286 qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); 287 qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); 288 289 if (kvm_irqchip_in_kernel()) { 290 kvm_pic_realize(dev, errp); 291 } else { 292 memory_region_init_io(&s->iomem, OBJECT(dev), 293 &loongarch_pch_pic_ops, 294 s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE); 295 sysbus_init_mmio(sbd, &s->iomem); 296 } 297 } 298 299 static int loongarch_pic_pre_save(LoongArchPICCommonState *opaque) 300 { 301 if (kvm_irqchip_in_kernel()) { 302 return kvm_pic_get(opaque); 303 } 304 305 return 0; 306 } 307 308 static int loongarch_pic_post_load(LoongArchPICCommonState *opaque, 309 int version_id) 310 { 311 if (kvm_irqchip_in_kernel()) { 312 return kvm_pic_put(opaque, version_id); 313 } 314 315 return 0; 316 } 317 318 static void loongarch_pic_class_init(ObjectClass *klass, const void *data) 319 { 320 DeviceClass *dc = DEVICE_CLASS(klass); 321 LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); 322 LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); 323 ResettableClass *rc = RESETTABLE_CLASS(klass); 324 325 resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold, 326 NULL, &lpc->parent_phases); 327 device_class_set_parent_realize(dc, loongarch_pic_realize, 328 &lpc->parent_realize); 329 lpcc->pre_save = loongarch_pic_pre_save; 330 lpcc->post_load = loongarch_pic_post_load; 331 } 332 333 static const TypeInfo loongarch_pic_types[] = { 334 { 335 .name = TYPE_LOONGARCH_PIC, 336 .parent = TYPE_LOONGARCH_PIC_COMMON, 337 .instance_size = sizeof(LoongarchPICState), 338 .class_size = sizeof(LoongarchPICClass), 339 .class_init = loongarch_pic_class_init, 340 } 341 }; 342 343 DEFINE_TYPES(loongarch_pic_types) 344