diff options
Diffstat (limited to 'kernel/common/ethosu_buffer.c')
-rw-r--r-- | kernel/common/ethosu_buffer.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/kernel/common/ethosu_buffer.c b/kernel/common/ethosu_buffer.c new file mode 100644 index 0000000..cf41b8d --- /dev/null +++ b/kernel/common/ethosu_buffer.c @@ -0,0 +1,215 @@ +/* + * SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates <open-source-office@arm.com> + * SPDX-License-Identifier: GPL-2.0-only + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms + * of such GNU licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +/**************************************************************************** + * Includes + ****************************************************************************/ + +#include <common/ethosu_buffer.h> + +#include <common/ethosu_device.h> +#include <common/ethosu_dma_mem.h> +#include <uapi/ethosu.h> + +#include <linux/anon_inodes.h> +#include <linux/dma-mapping.h> +#include <linux/of_address.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/remoteproc.h> +#include <linux/uaccess.h> + +/**************************************************************************** + * Variables + ****************************************************************************/ + +static int ethosu_buffer_release(struct inode *inode, + struct file *file); + +static int ethosu_buffer_mmap(struct file *file, + struct vm_area_struct *vma); + +static loff_t ethosu_buffer_llseek(struct file *file, + loff_t offset, + int whence); + +static const struct file_operations ethosu_buffer_fops = { + .release = ðosu_buffer_release, + .mmap = ðosu_buffer_mmap, + .llseek = ðosu_buffer_llseek, +}; + +/**************************************************************************** + * Functions + ****************************************************************************/ + +static bool ethosu_buffer_verify(struct file *file) +{ + return file->f_op == ðosu_buffer_fops; +} + +static void ethosu_buffer_destroy(struct kref *kref) +{ + struct ethosu_buffer *buf = + container_of(kref, struct ethosu_buffer, kref); + struct device *dev = buf->dev; + + dev_dbg(dev, "Buffer destroy. buf=0x%pK", buf); + + ethosu_dma_mem_free(&buf->dma_mem); + + memset(buf, 0, sizeof(*buf)); + devm_kfree(dev, buf); +} + +static int ethosu_buffer_release(struct inode *inode, + struct file *file) +{ + struct ethosu_buffer *buf = file->private_data; + struct device *dev = buf->dev; + + dev_dbg(dev, "Buffer release. file=0x%pK, buf=0x%pK\n", + file, buf); + + ethosu_buffer_put(buf); + + return 0; +} + +static int ethosu_buffer_mmap(struct file *file, + struct vm_area_struct *vma) +{ + struct ethosu_buffer *buf = file->private_data; + struct device *dev = buf->dev; + int ret; + + dev_dbg(dev, "Buffer mmap. file=0x%pK, buf=0x%pK\n", + file, buf); + + ret = dma_mmap_coherent(dev, vma, buf->dma_mem->cpu_addr, + buf->dma_mem->dma_addr, buf->dma_mem->size); + + return ret; +} + +static loff_t ethosu_buffer_llseek(struct file *file, + loff_t offset, + int whence) +{ + struct ethosu_buffer *buf = file->private_data; + + if (offset != 0) + return -EINVAL; + + /* + * SEEK_END and SEEK_SET is supported with a zero offset to allow buffer + * size discovery using seek functions e.g. + * size = lseek(buf_fd, 0, SEEK_END); + * lseek(buf_fd, 0, SEEK_SET); + */ + switch (whence) { + case SEEK_END: + return buf->dma_mem->size; + case SEEK_SET: + return 0; + default: + return -EINVAL; + } +} + +int ethosu_buffer_create(struct device *dev, + size_t size) +{ + struct ethosu_buffer *buf; + int ret = -ENOMEM; + + if (!size) + return -EINVAL; + + buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf->dev = dev; + kref_init(&buf->kref); + + buf->dma_mem = ethosu_dma_mem_alloc(dev, size); + if (IS_ERR(buf->dma_mem)) { + ret = PTR_ERR(buf->dma_mem); + goto free_buf; + } + + ret = anon_inode_getfd("ethosu-buffer", ðosu_buffer_fops, buf, + O_RDWR | O_CLOEXEC); + if (ret < 0) + goto free_dma; + + buf->file = fget(ret); + buf->file->f_mode |= FMODE_LSEEK; + + fput(buf->file); + + dev_dbg(dev, + "Buffer create. file=0x%pK, fd=%d, buf=0x%pK, size=%zu, cpu_addr=0x%pK, dma_addr=0x%llx, phys_addr=0x%llx\n", + buf->file, ret, buf, size, buf->dma_mem->cpu_addr, + buf->dma_mem->dma_addr, virt_to_phys(buf->dma_mem->cpu_addr)); + + return ret; + +free_dma: + ethosu_dma_mem_free(&buf->dma_mem); + +free_buf: + memset(buf, 0, sizeof(*buf)); + devm_kfree(dev, buf); + + return ret; +} + +struct ethosu_buffer *ethosu_buffer_get_from_fd(int fd) +{ + struct ethosu_buffer *buf; + struct file *file; + + file = fget(fd); + if (!file) + return ERR_PTR(-EINVAL); + + if (!ethosu_buffer_verify(file)) { + fput(file); + + return ERR_PTR(-EINVAL); + } + + buf = file->private_data; + ethosu_buffer_get(buf); + fput(file); + + return buf; +} + +void ethosu_buffer_get(struct ethosu_buffer *buf) +{ + kref_get(&buf->kref); +} + +void ethosu_buffer_put(struct ethosu_buffer *buf) +{ + kref_put(&buf->kref, ethosu_buffer_destroy); +} |