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