1 /* 2 * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * * Neither the name of the Open Source and Linux Lab nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "qemu/osdep.h" 29 #include "hw/hw.h" 30 #include "hw/irq.h" 31 #include "hw/xtensa/mx_pic.h" 32 #include "qemu/log.h" 33 34 #define MX_MAX_CPU 32 35 #define MX_MAX_IRQ 32 36 37 #define MIROUT 0x0 38 #define MIPICAUSE 0x100 39 #define MIPISET 0x140 40 #define MIENG 0x180 41 #define MIENGSET 0x184 42 #define MIASG 0x188 43 #define MIASGSET 0x18c 44 #define MIPIPART 0x190 45 #define SYSCFGID 0x1a0 46 #define MPSCORE 0x200 47 #define CCON 0x220 48 49 struct XtensaMxPic { 50 unsigned n_cpu; 51 unsigned n_irq; 52 53 uint32_t ext_irq_state; 54 uint32_t mieng; 55 uint32_t miasg; 56 uint32_t mirout[MX_MAX_IRQ]; 57 uint32_t mipipart; 58 uint32_t runstall; 59 60 qemu_irq *irq_inputs; 61 struct XtensaMxPicCpu { 62 XtensaMxPic *mx; 63 qemu_irq *irq; 64 qemu_irq runstall; 65 uint32_t mipicause; 66 uint32_t mirout_cache; 67 uint32_t irq_state_cache; 68 uint32_t ccon; 69 MemoryRegion reg; 70 } cpu[MX_MAX_CPU]; 71 }; 72 73 static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset, 74 unsigned size) 75 { 76 struct XtensaMxPicCpu *mx_cpu = opaque; 77 struct XtensaMxPic *mx = mx_cpu->mx; 78 79 if (offset < MIROUT + MX_MAX_IRQ) { 80 return mx->mirout[offset - MIROUT]; 81 } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) { 82 return mx->cpu[offset - MIPICAUSE].mipicause; 83 } else { 84 switch (offset) { 85 case MIENG: 86 return mx->mieng; 87 88 case MIASG: 89 return mx->miasg; 90 91 case MIPIPART: 92 return mx->mipipart; 93 94 case SYSCFGID: 95 return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu); 96 97 case MPSCORE: 98 return mx->runstall; 99 100 case CCON: 101 return mx_cpu->ccon; 102 103 default: 104 qemu_log_mask(LOG_GUEST_ERROR, 105 "unknown RER in MX PIC range: 0x%08x\n", 106 (uint32_t)offset); 107 return 0; 108 } 109 } 110 } 111 112 static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx, 113 unsigned cpu) 114 { 115 uint32_t mipicause = mx->cpu[cpu].mipicause; 116 uint32_t mipipart = mx->mipipart; 117 118 return (((mipicause & 1) << (mipipart & 3)) | 119 ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) | 120 ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) | 121 ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7; 122 } 123 124 static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx, 125 unsigned cpu) 126 { 127 return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) & 128 mx->cpu[cpu].mirout_cache) << 2) | 129 xtensa_mx_pic_get_ipi_for_cpu(mx, cpu); 130 } 131 132 static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu) 133 { 134 uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu); 135 uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq; 136 unsigned i; 137 138 qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n", 139 __func__, cpu, irq, changed_irq); 140 mx->cpu[cpu].irq_state_cache = irq; 141 for (i = 0; changed_irq; ++i) { 142 uint32_t mask = 1u << i; 143 144 if (changed_irq & mask) { 145 changed_irq ^= mask; 146 qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask); 147 } 148 } 149 } 150 151 static void xtensa_mx_pic_update_all(XtensaMxPic *mx) 152 { 153 unsigned cpu; 154 155 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 156 xtensa_mx_pic_update_cpu(mx, cpu); 157 } 158 } 159 160 static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset, 161 uint64_t v, unsigned size) 162 { 163 struct XtensaMxPicCpu *mx_cpu = opaque; 164 struct XtensaMxPic *mx = mx_cpu->mx; 165 unsigned cpu; 166 167 if (offset < MIROUT + mx->n_irq) { 168 mx->mirout[offset - MIROUT] = v; 169 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 170 uint32_t mask = 1u << (offset - MIROUT); 171 172 if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) { 173 mx->cpu[cpu].mirout_cache ^= mask; 174 xtensa_mx_pic_update_cpu(mx, cpu); 175 } 176 } 177 } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) { 178 cpu = offset - MIPICAUSE; 179 mx->cpu[cpu].mipicause &= ~v; 180 xtensa_mx_pic_update_cpu(mx, cpu); 181 } else if (offset >= MIPISET && offset < MIPISET + 16) { 182 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 183 if (v & (1u << cpu)) { 184 mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET); 185 xtensa_mx_pic_update_cpu(mx, cpu); 186 } 187 } 188 } else { 189 uint32_t change = 0; 190 uint32_t oldv, newv; 191 const char *name = "???"; 192 193 switch (offset) { 194 case MIENG: 195 change = mx->mieng & v; 196 oldv = mx->mieng; 197 mx->mieng &= ~v; 198 newv = mx->mieng; 199 name = "MIENG"; 200 break; 201 202 case MIENGSET: 203 change = ~mx->mieng & v; 204 oldv = mx->mieng; 205 mx->mieng |= v; 206 newv = mx->mieng; 207 name = "MIENG"; 208 break; 209 210 case MIASG: 211 change = mx->miasg & v; 212 oldv = mx->miasg; 213 mx->miasg &= ~v; 214 newv = mx->miasg; 215 name = "MIASG"; 216 break; 217 218 case MIASGSET: 219 change = ~mx->miasg & v; 220 oldv = mx->miasg; 221 mx->miasg |= v; 222 newv = mx->miasg; 223 name = "MIASG"; 224 break; 225 226 case MIPIPART: 227 change = mx->mipipart ^ v; 228 oldv = mx->mipipart; 229 mx->mipipart = v; 230 newv = mx->mipipart; 231 name = "MIPIPART"; 232 break; 233 234 case MPSCORE: 235 change = mx->runstall ^ v; 236 oldv = mx->runstall; 237 mx->runstall = v; 238 newv = mx->runstall; 239 name = "RUNSTALL"; 240 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 241 if (change & (1u << cpu)) { 242 qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu)); 243 } 244 } 245 break; 246 247 case CCON: 248 mx_cpu->ccon = v & 0x1; 249 break; 250 251 default: 252 qemu_log_mask(LOG_GUEST_ERROR, 253 "unknown WER in MX PIC range: 0x%08x = 0x%08x\n", 254 (uint32_t)offset, (uint32_t)v); 255 break; 256 } 257 if (change) { 258 qemu_log_mask(CPU_LOG_INT, 259 "%s: %s changed by CPU %d: %08x -> %08x\n", 260 __func__, name, (int)(mx_cpu - mx->cpu), 261 oldv, newv); 262 xtensa_mx_pic_update_all(mx); 263 } 264 } 265 } 266 267 static const MemoryRegionOps xtensa_mx_pic_ops = { 268 .read = xtensa_mx_pic_ext_reg_read, 269 .write = xtensa_mx_pic_ext_reg_write, 270 .endianness = DEVICE_NATIVE_ENDIAN, 271 .valid = { 272 .unaligned = true, 273 }, 274 }; 275 276 MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx, 277 qemu_irq *irq, 278 qemu_irq runstall) 279 { 280 struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu; 281 282 mx_cpu->mx = mx; 283 mx_cpu->irq = irq; 284 mx_cpu->runstall = runstall; 285 286 memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu, 287 "mx_pic", 0x280); 288 289 ++mx->n_cpu; 290 return &mx_cpu->reg; 291 } 292 293 static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active) 294 { 295 XtensaMxPic *mx = opaque; 296 297 if (irq < mx->n_irq) { 298 uint32_t old_irq_state = mx->ext_irq_state; 299 300 if (active) { 301 mx->ext_irq_state |= 1u << irq; 302 } else { 303 mx->ext_irq_state &= ~(1u << irq); 304 } 305 if (old_irq_state != mx->ext_irq_state) { 306 qemu_log_mask(CPU_LOG_INT, 307 "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n", 308 __func__, irq, active, 309 old_irq_state, mx->ext_irq_state); 310 xtensa_mx_pic_update_all(mx); 311 } 312 } else { 313 qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n", 314 __func__, irq); 315 } 316 } 317 318 XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq) 319 { 320 XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic)); 321 322 mx->n_irq = n_irq + 1; 323 mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx, 324 mx->n_irq); 325 return mx; 326 } 327 328 void xtensa_mx_pic_reset(void *opaque) 329 { 330 XtensaMxPic *mx = opaque; 331 unsigned i; 332 333 mx->ext_irq_state = 0; 334 mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u; 335 mx->miasg = 0; 336 mx->mipipart = 0; 337 for (i = 0; i < mx->n_irq; ++i) { 338 mx->mirout[i] = 1; 339 } 340 for (i = 0; i < mx->n_cpu; ++i) { 341 mx->cpu[i].mipicause = 0; 342 mx->cpu[i].mirout_cache = i ? 0 : mx->mieng; 343 mx->cpu[i].irq_state_cache = 0; 344 mx->cpu[i].ccon = 0; 345 } 346 mx->runstall = (1u << mx->n_cpu) - 2; 347 for (i = 0; i < mx->n_cpu; ++i) { 348 qemu_set_irq(mx->cpu[i].runstall, i > 0); 349 } 350 } 351 352 qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx) 353 { 354 return mx->irq_inputs + 1; 355 } 356