1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Michal Meloun <[email protected]>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33
34 #include <sys/bitset.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/rman.h>
38 #include <sys/lock.h>
39 #include <sys/module.h>
40 #include <sys/mutex.h>
41
42 #include <machine/bus.h>
43 #include <machine/intr.h>
44 #include <machine/resource.h>
45
46 #include <dev/fdt/simplebus.h>
47
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50
51 #include "msi_if.h"
52 #include "pic_if.h"
53
54 #define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx)
55 #define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
56 #define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \
57 device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF)
58 #define MV_AP806_SEI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx);
59 #define MV_AP806_SEI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED);
60 #define MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
61
62 #define GICP_SECR0 0x00
63 #define GICP_SECR1 0x04
64 #define GICP_SECR(i) (0x00 + (((i)/32) * 0x4))
65 #define GICP_SECR_BIT(i) ((i) % 32)
66 #define GICP_SEMR0 0x20
67 #define GICP_SEMR1 0x24
68 #define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4))
69 #define GICP_SEMR_BIT(i) ((i) % 32)
70
71 #define MV_AP806_SEI_AP_FIRST 0
72 #define MV_AP806_SEI_AP_SIZE 21
73 #define MV_AP806_SEI_CP_FIRST 21
74 #define MV_AP806_SEI_CP_SIZE 43
75 #define MV_AP806_SEI_MAX_NIRQS (MV_AP806_SEI_AP_SIZE + MV_AP806_SEI_CP_SIZE)
76
77 #define MV_AP806_SEI_SETSPI_OFFSET 0x30
78
79 BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE);
80
81 struct mv_ap806_sei_irqsrc {
82 struct intr_irqsrc isrc;
83 u_int irq;
84 };
85
86 struct mv_ap806_sei_softc {
87 device_t dev;
88 struct resource *mem_res;
89 struct resource *irq_res;
90 void *irq_ih;
91 struct mtx mtx;
92
93 struct mv_ap806_sei_irqsrc *isrcs;
94
95 struct sei_msi_bitmap msi_bitmap;
96 };
97
98 static struct ofw_compat_data compat_data[] = {
99 {"marvell,ap806-sei", 1},
100 {NULL, 0}
101 };
102
103 #define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg))
104 #define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val))
105
106 static msi_alloc_msi_t mv_ap806_sei_alloc_msi;
107 static msi_release_msi_t mv_ap806_sei_release_msi;
108 static msi_map_msi_t mv_ap806_sei_map_msi;
109
110 static inline void
mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc * sc,struct mv_ap806_sei_irqsrc * sisrc,uint32_t val)111 mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc,
112 struct mv_ap806_sei_irqsrc *sisrc, uint32_t val)
113 {
114 uint32_t tmp;
115 int bit;
116
117 bit = GICP_SEMR_BIT(sisrc->irq);
118 MV_AP806_SEI_LOCK(sc);
119 tmp = RD4(sc, GICP_SEMR(sisrc->irq));
120 if (val != 0)
121 tmp |= 1 << bit;
122 else
123 tmp &= ~(1 << bit);
124 WR4(sc, GICP_SEMR(sisrc->irq), tmp);
125 MV_AP806_SEI_UNLOCK(sc);
126 }
127
128 static inline void
mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc * sc,struct mv_ap806_sei_irqsrc * sisrc)129 mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc,
130 struct mv_ap806_sei_irqsrc *sisrc)
131 {
132
133 WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq));
134 }
135
136 static void
mv_ap806_sei_enable_intr(device_t dev,struct intr_irqsrc * isrc)137 mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc)
138 {
139 struct mv_ap806_sei_softc *sc;
140 struct mv_ap806_sei_irqsrc *sisrc;
141
142 sc = device_get_softc(dev);
143 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
144 mv_ap806_sei_isrc_mask(sc, sisrc, 0);
145 }
146
147 static void
mv_ap806_sei_disable_intr(device_t dev,struct intr_irqsrc * isrc)148 mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc)
149 {
150 struct mv_ap806_sei_softc *sc;
151 struct mv_ap806_sei_irqsrc *sisrc;
152
153 sc = device_get_softc(dev);
154 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
155 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
156 }
157
158 static int
mv_ap806_sei_map(device_t dev,struct intr_map_data * data,u_int * irqp)159 mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp)
160 {
161 struct intr_map_data_fdt *daf;
162 u_int irq;
163
164 if (data->type != INTR_MAP_DATA_FDT)
165 return (ENOTSUP);
166
167 daf = (struct intr_map_data_fdt *)data;
168 if (daf->ncells != 1)
169 return (EINVAL);
170
171 if (daf->cells[0] < MV_AP806_SEI_AP_FIRST ||
172 daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE)
173 return (EINVAL);
174
175 irq = daf->cells[0];
176 if (irqp != NULL)
177 *irqp = irq;
178
179 return(0);
180 }
181
182 static int
mv_ap806_sei_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)183 mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data,
184 struct intr_irqsrc **isrcp)
185 {
186 struct mv_ap806_sei_softc *sc;
187 u_int irq;
188 int rv;
189
190 sc = device_get_softc(dev);
191 rv = mv_ap806_sei_map(dev, data, &irq);
192 if (rv == 0)
193 *isrcp = &sc->isrcs[irq].isrc;
194
195 return (rv);
196 }
197
198 static int
mv_ap806_sei_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)199 mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc,
200 struct resource *res, struct intr_map_data *data)
201 {
202 struct mv_ap806_sei_softc *sc;
203 struct mv_ap806_sei_irqsrc *sisrc;
204 u_int irq;
205 int rv;
206
207 sc = device_get_softc(dev);
208 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
209 if (data == NULL)
210 return (ENOTSUP);
211 rv = mv_ap806_sei_map(dev, data, &irq);
212 if (rv != 0)
213 return (rv);
214 if (irq != sisrc->irq)
215 return (EINVAL);
216 mv_ap806_sei_isrc_mask(sc, sisrc, 0);
217 return (0);
218 }
219
220 static int
mv_ap806_sei_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)221 mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
222 struct resource *res, struct intr_map_data *data)
223 {
224 struct mv_ap806_sei_softc *sc;
225 struct mv_ap806_sei_irqsrc *sisrc;
226
227 sc = device_get_softc(dev);
228 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
229
230 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
231 return (0);
232 }
233
234 static void
mv_ap806_sei_pre_ithread(device_t dev,struct intr_irqsrc * isrc)235 mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
236 {
237 struct mv_ap806_sei_softc *sc;
238 struct mv_ap806_sei_irqsrc *sisrc;
239
240 sc = device_get_softc(dev);
241 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
242
243 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
244 mv_ap806_sei_isrc_eoi(sc, sisrc);
245 }
246
247 static void
mv_ap806_sei_post_ithread(device_t dev,struct intr_irqsrc * isrc)248 mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc)
249 {
250 struct mv_ap806_sei_softc *sc;
251 struct mv_ap806_sei_irqsrc *sisrc;
252
253 sc = device_get_softc(dev);
254 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
255
256 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
257 }
258
259 static void
mv_ap806_sei_post_filter(device_t dev,struct intr_irqsrc * isrc)260 mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc)
261 {
262 struct mv_ap806_sei_softc *sc;
263 struct mv_ap806_sei_irqsrc *sisrc;
264
265 sc = device_get_softc(dev);
266 sisrc = (struct mv_ap806_sei_irqsrc *)isrc;
267
268 mv_ap806_sei_isrc_mask(sc, sisrc, 1);
269 mv_ap806_sei_isrc_eoi(sc, sisrc);
270 }
271
272 /* ----------------------------------------------------------------------------
273 *
274 * B u s i n t e r f a c e
275 */
276 static int
mv_ap806_sei_intr(void * arg)277 mv_ap806_sei_intr(void *arg)
278 {
279 struct mv_ap806_sei_softc *sc;
280 struct mv_ap806_sei_irqsrc *sirq;
281 struct trapframe *tf;
282 uint64_t cause;
283 u_int irq;
284
285 sc = (struct mv_ap806_sei_softc *)arg;
286 tf = curthread->td_intr_frame;
287 while (1) {
288 cause = RD4(sc, GICP_SECR1);
289 cause <<= 32;
290 cause |= RD4(sc, GICP_SECR0);
291
292 irq = ffsll(cause);
293 if (irq == 0) break;
294 irq--;
295 sirq = &sc->isrcs[irq];
296 if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) {
297 mv_ap806_sei_isrc_mask(sc, sirq, 0);
298 mv_ap806_sei_isrc_eoi(sc, sirq);
299 device_printf(sc->dev,
300 "Stray irq %u disabled\n", irq);
301 }
302 }
303
304 return (FILTER_HANDLED);
305 }
306
307 static int
mv_ap806_sei_probe(device_t dev)308 mv_ap806_sei_probe(device_t dev)
309 {
310
311 if (!ofw_bus_status_okay(dev))
312 return (ENXIO);
313
314 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
315 return (ENXIO);
316
317 device_set_desc(dev, "Marvell SEI");
318 return (BUS_PROBE_DEFAULT);
319 }
320
321 static int
mv_ap806_sei_attach(device_t dev)322 mv_ap806_sei_attach(device_t dev)
323 {
324 struct mv_ap806_sei_softc *sc;
325 phandle_t xref, node;
326 uint32_t irq;
327 const char *name;
328 int rv, rid;
329
330 sc = device_get_softc(dev);
331 sc->dev = dev;
332 node = ofw_bus_get_node(dev);
333 MV_AP806_SEI_LOCK_INIT(sc);
334
335 /* Allocate resources. */
336 rid = 0;
337 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
338 RF_ACTIVE);
339 if (sc->mem_res == NULL) {
340 device_printf(dev, "Cannot allocate memory resources\n");
341 rv = ENXIO;
342 goto fail;
343 }
344
345 rid = 0;
346 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
347 if (sc->irq_res == NULL) {
348 device_printf(dev, "Cannot allocate IRQ resources\n");
349 rv = ENXIO;
350 goto fail;
351 }
352
353 /* Mask all interrupts) */
354 WR4(sc, GICP_SEMR0, 0xFFFFFFFF);
355 WR4(sc, GICP_SEMR1, 0xFFFFFFFF);
356
357 /* Create all interrupt sources */
358 sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS,
359 M_DEVBUF, M_WAITOK | M_ZERO);
360 name = device_get_nameunit(sc->dev);
361 for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) {
362 sc->isrcs[irq].irq = irq;
363 rv = intr_isrc_register(&sc->isrcs[irq].isrc,
364 sc->dev, 0, "%s,%u", name, irq);
365 if (rv != 0)
366 goto fail; /* XXX deregister ISRCs */
367 }
368 xref = OF_xref_from_node(node);
369 if (intr_pic_register(dev, xref) == NULL) {
370 device_printf(dev, "Cannot register SEI\n");
371 rv = ENXIO;
372 goto fail;
373 }
374 if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE,
375 mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) {
376 device_printf(dev,
377 "Unable to register interrupt handler\n");
378 rv = ENXIO;
379 goto fail;
380 }
381
382 /*
383 * Bitmap of all IRQs.
384 * 1 - available, 0 - used.
385 */
386 BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap);
387
388 OF_device_register_xref(xref, dev);
389 return (0);
390
391 fail:
392 if (sc->irq_ih != NULL)
393 bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
394 if (sc->irq_res != NULL)
395 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
396 if (sc->mem_res != NULL)
397 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
398 MV_AP806_SEI_LOCK_DESTROY(sc);
399 return (ENXIO);
400 }
401
402 static int
mv_ap806_sei_detach(device_t dev)403 mv_ap806_sei_detach(device_t dev)
404 {
405
406 return (EBUSY);
407 }
408
409 static int
mv_ap806_sei_alloc_msi(device_t dev,device_t child,int count,int maxcount,device_t * pic,struct intr_irqsrc ** srcs)410 mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount,
411 device_t *pic, struct intr_irqsrc **srcs)
412 {
413 struct mv_ap806_sei_softc *sc;
414 int i, ret = 0, vector;
415
416 sc = device_get_softc(dev);
417
418 for (i = 0; i < count; i++) {
419 /*
420 * Find first available MSI vector represented by first set bit
421 * in the bitmap. BIT_FFS starts the count from 1,
422 * 0 means that nothing was found.
423 */
424 vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0);
425 if (vector == 0) {
426 ret = ENOMEM;
427 i--;
428 goto fail;
429 }
430
431 vector--;
432 BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap);
433 vector += MV_AP806_SEI_CP_FIRST;
434
435 srcs[i] = &sc->isrcs[vector].isrc;
436 }
437
438 return (ret);
439 fail:
440 mv_ap806_sei_release_msi(dev, child, i + 1, srcs);
441 return (ret);
442 }
443
444 static int
mv_ap806_sei_release_msi(device_t dev,device_t child,int count,struct intr_irqsrc ** srcs)445 mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs)
446 {
447 struct mv_ap806_sei_softc *sc;
448 int i;
449
450 sc = device_get_softc(dev);
451
452 for (i = 0; i < count; i++) {
453 BIT_SET(MV_AP806_SEI_CP_SIZE,
454 srcs[i]->isrc_irq - MV_AP806_SEI_CP_FIRST,
455 &sc->msi_bitmap);
456 }
457
458 return (0);
459 }
460
461 static int
mv_ap806_sei_map_msi(device_t dev,device_t child,struct intr_irqsrc * isrc,uint64_t * addr,uint32_t * data)462 mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
463 uint64_t *addr, uint32_t *data)
464 {
465 struct mv_ap806_sei_softc *sc;
466
467 sc = device_get_softc(dev);
468
469 *addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET;
470 *data = isrc->isrc_irq;
471
472 return (0);
473 }
474
475 static device_method_t mv_ap806_sei_methods[] = {
476 /* Device interface */
477 DEVMETHOD(device_probe, mv_ap806_sei_probe),
478 DEVMETHOD(device_attach, mv_ap806_sei_attach),
479 DEVMETHOD(device_detach, mv_ap806_sei_detach),
480
481 /* Interrupt controller interface */
482 DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr),
483 DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr),
484 DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr),
485 DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr),
486 DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr),
487 DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter),
488 DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread),
489 DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread),
490
491 /* MSI interface */
492 DEVMETHOD(msi_alloc_msi, mv_ap806_sei_alloc_msi),
493 DEVMETHOD(msi_release_msi, mv_ap806_sei_release_msi),
494 DEVMETHOD(msi_map_msi, mv_ap806_sei_map_msi),
495
496 DEVMETHOD_END
497 };
498
499 static driver_t mv_ap806_sei_driver = {
500 "mv_ap806_sei",
501 mv_ap806_sei_methods,
502 sizeof(struct mv_ap806_sei_softc),
503 };
504
505 EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, 0, 0,
506 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
507