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
al_msix_probe(device_t dev)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
al_msix_attach(device_t dev)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
al_find_intr_pos_in_map(device_t dev,struct intr_irqsrc * isrc)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
al_msix_map_msi(device_t dev,device_t child,struct intr_irqsrc * isrc,uint64_t * addr,uint32_t * data)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
al_msix_alloc_msi(device_t dev,device_t child,int count,int maxcount,device_t * pic,struct intr_irqsrc ** srcs)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
al_msix_release_msi(device_t dev,device_t child,int count,struct intr_irqsrc ** srcs)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
al_msix_alloc_msix(device_t dev,device_t child,device_t * pic,struct intr_irqsrc ** isrcp)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
al_msix_release_msix(device_t dev,device_t child,struct intr_irqsrc * isrc)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