]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/riscv/riscv/plic.c
Merge llvm trunk r338150, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / riscv / riscv / plic.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com>
5  * All rights reserved.
6  *
7  * This software was developed by SRI International and the University of
8  * Cambridge Computer Laboratory (Department of Computer Science and
9  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
10  * DARPA SSITH research programme.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/ktr.h>
42 #include <sys/module.h>
43 #include <sys/proc.h>
44 #include <sys/rman.h>
45
46 #include <machine/bus.h>
47 #include <machine/intr.h>
48
49 #include <dev/ofw/openfirm.h>
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52
53 #include "pic_if.h"
54
55 #define PLIC_NIRQS              32
56 #define PLIC_PRIORITY(n)        (0x000000 + (n) * 0x4)
57 #define PLIC_ENABLE(n, h)       (0x002000 + (h) * 0x80 + (n) / 32)
58 #define PLIC_THRESHOLD(h)       (0x200000 + (h) * 0x1000 + 0x0)
59 #define PLIC_CLAIM(h)           (0x200000 + (h) * 0x1000 + 0x4)
60
61 struct plic_irqsrc {
62         struct intr_irqsrc      isrc;
63         u_int                   irq;
64 };
65
66 struct plic_softc {
67         device_t                dev;
68         struct resource *       intc_res;
69         struct plic_irqsrc      isrcs[PLIC_NIRQS];
70 };
71
72 #define RD4(sc, reg)                            \
73     bus_read_4(sc->intc_res, (reg))
74 #define WR4(sc, reg, val)                       \
75     bus_write_4(sc->intc_res, (reg), (val))
76
77 static inline void
78 plic_irq_dispatch(struct plic_softc *sc, u_int irq,
79     struct trapframe *tf)
80 {
81         struct plic_irqsrc *src;
82
83         src = &sc->isrcs[irq];
84
85         if (intr_isrc_dispatch(&src->isrc, tf) != 0)
86                 device_printf(sc->dev, "Stray irq %u detected\n", irq);
87 }
88
89 static int
90 plic_intr(void *arg)
91 {
92         struct plic_softc *sc;
93         struct trapframe *tf;
94         uint32_t pending;
95         uint32_t cpu;
96
97         sc = arg;
98         cpu = PCPU_GET(cpuid);
99
100         pending = RD4(sc, PLIC_CLAIM(cpu));
101         if (pending) {
102                 tf = curthread->td_intr_frame;
103                 plic_irq_dispatch(sc, pending, tf);
104                 WR4(sc, PLIC_CLAIM(cpu), pending);
105         }
106
107         return (FILTER_HANDLED);
108 }
109
110 static void
111 plic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
112 {
113         struct plic_softc *sc;
114         struct plic_irqsrc *src;
115         uint32_t reg;
116         uint32_t cpu;
117
118         sc = device_get_softc(dev);
119         src = (struct plic_irqsrc *)isrc;
120
121         cpu = PCPU_GET(cpuid);
122
123         reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
124         reg &= ~(1 << (src->irq % 32));
125         WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
126 }
127
128 static void
129 plic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
130 {
131         struct plic_softc *sc;
132         struct plic_irqsrc *src;
133         uint32_t reg;
134         uint32_t cpu;
135
136         sc = device_get_softc(dev);
137         src = (struct plic_irqsrc *)isrc;
138
139         WR4(sc, PLIC_PRIORITY(src->irq), 1);
140
141         cpu = PCPU_GET(cpuid);
142
143         reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
144         reg |= (1 << (src->irq % 32));
145         WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
146 }
147
148 static int
149 plic_map_intr(device_t dev, struct intr_map_data *data,
150     struct intr_irqsrc **isrcp)
151 {
152         struct intr_map_data_fdt *daf;
153         struct plic_softc *sc;
154
155         sc = device_get_softc(dev);
156
157         if (data->type != INTR_MAP_DATA_FDT)
158                 return (ENOTSUP);
159
160         daf = (struct intr_map_data_fdt *)data;
161         if (daf->ncells != 1 || daf->cells[0] >= PLIC_NIRQS)
162                 return (EINVAL);
163
164         *isrcp = &sc->isrcs[daf->cells[0]].isrc;
165
166         return (0);
167 }
168
169 static int
170 plic_probe(device_t dev)
171 {
172
173         if (!ofw_bus_status_okay(dev))
174                 return (ENXIO);
175
176         if (!ofw_bus_is_compatible(dev, "riscv,plic0"))
177                 return (ENXIO);
178
179         device_set_desc(dev, "RISC-V PLIC");
180
181         return (BUS_PROBE_DEFAULT);
182 }
183
184 static int
185 plic_attach(device_t dev)
186 {
187         struct plic_irqsrc *isrcs;
188         struct plic_softc *sc;
189         struct intr_pic *pic;
190         uint32_t irq;
191         const char *name;
192         phandle_t xref;
193         uint32_t cpu;
194         int error;
195         int rid;
196
197         sc = device_get_softc(dev);
198
199         sc->dev = dev;
200
201         /* Request memory resources */
202         rid = 0;
203         sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
204             RF_ACTIVE);
205         if (sc->intc_res == NULL) {
206                 device_printf(dev,
207                     "Error: could not allocate memory resources\n");
208                 return (ENXIO);
209         }
210
211         isrcs = sc->isrcs;
212         name = device_get_nameunit(sc->dev);
213         cpu = PCPU_GET(cpuid);
214         for (irq = 0; irq < PLIC_NIRQS; irq++) {
215                 isrcs[irq].irq = irq;
216                 error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
217                     0, "%s,%u", name, irq);
218                 if (error != 0)
219                         return (error);
220
221                 WR4(sc, PLIC_ENABLE(irq, cpu), 0);
222         }
223         WR4(sc, PLIC_THRESHOLD(cpu), 0);
224
225         xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
226         pic = intr_pic_register(sc->dev, xref);
227         if (pic == NULL)
228                 return (ENXIO);
229
230         csr_set(sie, SIE_SEIE);
231
232         return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0));
233 }
234
235 static device_method_t plic_methods[] = {
236         DEVMETHOD(device_probe,         plic_probe),
237         DEVMETHOD(device_attach,        plic_attach),
238
239         DEVMETHOD(pic_disable_intr,     plic_disable_intr),
240         DEVMETHOD(pic_enable_intr,      plic_enable_intr),
241         DEVMETHOD(pic_map_intr,         plic_map_intr),
242
243         DEVMETHOD_END
244 };
245
246 static driver_t plic_driver = {
247         "plic",
248         plic_methods,
249         sizeof(struct plic_softc),
250 };
251
252 static devclass_t plic_devclass;
253
254 EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, plic_devclass,
255     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);