xref: /qemu/hw/intc/sifive_plic.c (revision d680ff664e1c7e097922f62fef824027b8fb711a)
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"
28a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2984fcf3c1SBin Meng #include "hw/intc/sifive_plic.h"
301e24429eSMichael Clark #include "target/riscv/cpu.h"
31dbd3ec54SYifei Jiang #include "migration/vmstate.h"
32f436ecc3SAlistair Francis #include "hw/irq.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 {
92d73415a3SStefan Hajnoczi     uint32_t old, new, cmp = qatomic_read(a);
93d78940ecSMichael Clark 
94d78940ecSMichael Clark     do {
95d78940ecSMichael Clark         old = cmp;
96d78940ecSMichael Clark         new = (old & ~mask) | (value & mask);
97d73415a3SStefan Hajnoczi         cmp = qatomic_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;
143d78940ecSMichael Clark         int level = sifive_plic_irqs_pending(plic, addrid);
144f436ecc3SAlistair Francis 
1451e24429eSMichael Clark         switch (mode) {
1461e24429eSMichael Clark         case PLICMode_M:
147f436ecc3SAlistair Francis             qemu_set_irq(plic->m_external_irqs[hartid - plic->hartid_base], level);
1481e24429eSMichael Clark             break;
1491e24429eSMichael Clark         case PLICMode_S:
150f436ecc3SAlistair Francis             qemu_set_irq(plic->s_external_irqs[hartid - plic->hartid_base], level);
1511e24429eSMichael Clark             break;
1521e24429eSMichael Clark         default:
1531e24429eSMichael Clark             break;
1541e24429eSMichael Clark         }
1551e24429eSMichael Clark     }
1561e24429eSMichael Clark 
1571e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
1581e24429eSMichael Clark         sifive_plic_print_state(plic);
1591e24429eSMichael Clark     }
1601e24429eSMichael Clark }
1611e24429eSMichael Clark 
1621e24429eSMichael Clark static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
1631e24429eSMichael Clark {
1641e24429eSMichael Clark     int i, j;
165aa4d30f6SJessica Clarke     uint32_t max_irq = 0;
166aa4d30f6SJessica Clarke     uint32_t max_prio = plic->target_priority[addrid];
167aa4d30f6SJessica Clarke 
1681e24429eSMichael Clark     for (i = 0; i < plic->bitfield_words; i++) {
1691e24429eSMichael Clark         uint32_t pending_enabled_not_claimed =
1701e24429eSMichael Clark             (plic->pending[i] & ~plic->claimed[i]) &
1711e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + i];
1721e24429eSMichael Clark         if (!pending_enabled_not_claimed) {
1731e24429eSMichael Clark             continue;
1741e24429eSMichael Clark         }
1751e24429eSMichael Clark         for (j = 0; j < 32; j++) {
1761e24429eSMichael Clark             int irq = (i << 5) + j;
1771e24429eSMichael Clark             uint32_t prio = plic->source_priority[irq];
1781e24429eSMichael Clark             int enabled = pending_enabled_not_claimed & (1 << j);
179aa4d30f6SJessica Clarke             if (enabled && prio > max_prio) {
180aa4d30f6SJessica Clarke                 max_irq = irq;
181aa4d30f6SJessica Clarke                 max_prio = prio;
1821e24429eSMichael Clark             }
1831e24429eSMichael Clark         }
1841e24429eSMichael Clark     }
185aa4d30f6SJessica Clarke 
186aa4d30f6SJessica Clarke     if (max_irq) {
187aa4d30f6SJessica Clarke         sifive_plic_set_pending(plic, max_irq, false);
188aa4d30f6SJessica Clarke         sifive_plic_set_claimed(plic, max_irq, true);
189aa4d30f6SJessica Clarke     }
190aa4d30f6SJessica Clarke     return max_irq;
1911e24429eSMichael Clark }
1921e24429eSMichael Clark 
1931e24429eSMichael Clark static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
1941e24429eSMichael Clark {
1951e24429eSMichael Clark     SiFivePLICState *plic = opaque;
1961e24429eSMichael Clark 
1971e24429eSMichael Clark     /* writes must be 4 byte words */
1981e24429eSMichael Clark     if ((addr & 0x3) != 0) {
1991e24429eSMichael Clark         goto err;
2001e24429eSMichael Clark     }
2011e24429eSMichael Clark 
2021e24429eSMichael Clark     if (addr >= plic->priority_base && /* 4 bytes per source */
2031e24429eSMichael Clark         addr < plic->priority_base + (plic->num_sources << 2))
2041e24429eSMichael Clark     {
2050feb4a71SAlistair Francis         uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
2061e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2071e24429eSMichael Clark             qemu_log("plic: read priority: irq=%d priority=%d\n",
2081e24429eSMichael Clark                 irq, plic->source_priority[irq]);
2091e24429eSMichael Clark         }
2101e24429eSMichael Clark         return plic->source_priority[irq];
2111e24429eSMichael Clark     } else if (addr >= plic->pending_base && /* 1 bit per source */
2121e24429eSMichael Clark                addr < plic->pending_base + (plic->num_sources >> 3))
2131e24429eSMichael Clark     {
214e41848e5SMichael Clark         uint32_t word = (addr - plic->pending_base) >> 2;
2151e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2161e24429eSMichael Clark             qemu_log("plic: read pending: word=%d value=%d\n",
2171e24429eSMichael Clark                 word, plic->pending[word]);
2181e24429eSMichael Clark         }
2191e24429eSMichael Clark         return plic->pending[word];
2201e24429eSMichael Clark     } else if (addr >= plic->enable_base && /* 1 bit per source */
2211e24429eSMichael Clark              addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
2221e24429eSMichael Clark     {
2231e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
2241e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
2251e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
2261e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2271e24429eSMichael Clark                 qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
2281e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2291e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode), wordid,
2301e24429eSMichael Clark                     plic->enable[addrid * plic->bitfield_words + wordid]);
2311e24429eSMichael Clark             }
2321e24429eSMichael Clark             return plic->enable[addrid * plic->bitfield_words + wordid];
2331e24429eSMichael Clark         }
2341e24429eSMichael Clark     } else if (addr >= plic->context_base && /* 1 bit per source */
2351e24429eSMichael Clark              addr < plic->context_base + plic->num_addrs * plic->context_stride)
2361e24429eSMichael Clark     {
2371e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
2381e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
2391e24429eSMichael Clark         if (contextid == 0) {
2401e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2411e24429eSMichael Clark                 qemu_log("plic: read priority: hart%d-%c priority=%x\n",
2421e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2431e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
2441e24429eSMichael Clark                     plic->target_priority[addrid]);
2451e24429eSMichael Clark             }
2461e24429eSMichael Clark             return plic->target_priority[addrid];
2471e24429eSMichael Clark         } else if (contextid == 4) {
2481e24429eSMichael Clark             uint32_t value = sifive_plic_claim(plic, addrid);
2491e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
2501e24429eSMichael Clark                 qemu_log("plic: read claim: hart%d-%c irq=%x\n",
2511e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
2521e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
2531e24429eSMichael Clark                     value);
2541e24429eSMichael Clark             }
25555765822SJessica Clarke             sifive_plic_update(plic);
2561e24429eSMichael Clark             return value;
2571e24429eSMichael Clark         }
2581e24429eSMichael Clark     }
2591e24429eSMichael Clark 
2601e24429eSMichael Clark err:
26179bcac25SAlistair Francis     qemu_log_mask(LOG_GUEST_ERROR,
26279bcac25SAlistair Francis                   "%s: Invalid register read 0x%" HWADDR_PRIx "\n",
26379bcac25SAlistair Francis                   __func__, addr);
2641e24429eSMichael Clark     return 0;
2651e24429eSMichael Clark }
2661e24429eSMichael Clark 
2671e24429eSMichael Clark static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
2681e24429eSMichael Clark         unsigned size)
2691e24429eSMichael Clark {
2701e24429eSMichael Clark     SiFivePLICState *plic = opaque;
2711e24429eSMichael Clark 
2721e24429eSMichael Clark     /* writes must be 4 byte words */
2731e24429eSMichael Clark     if ((addr & 0x3) != 0) {
2741e24429eSMichael Clark         goto err;
2751e24429eSMichael Clark     }
2761e24429eSMichael Clark 
2771e24429eSMichael Clark     if (addr >= plic->priority_base && /* 4 bytes per source */
2781e24429eSMichael Clark         addr < plic->priority_base + (plic->num_sources << 2))
2791e24429eSMichael Clark     {
2800feb4a71SAlistair Francis         uint32_t irq = ((addr - plic->priority_base) >> 2) + 1;
2811e24429eSMichael Clark         plic->source_priority[irq] = value & 7;
2821e24429eSMichael Clark         if (RISCV_DEBUG_PLIC) {
2831e24429eSMichael Clark             qemu_log("plic: write priority: irq=%d priority=%d\n",
2841e24429eSMichael Clark                 irq, plic->source_priority[irq]);
2851e24429eSMichael Clark         }
28655765822SJessica Clarke         sifive_plic_update(plic);
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     {
29179bcac25SAlistair Francis         qemu_log_mask(LOG_GUEST_ERROR,
29279bcac25SAlistair Francis                       "%s: invalid pending write: 0x%" HWADDR_PRIx "",
29379bcac25SAlistair Francis                       __func__, addr);
2941e24429eSMichael Clark         return;
2951e24429eSMichael Clark     } else if (addr >= plic->enable_base && /* 1 bit per source */
2961e24429eSMichael Clark         addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
2971e24429eSMichael Clark     {
2981e24429eSMichael Clark         uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
2991e24429eSMichael Clark         uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
3001e24429eSMichael Clark         if (wordid < plic->bitfield_words) {
3011e24429eSMichael Clark             plic->enable[addrid * plic->bitfield_words + wordid] = value;
3021e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3031e24429eSMichael Clark                 qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
3041e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3051e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode), wordid,
3061e24429eSMichael Clark                     plic->enable[addrid * plic->bitfield_words + wordid]);
3071e24429eSMichael Clark             }
3081e24429eSMichael Clark             return;
3091e24429eSMichael Clark         }
3101e24429eSMichael Clark     } else if (addr >= plic->context_base && /* 4 bytes per reg */
3111e24429eSMichael Clark         addr < plic->context_base + plic->num_addrs * plic->context_stride)
3121e24429eSMichael Clark     {
3131e24429eSMichael Clark         uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
3141e24429eSMichael Clark         uint32_t contextid = (addr & (plic->context_stride - 1));
3151e24429eSMichael Clark         if (contextid == 0) {
3161e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3171e24429eSMichael Clark                 qemu_log("plic: write priority: hart%d-%c priority=%x\n",
3181e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3191e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
3201e24429eSMichael Clark                     plic->target_priority[addrid]);
3211e24429eSMichael Clark             }
3221e24429eSMichael Clark             if (value <= plic->num_priorities) {
3231e24429eSMichael Clark                 plic->target_priority[addrid] = value;
3241e24429eSMichael Clark                 sifive_plic_update(plic);
3251e24429eSMichael Clark             }
3261e24429eSMichael Clark             return;
3271e24429eSMichael Clark         } else if (contextid == 4) {
3281e24429eSMichael Clark             if (RISCV_DEBUG_PLIC) {
3291e24429eSMichael Clark                 qemu_log("plic: write claim: hart%d-%c irq=%x\n",
3301e24429eSMichael Clark                     plic->addr_config[addrid].hartid,
3311e24429eSMichael Clark                     mode_to_char(plic->addr_config[addrid].mode),
3321e24429eSMichael Clark                     (uint32_t)value);
3331e24429eSMichael Clark             }
3341e24429eSMichael Clark             if (value < plic->num_sources) {
3351e24429eSMichael Clark                 sifive_plic_set_claimed(plic, value, false);
3361e24429eSMichael Clark                 sifive_plic_update(plic);
3371e24429eSMichael Clark             }
3381e24429eSMichael Clark             return;
3391e24429eSMichael Clark         }
3401e24429eSMichael Clark     }
3411e24429eSMichael Clark 
3421e24429eSMichael Clark err:
34379bcac25SAlistair Francis     qemu_log_mask(LOG_GUEST_ERROR,
34479bcac25SAlistair Francis                   "%s: Invalid register write 0x%" HWADDR_PRIx "\n",
34579bcac25SAlistair Francis                   __func__, addr);
3461e24429eSMichael Clark }
3471e24429eSMichael Clark 
3481e24429eSMichael Clark static const MemoryRegionOps sifive_plic_ops = {
3491e24429eSMichael Clark     .read = sifive_plic_read,
3501e24429eSMichael Clark     .write = sifive_plic_write,
3511e24429eSMichael Clark     .endianness = DEVICE_LITTLE_ENDIAN,
3521e24429eSMichael Clark     .valid = {
3531e24429eSMichael Clark         .min_access_size = 4,
3541e24429eSMichael Clark         .max_access_size = 4
3551e24429eSMichael Clark     }
3561e24429eSMichael Clark };
3571e24429eSMichael Clark 
3581e24429eSMichael Clark /*
3591e24429eSMichael Clark  * parse PLIC hart/mode address offset config
3601e24429eSMichael Clark  *
3611e24429eSMichael Clark  * "M"              1 hart with M mode
3621e24429eSMichael Clark  * "MS,MS"          2 harts, 0-1 with M and S mode
3631e24429eSMichael Clark  * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
3641e24429eSMichael Clark  */
3651e24429eSMichael Clark static void parse_hart_config(SiFivePLICState *plic)
3661e24429eSMichael Clark {
3671e24429eSMichael Clark     int addrid, hartid, modes;
3681e24429eSMichael Clark     const char *p;
3691e24429eSMichael Clark     char c;
3701e24429eSMichael Clark 
3711e24429eSMichael Clark     /* count and validate hart/mode combinations */
3721e24429eSMichael Clark     addrid = 0, hartid = 0, modes = 0;
3731e24429eSMichael Clark     p = plic->hart_config;
3741e24429eSMichael Clark     while ((c = *p++)) {
3751e24429eSMichael Clark         if (c == ',') {
376244df421SMichael Clark             addrid += ctpop8(modes);
3771e24429eSMichael Clark             modes = 0;
3781e24429eSMichael Clark             hartid++;
3791e24429eSMichael Clark         } else {
3801e24429eSMichael Clark             int m = 1 << char_to_mode(c);
3811e24429eSMichael Clark             if (modes == (modes | m)) {
3821e24429eSMichael Clark                 error_report("plic: duplicate mode '%c' in config: %s",
3831e24429eSMichael Clark                              c, plic->hart_config);
3841e24429eSMichael Clark                 exit(1);
3851e24429eSMichael Clark             }
3861e24429eSMichael Clark             modes |= m;
3871e24429eSMichael Clark         }
3881e24429eSMichael Clark     }
3891e24429eSMichael Clark     if (modes) {
390244df421SMichael Clark         addrid += ctpop8(modes);
3911e24429eSMichael Clark     }
3921e24429eSMichael Clark     hartid++;
3931e24429eSMichael Clark 
3941e24429eSMichael Clark     plic->num_addrs = addrid;
395c9270e10SAnup Patel     plic->num_harts = hartid;
396c9270e10SAnup Patel 
397c9270e10SAnup Patel     /* store hart/mode combinations */
3981e24429eSMichael Clark     plic->addr_config = g_new(PLICAddr, plic->num_addrs);
399c9270e10SAnup Patel     addrid = 0, hartid = plic->hartid_base;
4001e24429eSMichael Clark     p = plic->hart_config;
4011e24429eSMichael Clark     while ((c = *p++)) {
4021e24429eSMichael Clark         if (c == ',') {
4031e24429eSMichael Clark             hartid++;
4041e24429eSMichael Clark         } else {
4051e24429eSMichael Clark             plic->addr_config[addrid].addrid = addrid;
4061e24429eSMichael Clark             plic->addr_config[addrid].hartid = hartid;
4071e24429eSMichael Clark             plic->addr_config[addrid].mode = char_to_mode(c);
4081e24429eSMichael Clark             addrid++;
4091e24429eSMichael Clark         }
4101e24429eSMichael Clark     }
4111e24429eSMichael Clark }
4121e24429eSMichael Clark 
4131e24429eSMichael Clark static void sifive_plic_irq_request(void *opaque, int irq, int level)
4141e24429eSMichael Clark {
4151e24429eSMichael Clark     SiFivePLICState *plic = opaque;
4161e24429eSMichael Clark     if (RISCV_DEBUG_PLIC) {
4171e24429eSMichael Clark         qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
4181e24429eSMichael Clark     }
4191e24429eSMichael Clark     sifive_plic_set_pending(plic, irq, level > 0);
4201e24429eSMichael Clark     sifive_plic_update(plic);
4211e24429eSMichael Clark }
4221e24429eSMichael Clark 
4231e24429eSMichael Clark static void sifive_plic_realize(DeviceState *dev, Error **errp)
4241e24429eSMichael Clark {
425*d680ff66SAlistair Francis     SiFivePLICState *s = SIFIVE_PLIC(dev);
426e3e7039cSMichael Clark     int i;
4271e24429eSMichael Clark 
428*d680ff66SAlistair Francis     memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_plic_ops, s,
429*d680ff66SAlistair Francis                           TYPE_SIFIVE_PLIC, s->aperture_size);
430*d680ff66SAlistair Francis     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
431e3e7039cSMichael Clark 
432*d680ff66SAlistair Francis     parse_hart_config(s);
433f436ecc3SAlistair Francis 
434*d680ff66SAlistair Francis     s->bitfield_words = (s->num_sources + 31) >> 5;
435*d680ff66SAlistair Francis     s->num_enables = s->bitfield_words * s->num_addrs;
436*d680ff66SAlistair Francis     s->source_priority = g_new0(uint32_t, s->num_sources);
437*d680ff66SAlistair Francis     s->target_priority = g_new(uint32_t, s->num_addrs);
438*d680ff66SAlistair Francis     s->pending = g_new0(uint32_t, s->bitfield_words);
439*d680ff66SAlistair Francis     s->claimed = g_new0(uint32_t, s->bitfield_words);
440*d680ff66SAlistair Francis     s->enable = g_new0(uint32_t, s->num_enables);
441*d680ff66SAlistair Francis 
442*d680ff66SAlistair Francis     qdev_init_gpio_in(dev, sifive_plic_irq_request, s->num_sources);
443*d680ff66SAlistair Francis 
444*d680ff66SAlistair Francis     s->s_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
445*d680ff66SAlistair Francis     qdev_init_gpio_out(dev, s->s_external_irqs, s->num_harts);
446*d680ff66SAlistair Francis 
447*d680ff66SAlistair Francis     s->m_external_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
448*d680ff66SAlistair Francis     qdev_init_gpio_out(dev, s->m_external_irqs, s->num_harts);
449f436ecc3SAlistair Francis 
450e3e7039cSMichael Clark     /* We can't allow the supervisor to control SEIP as this would allow the
451e3e7039cSMichael Clark      * supervisor to clear a pending external interrupt which will result in
452e3e7039cSMichael Clark      * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
453e3e7039cSMichael Clark      * hardware controlled when a PLIC is attached.
454e3e7039cSMichael Clark      */
455*d680ff66SAlistair Francis     for (i = 0; i < s->num_harts; i++) {
456*d680ff66SAlistair Francis         RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(s->hartid_base + i));
457e3e7039cSMichael Clark         if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
458e3e7039cSMichael Clark             error_report("SEIP already claimed");
459e3e7039cSMichael Clark             exit(1);
460e3e7039cSMichael Clark         }
461e3e7039cSMichael Clark     }
46284bdc58cSPeter Maydell 
4634f5604c4SAlistair Francis     msi_nonbroken = true;
4641e24429eSMichael Clark }
4651e24429eSMichael Clark 
466dbd3ec54SYifei Jiang static const VMStateDescription vmstate_sifive_plic = {
467dbd3ec54SYifei Jiang     .name = "riscv_sifive_plic",
468dbd3ec54SYifei Jiang     .version_id = 1,
469dbd3ec54SYifei Jiang     .minimum_version_id = 1,
470dbd3ec54SYifei Jiang     .fields = (VMStateField[]) {
471dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(source_priority, SiFivePLICState,
472dbd3ec54SYifei Jiang                                   num_sources, 0,
473dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
474dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(target_priority, SiFivePLICState,
475dbd3ec54SYifei Jiang                                   num_addrs, 0,
476dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
477dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(pending, SiFivePLICState, bitfield_words, 0,
478dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
479dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(claimed, SiFivePLICState, bitfield_words, 0,
480dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
481dbd3ec54SYifei Jiang             VMSTATE_VARRAY_UINT32(enable, SiFivePLICState, num_enables, 0,
482dbd3ec54SYifei Jiang                                   vmstate_info_uint32, uint32_t),
483dbd3ec54SYifei Jiang             VMSTATE_END_OF_LIST()
484dbd3ec54SYifei Jiang         }
485dbd3ec54SYifei Jiang };
486dbd3ec54SYifei Jiang 
487d8c6590fSAlistair Francis static Property sifive_plic_properties[] = {
488d8c6590fSAlistair Francis     DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
489d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0),
490d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
491d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
492d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
493d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
494d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
495d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
496d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
497d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
498d8c6590fSAlistair Francis     DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
499d8c6590fSAlistair Francis     DEFINE_PROP_END_OF_LIST(),
500d8c6590fSAlistair Francis };
501d8c6590fSAlistair Francis 
5021e24429eSMichael Clark static void sifive_plic_class_init(ObjectClass *klass, void *data)
5031e24429eSMichael Clark {
5041e24429eSMichael Clark     DeviceClass *dc = DEVICE_CLASS(klass);
5051e24429eSMichael Clark 
5064f67d30bSMarc-André Lureau     device_class_set_props(dc, sifive_plic_properties);
5071e24429eSMichael Clark     dc->realize = sifive_plic_realize;
508dbd3ec54SYifei Jiang     dc->vmsd = &vmstate_sifive_plic;
5091e24429eSMichael Clark }
5101e24429eSMichael Clark 
5111e24429eSMichael Clark static const TypeInfo sifive_plic_info = {
5121e24429eSMichael Clark     .name          = TYPE_SIFIVE_PLIC,
5131e24429eSMichael Clark     .parent        = TYPE_SYS_BUS_DEVICE,
5141e24429eSMichael Clark     .instance_size = sizeof(SiFivePLICState),
5151e24429eSMichael Clark     .class_init    = sifive_plic_class_init,
5161e24429eSMichael Clark };
5171e24429eSMichael Clark 
5181e24429eSMichael Clark static void sifive_plic_register_types(void)
5191e24429eSMichael Clark {
5201e24429eSMichael Clark     type_register_static(&sifive_plic_info);
5211e24429eSMichael Clark }
5221e24429eSMichael Clark 
5231e24429eSMichael Clark type_init(sifive_plic_register_types)
5241e24429eSMichael Clark 
5251e24429eSMichael Clark /*
5261e24429eSMichael Clark  * Create PLIC device.
5271e24429eSMichael Clark  */
5281e24429eSMichael Clark DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
529f436ecc3SAlistair Francis     uint32_t num_harts,
530c9270e10SAnup Patel     uint32_t hartid_base, uint32_t num_sources,
531c9270e10SAnup Patel     uint32_t num_priorities, uint32_t priority_base,
532c9270e10SAnup Patel     uint32_t pending_base, uint32_t enable_base,
533c9270e10SAnup Patel     uint32_t enable_stride, uint32_t context_base,
534c9270e10SAnup Patel     uint32_t context_stride, uint32_t aperture_size)
5351e24429eSMichael Clark {
5363e80f690SMarkus Armbruster     DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
537f436ecc3SAlistair Francis     int i;
538f436ecc3SAlistair Francis 
5391e24429eSMichael Clark     assert(enable_stride == (enable_stride & -enable_stride));
5401e24429eSMichael Clark     assert(context_stride == (context_stride & -context_stride));
5411e24429eSMichael Clark     qdev_prop_set_string(dev, "hart-config", hart_config);
542c9270e10SAnup Patel     qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
5431e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-sources", num_sources);
5441e24429eSMichael Clark     qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
5451e24429eSMichael Clark     qdev_prop_set_uint32(dev, "priority-base", priority_base);
5461e24429eSMichael Clark     qdev_prop_set_uint32(dev, "pending-base", pending_base);
5471e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-base", enable_base);
5481e24429eSMichael Clark     qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
5491e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-base", context_base);
5501e24429eSMichael Clark     qdev_prop_set_uint32(dev, "context-stride", context_stride);
5511e24429eSMichael Clark     qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
5523c6ef471SMarkus Armbruster     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
5531e24429eSMichael Clark     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
554f436ecc3SAlistair Francis 
555f436ecc3SAlistair Francis     for (i = 0; i < num_harts; i++) {
556f436ecc3SAlistair Francis         CPUState *cpu = qemu_get_cpu(hartid_base + i);
557f436ecc3SAlistair Francis 
558f436ecc3SAlistair Francis         qdev_connect_gpio_out(dev, i,
559f436ecc3SAlistair Francis                               qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
560f436ecc3SAlistair Francis         qdev_connect_gpio_out(dev, num_harts + i,
561f436ecc3SAlistair Francis                               qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
562f436ecc3SAlistair Francis     }
563f436ecc3SAlistair Francis 
5641e24429eSMichael Clark     return dev;
5651e24429eSMichael Clark }
566