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