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