]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/riscv/riscv/plic.c
MFV r346563:
[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_MAX_IRQS           2048
56 #define PLIC_PRIORITY(n)        (0x000000 + (n) * 0x4)
57 #define PLIC_ENABLE(n, h)       (0x002000 + (h) * 0x80 + 4 * ((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_MAX_IRQS];
70         int                     ndev;
71 };
72
73 #define RD4(sc, reg)                            \
74     bus_read_4(sc->intc_res, (reg))
75 #define WR4(sc, reg, val)                       \
76     bus_write_4(sc->intc_res, (reg), (val))
77
78 static inline void
79 plic_irq_dispatch(struct plic_softc *sc, u_int irq,
80     struct trapframe *tf)
81 {
82         struct plic_irqsrc *src;
83
84         src = &sc->isrcs[irq];
85
86         if (intr_isrc_dispatch(&src->isrc, tf) != 0)
87                 device_printf(sc->dev, "Stray irq %u detected\n", irq);
88 }
89
90 static int
91 plic_intr(void *arg)
92 {
93         struct plic_softc *sc;
94         struct trapframe *tf;
95         uint32_t pending;
96         uint32_t cpu;
97
98         sc = arg;
99         cpu = PCPU_GET(cpuid);
100
101         pending = RD4(sc, PLIC_CLAIM(cpu));
102         if (pending) {
103                 tf = curthread->td_intr_frame;
104                 plic_irq_dispatch(sc, pending, tf);
105                 WR4(sc, PLIC_CLAIM(cpu), pending);
106         }
107
108         return (FILTER_HANDLED);
109 }
110
111 static void
112 plic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
113 {
114         struct plic_softc *sc;
115         struct plic_irqsrc *src;
116         uint32_t reg;
117         uint32_t cpu;
118
119         sc = device_get_softc(dev);
120         src = (struct plic_irqsrc *)isrc;
121
122         cpu = PCPU_GET(cpuid);
123
124         reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
125         reg &= ~(1 << (src->irq % 32));
126         WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
127 }
128
129 static void
130 plic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
131 {
132         struct plic_softc *sc;
133         struct plic_irqsrc *src;
134         uint32_t reg;
135         uint32_t cpu;
136
137         sc = device_get_softc(dev);
138         src = (struct plic_irqsrc *)isrc;
139
140         WR4(sc, PLIC_PRIORITY(src->irq), 1);
141
142         cpu = PCPU_GET(cpuid);
143
144         reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
145         reg |= (1 << (src->irq % 32));
146         WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
147 }
148
149 static int
150 plic_map_intr(device_t dev, struct intr_map_data *data,
151     struct intr_irqsrc **isrcp)
152 {
153         struct intr_map_data_fdt *daf;
154         struct plic_softc *sc;
155
156         sc = device_get_softc(dev);
157
158         if (data->type != INTR_MAP_DATA_FDT)
159                 return (ENOTSUP);
160
161         daf = (struct intr_map_data_fdt *)data;
162         if (daf->ncells != 1 || daf->cells[0] > sc->ndev)
163                 return (EINVAL);
164
165         *isrcp = &sc->isrcs[daf->cells[0]].isrc;
166
167         return (0);
168 }
169
170 static int
171 plic_probe(device_t dev)
172 {
173
174         if (!ofw_bus_status_okay(dev))
175                 return (ENXIO);
176
177         if (!ofw_bus_is_compatible(dev, "riscv,plic0"))
178                 return (ENXIO);
179
180         device_set_desc(dev, "RISC-V PLIC");
181
182         return (BUS_PROBE_DEFAULT);
183 }
184
185 static int
186 plic_attach(device_t dev)
187 {
188         struct plic_irqsrc *isrcs;
189         struct plic_softc *sc;
190         struct intr_pic *pic;
191         uint32_t irq;
192         const char *name;
193         phandle_t node;
194         phandle_t xref;
195         uint32_t cpu;
196         int error;
197         int rid;
198
199         sc = device_get_softc(dev);
200
201         sc->dev = dev;
202
203         node = ofw_bus_get_node(dev);
204         if ((OF_getencprop(node, "riscv,ndev", &sc->ndev,
205             sizeof(sc->ndev))) < 0) {
206                 device_printf(dev,
207                     "Error: could not get number of devices\n");
208                 return (ENXIO);
209         }
210
211         if (sc->ndev >= PLIC_MAX_IRQS) {
212                 device_printf(dev,
213                     "Error: invalid ndev (%d)\n", sc->ndev);
214                 return (ENXIO);
215         }
216
217         /* Request memory resources */
218         rid = 0;
219         sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
220             RF_ACTIVE);
221         if (sc->intc_res == NULL) {
222                 device_printf(dev,
223                     "Error: could not allocate memory resources\n");
224                 return (ENXIO);
225         }
226
227         isrcs = sc->isrcs;
228         name = device_get_nameunit(sc->dev);
229         cpu = PCPU_GET(cpuid);
230         for (irq = 1; irq <= sc->ndev; irq++) {
231                 isrcs[irq].irq = irq;
232                 error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
233                     0, "%s,%u", name, irq);
234                 if (error != 0)
235                         return (error);
236
237                 WR4(sc, PLIC_PRIORITY(irq), 0);
238                 WR4(sc, PLIC_ENABLE(irq, cpu), 0);
239         }
240         WR4(sc, PLIC_THRESHOLD(cpu), 0);
241
242         xref = OF_xref_from_node(node);
243         pic = intr_pic_register(sc->dev, xref);
244         if (pic == NULL)
245                 return (ENXIO);
246
247         csr_set(sie, SIE_SEIE);
248
249         return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0));
250 }
251
252 static device_method_t plic_methods[] = {
253         DEVMETHOD(device_probe,         plic_probe),
254         DEVMETHOD(device_attach,        plic_attach),
255
256         DEVMETHOD(pic_disable_intr,     plic_disable_intr),
257         DEVMETHOD(pic_enable_intr,      plic_enable_intr),
258         DEVMETHOD(pic_map_intr,         plic_map_intr),
259
260         DEVMETHOD_END
261 };
262
263 static driver_t plic_driver = {
264         "plic",
265         plic_methods,
266         sizeof(struct plic_softc),
267 };
268
269 static devclass_t plic_devclass;
270
271 EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, plic_devclass,
272     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);