1/*-
2 * Copyright (c) 2015-2016 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * Portions of this software were developed by SRI International and the
6 * University of Cambridge Computer Laboratory under DARPA/AFRL contract
7 * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Portions of this software were developed by the University of Cambridge
10 * Computer Laboratory as part of the CTSRD Project, with support from the
11 * UK Higher Education Innovation Fund (HEIF).
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/bus.h>
41#include <sys/cpuset.h>
42#include <sys/interrupt.h>
43#include <sys/smp.h>
44
45#include <machine/clock.h>
46#include <machine/cpu.h>
47#include <machine/cpufunc.h>
48#include <machine/frame.h>
49#include <machine/intr.h>
50
51#ifdef SMP
52#include <machine/smp.h>
53#endif
54
55u_long intrcnt[NIRQS];
56size_t sintrcnt = sizeof(intrcnt);
57
58char intrnames[NIRQS * (MAXCOMLEN + 1) * 2];
59size_t sintrnames = sizeof(intrnames);
60
61static struct intr_event *intr_events[NIRQS];
62static riscv_intrcnt_t riscv_intr_counters[NIRQS];
63
64static int intrcnt_index;
65
66riscv_intrcnt_t
67riscv_intrcnt_create(const char* name)
68{
69	riscv_intrcnt_t counter;
70
71	counter = &intrcnt[intrcnt_index++];
72	riscv_intrcnt_setname(counter, name);
73
74	return (counter);
75}
76
77void
78riscv_intrcnt_setname(riscv_intrcnt_t counter, const char *name)
79{
80	int i;
81
82	i = (counter - intrcnt);
83
84	KASSERT(counter != NULL, ("riscv_intrcnt_setname: NULL counter"));
85
86	snprintf(intrnames + (MAXCOMLEN + 1) * i,
87	    MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
88}
89
90static void
91riscv_mask_irq(void *source)
92{
93	uintptr_t irq;
94
95	irq = (uintptr_t)source;
96
97	switch (irq) {
98	case IRQ_TIMER:
99		csr_clear(sie, SIE_STIE);
100		break;
101	case IRQ_SOFTWARE:
102		csr_clear(sie, SIE_SSIE);
103		break;
104	case IRQ_UART:
105		machine_command(ECALL_IO_IRQ_MASK, 0);
106		break;
107	default:
108		panic("Unknown irq %d\n", irq);
109	}
110}
111
112static void
113riscv_unmask_irq(void *source)
114{
115	uintptr_t irq;
116
117	irq = (uintptr_t)source;
118
119	switch (irq) {
120	case IRQ_TIMER:
121		csr_set(sie, SIE_STIE);
122		break;
123	case IRQ_SOFTWARE:
124		csr_set(sie, SIE_SSIE);
125		break;
126	case IRQ_UART:
127		machine_command(ECALL_IO_IRQ_MASK, 1);
128		break;
129	default:
130		panic("Unknown irq %d\n", irq);
131	}
132}
133
134void
135riscv_init_interrupts(void)
136{
137	char name[MAXCOMLEN + 1];
138	int i;
139
140	for (i = 0; i < NIRQS; i++) {
141		snprintf(name, MAXCOMLEN + 1, "int%d:", i);
142		riscv_intr_counters[i] = riscv_intrcnt_create(name);
143	}
144}
145
146int
147riscv_setup_intr(const char *name, driver_filter_t *filt,
148    void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
149{
150	struct intr_event *event;
151	int error;
152
153	if (irq < 0 || irq >= NIRQS)
154		panic("%s: unknown intr %d", __func__, irq);
155
156	event = intr_events[irq];
157	if (event == NULL) {
158		error = intr_event_create(&event, (void *)(uintptr_t)irq, 0,
159		    irq, riscv_mask_irq, riscv_unmask_irq,
160		    NULL, NULL, "int%d", irq);
161		if (error)
162			return (error);
163		intr_events[irq] = event;
164		riscv_unmask_irq((void*)(uintptr_t)irq);
165	}
166
167	error = intr_event_add_handler(event, name, filt, handler, arg,
168	    intr_priority(flags), flags, cookiep);
169	if (error) {
170		printf("Failed to setup intr: %d\n", irq);
171		return (error);
172	}
173
174	riscv_intrcnt_setname(riscv_intr_counters[irq],
175			     event->ie_fullname);
176
177	return (0);
178}
179
180int
181riscv_teardown_intr(void *ih)
182{
183
184	/* TODO */
185
186	return (0);
187}
188
189int
190riscv_config_intr(u_int irq, enum intr_trigger trig, enum intr_polarity pol)
191{
192
193	/* There is no configuration for interrupts */
194
195	return (0);
196}
197
198void
199riscv_cpu_intr(struct trapframe *frame)
200{
201	struct intr_event *event;
202	int active_irq;
203
204	critical_enter();
205
206	KASSERT(frame->tf_scause & EXCP_INTR,
207		("riscv_cpu_intr: wrong frame passed"));
208
209	active_irq = (frame->tf_scause & EXCP_MASK);
210
211	switch (active_irq) {
212	case IRQ_UART:
213	case IRQ_SOFTWARE:
214	case IRQ_TIMER:
215		event = intr_events[active_irq];
216		/* Update counters */
217		atomic_add_long(riscv_intr_counters[active_irq], 1);
218		PCPU_INC(cnt.v_intr);
219		break;
220	case IRQ_HTIF:
221		/* HTIF interrupts are only handled in machine mode */
222		panic("%s: HTIF interrupt", __func__);
223		break;
224	default:
225		event = NULL;
226	}
227
228	if (!event || TAILQ_EMPTY(&event->ie_handlers) ||
229	    (intr_event_handle(event, frame) != 0))
230		printf("stray interrupt %d\n", active_irq);
231
232	critical_exit();
233}
234
235#ifdef SMP
236void
237riscv_setup_ipihandler(driver_filter_t *filt)
238{
239
240	riscv_setup_intr("ipi", filt, NULL, NULL, IRQ_SOFTWARE,
241	    INTR_TYPE_MISC, NULL);
242}
243
244void
245riscv_unmask_ipi(void)
246{
247
248	csr_set(sie, SIE_SSIE);
249}
250
251/* Sending IPI */
252static void
253ipi_send(struct pcpu *pc, int ipi)
254{
255
256	CTR3(KTR_SMP, "%s: cpu=%d, ipi=%x", __func__, pc->pc_cpuid, ipi);
257
258	atomic_set_32(&pc->pc_pending_ipis, ipi);
259	machine_command(ECALL_SEND_IPI, pc->pc_reg);
260
261	CTR1(KTR_SMP, "%s: sent", __func__);
262}
263
264void
265ipi_all_but_self(u_int ipi)
266{
267	cpuset_t other_cpus;
268
269	other_cpus = all_cpus;
270	CPU_CLR(PCPU_GET(cpuid), &other_cpus);
271
272	CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
273	ipi_selected(other_cpus, ipi);
274}
275
276void
277ipi_cpu(int cpu, u_int ipi)
278{
279	cpuset_t cpus;
280
281	CPU_ZERO(&cpus);
282	CPU_SET(cpu, &cpus);
283
284	CTR3(KTR_SMP, "%s: cpu: %d, ipi: %x\n", __func__, cpu, ipi);
285	ipi_send(cpuid_to_pcpu[cpu], ipi);
286}
287
288void
289ipi_selected(cpuset_t cpus, u_int ipi)
290{
291	struct pcpu *pc;
292
293	CTR1(KTR_SMP, "ipi_selected: ipi: %x", ipi);
294
295	STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) {
296		if (CPU_ISSET(pc->pc_cpuid, &cpus)) {
297			CTR3(KTR_SMP, "%s: pc: %p, ipi: %x\n", __func__, pc,
298			    ipi);
299			ipi_send(pc, ipi);
300		}
301	}
302}
303
304#endif
305