Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/mv/mv_ap806_gicp.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2018 Rubicon Communications, LLC (Netgate)
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
#include <sys/param.h>
29
#include <sys/systm.h>
30
#include <sys/bus.h>
31
32
#include <sys/bitset.h>
33
#include <sys/kernel.h>
34
#include <sys/module.h>
35
#include <sys/rman.h>
36
#include <sys/lock.h>
37
#include <sys/mutex.h>
38
39
#include <machine/bus.h>
40
#include <machine/resource.h>
41
#include <machine/intr.h>
42
43
#include <dev/fdt/simplebus.h>
44
45
#include <dev/ofw/ofw_bus.h>
46
#include <dev/ofw/ofw_bus_subr.h>
47
48
#include <arm/arm/gic_common.h>
49
50
#include <dt-bindings/interrupt-controller/irq.h>
51
52
#include "msi_if.h"
53
#include "pic_if.h"
54
55
#define MV_AP806_GICP_MAX_NIRQS 207
56
57
MALLOC_DECLARE(M_GICP);
58
MALLOC_DEFINE(M_GICP, "gicp", "Marvell gicp driver");
59
60
struct mv_ap806_gicp_softc {
61
device_t dev;
62
device_t parent;
63
struct resource *res;
64
65
ssize_t spi_ranges_cnt;
66
uint32_t *spi_ranges;
67
struct intr_map_data_fdt *parent_map_data;
68
69
ssize_t msi_bitmap_size; /* Nr of bits in the bitmap. */
70
BITSET_DEFINE_VAR() *msi_bitmap;
71
};
72
73
static struct ofw_compat_data compat_data[] = {
74
{"marvell,ap806-gicp", 1},
75
{NULL, 0}
76
};
77
78
#define RD4(sc, reg) bus_read_4((sc)->res, (reg))
79
#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val))
80
81
static msi_alloc_msi_t mv_ap806_gicp_alloc_msi;
82
static msi_release_msi_t mv_ap806_gicp_release_msi;
83
static msi_map_msi_t mv_ap806_gicp_map_msi;
84
85
static int
86
mv_ap806_gicp_probe(device_t dev)
87
{
88
89
if (!ofw_bus_status_okay(dev))
90
return (ENXIO);
91
92
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
93
return (ENXIO);
94
95
device_set_desc(dev, "Marvell GICP");
96
return (BUS_PROBE_DEFAULT);
97
}
98
99
static int
100
mv_ap806_gicp_attach(device_t dev)
101
{
102
struct mv_ap806_gicp_softc *sc;
103
phandle_t node, xref, intr_parent;
104
int i, rid;
105
106
sc = device_get_softc(dev);
107
sc->dev = dev;
108
node = ofw_bus_get_node(dev);
109
110
/* Look for our parent */
111
if ((intr_parent = ofw_bus_find_iparent(node)) == 0) {
112
device_printf(dev,
113
"Cannot find our parent interrupt controller\n");
114
return (ENXIO);
115
}
116
if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) {
117
device_printf(dev,
118
"cannot find parent interrupt controller device\n");
119
return (ENXIO);
120
}
121
122
rid = 0;
123
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
124
if (sc->res == NULL) {
125
device_printf(dev, "cannot allocate resources for device\n");
126
return (ENXIO);
127
}
128
129
sc->spi_ranges_cnt = OF_getencprop_alloc_multi(node, "marvell,spi-ranges",
130
sizeof(*sc->spi_ranges), (void **)&sc->spi_ranges);
131
132
sc->msi_bitmap_size = 0;
133
for (i = 0; i < sc->spi_ranges_cnt; i += 2)
134
sc->msi_bitmap_size += sc->spi_ranges[i + 1];
135
136
/*
137
* Create a bitmap of all MSIs that we have.
138
* Each has a correspoding SPI in the GIC.
139
* It will be used to dynamically allocate IRQs when requested.
140
*/
141
sc->msi_bitmap = BITSET_ALLOC(sc->msi_bitmap_size, M_GICP, M_WAITOK);
142
BIT_FILL(sc->msi_bitmap_size, sc->msi_bitmap); /* 1 - available, 0 - used. */
143
144
xref = OF_xref_from_node(node);
145
if (intr_pic_register(dev, xref) == NULL) {
146
device_printf(dev, "Cannot register GICP\n");
147
return (ENXIO);
148
}
149
/* Allocate GIC compatible mapping entry (3 cells) */
150
sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
151
INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) +
152
+ 3 * sizeof(phandle_t), M_WAITOK | M_ZERO);
153
OF_device_register_xref(xref, dev);
154
155
return (0);
156
}
157
158
static int
159
mv_ap806_gicp_detach(device_t dev)
160
{
161
162
return (EBUSY);
163
}
164
165
static uint32_t
166
mv_ap806_gicp_msi_to_spi(struct mv_ap806_gicp_softc *sc, int irq)
167
{
168
int i;
169
170
for (i = 0; i < sc->spi_ranges_cnt; i += 2) {
171
if (irq < sc->spi_ranges[i + 1]) {
172
irq += sc->spi_ranges[i];
173
break;
174
}
175
irq -= sc->spi_ranges[i + 1];
176
}
177
178
return (irq - GIC_FIRST_SPI);
179
}
180
181
static uint32_t
182
mv_ap806_gicp_irq_to_msi(struct mv_ap806_gicp_softc *sc, int irq)
183
{
184
int i;
185
186
for (i = 0; i < sc->spi_ranges_cnt; i += 2) {
187
if (irq >= sc->spi_ranges[i] &&
188
irq - sc->spi_ranges[i] < sc->spi_ranges[i + 1]) {
189
irq -= sc->spi_ranges[i];
190
break;
191
}
192
}
193
194
return (irq);
195
}
196
197
static struct intr_map_data *
198
mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc *sc,
199
struct intr_map_data *data)
200
{
201
struct intr_map_data_fdt *daf;
202
uint32_t irq_num;
203
204
daf = (struct intr_map_data_fdt *)data;
205
if (daf->ncells != 2)
206
return (NULL);
207
208
irq_num = daf->cells[0];
209
if (irq_num >= MV_AP806_GICP_MAX_NIRQS)
210
return (NULL);
211
212
/* Construct GIC compatible mapping. */
213
sc->parent_map_data->ncells = 3;
214
sc->parent_map_data->cells[0] = 0; /* SPI */
215
sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, irq_num);
216
sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH;
217
218
return ((struct intr_map_data *)sc->parent_map_data);
219
}
220
221
static int
222
mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc,
223
struct resource *res, struct intr_map_data *data)
224
{
225
struct mv_ap806_gicp_softc *sc;
226
227
sc = device_get_softc(dev);
228
data = mv_ap806_gicp_convert_map_data(sc, data);
229
if (data == NULL)
230
return (EINVAL);
231
232
return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data));
233
}
234
235
static void
236
mv_ap806_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc)
237
{
238
struct mv_ap806_gicp_softc *sc;
239
240
sc = device_get_softc(dev);
241
242
PIC_ENABLE_INTR(sc->parent, isrc);
243
}
244
245
static void
246
mv_ap806_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc)
247
{
248
struct mv_ap806_gicp_softc *sc;
249
250
sc = device_get_softc(dev);
251
252
PIC_DISABLE_INTR(sc->parent, isrc);
253
}
254
255
static int
256
mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data,
257
struct intr_irqsrc **isrcp)
258
{
259
260
panic("%s: MSI interface has to be used to map an interrupt.\n",
261
__func__);
262
}
263
264
static int
265
mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc,
266
struct resource *res, struct intr_map_data *data)
267
{
268
struct mv_ap806_gicp_softc *sc;
269
270
sc = device_get_softc(dev);
271
272
data = mv_ap806_gicp_convert_map_data(sc, data);
273
if (data == NULL)
274
return (EINVAL);
275
276
return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data));
277
}
278
279
static int
280
mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc,
281
struct resource *res, struct intr_map_data *data)
282
{
283
struct mv_ap806_gicp_softc *sc;
284
285
sc = device_get_softc(dev);
286
data = mv_ap806_gicp_convert_map_data(sc, data);
287
if (data == NULL)
288
return (EINVAL);
289
290
return (PIC_SETUP_INTR(sc->parent, isrc, res, data));
291
}
292
293
static int
294
mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
295
struct resource *res, struct intr_map_data *data)
296
{
297
struct mv_ap806_gicp_softc *sc;
298
299
sc = device_get_softc(dev);
300
data = mv_ap806_gicp_convert_map_data(sc, data);
301
if (data == NULL)
302
return (EINVAL);
303
304
return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data));
305
}
306
307
static void
308
mv_ap806_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
309
{
310
struct mv_ap806_gicp_softc *sc;
311
312
sc = device_get_softc(dev);
313
314
PIC_PRE_ITHREAD(sc->parent, isrc);
315
}
316
317
static void
318
mv_ap806_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc)
319
{
320
struct mv_ap806_gicp_softc *sc;
321
322
sc = device_get_softc(dev);
323
324
PIC_POST_ITHREAD(sc->parent, isrc);
325
}
326
327
static void
328
mv_ap806_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc)
329
{
330
struct mv_ap806_gicp_softc *sc;
331
332
sc = device_get_softc(dev);
333
334
PIC_POST_FILTER(sc->parent, isrc);
335
}
336
337
static int
338
mv_ap806_gicp_alloc_msi(device_t dev, device_t child, int count, int maxcount,
339
device_t *pic, struct intr_irqsrc **srcs)
340
{
341
struct mv_ap806_gicp_softc *sc;
342
int i, ret, vector;
343
344
sc = device_get_softc(dev);
345
346
for (i = 0; i < count; i++) {
347
/*
348
* Find first available vector represented by first set bit
349
* in the bitmap. BIT_FFS starts the count from 1, 0 means
350
* that nothing was found.
351
*/
352
vector = BIT_FFS(sc->msi_bitmap_size, sc->msi_bitmap);
353
if (vector == 0) {
354
ret = ENOMEM;
355
i--;
356
goto fail;
357
}
358
vector--;
359
BIT_CLR(sc->msi_bitmap_size, vector, sc->msi_bitmap);
360
361
/* Create GIC compatible SPI interrupt description. */
362
sc->parent_map_data->ncells = 3;
363
sc->parent_map_data->cells[0] = 0; /* SPI */
364
sc->parent_map_data->cells[1] = mv_ap806_gicp_msi_to_spi(sc, vector);
365
sc->parent_map_data->cells[2] = IRQ_TYPE_LEVEL_HIGH;
366
367
ret = PIC_MAP_INTR(sc->parent,
368
(struct intr_map_data *)sc->parent_map_data,
369
&srcs[i]);
370
if (ret != 0)
371
goto fail;
372
373
srcs[i]->isrc_dev = dev;
374
}
375
376
return (0);
377
fail:
378
mv_ap806_gicp_release_msi(dev, child, i + 1, srcs);
379
return (ret);
380
}
381
382
static int
383
mv_ap806_gicp_release_msi(device_t dev, device_t child, int count,
384
struct intr_irqsrc **srcs)
385
{
386
struct mv_ap806_gicp_softc *sc;
387
int i;
388
389
sc = device_get_softc(dev);
390
391
for (i = 0; i < count; i++) {
392
BIT_SET(sc->msi_bitmap_size,
393
mv_ap806_gicp_irq_to_msi(sc, srcs[i]->isrc_irq),
394
sc->msi_bitmap);
395
}
396
397
return (0);
398
}
399
400
static int
401
mv_ap806_gicp_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
402
uint64_t *addr, uint32_t *data)
403
{
404
struct mv_ap806_gicp_softc *sc;
405
406
sc = device_get_softc(dev);
407
408
*addr = rman_get_start(sc->res);
409
*data = mv_ap806_gicp_irq_to_msi(sc, isrc->isrc_irq);
410
411
return (0);
412
}
413
414
static device_method_t mv_ap806_gicp_methods[] = {
415
/* Device interface */
416
DEVMETHOD(device_probe, mv_ap806_gicp_probe),
417
DEVMETHOD(device_attach, mv_ap806_gicp_attach),
418
DEVMETHOD(device_detach, mv_ap806_gicp_detach),
419
420
/* Interrupt controller interface */
421
DEVMETHOD(pic_activate_intr, mv_ap806_gicp_activate_intr),
422
DEVMETHOD(pic_disable_intr, mv_ap806_gicp_disable_intr),
423
DEVMETHOD(pic_enable_intr, mv_ap806_gicp_enable_intr),
424
DEVMETHOD(pic_map_intr, mv_ap806_gicp_map_intr),
425
DEVMETHOD(pic_deactivate_intr, mv_ap806_gicp_deactivate_intr),
426
DEVMETHOD(pic_setup_intr, mv_ap806_gicp_setup_intr),
427
DEVMETHOD(pic_teardown_intr, mv_ap806_gicp_teardown_intr),
428
DEVMETHOD(pic_post_filter, mv_ap806_gicp_post_filter),
429
DEVMETHOD(pic_post_ithread, mv_ap806_gicp_post_ithread),
430
DEVMETHOD(pic_pre_ithread, mv_ap806_gicp_pre_ithread),
431
432
/* MSI interface */
433
DEVMETHOD(msi_alloc_msi, mv_ap806_gicp_alloc_msi),
434
DEVMETHOD(msi_release_msi, mv_ap806_gicp_release_msi),
435
DEVMETHOD(msi_map_msi, mv_ap806_gicp_map_msi),
436
437
DEVMETHOD_END
438
};
439
440
static driver_t mv_ap806_gicp_driver = {
441
"mv_ap806_gicp",
442
mv_ap806_gicp_methods,
443
sizeof(struct mv_ap806_gicp_softc),
444
};
445
446
EARLY_DRIVER_MODULE(mv_ap806_gicp, simplebus, mv_ap806_gicp_driver, 0, 0,
447
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
448
449