]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/xdma/xdma_fdt_test.c
MFV: zlib 1.3
[FreeBSD/FreeBSD.git] / sys / dev / xdma / xdma_fdt_test.c
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);