xref: /freebsd/sys/dev/xdma/xdma_fdt_test.c (revision edf8578117e8844e02c0121147f45e4609b30680)
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 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/conf.h>
37 #include <sys/bus.h>
38 #include <sys/kernel.h>
39 #include <sys/kthread.h>
40 #include <sys/module.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <sys/resource.h>
44 #include <sys/rman.h>
45 
46 #include <machine/bus.h>
47 
48 #include <dev/xdma/xdma.h>
49 
50 #include <dev/fdt/fdt_common.h>
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
53 
54 /*
55  * To use this test add a compatible node to your dts, e.g.
56  *
57  * 	xdma_test {
58  *		compatible = "freebsd,xdma-test";
59  *
60  * 		dmas = <&dma 0 0 0xffffffff>;
61  * 		dma-names = "test";
62  *	};
63  */
64 
65 struct xdmatest_softc {
66 	device_t		dev;
67 	xdma_controller_t	*xdma;
68 	xdma_channel_t		*xchan;
69 	void			*ih;
70 	struct intr_config_hook config_intrhook;
71 	char			*src;
72 	char			*dst;
73 	uint32_t		len;
74 	uintptr_t		src_phys;
75 	uintptr_t		dst_phys;
76 	bus_dma_tag_t		src_dma_tag;
77 	bus_dmamap_t		src_dma_map;
78 	bus_dma_tag_t		dst_dma_tag;
79 	bus_dmamap_t		dst_dma_map;
80 	struct mtx		mtx;
81 	int			done;
82 	struct proc		*newp;
83 	struct xdma_request	req;
84 };
85 
86 static int xdmatest_probe(device_t dev);
87 static int xdmatest_attach(device_t dev);
88 static int xdmatest_detach(device_t dev);
89 
90 static int
91 xdmatest_intr(void *arg)
92 {
93 	struct xdmatest_softc *sc;
94 
95 	sc = arg;
96 
97 	sc->done = 1;
98 
99 	mtx_lock(&sc->mtx);
100 	wakeup(sc);
101 	mtx_unlock(&sc->mtx);
102 
103 	return (0);
104 }
105 
106 static void
107 xdmatest_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
108 {
109 	bus_addr_t *addr;
110 
111 	if (err)
112 		return;
113 
114 	addr = (bus_addr_t*)arg;
115 	*addr = segs[0].ds_addr;
116 }
117 
118 static int
119 xdmatest_alloc_test_memory(struct xdmatest_softc *sc)
120 {
121 	int err;
122 
123 	sc->len = (0x1000000 - 8); /* 16mb */
124 	sc->len = 8;
125 
126 	/* Source memory. */
127 
128 	err = bus_dma_tag_create(
129 	    bus_get_dma_tag(sc->dev),
130 	    1024, 0,			/* alignment, boundary */
131 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
132 	    BUS_SPACE_MAXADDR,		/* highaddr */
133 	    NULL, NULL,			/* filter, filterarg */
134 	    sc->len, 1,			/* maxsize, nsegments*/
135 	    sc->len, 0,			/* maxsegsize, flags */
136 	    NULL, NULL,			/* lockfunc, lockarg */
137 	    &sc->src_dma_tag);
138 	if (err) {
139 		device_printf(sc->dev,
140 		    "%s: Can't create bus_dma tag.\n", __func__);
141 		return (-1);
142 	}
143 
144 	err = bus_dmamem_alloc(sc->src_dma_tag, (void **)&sc->src,
145 	    BUS_DMA_WAITOK | BUS_DMA_COHERENT, &sc->src_dma_map);
146 	if (err) {
147 		device_printf(sc->dev,
148 		    "%s: Can't allocate memory.\n", __func__);
149 		return (-1);
150 	}
151 
152 	err = bus_dmamap_load(sc->src_dma_tag, sc->src_dma_map, sc->src,
153 	    sc->len, xdmatest_dmamap_cb, &sc->src_phys, BUS_DMA_WAITOK);
154 	if (err) {
155 		device_printf(sc->dev,
156 		    "%s: Can't load DMA map.\n", __func__);
157 		return (-1);
158 	}
159 
160 	/* Destination memory. */
161 
162 	err = bus_dma_tag_create(
163 	    bus_get_dma_tag(sc->dev),
164 	    1024, 0,			/* alignment, boundary */
165 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
166 	    BUS_SPACE_MAXADDR,		/* highaddr */
167 	    NULL, NULL,			/* filter, filterarg */
168 	    sc->len, 1,			/* maxsize, nsegments*/
169 	    sc->len, 0,			/* maxsegsize, flags */
170 	    NULL, NULL,			/* lockfunc, lockarg */
171 	    &sc->dst_dma_tag);
172 	if (err) {
173 		device_printf(sc->dev,
174 		    "%s: Can't create bus_dma tag.\n", __func__);
175 		return (-1);
176 	}
177 
178 	err = bus_dmamem_alloc(sc->dst_dma_tag, (void **)&sc->dst,
179 	    BUS_DMA_WAITOK | BUS_DMA_COHERENT, &sc->dst_dma_map);
180 	if (err) {
181 		device_printf(sc->dev,
182 		    "%s: Can't allocate memory.\n", __func__);
183 		return (-1);
184 	}
185 
186 	err = bus_dmamap_load(sc->dst_dma_tag, sc->dst_dma_map, sc->dst,
187 	    sc->len, xdmatest_dmamap_cb, &sc->dst_phys, BUS_DMA_WAITOK);
188 	if (err) {
189 		device_printf(sc->dev,
190 		    "%s: Can't load DMA map.\n", __func__);
191 		return (-1);
192 	}
193 
194 	return (0);
195 }
196 
197 static int
198 xdmatest_test(struct xdmatest_softc *sc)
199 {
200 	int err;
201 	int i;
202 
203 	/* Get xDMA controller. */
204 	sc->xdma = xdma_ofw_get(sc->dev, "test");
205 	if (sc->xdma == NULL) {
206 		device_printf(sc->dev, "Can't find xDMA controller.\n");
207 		return (-1);
208 	}
209 
210 	/* Alloc xDMA virtual channel. */
211 	sc->xchan = xdma_channel_alloc(sc->xdma);
212 	if (sc->xchan == NULL) {
213 		device_printf(sc->dev, "Can't alloc virtual DMA channel.\n");
214 		return (-1);
215 	}
216 
217 	/* Setup callback. */
218 	err = xdma_setup_intr(sc->xchan, 0, xdmatest_intr, sc, &sc->ih);
219 	if (err) {
220 		device_printf(sc->dev, "Can't setup xDMA interrupt handler.\n");
221 		return (-1);
222 	}
223 
224 	/* We are going to fill memory. */
225 	bus_dmamap_sync(sc->src_dma_tag, sc->src_dma_map, BUS_DMASYNC_PREWRITE);
226 	bus_dmamap_sync(sc->dst_dma_tag, sc->dst_dma_map, BUS_DMASYNC_PREWRITE);
227 
228 	/* Fill memory. */
229 	for (i = 0; i < sc->len; i++) {
230 		sc->src[i] = (i & 0xff);
231 		sc->dst[i] = 0;
232 	}
233 
234 	sc->req.type = XR_TYPE_PHYS_ADDR;
235 	sc->req.direction = XDMA_MEM_TO_MEM;
236 	sc->req.src_addr = sc->src_phys;
237 	sc->req.dst_addr = sc->dst_phys;
238 	sc->req.src_width = 4;
239 	sc->req.dst_width = 4;
240 	sc->req.block_len = sc->len;
241 	sc->req.block_num = 1;
242 
243 	err = xdma_request(sc->xchan, sc->src_phys, sc->dst_phys, sc->len);
244 	if (err != 0) {
245 		device_printf(sc->dev, "Can't configure virtual channel.\n");
246 		return (-1);
247 	}
248 
249 	/* Start operation. */
250 	xdma_begin(sc->xchan);
251 
252 	return (0);
253 }
254 
255 static int
256 xdmatest_verify(struct xdmatest_softc *sc)
257 {
258 	int err;
259 	int i;
260 
261 	/* We have memory updated by DMA controller. */
262 	bus_dmamap_sync(sc->src_dma_tag, sc->src_dma_map, BUS_DMASYNC_POSTREAD);
263 	bus_dmamap_sync(sc->dst_dma_tag, sc->dst_dma_map, BUS_DMASYNC_POSTWRITE);
264 
265 	for (i = 0; i < sc->len; i++) {
266 		if (sc->dst[i] != sc->src[i]) {
267 			device_printf(sc->dev,
268 			    "%s: Test failed: iter %d\n", __func__, i);
269 			return (-1);
270 		}
271 	}
272 
273 	err = xdma_channel_free(sc->xchan);
274 	if (err != 0) {
275 		device_printf(sc->dev,
276 		    "%s: Test failed: can't deallocate channel.\n", __func__);
277 		return (-1);
278 	}
279 
280 	err = xdma_put(sc->xdma);
281 	if (err != 0) {
282 		device_printf(sc->dev,
283 		    "%s: Test failed: can't deallocate xDMA.\n", __func__);
284 		return (-1);
285 	}
286 
287 	return (0);
288 }
289 
290 static void
291 xdmatest_worker(void *arg)
292 {
293 	struct xdmatest_softc *sc;
294 	int timeout;
295 	int err;
296 
297 	sc = arg;
298 
299 	device_printf(sc->dev, "Worker %d started.\n",
300 	    device_get_unit(sc->dev));
301 
302 	while (1) {
303 		sc->done = 0;
304 
305 		mtx_lock(&sc->mtx);
306 
307 		if (xdmatest_test(sc) != 0) {
308 			mtx_unlock(&sc->mtx);
309 			device_printf(sc->dev,
310 			    "%s: Test failed.\n", __func__);
311 			break;
312 		}
313 
314 		timeout = 100;
315 
316 		do {
317 			mtx_sleep(sc, &sc->mtx, 0, "xdmatest_wait", hz);
318 		} while (timeout-- && sc->done == 0);
319 
320 		if (timeout != 0) {
321 			err = xdmatest_verify(sc);
322 			if (err == 0) {
323 				/* Test succeeded. */
324 				mtx_unlock(&sc->mtx);
325 				continue;
326 			}
327 		}
328 
329 		mtx_unlock(&sc->mtx);
330 		device_printf(sc->dev,
331 		    "%s: Test failed.\n", __func__);
332 		break;
333 	}
334 }
335 
336 static void
337 xdmatest_delayed_attach(void *arg)
338 {
339 	struct xdmatest_softc *sc;
340 
341 	sc = arg;
342 
343 	if (kproc_create(xdmatest_worker, (void *)sc, &sc->newp, 0, 0,
344             "xdmatest_worker") != 0) {
345 		device_printf(sc->dev,
346 		    "%s: Failed to create worker thread.\n", __func__);
347 	}
348 
349 	config_intrhook_disestablish(&sc->config_intrhook);
350 }
351 
352 static int
353 xdmatest_probe(device_t dev)
354 {
355 
356 	if (!ofw_bus_status_okay(dev))
357 		return (ENXIO);
358 
359 	if (!ofw_bus_is_compatible(dev, "freebsd,xdma-test"))
360 		return (ENXIO);
361 
362 	device_set_desc(dev, "xDMA test driver");
363 
364 	return (BUS_PROBE_DEFAULT);
365 }
366 
367 static int
368 xdmatest_attach(device_t dev)
369 {
370 	struct xdmatest_softc *sc;
371 	int err;
372 
373 	sc = device_get_softc(dev);
374 	sc->dev = dev;
375 
376 	mtx_init(&sc->mtx, device_get_nameunit(dev), "xdmatest", MTX_DEF);
377 
378 	/* Allocate test memory */
379 	err = xdmatest_alloc_test_memory(sc);
380 	if (err != 0) {
381 		device_printf(sc->dev, "Can't allocate test memory.\n");
382 		return (-1);
383 	}
384 
385 	/* We'll run test later, but before / mount. */
386 	sc->config_intrhook.ich_func = xdmatest_delayed_attach;
387 	sc->config_intrhook.ich_arg = sc;
388 	if (config_intrhook_establish(&sc->config_intrhook) != 0)
389 		device_printf(dev, "config_intrhook_establish failed\n");
390 
391 	return (0);
392 }
393 
394 static int
395 xdmatest_detach(device_t dev)
396 {
397 	struct xdmatest_softc *sc;
398 
399 	sc = device_get_softc(dev);
400 
401 	bus_dmamap_unload(sc->src_dma_tag, sc->src_dma_map);
402 	bus_dmamem_free(sc->src_dma_tag, sc->src, sc->src_dma_map);
403 	bus_dma_tag_destroy(sc->src_dma_tag);
404 
405 	bus_dmamap_unload(sc->dst_dma_tag, sc->dst_dma_map);
406 	bus_dmamem_free(sc->dst_dma_tag, sc->dst, sc->dst_dma_map);
407 	bus_dma_tag_destroy(sc->dst_dma_tag);
408 
409 	return (0);
410 }
411 
412 static device_method_t xdmatest_methods[] = {
413 	/* Device interface */
414 	DEVMETHOD(device_probe,			xdmatest_probe),
415 	DEVMETHOD(device_attach,		xdmatest_attach),
416 	DEVMETHOD(device_detach,		xdmatest_detach),
417 
418 	DEVMETHOD_END
419 };
420 
421 static driver_t xdmatest_driver = {
422 	"xdmatest",
423 	xdmatest_methods,
424 	sizeof(struct xdmatest_softc),
425 };
426 
427 DRIVER_MODULE(xdmatest, simplebus, xdmatest_driver, 0, 0);
428