xref: /freebsd/sys/arm/annapurna/alpine/alpine_pci_msix.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/lock.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
36 #include <sys/mutex.h>
37 #include <sys/bus.h>
38 #include <sys/rman.h>
39 #include <sys/vmem.h>
40 
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 
44 #include "msi_if.h"
45 #include "pic_if.h"
46 
47 #define	AL_SPI_INTR		0
48 #define	AL_EDGE_HIGH		1
49 #define	ERR_NOT_IN_MAP		-1
50 #define	IRQ_OFFSET		1
51 #define	GIC_INTR_CELL_CNT	3
52 #define	INTR_RANGE_COUNT	2
53 #define	MAX_MSIX_COUNT		160
54 
55 static int al_msix_attach(device_t);
56 static int al_msix_probe(device_t);
57 
58 static msi_alloc_msi_t al_msix_alloc_msi;
59 static msi_release_msi_t al_msix_release_msi;
60 static msi_alloc_msix_t al_msix_alloc_msix;
61 static msi_release_msix_t al_msix_release_msix;
62 static msi_map_msi_t al_msix_map_msi;
63 
64 static int al_find_intr_pos_in_map(device_t, struct intr_irqsrc *);
65 
66 static struct ofw_compat_data compat_data[] = {
67 	{"annapurna-labs,al-msix",	true},
68 	{"annapurna-labs,alpine-msix",	true},
69 	{NULL,				false}
70 };
71 
72 /*
73  * Bus interface definitions.
74  */
75 static device_method_t al_msix_methods[] = {
76 	DEVMETHOD(device_probe,		al_msix_probe),
77 	DEVMETHOD(device_attach,	al_msix_attach),
78 
79 	/* Interrupt controller interface */
80 	DEVMETHOD(msi_alloc_msi,	al_msix_alloc_msi),
81 	DEVMETHOD(msi_release_msi,	al_msix_release_msi),
82 	DEVMETHOD(msi_alloc_msix,	al_msix_alloc_msix),
83 	DEVMETHOD(msi_release_msix,	al_msix_release_msix),
84 	DEVMETHOD(msi_map_msi,		al_msix_map_msi),
85 
86 	DEVMETHOD_END
87 };
88 
89 struct al_msix_softc {
90 	bus_addr_t	base_addr;
91 	struct resource	*res;
92 	uint32_t	irq_min;
93 	uint32_t	irq_max;
94 	uint32_t	irq_count;
95 	struct mtx	msi_mtx;
96 	vmem_t		*irq_alloc;
97 	device_t	gic_dev;
98 	/* Table of isrcs maps isrc pointer to vmem_alloc'd irq number */
99 	struct intr_irqsrc	*isrcs[MAX_MSIX_COUNT];
100 };
101 
102 static driver_t al_msix_driver = {
103 	"al_msix",
104 	al_msix_methods,
105 	sizeof(struct al_msix_softc),
106 };
107 
108 DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, 0, 0);
109 DRIVER_MODULE(al_msix, simplebus, al_msix_driver, 0, 0);
110 
111 MALLOC_DECLARE(M_AL_MSIX);
112 MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX");
113 
114 static int
115 al_msix_probe(device_t dev)
116 {
117 
118 	if (!ofw_bus_status_okay(dev))
119 		return (ENXIO);
120 
121 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
122 		return (ENXIO);
123 
124 	device_set_desc(dev, "Annapurna-Labs MSI-X Controller");
125 	return (BUS_PROBE_DEFAULT);
126 }
127 
128 static int
129 al_msix_attach(device_t dev)
130 {
131 	struct al_msix_softc	*sc;
132 	device_t		gic_dev;
133 	phandle_t		iparent;
134 	phandle_t		node;
135 	intptr_t		xref;
136 	int			interrupts[INTR_RANGE_COUNT];
137 	int			nintr, i, rid;
138 	uint32_t		icells, *intr;
139 
140 	sc = device_get_softc(dev);
141 
142 	node = ofw_bus_get_node(dev);
143 	xref = OF_xref_from_node(node);
144 	OF_device_register_xref(xref, dev);
145 
146 	rid = 0;
147 	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
148 	if (sc->res == NULL) {
149 		device_printf(dev, "Failed to allocate resource\n");
150 		return (ENXIO);
151 	}
152 
153 	sc->base_addr = (bus_addr_t)rman_get_start(sc->res);
154 
155 	/* Register this device to handle MSI interrupts */
156 	if (intr_msi_register(dev, xref) != 0) {
157 		device_printf(dev, "could not register MSI-X controller\n");
158 		return (ENXIO);
159 	}
160 	else
161 		device_printf(dev, "MSI-X controller registered\n");
162 
163 	/* Find root interrupt controller */
164 	iparent = ofw_bus_find_iparent(node);
165 	if (iparent == 0) {
166 		device_printf(dev, "No interrupt-parrent found. "
167 				"Error in DTB\n");
168 		return (ENXIO);
169 	} else {
170 		/* While at parent - store interrupt cells prop */
171 		if (OF_searchencprop(OF_node_from_xref(iparent),
172 		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
173 			device_printf(dev, "DTB: Missing #interrupt-cells "
174 			    "property in GIC node\n");
175 			return (ENXIO);
176 		}
177 	}
178 
179 	gic_dev = OF_device_from_xref(iparent);
180 	if (gic_dev == NULL) {
181 		device_printf(dev, "Cannot find GIC device\n");
182 		return (ENXIO);
183 	}
184 	sc->gic_dev = gic_dev;
185 
186 	/* Manually read range of interrupts from DTB */
187 	nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr),
188 	    (void **)&intr);
189 	if (nintr == 0) {
190 		device_printf(dev, "Cannot read interrupts prop from DTB\n");
191 		return (ENXIO);
192 	} else if ((nintr / icells) != INTR_RANGE_COUNT) {
193 		/* Supposed to have min and max value only */
194 		device_printf(dev, "Unexpected count of interrupts "
195 				"in DTB node\n");
196 		return (EINVAL);
197 	}
198 
199 	/* Read interrupt range values */
200 	for (i = 0; i < INTR_RANGE_COUNT; i++)
201 		interrupts[i] = intr[(i * icells) + IRQ_OFFSET];
202 
203 	sc->irq_min = interrupts[0];
204 	sc->irq_max = interrupts[1];
205 	sc->irq_count = (sc->irq_max - sc->irq_min + 1);
206 
207 	if (sc->irq_count > MAX_MSIX_COUNT) {
208 		device_printf(dev, "Available MSI-X count exceeds buffer size."
209 				" Capping to %d\n", MAX_MSIX_COUNT);
210 		sc->irq_count = MAX_MSIX_COUNT;
211 	}
212 
213 	mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF);
214 
215 	sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count,
216 	    1, 0, M_FIRSTFIT | M_WAITOK);
217 
218 	device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max);
219 
220 	return (bus_generic_attach(dev));
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