forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 229
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LF-6285-1 npu: ethosu: Add Arm ethos-u driver
This Linux drvier is to provide an example of how a rich operating system like Linux can dispatch inferences to an Arm Cortex-M subsystem, consisting of an Arm Cortex-M and an Arm Ethos-U NPU. The code is from link: https://git.mlplatform.org/ml/ethos-u/ethos-u-linux-driver-stack.git. Tag: 22.02. Signed-off-by: Kristofer Jonsson <kristofer.jonsson@arm.com> Signed-off-by: Per Astrand <per.astrand@arm.com> Signed-off-by: Jonny Svärd <jonny.svaerd@arm.com> Signed-off-by: Lior Dekel <Lior.dekel@arm.com> Signed-off-by: Henrik Hoglind <henrik.hoglind@arm.com> Signed-off-by: Davide Grohmann <davide.grohmann@arm.com>
- Loading branch information
Showing
17 changed files
with
2,952 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# | ||
# (C) COPYRIGHT 2020 ARM Limited. All rights reserved. | ||
# | ||
# 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. | ||
# | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
# | ||
|
||
config ETHOSU | ||
tristate "Arm Ethos-U NPU support" | ||
help | ||
Arm Ethos-U NPU driver. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# | ||
# (C) COPYRIGHT 2020 ARM Limited. All rights reserved. | ||
# | ||
# 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. | ||
# | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
# | ||
|
||
obj-$(CONFIG_ETHOSU) = ethosu.o | ||
|
||
ethosu-objs := ethosu_driver.o \ | ||
ethosu_buffer.o \ | ||
ethosu_device.o \ | ||
ethosu_inference.o \ | ||
ethosu_mailbox.o \ | ||
ethosu_network.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,312 @@ | ||
/* | ||
* (C) COPYRIGHT 2020-2021 Arm Limited. All rights reserved. | ||
* | ||
* 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. | ||
* | ||
* SPDX-License-Identifier: GPL-2.0-only | ||
*/ | ||
|
||
/**************************************************************************** | ||
* Includes | ||
****************************************************************************/ | ||
|
||
#include "ethosu_buffer.h" | ||
|
||
#include "ethosu_device.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/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 long ethosu_buffer_ioctl(struct file *file, | ||
unsigned int cmd, | ||
unsigned long arg); | ||
|
||
static const struct file_operations ethosu_buffer_fops = { | ||
.release = ðosu_buffer_release, | ||
.mmap = ðosu_buffer_mmap, | ||
.unlocked_ioctl = ðosu_buffer_ioctl, | ||
#ifdef CONFIG_COMPAT | ||
.compat_ioctl = ðosu_buffer_ioctl, | ||
#endif | ||
}; | ||
|
||
/**************************************************************************** | ||
* Functions | ||
****************************************************************************/ | ||
|
||
/* | ||
* The 'dma-ranges' device tree property for shared dma memory does not seem | ||
* to be fully supported for coherent memory. Therefor we apply the DMA range | ||
* offset ourselves. | ||
*/ | ||
static dma_addr_t ethosu_buffer_dma_ranges(struct device *dev, | ||
dma_addr_t dma_addr, | ||
size_t dma_buf_size) | ||
{ | ||
struct device_node *node = dev->of_node; | ||
const __be32 *ranges; | ||
int len; | ||
int naddr; | ||
int nsize; | ||
int inc; | ||
int i; | ||
|
||
if (!node) | ||
return dma_addr; | ||
|
||
/* Get the #address-cells and #size-cells properties */ | ||
naddr = of_n_addr_cells(node); | ||
nsize = of_n_size_cells(node); | ||
|
||
/* Read the 'dma-ranges' property */ | ||
ranges = of_get_property(node, "dma-ranges", &len); | ||
if (!ranges || len <= 0) | ||
return dma_addr; | ||
|
||
dev_dbg(dev, "ranges=%p, len=%d, naddr=%d, nsize=%d\n", | ||
ranges, len, naddr, nsize); | ||
|
||
len /= sizeof(*ranges); | ||
inc = naddr + naddr + nsize; | ||
|
||
for (i = 0; (i + inc) <= len; i += inc) { | ||
dma_addr_t daddr; | ||
dma_addr_t paddr; | ||
dma_addr_t size; | ||
|
||
daddr = of_read_number(&ranges[i], naddr); | ||
paddr = of_read_number(&ranges[i + naddr], naddr); | ||
size = of_read_number(&ranges[i + naddr + naddr], nsize); | ||
|
||
dev_dbg(dev, "daddr=0x%llx, paddr=0x%llx, size=0x%llx\n", | ||
daddr, paddr, size); | ||
|
||
if (dma_addr >= paddr && | ||
(dma_addr + dma_buf_size) < (paddr + size)) | ||
return dma_addr + daddr - paddr; | ||
} | ||
|
||
return dma_addr; | ||
} | ||
|
||
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); | ||
|
||
dev_info(buf->edev->dev, "Buffer destroy. handle=0x%pK\n", buf); | ||
|
||
dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr, | ||
buf->dma_addr_orig); | ||
devm_kfree(buf->edev->dev, buf); | ||
} | ||
|
||
static int ethosu_buffer_release(struct inode *inode, | ||
struct file *file) | ||
{ | ||
struct ethosu_buffer *buf = file->private_data; | ||
|
||
dev_info(buf->edev->dev, "Buffer release. handle=0x%pK\n", 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; | ||
int ret; | ||
|
||
dev_info(buf->edev->dev, "Buffer mmap. handle=0x%pK\n", buf); | ||
|
||
ret = dma_mmap_coherent(buf->edev->dev, vma, buf->cpu_addr, | ||
buf->dma_addr_orig, | ||
buf->capacity); | ||
|
||
return ret; | ||
} | ||
|
||
static long ethosu_buffer_ioctl(struct file *file, | ||
unsigned int cmd, | ||
unsigned long arg) | ||
{ | ||
struct ethosu_buffer *buf = file->private_data; | ||
void __user *udata = (void __user *)arg; | ||
int ret = -EINVAL; | ||
|
||
ret = mutex_lock_interruptible(&buf->edev->mutex); | ||
if (ret) | ||
return ret; | ||
|
||
dev_info(buf->edev->dev, "Ioctl. cmd=%u, arg=%lu\n", cmd, arg); | ||
|
||
switch (cmd) { | ||
case ETHOSU_IOCTL_BUFFER_SET: { | ||
struct ethosu_uapi_buffer uapi; | ||
|
||
if (copy_from_user(&uapi, udata, sizeof(uapi))) | ||
break; | ||
|
||
dev_info(buf->edev->dev, | ||
"Ioctl: Buffer set. size=%u, offset=%u\n", | ||
uapi.size, uapi.offset); | ||
|
||
ret = ethosu_buffer_resize(buf, uapi.size, uapi.offset); | ||
break; | ||
} | ||
case ETHOSU_IOCTL_BUFFER_GET: { | ||
struct ethosu_uapi_buffer uapi; | ||
|
||
uapi.size = buf->size; | ||
uapi.offset = buf->offset; | ||
|
||
dev_info(buf->edev->dev, | ||
"Ioctl: Buffer get. size=%u, offset=%u\n", | ||
uapi.size, uapi.offset); | ||
|
||
if (copy_to_user(udata, &uapi, sizeof(uapi))) | ||
break; | ||
|
||
ret = 0; | ||
break; | ||
} | ||
default: { | ||
dev_err(buf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu", | ||
cmd, arg); | ||
break; | ||
} | ||
} | ||
|
||
mutex_unlock(&buf->edev->mutex); | ||
|
||
return ret; | ||
} | ||
|
||
int ethosu_buffer_create(struct ethosu_device *edev, | ||
size_t capacity) | ||
{ | ||
struct ethosu_buffer *buf; | ||
int ret = -ENOMEM; | ||
|
||
buf = devm_kzalloc(edev->dev, sizeof(*buf), GFP_KERNEL); | ||
if (!buf) | ||
return -ENOMEM; | ||
|
||
buf->edev = edev; | ||
buf->capacity = capacity; | ||
buf->offset = 0; | ||
buf->size = 0; | ||
kref_init(&buf->kref); | ||
|
||
buf->cpu_addr = dma_alloc_coherent(buf->edev->dev, capacity, | ||
&buf->dma_addr_orig, GFP_KERNEL); | ||
if (!buf->cpu_addr) | ||
goto free_buf; | ||
|
||
buf->dma_addr = ethosu_buffer_dma_ranges(buf->edev->dev, | ||
buf->dma_addr_orig, | ||
buf->capacity); | ||
|
||
ret = anon_inode_getfd("ethosu-buffer", ðosu_buffer_fops, buf, | ||
O_RDWR | O_CLOEXEC); | ||
if (ret < 0) | ||
goto free_dma; | ||
|
||
buf->file = fget(ret); | ||
fput(buf->file); | ||
|
||
dev_info(buf->edev->dev, | ||
"Buffer create. handle=0x%pK, capacity=%zu, cpu_addr=0x%pK, dma_addr=0x%llx, dma_addr_orig=0x%llx, phys_addr=0x%llx\n", | ||
buf, capacity, buf->cpu_addr, buf->dma_addr, | ||
buf->dma_addr_orig, virt_to_phys(buf->cpu_addr)); | ||
|
||
return ret; | ||
|
||
free_dma: | ||
dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr, | ||
buf->dma_addr_orig); | ||
|
||
free_buf: | ||
devm_kfree(buf->edev->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); | ||
} | ||
|
||
int ethosu_buffer_resize(struct ethosu_buffer *buf, | ||
size_t size, | ||
size_t offset) | ||
{ | ||
if ((size + offset) > buf->capacity) | ||
return -EINVAL; | ||
|
||
buf->size = size; | ||
buf->offset = offset; | ||
|
||
return 0; | ||
} |
Oops, something went wrong.