qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

mx_pic.c (10785B)


      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] = 0;
    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 }