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/irq.h" 30 #include "hw/xtensa/mx_pic.h" 31 #include "qemu/log.h" 32 33 #define MX_MAX_CPU 32 34 #define MX_MAX_IRQ 32 35 36 #define MIROUT 0x0 37 #define MIPICAUSE 0x100 38 #define MIPISET 0x140 39 #define MIENG 0x180 40 #define MIENGSET 0x184 41 #define MIASG 0x188 42 #define MIASGSET 0x18c 43 #define MIPIPART 0x190 44 #define SYSCFGID 0x1a0 45 #define MPSCORE 0x200 46 #define CCON 0x220 47 48 struct XtensaMxPic { 49 unsigned n_cpu; 50 unsigned n_irq; 51 52 uint32_t ext_irq_state; 53 uint32_t mieng; 54 uint32_t miasg; 55 uint32_t mirout[MX_MAX_IRQ]; 56 uint32_t mipipart; 57 uint32_t runstall; 58 59 qemu_irq *irq_inputs; 60 struct XtensaMxPicCpu { 61 XtensaMxPic *mx; 62 qemu_irq *irq; 63 qemu_irq runstall; 64 uint32_t mipicause; 65 uint32_t mirout_cache; 66 uint32_t irq_state_cache; 67 uint32_t ccon; 68 MemoryRegion reg; 69 } cpu[MX_MAX_CPU]; 70 }; 71 72 static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset, 73 unsigned size) 74 { 75 struct XtensaMxPicCpu *mx_cpu = opaque; 76 struct XtensaMxPic *mx = mx_cpu->mx; 77 78 if (offset < MIROUT + MX_MAX_IRQ) { 79 return mx->mirout[offset - MIROUT]; 80 } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) { 81 return mx->cpu[offset - MIPICAUSE].mipicause; 82 } else { 83 switch (offset) { 84 case MIENG: 85 return mx->mieng; 86 87 case MIASG: 88 return mx->miasg; 89 90 case MIPIPART: 91 return mx->mipipart; 92 93 case SYSCFGID: 94 return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu); 95 96 case MPSCORE: 97 return mx->runstall; 98 99 case CCON: 100 return mx_cpu->ccon; 101 102 default: 103 qemu_log_mask(LOG_GUEST_ERROR, 104 "unknown RER in MX PIC range: 0x%08x\n", 105 (uint32_t)offset); 106 return 0; 107 } 108 } 109 } 110 111 static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx, 112 unsigned cpu) 113 { 114 uint32_t mipicause = mx->cpu[cpu].mipicause; 115 uint32_t mipipart = mx->mipipart; 116 117 return (((mipicause & 1) << (mipipart & 3)) | 118 ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) | 119 ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) | 120 ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7; 121 } 122 123 static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx, 124 unsigned cpu) 125 { 126 return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) & 127 mx->cpu[cpu].mirout_cache) << 2) | 128 xtensa_mx_pic_get_ipi_for_cpu(mx, cpu); 129 } 130 131 static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu) 132 { 133 uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu); 134 uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq; 135 unsigned i; 136 137 qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n", 138 __func__, cpu, irq, changed_irq); 139 mx->cpu[cpu].irq_state_cache = irq; 140 for (i = 0; changed_irq; ++i) { 141 uint32_t mask = 1u << i; 142 143 if (changed_irq & mask) { 144 changed_irq ^= mask; 145 qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask); 146 } 147 } 148 } 149 150 static void xtensa_mx_pic_update_all(XtensaMxPic *mx) 151 { 152 unsigned cpu; 153 154 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 155 xtensa_mx_pic_update_cpu(mx, cpu); 156 } 157 } 158 159 static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset, 160 uint64_t v, unsigned size) 161 { 162 struct XtensaMxPicCpu *mx_cpu = opaque; 163 struct XtensaMxPic *mx = mx_cpu->mx; 164 unsigned cpu; 165 166 if (offset < MIROUT + mx->n_irq) { 167 mx->mirout[offset - MIROUT] = v; 168 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 169 uint32_t mask = 1u << (offset - MIROUT); 170 171 if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) { 172 mx->cpu[cpu].mirout_cache ^= mask; 173 xtensa_mx_pic_update_cpu(mx, cpu); 174 } 175 } 176 } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) { 177 cpu = offset - MIPICAUSE; 178 mx->cpu[cpu].mipicause &= ~v; 179 xtensa_mx_pic_update_cpu(mx, cpu); 180 } else if (offset >= MIPISET && offset < MIPISET + 16) { 181 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 182 if (v & (1u << cpu)) { 183 mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET); 184 xtensa_mx_pic_update_cpu(mx, cpu); 185 } 186 } 187 } else { 188 uint32_t change = 0; 189 uint32_t oldv, newv; 190 const char *name = "???"; 191 192 switch (offset) { 193 case MIENG: 194 change = mx->mieng & v; 195 oldv = mx->mieng; 196 mx->mieng &= ~v; 197 newv = mx->mieng; 198 name = "MIENG"; 199 break; 200 201 case MIENGSET: 202 change = ~mx->mieng & v; 203 oldv = mx->mieng; 204 mx->mieng |= v; 205 newv = mx->mieng; 206 name = "MIENG"; 207 break; 208 209 case MIASG: 210 change = mx->miasg & v; 211 oldv = mx->miasg; 212 mx->miasg &= ~v; 213 newv = mx->miasg; 214 name = "MIASG"; 215 break; 216 217 case MIASGSET: 218 change = ~mx->miasg & v; 219 oldv = mx->miasg; 220 mx->miasg |= v; 221 newv = mx->miasg; 222 name = "MIASG"; 223 break; 224 225 case MIPIPART: 226 change = mx->mipipart ^ v; 227 oldv = mx->mipipart; 228 mx->mipipart = v; 229 newv = mx->mipipart; 230 name = "MIPIPART"; 231 break; 232 233 case MPSCORE: 234 change = mx->runstall ^ v; 235 oldv = mx->runstall; 236 mx->runstall = v; 237 newv = mx->runstall; 238 name = "RUNSTALL"; 239 for (cpu = 0; cpu < mx->n_cpu; ++cpu) { 240 if (change & (1u << cpu)) { 241 qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu)); 242 } 243 } 244 break; 245 246 case CCON: 247 mx_cpu->ccon = v & 0x1; 248 break; 249 250 default: 251 qemu_log_mask(LOG_GUEST_ERROR, 252 "unknown WER in MX PIC range: 0x%08x = 0x%08x\n", 253 (uint32_t)offset, (uint32_t)v); 254 break; 255 } 256 if (change) { 257 qemu_log_mask(CPU_LOG_INT, 258 "%s: %s changed by CPU %d: %08x -> %08x\n", 259 __func__, name, (int)(mx_cpu - mx->cpu), 260 oldv, newv); 261 xtensa_mx_pic_update_all(mx); 262 } 263 } 264 } 265 266 static const MemoryRegionOps xtensa_mx_pic_ops = { 267 .read = xtensa_mx_pic_ext_reg_read, 268 .write = xtensa_mx_pic_ext_reg_write, 269 .endianness = DEVICE_NATIVE_ENDIAN, 270 .valid = { 271 .unaligned = true, 272 }, 273 }; 274 275 MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx, 276 qemu_irq *irq, 277 qemu_irq runstall) 278 { 279 struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu; 280 281 mx_cpu->mx = mx; 282 mx_cpu->irq = irq; 283 mx_cpu->runstall = runstall; 284 285 memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu, 286 "mx_pic", 0x280); 287 288 ++mx->n_cpu; 289 return &mx_cpu->reg; 290 } 291 292 static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active) 293 { 294 XtensaMxPic *mx = opaque; 295 296 if (irq < mx->n_irq) { 297 uint32_t old_irq_state = mx->ext_irq_state; 298 299 if (active) { 300 mx->ext_irq_state |= 1u << irq; 301 } else { 302 mx->ext_irq_state &= ~(1u << irq); 303 } 304 if (old_irq_state != mx->ext_irq_state) { 305 qemu_log_mask(CPU_LOG_INT, 306 "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n", 307 __func__, irq, active, 308 old_irq_state, mx->ext_irq_state); 309 xtensa_mx_pic_update_all(mx); 310 } 311 } else { 312 qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n", 313 __func__, irq); 314 } 315 } 316 317 XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq) 318 { 319 XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic)); 320 321 mx->n_irq = n_irq + 1; 322 mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx, 323 mx->n_irq); 324 return mx; 325 } 326 327 void xtensa_mx_pic_reset(void *opaque) 328 { 329 XtensaMxPic *mx = opaque; 330 unsigned i; 331 332 mx->ext_irq_state = 0; 333 mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u; 334 mx->miasg = 0; 335 mx->mipipart = 0; 336 for (i = 0; i < mx->n_irq; ++i) { 337 mx->mirout[i] = 1; 338 } 339 for (i = 0; i < mx->n_cpu; ++i) { 340 mx->cpu[i].mipicause = 0; 341 mx->cpu[i].mirout_cache = i ? 0 : mx->mieng; 342 mx->cpu[i].irq_state_cache = 0; 343 mx->cpu[i].ccon = 0; 344 } 345 mx->runstall = (1u << mx->n_cpu) - 2; 346 for (i = 0; i < mx->n_cpu; ++i) { 347 qemu_set_irq(mx->cpu[i].runstall, i > 0); 348 } 349 } 350 351 qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx) 352 { 353 return mx->irq_inputs + 1; 354 } 355