Module Name: src Committed By: jmcneill Date: Sat Apr 29 11:00:56 UTC 2017
Modified Files: src/sys/dev/fdt: fdtvar.h files.fdt Added Files: src/sys/dev/fdt: fdt_dma.c Log Message: Add FDT DMA controller API. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/fdt_dma.c cvs rdiff -u -r1.13 -r1.14 src/sys/dev/fdt/fdtvar.h cvs rdiff -u -r1.11 -r1.12 src/sys/dev/fdt/files.fdt Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/fdtvar.h diff -u src/sys/dev/fdt/fdtvar.h:1.13 src/sys/dev/fdt/fdtvar.h:1.14 --- src/sys/dev/fdt/fdtvar.h:1.13 Wed Apr 26 01:51:52 2017 +++ src/sys/dev/fdt/fdtvar.h Sat Apr 29 11:00:56 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: fdtvar.h,v 1.13 2017/04/26 01:51:52 jmcneill Exp $ */ +/* $NetBSD: fdtvar.h,v 1.14 2017/04/29 11:00:56 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -119,6 +119,51 @@ struct fdtbus_reset_controller_func { int (*reset_deassert)(device_t, void *); }; +struct fdtbus_dma_controller; + +struct fdtbus_dma { + struct fdtbus_dma_controller *dma_dc; + void *dma_priv; +}; + +enum fdtbus_dma_dir { + FDT_DMA_READ, /* device -> memory */ + FDT_DMA_WRITE /* memory -> device */ +}; + +struct fdtbus_dma_opt { + int opt_bus_width; /* Bus width */ + int opt_burst_len; /* Burst length */ + int opt_swap; /* Enable data swapping */ + int opt_dblbuf; /* Enable double buffering */ + int opt_wrap_len; /* Address wrap-around window */ +}; + +struct fdtbus_dma_req { + bus_dma_segment_t *dreq_segs; /* Memory */ + int dreq_nsegs; + + bus_addr_t dreq_dev_phys; /* Device */ + int dreq_sel; /* Device selector */ + + enum fdtbus_dma_dir dreq_dir; /* Transfer direction */ + + int dreq_block_irq; /* Enable IRQ at end of block */ + int dreq_block_multi; /* Enable multiple block transfers */ + int dreq_flow; /* Enable flow control */ + + struct fdtbus_dma_opt dreq_mem_opt; /* Memory options */ + struct fdtbus_dma_opt dreq_dev_opt; /* Device options */ +}; + +struct fdtbus_dma_controller_func { + void * (*acquire)(device_t, const void *, size_t, + void (*)(void *), void *); + void (*release)(device_t, void *); + int (*transfer)(device_t, void *, struct fdtbus_dma_req *); + void (*halt)(device_t, void *); +}; + int fdtbus_register_interrupt_controller(device_t, int, const struct fdtbus_interrupt_controller_func *); int fdtbus_register_i2c_controller(device_t, int, @@ -133,6 +178,8 @@ int fdtbus_register_clock_controller(de const struct fdtbus_clock_controller_func *); int fdtbus_register_reset_controller(device_t, int, const struct fdtbus_reset_controller_func *); +int fdtbus_register_dma_controller(device_t, int, + const struct fdtbus_dma_controller_func *); int fdtbus_get_reg(int, u_int, bus_addr_t *, bus_size_t *); int fdtbus_get_phandle(int, const char *); @@ -159,6 +206,14 @@ int fdtbus_regulator_set_voltage(struct int fdtbus_regulator_get_voltage(struct fdtbus_regulator *, u_int *); +struct fdtbus_dma *fdtbus_dma_get(int, const char *, void (*)(void *), void *); +struct fdtbus_dma *fdtbus_dma_get_index(int, u_int, void (*)(void *), + void *); +void fdtbus_dma_put(struct fdtbus_dma *); +int fdtbus_dma_transfer(struct fdtbus_dma *, + struct fdtbus_dma_req *); +void fdtbus_dma_halt(struct fdtbus_dma *); + struct clk * fdtbus_clock_get(int, const char *); struct clk * fdtbus_clock_get_index(int, u_int); Index: src/sys/dev/fdt/files.fdt diff -u src/sys/dev/fdt/files.fdt:1.11 src/sys/dev/fdt/files.fdt:1.12 --- src/sys/dev/fdt/files.fdt:1.11 Wed Apr 26 01:51:52 2017 +++ src/sys/dev/fdt/files.fdt Sat Apr 29 11:00:56 2017 @@ -1,4 +1,4 @@ -# $NetBSD: files.fdt,v 1.11 2017/04/26 01:51:52 jmcneill Exp $ +# $NetBSD: files.fdt,v 1.12 2017/04/29 11:00:56 jmcneill Exp $ include "external/bsd/libfdt/conf/files.libfdt" @@ -25,6 +25,7 @@ file dev/fdt/gpiokeys.c gpiokeys file dev/fdt/fdt_openfirm.c fdtbus file dev/fdt/fdt_subr.c fdtbus file dev/fdt/fdt_clock.c fdtbus +file dev/fdt/fdt_dma.c fdtbus file dev/fdt/fdt_gpio.c fdtbus file dev/fdt/fdt_i2c.c fdtbus file dev/fdt/fdt_intr.c fdtbus Added files: Index: src/sys/dev/fdt/fdt_dma.c diff -u /dev/null src/sys/dev/fdt/fdt_dma.c:1.1 --- /dev/null Sat Apr 29 11:00:56 2017 +++ src/sys/dev/fdt/fdt_dma.c Sat Apr 29 11:00:56 2017 @@ -0,0 +1,190 @@ +/* $NetBSD: fdt_dma.c,v 1.1 2017/04/29 11:00:56 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fdt_dma.c,v 1.1 2017/04/29 11:00:56 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kmem.h> + +#include <libfdt.h> +#include <dev/fdt/fdtvar.h> + +struct fdtbus_dma_controller { + device_t dma_dev; + int dma_phandle; + const struct fdtbus_dma_controller_func *dma_funcs; + + struct fdtbus_dma_controller *dma_next; +}; + +static struct fdtbus_dma_controller *fdtbus_dma = NULL; + +int +fdtbus_register_dma_controller(device_t dev, int phandle, + const struct fdtbus_dma_controller_func *funcs) +{ + struct fdtbus_dma_controller *dma; + + dma = kmem_alloc(sizeof(*dma), KM_SLEEP); + dma->dma_dev = dev; + dma->dma_phandle = phandle; + dma->dma_funcs = funcs; + + dma->dma_next = fdtbus_dma; + fdtbus_dma = dma; + + return 0; +} + +static struct fdtbus_dma_controller * +fdtbus_get_dma_controller(int phandle) +{ + struct fdtbus_dma_controller *dma; + + for (dma = fdtbus_dma; dma; dma = dma->dma_next) { + if (dma->dma_phandle == phandle) { + return dma; + } + } + + return NULL; +} + +struct fdtbus_dma * +fdtbus_dma_get_index(int phandle, u_int index, void (*cb)(void *), void *cbarg) +{ + struct fdtbus_dma_controller *dc; + struct fdtbus_dma *dma = NULL; + void *dma_priv = NULL; + uint32_t *dmas = NULL; + uint32_t *p; + u_int n, dma_cells; + int len, resid; + + len = OF_getproplen(phandle, "dmas"); + if (len <= 0) + return NULL; + + dmas = kmem_alloc(len, KM_SLEEP); + if (OF_getprop(phandle, "dmas", dmas, len) != len) { + kmem_free(dmas, len); + return NULL; + } + + p = dmas; + for (n = 0, resid = len; resid > 0; n++) { + const int dc_phandle = + fdtbus_get_phandle_from_native(be32toh(p[0])); + if (of_getprop_uint32(dc_phandle, "#dma-cells", &dma_cells)) + break; + if (n == index) { + dc = fdtbus_get_dma_controller(dc_phandle); + if (dc == NULL) + goto done; + dma_priv = dc->dma_funcs->acquire(dc->dma_dev, + dma_cells > 0 ? &p[1] : NULL, dma_cells * 4, + cb, cbarg); + if (dma_priv) { + dma = kmem_alloc(sizeof(*dma), KM_SLEEP); + dma->dma_dc = dc; + dma->dma_priv = dma_priv; + } + break; + } + resid -= (dma_cells + 1) * 4; + p += dma_cells + 1; + } + +done: + if (dmas) + kmem_free(dmas, len); + + return dma; +} + +struct fdtbus_dma * +fdtbus_dma_get(int phandle, const char *name, void (*cb)(void *), void *cbarg) +{ + struct fdtbus_dma *dma = NULL; + char *dma_names = NULL; + const char *p; + u_int index; + int len, resid; + + len = OF_getproplen(phandle, "dma-names"); + if (len <= 0) + return NULL; + + dma_names = kmem_alloc(len, KM_SLEEP); + if (OF_getprop(phandle, "dma-names", dma_names, len) != len) { + kmem_free(dma_names, len); + return NULL; + } + + p = dma_names; + for (index = 0, resid = len; resid > 0; index++) { + if (strcmp(p, name) == 0) { + dma = fdtbus_dma_get_index(phandle, index, cb, cbarg); + break; + } + resid -= strlen(p); + p += strlen(p) + 1; + } + + if (dma_names) + kmem_free(dma_names, len); + + return dma; +} + +void +fdtbus_dma_put(struct fdtbus_dma *dma) +{ + struct fdtbus_dma_controller *dc = dma->dma_dc; + + dc->dma_funcs->release(dc->dma_dev, dma->dma_priv); + kmem_free(dma, sizeof(*dma)); +} + +int +fdtbus_dma_transfer(struct fdtbus_dma *dma, struct fdtbus_dma_req *req) +{ + struct fdtbus_dma_controller *dc = dma->dma_dc; + + return dc->dma_funcs->transfer(dc->dma_dev, dma->dma_priv, req); +} + +void +fdtbus_dma_halt(struct fdtbus_dma *dma) +{ + struct fdtbus_dma_controller *dc = dma->dma_dc; + + return dc->dma_funcs->halt(dc->dma_dev, dma->dma_priv); +}