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