Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/annapurna/alpine/alpine_pci_msix.c
39536 views
1
/*-
2
* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
3
* All rights reserved.
4
*
5
* Developed by Semihalf.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/systm.h>
31
#include <sys/kernel.h>
32
#include <sys/lock.h>
33
#include <sys/malloc.h>
34
#include <sys/module.h>
35
#include <sys/mutex.h>
36
#include <sys/bus.h>
37
#include <sys/rman.h>
38
#include <sys/vmem.h>
39
40
#include <dev/ofw/ofw_bus.h>
41
#include <dev/ofw/ofw_bus_subr.h>
42
43
#include "msi_if.h"
44
#include "pic_if.h"
45
46
#define AL_SPI_INTR 0
47
#define AL_EDGE_HIGH 1
48
#define ERR_NOT_IN_MAP -1
49
#define IRQ_OFFSET 1
50
#define GIC_INTR_CELL_CNT 3
51
#define INTR_RANGE_COUNT 2
52
#define MAX_MSIX_COUNT 160
53
54
static int al_msix_attach(device_t);
55
static int al_msix_probe(device_t);
56
57
static msi_alloc_msi_t al_msix_alloc_msi;
58
static msi_release_msi_t al_msix_release_msi;
59
static msi_alloc_msix_t al_msix_alloc_msix;
60
static msi_release_msix_t al_msix_release_msix;
61
static msi_map_msi_t al_msix_map_msi;
62
63
static int al_find_intr_pos_in_map(device_t, struct intr_irqsrc *);
64
65
static struct ofw_compat_data compat_data[] = {
66
{"annapurna-labs,al-msix", true},
67
{"annapurna-labs,alpine-msix", true},
68
{NULL, false}
69
};
70
71
/*
72
* Bus interface definitions.
73
*/
74
static device_method_t al_msix_methods[] = {
75
DEVMETHOD(device_probe, al_msix_probe),
76
DEVMETHOD(device_attach, al_msix_attach),
77
78
/* Interrupt controller interface */
79
DEVMETHOD(msi_alloc_msi, al_msix_alloc_msi),
80
DEVMETHOD(msi_release_msi, al_msix_release_msi),
81
DEVMETHOD(msi_alloc_msix, al_msix_alloc_msix),
82
DEVMETHOD(msi_release_msix, al_msix_release_msix),
83
DEVMETHOD(msi_map_msi, al_msix_map_msi),
84
85
DEVMETHOD_END
86
};
87
88
struct al_msix_softc {
89
bus_addr_t base_addr;
90
struct resource *res;
91
uint32_t irq_min;
92
uint32_t irq_max;
93
uint32_t irq_count;
94
struct mtx msi_mtx;
95
vmem_t *irq_alloc;
96
device_t gic_dev;
97
/* Table of isrcs maps isrc pointer to vmem_alloc'd irq number */
98
struct intr_irqsrc *isrcs[MAX_MSIX_COUNT];
99
};
100
101
static driver_t al_msix_driver = {
102
"al_msix",
103
al_msix_methods,
104
sizeof(struct al_msix_softc),
105
};
106
107
DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, 0, 0);
108
DRIVER_MODULE(al_msix, simplebus, al_msix_driver, 0, 0);
109
110
MALLOC_DECLARE(M_AL_MSIX);
111
MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX");
112
113
static int
114
al_msix_probe(device_t dev)
115
{
116
117
if (!ofw_bus_status_okay(dev))
118
return (ENXIO);
119
120
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
121
return (ENXIO);
122
123
device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
124
return (BUS_PROBE_DEFAULT);
125
}
126
127
static int
128
al_msix_attach(device_t dev)
129
{
130
struct al_msix_softc *sc;
131
device_t gic_dev;
132
phandle_t iparent;
133
phandle_t node;
134
intptr_t xref;
135
int interrupts[INTR_RANGE_COUNT];
136
int nintr, i, rid;
137
uint32_t icells, *intr;
138
139
sc = device_get_softc(dev);
140
141
node = ofw_bus_get_node(dev);
142
xref = OF_xref_from_node(node);
143
OF_device_register_xref(xref, dev);
144
145
rid = 0;
146
sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
147
if (sc->res == NULL) {
148
device_printf(dev, "Failed to allocate resource\n");
149
return (ENXIO);
150
}
151
152
sc->base_addr = (bus_addr_t)rman_get_start(sc->res);
153
154
/* Register this device to handle MSI interrupts */
155
if (intr_msi_register(dev, xref) != 0) {
156
device_printf(dev, "could not register MSI-X controller\n");
157
return (ENXIO);
158
}
159
else
160
device_printf(dev, "MSI-X controller registered\n");
161
162
/* Find root interrupt controller */
163
iparent = ofw_bus_find_iparent(node);
164
if (iparent == 0) {
165
device_printf(dev, "No interrupt-parrent found. "
166
"Error in DTB\n");
167
return (ENXIO);
168
} else {
169
/* While at parent - store interrupt cells prop */
170
if (OF_searchencprop(OF_node_from_xref(iparent),
171
"#interrupt-cells", &icells, sizeof(icells)) == -1) {
172
device_printf(dev, "DTB: Missing #interrupt-cells "
173
"property in GIC node\n");
174
return (ENXIO);
175
}
176
}
177
178
gic_dev = OF_device_from_xref(iparent);
179
if (gic_dev == NULL) {
180
device_printf(dev, "Cannot find GIC device\n");
181
return (ENXIO);
182
}
183
sc->gic_dev = gic_dev;
184
185
/* Manually read range of interrupts from DTB */
186
nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr),
187
(void **)&intr);
188
if (nintr == 0) {
189
device_printf(dev, "Cannot read interrupts prop from DTB\n");
190
return (ENXIO);
191
} else if ((nintr / icells) != INTR_RANGE_COUNT) {
192
/* Supposed to have min and max value only */
193
device_printf(dev, "Unexpected count of interrupts "
194
"in DTB node\n");
195
return (EINVAL);
196
}
197
198
/* Read interrupt range values */
199
for (i = 0; i < INTR_RANGE_COUNT; i++)
200
interrupts[i] = intr[(i * icells) + IRQ_OFFSET];
201
202
sc->irq_min = interrupts[0];
203
sc->irq_max = interrupts[1];
204
sc->irq_count = (sc->irq_max - sc->irq_min + 1);
205
206
if (sc->irq_count > MAX_MSIX_COUNT) {
207
device_printf(dev, "Available MSI-X count exceeds buffer size."
208
" Capping to %d\n", MAX_MSIX_COUNT);
209
sc->irq_count = MAX_MSIX_COUNT;
210
}
211
212
mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
213
214
sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count,
215
1, 0, M_FIRSTFIT | M_WAITOK);
216
217
device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max);
218
219
bus_attach_children(dev);
220
return (0);
221
}
222
223
static int
224
al_find_intr_pos_in_map(device_t dev, struct intr_irqsrc *isrc)
225
{
226
struct al_msix_softc *sc;
227
int i;
228
229
sc = device_get_softc(dev);
230
for (i = 0; i < MAX_MSIX_COUNT; i++)
231
if (sc->isrcs[i] == isrc)
232
return (i);
233
return (ERR_NOT_IN_MAP);
234
}
235
236
static int
237
al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
238
uint64_t *addr, uint32_t *data)
239
{
240
struct al_msix_softc *sc;
241
int i, spi;
242
243
sc = device_get_softc(dev);
244
245
i = al_find_intr_pos_in_map(dev, isrc);
246
if (i == ERR_NOT_IN_MAP)
247
return (EINVAL);
248
249
spi = sc->irq_min + i;
250
251
/*
252
* MSIX message address format:
253
* [63:20] - MSIx TBAR
254
* Same value as the MSIx Translation Base Address Register
255
* [19] - WFE_EXIT
256
* Once set by MSIx message, an EVENTI is signal to the CPUs
257
* cluster specified by ‘Local GIC Target List’
258
* [18:17] - Target GIC ID
259
* Specifies which IO-GIC (external shared GIC) is targeted
260
* 0: Local GIC, as specified by the Local GIC Target List
261
* 1: IO-GIC 0
262
* 2: Reserved
263
* 3: Reserved
264
* [16:13] - Local GIC Target List
265
* Specifies the Local GICs list targeted by this MSIx
266
* message.
267
* [16] If set, SPIn is set in Cluster 0 local GIC
268
* [15:13] Reserved
269
* [15] If set, SPIn is set in Cluster 1 local GIC
270
* [14] If set, SPIn is set in Cluster 2 local GIC
271
* [13] If set, SPIn is set in Cluster 3 local GIC
272
* [12:3] - SPIn
273
* Specifies the SPI (Shared Peripheral Interrupt) index to
274
* be set in target GICs
275
* Notes:
276
* If targeting any local GIC than only SPI[249:0] are valid
277
* [2] - Function vector
278
* MSI Data vector extension hint
279
* [1:0] - Reserved
280
* Must be set to zero
281
*/
282
*addr = (uint64_t)sc->base_addr + (uint64_t)((1 << 16) + (spi << 3));
283
*data = 0;
284
285
if (bootverbose)
286
device_printf(dev, "MSI mapping: SPI: %d addr: %jx data: %x\n",
287
spi, (uintmax_t)*addr, *data);
288
return (0);
289
}
290
291
static int
292
al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount,
293
device_t *pic, struct intr_irqsrc **srcs)
294
{
295
struct intr_map_data_fdt *fdt_data;
296
struct al_msix_softc *sc;
297
vmem_addr_t irq_base;
298
int error;
299
u_int i, j;
300
301
sc = device_get_softc(dev);
302
303
if ((powerof2(count) == 0) || (count > 8))
304
return (EINVAL);
305
306
if (vmem_alloc(sc->irq_alloc, count, M_FIRSTFIT | M_NOWAIT,
307
&irq_base) != 0)
308
return (ENOMEM);
309
310
/* Fabricate OFW data to get ISRC from GIC and return it */
311
fdt_data = malloc(sizeof(*fdt_data) +
312
GIC_INTR_CELL_CNT * sizeof(pcell_t), M_AL_MSIX, M_WAITOK);
313
fdt_data->hdr.type = INTR_MAP_DATA_FDT;
314
fdt_data->iparent = 0;
315
fdt_data->ncells = GIC_INTR_CELL_CNT;
316
fdt_data->cells[0] = AL_SPI_INTR; /* code for SPI interrupt */
317
fdt_data->cells[1] = 0; /* SPI number (uninitialized) */
318
fdt_data->cells[2] = AL_EDGE_HIGH; /* trig = edge, pol = high */
319
320
mtx_lock(&sc->msi_mtx);
321
322
for (i = irq_base; i < irq_base + count; i++) {
323
fdt_data->cells[1] = sc->irq_min + i;
324
error = PIC_MAP_INTR(sc->gic_dev,
325
(struct intr_map_data *)fdt_data, srcs);
326
if (error) {
327
for (j = irq_base; j < i; j++)
328
sc->isrcs[j] = NULL;
329
mtx_unlock(&sc->msi_mtx);
330
vmem_free(sc->irq_alloc, irq_base, count);
331
free(fdt_data, M_AL_MSIX);
332
return (error);
333
}
334
335
sc->isrcs[i] = *srcs;
336
srcs++;
337
}
338
339
mtx_unlock(&sc->msi_mtx);
340
free(fdt_data, M_AL_MSIX);
341
342
if (bootverbose)
343
device_printf(dev,
344
"MSI-X allocation: start SPI %d, count %d\n",
345
(int)irq_base + sc->irq_min, count);
346
347
*pic = sc->gic_dev;
348
349
return (0);
350
}
351
352
static int
353
al_msix_release_msi(device_t dev, device_t child, int count,
354
struct intr_irqsrc **srcs)
355
{
356
struct al_msix_softc *sc;
357
int i, pos;
358
359
sc = device_get_softc(dev);
360
361
mtx_lock(&sc->msi_mtx);
362
363
pos = al_find_intr_pos_in_map(dev, *srcs);
364
vmem_free(sc->irq_alloc, pos, count);
365
for (i = 0; i < count; i++) {
366
pos = al_find_intr_pos_in_map(dev, *srcs);
367
if (pos != ERR_NOT_IN_MAP)
368
sc->isrcs[pos] = NULL;
369
srcs++;
370
}
371
372
mtx_unlock(&sc->msi_mtx);
373
374
return (0);
375
}
376
377
static int
378
al_msix_alloc_msix(device_t dev, device_t child, device_t *pic,
379
struct intr_irqsrc **isrcp)
380
{
381
382
return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp));
383
}
384
385
static int
386
al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc)
387
{
388
389
return (al_msix_release_msi(dev, child, 1, &isrc));
390
}
391
392