]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/xdma/xdma_fdt_test.c
zfs: merge openzfs/zfs@a4bf6baae
[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/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);