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(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 }
395