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