Index: sys/dev/dmaengine/dmaengine.h =================================================================== --- /dev/null +++ sys/dev/dmaengine/dmaengine.h @@ -0,0 +1,91 @@ +/*- + * Copyright (C) 2020 Advanced Micro Devices, Inc. + * + * 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. + * 3. Neither the name of AMD corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * Contact Information : + * Rajesh Kumar + * + */ + +#ifndef _DMAENGINE_H_ +#define _DMAENGINE_H_ + +#include "dmaengine_if.h" + +extern devclass_t dmaengine_hw_devclass; +SYSCTL_DECL(_hw_dmaengine); + +int dmaengine_register_device(device_t); +int dmaengine_unregister_device(device_t); +int dmaengine_child_location_str(device_t, device_t, char *, size_t); +int dmaengine_print_child(device_t, device_t); + +/* + * dmaengine_get_chan_idx() - get the index of an available dma channel from + * the dmaengine + * @chan_cnt: Total number of dma channels needed + * + * Test applications can obtain the index of the available dma channel from + * the dmaengine based on the total number of dma channels needed. + */ +int dmaengine_get_chan_idx(ssize_t chan_cnt); + +/* + * dmaengine_get_channel() - get the reference of the pointed dma channel from + * the dmaengine + * @chan_idx: Index of the channel to get reference + * @flags: Additional optional params + * + * Test applications can obtain the reference of the pointed dma channel from + * the dmaengine. + */ +bus_dmaengine_t dmaengine_get_channel(uint32_t chan_idx, int flags); + +/* + * dmaengine_put_channel() - free the reference of the pointed dma channel from + * the dmaengine + * @channel: Reference of the channel to be freed. + * + * Test applications can free the reference of the pointed dma channel from + * the dmaengine. + */ +void dmaengine_put_channel(bus_dmaengine_t dmaengine); + +/* dmaengine_copy() - copy from source to destination using the specified + * dmaengine. + * @dmaengine: DMA engine to use for copy + * @dst: Destination address + * @src: Source address + * @len: Length of the data to be copied + * @callback_fn: Callback funtion to notify copy completion + * @callback_arg: Argument to be passed to @callback_fn + * @flags: Additional optional params + */ +int dmaengine_copy(bus_dmaengine_t dmaengine, bus_addr_t dst, bus_addr_t src, + bus_size_t len, bus_dmaengine_callback_t callback_fn, void *callback_arg, + uint32_t flags); + +#endif /* _DMAENGINE_H_ */ Index: sys/dev/dmaengine/dmaengine.c =================================================================== --- /dev/null +++ sys/dev/dmaengine/dmaengine.c @@ -0,0 +1,274 @@ +/*- + * Copyright (C) 2020 Advanced Micro Devices, Inc. + * + * 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. + * 3. Neither the name of AMD corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * Contact Information : + * Rajesh Kumar + * + */ + +/* + * This is a generic interface driver for any DMA ENGINE devices. This can + * be used by any test applications or drivers to request and release DMA + * channels, and to initiate the DMA operations without the knowledge of the + * underlying vendor specific DMA hardware. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" + +devclass_t dmaengine_hw_devclass; + +struct dmaengine_child { + + TAILQ_ENTRY(dmaengine_child) entry; + + device_t dmadev; + device_t parent; + int function; + int chan_idx; + int refcnt; + bus_dmaengine_t dmaengine; + struct dmaengine_child *next; +}; + +struct mtx dma_channel_lock; + +/* List of registered DMA channels */ +TAILQ_HEAD(dma_channel, dmaengine_child) dma_channel; + +SYSCTL_NODE(_hw, OID_AUTO, dmaengine, CTLFLAG_RW, 0, "DMA engine sysctls"); + +static unsigned dma_channel_idx = 0; +SYSCTL_UINT(_hw_dmaengine, OID_AUTO, channels, CTLFLAG_RD, &dma_channel_idx, 0, + "Number of DMA channels registered"); + +/* + * Register a DMA channel + * + */ +int +dmaengine_register_device(device_t dmadev) +{ + struct dmaengine_child **cpp = device_get_softc(dmadev); + struct dmaengine_child *dc; + + device_printf(dmadev, "Registering DMA Engine\n"); + + if (dma_channel.tqh_last == NULL) { + mtx_init(&dma_channel_lock, "DMA Channel list lock", 0, MTX_DEF); + TAILQ_INIT(&dma_channel); + } + + mtx_lock(&dma_channel_lock); + + dc = malloc(sizeof(*dc), M_DEVBUF, M_WAITOK | M_ZERO); + if (dc == NULL) + return (ENOMEM); + + dc->function = 0; + dc->chan_idx = dma_channel_idx; + dc->refcnt = 0; + dc->parent = dmadev; + dc->dmadev = device_add_child(dmadev, NULL, -1); + if (dc->dmadev == NULL) { + free(dc, M_DEVBUF); + return (ENOMEM); + } + device_set_ivars(dc->dmadev, dc); + *cpp = dc; + dma_channel_idx++; + + TAILQ_INSERT_TAIL(&dma_channel, dc, entry); + + mtx_unlock(&dma_channel_lock); + + bus_generic_attach(dmadev); + return (0); +} + +/* + * UnRegister a DMA channel + * + */ +int +dmaengine_unregister_device(device_t dmadev) +{ + struct dmaengine_child **cpp = device_get_softc(dmadev); + struct dmaengine_child *dc = *cpp; + int error = 0; + + device_printf(dmadev, "Registering DMA Engine\n"); + + mtx_lock(&dma_channel_lock); + + TAILQ_REMOVE(&dma_channel, dc, entry); + + error = device_delete_child(dmadev, dc->dmadev); + if (error) + goto out; + + free(dc, M_DEVBUF); + dma_channel_idx--; + + mtx_unlock(&dma_channel_lock); +out: + + return (0); +} + +int +dmaengine_child_location_str(device_t dmadev, device_t child, char *buf, + size_t buflen) +{ + struct dmaengine_child *dc = device_get_ivars(child); + + snprintf(buf, buflen, "function=%d", dc->function); + return (0); +} + +int +dmaengine_print_child(device_t dmadev, device_t child) +{ + struct dmaengine_child *dc = device_get_ivars(child); + int retval; + + retval = bus_print_child_header(dmadev, child); + retval += printf(" at function %d", dc->function); + retval += bus_print_child_domain(dmadev, child); + retval += bus_print_child_footer(dmadev, child); + + return (retval); +} + +/* + * Get a reference to the child of a registered DMA channel + * + */ +static struct dmaengine_child * +dmaengine_get_dc(int chan_idx, bus_dmaengine_t dmaengine, int tcnt) +{ + struct dmaengine_child *dc = NULL, *dc_tmp = NULL; + + mtx_lock(&dma_channel_lock); + + TAILQ_FOREACH_SAFE(dc, &dma_channel, entry, dc_tmp) { + if (chan_idx >= 0) { + if (dc->chan_idx == chan_idx) + break; + } + + if (dmaengine != NULL) { + if (dc->dmaengine == dmaengine) + break; + } + + if (tcnt > 0) { + if (dc->refcnt == 0) { + atomic_add_int(&dc->refcnt, 1); + break; + } + } + } + + mtx_unlock(&dma_channel_lock); + + return (dc); +} + +/* + * Get the index of a DMA channel to be used + * + */ +int +dmaengine_get_chan_idx(ssize_t tcnt) +{ + struct dmaengine_child *dc; + + dc = dmaengine_get_dc(-1, NULL, tcnt); + + return (dc->chan_idx); +} + +/* + * Take a reference to the DMA channel of the specified index + * + */ +bus_dmaengine_t +dmaengine_get_channel(uint32_t chan_idx, int flags) +{ + struct dmaengine_child *dc; + + dc = dmaengine_get_dc(chan_idx, NULL, 0); + dc->dmaengine = DMAENGINE_GET_CHANNEL(dc->parent, chan_idx, flags); + + return (dc->dmaengine); +} + +/* + * Free the reference of the specified DMA channel + * + */ +void +dmaengine_put_channel(bus_dmaengine_t dmaengine) +{ + struct dmaengine_child *dc; + + dc = dmaengine_get_dc(-1, dmaengine, 0); + dc->dmaengine = NULL; + DMAENGINE_PUT_CHANNEL(dc->parent, dmaengine); +} + +/* + * Initiate a DMA copy on the specified DMA channel with the specified + * parameters along with a callback function to notify the completion. + * + */ +int +dmaengine_copy(bus_dmaengine_t dmaengine, bus_addr_t dst, + bus_addr_t src, bus_size_t len, bus_dmaengine_callback_t callback_fn, + void *callback_arg, uint32_t flags) +{ + struct dmaengine_child *dc; + + dc = dmaengine_get_dc(-1, dmaengine, 0); + return DMAENGINE_COPY(dc->parent, dmaengine, dst, src, len, callback_fn, + callback_arg, flags); +} + +MODULE_VERSION(dmaengine, 1); Index: sys/dev/dmaengine/dmaengine_if.m =================================================================== --- /dev/null +++ sys/dev/dmaengine/dmaengine_if.m @@ -0,0 +1,69 @@ +#- +# Copyright (C) 2020 Advanced Micro Devices, Inc. +# +# 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. +# 3. Neither the name of AMD corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +# +# Contact Information : +# Rajesh Kumar +# +# $FreeBSD$ +# + +#include +#include + +INTERFACE dmaengine; + +HEADER { + typedef void *bus_dmaengine_t; + typedef void (*bus_dmaengine_callback_t)(void *arg, int error); +}; + +METHOD int get_chan_idx { + device_t dmadev; + ssize_t chan_cnt; +}; + +METHOD bus_dmaengine_t get_channel { + device_t dmadev; + uint32_t chan_idx; + int flags; +}; + +METHOD void put_channel { + device_t dmadev; + bus_dmaengine_t dmaengine; +}; + +METHOD int copy { + device_t dmadev; + bus_dmaengine_t dmaengine; + bus_addr_t dst; + bus_addr_t src; + bus_size_t len; + bus_dmaengine_callback_t callback_fn; + void *callback_arg; + uint32_t flags; +}; Index: sys/modules/dmaengine/Makefile =================================================================== --- /dev/null +++ sys/modules/dmaengine/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= dmaengine + +.include Index: sys/modules/dmaengine/dmaengine/Makefile =================================================================== --- /dev/null +++ sys/modules/dmaengine/dmaengine/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/dmaengine + +KMOD = dmaengine +SRCS = dmaengine.c dmaengine_if.c +SRCS += device_if.h bus_if.h dmaengine_if.h + +.include