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