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
xdmatest_intr(void * arg)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
xdmatest_dmamap_cb(void * arg,bus_dma_segment_t * segs,int nseg,int err)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
xdmatest_alloc_test_memory(struct xdmatest_softc * sc)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
xdmatest_test(struct xdmatest_softc * sc)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
xdmatest_verify(struct xdmatest_softc * sc)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
xdmatest_worker(void * arg)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
xdmatest_delayed_attach(void * arg)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
xdmatest_probe(device_t dev)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
xdmatest_attach(device_t dev)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
xdmatest_detach(device_t dev)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