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