xref: /qemu/hw/intc/sifive_plic.c (revision c9270e10a5e3aa18a55bb5c115ae7838c8c43e41)
11e24429eSMichael Clark /*
21e24429eSMichael Clark  * SiFive PLIC (Platform Level Interrupt Controller)
31e24429eSMichael Clark  *
41e24429eSMichael Clark  * Copyright (c) 2017 SiFive, Inc.
51e24429eSMichael Clark  *
61e24429eSMichael Clark  * This provides a parameterizable interrupt controller based on SiFive's PLIC.
71e24429eSMichael Clark  *
81e24429eSMichael Clark  * This program is free software; you can redistribute it and/or modify it
91e24429eSMichael Clark  * under the terms and conditions of the GNU General Public License,
101e24429eSMichael Clark  * version 2 or later, as published by the Free Software Foundation.
111e24429eSMichael Clark  *
121e24429eSMichael Clark  * This program is distributed in the hope it will be useful, but WITHOUT
131e24429eSMichael Clark  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
141e24429eSMichael Clark  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
151e24429eSMichael Clark  * more details.
161e24429eSMichael Clark  *
171e24429eSMichael Clark  * You should have received a copy of the GNU General Public License along with
181e24429eSMichael Clark  * this program.  If not, see <http://www.gnu.org/licenses/>.
191e24429eSMichael Clark  */
201e24429eSMichael Clark 
211e24429eSMichael Clark #include "qemu/osdep.h"
223e80f690SMarkus Armbruster #include "qapi/error.h"
231e24429eSMichael Clark #include "qemu/log.h"
240b8fa32fSMarkus Armbruster #include "qemu/module.h"
251e24429eSMichael Clark #include "qemu/error-report.h"
261e24429eSMichael Clark #include "hw/sysbus.h"
274f5604c4SAlistair Francis #include "hw/pci/msi.h"
28c4473127SLike Xu #include "hw/boards.h"
29a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
301e24429eSMichael Clark #include "target/riscv/cpu.h"
31e3e7039cSMichael Clark #include "sysemu/sysemu.h"
321e24429eSMichael Clark #include "hw/riscv/sifive_plic.h"
331e24429eSMichael Clark 
341e24429eSMichael Clark #define RISCV_DEBUG_PLIC 0
351e24429eSMichael Clark 
361e24429eSMichael Clark static PLICMode char_to_mode(char c)
371e24429eSMichael Clark {
381e24429eSMichael Clark     switch (c) {
391e24429eSMichael Clark     case 'U': return PLICMode_U;
401e24429eSMichael Clark     case 'S': return PLICMode_S;
411e24429eSMichael Clark     case 'H': return PLICMode_H;
421e24429eSMichael Clark     case 'M': return PLICMode_M;
431e24429eSMichael Clark     default:
441e24429eSMichael Clark         error_report("plic: invalid mode '%c'", c);
451e24429eSMichael Clark         exit(1);
461e24429eSMichael Clark     }
471e24429eSMichael Clark }
481e24429eSMichael Clark 
491e24429eSMichael Clark static char mode_to_char(PLICMode m)
501e24429eSMichael Clark {
511e24429eSMichael Clark     switch (m) {
521e24429eSMichael Clark     case PLICMode_U: return 'U';
531e24429eSMichael Clark     case PLICMode_S: return 'S';
541e24429eSMichael Clark     case PLICMode_H: return 'H';
551e24429eSMichael Clark     case PLICMode_M: return 'M';
561e24429eSMichael Clark     default: return '?';
571e24429eSMichael Clark     }
581e24429eSMichael Clark }
591e24429eSMichael Clark 
601e24429eSMichael Clark static void sifive_plic_print_state(SiFivePLICState *plic)
611e24429eSMichael Clark {
621e24429eSMichael Clark     int i;
631e24429eSMichael Clark     int addrid;
641e24429eSMichael Clark 
651e24429eSMichael Clark     /* pending */
661e24429eSMichael Clark     qemu_log("pending       : ");
671e24429eSMichael Clark     for (i = plic->bitfield_words - 1; i >= 0; i--) {
681e24429eSMichael Clark         qemu_log("%08x", plic->pending[i]);
691e24429eSMichael Clark     }
701e24429eSMichael Clark     qemu_log("\n");
711e24429eSMichael Clark 
721e24429eSMichael Clark     /* pending */
731e24429eSMichael Clark     qemu_log("claimed       : ");
741e24429eSMichael Clark     for (i = plic->bitfield_words - 1; i >= 0; i--) {
751e24429eSMichael Clark         qemu_log("%08x", plic->claimed[i]);
761e24429eSMichael Clark     }
771e24429eSMichael Clark     qemu_log("\n");
781e24429eSMichael Clark 
791e24429eSMichael Clark     for (addrid = 0; addrid < plic->num_addrs; addrid++) {
801e24429eSMichael Clark         qemu_log("hart%d-%c enable: ",
811e24429eSMichael Clark             plic->addr_config[addrid].hartid,
821e24429eSMichael Clark             mode_to_char(plic->addr_config[addrid].mode));
831e24429eSMichael Clark         for (i = plic->bitfield_words - 1; i >= 0; i--) {
841e24429eSMichael Clark             qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
851e24429eSMichael Clark         }
861e24429eSMichael Clark         qemu_log("\n");
871e24429eSMichael Clark     }
881e24429eSMichael Clark }
891e24429eSMichael Clark 
90d78940ecSMichael Clark static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
911e24429eSMichael Clark {
92d78940ecSMichael Clark     uint32_t old, new, cmp = atomic_read(a);
93d78940ecSMichael Clark 
94d78940ecSMichael Clark     do {
95d78940ecSMichael Clark         old = cmp;
96d78940ecSMichael Clark         new = (old & ~mask) | (value & mask);
97d78940ecSMichael Clark         cmp = atomic_cmpxchg(a, old, new);
98d78940ecSMichael Clark     } while (old != cmp);
99d78940ecSMichael Clark 
100d78940ecSMichael Clark     return old;
1011e24429eSMichael Clark }
1021e24429eSMichael Clark 
103d78940ecSMichael Clark static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
1041e24429eSMichael Clark {
105d78940ecSMichael Clark     atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
1061e24429eSMichael Clark }
1071e24429eSMichael Clark 
108d78940ecSMichael Clark static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
1091e24429eSMichael Clark {
110d78940ecSMichael Clark     atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
111d78940ecSMichael Clark }
112d78940ecSMichael Clark 
113d78940ecSMichael Clark static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
114d78940ecSMichael Clark {
115d78940ecSMichael Clark     int i, j;
1161e24429eSMichael Clark     for (i = 0; i < plic->bitfield_words; i++) {
1171e24429eSMichael Clark         uint32_t pending_enabled_not_claimed =
1181e24429eSMichael Clark             (plic->pending[i] & ~plic->claimed[i]) &
1191e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + i];
1201e24429eSMichael Clark         if (!pending_enabled_not_claimed) {
1211e24429eSMichael Clark             continue;
1221e24429eSMichael Clark         }
1231e24429eSMichael Clark         for (j = 0; j < 32; j++) {
1241e24429eSMichael Clark             int irq = (i << 5) + j;
1251e24429eSMichael Clark             uint32_t prio = plic->source_priority[irq];
1261e24429eSMichael Clark             int enabled = pending_enabled_not_claimed & (1 << j);
1271e24429eSMichael Clark             if (enabled && prio > plic->target_priority[addrid]) {
128d78940ecSMichael Clark                 return 1;
1291e24429eSMichael Clark             }
1301e24429eSMichael Clark         }
1311e24429eSMichael Clark     }
132d78940ecSMichael Clark     return 0;
1331e24429eSMichael Clark }
1341e24429eSMichael Clark 
1351e24429eSMichael Clark static void sifive_plic_update(SiFivePLICState *plic)
1361e24429eSMichael Clark {
1371e24429eSMichael Clark     int addrid;
1381e24429eSMichael Clark 
1391e24429eSMichael Clark     /* raise irq on harts where this irq is enabled */
1401e24429eSMichael Clark     for (addrid = 0; addrid < plic->num_addrs; addrid++) {
1411e24429eSMichael Clark         uint32_t hartid = plic->addr_config[addrid].hartid;
1421e24429eSMichael Clark         PLICMode mode = plic->addr_config[addrid].mode;
1431e24429eSMichael Clark         CPUState *cpu = qemu_get_cpu(hartid);
1441e24429eSMichael Clark         CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
1451e24429eSMichael Clark         if (!env) {
1461e24429eSMichael Clark             continue;
1471e24429eSMichael Clark         }
148d78940ecSMichael Clark         int level = sifive_plic_irqs_pending(plic, addrid);
1491e24429eSMichael Clark         switch (mode) {
1501e24429eSMichael Clark         case PLICMode_M:
15185ba724fSMichael Clark             riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
1521e24429eSMichael Clark             break;
1531e24429eSMichael Clark         case PLICMode_S:
15485ba724fSMichael Clark             riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level));
1551e24429eSMichael Clark             break;
1561e24429eSMichael Clark         default:
1571e24429eSMichael Clark             break;
1581e24429eSMichael Clark         }
1591e24429eSMichael Clark     }
1601e24429eSMichael Clark 
1611e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
1621e24429eSMichael Clark         sifive_plic_print_state(plic);
1631e24429eSMichael Clark     }
1641e24429eSMichael Clark }
1651e24429eSMichael Clark 
1661e24429eSMichael Clark static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
1671e24429eSMichael Clark {
1681e24429eSMichael Clark     int i, j;
169aa4d30f6SJessica Clarke     uint32_t max_irq = 0;
170aa4d30f6SJessica Clarke     uint32_t max_prio = plic->target_priority[addrid];
171aa4d30f6SJessica Clarke 
1721e24429eSMichael Clark     for (i = 0; i < plic->bitfield_words; i++) {
1731e24429eSMichael Clark         uint32_t pending_enabled_not_claimed =
1741e24429eSMichael Clark             (plic->pending[i] & ~plic->claimed[i]) &
1751e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + i];
1761e24429eSMichael Clark         if (!pending_enabled_not_claimed) {
1771e24429eSMichael Clark             continue;
1781e24429eSMichael Clark         }
1791e24429eSMichael Clark         for (j = 0; j < 32; j++) {
1801e24429eSMichael Clark             int irq = (i << 5) + j;
1811e24429eSMichael Clark             uint32_t prio = plic->source_priority[irq];
1821e24429eSMichael Clark             int enabled = pending_enabled_not_claimed & (1 << j);
183aa4d30f6SJessica Clarke             if (enabled && prio > max_prio) {
184aa4d30f6SJessica Clarke                 max_irq = irq;
185aa4d30f6SJessica Clarke                 max_prio = prio;
1861e24429eSMichael Clark             }
1871e24429eSMichael Clark         }
1881e24429eSMichael Clark     }
189aa4d30f6SJessica Clarke 
190aa4d30f6SJessica Clarke     if (max_irq) {
191aa4d30f6SJessica Clarke         sifive_plic_set_pending(plic, max_irq, false);
192aa4d30f6SJessica Clarke         sifive_plic_set_claimed(plic, max_irq, true);
193aa4d30f6SJessica Clarke     }
194aa4d30f6SJessica Clarke     return max_irq;
1951e24429eSMichael Clark }
1961e24429eSMichael Clark 
1971e24429eSMichael Clark static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
1981e24429eSMichael Clark {
1991e24429eSMichael Clark     SiFivePLICState *plic = opaque;
2001e24429eSMichael Clark 
2011e24429eSMichael Clark     /* writes must be 4 byte words */
2021e24429eSMichael Clark     if ((addr & 0x3) != 0) {
2031e24429eSMichael Clark         goto err;
2041e24429eSMichael Clark     }
2051e24429eSMichael Clark 
2061e24429eSMichael Clark     if (addr >= plic->priority_base && /* 4 bytes per source */
2071e24429eSMichael Clark         addr < plic->priority_base + (plic->num_sources << 2))
2081e24429eSMichael Clark     {
2090feb4a71SAlistair Francis         uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
2101e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2111e24429eSMichael Clark             qemu_log("plic: read priority: irq=%d priority=%d\n",
2121e24429eSMichael Clark                 irq, plic->source_priority[irq]);
2131e24429eSMichael Clark         }
2141e24429eSMichael Clark         return plic->source_priority[irq];
2151e24429eSMichael Clark     } else if (addr >= plic->pending_base && /* 1 bit per source */
2161e24429eSMichael Clark                addr < plic->pending_base + (plic->num_sources >> 3))
2171e24429eSMichael Clark     {
218e41848e5SMichael Clark         uint32_t word = (addr - plic->pending_base) >> 2;
2191e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2201e24429eSMichael Clark             qemu_log("plic: read pending: word=%d value=%d\n",
2211e24429eSMichael Clark                 word, plic->pending[word]);
2221e24429eSMichael Clark         }
2231e24429eSMichael Clark         return plic->pending[word];
2241e24429eSMichael Clark     } else if (addr >= plic->enable_base && /* 1 bit per source */
2251e24429eSMichael Clark              addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
2261e24429eSMichael Clark     {
2271e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
2281e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
2291e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
2301e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2311e24429eSMichael Clark                 qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
2321e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2331e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode), wordid,
2341e24429eSMichael Clark                     plic->enable[addrid * plic->bitfield_words + wordid]);
2351e24429eSMichael Clark             }
2361e24429eSMichael Clark             return plic->enable[addrid * plic->bitfield_words + wordid];
2371e24429eSMichael Clark         }
2381e24429eSMichael Clark     } else if (addr >= plic->context_base && /* 1 bit per source */
2391e24429eSMichael Clark              addr < plic->context_base + plic->num_addrs * plic->context_stride)
2401e24429eSMichael Clark     {
2411e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
2421e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
2431e24429eSMichael Clark         if (contextid == 0) {
2441e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2451e24429eSMichael Clark                 qemu_log("plic: read priority: hart%d-%c priority=%x\n",
2461e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2471e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
2481e24429eSMichael Clark                     plic->target_priority[addrid]);
2491e24429eSMichael Clark             }
2501e24429eSMichael Clark             return plic->target_priority[addrid];
2511e24429eSMichael Clark         } else if (contextid == 4) {
2521e24429eSMichael Clark             uint32_t value = sifive_plic_claim(plic, addrid);
2531e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2541e24429eSMichael Clark                 qemu_log("plic: read claim: hart%d-%c irq=%x\n",
2551e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2561e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
2571e24429eSMichael Clark                     value);
2581e24429eSMichael Clark             }
25955765822SJessica Clarke             sifive_plic_update(plic);
2601e24429eSMichael Clark             return value;
2611e24429eSMichael Clark         }
2621e24429eSMichael Clark     }
2631e24429eSMichael Clark 
2641e24429eSMichael Clark err:
26579bcac25SAlistair Francis     qemu_log_mask(LOG_GUEST_ERROR,
26679bcac25SAlistair Francis                   "%s: Invalid register read 0x%" HWADDR_PRIx "\n",
26779bcac25SAlistair Francis                   __func__, addr);
2681e24429eSMichael Clark     return 0;
2691e24429eSMichael Clark }
2701e24429eSMichael Clark 
2711e24429eSMichael Clark static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
2721e24429eSMichael Clark         unsigned size)
2731e24429eSMichael Clark {
2741e24429eSMichael Clark     SiFivePLICState *plic = opaque;
2751e24429eSMichael Clark 
2761e24429eSMichael Clark     /* writes must be 4 byte words */
2771e24429eSMichael Clark     if ((addr & 0x3) != 0) {
2781e24429eSMichael Clark         goto err;
2791e24429eSMichael Clark     }
2801e24429eSMichael Clark 
2811e24429eSMichael Clark     if (addr >= plic->priority_base && /* 4 bytes per source */
2821e24429eSMichael Clark         addr < plic->priority_base + (plic->num_sources << 2))
2831e24429eSMichael Clark     {
2840feb4a71SAlistair Francis         uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
2851e24429eSMichael Clark         plic->source_priority[irq] = value & 7;
2861e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2871e24429eSMichael Clark             qemu_log("plic: write priority: irq=%d priority=%d\n",
2881e24429eSMichael Clark                 irq, plic->source_priority[irq]);
2891e24429eSMichael Clark         }
29055765822SJessica Clarke         sifive_plic_update(plic);
2911e24429eSMichael Clark         return;
2921e24429eSMichael Clark     } else if (addr >= plic->pending_base && /* 1 bit per source */
2931e24429eSMichael Clark                addr < plic->pending_base + (plic->num_sources >> 3))
2941e24429eSMichael Clark     {
29579bcac25SAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
29679bcac25SAlistair Francis                       "%s: invalid pending write: 0x%" HWADDR_PRIx "",
29779bcac25SAlistair Francis                       __func__, addr);
2981e24429eSMichael Clark         return;
2991e24429eSMichael Clark     } else if (addr >= plic->enable_base && /* 1 bit per source */
3001e24429eSMichael Clark         addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
3011e24429eSMichael Clark     {
3021e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
3031e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
3041e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
3051e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + wordid] = value;
3061e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3071e24429eSMichael Clark                 qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
3081e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3091e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode), wordid,
3101e24429eSMichael Clark                     plic->enable[addrid * plic->bitfield_words + wordid]);
3111e24429eSMichael Clark             }
3121e24429eSMichael Clark             return;
3131e24429eSMichael Clark         }
3141e24429eSMichael Clark     } else if (addr >= plic->context_base && /* 4 bytes per reg */
3151e24429eSMichael Clark         addr < plic->context_base + plic->num_addrs * plic->context_stride)
3161e24429eSMichael Clark     {
3171e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
3181e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
3191e24429eSMichael Clark         if (contextid == 0) {
3201e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3211e24429eSMichael Clark                 qemu_log("plic: write priority: hart%d-%c priority=%x\n",
3221e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3231e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
3241e24429eSMichael Clark                     plic->target_priority[addrid]);
3251e24429eSMichael Clark             }
3261e24429eSMichael Clark             if (value <= plic->num_priorities) {
3271e24429eSMichael Clark                 plic->target_priority[addrid] = value;
3281e24429eSMichael Clark                 sifive_plic_update(plic);
3291e24429eSMichael Clark             }
3301e24429eSMichael Clark             return;
3311e24429eSMichael Clark         } else if (contextid == 4) {
3321e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3331e24429eSMichael Clark                 qemu_log("plic: write claim: hart%d-%c irq=%x\n",
3341e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3351e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
3361e24429eSMichael Clark                     (uint32_t)value);
3371e24429eSMichael Clark             }
3381e24429eSMichael Clark             if (value < plic->num_sources) {
3391e24429eSMichael Clark                 sifive_plic_set_claimed(plic, value, false);
3401e24429eSMichael Clark                 sifive_plic_update(plic);
3411e24429eSMichael Clark             }
3421e24429eSMichael Clark             return;
3431e24429eSMichael Clark         }
3441e24429eSMichael Clark     }
3451e24429eSMichael Clark 
3461e24429eSMichael Clark err:
34779bcac25SAlistair Francis     qemu_log_mask(LOG_GUEST_ERROR,
34879bcac25SAlistair Francis                   "%s: Invalid register write 0x%" HWADDR_PRIx "\n",
34979bcac25SAlistair Francis                   __func__, addr);
3501e24429eSMichael Clark }
3511e24429eSMichael Clark 
3521e24429eSMichael Clark static const MemoryRegionOps sifive_plic_ops = {
3531e24429eSMichael Clark     .read = sifive_plic_read,
3541e24429eSMichael Clark     .write = sifive_plic_write,
3551e24429eSMichael Clark     .endianness = DEVICE_LITTLE_ENDIAN,
3561e24429eSMichael Clark     .valid = {
3571e24429eSMichael Clark         .min_access_size = 4,
3581e24429eSMichael Clark         .max_access_size = 4
3591e24429eSMichael Clark     }
3601e24429eSMichael Clark };
3611e24429eSMichael Clark 
3621e24429eSMichael Clark static Property sifive_plic_properties[] = {
3631e24429eSMichael Clark     DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
364*c9270e10SAnup Patel     DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0),
3651e24429eSMichael Clark     DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
3661e24429eSMichael Clark     DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
3671e24429eSMichael Clark     DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
3681e24429eSMichael Clark     DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
3691e24429eSMichael Clark     DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
3701e24429eSMichael Clark     DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
3711e24429eSMichael Clark     DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
3721e24429eSMichael Clark     DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
3731e24429eSMichael Clark     DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
3741e24429eSMichael Clark     DEFINE_PROP_END_OF_LIST(),
3751e24429eSMichael Clark };
3761e24429eSMichael Clark 
3771e24429eSMichael Clark /*
3781e24429eSMichael Clark  * parse PLIC hart/mode address offset config
3791e24429eSMichael Clark  *
3801e24429eSMichael Clark  * "M"              1 hart with M mode
3811e24429eSMichael Clark  * "MS,MS"          2 harts, 0-1 with M and S mode
3821e24429eSMichael Clark  * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
3831e24429eSMichael Clark  */
3841e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic)
3851e24429eSMichael Clark {
3861e24429eSMichael Clark     int addrid, hartid, modes;
3871e24429eSMichael Clark     const char *p;
3881e24429eSMichael Clark     char c;
3891e24429eSMichael Clark 
3901e24429eSMichael Clark     /* count and validate hart/mode combinations */
3911e24429eSMichael Clark     addrid = 0, hartid = 0, modes = 0;
3921e24429eSMichael Clark     p = plic->hart_config;
3931e24429eSMichael Clark     while ((c = *p++)) {
3941e24429eSMichael Clark         if (c == ',') {
395244df421SMichael Clark             addrid += ctpop8(modes);
3961e24429eSMichael Clark             modes = 0;
3971e24429eSMichael Clark             hartid++;
3981e24429eSMichael Clark         } else {
3991e24429eSMichael Clark             int m = 1 << char_to_mode(c);
4001e24429eSMichael Clark             if (modes == (modes | m)) {
4011e24429eSMichael Clark                 error_report("plic: duplicate mode '%c' in config: %s",
4021e24429eSMichael Clark                              c, plic->hart_config);
4031e24429eSMichael Clark                 exit(1);
4041e24429eSMichael Clark             }
4051e24429eSMichael Clark             modes |= m;
4061e24429eSMichael Clark         }
4071e24429eSMichael Clark     }
4081e24429eSMichael Clark     if (modes) {
409244df421SMichael Clark         addrid += ctpop8(modes);
4101e24429eSMichael Clark     }
4111e24429eSMichael Clark     hartid++;
4121e24429eSMichael Clark 
4131e24429eSMichael Clark     plic->num_addrs = addrid;
414*c9270e10SAnup Patel     plic->num_harts = hartid;
415*c9270e10SAnup Patel 
416*c9270e10SAnup Patel     /* store hart/mode combinations */
4171e24429eSMichael Clark     plic->addr_config = g_new(PLICAddr, plic->num_addrs);
418*c9270e10SAnup Patel     addrid = 0, hartid = plic->hartid_base;
4191e24429eSMichael Clark     p = plic->hart_config;
4201e24429eSMichael Clark     while ((c = *p++)) {
4211e24429eSMichael Clark         if (c == ',') {
4221e24429eSMichael Clark             hartid++;
4231e24429eSMichael Clark         } else {
4241e24429eSMichael Clark             plic->addr_config[addrid].addrid = addrid;
4251e24429eSMichael Clark             plic->addr_config[addrid].hartid = hartid;
4261e24429eSMichael Clark             plic->addr_config[addrid].mode = char_to_mode(c);
4271e24429eSMichael Clark             addrid++;
4281e24429eSMichael Clark         }
4291e24429eSMichael Clark     }
4301e24429eSMichael Clark }
4311e24429eSMichael Clark 
4321e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level)
4331e24429eSMichael Clark {
4341e24429eSMichael Clark     SiFivePLICState *plic = opaque;
4351e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
4361e24429eSMichael Clark         qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
4371e24429eSMichael Clark     }
4381e24429eSMichael Clark     sifive_plic_set_pending(plic, irq, level > 0);
4391e24429eSMichael Clark     sifive_plic_update(plic);
4401e24429eSMichael Clark }
4411e24429eSMichael Clark 
4421e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp)
4431e24429eSMichael Clark {
4441e24429eSMichael Clark     SiFivePLICState *plic = SIFIVE_PLIC(dev);
445e3e7039cSMichael Clark     int i;
4461e24429eSMichael Clark 
4471e24429eSMichael Clark     memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
4481e24429eSMichael Clark                           TYPE_SIFIVE_PLIC, plic->aperture_size);
4491e24429eSMichael Clark     parse_hart_config(plic);
4501e24429eSMichael Clark     plic->bitfield_words = (plic->num_sources + 31) >> 5;
4511e24429eSMichael Clark     plic->source_priority = g_new0(uint32_t, plic->num_sources);
4521e24429eSMichael Clark     plic->target_priority = g_new(uint32_t, plic->num_addrs);
4531e24429eSMichael Clark     plic->pending = g_new0(uint32_t, plic->bitfield_words);
4541e24429eSMichael Clark     plic->claimed = g_new0(uint32_t, plic->bitfield_words);
4551e24429eSMichael Clark     plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
4561e24429eSMichael Clark     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
457647a70a1SAlistair Francis     qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
458e3e7039cSMichael Clark 
459e3e7039cSMichael Clark     /* We can't allow the supervisor to control SEIP as this would allow the
460e3e7039cSMichael Clark      * supervisor to clear a pending external interrupt which will result in
461e3e7039cSMichael Clark      * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
462e3e7039cSMichael Clark      * hardware controlled when a PLIC is attached.
463e3e7039cSMichael Clark      */
464*c9270e10SAnup Patel     for (i = 0; i < plic->num_harts; i++) {
465*c9270e10SAnup Patel         RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(plic->hartid_base + i));
466e3e7039cSMichael Clark         if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
467e3e7039cSMichael Clark             error_report("SEIP already claimed");
468e3e7039cSMichael Clark             exit(1);
469e3e7039cSMichael Clark         }
470e3e7039cSMichael Clark     }
47184bdc58cSPeter Maydell 
4724f5604c4SAlistair Francis     msi_nonbroken = true;
4731e24429eSMichael Clark }
4741e24429eSMichael Clark 
4751e24429eSMichael Clark static void sifive_plic_class_init(ObjectClass *klass, void *data)
4761e24429eSMichael Clark {
4771e24429eSMichael Clark     DeviceClass *dc = DEVICE_CLASS(klass);
4781e24429eSMichael Clark 
4794f67d30bSMarc-André Lureau     device_class_set_props(dc, sifive_plic_properties);
4801e24429eSMichael Clark     dc->realize = sifive_plic_realize;
4811e24429eSMichael Clark }
4821e24429eSMichael Clark 
4831e24429eSMichael Clark static const TypeInfo sifive_plic_info = {
4841e24429eSMichael Clark     .name          = TYPE_SIFIVE_PLIC,
4851e24429eSMichael Clark     .parent        = TYPE_SYS_BUS_DEVICE,
4861e24429eSMichael Clark     .instance_size = sizeof(SiFivePLICState),
4871e24429eSMichael Clark     .class_init    = sifive_plic_class_init,
4881e24429eSMichael Clark };
4891e24429eSMichael Clark 
4901e24429eSMichael Clark static void sifive_plic_register_types(void)
4911e24429eSMichael Clark {
4921e24429eSMichael Clark     type_register_static(&sifive_plic_info);
4931e24429eSMichael Clark }
4941e24429eSMichael Clark 
4951e24429eSMichael Clark type_init(sifive_plic_register_types)
4961e24429eSMichael Clark 
4971e24429eSMichael Clark /*
4981e24429eSMichael Clark  * Create PLIC device.
4991e24429eSMichael Clark  */
5001e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
501*c9270e10SAnup Patel     uint32_t hartid_base, uint32_t num_sources,
502*c9270e10SAnup Patel     uint32_t num_priorities, uint32_t priority_base,
503*c9270e10SAnup Patel     uint32_t pending_base, uint32_t enable_base,
504*c9270e10SAnup Patel     uint32_t enable_stride, uint32_t context_base,
505*c9270e10SAnup Patel     uint32_t context_stride, uint32_t aperture_size)
5061e24429eSMichael Clark {
5073e80f690SMarkus Armbruster     DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
5081e24429eSMichael Clark     assert(enable_stride == (enable_stride & -enable_stride));
5091e24429eSMichael Clark     assert(context_stride == (context_stride & -context_stride));
5101e24429eSMichael Clark     qdev_prop_set_string(dev, "hart-config", hart_config);
511*c9270e10SAnup Patel     qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
5121e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-sources", num_sources);
5131e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
5141e24429eSMichael Clark     qdev_prop_set_uint32(dev, "priority-base", priority_base);
5151e24429eSMichael Clark     qdev_prop_set_uint32(dev, "pending-base", pending_base);
5161e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-base", enable_base);
5171e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
5181e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-base", context_base);
5191e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-stride", context_stride);
5201e24429eSMichael Clark     qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
5213c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
5221e24429eSMichael Clark     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
5231e24429eSMichael Clark     return dev;
5241e24429eSMichael Clark }
525