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