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