xref: /freebsd/sys/dev/xdma/xdma_fdt_test.c (revision c697fb7f)
1 /*-
2  * Copyright (c) 2016 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * This software was developed by SRI International and the University of
6  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
7  * ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 /* xDMA memcpy test driver. */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/conf.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/kthread.h>
42 #include <sys/module.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/resource.h>
46 #include <sys/rman.h>
47 
48 #include <machine/bus.h>
49 
50 #include <dev/xdma/xdma.h>
51 
52 #include <dev/fdt/fdt_common.h>
53 #include <dev/ofw/ofw_bus.h>
54 #include <dev/ofw/ofw_bus_subr.h>
55 
56 /*
57  * To use this test add a compatible node to your dts, e.g.
58  *
59  * 	xdma_test {
60  *		compatible = "freebsd,xdma-test";
61  *
62  * 		dmas = <&dma 0 0 0xffffffff>;
63  * 		dma-names = "test";
64  *	};
65  */
66 
67 struct xdmatest_softc {
68 	device_t		dev;
69 	xdma_controller_t	*xdma;
70 	xdma_channel_t		*xchan;
71 	void			*ih;
72 	struct intr_config_hook config_intrhook;
73 	char			*src;
74 	char			*dst;
75 	uint32_t		len;
76 	uintptr_t		src_phys;
77 	uintptr_t		dst_phys;
78 	bus_dma_tag_t		src_dma_tag;
79 	bus_dmamap_t		src_dma_map;
80 	bus_dma_tag_t		dst_dma_tag;
81 	bus_dmamap_t		dst_dma_map;
82 	struct mtx		mtx;
83 	int			done;
84 	struct proc		*newp;
85 	struct xdma_request	req;
86 };
87 
88 static int xdmatest_probe(device_t dev);
89 static int xdmatest_attach(device_t dev);
90 static int xdmatest_detach(device_t dev);
91 
92 static int
93 xdmatest_intr(void *arg)
94 {
95 	struct xdmatest_softc *sc;
96 
97 	sc = arg;
98 
99 	sc->done = 1;
100 
101 	mtx_lock(&sc->mtx);
102 	wakeup(sc);
103 	mtx_unlock(&sc->mtx);
104 
105 	return (0);
106 }
107 
108 static void
109 xdmatest_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
110 {
111 	bus_addr_t *addr;
112 
113 	if (err)
114 		return;
115 
116 	addr = (bus_addr_t*)arg;
117 	*addr = segs[0].ds_addr;
118 }
119 
120 static int
121 xdmatest_alloc_test_memory(struct xdmatest_softc *sc)
122 {
123 	int err;
124 
125 	sc->len = (0x1000000 - 8); /* 16mb */
126 	sc->len = 8;
127 
128 	/* Source memory. */
129 
130 	err = bus_dma_tag_create(
131 	    bus_get_dma_tag(sc->dev),
132 	    1024, 0,			/* alignment, boundary */
133 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
134 	    BUS_SPACE_MAXADDR,		/* highaddr */
135 	    NULL, NULL,			/* filter, filterarg */
136 	    sc->len, 1,			/* maxsize, nsegments*/
137 	    sc->len, 0,			/* maxsegsize, flags */
138 	    NULL, NULL,			/* lockfunc, lockarg */
139 	    &sc->src_dma_tag);
140 	if (err) {
141 		device_printf(sc->dev,
142 		    "%s: Can't create bus_dma tag.\n", __func__);
143 		return (-1);
144 	}
145 
146 	err = bus_dmamem_alloc(sc->src_dma_tag, (void **)&sc->src,
147 	    BUS_DMA_WAITOK | BUS_DMA_COHERENT, &sc->src_dma_map);
148 	if (err) {
149 		device_printf(sc->dev,
150 		    "%s: Can't allocate memory.\n", __func__);
151 		return (-1);
152 	}
153 
154 	err = bus_dmamap_load(sc->src_dma_tag, sc->src_dma_map, sc->src,
155 	    sc->len, xdmatest_dmamap_cb, &sc->src_phys, BUS_DMA_WAITOK);
156 	if (err) {
157 		device_printf(sc->dev,
158 		    "%s: Can't load DMA map.\n", __func__);
159 		return (-1);
160 	}
161 
162 	/* Destination memory. */
163 
164 	err = bus_dma_tag_create(
165 	    bus_get_dma_tag(sc->dev),
166 	    1024, 0,			/* alignment, boundary */
167 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
168 	    BUS_SPACE_MAXADDR,		/* highaddr */
169 	    NULL, NULL,			/* filter, filterarg */
170 	    sc->len, 1,			/* maxsize, nsegments*/
171 	    sc->len, 0,			/* maxsegsize, flags */
172 	    NULL, NULL,			/* lockfunc, lockarg */
173 	    &sc->dst_dma_tag);
174 	if (err) {
175 		device_printf(sc->dev,
176 		    "%s: Can't create bus_dma tag.\n", __func__);
177 		return (-1);
178 	}
179 
180 	err = bus_dmamem_alloc(sc->dst_dma_tag, (void **)&sc->dst,
181 	    BUS_DMA_WAITOK | BUS_DMA_COHERENT, &sc->dst_dma_map);
182 	if (err) {
183 		device_printf(sc->dev,
184 		    "%s: Can't allocate memory.\n", __func__);
185 		return (-1);
186 	}
187 
188 	err = bus_dmamap_load(sc->dst_dma_tag, sc->dst_dma_map, sc->dst,
189 	    sc->len, xdmatest_dmamap_cb, &sc->dst_phys, BUS_DMA_WAITOK);
190 	if (err) {
191 		device_printf(sc->dev,
192 		    "%s: Can't load DMA map.\n", __func__);
193 		return (-1);
194 	}
195 
196 	return (0);
197 }
198 
199 static int
200 xdmatest_test(struct xdmatest_softc *sc)
201 {
202 	int err;
203 	int i;
204 
205 	/* Get xDMA controller. */
206 	sc->xdma = xdma_ofw_get(sc->dev, "test");
207 	if (sc->xdma == NULL) {
208 		device_printf(sc->dev, "Can't find xDMA controller.\n");
209 		return (-1);
210 	}
211 
212 	/* Alloc xDMA virtual channel. */
213 	sc->xchan = xdma_channel_alloc(sc->xdma);
214 	if (sc->xchan == NULL) {
215 		device_printf(sc->dev, "Can't alloc virtual DMA channel.\n");
216 		return (-1);
217 	}
218 
219 	/* Setup callback. */
220 	err = xdma_setup_intr(sc->xchan, 0, xdmatest_intr, sc, &sc->ih);
221 	if (err) {
222 		device_printf(sc->dev, "Can't setup xDMA interrupt handler.\n");
223 		return (-1);
224 	}
225 
226 	/* We are going to fill memory. */
227 	bus_dmamap_sync(sc->src_dma_tag, sc->src_dma_map, BUS_DMASYNC_PREWRITE);
228 	bus_dmamap_sync(sc->dst_dma_tag, sc->dst_dma_map, BUS_DMASYNC_PREWRITE);
229 
230 	/* Fill memory. */
231 	for (i = 0; i < sc->len; i++) {
232 		sc->src[i] = (i & 0xff);
233 		sc->dst[i] = 0;
234 	}
235 
236 	sc->req.type = XR_TYPE_PHYS_ADDR;
237 	sc->req.direction = XDMA_MEM_TO_MEM;
238 	sc->req.src_addr = sc->src_phys;
239 	sc->req.dst_addr = sc->dst_phys;
240 	sc->req.src_width = 4;
241 	sc->req.dst_width = 4;
242 	sc->req.block_len = sc->len;
243 	sc->req.block_num = 1;
244 
245 	err = xdma_request(sc->xchan, sc->src_phys, sc->dst_phys, sc->len);
246 	if (err != 0) {
247 		device_printf(sc->dev, "Can't configure virtual channel.\n");
248 		return (-1);
249 	}
250 
251 	/* Start operation. */
252 	xdma_begin(sc->xchan);
253 
254 	return (0);
255 }
256 
257 static int
258 xdmatest_verify(struct xdmatest_softc *sc)
259 {
260 	int err;
261 	int i;
262 
263 	/* We have memory updated by DMA controller. */
264 	bus_dmamap_sync(sc->src_dma_tag, sc->src_dma_map, BUS_DMASYNC_POSTREAD);
265 	bus_dmamap_sync(sc->dst_dma_tag, sc->dst_dma_map, BUS_DMASYNC_POSTWRITE);
266 
267 	for (i = 0; i < sc->len; i++) {
268 		if (sc->dst[i] != sc->src[i]) {
269 			device_printf(sc->dev,
270 			    "%s: Test failed: iter %d\n", __func__, i);
271 			return (-1);
272 		}
273 	}
274 
275 	err = xdma_channel_free(sc->xchan);
276 	if (err != 0) {
277 		device_printf(sc->dev,
278 		    "%s: Test failed: can't deallocate channel.\n", __func__);
279 		return (-1);
280 	}
281 
282 	err = xdma_put(sc->xdma);
283 	if (err != 0) {
284 		device_printf(sc->dev,
285 		    "%s: Test failed: can't deallocate xDMA.\n", __func__);
286 		return (-1);
287 	}
288 
289 	return (0);
290 }
291 
292 static void
293 xdmatest_worker(void *arg)
294 {
295 	struct xdmatest_softc *sc;
296 	int timeout;
297 	int err;
298 
299 	sc = arg;
300 
301 	device_printf(sc->dev, "Worker %d started.\n",
302 	    device_get_unit(sc->dev));
303 
304 	while (1) {
305 		sc->done = 0;
306 
307 		mtx_lock(&sc->mtx);
308 
309 		if (xdmatest_test(sc) != 0) {
310 			mtx_unlock(&sc->mtx);
311 			device_printf(sc->dev,
312 			    "%s: Test failed.\n", __func__);
313 			break;
314 		}
315 
316 		timeout = 100;
317 
318 		do {
319 			mtx_sleep(sc, &sc->mtx, 0, "xdmatest_wait", hz);
320 		} while (timeout-- && sc->done == 0);
321 
322 		if (timeout != 0) {
323 			err = xdmatest_verify(sc);
324 			if (err == 0) {
325 				/* Test succeeded. */
326 				mtx_unlock(&sc->mtx);
327 				continue;
328 			}
329 		}
330 
331 		mtx_unlock(&sc->mtx);
332 		device_printf(sc->dev,
333 		    "%s: Test failed.\n", __func__);
334 		break;
335 	}
336 }
337 
338 static void
339 xdmatest_delayed_attach(void *arg)
340 {
341 	struct xdmatest_softc *sc;
342 
343 	sc = arg;
344 
345 	if (kproc_create(xdmatest_worker, (void *)sc, &sc->newp, 0, 0,
346             "xdmatest_worker") != 0) {
347 		device_printf(sc->dev,
348 		    "%s: Failed to create worker thread.\n", __func__);
349 	}
350 
351 	config_intrhook_disestablish(&sc->config_intrhook);
352 }
353 
354 static int
355 xdmatest_probe(device_t dev)
356 {
357 
358 	if (!ofw_bus_status_okay(dev))
359 		return (ENXIO);
360 
361 	if (!ofw_bus_is_compatible(dev, "freebsd,xdma-test"))
362 		return (ENXIO);
363 
364 	device_set_desc(dev, "xDMA test driver");
365 
366 	return (BUS_PROBE_DEFAULT);
367 }
368 
369 static int
370 xdmatest_attach(device_t dev)
371 {
372 	struct xdmatest_softc *sc;
373 	int err;
374 
375 	sc = device_get_softc(dev);
376 	sc->dev = dev;
377 
378 	mtx_init(&sc->mtx, device_get_nameunit(dev), "xdmatest", MTX_DEF);
379 
380 	/* Allocate test memory */
381 	err = xdmatest_alloc_test_memory(sc);
382 	if (err != 0) {
383 		device_printf(sc->dev, "Can't allocate test memory.\n");
384 		return (-1);
385 	}
386 
387 	/* We'll run test later, but before / mount. */
388 	sc->config_intrhook.ich_func = xdmatest_delayed_attach;
389 	sc->config_intrhook.ich_arg = sc;
390 	if (config_intrhook_establish(&sc->config_intrhook) != 0)
391 		device_printf(dev, "config_intrhook_establish failed\n");
392 
393 	return (0);
394 }
395 
396 static int
397 xdmatest_detach(device_t dev)
398 {
399 	struct xdmatest_softc *sc;
400 
401 	sc = device_get_softc(dev);
402 
403 	bus_dmamap_unload(sc->src_dma_tag, sc->src_dma_map);
404 	bus_dmamem_free(sc->src_dma_tag, sc->src, sc->src_dma_map);
405 	bus_dma_tag_destroy(sc->src_dma_tag);
406 
407 	bus_dmamap_unload(sc->dst_dma_tag, sc->dst_dma_map);
408 	bus_dmamem_free(sc->dst_dma_tag, sc->dst, sc->dst_dma_map);
409 	bus_dma_tag_destroy(sc->dst_dma_tag);
410 
411 	return (0);
412 }
413 
414 static device_method_t xdmatest_methods[] = {
415 	/* Device interface */
416 	DEVMETHOD(device_probe,			xdmatest_probe),
417 	DEVMETHOD(device_attach,		xdmatest_attach),
418 	DEVMETHOD(device_detach,		xdmatest_detach),
419 
420 	DEVMETHOD_END
421 };
422 
423 static driver_t xdmatest_driver = {
424 	"xdmatest",
425 	xdmatest_methods,
426 	sizeof(struct xdmatest_softc),
427 };
428 
429 static devclass_t xdmatest_devclass;
430 
431 DRIVER_MODULE(xdmatest, simplebus, xdmatest_driver, xdmatest_devclass, 0, 0);
432