1/* $NetBSD: plic.c,v 1.5 2024/03/24 08:34:20 skrll Exp $ */ 2 3/*- 4 * Copyright (c) 2022 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Portions of this code is derived from software contributed to The NetBSD 8 * Foundation by Simon Burge. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "opt_multiprocessor.h" 33 34#include <sys/cdefs.h> 35__KERNEL_RCSID(0, "$NetBSD: plic.c,v 1.5 2024/03/24 08:34:20 skrll Exp $"); 36 37#include <sys/param.h> 38 39#include <sys/bus.h> 40#include <sys/cpu.h> 41#include <sys/kmem.h> 42 43#include <riscv/sysreg.h> 44#include <riscv/dev/plicreg.h> 45#include <riscv/dev/plicvar.h> 46 47#define PLIC_PRIORITY(irq) (PLIC_PRIORITY_BASE + (irq) * 4) 48 49#define PLIC_ENABLE(sc, h, irq) (PLIC_ENABLE_BASE + \ 50 sc->sc_context[(h)] * PLIC_ENABLE_SIZE + \ 51 ((irq / 32) * sizeof(uint32_t))) 52 53#define PLIC_CONTEXT(sc, h) (PLIC_CONTEXT_BASE + \ 54 sc->sc_context[(h)] * PLIC_CONTEXT_SIZE) 55#define PLIC_CLAIM(sc, h) (PLIC_CONTEXT(sc, h) + PLIC_CLAIM_COMPLETE_OFFS) 56#define PLIC_COMPLETE(sc, h) PLIC_CLAIM(sc, h) /* same address */ 57#define PLIC_THRESHOLD(sc, h) (PLIC_CONTEXT(sc, h) + PLIC_THRESHOLD_OFFS) 58 59#define PLIC_READ(sc, reg) \ 60 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 61#define PLIC_WRITE(sc, reg, val) \ 62 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 63 64 65struct plic_softc *plic_sc; 66 67 68void * 69plic_intr_establish_xname(u_int irq, int ipl, int flags, 70 int (*func)(void *), void *arg, const char *xname) 71{ 72 struct plic_softc * const sc = plic_sc; 73 struct plic_intrhand *ih; 74 75 /* 76 * Choose calling hart. 77 * XXX need a better hart selection method 78 */ 79 u_int hartid = curcpu()->ci_cpuid; 80 81 evcnt_attach_dynamic(&sc->sc_intrevs[irq], EVCNT_TYPE_INTR, NULL, 82 "plic", xname); 83 84 ih = &sc->sc_intr[irq]; 85 KASSERTMSG(ih->ih_func == NULL, 86 "Oops, we need to chain PLIC interrupt handlers"); 87 if (ih->ih_func != NULL) { 88 aprint_error_dev(sc->sc_dev, "irq slot %d already used\n", irq); 89 return NULL; 90 } 91 ih->ih_mpsafe = (flags & IST_MPSAFE) != 0; 92 ih->ih_func = func; 93 ih->ih_arg = arg; 94 ih->ih_irq = irq; 95 ih->ih_hartid = hartid; 96 97 plic_set_priority(sc, irq, 1); 98 plic_enable(sc, hartid, irq); 99 100 return ih; 101} 102 103void 104plic_intr_disestablish(void *cookie) 105{ 106 struct plic_softc * const sc = plic_sc; 107 struct plic_intrhand * const ih = cookie; 108 const u_int hartid = ih->ih_hartid; 109 const u_int irq = ih->ih_irq; 110 111 plic_disable(sc, hartid, irq); 112 plic_set_priority(sc, irq, 0); 113 114 memset(&sc->sc_intr[irq], 0, sizeof(*sc->sc_intr)); 115} 116 117int 118plic_intr(void *arg) 119{ 120 struct plic_softc * const sc = arg; 121 const cpuid_t hartid = cpu_number(); 122 const bus_addr_t claim_addr = PLIC_CLAIM(sc, hartid); 123 const bus_addr_t complete_addr = PLIC_COMPLETE(sc, hartid); 124 uint32_t pending; 125 int rv = 0; 126 127 while ((pending = PLIC_READ(sc, claim_addr)) > 0) { 128 struct plic_intrhand *ih = &sc->sc_intr[pending]; 129 130 sc->sc_intrevs[pending].ev_count++; 131 132 KASSERT(ih->ih_func != NULL); 133#ifdef MULTIPROCESSOR 134 if (!ih->ih_mpsafe) { 135 KERNEL_LOCK(1, NULL); 136 rv |= ih->ih_func(ih->ih_arg); 137 KERNEL_UNLOCK_ONE(NULL); 138 } else 139#endif 140 rv |= ih->ih_func(ih->ih_arg); 141 142 PLIC_WRITE(sc, complete_addr, pending); 143 } 144 145 return rv; 146} 147 148void 149plic_enable(struct plic_softc *sc, u_int hartid, u_int irq) 150{ 151 KASSERT(irq < PLIC_NIRQ); 152 const bus_addr_t addr = PLIC_ENABLE(sc, hartid, irq); 153 const uint32_t mask = __BIT(irq % 32); 154 155 uint32_t reg = PLIC_READ(sc, addr); 156 reg |= mask; 157 158 PLIC_WRITE(sc, addr, reg); 159} 160 161void 162plic_disable(struct plic_softc *sc, u_int hartid, u_int irq) 163{ 164 KASSERT(irq < PLIC_NIRQ); 165 const bus_addr_t addr = PLIC_ENABLE(sc, hartid, irq); 166 const uint32_t mask = __BIT(irq % 32); 167 168 uint32_t reg = PLIC_READ(sc, addr); 169 reg &= ~mask; 170 PLIC_WRITE(sc, addr, reg); 171} 172 173void 174plic_set_priority(struct plic_softc *sc, u_int irq, uint32_t priority) 175{ 176 KASSERT(irq < PLIC_NIRQ); 177 const bus_addr_t addr = PLIC_PRIORITY(irq); 178 179 PLIC_WRITE(sc, addr, priority); 180} 181 182void 183plic_set_threshold(struct plic_softc *sc, cpuid_t hartid, uint32_t threshold) 184{ 185 const bus_addr_t addr = PLIC_THRESHOLD(sc, hartid); 186 187 PLIC_WRITE(sc, addr, threshold); 188} 189 190int 191plic_attach_common(struct plic_softc *sc, bus_addr_t addr, bus_size_t size) 192{ 193 const size_t szintrs = sizeof(*sc->sc_intr) * sc->sc_ndev; 194 const size_t szintrevs = sizeof(*sc->sc_intrevs) * sc->sc_ndev; 195 196 sc->sc_intr = kmem_zalloc(szintrs, KM_SLEEP); 197 sc->sc_intrevs = kmem_zalloc(szintrevs, KM_SLEEP); 198 199 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 200 aprint_error_dev(sc->sc_dev, "couldn't map registers\n"); 201 kmem_free(sc->sc_intr, szintrs); 202 kmem_free(sc->sc_intrevs, szintrevs); 203 return -1; 204 } 205 206 aprint_naive("\n"); 207 aprint_normal(": RISC-V PLIC (%u IRQs)\n", sc->sc_ndev); 208 209 plic_sc = sc; 210 211 /* Start with all interrupts disabled. */ 212 u_int irq; 213 for (irq = PLIC_FIRST_IRQ; irq < sc->sc_ndev; irq++) { 214 plic_set_priority(sc, irq, 0); 215 } 216 217 struct cpu_info *ci; 218 CPU_INFO_ITERATOR cii; 219 /* Set priority thresholds for all interrupts to 0 (not masked). */ 220 for (CPU_INFO_FOREACH(cii, ci)) { 221 plic_set_threshold(sc, ci->ci_cpuid, 0); 222 } 223 224 return 0; 225} 226