1 /*- 2 * Copyright (c) 2016-2018 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 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include "opt_platform.h" 35 #include <sys/param.h> 36 #include <sys/conf.h> 37 #include <sys/bus.h> 38 #include <sys/kernel.h> 39 #include <sys/queue.h> 40 #include <sys/kobj.h> 41 #include <sys/malloc.h> 42 #include <sys/limits.h> 43 #include <sys/lock.h> 44 #include <sys/sysctl.h> 45 #include <sys/systm.h> 46 #include <sys/sx.h> 47 48 #include <machine/bus.h> 49 50 #ifdef FDT 51 #include <dev/fdt/fdt_common.h> 52 #include <dev/ofw/ofw_bus.h> 53 #include <dev/ofw/ofw_bus_subr.h> 54 #endif 55 56 #include <dev/xdma/xdma.h> 57 58 #include <xdma_if.h> 59 60 /* 61 * Multiple xDMA controllers may work with single DMA device, 62 * so we have global lock for physical channel management. 63 */ 64 static struct sx xdma_sx; 65 66 #define XDMA_LOCK() sx_xlock(&xdma_sx) 67 #define XDMA_UNLOCK() sx_xunlock(&xdma_sx) 68 #define XDMA_ASSERT_LOCKED() sx_xassert(&xdma_sx, MA_OWNED) 69 70 /* 71 * Allocate virtual xDMA channel. 72 */ 73 xdma_channel_t * 74 xdma_channel_alloc(xdma_controller_t *xdma, uint32_t caps) 75 { 76 xdma_channel_t *xchan; 77 int ret; 78 79 xchan = malloc(sizeof(xdma_channel_t), M_XDMA, M_WAITOK | M_ZERO); 80 xchan->xdma = xdma; 81 xchan->caps = caps; 82 83 XDMA_LOCK(); 84 85 /* Request a real channel from hardware driver. */ 86 ret = XDMA_CHANNEL_ALLOC(xdma->dma_dev, xchan); 87 if (ret != 0) { 88 device_printf(xdma->dev, 89 "%s: Can't request hardware channel.\n", __func__); 90 XDMA_UNLOCK(); 91 free(xchan, M_XDMA); 92 93 return (NULL); 94 } 95 96 TAILQ_INIT(&xchan->ie_handlers); 97 98 sx_init(&xchan->sx_lock, "xDMA chan"); 99 sx_init(&xchan->sx_qin_lock, "xDMA qin"); 100 sx_init(&xchan->sx_qout_lock, "xDMA qout"); 101 sx_init(&xchan->sx_bank_lock, "xDMA bank"); 102 sx_init(&xchan->sx_proc_lock, "xDMA proc"); 103 104 TAILQ_INIT(&xchan->bank); 105 TAILQ_INIT(&xchan->queue_in); 106 TAILQ_INIT(&xchan->queue_out); 107 TAILQ_INIT(&xchan->processing); 108 109 TAILQ_INSERT_TAIL(&xdma->channels, xchan, xchan_next); 110 111 XDMA_UNLOCK(); 112 113 return (xchan); 114 } 115 116 int 117 xdma_channel_free(xdma_channel_t *xchan) 118 { 119 xdma_controller_t *xdma; 120 int err; 121 122 xdma = xchan->xdma; 123 KASSERT(xdma != NULL, ("xdma is NULL")); 124 125 XDMA_LOCK(); 126 127 /* Free the real DMA channel. */ 128 err = XDMA_CHANNEL_FREE(xdma->dma_dev, xchan); 129 if (err != 0) { 130 device_printf(xdma->dev, 131 "%s: Can't free real hw channel.\n", __func__); 132 XDMA_UNLOCK(); 133 return (-1); 134 } 135 136 if (xchan->flags & XCHAN_TYPE_SG) 137 xdma_channel_free_sg(xchan); 138 139 xdma_teardown_all_intr(xchan); 140 141 sx_destroy(&xchan->sx_lock); 142 sx_destroy(&xchan->sx_qin_lock); 143 sx_destroy(&xchan->sx_qout_lock); 144 sx_destroy(&xchan->sx_bank_lock); 145 sx_destroy(&xchan->sx_proc_lock); 146 147 TAILQ_REMOVE(&xdma->channels, xchan, xchan_next); 148 149 free(xchan, M_XDMA); 150 151 XDMA_UNLOCK(); 152 153 return (0); 154 } 155 156 int 157 xdma_setup_intr(xdma_channel_t *xchan, 158 int (*cb)(void *, xdma_transfer_status_t *), 159 void *arg, void **ihandler) 160 { 161 struct xdma_intr_handler *ih; 162 xdma_controller_t *xdma; 163 164 xdma = xchan->xdma; 165 KASSERT(xdma != NULL, ("xdma is NULL")); 166 167 /* Sanity check. */ 168 if (cb == NULL) { 169 device_printf(xdma->dev, 170 "%s: Can't setup interrupt handler.\n", 171 __func__); 172 173 return (-1); 174 } 175 176 ih = malloc(sizeof(struct xdma_intr_handler), 177 M_XDMA, M_WAITOK | M_ZERO); 178 ih->cb = cb; 179 ih->cb_user = arg; 180 181 XCHAN_LOCK(xchan); 182 TAILQ_INSERT_TAIL(&xchan->ie_handlers, ih, ih_next); 183 XCHAN_UNLOCK(xchan); 184 185 if (ihandler != NULL) 186 *ihandler = ih; 187 188 return (0); 189 } 190 191 int 192 xdma_teardown_intr(xdma_channel_t *xchan, struct xdma_intr_handler *ih) 193 { 194 xdma_controller_t *xdma; 195 196 xdma = xchan->xdma; 197 KASSERT(xdma != NULL, ("xdma is NULL")); 198 199 /* Sanity check. */ 200 if (ih == NULL) { 201 device_printf(xdma->dev, 202 "%s: Can't teardown interrupt.\n", __func__); 203 return (-1); 204 } 205 206 TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next); 207 free(ih, M_XDMA); 208 209 return (0); 210 } 211 212 int 213 xdma_teardown_all_intr(xdma_channel_t *xchan) 214 { 215 struct xdma_intr_handler *ih_tmp; 216 struct xdma_intr_handler *ih; 217 xdma_controller_t *xdma; 218 219 xdma = xchan->xdma; 220 KASSERT(xdma != NULL, ("xdma is NULL")); 221 222 TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) { 223 TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next); 224 free(ih, M_XDMA); 225 } 226 227 return (0); 228 } 229 230 int 231 xdma_request(xdma_channel_t *xchan, struct xdma_request *req) 232 { 233 xdma_controller_t *xdma; 234 int ret; 235 236 xdma = xchan->xdma; 237 238 KASSERT(xdma != NULL, ("xdma is NULL")); 239 240 XCHAN_LOCK(xchan); 241 ret = XDMA_CHANNEL_REQUEST(xdma->dma_dev, xchan, req); 242 if (ret != 0) { 243 device_printf(xdma->dev, 244 "%s: Can't request a transfer.\n", __func__); 245 XCHAN_UNLOCK(xchan); 246 247 return (-1); 248 } 249 XCHAN_UNLOCK(xchan); 250 251 return (0); 252 } 253 254 int 255 xdma_control(xdma_channel_t *xchan, enum xdma_command cmd) 256 { 257 xdma_controller_t *xdma; 258 int ret; 259 260 xdma = xchan->xdma; 261 KASSERT(xdma != NULL, ("xdma is NULL")); 262 263 ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, cmd); 264 if (ret != 0) { 265 device_printf(xdma->dev, 266 "%s: Can't process command.\n", __func__); 267 return (-1); 268 } 269 270 return (0); 271 } 272 273 void 274 xdma_callback(xdma_channel_t *xchan, xdma_transfer_status_t *status) 275 { 276 struct xdma_intr_handler *ih_tmp; 277 struct xdma_intr_handler *ih; 278 xdma_controller_t *xdma; 279 280 xdma = xchan->xdma; 281 KASSERT(xdma != NULL, ("xdma is NULL")); 282 283 TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) 284 if (ih->cb != NULL) 285 ih->cb(ih->cb_user, status); 286 287 if (xchan->flags & XCHAN_TYPE_SG) 288 xdma_queue_submit(xchan); 289 } 290 291 #ifdef FDT 292 /* 293 * Notify the DMA driver we have machine-dependent data in FDT. 294 */ 295 static int 296 xdma_ofw_md_data(xdma_controller_t *xdma, pcell_t *cells, int ncells) 297 { 298 uint32_t ret; 299 300 ret = XDMA_OFW_MD_DATA(xdma->dma_dev, 301 cells, ncells, (void **)&xdma->data); 302 303 return (ret); 304 } 305 306 /* 307 * Allocate xdma controller. 308 */ 309 xdma_controller_t * 310 xdma_ofw_get(device_t dev, const char *prop) 311 { 312 phandle_t node, parent; 313 xdma_controller_t *xdma; 314 device_t dma_dev; 315 pcell_t *cells; 316 int ncells; 317 int error; 318 int ndmas; 319 int idx; 320 321 node = ofw_bus_get_node(dev); 322 if (node <= 0) 323 device_printf(dev, 324 "%s called on not ofw based device.\n", __func__); 325 326 error = ofw_bus_parse_xref_list_get_length(node, 327 "dmas", "#dma-cells", &ndmas); 328 if (error) { 329 device_printf(dev, 330 "%s can't get dmas list.\n", __func__); 331 return (NULL); 332 } 333 334 if (ndmas == 0) { 335 device_printf(dev, 336 "%s dmas list is empty.\n", __func__); 337 return (NULL); 338 } 339 340 error = ofw_bus_find_string_index(node, "dma-names", prop, &idx); 341 if (error != 0) { 342 device_printf(dev, 343 "%s can't find string index.\n", __func__); 344 return (NULL); 345 } 346 347 error = ofw_bus_parse_xref_list_alloc(node, "dmas", "#dma-cells", 348 idx, &parent, &ncells, &cells); 349 if (error != 0) { 350 device_printf(dev, 351 "%s can't get dma device xref.\n", __func__); 352 return (NULL); 353 } 354 355 dma_dev = OF_device_from_xref(parent); 356 if (dma_dev == NULL) { 357 device_printf(dev, 358 "%s can't get dma device.\n", __func__); 359 return (NULL); 360 } 361 362 xdma = malloc(sizeof(struct xdma_controller), 363 M_XDMA, M_WAITOK | M_ZERO); 364 xdma->dev = dev; 365 xdma->dma_dev = dma_dev; 366 367 TAILQ_INIT(&xdma->channels); 368 369 xdma_ofw_md_data(xdma, cells, ncells); 370 free(cells, M_OFWPROP); 371 372 return (xdma); 373 } 374 #endif 375 376 /* 377 * Free xDMA controller object. 378 */ 379 int 380 xdma_put(xdma_controller_t *xdma) 381 { 382 383 XDMA_LOCK(); 384 385 /* Ensure no channels allocated. */ 386 if (!TAILQ_EMPTY(&xdma->channels)) { 387 device_printf(xdma->dev, "%s: Can't free xDMA\n", __func__); 388 return (-1); 389 } 390 391 free(xdma->data, M_DEVBUF); 392 free(xdma, M_XDMA); 393 394 XDMA_UNLOCK(); 395 396 return (0); 397 } 398 399 static void 400 xdma_init(void) 401 { 402 403 sx_init(&xdma_sx, "xDMA"); 404 } 405 406 SYSINIT(xdma, SI_SUB_DRIVERS, SI_ORDER_FIRST, xdma_init, NULL); 407