qemu/hw/xtensa/mx_pic.c
<<
>>
Prefs
   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
  48struct 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
  72static 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
 111static 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
 123static 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
 131static 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
 150static 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
 159static 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
 266static 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
 275MemoryRegion *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
 292static 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
 317XtensaMxPic *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
 327void 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
 351qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
 352{
 353    return mx->irq_inputs + 1;
 354}
 355