Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/mv/mv_ap806_sei.c
39536 views
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/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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
402
mv_ap806_sei_detach(device_t dev)
403
{
404
405
return (EBUSY);
406
}
407
408
static int
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
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
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
507