accel: Add Arm Ethos-U NPU
Subsystems (6)
Rob Herring (Arm)
maintainerv4 cover accel: Add Arm Ethos-U NPU
The Arm Ethos-U65/85 NPUs are designed for edge AI inference applications[0].
The driver works with Mesa Teflon. A merge request for Ethos support is here[1]. The UAPI should also be compatible with the downstream (open source) driver stack[2] and Vela compiler though that has not been implemented.
Testing so far has been on i.MX93 boards with Ethos-U65 and a FVP model with Ethos-U85. More work is needed in mesa for handling U85 command stream differences, but that doesn't affect the UABI.
A git tree is here[3].
Rob
[0] https://www.arm.com/products/silicon-ip-cpu?families=ethos%20npus
[1] https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36699/
[2] https://gitlab.arm.com/artificial-intelligence/ethos-u/
[3] git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git ethos-v4
---
Changes in v4:
- Use bulk clk API
- Various whitespace fixes mostly due to ethos->ethosu rename
- Drop error check on dma_set_mask_and_coherent()
- Drop unnecessary pm_runtime_mark_last_busy() call
- Move variable declarations out of switch (a riscv/clang build failure)
- Use lowercase hex in all defines
- Drop unused ethosu_device.coherent member
- Add comments on all locks
- Link to v3: https://lore.kernel.org/r/[email protected]
Changes in v3:
- Rework and improve job submit validation
- Rename ethos to ethosu. There was an Ethos-Nxx that's unrelated.
- Add missing init for sched_lock mutex
- Drop some prints to debug level
- Fix i.MX93 SRAM accesses (AXI config)
- Add U85 AXI configuration and test on FVP with U85
- Print the current cmd value on timeout
- Link to v2: https://lore.kernel.org/r/[email protected]
Changes in v2:
- Rebase on v6.17-rc1 adapting to scheduler changes
- scheduler: Drop the reset workqueue. According to the scheduler docs, we don't need it since we have a single h/w queue.
- scheduler: Rework the timeout handling to continue running if we are making progress. Fixes timeouts on larger jobs.
- Reset the NPU on resume so it's in a known state
- Add error handling on clk_get() calls
- Fix drm_mm splat on module unload. We were missing a put on the cmdstream BO in the scheduler clean-up.
- Fix 0-day report needing explicit bitfield.h include
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Rob Herring (Arm) (2):
dt-bindings: npu: Add Arm Ethos-U65/U85
accel: Add Arm Ethos-U NPU driver
.../devicetree/bindings/npu/arm,ethos.yaml | 79 +++ MAINTAINERS | 9 + drivers/accel/Kconfig | 1 + drivers/accel/Makefile | 1 + drivers/accel/ethosu/Kconfig | 10 + drivers/accel/ethosu/Makefile | 4 + drivers/accel/ethosu/ethosu_device.h | 190 ++++++ drivers/accel/ethosu/ethosu_drv.c | 418 ++++++++++++ drivers/accel/ethosu/ethosu_drv.h | 15 + drivers/accel/ethosu/ethosu_gem.c | 710 +++++++++++++++++++++ drivers/accel/ethosu/ethosu_gem.h | 46 ++ drivers/accel/ethosu/ethosu_job.c | 539 ++++++++++++++++ drivers/accel/ethosu/ethosu_job.h | 41 ++ include/uapi/drm/ethosu_accel.h | 261 ++++++++ 14 files changed, 2324 insertions(+)
---
base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787
change-id: 20250715-ethos-3fdd39ef6f19
Best regards,
Signature · 1 line
Rob Herring (Arm) <[email protected]>
Rob Herring (Arm)
maintainerPATCH v4 1/2 dt-bindings: npu: Add Arm Ethos-U65/U85
Add a binding schema for Arm Ethos-U65/U85 NPU. The Arm Ethos-U NPUs are designed for edge AI inference applications.
--- .../devicetree/bindings/npu/arm,ethos.yaml | 79 ++++++++++++++++++++++ 1 file changed, 79 insertions(+)
Documentation/devicetree/bindings/npu/arm,ethos.yaml new file +79 -0
@@ -0,0 +1,79 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/npu/arm,ethos.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm Ethos U65/U85 + +maintainers: + - Rob Herring <[email protected]> + +description: > + The Arm Ethos-U NPUs are designed for IoT inference applications. The NPUs + can accelerate 8-bit and 16-bit integer quantized networks: + + Transformer networks (U85 only) + Convolutional Neural Networks (CNN) + Recurrent Neural Networks (RNN) + + Further documentation is available here: + + U65 TRM: https://developer.arm.com/documentation/102023/ + U85 TRM: https://developer.arm.com/documentation/102685/ + +properties: + compatible: + oneOf: + - items: + - enum: + - fsl,imx93-npu + - const: arm,ethos-u65 + - items: + - {} + - const: arm,ethos-u85 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: core + - const: apb + + power-domains: + maxItems: 1 + + sram: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/imx93-clock.h> + + npu@4a900000 { + compatible = "fsl,imx93-npu", "arm,ethos-u65"; + reg = <0x4a900000 0x1000>; + interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&mlmix>; + clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>; + clock-names = "core", "apb"; + sram = <&sram>; + }; +...
Signature · 1 line
2.51.0
Rob Herring (Arm)
maintainerPATCH v4 2/2 accel: Add Arm Ethos-U NPU driver
In reply to Rob Herring (Arm)Add a driver for Arm Ethos-U65/U85 NPUs. The Ethos-U NPU has a relatively simple interface with single command stream to describe buffers, operation settings, and network operations. It supports up to 8 memory regions (though no h/w bounds on a region). The Ethos NPUs are designed to use an SRAM for scratch memory. Region 2 is reserved for SRAM (like the downstream driver stack and compiler). Userspace doesn't need access to the SRAM.
The h/w has no MMU nor external IOMMU and is a DMA engine which can read and write anywhere in memory without h/w bounds checks. The user submitted command streams must be validated against the bounds of the GEM BOs. This is similar to the VC4 design which validates shaders.
The job submit is based on the rocket driver for the Rockchip NPU utilizing the GPU scheduler. It is simpler as there's only 1 core rather than 3.
Tested on i.MX93 platform (U65) and FVP (U85) with WIP Mesa Teflon support.
---
v4:
- Use bulk clk API
- Various whitespace fixes mostly due to ethos->ethosu rename
- Drop error check on dma_set_mask_and_coherent()
- Drop unnecessary pm_runtime_mark_last_busy() call
- Move variable declarations out of switch (a riscv/clang build failure)
- Use lowercase hex in all defines
- Drop unused ethosu_device.coherent member
- Add comments on all locks
v3:
- Rework and improve job submit validation
- Rename ethos to ethosu. There was an Ethos-Nxx that's unrelated.
- Add missing init for sched_lock mutex
- Drop some prints to debug level
- Fix i.MX93 SRAM accesses (AXI config)
- Add U85 AXI configuration
- Print the current cmd value on timeout
v2:
- Rebase on v6.17-rc1 adapting to scheduler changes
- scheduler: Drop the reset workqueue. According to the scheduler docs, we don't need it since we have a single h/w queue.
- scheduler: Rework the timeout handling to continue running if we are making progress.
- Reset the NPU on resume so it's in a known state
- Add error handling on clk_get() calls
- Fix drm_mm splat on module unload. We were missing a put on the cmdstream BO in the scheduler clean-up.
- Fix 0-day report needing explicit bitfield.h include
--- MAINTAINERS | 9 + drivers/accel/Kconfig | 1 + drivers/accel/Makefile | 1 + drivers/accel/ethosu/Kconfig | 10 + drivers/accel/ethosu/Makefile | 4 + drivers/accel/ethosu/ethosu_device.h | 190 ++++++++++ drivers/accel/ethosu/ethosu_drv.c | 418 +++++++++++++++++++++ drivers/accel/ethosu/ethosu_drv.h | 15 + drivers/accel/ethosu/ethosu_gem.c | 710 +++++++++++++++++++++++++++++++++++ drivers/accel/ethosu/ethosu_gem.h | 46 +++ drivers/accel/ethosu/ethosu_job.c | 539 ++++++++++++++++++++++++++ drivers/accel/ethosu/ethosu_job.h | 41 ++ include/uapi/drm/ethosu_accel.h | 261 +++++++++++++ 13 files changed, 2245 insertions(+)
MAINTAINERS +9 -0
@@ -2017,6 +2017,15 @@ F: arch/arm64/include/asm/arch_timer.h F: drivers/clocksource/arm_arch_timer.c F: drivers/clocksource/arm_arch_timer_mmio.c +ARM ETHOS-U NPU DRIVER +M: Rob Herring (Arm) <[email protected]> +M: Tomeu Vizoso <[email protected]> +L: [email protected] +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/accel/ethosu/ +F: include/uapi/drm/ethosu_accel.h + ARM GENERIC INTERRUPT CONTROLLER DRIVERS M: Marc Zyngier <[email protected]> L: [email protected] (moderated for non-subscribers)
drivers/accel/Kconfig +1 -0
@@ -25,6 +25,7 @@ menuconfig DRM_ACCEL and debugfs). source "drivers/accel/amdxdna/Kconfig" +source "drivers/accel/ethosu/Kconfig" source "drivers/accel/habanalabs/Kconfig" source "drivers/accel/ivpu/Kconfig" source "drivers/accel/qaic/Kconfig"
drivers/accel/Makefile +1 -0
@@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/ +obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/ obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/ obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/ obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/
drivers/accel/ethosu/Kconfig new file +10 -0
@@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config DRM_ACCEL_ARM_ETHOSU + tristate "Arm Ethos-U65/U85 NPU" + depends on HAS_IOMEM + depends on DRM_ACCEL + select DRM_GEM_DMA_HELPER + select DRM_SCHED + help + Enables driver for Arm Ethos-U65/U85 NPUs
drivers/accel/ethosu/Makefile new file +4 -0
@@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o +ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o
drivers/accel/ethosu/ethosu_device.h new file +190 -0
190 lines not on this page · raw message
drivers/accel/ethosu/ethosu_drv.c new file +418 -0
418 lines not on this page · raw message
drivers/accel/ethosu/ethosu_drv.h new file +15 -0
@@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright 2025 Arm, Ltd. */ +#ifndef __ETHOSU_DRV_H__ +#define __ETHOSU_DRV_H__ + +#include <drm/gpu_scheduler.h> + +struct ethosu_device ; + +struct ethosu_file_priv { +struct ethosu_device *edev; +struct drm_sched_entity sched_entity; +}; + +#endif
drivers/accel/ethosu/ethosu_gem.c new file +710 -0
710 lines not on this page · raw message
drivers/accel/ethosu/ethosu_gem.h new file +46 -0
46 lines not on this page · raw message
drivers/accel/ethosu/ethosu_job.c new file +539 -0
539 lines not on this page · raw message
drivers/accel/ethosu/ethosu_job.h new file +41 -0
41 lines not on this page · raw message
include/uapi/drm/ethosu_accel.h new file +261 -0
261 lines not on this page · raw message
Signature · 1 line
2.51.0
Re: [PATCH v4 2/2] accel: Add Arm Ethos-U NPU driver
Quoted 17 lines · Rob Herring (Arm)
Add a driver for Arm Ethos-U65/U85 NPUs. The Ethos-U NPU has a relatively simple interface with single command stream to describe buffers, operation settings, and network operations. It supports up to 8 memory regions (though no h/w bounds on a region). The Ethos NPUs are designed to use an SRAM for scratch memory. Region 2 is reserved for SRAM (like the downstream driver stack and compiler). Userspace doesn't need access to the SRAM.
The h/w has no MMU nor external IOMMU and is a DMA engine which can read and write anywhere in memory without h/w bounds checks. The user submitted command streams must be validated against the bounds of the GEM BOs. This is similar to the VC4 design which validates shaders.
The job submit is based on the rocket driver for the Rockchip NPU utilizing the GPU scheduler. It is simpler as there's only 1 core rather than 3.
Quoted 6 lines
Tested on i.MX93 platform (U65) and FVP (U85) with WIP Mesa Teflon support.
Signed-off-by Rob Herring (Arm) <[email protected]>---
How to test this driver?
Quoted 10 lines
v4:
- Use bulk clk API
- Various whitespace fixes mostly due toethos->ethosurename
- Drop error check ondma_set_mask_and_coherent()
- Drop unnecessarypm_runtime_mark_last_busy()call
- Move variable declarations out of switch (a riscv/clang build failure)
- Use lowercase hex in all defines
- Drop unused ethosu_device.coherent member
- Add comments on all locks
...
Quoted 11 lines
drivers/accel/ethosu/ethosu_device.h new file +5
-0@@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0-only or MIT */ +/* Copyright 2025 Arm, Ltd. */ + +#ifndef __ETHOSU_DEVICE_H__ +#define __ETHOSU_DEVICE_H__
drivers/accel/ethosu/ethosu_device.h line 15
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ +#include <linux/types.h> + +#include <drm/drm_device.h> +#include <drm/gpu_scheduler.h> + +#include <drm/ethosu_accel.h> + +struct clk ; +struct gen_pool ;
Supposed should include clk.h instead declear a struct.
...
Quoted 11 lines
drivers/accel/ethosu/ethosu_device.h new file +11
-0+ +static int ethosu_open (struct drm_device *ddev,struct drm_file *file) +{ +int ret =0 ; +struct ethosu_file_priv *priv; + +if (!try_module_get (THIS_MODULE )) +return -EINVAL ; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) {
drivers/accel/ethosu/ethosu_device.h line 36
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ ret = -ENOMEM ; + goto err_put_mod; + } + priv->edev =to_ethosu_device (ddev); + + ret =ethosu_job_open (priv); +if (ret) + goto err_free; + + file->driver_priv = priv;
slice simple.
struct ethosu_file_priv __free(kfree) *priv = NULL;
...
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
...
file->driver_priv = no_free_ptr(priv);
Quoted 9 lines
drivers/accel/ethosu/ethosu_device.h new file +9
-0+return 0 ; + +err_free: +kfree (priv); +err_put_mod: +module_put (THIS_MODULE ); +return ret; +} +
...
Quoted 14 lines
drivers/accel/ethosu/ethosu_device.h new file +14
-0+ + +static int ethosu_init (struct ethosu_device *ethosudev) +{ +int ret; +u32 id, config; + + ret =devm_pm_runtime_enable (ethosudev->base.dev); +if (ret) +return ret; + + ret =pm_runtime_resume_and_get (ethosudev->base.dev); +if (ret) +return ret;
drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ +pm_runtime_set_autosuspend_delay (ethosudev->base.dev,50 ); +pm_runtime_use_autosuspend (ethosudev->base.dev); + +/* If PM is disabled, we need to call ethosu_device_resume() manually. */ +if (!IS_ENABLED (CONFIG_PM )) { + ret =ethosu_device_resume (ethosudev->base.dev); +if (ret) +return ret; + }
I think it should call ethosu_device_resume() unconditional before devm_pm_runtime_enable();
ethosu_device_resume();pm_runtime_set_active(); pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50); devm_pm_runtime_enable();
....
return 0;
//pm_runtime_autosuspend(); /* you already call it at caller probe() */
if not CONFIG_PM, ethosu_device_resume() will enable clk unconditional.
If CONFIG_PM, pm_runtime_autosuspend() will turn off clock later.
Quoted 55 lines
drivers/accel/ethosu/ethosu_device.h new file +55
-0+ + ethosudev->npu_info.id = id =readl_relaxed (ethosudev->regs +NPU_REG_ID ); + ethosudev->npu_info.config = config =readl_relaxed (ethosudev->regs +NPU_REG_CONFIG ); + +ethosu_sram_init (ethosudev); + +dev_info (ethosudev->base.dev, +"Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n" , +FIELD_GET (ID_ARCH_MAJOR_MASK , id), +FIELD_GET (ID_ARCH_MINOR_MASK , id), +FIELD_GET (ID_ARCH_PATCH_MASK , id), +FIELD_GET (ID_VER_MAJOR_MASK , id), +FIELD_GET (ID_VER_MINOR_MASK , id), +FIELD_GET (CONFIG_CMD_STREAM_VER_MASK , config), +1 <<FIELD_GET (CONFIG_MACS_PER_CC_MASK , config), + ethosudev->npu_info.sram_size /1024 ); + +return 0 ; +} + +static int ethosu_probe (struct platform_device *pdev) +{ +int ret; +struct ethosu_device *ethosudev; + + ethosudev =devm_drm_dev_alloc (&pdev->dev, ðosu_drm_driver, + struct ethosu_device, base); +if (IS_ERR (ethosudev)) +return -ENOMEM ; +platform_set_drvdata (pdev, ethosudev); + +dma_set_mask_and_coherent (&pdev->dev,DMA_BIT_MASK (40 )); + + ethosudev->regs =devm_platform_ioremap_resource (pdev,0 ); + + ethosudev->num_clks =devm_clk_bulk_get_all (&pdev->dev, ðosudev->clks); +if (ethosudev->num_clks <0 ) +return ethosudev->num_clks; + + ret =ethosu_job_init (ethosudev); +if (ret) +return ret; + + ret =ethosu_init (ethosudev); +if (ret) +return ret; + + ret =drm_dev_register (ðosudev->base,0 ); +if (ret) +pm_runtime_dont_use_autosuspend (ethosudev->base.dev); + +pm_runtime_put_autosuspend (ethosudev->base.dev); +return ret; +} +
...
Quoted 6 lines
drivers/accel/ethosu/ethosu_device.h new file +6
-0+ +static int ethosu_gem_cmdstream_copy_and_validate (struct drm_device *ddev, +u32 __user *ucmds, +struct ethosu_gem_object *bo, +u32 size) +{
drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+struct ethosu_validated_cmdstream_info *info; +u32 *bocmds = bo->base.vaddr; +struct cmd_state st; +int i, ret; + +cmd_state_init (&st); + + info =kzalloc (sizeof (*info),GFP_KERNEL ); +if (!info) +return -ENOMEM ;
this one use cleanup will simple code much
struct ethosu_validated_cmdstream_info __free(free) * info = kzalloc(sizeof(*info), GFP_KERNEL);
drivers/accel/ethosu/ethosu_device.h
Quoted 12 lines
drivers/accel/ethosu/ethosu_device.h new file +12
-0+ info->cmd_size = size; + +for (i =0 ; i < size /4 ; i++) { +bool use_ifm, use_ifm2, use_scale; +u64 dstlen, srclen; +u16 cmd, param; +u32 cmds[2 ]; +u64 addr; + +if (get_user (cmds[0 ], ucmds++)) { + ret = -EFAULT ; + goto fault;
so all goto, can be use
return ret;
Quoted 273 lines
drivers/accel/ethosu/ethosu_device.h new file +273
-0+ } + bocmds[i] = cmds[0 ]; + + cmd = cmds[0 ]; + param = cmds[0 ] >>16 ; + +if (cmd &0x4000 ) { +if (get_user (cmds[1 ], ucmds++)) { + ret = -EFAULT ; + goto fault; + } + i++; + bocmds[i] = cmds[1 ]; + addr =cmd_to_addr (cmds); + } + +switch (cmd) { +case NPU_OP_DMA_START : + srclen =dma_length (info, &st.dma, &st.dma.src); + dstlen =dma_length (info, &st.dma, &st.dma.dst); + +if (st.dma.dst.region >=0 ) + info->output_region[st.dma.dst.region] = true; +dev_dbg (ddev->dev,"cmd: DMA SRC:%d:0x%llx+0x%llx DST:%d:0x%llx+0x%llx\n" , + st.dma.src.region, st.dma.src.offset, srclen, + st.dma.dst.region, st.dma.dst.offset, dstlen); +break ; +case NPU_OP_CONV : +case NPU_OP_DEPTHWISE : + use_ifm2 = param &0x1 ;// weights_ifm2 + use_scale = !(st.ofm.precision &0x100 ); + ret =calc_sizes (ddev, info, cmd, &st, true, use_ifm2, + !use_ifm2, use_scale); +if (ret) + goto fault; +break ; +case NPU_OP_POOL : + use_ifm = param !=0x4 ;// pooling mode + use_scale = !(st.ofm.precision &0x100 ); + ret =calc_sizes (ddev, info, cmd, &st, use_ifm, false, + false, use_scale); +if (ret) + goto fault; +break ; +case NPU_OP_ELEMENTWISE : + use_ifm2 = !((st.ifm2.broadcast ==8 ) || (param ==5 ) || + (param ==6 ) || (param ==7 ) || (param ==0x24 )); + use_ifm = st.ifm.broadcast !=8 ; + ret =calc_sizes_elemwise (ddev, info, cmd, &st, use_ifm, use_ifm2); +if (ret) + goto fault; +break ; +case NPU_OP_RESIZE :// U85 only +WARN_ON (1 );// TODO +break ; +case NPU_SET_KERNEL_WIDTH_M1 : + st.ifm.width = param; +break ; +case NPU_SET_KERNEL_HEIGHT_M1 : + st.ifm.height[2 ] = param; +break ; +case NPU_SET_KERNEL_STRIDE : + st.ifm.stride_kernel = param; +break ; +case NPU_SET_IFM_PAD_TOP : + st.ifm.pad_top = param &0x7f ; +break ; +case NPU_SET_IFM_PAD_LEFT : + st.ifm.pad_left = param &0x7f ; +break ; +case NPU_SET_IFM_PAD_RIGHT : + st.ifm.pad_right = param &0xff ; +break ; +case NPU_SET_IFM_PAD_BOTTOM : + st.ifm.pad_bottom = param &0xff ; +break ; +case NPU_SET_IFM_DEPTH_M1 : + st.ifm.depth = param; +break ; +case NPU_SET_IFM_PRECISION : + st.ifm.precision = param; +break ; +case NPU_SET_IFM_BROADCAST : + st.ifm.broadcast = param; +break ; +case NPU_SET_IFM_REGION : + st.ifm.region = param &0x7f ; +break ; +case NPU_SET_IFM_WIDTH0_M1 : + st.ifm.width0 = param; +break ; +case NPU_SET_IFM_HEIGHT0_M1 : + st.ifm.height[0 ] = param; +break ; +case NPU_SET_IFM_HEIGHT1_M1 : + st.ifm.height[1 ] = param; +break ; +case NPU_SET_IFM_BASE0 : +case NPU_SET_IFM_BASE1 : +case NPU_SET_IFM_BASE2 : +case NPU_SET_IFM_BASE3 : + st.ifm.base[cmd &0x3 ] = addr; +break ; +case NPU_SET_IFM_STRIDE_X : + st.ifm.stride_x = addr; +break ; +case NPU_SET_IFM_STRIDE_Y : + st.ifm.stride_y = addr; +break ; +case NPU_SET_IFM_STRIDE_C : + st.ifm.stride_c = addr; +break ; + +case NPU_SET_OFM_WIDTH_M1 : + st.ofm.width = param; +break ; +case NPU_SET_OFM_HEIGHT_M1 : + st.ofm.height[2 ] = param; +break ; +case NPU_SET_OFM_DEPTH_M1 : + st.ofm.depth = param; +break ; +case NPU_SET_OFM_PRECISION : + st.ofm.precision = param; +break ; +case NPU_SET_OFM_REGION : + st.ofm.region = param &0x7 ; +break ; +case NPU_SET_OFM_WIDTH0_M1 : + st.ofm.width0 = param; +break ; +case NPU_SET_OFM_HEIGHT0_M1 : + st.ofm.height[0 ] = param; +break ; +case NPU_SET_OFM_HEIGHT1_M1 : + st.ofm.height[1 ] = param; +break ; +case NPU_SET_OFM_BASE0 : +case NPU_SET_OFM_BASE1 : +case NPU_SET_OFM_BASE2 : +case NPU_SET_OFM_BASE3 : + st.ofm.base[cmd &0x3 ] = addr; +break ; +case NPU_SET_OFM_STRIDE_X : + st.ofm.stride_x = addr; +break ; +case NPU_SET_OFM_STRIDE_Y : + st.ofm.stride_y = addr; +break ; +case NPU_SET_OFM_STRIDE_C : + st.ofm.stride_c = addr; +break ; + +case NPU_SET_IFM2_BROADCAST : + st.ifm2.broadcast = param; +break ; +case NPU_SET_IFM2_PRECISION : + st.ifm2.precision = param; +break ; +case NPU_SET_IFM2_REGION : + st.ifm2.region = param &0x7 ; +break ; +case NPU_SET_IFM2_WIDTH0_M1 : + st.ifm2.width0 = param; +break ; +case NPU_SET_IFM2_HEIGHT0_M1 : + st.ifm2.height[0 ] = param; +break ; +case NPU_SET_IFM2_HEIGHT1_M1 : + st.ifm2.height[1 ] = param; +break ; +case NPU_SET_IFM2_BASE0 : +case NPU_SET_IFM2_BASE1 : +case NPU_SET_IFM2_BASE2 : +case NPU_SET_IFM2_BASE3 : + st.ifm2.base[cmd &0x3 ] = addr; +break ; +case NPU_SET_IFM2_STRIDE_X : + st.ifm2.stride_x = addr; +break ; +case NPU_SET_IFM2_STRIDE_Y : + st.ifm2.stride_y = addr; +break ; +case NPU_SET_IFM2_STRIDE_C : + st.ifm2.stride_c = addr; +break ; + +case NPU_SET_WEIGHT_REGION : + st.weight[0 ].region = param &0x7 ; +break ; +case NPU_SET_SCALE_REGION : + st.scale[0 ].region = param &0x7 ; +break ; +case NPU_SET_WEIGHT_BASE : + st.weight[0 ].base = addr; +break ; +case NPU_SET_WEIGHT_LENGTH : + st.weight[0 ].length = cmds[1 ]; +break ; +case NPU_SET_SCALE_BASE : + st.scale[0 ].base = addr; +break ; +case NPU_SET_SCALE_LENGTH : + st.scale[0 ].length = cmds[1 ]; +break ; +case NPU_SET_WEIGHT1_BASE : + st.weight[1 ].base = addr; +break ; +case NPU_SET_WEIGHT1_LENGTH : + st.weight[1 ].length = cmds[1 ]; +break ; +case NPU_SET_SCALE1_BASE :// NPU_SET_WEIGHT2_BASE (U85) +if (0 /*U85*/ ) + st.weight[2 ].base = addr; +else + st.scale[1 ].base = addr; +break ; +case NPU_SET_SCALE1_LENGTH :// NPU_SET_WEIGHT2_LENGTH (U85) +if (0 /*U85*/ ) + st.weight[1 ].length = cmds[1 ]; +else + st.scale[1 ].length = cmds[1 ]; +break ; +case NPU_SET_WEIGHT3_BASE : + st.weight[3 ].base = addr; +break ; +case NPU_SET_WEIGHT3_LENGTH : + st.weight[3 ].length = cmds[1 ]; +break ; + +case NPU_SET_DMA0_SRC_REGION : +if (param &0x100 ) + st.dma.src.region =-1 ; +else + st.dma.src.region = param &0x7 ; + st.dma.mode = (param >>9 ) &0x3 ; +break ; +case NPU_SET_DMA0_DST_REGION : +if (param &0x100 ) + st.dma.dst.region =-1 ; +else + st.dma.dst.region = param &0x7 ; +break ; +case NPU_SET_DMA0_SIZE0 : + st.dma.size0 = param; +break ; +case NPU_SET_DMA0_SIZE1 : + st.dma.size1 = param; +break ; +case NPU_SET_DMA0_SRC_STRIDE0 : + st.dma.src.stride[0 ] = ((s64 )addr <<24 ) >>24 ; +break ; +case NPU_SET_DMA0_SRC_STRIDE1 : + st.dma.src.stride[1 ] = ((s64 )addr <<24 ) >>24 ; +break ; +case NPU_SET_DMA0_DST_STRIDE0 : + st.dma.dst.stride[0 ] = ((s64 )addr <<24 ) >>24 ; +break ; +case NPU_SET_DMA0_DST_STRIDE1 : + st.dma.dst.stride[1 ] = ((s64 )addr <<24 ) >>24 ; +break ; +case NPU_SET_DMA0_SRC : + st.dma.src.offset = addr; +break ; +case NPU_SET_DMA0_DST : + st.dma.dst.offset = addr; +break ; +case NPU_SET_DMA0_LEN : + st.dma.src.len = st.dma.dst.len = addr; +break ; +default : +break ; + }
drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ } + +for (i =0 ; i <NPU_BASEP_REGION_MAX ; i++) { +if (!info->region_size[i]) +continue ; +dev_dbg (ddev->dev,"region %d max size: 0x%llx\n" , + i, info->region_size[i]); + } + + bo->info = info;
bo->info = no_free_ptr(info);
Quoted 6 lines
drivers/accel/ethosu/ethosu_device.h new file +6
-0+return 0 ; + +fault: +kfree (info); +return ret; +}
...
Quoted 12 lines
drivers/accel/ethosu/ethosu_device.h new file +12
-0+ +static const struct drm_sched_backend_ops ethosu_sched_ops = { + .run_job = ethosu_job_run, + .timedout_job = ethosu_job_timedout, + .free_job = ethosu_job_free +}; + +int ethosu_job_init (struct ethosu_device *dev) +{ +struct drm_sched_init_args args = { + .ops = ðosu_sched_ops, + .num_rqs =DRM_SCHED_PRIORITY_COUNT ,
drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ .credit_limit = 1, + .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), + .name = dev_name(dev->base.dev), + .dev = dev->base.dev, + }; +int ret; + +spin_lock_init (&dev->fence_lock); +mutex_init (&dev->job_lock); +mutex_init (&dev->sched_lock);
use devm_mutex_init();
Quoted 165 lines
drivers/accel/ethosu/ethosu_device.h new file +165
-0+ + dev->irq =platform_get_irq (to_platform_device (dev->base.dev),0 ); +if (dev->irq <0 ) +return dev->irq; + + ret =devm_request_threaded_irq (dev->base.dev, dev->irq, + ethosu_job_irq_handler, + ethosu_job_irq_handler_thread, +IRQF_SHARED ,KBUILD_MODNAME , + dev); +if (ret) { +dev_err (dev->base.dev,"failed to request irq" ); +return ret; + } + + dev->fence_context =dma_fence_context_alloc (1 ); + + ret =drm_sched_init (&dev->sched, &args); +if (ret) { +dev_err (dev->base.dev,"Failed to create scheduler: %d." , ret); + goto err_sched; + } + +return 0 ; + +err_sched: +drm_sched_fini (&dev->sched); +return ret; +} + +void ethosu_job_fini (struct ethosu_device *dev) +{ +drm_sched_fini (&dev->sched); +} + +int ethosu_job_open (struct ethosu_file_priv *ethosu_priv) +{ +struct ethosu_device *dev = ethosu_priv->edev; +struct drm_gpu_scheduler *sched = &dev->sched; +int ret; + + ret =drm_sched_entity_init (ðosu_priv->sched_entity, +DRM_SCHED_PRIORITY_NORMAL , + &sched,1 ,NULL ); +return WARN_ON (ret); +} + +void ethosu_job_close (struct ethosu_file_priv *ethosu_priv) +{ +struct drm_sched_entity *entity = ðosu_priv->sched_entity; + +drm_sched_entity_destroy (entity); +} + +int ethosu_job_is_idle (struct ethosu_device *dev) +{ +/* If there are any jobs in this HW queue, we're not idle */ +if (atomic_read (&dev->sched.credit_count)) +return false; + +return true; +} + +static int ethosu_ioctl_submit_job (struct drm_device *dev,struct drm_file *file, +struct drm_ethosu_job *job) +{ +struct ethosu_device *edev =to_ethosu_device (dev); +struct ethosu_file_priv *file_priv = file->driver_priv; +struct ethosu_job *ejob =NULL ; +struct ethosu_validated_cmdstream_info *cmd_info; +int ret =0 ; + +/* BO region 2 is reserved if SRAM is used */ +if (job->region_bo_handles[ETHOSU_SRAM_REGION ] && job->sram_size) +return -EINVAL ; + +if (edev->npu_info.sram_size < job->sram_size) +return -EINVAL ; + + ejob =kzalloc (sizeof (*ejob),GFP_KERNEL ); +if (!ejob) +return -ENOMEM ; + +kref_init (&ejob->refcount); + + ejob->dev = edev; + ejob->sram_size = job->sram_size; + + ret =drm_sched_job_init (&ejob->base, + &file_priv->sched_entity, +1 ,NULL , file->client_id); +if (ret) + goto out_put_job; + + ejob->cmd_bo =drm_gem_object_lookup (file, job->cmd_bo); +if (!ejob->cmd_bo) { + ret = -ENOENT ; + goto out_cleanup_job; + } + cmd_info =to_ethosu_bo (ejob->cmd_bo)->info; +if (!cmd_info) { + ret = -EINVAL ; + goto out_cleanup_job; + } + +for (int i =0 ; i <NPU_BASEP_REGION_MAX ; i++) { +struct drm_gem_object *gem; + +/* Can only omit a BO handle if the region is not used or used for SRAM */ +if (!job->region_bo_handles[i] && + (!cmd_info->region_size[i] || (i ==ETHOSU_SRAM_REGION && job->sram_size))) +continue ; + +if (job->region_bo_handles[i] && !cmd_info->region_size[i]) { +dev_err (dev->dev, +"Cmdstream BO handle %d set for unused region %d\n" , + job->region_bo_handles[i], i); + ret = -EINVAL ; + goto out_cleanup_job; + } + + gem =drm_gem_object_lookup (file, job->region_bo_handles[i]); +if (!gem) { +dev_err (dev->dev, +"Invalid BO handle %d for region %d\n" , + job->region_bo_handles[i], i); + ret = -ENOENT ; + goto out_cleanup_job; + } + + ejob->region_bo[ejob->region_cnt] = gem; + ejob->region_bo_num[ejob->region_cnt] = i; + ejob->region_cnt++; + +if (to_ethosu_bo (gem)->info) { +dev_err (dev->dev, +"Cmdstream BO handle %d used for region %d\n" , + job->region_bo_handles[i], i); + ret = -EINVAL ; + goto out_cleanup_job; + } + +/* Verify the command stream doesn't have accesses outside the BO */ +if (cmd_info->region_size[i] > gem->size) { +dev_err (dev->dev, +"cmd stream region %d size greater than BO size (%llu > %zu)\n" , + i, cmd_info->region_size[i], gem->size); + ret = -EOVERFLOW ; + goto out_cleanup_job; + } + } + ret =ethosu_job_push (ejob); + +out_cleanup_job: +if (ret) +drm_sched_job_cleanup (&ejob->base); +out_put_job: +ethosu_job_put (ejob); + +return ret; +} + +int ethosu_ioctl_submit (struct drm_device *dev,void *data,struct drm_file *file) +{ +struct drm_ethosu_submit *args = data;
drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+struct drm_ethosu_job *jobs; +int ret =0 ; +unsigned int i =0 ; + +if (args->pad) { +drm_dbg (dev,"Reserved field in drm_ethosu_submit struct should be 0.\n" ); +return -EINVAL ; + } + + jobs =kvmalloc_array (args->job_count,sizeof (*jobs),GFP_KERNEL );
here use _free() can remote all goto
Frank
Quoted 340 lines
drivers/accel/ethosu/ethosu_device.h new file +22
-0+if (!jobs) +return -ENOMEM ; + +if (copy_from_user (jobs, + (void __user *)(uintptr_t )args->jobs, + args->job_count *sizeof (*jobs))) { + ret = -EFAULT ; +drm_dbg (dev,"Failed to copy incoming job array\n" ); + goto exit; + } + +for (i =0 ; i < args->job_count; i++) { + ret =ethosu_ioctl_submit_job (dev, file, &jobs[i]); +if (ret) +break ; + } + +exit: +kfree (jobs); + +return ret; +}drivers/accel/ethosu/ethosu_job.h new file +41
-0@@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright 2024-2025 Tomeu Vizoso <[email protected]> */ +/* Copyright 2025 Arm, Ltd. */ + +#ifndef __ETHOSU_JOB_H__ +#define __ETHOSU_JOB_H__ + +#include <linux/kref.h> +#include <drm/gpu_scheduler.h> + +struct ethosu_device ; +struct ethosu_file_priv ; + +struct ethosu_job { +struct drm_sched_job base; +struct ethosu_device *dev; + +struct drm_gem_object *cmd_bo; +struct drm_gem_object *region_bo[NPU_BASEP_REGION_MAX ]; +u8 region_bo_num[NPU_BASEP_REGION_MAX ]; +u8 region_cnt; +u32 sram_size; + +/* Fence to be signaled by drm-sched once its done with the job */ +struct dma_fence *inference_done_fence; + +/* Fence to be signaled by IRQ handler when the job is complete. */ +struct dma_fence *done_fence; + +struct kref refcount; +}; + +int ethosu_ioctl_submit (struct drm_device *dev,void *data,struct drm_file *file); + +int ethosu_job_init (struct ethosu_device *dev); +void ethosu_job_fini (struct ethosu_device *dev); +int ethosu_job_open (struct ethosu_file_priv *ethosu_priv); +void ethosu_job_close (struct ethosu_file_priv *ethosu_priv); +int ethosu_job_is_idle (struct ethosu_device *dev); + +#endif include/uapi/drm/ethosu_accel.h new file +261
-1@@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright (C) 2025 Arm, Ltd. */ +#ifndef _ETHOSU_DRM_H_ +#define _ETHOSU_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** +* DOC: IOCTL IDs +* +* enum drm_ethosu_ioctl_id - IOCTL IDs +* +* Place new ioctls at the end, don't re-order, don't replace or remove entries. +* +* These IDs are not meant to be used directly. Use the DRM_IOCTL_ETHOSU_xxx +* definitions instead. +*/ +enum drm_ethosu_ioctl_id { +/** @DRM_ETHOSU_DEV_QUERY: Query device information. */ +DRM_ETHOSU_DEV_QUERY =0 , + +/** @DRM_ETHOSU_BO_CREATE: Create a buffer object. */ +DRM_ETHOSU_BO_CREATE , + +/** @DRM_ETHOSU_BO_WAIT: Wait on a buffer object's fence. */ +DRM_ETHOSU_BO_WAIT , + +/** +* @DRM_ETHOSU_BO_MMAP_OFFSET: Get the file offset to pass to +* mmap to map a GEM object. +*/ +DRM_ETHOSU_BO_MMAP_OFFSET , + +/** +* @DRM_ETHOSU_CMDSTREAM_BO_CREATE: Create a command stream buffer +* object. +*/ +DRM_ETHOSU_CMDSTREAM_BO_CREATE , + +/** @DRM_ETHOSU_SUBMIT: Submit a job and BOs to run. */ +DRM_ETHOSU_SUBMIT , +}; + +/** +* DOC: IOCTL arguments +*/ + +/** +* enum drm_ethosu_dev_query_type - Query type +* +* Place new types at the end, don't re-order, don't remove or replace. +*/ +enum drm_ethosu_dev_query_type { +/** @DRM_ETHOSU_DEV_QUERY_NPU_INFO: Query NPU information. */ +DRM_ETHOSU_DEV_QUERY_NPU_INFO =0 , +}; + +/** +* struct drm_ethosu_gpu_info - NPU information +* +* Structure grouping all queryable information relating to the NPU. +*/ +struct drm_ethosu_npu_info { +/** @id : NPU ID. */ +__u32 id; +#define DRM_ETHOSU_ARCH_MAJOR (x) ((x) >> 28) +#define DRM_ETHOSU_ARCH_MINOR (x) (((x) >> 20) & 0xff) +#define DRM_ETHOSU_ARCH_PATCH (x) (((x) >> 16) & 0xf) +#define DRM_ETHOSU_PRODUCT_MAJOR (x) (((x) >> 12) & 0xf) +#define DRM_ETHOSU_VERSION_MAJOR (x) (((x) >> 8) & 0xf) +#define DRM_ETHOSU_VERSION_MINOR (x) (((x) >> 4) & 0xff) +#define DRM_ETHOSU_VERSION_STATUS (x) ((x) & 0xf) + +/** @gpu_rev: GPU revision. */ +__u32 config; + +__u32 sram_size; +}; + +/** +* struct drm_ethosu_dev_query - Arguments passed to DRM_ETHOSU_IOCTL_DEV_QUERY +*/ +struct drm_ethosu_dev_query { +/** @type: the query type (see drm_ethosu_dev_query_type). */ +__u32 type; + +/** +* @size: size of the type being queried. +* +* If pointer is NULL, size is updated by the driver to provide the +* output structure size. If pointer is not NULL, the driver will +* only copy min(size, actual_structure_size) bytes to the pointer, +* and update the size accordingly. This allows us to extend query +* types without breaking userspace. +*/ +__u32 size; + +/** +* @pointer: user pointer to a query type struct. +* +* Pointer can be NULL, in which case, nothing is copied, but the +* actual structure size is returned. If not NULL, it must point to +* a location that's large enough to hold size bytes. +*/ +__u64 pointer; +}; + +/** +* enum drm_ethosu_bo_flags - Buffer object flags, passed at creation time. +*/ +enum drm_ethosu_bo_flags { +/** +* @DRM_ETHOSU_BO_NO_MMAP: The buffer object will never be CPU-mapped +* in userspace. +*/ +DRM_ETHOSU_BO_NO_MMAP = (1 <<0 ), +}; + +/** +* struct drm_ethosu_bo_create - Arguments passed to DRM_IOCTL_ETHOSU_BO_CREATE. +*/ +struct drm_ethosu_bo_create { +/** +* @size: Requested size for the object +* +* The (page-aligned) allocated size for the object will be returned. +*/ +__u64 size; + +/** +* @flags: Flags. Must be a combination of drm_ethosu_bo_flags flags. +*/ +__u32 flags; + +/** +* @handle: Returned handle for the object. +* +* Object handles are nonzero. +*/ +__u32 handle; +}; + +/** +* struct drm_ethosu_bo_mmap_offset - Arguments passed to DRM_IOCTL_ETHOSU_BO_MMAP_OFFSET. +*/ +struct drm_ethosu_bo_mmap_offset { +/** @handle: Handle of the object we want an mmap offset for. */ +__u32 handle; + +/** @pad: MBZ. */ +__u32 pad; + +/** @offset: The fake offset to use for subsequent mmap calls. */ +__u64 offset; +}; + +/** +* struct drm_ethosu_wait_bo - ioctl argument for waiting for +* completion of the last DRM_ETHOSU_SUBMIT on a BO. +* +* This is useful for cases where multiple processes might be +* rendering to a BO and you want to wait for all rendering to be +* completed. +*/ +struct drm_ethosu_bo_wait { +__u32 handle; +__u32 pad; +__s64 timeout_ns;/* absolute */ +}; + +struct drm_ethosu_cmdstream_bo_create { +/* Size of the data argument. */ +__u32 size; + +/* Flags, currently must be 0. */ +__u32 flags; + +/* Pointer to the data. */ +__u64 data; + +/** Returned GEM handle for the BO. */ +__u32 handle; + +/* Pad, must be 0. */ +__u32 pad; +}; + +/** +* struct drm_ethosu_job - A job to be run on the NPU +* +* The kernel will schedule the execution of this job taking into account its +* dependencies with other jobs. All tasks in the same job will be executed +* sequentially on the same core, to benefit from memory residency in SRAM. +*/ +struct drm_ethosu_job { +/** Input: BO handle for cmdstream. */ +__u32 cmd_bo; + +/** Input: Amount of SRAM to use. */ +__u32 sram_size; + +#define ETHOSU_MAX_REGIONS 8 +/** Input: Array of BO handles for each region. */ +__u32 region_bo_handles[ETHOSU_MAX_REGIONS ]; +}; + +/** +* struct drm_ethosu_submit - ioctl argument for submitting commands to the NPU. +* +* The kernel will schedule the execution of these jobs in dependency order. +*/ +struct drm_ethosu_submit { +/** Input: Pointer to an array of struct drm_ethosu_job. */ +__u64 jobs; + +/** Input: Number of jobs passed in. */ +__u32 job_count; + +/** Reserved, must be zero. */ +__u32 pad; +}; + +/** +* DRM_IOCTL_ETHOSU() - Build a ethosu IOCTL number +* @__access: Access type. Must be R, W or RW. +* @__id: One of the DRM_ETHOSU_xxx id. +* @__type: Suffix of the type being passed to the IOCTL. +* +* Don't use this macro directly, use the DRM_IOCTL_ETHOSU_xxx +* values instead. +* +* Return: An IOCTL number to be passed to ioctl() from userspace. +*/ +#define DRM_IOCTL_ETHOSU (__access, __id, __type) \ + DRM_IO ## __access(DRM_COMMAND_BASE + DRM_ETHOSU_ ## __id, \ + struct drm_ethosu_ ## __type) + +enum { +DRM_IOCTL_ETHOSU_DEV_QUERY = +DRM_IOCTL_ETHOSU (WR ,DEV_QUERY , dev_query), +DRM_IOCTL_ETHOSU_BO_CREATE = +DRM_IOCTL_ETHOSU (WR ,BO_CREATE , bo_create), +DRM_IOCTL_ETHOSU_BO_WAIT = +DRM_IOCTL_ETHOSU (WR ,BO_WAIT , bo_wait), +DRM_IOCTL_ETHOSU_BO_MMAP_OFFSET = +DRM_IOCTL_ETHOSU (WR ,BO_MMAP_OFFSET , bo_mmap_offset), +DRM_IOCTL_ETHOSU_CMDSTREAM_BO_CREATE = +DRM_IOCTL_ETHOSU (WR ,CMDSTREAM_BO_CREATE , cmdstream_bo_create), +DRM_IOCTL_ETHOSU_SUBMIT = +DRM_IOCTL_ETHOSU (WR ,SUBMIT , submit), +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _ETHOSU_DRM_H_ */ --2.51.0
Rob Herring
maintainerRe: [PATCH v4 2/2] accel: Add Arm Ethos-U NPU driver
Quoted 19 lines · Frank Li
Quoted 17 lines · Rob Herring (Arm)
Add a driver for Arm Ethos-U65/U85 NPUs. The Ethos-U NPU has a relatively simple interface with single command stream to describe buffers, operation settings, and network operations. It supports up to 8 memory regions (though no h/w bounds on a region). The Ethos NPUs are designed to use an SRAM for scratch memory. Region 2 is reserved for SRAM (like the downstream driver stack and compiler). Userspace doesn't need access to the SRAM.
The h/w has no MMU nor external IOMMU and is a DMA engine which can read and write anywhere in memory without h/w bounds checks. The user submitted command streams must be validated against the bounds of the GEM BOs. This is similar to the VC4 design which validates shaders.
The job submit is based on the rocket driver for the Rockchip NPU utilizing the GPU scheduler. It is simpler as there's only 1 core rather than 3.
Quoted 8 lines
Quoted 6 lines
Tested on i.MX93 platform (U65) and FVP (U85) with WIP Mesa Teflon support.
Signed-off-by Rob Herring (Arm) <[email protected]>---
How to test this driver?
You need to add the DT node to i.MX93 .dts like the example, build the mesa ethosu branch, and then run tflite with it pointed to the mesa delegate.
I can send an i.MX93 dts patch after this is merged.
Quoted 26 lines
Quoted 10 lines
v4:
- Use bulk clk API
- Various whitespace fixes mostly due toethos->ethosurename
- Drop error check ondma_set_mask_and_coherent()
- Drop unnecessarypm_runtime_mark_last_busy()call
- Move variable declarations out of switch (a riscv/clang build failure)
- Use lowercase hex in all defines
- Drop unused ethosu_device.coherent member
- Add comments on all locks...
Quoted 15 lines
drivers/accel/ethosu/ethosu_device.h new file +9
-0@@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0-only or MIT */ +/* Copyright 2025 Arm, Ltd. */ + +#ifndef __ETHOSU_DEVICE_H__ +#define __ETHOSU_DEVICE_H__ + +#include <linux/types.h> + +#include <drm/drm_device.h>
Quoted 8 lines
drivers/accel/ethosu/ethosu_device.h line 15
Quoted 6 lines
drivers/accel/ethosu/ethosu_device.h new file +6
-0+#include <drm/gpu_scheduler.h> + +#include <drm/ethosu_accel.h> + +struct clk ; +struct gen_pool ;Supposed should include
clk.hinstead declear a struct.
Headers should only use a forward declaration if that's all they need. It keeps the struct opaque for starters.
Quoted 25 lines
...
Quoted 11 lines
drivers/accel/ethosu/ethosu_device.h new file +11
-0+ +static int ethosu_open (struct drm_device *ddev,struct drm_file *file) +{ +int ret =0 ; +struct ethosu_file_priv *priv; + +if (!try_module_get (THIS_MODULE )) +return -EINVAL ; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) {drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ ret = -ENOMEM ; + goto err_put_mod; + } + priv->edev =to_ethosu_device (ddev); + + ret =ethosu_job_open (priv); +if (ret) + goto err_free; + + file->driver_priv = priv;slice simple.
Quoted 3 lines
struct
ethosu_file_priv__free(kfree)*priv = NULL;
...
priv =kzalloc(sizeof(*priv), GFP_KERNEL);
Linus has voiced his opinion that the above should not be done. It should be all one line *only*. But now we allow C99 declarations, so we can move it down. We can't get rid of the goto for module_put(), so it only marginally helps here.
Quoted 43 lines
...
file->driver_priv=no_free_ptr(priv);Quoted 9 lines
drivers/accel/ethosu/ethosu_device.h new file +9
-0+return 0 ; + +err_free: +kfree (priv); +err_put_mod: +module_put (THIS_MODULE ); +return ret; +} +...
Quoted 14 lines
drivers/accel/ethosu/ethosu_device.h new file +14
-0+ + +static int ethosu_init (struct ethosu_device *ethosudev) +{ +int ret; +u32 id, config; + + ret =devm_pm_runtime_enable (ethosudev->base.dev); +if (ret) +return ret; + + ret =pm_runtime_resume_and_get (ethosudev->base.dev); +if (ret) +return ret;drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ +pm_runtime_set_autosuspend_delay (ethosudev->base.dev,50 ); +pm_runtime_use_autosuspend (ethosudev->base.dev); + +/* If PM is disabled, we need to call ethosu_device_resume() manually. */ +if (!IS_ENABLED (CONFIG_PM )) { + ret =ethosu_device_resume (ethosudev->base.dev); +if (ret) +return ret; + }I think it should call
ethosu_device_resume()unconditional beforedevm_pm_runtime_enable();
Quoted 4 lines
ethosu_device_resume();pm_runtime_set_active();pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);devm_pm_runtime_enable();
Why do you think this? Does this do a get?
I don't think it is good to call the resume hook on our own, but we have no choice with !CONFIG_PM. With CONFIG_PM, we should only use the pm_runtime API.
Rob
Re: [PATCH v4 2/2] accel: Add Arm Ethos-U NPU driver
Quoted 155 lines · Rob Herring
Quoted 19 lines · Frank Li
Quoted 17 lines · Rob Herring (Arm)
Add a driver for Arm Ethos-U65/U85 NPUs. The Ethos-U NPU has a relatively simple interface with single command stream to describe buffers, operation settings, and network operations. It supports up to 8 memory regions (though no h/w bounds on a region). The Ethos NPUs are designed to use an SRAM for scratch memory. Region 2 is reserved for SRAM (like the downstream driver stack and compiler). Userspace doesn't need access to the SRAM.
The h/w has no MMU nor external IOMMU and is a DMA engine which can read and write anywhere in memory without h/w bounds checks. The user submitted command streams must be validated against the bounds of the GEM BOs. This is similar to the VC4 design which validates shaders.
The job submit is based on the rocket driver for the Rockchip NPU utilizing the GPU scheduler. It is simpler as there's only 1 core rather than 3.
Quoted 8 lines
Quoted 6 lines
Tested on i.MX93 platform (U65) and FVP (U85) with WIP Mesa Teflon support.
Signed-off-by Rob Herring (Arm) <[email protected]>---
How to test this driver?
You need to add the DT node to i.MX93 .dts like the example, build the mesa ethosu branch, and then run tflite with it pointed to the mesa delegate.
I can send an i.MX93 dts patch after this is merged.
Quoted 26 lines
Quoted 10 lines
v4:
- Use bulk clk API
- Various whitespace fixes mostly due toethos->ethosurename
- Drop error check ondma_set_mask_and_coherent()
- Drop unnecessarypm_runtime_mark_last_busy()call
- Move variable declarations out of switch (a riscv/clang build failure)
- Use lowercase hex in all defines
- Drop unused ethosu_device.coherent member
- Add comments on all locks...
Quoted 15 lines
drivers/accel/ethosu/ethosu_device.h new file +9
-0@@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0-only or MIT */ +/* Copyright 2025 Arm, Ltd. */ + +#ifndef __ETHOSU_DEVICE_H__ +#define __ETHOSU_DEVICE_H__ + +#include <linux/types.h> + +#include <drm/drm_device.h> Quoted 8 lines
drivers/accel/ethosu/ethosu_device.h line 15
Quoted 6 lines
drivers/accel/ethosu/ethosu_device.h new file +6
-0+#include <drm/gpu_scheduler.h> + +#include <drm/ethosu_accel.h> + +struct clk ; +struct gen_pool ;Supposed should include
clk.hinstead declear a struct.Headers should only use a forward declaration if that's all they need. It keeps the struct opaque for starters.
Quoted 25 lines
...
Quoted 11 lines
drivers/accel/ethosu/ethosu_device.h new file +11
-0+ +static int ethosu_open (struct drm_device *ddev,struct drm_file *file) +{ +int ret =0 ; +struct ethosu_file_priv *priv; + +if (!try_module_get (THIS_MODULE )) +return -EINVAL ; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) {drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ ret = -ENOMEM ; + goto err_put_mod; + } + priv->edev =to_ethosu_device (ddev); + + ret =ethosu_job_open (priv); +if (ret) + goto err_free; + + file->driver_priv = priv;slice simple.
Quoted 3 lines
struct
ethosu_file_priv__free(kfree)*priv = NULL;
...
priv =kzalloc(sizeof(*priv), GFP_KERNEL);Linus has voiced his opinion that the above should not be done. It should be all one line *only*. But now we allow C99 declarations, so we can move it down. We can't get rid of the goto for
module_put(), so it only marginally helps here.Quoted 43 lines
...
file->driver_priv=no_free_ptr(priv);Quoted 9 lines
drivers/accel/ethosu/ethosu_device.h new file +9
-0+return 0 ; + +err_free: +kfree (priv); +err_put_mod: +module_put (THIS_MODULE ); +return ret; +} +...
Quoted 14 lines
drivers/accel/ethosu/ethosu_device.h new file +14
-0+ + +static int ethosu_init (struct ethosu_device *ethosudev) +{ +int ret; +u32 id, config; + + ret =devm_pm_runtime_enable (ethosudev->base.dev); +if (ret) +return ret; + + ret =pm_runtime_resume_and_get (ethosudev->base.dev); +if (ret) +return ret;drivers/accel/ethosu/ethosu_device.h
Quoted 10 lines
drivers/accel/ethosu/ethosu_device.h new file +10
-0+ +pm_runtime_set_autosuspend_delay (ethosudev->base.dev,50 ); +pm_runtime_use_autosuspend (ethosudev->base.dev); + +/* If PM is disabled, we need to call ethosu_device_resume() manually. */ +if (!IS_ENABLED (CONFIG_PM )) { + ret =ethosu_device_resume (ethosudev->base.dev); +if (ret) +return ret; + }I think it should call
ethosu_device_resume()unconditional beforedevm_pm_runtime_enable();Quoted 4 lines
ethosu_device_resume();pm_runtime_set_active();pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);devm_pm_runtime_enable();
Quoted 5 lines
Why do you think this? Does this do a get?
I don't think it is good to call the resume hook on our own, but we have no choice with !CONFIG_PM. With
CONFIG_PM, we should only use thepm_runtimeAPI.
Enable clock and do some init work at probe() is quite common. But I never seen IS_ENABLED(CONFIG_PM) check. It is quite weird and not necessary to check CONFIG_PM flags. The most CONFIG_PM is enabled, so the branch !CONFIG_PM almost never tested.
probe()
{
devm_clk_bulk_get_all_enabled();
... did some init work
pm_runtime_set_active();
devm_pm_runtime_enable();
...
pm_runtime_put_autosuspend(ethosudev->base.dev);
}ethosu_init() only is called by ethosu_probe(). with above pattern, needn't check CONFIG_PM and call resume hook ethosu_device_resume();
Frank
Quoted 2 lines
Rob
Rob Herring
maintainerRe: [PATCH v4 2/2] accel: Add Arm Ethos-U NPU driver
Quoted 46 lines · Frank Li
Quoted 44 lines · Rob Herring
Quoted 36 lines · Frank Li
Quoted 20 lines · Rob Herring (Arm)
Add a driver for Arm Ethos-U65/U85 NPUs. The Ethos-U NPU has a relatively simple interface with single command stream to describe buffers, operation settings, and network operations. It supports up to 8 memory regions (though no h/w bounds on a region). The Ethos NPUs are designed to use an SRAM for scratch memory. Region 2 is reserved for SRAM (like the downstream driver stack and compiler). Userspace doesn't need access to the SRAM.
drivers/accel/ethosu/ethosu_drv.c new file +12
-0+static int ethosu_init (struct ethosu_device *ethosudev) +{ +int ret; +u32 id, config; + + ret =devm_pm_runtime_enable (ethosudev->base.dev); +if (ret) +return ret; + + ret =pm_runtime_resume_and_get (ethosudev->base.dev); +if (ret) +return ret;drivers/accel/ethosu/ethosu_drv.c line 326
Quoted 10 lines
drivers/accel/ethosu/ethosu_drv.c new file +10
-0+ +pm_runtime_set_autosuspend_delay (ethosudev->base.dev,50 ); +pm_runtime_use_autosuspend (ethosudev->base.dev); + +/* If PM is disabled, we need to call ethosu_device_resume() manually. */ +if (!IS_ENABLED (CONFIG_PM )) { + ret =ethosu_device_resume (ethosudev->base.dev); +if (ret) +return ret; + }I think it should call
ethosu_device_resume()unconditional beforedevm_pm_runtime_enable();Quoted 4 lines
ethosu_device_resume();pm_runtime_set_active();pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);devm_pm_runtime_enable();Why do you think this? Does this do a get?
Quoted 8 lines
Quoted 3 lines
I don't think it is good to call the resume hook on our own, but we have no choice with !CONFIG_PM. With
CONFIG_PM, we should only use thepm_runtimeAPI.Enable clock and do some init work at
probe()is quite common. But I never seenIS_ENABLED(CONFIG_PM)check. It is quite weird and not necessary to checkCONFIG_PMflags. The mostCONFIG_PMis enabled, so the branch !CONFIG_PM almost never tested.
Okay, I get what you meant.
Quoted 13 lines
probe(){ devm_clk_bulk_get_all_enabled(); ... did some init work pm_runtime_set_active(); devm_pm_runtime_enable(); ... pm_runtime_put_autosuspend(ethosudev->base.dev); }
I think we still need a pm_runtime_get_noresume() in here since we do a put later on. Here's what I have now:
ret = ethosu_device_resume(ethosudev->base.dev);
if (ret)
return ret;
pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);
pm_runtime_use_autosuspend(ethosudev->base.dev);
ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev);
if (ret)
return ret;
pm_runtime_get_noresume(ethosudev->base.dev);Rob
Re: [PATCH v4 2/2] accel: Add Arm Ethos-U NPU driver
Quoted 75 lines · Rob Herring
Quoted 46 lines · Frank Li
Quoted 44 lines · Rob Herring
Quoted 36 lines · Frank Li
Quoted 20 lines · Rob Herring (Arm)
Add a driver for Arm Ethos-U65/U85 NPUs. The Ethos-U NPU has a relatively simple interface with single command stream to describe buffers, operation settings, and network operations. It supports up to 8 memory regions (though no h/w bounds on a region). The Ethos NPUs are designed to use an SRAM for scratch memory. Region 2 is reserved for SRAM (like the downstream driver stack and compiler). Userspace doesn't need access to the SRAM.
drivers/accel/ethosu/ethosu_drv.c new file +12
-0+static int ethosu_init (struct ethosu_device *ethosudev) +{ +int ret; +u32 id, config; + + ret =devm_pm_runtime_enable (ethosudev->base.dev); +if (ret) +return ret; + + ret =pm_runtime_resume_and_get (ethosudev->base.dev); +if (ret) +return ret;drivers/accel/ethosu/ethosu_drv.c line 326
Quoted 10 lines
drivers/accel/ethosu/ethosu_drv.c new file +10
-0+ +pm_runtime_set_autosuspend_delay (ethosudev->base.dev,50 ); +pm_runtime_use_autosuspend (ethosudev->base.dev); + +/* If PM is disabled, we need to call ethosu_device_resume() manually. */ +if (!IS_ENABLED (CONFIG_PM )) { + ret =ethosu_device_resume (ethosudev->base.dev); +if (ret) +return ret; + }I think it should call
ethosu_device_resume()unconditional beforedevm_pm_runtime_enable();Quoted 4 lines
ethosu_device_resume();pm_runtime_set_active();pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);devm_pm_runtime_enable();Why do you think this? Does this do a get?
Quoted 8 lines
Quoted 3 lines
I don't think it is good to call the resume hook on our own, but we have no choice with !CONFIG_PM. With
CONFIG_PM, we should only use thepm_runtimeAPI.Enable clock and do some init work at
probe()is quite common. But I never seenIS_ENABLED(CONFIG_PM)check. It is quite weird and not necessary to checkCONFIG_PMflags. The mostCONFIG_PMis enabled, so the branch !CONFIG_PM almost never tested.Okay, I get what you meant.
Quoted 13 lines
probe(){ devm_clk_bulk_get_all_enabled(); ... did some init work pm_runtime_set_active(); devm_pm_runtime_enable(); ... pm_runtime_put_autosuspend(ethosudev->base.dev); }I think we still need a
pm_runtime_get_noresume()in here since we do a put later on. Here's what I have now:
Quoted 10 lines
ret = ethosu_device_resume(ethosudev->base.dev);
if (ret)
return ret;
pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);
pm_runtime_use_autosuspend(ethosudev->base.dev);
ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev);
if (ret)
return ret;
pm_runtime_get_noresume(ethosudev->base.dev);Look good.
Frank
Quoted 3 lines
Rob
Quoted 15 lines · Rob Herring (Arm)
The Arm Ethos-U65/85 NPUs are designed for edge AI inference applications[0].
The driver works with Mesa Teflon. A merge request for Ethos support is here[1]. The UAPI should also be compatible with the downstream (open source) driver stack[2] and Vela compiler though that has not been implemented.
Testing so far has been on i.MX93 boards with Ethos-U65 and a FVP model with Ethos-U85. More work is needed in mesa for handling U85 command stream differences, but that doesn't affect the UABI.
A git tree is here[3].
Quoted 8 lines
Rob
[0] https://www.arm.com/products/silicon-ip-cpu?families=ethos%20npus
[1] https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36699/
[2] https://gitlab.arm.com/artificial-intelligence/ethos-u/
[3]git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.gitethos-v4Signed-off-by Rob Herring (Arm) <[email protected]>
For the series:
Regards,
Tomeu
Quoted 63 lines
---
Changes in v4:
- Use bulk clk API
- Various whitespace fixes mostly due toethos->ethosurename
- Drop error check ondma_set_mask_and_coherent()
- Drop unnecessarypm_runtime_mark_last_busy()call
- Move variable declarations out of switch (a riscv/clang build failure)
- Use lowercase hex in all defines
- Drop unused ethosu_device.coherent member
- Add comments on all locks
- Link to v3: https://lore.kernel.org/r/[email protected]Changes in v3:
- Rework and improve job submit validation
- Rename ethos to ethosu. There was an Ethos-Nxx that's unrelated.
- Add missing init forsched_lockmutex
- Drop some prints to debug level
- Fix i.MX93 SRAM accesses (AXI config)
- Add U85 AXI configuration and test on FVP with U85
- Print the current cmd value on timeout
- Link to v2: https://lore.kernel.org/r/[email protected]Changes in v2:
- Rebase on v6.17-rc1 adapting to scheduler changes
- scheduler: Drop the reset workqueue. According to the scheduler docs, we don't need it since we have a single h/w queue.
- scheduler: Rework the timeout handling to continue running if we are making progress. Fixes timeouts on larger jobs.
- Reset the NPU on resume so it's in a known state
- Add error handling onclk_get()calls
- Fixdrm_mmsplat on module unload. We were missing a put on the cmdstream BO in the scheduler clean-up.
- Fix 0-day report needing explicitbitfield.hinclude
- Link to v1: https://lore.kernel.org/r/[email protected]---
Rob Herring (Arm) (2):
dt-bindings: npu: Add Arm Ethos-U65/U85
accel: Add Arm Ethos-U NPU driver.../devicetree/bindings/npu/arm,ethos.yaml | 79 +++ MAINTAINERS | 9 + drivers/accel/Kconfig | 1 + drivers/accel/Makefile | 1 + drivers/accel/ethosu/Kconfig | 10 + drivers/accel/ethosu/Makefile | 4 + drivers/accel/ethosu/ethosu_device.h | 190 ++++++ drivers/accel/ethosu/ethosu_drv.c | 418 ++++++++++++ drivers/accel/ethosu/ethosu_drv.h | 15 + drivers/accel/ethosu/ethosu_gem.c | 710 +++++++++++++++++++++ drivers/accel/ethosu/ethosu_gem.h | 46 ++ drivers/accel/ethosu/ethosu_job.c | 539 ++++++++++++++++ drivers/accel/ethosu/ethosu_job.h | 41 ++ include/uapi/drm/ethosu_accel.h | 261 ++++++++ 14 files changed, 2324 insertions(+)---
base-commit:3a8660878839faadb4f1a6dd72c3179c1df56787
change-id: 20250715-ethos-3fdd39ef6f19Best regards,
--
Rob Herring (Arm) <[email protected]>