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