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); 427