aboutsummaryrefslogtreecommitdiff
path: root/kernel/common/ethosu_buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/common/ethosu_buffer.c')
-rw-r--r--kernel/common/ethosu_buffer.c215
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 = &ethosu_buffer_release,
+ .mmap = &ethosu_buffer_mmap,
+ .llseek = &ethosu_buffer_llseek,
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+static bool ethosu_buffer_verify(struct file *file)
+{
+ return file->f_op == &ethosu_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", &ethosu_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);
+}