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, 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 static devclass_t xdmatest_devclass; 430 431 DRIVER_MODULE(xdmatest, simplebus, xdmatest_driver, xdmatest_devclass, 0, 0); 432