xref: /qemu/hw/intc/sifive_plic.c (revision d78940ec5d07d3b514f2fd8f941c58118fce2815)
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"
221e24429eSMichael Clark #include "qemu/log.h"
231e24429eSMichael Clark #include "qemu/error-report.h"
241e24429eSMichael Clark #include "hw/sysbus.h"
251e24429eSMichael Clark #include "target/riscv/cpu.h"
261e24429eSMichael Clark #include "hw/riscv/sifive_plic.h"
271e24429eSMichael Clark 
281e24429eSMichael Clark #define RISCV_DEBUG_PLIC 0
291e24429eSMichael Clark 
301e24429eSMichael Clark static PLICMode char_to_mode(char c)
311e24429eSMichael Clark {
321e24429eSMichael Clark     switch (c) {
331e24429eSMichael Clark     case 'U': return PLICMode_U;
341e24429eSMichael Clark     case 'S': return PLICMode_S;
351e24429eSMichael Clark     case 'H': return PLICMode_H;
361e24429eSMichael Clark     case 'M': return PLICMode_M;
371e24429eSMichael Clark     default:
381e24429eSMichael Clark         error_report("plic: invalid mode '%c'", c);
391e24429eSMichael Clark         exit(1);
401e24429eSMichael Clark     }
411e24429eSMichael Clark }
421e24429eSMichael Clark 
431e24429eSMichael Clark static char mode_to_char(PLICMode m)
441e24429eSMichael Clark {
451e24429eSMichael Clark     switch (m) {
461e24429eSMichael Clark     case PLICMode_U: return 'U';
471e24429eSMichael Clark     case PLICMode_S: return 'S';
481e24429eSMichael Clark     case PLICMode_H: return 'H';
491e24429eSMichael Clark     case PLICMode_M: return 'M';
501e24429eSMichael Clark     default: return '?';
511e24429eSMichael Clark     }
521e24429eSMichael Clark }
531e24429eSMichael Clark 
541e24429eSMichael Clark static void sifive_plic_print_state(SiFivePLICState *plic)
551e24429eSMichael Clark {
561e24429eSMichael Clark     int i;
571e24429eSMichael Clark     int addrid;
581e24429eSMichael Clark 
591e24429eSMichael Clark     /* pending */
601e24429eSMichael Clark     qemu_log("pending       : ");
611e24429eSMichael Clark     for (i = plic->bitfield_words - 1; i >= 0; i--) {
621e24429eSMichael Clark         qemu_log("%08x", plic->pending[i]);
631e24429eSMichael Clark     }
641e24429eSMichael Clark     qemu_log("\n");
651e24429eSMichael Clark 
661e24429eSMichael Clark     /* pending */
671e24429eSMichael Clark     qemu_log("claimed       : ");
681e24429eSMichael Clark     for (i = plic->bitfield_words - 1; i >= 0; i--) {
691e24429eSMichael Clark         qemu_log("%08x", plic->claimed[i]);
701e24429eSMichael Clark     }
711e24429eSMichael Clark     qemu_log("\n");
721e24429eSMichael Clark 
731e24429eSMichael Clark     for (addrid = 0; addrid < plic->num_addrs; addrid++) {
741e24429eSMichael Clark         qemu_log("hart%d-%c enable: ",
751e24429eSMichael Clark             plic->addr_config[addrid].hartid,
761e24429eSMichael Clark             mode_to_char(plic->addr_config[addrid].mode));
771e24429eSMichael Clark         for (i = plic->bitfield_words - 1; i >= 0; i--) {
781e24429eSMichael Clark             qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
791e24429eSMichael Clark         }
801e24429eSMichael Clark         qemu_log("\n");
811e24429eSMichael Clark     }
821e24429eSMichael Clark }
831e24429eSMichael Clark 
84*d78940ecSMichael Clark static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
851e24429eSMichael Clark {
86*d78940ecSMichael Clark     uint32_t old, new, cmp = atomic_read(a);
87*d78940ecSMichael Clark 
88*d78940ecSMichael Clark     do {
89*d78940ecSMichael Clark         old = cmp;
90*d78940ecSMichael Clark         new = (old & ~mask) | (value & mask);
91*d78940ecSMichael Clark         cmp = atomic_cmpxchg(a, old, new);
92*d78940ecSMichael Clark     } while (old != cmp);
93*d78940ecSMichael Clark 
94*d78940ecSMichael Clark     return old;
951e24429eSMichael Clark }
961e24429eSMichael Clark 
97*d78940ecSMichael Clark static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
981e24429eSMichael Clark {
99*d78940ecSMichael Clark     atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
1001e24429eSMichael Clark }
1011e24429eSMichael Clark 
102*d78940ecSMichael Clark static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
1031e24429eSMichael Clark {
104*d78940ecSMichael Clark     atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
105*d78940ecSMichael Clark }
106*d78940ecSMichael Clark 
107*d78940ecSMichael Clark static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
108*d78940ecSMichael Clark {
109*d78940ecSMichael Clark     int i, j;
1101e24429eSMichael Clark     for (i = 0; i < plic->bitfield_words; i++) {
1111e24429eSMichael Clark         uint32_t pending_enabled_not_claimed =
1121e24429eSMichael Clark             (plic->pending[i] & ~plic->claimed[i]) &
1131e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + i];
1141e24429eSMichael Clark         if (!pending_enabled_not_claimed) {
1151e24429eSMichael Clark             continue;
1161e24429eSMichael Clark         }
1171e24429eSMichael Clark         for (j = 0; j < 32; j++) {
1181e24429eSMichael Clark             int irq = (i << 5) + j;
1191e24429eSMichael Clark             uint32_t prio = plic->source_priority[irq];
1201e24429eSMichael Clark             int enabled = pending_enabled_not_claimed & (1 << j);
1211e24429eSMichael Clark             if (enabled && prio > plic->target_priority[addrid]) {
122*d78940ecSMichael Clark                 return 1;
1231e24429eSMichael Clark             }
1241e24429eSMichael Clark         }
1251e24429eSMichael Clark     }
126*d78940ecSMichael Clark     return 0;
1271e24429eSMichael Clark }
1281e24429eSMichael Clark 
1291e24429eSMichael Clark static void sifive_plic_update(SiFivePLICState *plic)
1301e24429eSMichael Clark {
1311e24429eSMichael Clark     int addrid;
1321e24429eSMichael Clark 
1331e24429eSMichael Clark     /* raise irq on harts where this irq is enabled */
1341e24429eSMichael Clark     for (addrid = 0; addrid < plic->num_addrs; addrid++) {
1351e24429eSMichael Clark         uint32_t hartid = plic->addr_config[addrid].hartid;
1361e24429eSMichael Clark         PLICMode mode = plic->addr_config[addrid].mode;
1371e24429eSMichael Clark         CPUState *cpu = qemu_get_cpu(hartid);
1381e24429eSMichael Clark         CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
1391e24429eSMichael Clark         if (!env) {
1401e24429eSMichael Clark             continue;
1411e24429eSMichael Clark         }
142*d78940ecSMichael Clark         int level = sifive_plic_irqs_pending(plic, addrid);
1431e24429eSMichael Clark         switch (mode) {
1441e24429eSMichael Clark         case PLICMode_M:
1451e24429eSMichael Clark             riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
1461e24429eSMichael Clark             break;
1471e24429eSMichael Clark         case PLICMode_S:
1481e24429eSMichael Clark             riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
1491e24429eSMichael Clark             break;
1501e24429eSMichael Clark         default:
1511e24429eSMichael Clark             break;
1521e24429eSMichael Clark         }
1531e24429eSMichael Clark     }
1541e24429eSMichael Clark 
1551e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
1561e24429eSMichael Clark         sifive_plic_print_state(plic);
1571e24429eSMichael Clark     }
1581e24429eSMichael Clark }
1591e24429eSMichael Clark 
1601e24429eSMichael Clark void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq)
1611e24429eSMichael Clark {
1621e24429eSMichael Clark     sifive_plic_set_pending(plic, irq, true);
1631e24429eSMichael Clark     sifive_plic_update(plic);
1641e24429eSMichael Clark }
1651e24429eSMichael Clark 
1661e24429eSMichael Clark void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq)
1671e24429eSMichael Clark {
1681e24429eSMichael Clark     sifive_plic_set_pending(plic, irq, false);
1691e24429eSMichael Clark     sifive_plic_update(plic);
1701e24429eSMichael Clark }
1711e24429eSMichael Clark 
1721e24429eSMichael Clark static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
1731e24429eSMichael Clark {
1741e24429eSMichael Clark     int i, j;
1751e24429eSMichael Clark     for (i = 0; i < plic->bitfield_words; i++) {
1761e24429eSMichael Clark         uint32_t pending_enabled_not_claimed =
1771e24429eSMichael Clark             (plic->pending[i] & ~plic->claimed[i]) &
1781e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + i];
1791e24429eSMichael Clark         if (!pending_enabled_not_claimed) {
1801e24429eSMichael Clark             continue;
1811e24429eSMichael Clark         }
1821e24429eSMichael Clark         for (j = 0; j < 32; j++) {
1831e24429eSMichael Clark             int irq = (i << 5) + j;
1841e24429eSMichael Clark             uint32_t prio = plic->source_priority[irq];
1851e24429eSMichael Clark             int enabled = pending_enabled_not_claimed & (1 << j);
1861e24429eSMichael Clark             if (enabled && prio > plic->target_priority[addrid]) {
1871e24429eSMichael Clark                 sifive_plic_set_pending(plic, irq, false);
1881e24429eSMichael Clark                 sifive_plic_set_claimed(plic, irq, true);
1891e24429eSMichael Clark                 return irq;
1901e24429eSMichael Clark             }
1911e24429eSMichael Clark         }
1921e24429eSMichael Clark     }
1931e24429eSMichael Clark     return 0;
1941e24429eSMichael Clark }
1951e24429eSMichael Clark 
1961e24429eSMichael Clark static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
1971e24429eSMichael Clark {
1981e24429eSMichael Clark     SiFivePLICState *plic = opaque;
1991e24429eSMichael Clark 
2001e24429eSMichael Clark     /* writes must be 4 byte words */
2011e24429eSMichael Clark     if ((addr & 0x3) != 0) {
2021e24429eSMichael Clark         goto err;
2031e24429eSMichael Clark     }
2041e24429eSMichael Clark 
2051e24429eSMichael Clark     if (addr >= plic->priority_base && /* 4 bytes per source */
2061e24429eSMichael Clark         addr < plic->priority_base + (plic->num_sources << 2))
2071e24429eSMichael Clark     {
2081e24429eSMichael Clark         uint32_t irq = (addr - plic->priority_base) >> 2;
2091e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2101e24429eSMichael Clark             qemu_log("plic: read priority: irq=%d priority=%d\n",
2111e24429eSMichael Clark                 irq, plic->source_priority[irq]);
2121e24429eSMichael Clark         }
2131e24429eSMichael Clark         return plic->source_priority[irq];
2141e24429eSMichael Clark     } else if (addr >= plic->pending_base && /* 1 bit per source */
2151e24429eSMichael Clark                addr < plic->pending_base + (plic->num_sources >> 3))
2161e24429eSMichael Clark     {
2171e24429eSMichael Clark         uint32_t word = (addr - plic->priority_base) >> 2;
2181e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2191e24429eSMichael Clark             qemu_log("plic: read pending: word=%d value=%d\n",
2201e24429eSMichael Clark                 word, plic->pending[word]);
2211e24429eSMichael Clark         }
2221e24429eSMichael Clark         return plic->pending[word];
2231e24429eSMichael Clark     } else if (addr >= plic->enable_base && /* 1 bit per source */
2241e24429eSMichael Clark              addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
2251e24429eSMichael Clark     {
2261e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
2271e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
2281e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
2291e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2301e24429eSMichael Clark                 qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
2311e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2321e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode), wordid,
2331e24429eSMichael Clark                     plic->enable[addrid * plic->bitfield_words + wordid]);
2341e24429eSMichael Clark             }
2351e24429eSMichael Clark             return plic->enable[addrid * plic->bitfield_words + wordid];
2361e24429eSMichael Clark         }
2371e24429eSMichael Clark     } else if (addr >= plic->context_base && /* 1 bit per source */
2381e24429eSMichael Clark              addr < plic->context_base + plic->num_addrs * plic->context_stride)
2391e24429eSMichael Clark     {
2401e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
2411e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
2421e24429eSMichael Clark         if (contextid == 0) {
2431e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2441e24429eSMichael Clark                 qemu_log("plic: read priority: hart%d-%c priority=%x\n",
2451e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2461e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
2471e24429eSMichael Clark                     plic->target_priority[addrid]);
2481e24429eSMichael Clark             }
2491e24429eSMichael Clark             return plic->target_priority[addrid];
2501e24429eSMichael Clark         } else if (contextid == 4) {
2511e24429eSMichael Clark             uint32_t value = sifive_plic_claim(plic, addrid);
2521e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2531e24429eSMichael Clark                 qemu_log("plic: read claim: hart%d-%c irq=%x\n",
2541e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2551e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
2561e24429eSMichael Clark                     value);
2571e24429eSMichael Clark                 sifive_plic_print_state(plic);
2581e24429eSMichael Clark             }
2591e24429eSMichael Clark             return value;
2601e24429eSMichael Clark         }
2611e24429eSMichael Clark     }
2621e24429eSMichael Clark 
2631e24429eSMichael Clark err:
2641e24429eSMichael Clark     error_report("plic: invalid register read: %08x", (uint32_t)addr);
2651e24429eSMichael Clark     return 0;
2661e24429eSMichael Clark }
2671e24429eSMichael Clark 
2681e24429eSMichael Clark static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
2691e24429eSMichael Clark         unsigned size)
2701e24429eSMichael Clark {
2711e24429eSMichael Clark     SiFivePLICState *plic = opaque;
2721e24429eSMichael Clark 
2731e24429eSMichael Clark     /* writes must be 4 byte words */
2741e24429eSMichael Clark     if ((addr & 0x3) != 0) {
2751e24429eSMichael Clark         goto err;
2761e24429eSMichael Clark     }
2771e24429eSMichael Clark 
2781e24429eSMichael Clark     if (addr >= plic->priority_base && /* 4 bytes per source */
2791e24429eSMichael Clark         addr < plic->priority_base + (plic->num_sources << 2))
2801e24429eSMichael Clark     {
2811e24429eSMichael Clark         uint32_t irq = (addr - plic->priority_base) >> 2;
2821e24429eSMichael Clark         plic->source_priority[irq] = value & 7;
2831e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2841e24429eSMichael Clark             qemu_log("plic: write priority: irq=%d priority=%d\n",
2851e24429eSMichael Clark                 irq, plic->source_priority[irq]);
2861e24429eSMichael Clark         }
2871e24429eSMichael Clark         return;
2881e24429eSMichael Clark     } else if (addr >= plic->pending_base && /* 1 bit per source */
2891e24429eSMichael Clark                addr < plic->pending_base + (plic->num_sources >> 3))
2901e24429eSMichael Clark     {
2911e24429eSMichael Clark         error_report("plic: invalid pending write: %08x", (uint32_t)addr);
2921e24429eSMichael Clark         return;
2931e24429eSMichael Clark     } else if (addr >= plic->enable_base && /* 1 bit per source */
2941e24429eSMichael Clark         addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
2951e24429eSMichael Clark     {
2961e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
2971e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
2981e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
2991e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + wordid] = value;
3001e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3011e24429eSMichael Clark                 qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
3021e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3031e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode), wordid,
3041e24429eSMichael Clark                     plic->enable[addrid * plic->bitfield_words + wordid]);
3051e24429eSMichael Clark             }
3061e24429eSMichael Clark             return;
3071e24429eSMichael Clark         }
3081e24429eSMichael Clark     } else if (addr >= plic->context_base && /* 4 bytes per reg */
3091e24429eSMichael Clark         addr < plic->context_base + plic->num_addrs * plic->context_stride)
3101e24429eSMichael Clark     {
3111e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
3121e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
3131e24429eSMichael Clark         if (contextid == 0) {
3141e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3151e24429eSMichael Clark                 qemu_log("plic: write priority: hart%d-%c priority=%x\n",
3161e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3171e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
3181e24429eSMichael Clark                     plic->target_priority[addrid]);
3191e24429eSMichael Clark             }
3201e24429eSMichael Clark             if (value <= plic->num_priorities) {
3211e24429eSMichael Clark                 plic->target_priority[addrid] = value;
3221e24429eSMichael Clark                 sifive_plic_update(plic);
3231e24429eSMichael Clark             }
3241e24429eSMichael Clark             return;
3251e24429eSMichael Clark         } else if (contextid == 4) {
3261e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3271e24429eSMichael Clark                 qemu_log("plic: write claim: hart%d-%c irq=%x\n",
3281e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3291e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
3301e24429eSMichael Clark                     (uint32_t)value);
3311e24429eSMichael Clark             }
3321e24429eSMichael Clark             if (value < plic->num_sources) {
3331e24429eSMichael Clark                 sifive_plic_set_claimed(plic, value, false);
3341e24429eSMichael Clark                 sifive_plic_update(plic);
3351e24429eSMichael Clark             }
3361e24429eSMichael Clark             return;
3371e24429eSMichael Clark         }
3381e24429eSMichael Clark     }
3391e24429eSMichael Clark 
3401e24429eSMichael Clark err:
3411e24429eSMichael Clark     error_report("plic: invalid register write: %08x", (uint32_t)addr);
3421e24429eSMichael Clark }
3431e24429eSMichael Clark 
3441e24429eSMichael Clark static const MemoryRegionOps sifive_plic_ops = {
3451e24429eSMichael Clark     .read = sifive_plic_read,
3461e24429eSMichael Clark     .write = sifive_plic_write,
3471e24429eSMichael Clark     .endianness = DEVICE_LITTLE_ENDIAN,
3481e24429eSMichael Clark     .valid = {
3491e24429eSMichael Clark         .min_access_size = 4,
3501e24429eSMichael Clark         .max_access_size = 4
3511e24429eSMichael Clark     }
3521e24429eSMichael Clark };
3531e24429eSMichael Clark 
3541e24429eSMichael Clark static Property sifive_plic_properties[] = {
3551e24429eSMichael Clark     DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
3561e24429eSMichael Clark     DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
3571e24429eSMichael Clark     DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
3581e24429eSMichael Clark     DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
3591e24429eSMichael Clark     DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
3601e24429eSMichael Clark     DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
3611e24429eSMichael Clark     DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
3621e24429eSMichael Clark     DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
3631e24429eSMichael Clark     DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
3641e24429eSMichael Clark     DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
3651e24429eSMichael Clark     DEFINE_PROP_END_OF_LIST(),
3661e24429eSMichael Clark };
3671e24429eSMichael Clark 
3681e24429eSMichael Clark /*
3691e24429eSMichael Clark  * parse PLIC hart/mode address offset config
3701e24429eSMichael Clark  *
3711e24429eSMichael Clark  * "M"              1 hart with M mode
3721e24429eSMichael Clark  * "MS,MS"          2 harts, 0-1 with M and S mode
3731e24429eSMichael Clark  * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
3741e24429eSMichael Clark  */
3751e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic)
3761e24429eSMichael Clark {
3771e24429eSMichael Clark     int addrid, hartid, modes;
3781e24429eSMichael Clark     const char *p;
3791e24429eSMichael Clark     char c;
3801e24429eSMichael Clark 
3811e24429eSMichael Clark     /* count and validate hart/mode combinations */
3821e24429eSMichael Clark     addrid = 0, hartid = 0, modes = 0;
3831e24429eSMichael Clark     p = plic->hart_config;
3841e24429eSMichael Clark     while ((c = *p++)) {
3851e24429eSMichael Clark         if (c == ',') {
3861e24429eSMichael Clark             addrid += __builtin_popcount(modes);
3871e24429eSMichael Clark             modes = 0;
3881e24429eSMichael Clark             hartid++;
3891e24429eSMichael Clark         } else {
3901e24429eSMichael Clark             int m = 1 << char_to_mode(c);
3911e24429eSMichael Clark             if (modes == (modes | m)) {
3921e24429eSMichael Clark                 error_report("plic: duplicate mode '%c' in config: %s",
3931e24429eSMichael Clark                              c, plic->hart_config);
3941e24429eSMichael Clark                 exit(1);
3951e24429eSMichael Clark             }
3961e24429eSMichael Clark             modes |= m;
3971e24429eSMichael Clark         }
3981e24429eSMichael Clark     }
3991e24429eSMichael Clark     if (modes) {
4001e24429eSMichael Clark         addrid += __builtin_popcount(modes);
4011e24429eSMichael Clark     }
4021e24429eSMichael Clark     hartid++;
4031e24429eSMichael Clark 
4041e24429eSMichael Clark     /* store hart/mode combinations */
4051e24429eSMichael Clark     plic->num_addrs = addrid;
4061e24429eSMichael Clark     plic->addr_config = g_new(PLICAddr, plic->num_addrs);
4071e24429eSMichael Clark     addrid = 0, hartid = 0;
4081e24429eSMichael Clark     p = plic->hart_config;
4091e24429eSMichael Clark     while ((c = *p++)) {
4101e24429eSMichael Clark         if (c == ',') {
4111e24429eSMichael Clark             hartid++;
4121e24429eSMichael Clark         } else {
4131e24429eSMichael Clark             plic->addr_config[addrid].addrid = addrid;
4141e24429eSMichael Clark             plic->addr_config[addrid].hartid = hartid;
4151e24429eSMichael Clark             plic->addr_config[addrid].mode = char_to_mode(c);
4161e24429eSMichael Clark             addrid++;
4171e24429eSMichael Clark         }
4181e24429eSMichael Clark     }
4191e24429eSMichael Clark }
4201e24429eSMichael Clark 
4211e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level)
4221e24429eSMichael Clark {
4231e24429eSMichael Clark     SiFivePLICState *plic = opaque;
4241e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
4251e24429eSMichael Clark         qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
4261e24429eSMichael Clark     }
4271e24429eSMichael Clark     sifive_plic_set_pending(plic, irq, level > 0);
4281e24429eSMichael Clark     sifive_plic_update(plic);
4291e24429eSMichael Clark }
4301e24429eSMichael Clark 
4311e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp)
4321e24429eSMichael Clark {
4331e24429eSMichael Clark     SiFivePLICState *plic = SIFIVE_PLIC(dev);
4341e24429eSMichael Clark 
4351e24429eSMichael Clark     memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
4361e24429eSMichael Clark                           TYPE_SIFIVE_PLIC, plic->aperture_size);
4371e24429eSMichael Clark     parse_hart_config(plic);
4381e24429eSMichael Clark     plic->bitfield_words = (plic->num_sources + 31) >> 5;
4391e24429eSMichael Clark     plic->source_priority = g_new0(uint32_t, plic->num_sources);
4401e24429eSMichael Clark     plic->target_priority = g_new(uint32_t, plic->num_addrs);
4411e24429eSMichael Clark     plic->pending = g_new0(uint32_t, plic->bitfield_words);
4421e24429eSMichael Clark     plic->claimed = g_new0(uint32_t, plic->bitfield_words);
4431e24429eSMichael Clark     plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
4441e24429eSMichael Clark     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
445647a70a1SAlistair Francis     qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
4461e24429eSMichael Clark }
4471e24429eSMichael Clark 
4481e24429eSMichael Clark static void sifive_plic_class_init(ObjectClass *klass, void *data)
4491e24429eSMichael Clark {
4501e24429eSMichael Clark     DeviceClass *dc = DEVICE_CLASS(klass);
4511e24429eSMichael Clark 
4521e24429eSMichael Clark     dc->props = sifive_plic_properties;
4531e24429eSMichael Clark     dc->realize = sifive_plic_realize;
4541e24429eSMichael Clark }
4551e24429eSMichael Clark 
4561e24429eSMichael Clark static const TypeInfo sifive_plic_info = {
4571e24429eSMichael Clark     .name          = TYPE_SIFIVE_PLIC,
4581e24429eSMichael Clark     .parent        = TYPE_SYS_BUS_DEVICE,
4591e24429eSMichael Clark     .instance_size = sizeof(SiFivePLICState),
4601e24429eSMichael Clark     .class_init    = sifive_plic_class_init,
4611e24429eSMichael Clark };
4621e24429eSMichael Clark 
4631e24429eSMichael Clark static void sifive_plic_register_types(void)
4641e24429eSMichael Clark {
4651e24429eSMichael Clark     type_register_static(&sifive_plic_info);
4661e24429eSMichael Clark }
4671e24429eSMichael Clark 
4681e24429eSMichael Clark type_init(sifive_plic_register_types)
4691e24429eSMichael Clark 
4701e24429eSMichael Clark /*
4711e24429eSMichael Clark  * Create PLIC device.
4721e24429eSMichael Clark  */
4731e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
4741e24429eSMichael Clark     uint32_t num_sources, uint32_t num_priorities,
4751e24429eSMichael Clark     uint32_t priority_base, uint32_t pending_base,
4761e24429eSMichael Clark     uint32_t enable_base, uint32_t enable_stride,
4771e24429eSMichael Clark     uint32_t context_base, uint32_t context_stride,
4781e24429eSMichael Clark     uint32_t aperture_size)
4791e24429eSMichael Clark {
4801e24429eSMichael Clark     DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC);
4811e24429eSMichael Clark     assert(enable_stride == (enable_stride & -enable_stride));
4821e24429eSMichael Clark     assert(context_stride == (context_stride & -context_stride));
4831e24429eSMichael Clark     qdev_prop_set_string(dev, "hart-config", hart_config);
4841e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-sources", num_sources);
4851e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
4861e24429eSMichael Clark     qdev_prop_set_uint32(dev, "priority-base", priority_base);
4871e24429eSMichael Clark     qdev_prop_set_uint32(dev, "pending-base", pending_base);
4881e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-base", enable_base);
4891e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
4901e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-base", context_base);
4911e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-stride", context_stride);
4921e24429eSMichael Clark     qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
4931e24429eSMichael Clark     qdev_init_nofail(dev);
4941e24429eSMichael Clark     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
4951e24429eSMichael Clark     return dev;
4961e24429eSMichael Clark }
497