1 /* $NetBSD: plic.c,v 1.1 2023/05/07 12:41:48 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.1 2023/05/07 12:41:48 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, c, irq) (PLIC_ENABLE_BASE + \ 50 sc->sc_context[(c)] * PLIC_ENABLE_SIZE + \ 51 ((irq / 32) * sizeof(uint32_t))) 52 53 #define PLIC_CONTEXT(sc, c) (PLIC_CONTEXT_BASE + \ 54 sc->sc_context[(c)] * PLIC_CONTEXT_SIZE) 55 #define PLIC_CLAIM(sc, c) (PLIC_CONTEXT(sc, c) + PLIC_CLAIM_COMPLETE_OFFS) 56 #define PLIC_COMPLETE(sc, c) PLIC_CLAIM(sc, c) /* same address */ 57 #define PLIC_THRESHOLD(sc, c) (PLIC_CONTEXT(sc, c) + 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 65 struct plic_softc *plic_sc; 66 67 68 void * 69 plic_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 /* XXX need a better CPU selection method */ 76 // u_int cidx = cpu_index(curcpu()); 77 u_int cidx = 0; 78 79 evcnt_attach_dynamic(&sc->sc_intrevs[irq], EVCNT_TYPE_INTR, NULL, 80 "plic", xname); 81 82 ih = &sc->sc_intr[irq]; 83 KASSERTMSG(ih->ih_func == NULL, 84 "Oops, we need to chain PLIC interrupt handlers"); 85 if (ih->ih_func != NULL) { 86 aprint_error_dev(sc->sc_dev, "irq slot %d already used\n", irq); 87 return NULL; 88 } 89 ih->ih_mpsafe = (flags & IST_MPSAFE) != 0; 90 ih->ih_func = func; 91 ih->ih_arg = arg; 92 ih->ih_irq = irq; 93 ih->ih_cidx = cidx; 94 95 plic_set_priority(sc, irq, 1); 96 plic_enable(sc, cidx, irq); 97 98 return ih; 99 } 100 101 void 102 plic_intr_disestablish(void *cookie) 103 { 104 struct plic_softc * const sc = plic_sc; 105 struct plic_intrhand * const ih = cookie; 106 const u_int cidx = ih->ih_cidx; 107 const u_int irq = ih->ih_irq; 108 109 plic_disable(sc, cidx, irq); 110 plic_set_priority(sc, irq, 0); 111 112 memset(&sc->sc_intr[irq], 0, sizeof(*sc->sc_intr)); 113 } 114 115 int 116 plic_intr(void *arg) 117 { 118 struct plic_softc * const sc = arg; 119 const cpuid_t cpuid = cpu_number(); 120 const bus_addr_t claim_addr = PLIC_CLAIM(sc, cpuid); 121 const bus_addr_t complete_addr = PLIC_COMPLETE(sc, cpuid); 122 uint32_t pending; 123 int rv = 0; 124 125 while ((pending = PLIC_READ(sc, claim_addr)) > 0) { 126 struct plic_intrhand *ih = &sc->sc_intr[pending]; 127 128 sc->sc_intrevs[pending].ev_count++; 129 130 KASSERT(ih->ih_func != NULL); 131 #ifdef MULTIPROCESSOR 132 if (!ih->ih_mpsafe) { 133 KERNEL_LOCK(1, NULL); 134 rv |= ih->ih_func(ih->ih_arg); 135 KERNEL_UNLOCK_ONE(NULL); 136 } else 137 #endif 138 rv |= ih->ih_func(ih->ih_arg); 139 140 PLIC_WRITE(sc, complete_addr, pending); 141 } 142 143 return rv; 144 } 145 146 void 147 plic_enable(struct plic_softc *sc, u_int cpu, u_int irq) 148 { 149 KASSERT(irq < PLIC_NIRQ); 150 const bus_addr_t addr = PLIC_ENABLE(sc, cpu, irq); 151 const uint32_t mask = __BIT(irq % 32); 152 153 uint32_t reg = PLIC_READ(sc, addr); 154 reg |= mask; 155 PLIC_WRITE(sc, addr, reg); 156 } 157 158 void 159 plic_disable(struct plic_softc *sc, u_int cpu, u_int irq) 160 { 161 KASSERT(irq < PLIC_NIRQ); 162 const bus_addr_t addr = PLIC_ENABLE(sc, cpu, irq); 163 const uint32_t mask = __BIT(irq % 32); 164 165 uint32_t reg = PLIC_READ(sc, addr); 166 reg &= ~mask; 167 PLIC_WRITE(sc, addr, reg); 168 } 169 170 void 171 plic_set_priority(struct plic_softc *sc, u_int irq, uint32_t priority) 172 { 173 KASSERT(irq < PLIC_NIRQ); 174 const bus_addr_t addr = PLIC_PRIORITY(irq); 175 176 PLIC_WRITE(sc, addr, priority); 177 } 178 179 void 180 plic_set_threshold(struct plic_softc *sc, cpuid_t cpu, uint32_t threshold) 181 { 182 const bus_addr_t addr = PLIC_THRESHOLD(sc, cpu); 183 184 PLIC_WRITE(sc, addr, threshold); 185 } 186 187 int 188 plic_attach_common(struct plic_softc *sc, bus_addr_t addr, bus_size_t size) 189 { 190 struct cpu_info *ci; 191 CPU_INFO_ITERATOR cii; 192 u_int irq; 193 194 sc->sc_intr = kmem_zalloc(sizeof(*sc->sc_intr) * sc->sc_ndev, 195 KM_SLEEP); 196 sc->sc_intrevs = kmem_zalloc(sizeof(*sc->sc_intrevs) * sc->sc_ndev, 197 KM_SLEEP); 198 199 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 200 aprint_error("couldn't map registers\n"); 201 return -1; 202 } 203 204 aprint_naive("\n"); 205 aprint_normal("RISC-V PLIC (%u IRQs)\n", sc->sc_ndev); 206 207 plic_sc = sc; 208 209 /* Start with all interrupts disabled. */ 210 for (irq = PLIC_FIRST_IRQ; irq < sc->sc_ndev; irq++) { 211 plic_set_priority(sc, irq, 0); 212 } 213 214 /* Set priority thresholds for all interrupts to 0 (not masked). */ 215 for (CPU_INFO_FOREACH(cii, ci)) { 216 plic_set_threshold(sc, ci->ci_cpuid, 0); 217 } 218 219 return 0; 220 } 221