From nobody Tue Apr 7 15:27:36 2026 Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F48136C0DF; Thu, 26 Feb 2026 13:41:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113267; cv=none; b=kUuIipOwRKIcWwk5APD1NFhZ0fhKtXisTwFR1MDkJCyRkEBk+M38FBITNoqeD6F1WLVuxzaKju7W5WAl3Svy80iQ6RXPqzI/41b5AQf2GjPOzOwb4sXm+Ca5y3LOBxcSjkc4AXpK8E/ONtpkg3cEEAxENqLKHfP9C2NrKgFMdDw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113267; c=relaxed/simple; bh=t5jOCwYs4a0qJx+g0j9jr/ql5g59uodTYfBmURX7TLI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YWPBfD+MSm0O/kUxH0FBpBaX5Jv/Hbd69vIKyoYbcp7IpRQM5X0++bf5mPPunUxPUvnmELwcJebrLA/Rs6/1gsay1xI4e64PuGq5LfJncaEvRd+lnMSqAg2Em71e+75zmJ2oPjQNjlElHOM1mQexg7eSxbQQ2DAIej0saCdKvMs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 32821201553; Thu, 26 Feb 2026 14:40:59 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 101AA201509; Thu, 26 Feb 2026 14:40:59 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 56D7A2035B; Thu, 26 Feb 2026 14:40:57 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:40 +0200 Subject: [PATCH 1/9] drm/gem-dma: Add flag for bidirectional mapping of non-coherent GEM DMA buffers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-1-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=2892; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=t5jOCwYs4a0qJx+g0j9jr/ql5g59uodTYfBmURX7TLI=; b=FuK2EVogBLiKqAWSpQj/VvRmlxkqGBRQdPtOI6DDSNPrrj8FO4pZ0CErQ51iQLLpeRvWIydJH 0NCLiSNBTUhBnS9S4+7LuX+o93vLCiofdPsrriBRdgeEv0BJykkTJWe X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Introduce a flag that allows a user to request non-coherent buffers allocated via the GEM DMA helper for bidirectional use. Keep current behaviour (DMA_TO_DEVICE mapping) as default, with no change required for existing GEM DMA users. While it hasn't been the case until now, some devices like NXP's Neutron Neural Processing Unit (NPU) require contiguous, non-coherent DMA buffers they can both read from and write to. Unlike traditional DRM devices, Neutron uses the same DMA buffer both for reading model data and for writing inference output. Neutron's usage scenario is a good match for the GEM DMA helpers, except for the fact that current implementation only considers the DMA_TO_DEVICE direction. Signed-off-by: Ioana Ciocoi-Radulescu --- drivers/gpu/drm/drm_gem_dma_helper.c | 6 ++++-- include/drm/drm_gem_dma_helper.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_dma_helper.c b/drivers/gpu/drm/drm_gem= _dma_helper.c index ecb9746f4da8..dbf5ad4426d3 100644 --- a/drivers/gpu/drm/drm_gem_dma_helper.c +++ b/drivers/gpu/drm/drm_gem_dma_helper.c @@ -148,7 +148,8 @@ struct drm_gem_dma_object *drm_gem_dma_create(struct dr= m_device *drm, if (dma_obj->map_noncoherent) { dma_obj->vaddr =3D dma_alloc_noncoherent(drm->dev, size, &dma_obj->dma_addr, - DMA_TO_DEVICE, + dma_obj->map_bidirectional ? + DMA_BIDIRECTIONAL : DMA_TO_DEVICE, GFP_KERNEL | __GFP_NOWARN); } else { dma_obj->vaddr =3D dma_alloc_wc(drm->dev, size, @@ -238,7 +239,8 @@ void drm_gem_dma_free(struct drm_gem_dma_object *dma_ob= j) if (dma_obj->map_noncoherent) dma_free_noncoherent(gem_obj->dev->dev, dma_obj->base.size, dma_obj->vaddr, dma_obj->dma_addr, - DMA_TO_DEVICE); + dma_obj->map_bidirectional ? + DMA_BIDIRECTIONAL : DMA_TO_DEVICE); else dma_free_wc(gem_obj->dev->dev, dma_obj->base.size, dma_obj->vaddr, dma_obj->dma_addr); diff --git a/include/drm/drm_gem_dma_helper.h b/include/drm/drm_gem_dma_hel= per.h index f2678e7ecb98..e0022f2fdfef 100644 --- a/include/drm/drm_gem_dma_helper.h +++ b/include/drm/drm_gem_dma_helper.h @@ -17,6 +17,8 @@ struct drm_mode_create_dumb; * DMA addresses. * @vaddr: kernel virtual address of the backing memory * @map_noncoherent: if true, the GEM object is backed by non-coherent mem= ory + * @map_bidirectional: valid only if map_noncoherent flag is set. If true,= allow + * bidirectional use of the non-coherent memory buffer */ struct drm_gem_dma_object { struct drm_gem_object base; @@ -27,6 +29,7 @@ struct drm_gem_dma_object { void *vaddr; =20 bool map_noncoherent; + bool map_bidirectional; }; =20 #define to_drm_gem_dma_obj(gem_obj) \ --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ED08636CE0B; Thu, 26 Feb 2026 13:41:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113269; cv=none; b=KalRtcOeVRk8iTDVFs7QUHLEnLcSnQhFV92NRehOMJyUKRpGJIg2IUzoakpg03xSLSkCBEyC54nricpkn2YyJCIdT7xhIBPBQwNE0+cVYZ1AiyvDqwuSofzwO/x0IfDiQD3nXfC7MV6hLZWhsOeVxY3WuM2UuqQli+lYT5vyTec= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113269; c=relaxed/simple; bh=70OP0Dj1Z9Nhg2uTfcWxmjST7VKQFl9/cXvxpjdBVks=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=btXbmMNB80O1NzHmJnsHti030gUO1I4GqbOSZ7zD5xj9l3lV9pyZPLagqWTs3yX6yWCChRDhEA5xUbdla9YCdG+vqrAJ0ed3IBXJ5UUeONyC1wfq1MwwqsHTpj7AlXK+p5lizhk2hp3GaDsG7HHUkmTRNM7CrRT1h3SBPRk+puY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 6231F202549; Thu, 26 Feb 2026 14:41:00 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 28BDC202C27; Thu, 26 Feb 2026 14:41:00 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 6EDD020376; Thu, 26 Feb 2026 14:40:58 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:41 +0200 Subject: [PATCH 2/9] accel/neutron: Add documentation for NXP Neutron accelerator driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-2-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=7020; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=70OP0Dj1Z9Nhg2uTfcWxmjST7VKQFl9/cXvxpjdBVks=; b=8snYNlqD0ArdowhVMHi4fky8m1s3uu2Sgmh8z+MCPxBpHYyxaM/jPPavdv2oTxe3/SS+vrOs2 KGH+5xMQ5zNDdOXXUoc9yvcYl3RvMBDTlrafP7LIw3aSly+FkLo3DO5 X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Neutron is NXP's Neural Processing Unit (NPU) and it's integrated on the i.MX95 SoC. It is capable of running inferences on a large range of ML models and targets edge AI applications. Signed-off-by: Ioana Ciocoi-Radulescu --- Documentation/accel/index.rst | 1 + Documentation/accel/neutron/index.rst | 12 +++ Documentation/accel/neutron/neutron.rst | 131 ++++++++++++++++++++++++++++= ++++ 3 files changed, 144 insertions(+) diff --git a/Documentation/accel/index.rst b/Documentation/accel/index.rst index cbc7d4c3876a..dbe177074739 100644 --- a/Documentation/accel/index.rst +++ b/Documentation/accel/index.rst @@ -9,5 +9,6 @@ Compute Accelerators =20 introduction amdxdna/index + neutron/index qaic/index rocket/index diff --git a/Documentation/accel/neutron/index.rst b/Documentation/accel/ne= utron/index.rst new file mode 100644 index 000000000000..8f15346d16c7 --- /dev/null +++ b/Documentation/accel/neutron/index.rst @@ -0,0 +1,12 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + accel/neutron NPU driver +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + +The accel/neutron driver supports the Neutron NPU (Neural Processing Unit) +from NXP. + +.. toctree:: + + neutron diff --git a/Documentation/accel/neutron/neutron.rst b/Documentation/accel/= neutron/neutron.rst new file mode 100644 index 000000000000..c5066d53ce69 --- /dev/null +++ b/Documentation/accel/neutron/neutron.rst @@ -0,0 +1,131 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +.. include:: + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + Neutron NPU Driver +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +:Copyright: |copy| 2026 NXP + +Overview +=3D=3D=3D=3D=3D=3D=3D=3D + +Neutron is NXP's eIQ Neutron Neural Processing Unit (NPU). It is a highly +scalable, power-efficient machine learning accelerator targeting quantized +ML models for edge AI applications. Neutron is integrated into i.MX95 and +other NXP platforms. + +A more detailed description of Neutron NPU and usage scenarios can be +found at [1]_. + +Hardware Description +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Neutron has the following hardware components: + +- RISC-V core: this is the "brain" of the Neutron NPU. It runs a proprieta= ry + firmware responsible for programming registers, processing commands and + managing the other hardware components +- one or more Neutron cores: the main computation engine performing Machine + Learning (ML) operations +- TCM: a dedicated fast memory +- Data Mover: a DMA engine that handles data transfers between system memo= ry + and Neutron's internal memory + +Software Stack +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The following software components are required for running an inference +on the Neutron accelerator: + +- Neutron converter [2]_, [3]_: this is an offline tool that converts mode= ls + from standard TFLite (LiteRT) format to a custom format for execution on= the + Neutron NPU; +- An inference engine, e.g. LiteRT's XNNPack, which in turn uses +- A LiteRT custom delegate [4]_ to dispatch custom operators to Neutron NP= U; +- A userspace library [5]_ that the delegate links to, which wraps IOCTLs + to the kernel driver in a higher-level API. It handles microcode, weights + and kernels preparation and base address computations needed by the NPU = for + job execution. It also triggers cache syncs when required; +- The Neutron kernel driver, which handles device initialization and + communicates directly with the Neutron firmware; +- Neutron firmware [5]_, a proprietary firmware that executes on the RISC-V + core and directly drives the execution of the NPU hardware. + +Usage Flow +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This section describes the steps required to run an inference job on the +Neutron NPU. + +Offline Conversion +------------------ + +The first step is to convert a standard TFLite model using the Neutron +converter. Supported standard operators are extracted together and mapped +to one or multiple **NeutronGraph** custom operators in the converted mode= l. +Standard operators that are not supported by the NPU are left unchanged and +will be executed on the CPU. + +Runtime Flow +------------ + +On the platform's Cortex-A cores running Linux, the LiteRT inference engine +is responsible for loading the ML model, pre-processing the input data and +handing over the tensor computation to the NPU via the custom delegate. + +The inference engine can be exercised via one of the standard TFLite tools +(e.g. benchmark_model, label_image, etc) or via any custom application that +uses the LiteRT runtime API. + +When preparing to run an inference job, userspace requests a memory buffer +from the kernel driver. It loads both the model and the input data in the +buffer, while also reserving a section for the inference output. It then +issues a job submission command with the prepared buffer and waits for +completion. + +The kernel driver sends the inference job details to the Neutron firmware +via mailbox registers. The NPU executes the inference and issues an interr= upt +to the Linux core once it is finished. The driver in return marks the job +as complete so userspace can access and post-process the output. + +Boot Sequence +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The Neutron driver is responsible for loading the firmware image and +initiating the NPU boot sequence. The device is powered down during suspend +and each resume operation implies running the firmware load and boot seque= nce +again. + +Hardware Constraints +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Cache Coherency +--------------- + +Some of the NXP platforms that Neutron is integrated on, including i.MX95, +do not ensure Neutron memory coherency at hardware level, generating the +need for explicit DMA sync operations. Given that only parts of the memory +buffer may require syncing at any given time (e.g. multiple inferences usi= ng +the same model but different input data) and that the kernel driver is una= ware +of the buffer partitioning, the sync operations are driven from userspace. + +Buffer alignment +---------------- + +The Neutron DMA engine requires the inference buffers to be aligned to 1MB +boundary. We allocate buffers for Neutron NPU from a reserved CMA pool that +satisfies this alignment requirement. + +References +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +.. [1] i.MX Machine Learning User's Guide: https://www.nxp.com/docs/en/use= r-guide/UG10166.pdf +.. [2] Neutron Converter binary and User Guide available for download here: + https://www.nxp.com/design/design-center/software/eiq-ai-developmen= t-environment/eiq-toolkit-for-end-to-end-model-development-and-deployment:E= IQ-TOOLKIT +.. [3] NXP's eIQ PyPi repository: https://eiq.nxp.com/repository/eiq-neutr= on-sdk/ +.. [4] TFLite delegate source code: https://github.com/nxp-imx/tflite-neut= ron-delegate +.. [5] Neutron firmware, library and TFLite delegate available here as bin= aries: + https://github.com/nxp-upstream/neutron/tree/upstream + --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva020.nxp.com (inva020.nxp.com [92.121.34.13]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0B7D836CE1B; Thu, 26 Feb 2026 13:41:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.13 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113269; cv=none; b=lVAff3VOa3fmt9ggBGJU7J3ajwlu3Nl0weZE/Thu9dYhtruqFKPJ0te8+GfIqRt9U5SjAkNaFDIk2NB6S3TKOrWbi77e57Ausg07TL4qrBOUNbuy3yFcyqqURJozr6mzwqHLEhHIcA9tRBApxFx4540IgjaitZnXIzGwhgvNGjo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113269; c=relaxed/simple; bh=EX2bIZOSowP7el+n7llkL5Myq7aMQsIPLE13DNgNaf0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=trzaRKPIa8GHgAF3WtlGEY/1tDiWyQxIB2yLRRvUjtL2k/lUUnH7RLPIO42VwfOvZT8X5w9CNYgi1MnYRQYA+hI1XuObESP8DjISm7pxy9fozS8KnTniKtUxTGDmmesVNORQIXjmBSOt0LNFoO+iyD3T992kTu1wupr4U2kvCRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.13 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 55E461A14A2; Thu, 26 Feb 2026 14:41:01 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 405991A14A9; Thu, 26 Feb 2026 14:41:01 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 8A45A2035B; Thu, 26 Feb 2026 14:40:59 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:42 +0200 Subject: [PATCH 3/9] dt-bindings: npu: Add bindings for NXP Neutron Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-3-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=3009; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=EX2bIZOSowP7el+n7llkL5Myq7aMQsIPLE13DNgNaf0=; b=tPxIFYSc7cbZSar8MAZ2vi5SJTPMRU/MOwR6ASpB1hIm7foVZJIzow1x+B7uPluDrCqVmZjC0 UbPbT0uDRp6D2R7LlgfcgzTj+tB0JQdwp92nBdYyDP1Jra2XEl0bXlE X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Add the bindings for Neutron, a Neural Processing Unit from NXP. Signed-off-by: Jiwei Fu Signed-off-by: Ioana Ciocoi-Radulescu --- .../devicetree/bindings/npu/nxp,imx95-neutron.yaml | 95 ++++++++++++++++++= ++++ 1 file changed, 95 insertions(+) diff --git a/Documentation/devicetree/bindings/npu/nxp,imx95-neutron.yaml b= /Documentation/devicetree/bindings/npu/nxp,imx95-neutron.yaml new file mode 100644 index 000000000000..ba1f6851866b --- /dev/null +++ b/Documentation/devicetree/bindings/npu/nxp,imx95-neutron.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/npu/nxp,imx95-neutron.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP Neutron NPU + +maintainers: + - Ioana Ciocoi-Radulescu + - Jiwei Fu + +description: + Neutron is an NPU from NXP targeting edge AI inference applications. + Initially supported on i.MX95 SoCs. + +properties: + compatible: + enum: + - nxp,imx95-neutron + + reg: + items: + - description: Register space + - description: Instruction area of the TCM space + - description: Data area of the TCM space + + reg-names: + items: + - const: regs + - const: itcm + - const: dtcm + + memory-region: + description: + Phandle referencing a "shared-dma-pool" to be used for Neutron + inference buffers, which need to be 1MB aligned. + + The memory region must be defined with alignment of 1MB and size + should be large enough to accommodate the targeted ML models. It + should be marked as reusable. + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 3 + + clock-names: + minItems: 2 + items: + - const: npu + - const: npu_apb + - const: npu_cgc + + iommus: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - memory-region + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + bus { + #address-cells =3D <2>; + #size-cells =3D <2>; + + neutron@4ab00000 { + compatible =3D "nxp,imx95-neutron"; + reg =3D <0x0 0x4ab00000 0x0 0x00000400>, + <0x0 0x4AB10000 0x0 0x00010000>, + <0x0 0x4AB08000 0x0 0x00008000>; + reg-names =3D "regs", "itcm", "dtcm"; + memory-region =3D <&neutron_pool>; + interrupts =3D ; + clocks =3D <&scmi_clk 68>, <&scmi_clk 67>; + clock-names =3D "npu", "npu_apb"; + power-domains =3D <&scmi_devpd 20>; + }; + }; +... --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE71B36D515; Thu, 26 Feb 2026 13:41:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113273; cv=none; b=rrO5XPwsc2cKIyMzSXTt+gUbegvC7g5SeZgLl/7bGeor418S58MiuLDXT6niTwbkw/5OfM88QuUadglxT3TGLZ2wiC6cKIgFqcmtzN71RMtvkML5uouVQg553LGmkxULsulFnlDGZ6IERyJDLX9HbuVTsNEIhbn08+k1Yl1AuxM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113273; c=relaxed/simple; bh=pYXC0MSlwCY7JD6MYpmz5zSNRNT+Qd1aJUJzXayzKAE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=E/v7f/h6AenIAt5kJ0FoN5EzxL5QPdCOZnP7VX/Q90mhR0Coczbong+Utru/UBk9jg+dYkKmZxF85xTIZrSLQmyQNk8MV9mEhKzsDBmmSe+8qi2DBDPlyB3j1Vl9/2iHSEWiDcGaEM1G7dlqvoXiOr7ZOQFAPYgF1ydwY1V78NY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 65209201509; Thu, 26 Feb 2026 14:41:02 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 6172B202586; Thu, 26 Feb 2026 14:41:02 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 9FAD720399; Thu, 26 Feb 2026 14:41:00 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:43 +0200 Subject: [PATCH 4/9] accel/neutron: Add driver for NXP Neutron NPU Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-4-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=18120; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=pYXC0MSlwCY7JD6MYpmz5zSNRNT+Qd1aJUJzXayzKAE=; b=V1jMAR1ZeiyWjC5U4ZFXueYvHHfkBjZHw8+AF6bPjUHtoFCda9RxZE69pJuJKz/5iWFq2sSs5 QLjvlk4+7AyAQNisoClqsZvWws1UkmdcLjHnSfwlophscpiD9s9nace X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Add a driver for the Neutron Neural Processing Unit from NXP. Neutron NPU provides machine learning (ML) acceleration for edge AI applications. Neutron is integrated on NXP SoCs such as the i.MX95. More information can be found under Documentation/accel/neutron. For now introduce basic functionalities only: device probe and remove, firmware load, boot and shutdown procedures, interrupt support, power management. Signed-off-by: Jiwei Fu Signed-off-by: Ioana Ciocoi-Radulescu --- MAINTAINERS | 10 ++ drivers/accel/Kconfig | 1 + drivers/accel/Makefile | 3 +- drivers/accel/neutron/Kconfig | 16 +++ drivers/accel/neutron/Makefile | 7 + drivers/accel/neutron/neutron_device.c | 160 +++++++++++++++++++++++ drivers/accel/neutron/neutron_device.h | 120 +++++++++++++++++ drivers/accel/neutron/neutron_driver.c | 228 +++++++++++++++++++++++++++++= ++++ drivers/accel/neutron/neutron_driver.h | 13 ++ 9 files changed, 557 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..5ad757797f50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19181,6 +19181,16 @@ S: Orphan F: Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml F: drivers/nfc/nxp-nci =20 +NXP Neutron NPU DRIVER +M: Ioana Ciocoi Radulescu +M: Jiwei Fu +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/accel/neutron/ +F: drivers/accel/neutron/ +F: include/uapi/drm/neutron_accel.h + NXP/Goodix TFA989X (TFA1) DRIVER M: Stephan Gerhold L: linux-sound@vger.kernel.org diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig index bdf48ccafcf2..ba392371b972 100644 --- a/drivers/accel/Kconfig +++ b/drivers/accel/Kconfig @@ -28,6 +28,7 @@ source "drivers/accel/amdxdna/Kconfig" source "drivers/accel/ethosu/Kconfig" source "drivers/accel/habanalabs/Kconfig" source "drivers/accel/ivpu/Kconfig" +source "drivers/accel/neutron/Kconfig" source "drivers/accel/qaic/Kconfig" source "drivers/accel/rocket/Kconfig" =20 diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile index 1d3a7251b950..698136e12cce 100644 --- a/drivers/accel/Makefile +++ b/drivers/accel/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_DRM_ACCEL_AMDXDNA) +=3D amdxdna/ obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) +=3D ethosu/ obj-$(CONFIG_DRM_ACCEL_HABANALABS) +=3D habanalabs/ obj-$(CONFIG_DRM_ACCEL_IVPU) +=3D ivpu/ +obj-$(CONFIG_DRM_ACCEL_NXP_NEUTRON) +=3D neutron/ obj-$(CONFIG_DRM_ACCEL_QAIC) +=3D qaic/ -obj-$(CONFIG_DRM_ACCEL_ROCKET) +=3D rocket/ \ No newline at end of file +obj-$(CONFIG_DRM_ACCEL_ROCKET) +=3D rocket/ diff --git a/drivers/accel/neutron/Kconfig b/drivers/accel/neutron/Kconfig new file mode 100644 index 000000000000..37b8ecb49804 --- /dev/null +++ b/drivers/accel/neutron/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0+ + +config DRM_ACCEL_NXP_NEUTRON + tristate "NXP Neutron NPU" + depends on HAS_IOMEM + depends on DRM_ACCEL + depends on ARCH_MXC + select DRM_GEM_DMA_HELPER + select DRM_SCHED + help + Enables driver for NXP Neutron NPU. + + Select this if you have an NXP SoC with Neutron, like i.MX95, + and want to run machine learning applications. + + If built as module, the module is named neutron. diff --git a/drivers/accel/neutron/Makefile b/drivers/accel/neutron/Makefile new file mode 100644 index 000000000000..7592e318dd83 --- /dev/null +++ b/drivers/accel/neutron/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_DRM_ACCEL_NXP_NEUTRON) :=3D neutron.o + +neutron-y :=3D \ + neutron_driver.o \ + neutron_device.o diff --git a/drivers/accel/neutron/neutron_device.c b/drivers/accel/neutron= /neutron_device.c new file mode 100644 index 000000000000..61b3c96b4996 --- /dev/null +++ b/drivers/accel/neutron/neutron_device.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2025-2026 NXP */ + +#include +#include +#include +#include + +#include "neutron_device.h" + +void neutron_enable_irq(struct neutron_device *ndev) +{ + u32 val; + + val =3D readl_relaxed(NEUTRON_REG(ndev, INTENA)); + val |=3D INTENA_INFDONE; + writel_relaxed(val, NEUTRON_REG(ndev, INTENA)); +} + +void neutron_disable_irq(struct neutron_device *ndev) +{ + writel_relaxed(INTENA_INFDONE, NEUTRON_REG(ndev, INTCLR)); +} + +void neutron_handle_irq(struct neutron_device *ndev) +{ + u32 appstatus; + + appstatus =3D readl_relaxed(NEUTRON_REG(ndev, APPSTATUS)); + + /* Write 1 to clear */ + writel_relaxed(appstatus & APPSTATUS_CLEAR_MASK, NEUTRON_REG(ndev, APPSTA= TUS)); + + if (appstatus & APPSTATUS_FAULTCAUSE_MASK) + dev_err(ndev->dev, "Neutron halted due to fault: 0x%lx\n", + FIELD_GET(APPSTATUS_FAULTCAUSE_MASK, appstatus)); +} + +#define neutron_boot_done(appctrl) \ + (FIELD_GET(APPCTRL_MBWR_MASK, (appctrl)) =3D=3D APPCTRL_MBWR_MAGIC) + +static int neutron_start(struct neutron_device *ndev) +{ + u32 resetctrl, appctrl; + int ret; + + resetctrl =3D readl_relaxed(NEUTRON_REG(ndev, RESETCTRL)); + writel_relaxed(resetctrl | RESETCTRL_ZVRUN, NEUTRON_REG(ndev, RESETCTRL)); + + ret =3D readl_poll_timeout(NEUTRON_REG(ndev, APPCTRL), + appctrl, neutron_boot_done(appctrl), + 100, 1000 * USEC_PER_MSEC); + if (ret) { + dev_err(ndev->dev, "Neutron boot timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void neutron_stop(struct neutron_device *ndev) +{ + u32 resetctrl; + + resetctrl =3D readl_relaxed(NEUTRON_REG(ndev, RESETCTRL)); + writel_relaxed(resetctrl & ~RESETCTRL_ZVRUN, NEUTRON_REG(ndev, RESETCTRL)= ); + + readl_poll_timeout(NEUTRON_REG(ndev, RESETCTRL), + resetctrl, !(resetctrl & RESETCTRL_ZVRUN), + 100, 100 * USEC_PER_MSEC); +} + +static void __iomem *neutron_tcm_da_to_va(struct neutron_device *ndev, u64= da) +{ + struct neutron_mem_region *mem; + int offset, i; + + for (i =3D 0; i < NEUTRON_MEM_MAX; i++) { + if (i !=3D NEUTRON_MEM_ITCM && i !=3D NEUTRON_MEM_DTCM) + continue; + mem =3D &ndev->mem_regions[i]; + if (da >=3D mem->da && da < mem->da + mem->size) { + offset =3D da - mem->da; + return mem->va + offset; + } + } + + return NULL; +} + +static int neutron_load_firmware(struct neutron_device *ndev) +{ + const struct firmware *fw; + struct elf32_hdr *ehdr; + struct elf32_phdr *phdr, *seg; + void __iomem *dest; + int i, ret; + + ret =3D request_firmware(&fw, NEUTRON_FIRMWARE_NAME, ndev->dev); + if (ret) { + dev_err(ndev->dev, "Failed to request firmware\n"); + return ret; + } + + ehdr =3D (struct elf32_hdr *)fw->data; + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) !=3D 0) { + dev_err(ndev->dev, "Invalid firmware image\n"); + ret =3D -EINVAL; + goto out_release_fw; + } + + phdr =3D (struct elf32_phdr *)(fw->data + ehdr->e_phoff); + for (i =3D 0; i < ehdr->e_phnum; i++) { + seg =3D &phdr[i]; + if (seg->p_type !=3D PT_LOAD || !seg->p_memsz) + continue; + + dest =3D neutron_tcm_da_to_va(ndev, seg->p_paddr); + if (!dest) { + dev_err(ndev->dev, "Invalid firmware segment: 0x%x\n", seg->p_paddr); + ret =3D -EINVAL; + goto out_release_fw; + } + + memcpy_toio(dest, fw->data + seg->p_offset, seg->p_filesz); + if (seg->p_memsz > seg->p_filesz) + memset_io(dest + seg->p_filesz, 0, seg->p_memsz - seg->p_filesz); + } + +out_release_fw: + release_firmware(fw); + + return ret; +} + +int neutron_boot(struct neutron_device *ndev) +{ + int ret; + + if (ndev->flags & NEUTRON_BOOTED) + neutron_shutdown(ndev); + + ret =3D neutron_load_firmware(ndev); + if (ret) + return ret; + + ret =3D neutron_start(ndev); + if (ret) + return ret; + + ndev->flags |=3D NEUTRON_BOOTED; + + return 0; +} + +void neutron_shutdown(struct neutron_device *ndev) +{ + neutron_stop(ndev); + ndev->flags &=3D ~NEUTRON_BOOTED; +} diff --git a/drivers/accel/neutron/neutron_device.h b/drivers/accel/neutron= /neutron_device.h new file mode 100644 index 000000000000..8e4df7462d82 --- /dev/null +++ b/drivers/accel/neutron/neutron_device.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2025-2026 NXP */ + +#ifndef __NEUTRON_DEVICE_H__ +#define __NEUTRON_DEVICE_H__ + +#include +#include +#include +#include +#include + +struct clk_bulk_data; + +#define NEUTRON_FIRMWARE_NAME "NeutronFirmware.elf" + +/* Register offsets */ +#define NEUTRON_REG_RESETCTRL 0x00 +#define NEUTRON_REG_STATUSERR 0x04 +#define NEUTRON_REG_INTENA 0x08 +#define NEUTRON_REG_INTCLR 0x0C +#define NEUTRON_REG_APPCTRL 0x200 +#define NEUTRON_REG_APPSTATUS 0x204 +#define NEUTRON_REG_BASEDDRL 0x208 +#define NEUTRON_REG_BASEDDRH 0x20C +#define NEUTRON_REG_RINGCTRL 0x230 +#define NEUTRON_REG_TAIL 0x238 +#define NEUTRON_REG_HEAD 0x23C +#define NEUTRON_REG_MBOX0 0x240 +#define NEUTRON_REG_MBOX1 0x244 +#define NEUTRON_REG_MBOX2 0x248 +#define NEUTRON_REG_MBOX3 0x24C +#define NEUTRON_REG_MBOX4 0x250 +#define NEUTRON_REG_MBOX5 0x254 +#define NEUTRON_REG_MBOX6 0x258 +#define NEUTRON_REG_MBOX7 0x25C +#define NEUTRON_REG_BASEINOUTL 0x280 +#define NEUTRON_REG_BASEINOUTH 0x284 +#define NEUTRON_REG_BASESPILLL 0x288 +#define NEUTRON_REG_BASESPILLH 0x28C + +/* Register fields */ +#define RESETCTRL_ZVRUN BIT(0) + +#define INTENA_INFDONE BIT(1) + +#define APPCTRL_MBWR_MASK GENMASK(31, 16) +#define APPCTRL_MBWR_MAGIC 0xF807 + +#define APPSTATUS_INFDONE BIT(0) +#define APPSTATUS_INFHALTED BIT(1) +#define APPSTATUS_FAULTCAUSE_MASK GENMASK(21, 16) +#define APPSTATUS_CLEAR_MASK GENMASK(4, 0) + +#define RINGCTRL_ADDR_MASK GENMASK(16, 8) +#define RINGCTRL_SIZE_MASK GENMASK(7, 0) +#define RINGCTRL_SIZE_MULT 256 + +/* Neutron device-side memory map */ +#define NEUTRON_ITCM_DA 0x0 +#define NEUTRON_DTCM_DA 0x40000 +#define NEUTRON_DTCM_BANK1_OFFSET 0x4000 + +/* Driver flags */ +#define NEUTRON_BOOTED BIT(0) + +/** + * struct neutron_mem_region - Neutron memory region descriptor + * @va: kernel virtual address of the memory region + * @da: Device address of the memory region + * @size: size of the memory region + */ +struct neutron_mem_region { + void __iomem *va; + u64 da; + size_t size; +}; + +enum neutron_mem_id { + NEUTRON_MEM_REGS =3D 0, + NEUTRON_MEM_ITCM, + NEUTRON_MEM_DTCM, + NEUTRON_MEM_MAX +}; + +/** + * struct neutron_device - Neutron device structure + * @base: Base DRM device + * @dev: Pointer to underlying device + * @mem_regions: Array of memory region descriptors + * @irq: IRQ number + * @clks: Neutron clocks + * @num_clks: Number of clocks + * @flags: Software flags used by driver + */ +struct neutron_device { + struct drm_device base; + struct device *dev; + + struct neutron_mem_region mem_regions[NEUTRON_MEM_MAX]; + + int irq; + struct clk_bulk_data *clks; + int num_clks; + u32 flags; +}; + +#define to_neutron_device(drm) \ + container_of(drm, struct neutron_device, base) + +#define NEUTRON_REG(ndev, name) \ + ((ndev)->mem_regions[NEUTRON_MEM_REGS].va + NEUTRON_REG_##name) + +int neutron_boot(struct neutron_device *ndev); +void neutron_shutdown(struct neutron_device *ndev); +void neutron_enable_irq(struct neutron_device *ndev); +void neutron_disable_irq(struct neutron_device *ndev); +void neutron_handle_irq(struct neutron_device *ndev); + +#endif /* __NEUTRON_DEVICE_H__ */ diff --git a/drivers/accel/neutron/neutron_driver.c b/drivers/accel/neutron= /neutron_driver.c new file mode 100644 index 000000000000..7f34785216cf --- /dev/null +++ b/drivers/accel/neutron/neutron_driver.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2025-2026 NXP */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "neutron_device.h" +#include "neutron_driver.h" + +#define NEUTRON_SUSPEND_DELAY_MS 1000 + +static int neutron_open(struct drm_device *drm, struct drm_file *file) +{ + struct neutron_device *ndev =3D to_neutron_device(drm); + struct neutron_file_priv *npriv; + + npriv =3D kzalloc_obj(*npriv); + if (!npriv) + return -ENOMEM; + + npriv->ndev =3D ndev; + file->driver_priv =3D npriv; + + return 0; +} + +static void neutron_postclose(struct drm_device *drm, struct drm_file *fil= e) +{ + struct neutron_file_priv *npriv =3D file->driver_priv; + + kfree(npriv); +} + +DEFINE_DRM_ACCEL_FOPS(neutron_drm_driver_fops); + +static const struct drm_driver neutron_drm_driver =3D { + .driver_features =3D DRIVER_COMPUTE_ACCEL, + .name =3D "neutron", + .desc =3D "NXP Neutron driver", + .major =3D 1, + .minor =3D 0, + + .fops =3D &neutron_drm_driver_fops, + .open =3D neutron_open, + .postclose =3D neutron_postclose, +}; + +static irqreturn_t neutron_irq_handler_thread(int irq, void *data) +{ + struct neutron_device *ndev =3D data; + + neutron_handle_irq(ndev); + + return IRQ_HANDLED; +} + +static int neutron_map_region(struct platform_device *pdev, char *name, + enum neutron_mem_id id) +{ + struct neutron_device *ndev =3D platform_get_drvdata(pdev); + struct neutron_mem_region *mem =3D &ndev->mem_regions[id]; + struct resource *res; + + res =3D platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) + return -EINVAL; + + mem->va =3D devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mem->va)) + return PTR_ERR(mem->va); + + mem->size =3D resource_size(res); + + if (id =3D=3D NEUTRON_MEM_ITCM) + mem->da =3D NEUTRON_ITCM_DA; + else if (id =3D=3D NEUTRON_MEM_DTCM) + mem->da =3D NEUTRON_DTCM_DA; + + return 0; +} + +static int neutron_probe(struct platform_device *pdev) +{ + struct neutron_device *ndev; + struct device *dev; + int ret; + + ndev =3D devm_drm_dev_alloc(&pdev->dev, &neutron_drm_driver, + struct neutron_device, base); + if (IS_ERR(ndev)) + return PTR_ERR(ndev); + + platform_set_drvdata(pdev, ndev); + dev =3D &pdev->dev; + ndev->dev =3D dev; + + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + + /* Map registers, ITCM and DTCM regions of the Neutron device */ + ret =3D neutron_map_region(pdev, "regs", NEUTRON_MEM_REGS); + if (ret) + return ret; + ret =3D neutron_map_region(pdev, "itcm", NEUTRON_MEM_ITCM); + if (ret) + return ret; + ret =3D neutron_map_region(pdev, "dtcm", NEUTRON_MEM_DTCM); + if (ret) + return ret; + + ndev->num_clks =3D devm_clk_bulk_get_all(dev, &ndev->clks); + if (ndev->num_clks < 0) + return ndev->num_clks; + + ndev->irq =3D platform_get_irq(pdev, 0); + if (ndev->irq < 0) + return ndev->irq; + + ret =3D devm_request_threaded_irq(dev, ndev->irq, NULL, + neutron_irq_handler_thread, + IRQF_ONESHOT, KBUILD_MODNAME, ndev); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", ndev->irq); + return ret; + } + + ret =3D of_reserved_mem_device_init(&pdev->dev); + if (ret) { + dev_err(dev, "Failed to initialize reserved memory\n"); + return ret; + } + + ret =3D devm_pm_runtime_enable(dev); + if (ret) + goto free_reserved; + + pm_runtime_set_autosuspend_delay(dev, NEUTRON_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + ret =3D drm_dev_register(&ndev->base, 0); + if (ret) + goto free_reserved; + + return 0; + +free_reserved: + of_reserved_mem_device_release(&pdev->dev); + + return ret; +} + +static void neutron_remove(struct platform_device *pdev) +{ + struct neutron_device *ndev =3D platform_get_drvdata(pdev); + + drm_dev_unregister(&ndev->base); + of_reserved_mem_device_release(&pdev->dev); +} + +static int neutron_runtime_suspend(struct device *dev) +{ + struct neutron_device *ndev =3D dev_get_drvdata(dev); + + neutron_disable_irq(ndev); + neutron_shutdown(ndev); + + clk_bulk_disable_unprepare(ndev->num_clks, ndev->clks); + + return 0; +} + +static int neutron_runtime_resume(struct device *dev) +{ + struct neutron_device *ndev =3D dev_get_drvdata(dev); + int ret; + + ret =3D clk_bulk_prepare_enable(ndev->num_clks, ndev->clks); + if (ret) + return ret; + + ret =3D neutron_boot(ndev); + if (ret) { + clk_bulk_disable_unprepare(ndev->num_clks, ndev->clks); + return ret; + } + + neutron_enable_irq(ndev); + + return 0; +} + +static const struct dev_pm_ops neutron_pm_ops =3D { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(neutron_runtime_suspend, neutron_runtime_resume, NULL) +}; + +static const struct of_device_id neutron_match_table[] =3D { + { .compatible =3D "nxp,imx95-neutron" }, + {} +}; + +MODULE_DEVICE_TABLE(of, neutron_match_table); + +static struct platform_driver neutron_driver =3D { + .probe =3D &neutron_probe, + .remove =3D &neutron_remove, + .driver =3D { + .name =3D "neutron", + .of_match_table =3D of_match_ptr(neutron_match_table), + .pm =3D pm_ptr(&neutron_pm_ops), + }, +}; +module_platform_driver(neutron_driver); + +MODULE_AUTHOR("NXP"); +MODULE_DESCRIPTION("NXP Neutron Accel Driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(NEUTRON_FIRMWARE_NAME); diff --git a/drivers/accel/neutron/neutron_driver.h b/drivers/accel/neutron= /neutron_driver.h new file mode 100644 index 000000000000..cd52b5eb2d27 --- /dev/null +++ b/drivers/accel/neutron/neutron_driver.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2025 NXP */ + +#ifndef __NEUTRON_DRIVER_H__ +#define __NEUTRON_DRIVER_H__ + +struct neutron_device; + +struct neutron_file_priv { + struct neutron_device *ndev; +}; + +#endif /* __NEUTRON_DRIVER_H__ */ --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva020.nxp.com (inva020.nxp.com [92.121.34.13]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 20A1336EAA5; Thu, 26 Feb 2026 13:41:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.13 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113273; cv=none; b=H00FoBYwG2COAelbFnQZPOkmppVBDGLSpyHBunbchXaOphXKtfPOqwThh6mj6P5w9JoqVzRAEoZ52SC8RP53EOwxkvTFOFoU5VaWgmB7FtdA6Va8fuCvFZKlvJ3ZZ6Oo56oqF7DEn3RSFJpMx6qSmLPYl9gruse0aSePNCZxP2s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113273; c=relaxed/simple; bh=Gm6zpMS/O0KecyGAyBqqHMpvs6vSpEpvKk7wEge/k7Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ADlQvk9KLOOnUs99oXS+jxoK+n272YEquvw5l15RT27X6LN2N2N7+Smd5OOgrdNqY8xE/oOgAkdfZsgBbq9dWHR8PUIiZzzMjFUDe80zIEiHCTYDKJSRbr25I66u3GjiXwkjS2jFNeAM0DPwI7B61iWt8munQLaSU3tN+b8Xs1w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.13 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 87E2A1A2550; Thu, 26 Feb 2026 14:41:03 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 773541A2559; Thu, 26 Feb 2026 14:41:03 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id BFD5D2035B; Thu, 26 Feb 2026 14:41:01 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:44 +0200 Subject: [PATCH 5/9] accel/neutron: Add GEM buffer object support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-5-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=9299; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=Gm6zpMS/O0KecyGAyBqqHMpvs6vSpEpvKk7wEge/k7Y=; b=rUusO9TFRzN9pG9c1CNh8YOm96d5xwQDBjY3QjlGRPNoyHfgHzGrwd9XEGxDb9qUVcXIHrSlD zLpx9QNarv3BhIyUaIf8saTHCCehxewAtIdbGnuH+2Qoty1DHwdnIA/ X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Add the following IOCTLs: - CREATE_BO - for creating a new buffer object and passing BO info back to user - SYNC_BO - for explicit DMA sync operations on the BO memory, since Neutron isn't guaranteed to be cache coherent. User controls which portions of the buffer memory to sync and the direction. The Neutron device requires contiguous DMA buffers, so use the GEM DMA helpers for creating and managing the BOs. Depending on the platform it is integrated on, Neutron device may or may not be cache coherent. On i.MX95, the first platform for which we add Neutron support, it is not. Signed-off-by: Ioana Ciocoi-Radulescu --- drivers/accel/neutron/Makefile | 3 +- drivers/accel/neutron/neutron_driver.c | 13 +++- drivers/accel/neutron/neutron_gem.c | 115 +++++++++++++++++++++++++++++= ++++ drivers/accel/neutron/neutron_gem.h | 14 ++++ include/uapi/drm/neutron_accel.h | 79 ++++++++++++++++++++++ 5 files changed, 222 insertions(+), 2 deletions(-) diff --git a/drivers/accel/neutron/Makefile b/drivers/accel/neutron/Makefile index 7592e318dd83..d4298c7a8535 100644 --- a/drivers/accel/neutron/Makefile +++ b/drivers/accel/neutron/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_DRM_ACCEL_NXP_NEUTRON) :=3D neutron.o =20 neutron-y :=3D \ neutron_driver.o \ - neutron_device.o + neutron_device.o \ + neutron_gem.o diff --git a/drivers/accel/neutron/neutron_driver.c b/drivers/accel/neutron= /neutron_driver.c index 7f34785216cf..c9a18bf52037 100644 --- a/drivers/accel/neutron/neutron_driver.c +++ b/drivers/accel/neutron/neutron_driver.c @@ -14,12 +14,19 @@ #include #include #include +#include =20 #include "neutron_device.h" #include "neutron_driver.h" +#include "neutron_gem.h" =20 #define NEUTRON_SUSPEND_DELAY_MS 1000 =20 +static const struct drm_ioctl_desc neutron_drm_ioctls[] =3D { + DRM_IOCTL_DEF_DRV(NEUTRON_CREATE_BO, neutron_ioctl_create_bo, 0), + DRM_IOCTL_DEF_DRV(NEUTRON_SYNC_BO, neutron_ioctl_sync_bo, 0), +}; + static int neutron_open(struct drm_device *drm, struct drm_file *file) { struct neutron_device *ndev =3D to_neutron_device(drm); @@ -45,7 +52,7 @@ static void neutron_postclose(struct drm_device *drm, str= uct drm_file *file) DEFINE_DRM_ACCEL_FOPS(neutron_drm_driver_fops); =20 static const struct drm_driver neutron_drm_driver =3D { - .driver_features =3D DRIVER_COMPUTE_ACCEL, + .driver_features =3D DRIVER_COMPUTE_ACCEL | DRIVER_GEM, .name =3D "neutron", .desc =3D "NXP Neutron driver", .major =3D 1, @@ -54,6 +61,10 @@ static const struct drm_driver neutron_drm_driver =3D { .fops =3D &neutron_drm_driver_fops, .open =3D neutron_open, .postclose =3D neutron_postclose, + .ioctls =3D neutron_drm_ioctls, + .num_ioctls =3D ARRAY_SIZE(neutron_drm_ioctls), + + .gem_create_object =3D neutron_gem_create_object, }; =20 static irqreturn_t neutron_irq_handler_thread(int irq, void *data) diff --git a/drivers/accel/neutron/neutron_gem.c b/drivers/accel/neutron/ne= utron_gem.c new file mode 100644 index 000000000000..9b5460d9bcc6 --- /dev/null +++ b/drivers/accel/neutron/neutron_gem.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2025-2026 NXP */ + +#include +#include +#include +#include +#include +#include +#include + +#include "neutron_device.h" +#include "neutron_gem.h" + +#define NEUTRON_BO_ALIGN SZ_1M + +struct drm_gem_object *neutron_gem_create_object(struct drm_device *drm, s= ize_t size) +{ + struct neutron_device *ndev =3D to_neutron_device(drm); + struct drm_gem_dma_object *dma_obj; + struct drm_gem_object *gem_obj; + + dma_obj =3D kzalloc_obj(*dma_obj); + if (!dma_obj) + return ERR_PTR(-ENOMEM); + + dma_obj->map_noncoherent =3D !dev_is_dma_coherent(ndev->dev); + dma_obj->map_bidirectional =3D true; + gem_obj =3D &dma_obj->base; + + return gem_obj; +} + +int neutron_ioctl_create_bo(struct drm_device *drm, void *data, struct drm= _file *filp) +{ + struct drm_neutron_create_bo *args =3D data; + struct drm_gem_dma_object *dma_obj; + struct drm_gem_object *gem_obj; + size_t size; + int ret; + + if (!args->size || args->pad) + return -EINVAL; + + size =3D ALIGN(args->size, NEUTRON_BO_ALIGN); + + dma_obj =3D drm_gem_dma_create(drm, size); + if (IS_ERR(dma_obj)) + return PTR_ERR(dma_obj); + + /* We expect correctly aligned buffers, but double-check */ + if (drm_WARN_ON(drm, !IS_ALIGNED(dma_obj->dma_addr, NEUTRON_BO_ALIGN))) { + ret =3D -EFAULT; + goto out_put; + } + + gem_obj =3D &dma_obj->base; + ret =3D drm_gem_handle_create(filp, gem_obj, &args->handle); + if (ret) + goto out_put; + + args->map_offset =3D drm_vma_node_offset_addr(&gem_obj->vma_node); + args->size =3D gem_obj->size; + +out_put: + /* No need to keep a reference of the GEM object. Freeing is handled by u= ser */ + drm_gem_object_put(gem_obj); + + return ret; +} + +int neutron_ioctl_sync_bo(struct drm_device *drm, void *data, struct drm_f= ile *filp) +{ + struct drm_neutron_sync_bo *args =3D data; + struct drm_gem_dma_object *dma_obj; + struct drm_gem_object *gem_obj; + dma_addr_t start_addr; + int ret =3D 0; + + gem_obj =3D drm_gem_object_lookup(filp, args->handle); + if (!gem_obj) { + dev_dbg(drm->dev, "Invalid BO handle %u\n", args->handle); + return -ENOENT; + } + + dma_obj =3D to_drm_gem_dma_obj(gem_obj); + + if (!args->size || args->offset >=3D gem_obj->size || + args->size > gem_obj->size - args->offset) { + dev_dbg(drm->dev, "Invalid offset/size for BO sync\n"); + ret =3D -EINVAL; + goto out_put; + } + + start_addr =3D dma_obj->dma_addr + args->offset; + + switch (args->direction) { + case DRM_NEUTRON_SYNC_TO_DEVICE: + dma_sync_single_for_device(drm->dev, start_addr, args->size, + DMA_BIDIRECTIONAL); + break; + case DRM_NEUTRON_SYNC_FROM_DEVICE: + dma_sync_single_for_cpu(drm->dev, start_addr, args->size, + DMA_BIDIRECTIONAL); + break; + default: + dev_dbg(drm->dev, "Invalid direction for BO sync\n"); + ret =3D -EINVAL; + } + +out_put: + drm_gem_object_put(gem_obj); + + return ret; +} diff --git a/drivers/accel/neutron/neutron_gem.h b/drivers/accel/neutron/ne= utron_gem.h new file mode 100644 index 000000000000..95ba2fe96617 --- /dev/null +++ b/drivers/accel/neutron/neutron_gem.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2025-2026 NXP */ + +#ifndef __NEUTRON_GEM_H__ +#define __NEUTRON_GEM_H__ + +#include + +struct drm_gem_object *neutron_gem_create_object(struct drm_device *drm, s= ize_t size); + +int neutron_ioctl_create_bo(struct drm_device *drm, void *data, struct drm= _file *filp); +int neutron_ioctl_sync_bo(struct drm_device *drm, void *data, struct drm_f= ile *filp); + +#endif /* __NEUTRON_GEM_H__ */ diff --git a/include/uapi/drm/neutron_accel.h b/include/uapi/drm/neutron_ac= cel.h new file mode 100644 index 000000000000..2f5639f2e0e8 --- /dev/null +++ b/include/uapi/drm/neutron_accel.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* Copyright 2025-2026 NXP */ + +#ifndef __NEUTRON_ACCEL_H__ +#define __NEUTRON_ACCEL_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * enum drm_neutron_ioctl - Neutron IOCTL IDs + * + * @DRM_NEUTRON_CREATE_BO: Create a buffer object + * @DRM_NEUTRON_SYNC_BO: Sync (parts of) the buffer object memory + */ +enum drm_neutron_ioctl { + DRM_NEUTRON_CREATE_BO =3D 0, + DRM_NEUTRON_SYNC_BO, +}; + +/** + * struct drm_neutron_create_bo - Create a buffer object and return buffer + * info to user + * + * @size: Size in bytes of requested buffer. May be updated by driver + * if allocated size different than requested + * @handle: Returned handle for the new buffer object + * @pad: MBZ + * @map_offset: Returned offset for mmap() calls + */ +struct drm_neutron_create_bo { + __u64 size; + __u32 handle; + __u32 pad; + __u64 map_offset; +}; + +/** + * enum drm_neutron_sync_dir - Direction of buffer object synchronization + * + * @DRM_NEUTRON_SYNC_TO_DEVICE: Sync from CPU to device + * @DRM_NEUTRON_SYNC_FROM_DEVICE: Sync from device to CPU + */ +enum drm_neutron_sync_dir { + DRM_NEUTRON_SYNC_TO_DEVICE =3D 0, + DRM_NEUTRON_SYNC_FROM_DEVICE, +}; + +/** + * struct drm_neutron_sync_bo - Sync buffer object memory + * + * @handle: Handle of buffer object to sync + * @direction: Direction of sync, can be one of enum drm_neutron_sync_dir + * @size: Size of the memory to sync, in bytes + * @offset: Offset inside the buffer, in bytes + */ +struct drm_neutron_sync_bo { + __u32 handle; + __u32 direction; + __u64 size; + __u64 offset; +}; + +#define DRM_IOCTL_NEUTRON_CREATE_BO \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_CREATE_BO, \ + struct drm_neutron_create_bo) + +#define DRM_IOCTL_NEUTRON_SYNC_BO \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_SYNC_BO, \ + struct drm_neutron_sync_bo) + +#if defined(__cplusplus) +} +#endif + +#endif /* __NEUTRON_ACCEL_H__ */ --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 156433859DB; Thu, 26 Feb 2026 13:41:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113273; cv=none; b=EmBN1eMgclfTujjttISfk25i/2QLQtB02VOJeF5qYOPYQed8vUtanukjAiYddEKswpkWiaW8V955ZxUylcrjM0beVbHrhPUprBZxdUNu78xhWKHyKn2fOqnwKPFe2jrr/gNpR5+W/xk6US9gGzXpLB4YfQiUkYtxW/VqARI/oQY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113273; c=relaxed/simple; bh=Jty6B0ytRRom/v7JJudk9QB5erW8GFmasf1vl2KeMcs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DgDsLVs4j5JMHsR8HfjtlRUMxSSJKHp8G8Sa7f3Sz419msuEzV/oSwA4Qw3jhynbE5FsAa/yDRH9kg/gkbsVqimph4Wc1Qotydk+Lll5D5R/auDX0NwecBd56kbH/zFRkNdLjoLLaX7/Aq3kLr9TeSCfRC5+4hS4VSRkGDueGJI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 9980D2014FB; Thu, 26 Feb 2026 14:41:04 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 8C9952005B7; Thu, 26 Feb 2026 14:41:04 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id D65BA2035B; Thu, 26 Feb 2026 14:41:02 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:45 +0200 Subject: [PATCH 6/9] accel/neutron: Add mailbox support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-6-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=4796; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=Jty6B0ytRRom/v7JJudk9QB5erW8GFmasf1vl2KeMcs=; b=knwxwSZo92Y/ebknfNXmi9yLBr7eb8/1HCXPEScLpQ6wM5aaZLAAtYJu/wZLqRyi+1MStjbEc grMwI3JbDuyBCUE5VQW/aFoLGvspo9gHCKoeBei1j8yMwK3pmkMliCi X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP The driver communicates with the Neutron firmware via eight register-backed mailboxes. A subset of the mailbox registers are used to pass commands from driver to Neutron, while the rest are written by Neutron firmware with status/ack info. Signed-off-by: Jiwei Fu Signed-off-by: Ioana Ciocoi-Radulescu --- drivers/accel/neutron/Makefile | 3 ++- drivers/accel/neutron/neutron_device.c | 4 +++ drivers/accel/neutron/neutron_mailbox.c | 47 +++++++++++++++++++++++++++++= ++++ drivers/accel/neutron/neutron_mailbox.h | 42 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/accel/neutron/Makefile b/drivers/accel/neutron/Makefile index d4298c7a8535..192ed896a9f9 100644 --- a/drivers/accel/neutron/Makefile +++ b/drivers/accel/neutron/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_DRM_ACCEL_NXP_NEUTRON) :=3D neutron.o neutron-y :=3D \ neutron_driver.o \ neutron_device.o \ - neutron_gem.o + neutron_gem.o \ + neutron_mailbox.o diff --git a/drivers/accel/neutron/neutron_device.c b/drivers/accel/neutron= /neutron_device.c index 61b3c96b4996..e5c09105be99 100644 --- a/drivers/accel/neutron/neutron_device.c +++ b/drivers/accel/neutron/neutron_device.c @@ -7,6 +7,7 @@ #include =20 #include "neutron_device.h" +#include "neutron_mailbox.h" =20 void neutron_enable_irq(struct neutron_device *ndev) { @@ -148,6 +149,9 @@ int neutron_boot(struct neutron_device *ndev) if (ret) return ret; =20 + /* Prepare device to receive jobs */ + neutron_mbox_reset_state(ndev); + ndev->flags |=3D NEUTRON_BOOTED; =20 return 0; diff --git a/drivers/accel/neutron/neutron_mailbox.c b/drivers/accel/neutro= n/neutron_mailbox.c new file mode 100644 index 000000000000..327ef2e8081d --- /dev/null +++ b/drivers/accel/neutron/neutron_mailbox.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2023, 2025-2026 NXP */ + +#include + +#include "neutron_device.h" +#include "neutron_mailbox.h" + +#define NEUTRON_MBOX_FW_STATUS(dev) NEUTRON_REG(dev, MBOX0) +#define NEUTRON_MBOX_FW_ERRCODE(dev) NEUTRON_REG(dev, MBOX1) +#define NEUTRON_MBOX_CMD_ID(dev) NEUTRON_REG(dev, MBOX3) +#define NEUTRON_MBOX_CMD_ARG_BASE(dev) NEUTRON_REG(dev, MBOX4) +#define NEUTRON_MBOX_CMD_ARG(dev, i) (NEUTRON_MBOX_CMD_ARG_BASE(dev) + (i)= * 4) + +int neutron_mbox_send_cmd(struct neutron_device *ndev, struct neutron_mbox= _cmd *cmd) +{ + u32 status; + int i; + + /* Make sure Neutron is ready to receive commands */ + status =3D readl_relaxed(NEUTRON_MBOX_FW_STATUS(ndev)); + if (status !=3D NEUTRON_FW_STATUS_RESET) + return -EBUSY; + + for (i =3D 0; i < NEUTRON_MBOX_MAX_CMD_ARGS; i++) + writel_relaxed(cmd->args[i], NEUTRON_MBOX_CMD_ARG(ndev, i)); + writel(cmd->id, NEUTRON_MBOX_CMD_ID(ndev)); + + return 0; +} + +int neutron_mbox_reset_state(struct neutron_device *ndev) +{ + u32 status; + + writel_relaxed(NEUTRON_CMD_RESET_STATE, NEUTRON_MBOX_CMD_ID(ndev)); + + return readl_poll_timeout(NEUTRON_MBOX_FW_STATUS(ndev), status, + status =3D=3D NEUTRON_FW_STATUS_RESET, + 100, 100 * USEC_PER_MSEC); +} + +void neutron_mbox_read_state(struct neutron_device *ndev, struct neutron_m= box_state *state) +{ + state->status =3D readl_relaxed(NEUTRON_MBOX_FW_STATUS(ndev)); + state->err_code =3D readl_relaxed(NEUTRON_MBOX_FW_ERRCODE(ndev)); +} diff --git a/drivers/accel/neutron/neutron_mailbox.h b/drivers/accel/neutro= n/neutron_mailbox.h new file mode 100644 index 000000000000..4fe40a2f6a0c --- /dev/null +++ b/drivers/accel/neutron/neutron_mailbox.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2023, 2025-2026 NXP */ + +#ifndef __NEUTRON_MAILBOX_H__ +#define __NEUTRON_MAILBOX_H__ + +#include + +struct neutron_device; + +/* Device (firmware) status magic values */ +enum neutron_mbox_fwstat { + NEUTRON_FW_STATUS_RESET =3D 0, + NEUTRON_FW_STATUS_ACK =3D 0xA3, + NEUTRON_FW_STATUS_DONE =3D 0xAD0, +}; + +/* Firmware command opcodes */ +enum neutron_mbox_cmdid { + NEUTRON_CMD_INFERENCE =3D 0x269, + NEUTRON_CMD_RESET_STATE =3D 0x23637, +}; + +#define NEUTRON_MBOX_MAX_CMD_ARGS 4 + +/* Firmware command */ +struct neutron_mbox_cmd { + enum neutron_mbox_cmdid id; + u32 args[NEUTRON_MBOX_MAX_CMD_ARGS]; +}; + +/* Device state */ +struct neutron_mbox_state { + enum neutron_mbox_fwstat status; + u32 err_code; +}; + +int neutron_mbox_send_cmd(struct neutron_device *ndev, struct neutron_mbox= _cmd *cmd); +void neutron_mbox_read_state(struct neutron_device *ndev, struct neutron_m= box_state *state); +int neutron_mbox_reset_state(struct neutron_device *ndev); + +#endif /* __NEUTRON_MAILBOX_H__ */ --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 26BA726CE2D; Thu, 26 Feb 2026 13:41:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113269; cv=none; b=mL2IuoybnT5Uor0WxoIo0vt1q/BMskrMKDJzh2KQpsbj9tJsLvbgXih2fqkfGV0RqpuUCqi4/t6wbL/LGar/Z+ZnSzYgXOg9vBd1WKOuXJIll57pxNoeugFhce7xHuRgoCTmONGKH2zHz/KqT/Mz4rkPkx0e+ulF4oPB7M5DvZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113269; c=relaxed/simple; bh=h3bWVlktlUdQ/pRmPzjGCjqsU3sTnai48N2RHMkisd0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B5G3tnCX6N/0P1V99Kr62eGk9i+zMgKSYO7RxEoHAI9l9HtXGhNfp3VHCEUDO/N6QWf/VzDQSFg0pQPbPpXW8DF+IJpuphYqkQDlrGCEes/lWZMxmY95qlQoxr4qmvfySBLvhkm2PHfZg+YDCn9OmXjEYBFHG0xwV8uIUWdxtsA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id B3AD1201533; Thu, 26 Feb 2026 14:41:05 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id A3E2920152C; Thu, 26 Feb 2026 14:41:05 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id EBF352035B; Thu, 26 Feb 2026 14:41:03 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:46 +0200 Subject: [PATCH 7/9] accel/neutron: Add job submission IOCTL Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-7-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=20473; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=h3bWVlktlUdQ/pRmPzjGCjqsU3sTnai48N2RHMkisd0=; b=pGoxDwycOetg2D7F9EEoF6cV0+sW+I3yqE9OYiCuxd3DtjkFlXNtebwVsGpi+MH+kjOBmUgeM IIoxg4rfRRsDR2iNSL6RXptE0copl5tFePqG2atiPRAzURyLoQWWz3Q X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Neutron can execute a single job at a time. For now, only inference jobs are supported. Each job has exactly one BO associated with it. When submitting a job, user also provides a syncobj handle on which it will wait for job completion. We use the DRM GPU scheduler for job management. Large part of the job submission code is based on the example of the ethosu driver. Signed-off-by: Jiwei Fu Signed-off-by: Ioana Ciocoi-Radulescu --- drivers/accel/neutron/Makefile | 1 + drivers/accel/neutron/neutron_device.c | 8 +- drivers/accel/neutron/neutron_device.h | 21 ++ drivers/accel/neutron/neutron_driver.c | 28 ++- drivers/accel/neutron/neutron_driver.h | 3 + drivers/accel/neutron/neutron_job.c | 367 +++++++++++++++++++++++++++++= ++++ drivers/accel/neutron/neutron_job.h | 45 ++++ include/uapi/drm/neutron_accel.h | 51 +++++ 8 files changed, 519 insertions(+), 5 deletions(-) diff --git a/drivers/accel/neutron/Makefile b/drivers/accel/neutron/Makefile index 192ed896a9f9..ac6dd576521c 100644 --- a/drivers/accel/neutron/Makefile +++ b/drivers/accel/neutron/Makefile @@ -6,4 +6,5 @@ neutron-y :=3D \ neutron_driver.o \ neutron_device.o \ neutron_gem.o \ + neutron_job.o \ neutron_mailbox.o diff --git a/drivers/accel/neutron/neutron_device.c b/drivers/accel/neutron= /neutron_device.c index e5c09105be99..571ec906ad72 100644 --- a/drivers/accel/neutron/neutron_device.c +++ b/drivers/accel/neutron/neutron_device.c @@ -7,6 +7,7 @@ #include =20 #include "neutron_device.h" +#include "neutron_job.h" #include "neutron_mailbox.h" =20 void neutron_enable_irq(struct neutron_device *ndev) @@ -32,9 +33,14 @@ void neutron_handle_irq(struct neutron_device *ndev) /* Write 1 to clear */ writel_relaxed(appstatus & APPSTATUS_CLEAR_MASK, NEUTRON_REG(ndev, APPSTA= TUS)); =20 - if (appstatus & APPSTATUS_FAULTCAUSE_MASK) + if (appstatus & APPSTATUS_FAULTCAUSE_MASK) { dev_err(ndev->dev, "Neutron halted due to fault: 0x%lx\n", FIELD_GET(APPSTATUS_FAULTCAUSE_MASK, appstatus)); + return neutron_job_err_handler(ndev); + } + + if (appstatus & APPSTATUS_INFDONE) + neutron_job_done_handler(ndev); } =20 #define neutron_boot_done(appctrl) \ diff --git a/drivers/accel/neutron/neutron_device.h b/drivers/accel/neutron= /neutron_device.h index 8e4df7462d82..0ed72965774d 100644 --- a/drivers/accel/neutron/neutron_device.h +++ b/drivers/accel/neutron/neutron_device.h @@ -9,8 +9,10 @@ #include #include #include +#include =20 struct clk_bulk_data; +struct neutron_job; =20 #define NEUTRON_FIRMWARE_NAME "NeutronFirmware.elf" =20 @@ -92,6 +94,13 @@ enum neutron_mem_id { * @clks: Neutron clocks * @num_clks: Number of clocks * @flags: Software flags used by driver + * @fence_lock: DMA fence lock + * @sched: GPU scheduler + * @sched_lock: Scheduler lock, for neutron_push_job + * @fence_context: Fence context + * @job_seqno: Job sequence number + * @job_lock: Job lock, for active_job handling + * @active_job: Currently active job */ struct neutron_device { struct drm_device base; @@ -103,6 +112,18 @@ struct neutron_device { struct clk_bulk_data *clks; int num_clks; u32 flags; + + /* For dma_fence */ + spinlock_t fence_lock; + struct drm_gpu_scheduler sched; + /* For neutron_push_job */ + struct mutex sched_lock; + u64 fence_context; + u64 job_seqno; + + /* For active_job handling */ + struct mutex job_lock; + struct neutron_job *active_job; }; =20 #define to_neutron_device(drm) \ diff --git a/drivers/accel/neutron/neutron_driver.c b/drivers/accel/neutron= /neutron_driver.c index c9a18bf52037..ceae1f7e8359 100644 --- a/drivers/accel/neutron/neutron_driver.c +++ b/drivers/accel/neutron/neutron_driver.c @@ -19,40 +19,53 @@ #include "neutron_device.h" #include "neutron_driver.h" #include "neutron_gem.h" +#include "neutron_job.h" =20 #define NEUTRON_SUSPEND_DELAY_MS 1000 =20 static const struct drm_ioctl_desc neutron_drm_ioctls[] =3D { DRM_IOCTL_DEF_DRV(NEUTRON_CREATE_BO, neutron_ioctl_create_bo, 0), DRM_IOCTL_DEF_DRV(NEUTRON_SYNC_BO, neutron_ioctl_sync_bo, 0), + DRM_IOCTL_DEF_DRV(NEUTRON_SUBMIT_JOB, neutron_ioctl_submit_job, 0), }; =20 static int neutron_open(struct drm_device *drm, struct drm_file *file) { struct neutron_device *ndev =3D to_neutron_device(drm); struct neutron_file_priv *npriv; + int ret; =20 npriv =3D kzalloc_obj(*npriv); if (!npriv) return -ENOMEM; =20 npriv->ndev =3D ndev; - file->driver_priv =3D npriv; =20 + ret =3D neutron_job_open(npriv); + if (ret) + goto err_free; + + file->driver_priv =3D npriv; return 0; + +err_free: + kfree(npriv); + return ret; } =20 static void neutron_postclose(struct drm_device *drm, struct drm_file *fil= e) { struct neutron_file_priv *npriv =3D file->driver_priv; =20 + neutron_job_close(npriv); kfree(npriv); } =20 DEFINE_DRM_ACCEL_FOPS(neutron_drm_driver_fops); =20 static const struct drm_driver neutron_drm_driver =3D { - .driver_features =3D DRIVER_COMPUTE_ACCEL | DRIVER_GEM, + .driver_features =3D DRIVER_COMPUTE_ACCEL | DRIVER_GEM | + DRIVER_SYNCOBJ, .name =3D "neutron", .desc =3D "NXP Neutron driver", .major =3D 1, @@ -151,19 +164,25 @@ static int neutron_probe(struct platform_device *pdev) return ret; } =20 - ret =3D devm_pm_runtime_enable(dev); + ret =3D neutron_job_init(ndev); if (ret) goto free_reserved; =20 + ret =3D devm_pm_runtime_enable(dev); + if (ret) + goto free_job; + pm_runtime_set_autosuspend_delay(dev, NEUTRON_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); =20 ret =3D drm_dev_register(&ndev->base, 0); if (ret) - goto free_reserved; + goto free_job; =20 return 0; =20 +free_job: + neutron_job_fini(ndev); free_reserved: of_reserved_mem_device_release(&pdev->dev); =20 @@ -175,6 +194,7 @@ static void neutron_remove(struct platform_device *pdev) struct neutron_device *ndev =3D platform_get_drvdata(pdev); =20 drm_dev_unregister(&ndev->base); + neutron_job_fini(ndev); of_reserved_mem_device_release(&pdev->dev); } =20 diff --git a/drivers/accel/neutron/neutron_driver.h b/drivers/accel/neutron= /neutron_driver.h index cd52b5eb2d27..b709de74105a 100644 --- a/drivers/accel/neutron/neutron_driver.h +++ b/drivers/accel/neutron/neutron_driver.h @@ -4,10 +4,13 @@ #ifndef __NEUTRON_DRIVER_H__ #define __NEUTRON_DRIVER_H__ =20 +#include + struct neutron_device; =20 struct neutron_file_priv { struct neutron_device *ndev; + struct drm_sched_entity sched_entity; }; =20 #endif /* __NEUTRON_DRIVER_H__ */ diff --git a/drivers/accel/neutron/neutron_job.c b/drivers/accel/neutron/ne= utron_job.c new file mode 100644 index 000000000000..316e361166a2 --- /dev/null +++ b/drivers/accel/neutron/neutron_job.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2025-2026 NXP */ + +#include +#include +#include +#include +#include +#include + +#include "neutron_driver.h" +#include "neutron_device.h" +#include "neutron_gem.h" +#include "neutron_mailbox.h" +#include "neutron_job.h" + +#define NEUTRON_JOB_TIMEOUT_MS 5000 + +static const char *neutron_fence_get_driver_name(struct dma_fence *fence) +{ + return "neutron"; +} + +static const char *neutron_fence_get_timeline_name(struct dma_fence *fence) +{ + return "neutron-npu"; +} + +static const struct dma_fence_ops neutron_fence_ops =3D { + .get_driver_name =3D neutron_fence_get_driver_name, + .get_timeline_name =3D neutron_fence_get_timeline_name, +}; + +static void neutron_hw_submit(struct neutron_job *job) +{ + struct neutron_device *ndev =3D job->ndev; + struct neutron_mbox_cmd cmd =3D {0}; + u32 base_l, base_h; + u64 base_addr; + int ret; + + switch (job->type) { + case DRM_NEUTRON_JOB_INFERENCE: + cmd.id =3D NEUTRON_CMD_INFERENCE; + cmd.args[0] =3D job->inference.tensor_offset; + cmd.args[1] =3D job->inference.microcode_offset; + cmd.args[2] =3D job->inference.tensor_count; + break; + default: + dev_WARN(ndev->dev, "Unknown job type: %d\n", job->type); + return; + } + + base_addr =3D to_drm_gem_dma_obj(job->bo)->dma_addr; + base_l =3D lower_32_bits(base_addr); + base_h =3D upper_32_bits(base_addr); + + writel_relaxed(base_l, NEUTRON_REG(ndev, BASEDDRL)); + writel_relaxed(base_l, NEUTRON_REG(ndev, BASEINOUTL)); + writel_relaxed(base_l, NEUTRON_REG(ndev, BASESPILLL)); + writel_relaxed(base_h, NEUTRON_REG(ndev, BASEDDRH)); + writel_relaxed(base_h, NEUTRON_REG(ndev, BASEINOUTH)); + writel_relaxed(base_h, NEUTRON_REG(ndev, BASESPILLH)); + + ret =3D neutron_mbox_send_cmd(ndev, &cmd); + if (ret) { + /* Nothing we can do here, we'll reset the device on timeout */ + dev_err(ndev->dev, "Failed to submit job, device is busy\n"); + } +} + +void neutron_job_err_handler(struct neutron_device *ndev) +{ + guard(mutex)(&ndev->job_lock); + + if (ndev->active_job) + drm_sched_fault(&ndev->sched); +} + +void neutron_job_done_handler(struct neutron_device *ndev) +{ + struct neutron_mbox_state state; + + neutron_mbox_read_state(ndev, &state); + if (state.status !=3D NEUTRON_FW_STATUS_DONE) { + dev_err(ndev->dev, "Inconsistent firmware state: status 0x%x, err 0x%x\n= ", + state.status, state.err_code); + return neutron_job_err_handler(ndev); + } + + if (state.err_code !=3D 0) + dev_warn(ndev->dev, "Job finished with error: 0x%x\n", + state.err_code); + + /* Reset Neutron internal state to prepare for next inference */ + neutron_mbox_reset_state(ndev); + + scoped_guard(mutex, &ndev->job_lock) { + if (ndev->active_job) { + dma_fence_signal(ndev->active_job->neutron_fence); + ndev->active_job =3D NULL; + } + } +} + +static void neutron_cleanup_job(struct kref *ref) +{ + struct neutron_job *job =3D container_of(ref, struct neutron_job, refcnt); + + pm_runtime_put_autosuspend(job->ndev->base.dev); + + dma_fence_put(job->neutron_fence); + dma_fence_put(job->sched_fence); + drm_gem_object_put(job->bo); + drm_syncobj_put(job->syncobj); + + kfree(job); +} + +static void neutron_put_job(struct neutron_job *job) +{ + kref_put(&job->refcnt, neutron_cleanup_job); +} + +static void neutron_free_job(struct drm_sched_job *sched_job) +{ + struct neutron_job *job =3D to_neutron_job(sched_job); + + drm_sched_job_cleanup(sched_job); + neutron_put_job(job); +} + +static struct dma_fence *neutron_run_job(struct drm_sched_job *sched_job) +{ + struct neutron_job *job =3D to_neutron_job(sched_job); + struct dma_fence *fence =3D job->neutron_fence; + struct neutron_device *ndev =3D job->ndev; + + if (unlikely(job->base.s_fence->finished.error)) + return NULL; + + dma_fence_init(fence, &neutron_fence_ops, &ndev->fence_lock, + ndev->fence_context, ++ndev->job_seqno); + dma_fence_get(fence); + + scoped_guard(mutex, &ndev->job_lock) { + ndev->active_job =3D job; + neutron_hw_submit(job); + } + + return fence; +} + +static enum drm_gpu_sched_stat neutron_timedout_job(struct drm_sched_job *= sched_job) +{ + struct neutron_job *job =3D to_neutron_job(sched_job); + struct neutron_device *ndev =3D job->ndev; + struct neutron_mbox_state state; + + /* We assume Neutron is stuck, retrieve current state and reset */ + neutron_mbox_read_state(ndev, &state); + dev_err(ndev->dev, "Neutron timedout, status: 0x%x, err: 0x%x\n", + state.status, state.err_code); + + drm_sched_stop(&ndev->sched, sched_job); + + scoped_guard(mutex, &ndev->job_lock) + ndev->active_job =3D NULL; + + pm_runtime_force_suspend(ndev->dev); + pm_runtime_force_resume(ndev->dev); + + drm_sched_start(&ndev->sched, 0); + + return DRM_GPU_SCHED_STAT_RESET; +} + +static void neutron_cancel_job(struct drm_sched_job *sched_job) +{ + struct neutron_job *job =3D to_neutron_job(sched_job); + struct neutron_device *ndev =3D job->ndev; + + guard(mutex)(&ndev->job_lock); + + if (!dma_fence_is_signaled(job->neutron_fence)) { + dma_fence_set_error(job->neutron_fence, -ECANCELED); + dma_fence_signal(job->neutron_fence); + } +} + +static const struct drm_sched_backend_ops neutron_sched_ops =3D { + .run_job =3D neutron_run_job, + .free_job =3D neutron_free_job, + .timedout_job =3D neutron_timedout_job, + .cancel_job =3D neutron_cancel_job, +}; + +int neutron_job_init(struct neutron_device *ndev) +{ + const struct drm_sched_init_args args =3D { + .ops =3D &neutron_sched_ops, + .num_rqs =3D DRM_SCHED_PRIORITY_COUNT, + .credit_limit =3D 1, + .timeout =3D msecs_to_jiffies(NEUTRON_JOB_TIMEOUT_MS), + .name =3D dev_name(ndev->dev), + .dev =3D ndev->dev, + }; + int ret; + + ret =3D devm_mutex_init(ndev->dev, &ndev->sched_lock); + if (ret) + return ret; + ret =3D devm_mutex_init(ndev->dev, &ndev->job_lock); + if (ret) + return ret; + spin_lock_init(&ndev->fence_lock); + + ndev->fence_context =3D dma_fence_context_alloc(1); + + ret =3D drm_sched_init(&ndev->sched, &args); + if (ret) + dev_err(ndev->dev, "Error creating DRM scheduler\n"); + + return ret; +} + +void neutron_job_fini(struct neutron_device *ndev) +{ + drm_sched_fini(&ndev->sched); +} + +int neutron_job_open(struct neutron_file_priv *npriv) +{ + struct neutron_device *ndev =3D npriv->ndev; + struct drm_gpu_scheduler *sched =3D &ndev->sched; + int ret; + + ret =3D drm_sched_entity_init(&npriv->sched_entity, + DRM_SCHED_PRIORITY_NORMAL, + &sched, 1, NULL); + if (ret) + dev_err(ndev->dev, "Error creating scheduler entity\n"); + + return ret; +} + +void neutron_job_close(struct neutron_file_priv *npriv) +{ + drm_sched_entity_destroy(&npriv->sched_entity); +} + +static int neutron_push_job(struct neutron_job *job) +{ + struct neutron_device *ndev =3D job->ndev; + struct ww_acquire_ctx acquire_ctx; + int ret; + + ret =3D drm_gem_lock_reservations(&job->bo, 1, &acquire_ctx); + if (ret) + return ret; + + ret =3D dma_resv_reserve_fences(job->bo->resv, 1); + if (ret) + goto out_unlock_res; + + ret =3D drm_sched_job_add_implicit_dependencies(&job->base, job->bo, true= ); + if (ret) + goto out_unlock_res; + + ret =3D pm_runtime_resume_and_get(ndev->base.dev); + if (ret) + goto out_unlock_res; + + scoped_guard(mutex, &ndev->sched_lock) { + drm_sched_job_arm(&job->base); + + job->sched_fence =3D dma_fence_get(&job->base.s_fence->finished); + drm_syncobj_replace_fence(job->syncobj, job->sched_fence); + + kref_get(&job->refcnt); + drm_sched_entity_push_job(&job->base); + + dma_resv_add_fence(job->bo->resv, job->sched_fence, + DMA_RESV_USAGE_WRITE); + } + +out_unlock_res: + drm_gem_unlock_reservations(&job->bo, 1, &acquire_ctx); + + return ret; +} + +int neutron_ioctl_submit_job(struct drm_device *drm, void *data, struct dr= m_file *filp) +{ + struct neutron_device *ndev =3D to_neutron_device(drm); + struct neutron_file_priv *npriv =3D filp->driver_priv; + struct drm_neutron_submit_job *args =3D data; + struct neutron_job *job; + int ret; + + if (args->pad) + return -EINVAL; + + job =3D kzalloc_obj(*job); + if (!job) + return -ENOMEM; + + job->ndev =3D ndev; + kref_init(&job->refcnt); + + job->neutron_fence =3D kzalloc_obj(*job->neutron_fence); + if (!job->neutron_fence) { + ret =3D -ENOMEM; + goto out_free_job; + } + + switch (args->type) { + case DRM_NEUTRON_JOB_INFERENCE: + memcpy(&job->inference, &args->inference, + sizeof(args->inference)); + break; + default: + dev_dbg(ndev->dev, "Invalid job type %d\n", args->type); + ret =3D -EINVAL; + goto out_free_fence; + } + + job->bo =3D drm_gem_object_lookup(filp, args->bo_handle); + if (!job->bo) { + dev_dbg(ndev->dev, "Invalid BO handle\n"); + ret =3D -ENOENT; + goto out_free_fence; + } + + job->syncobj =3D drm_syncobj_find(filp, args->syncobj_handle); + if (!job->syncobj) { + dev_dbg(ndev->dev, "Invalid syncobj handle\n"); + ret =3D -ENOENT; + goto out_put_gem; + } + + ret =3D drm_sched_job_init(&job->base, &npriv->sched_entity, 1, NULL, + filp->client_id); + if (ret) + goto out_put_syncobj; + + ret =3D neutron_push_job(job); + if (ret) + goto out_sched_cleanup; + + neutron_put_job(job); + + return 0; + +out_sched_cleanup: + drm_sched_job_cleanup(&job->base); +out_put_syncobj: + drm_syncobj_put(job->syncobj); +out_put_gem: + drm_gem_object_put(job->bo); +out_free_fence: + kfree(job->neutron_fence); +out_free_job: + kfree(job); + + return ret; +} diff --git a/drivers/accel/neutron/neutron_job.h b/drivers/accel/neutron/ne= utron_job.h new file mode 100644 index 000000000000..bb7773aeb218 --- /dev/null +++ b/drivers/accel/neutron/neutron_job.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2025-2026 NXP */ + +#ifndef __NEUTRON_JOB_H__ +#define __NEUTRON_JOB_H__ + +#include +#include +#include +#include +#include + +#include "neutron_driver.h" + +struct neutron_device; +struct neutron_file_priv; + +struct neutron_job { + struct drm_sched_job base; + struct neutron_device *ndev; + struct dma_fence *neutron_fence; + struct dma_fence *sched_fence; + struct drm_syncobj *syncobj; + struct drm_gem_object *bo; + enum drm_neutron_job_type type; + union { + struct drm_neutron_inference_job inference; + }; + struct kref refcnt; +}; + +#define to_neutron_job(job) \ + container_of(job, struct neutron_job, base) + +int neutron_job_init(struct neutron_device *dev); +void neutron_job_fini(struct neutron_device *dev); +int neutron_job_open(struct neutron_file_priv *npriv); +void neutron_job_close(struct neutron_file_priv *npriv); + +void neutron_job_done_handler(struct neutron_device *dev); +void neutron_job_err_handler(struct neutron_device *dev); + +int neutron_ioctl_submit_job(struct drm_device *dev, void *data, struct dr= m_file *filp); + +#endif /* __NEUTRON_JOB_H__ */ diff --git a/include/uapi/drm/neutron_accel.h b/include/uapi/drm/neutron_ac= cel.h index 2f5639f2e0e8..a9e5682709d2 100644 --- a/include/uapi/drm/neutron_accel.h +++ b/include/uapi/drm/neutron_accel.h @@ -15,10 +15,12 @@ extern "C" { * * @DRM_NEUTRON_CREATE_BO: Create a buffer object * @DRM_NEUTRON_SYNC_BO: Sync (parts of) the buffer object memory + * @DRM_NEUTRON_SUBMIT_JOB: Submit a job to the device */ enum drm_neutron_ioctl { DRM_NEUTRON_CREATE_BO =3D 0, DRM_NEUTRON_SYNC_BO, + DRM_NEUTRON_SUBMIT_JOB, }; =20 /** @@ -64,6 +66,51 @@ struct drm_neutron_sync_bo { __u64 offset; }; =20 +/** + * enum drm_neutron_job_type - Type of job to submit to Neutron device + * + * @DRM_NEUTRON_JOB_INFERENCE: Inference job + */ +enum drm_neutron_job_type { + DRM_NEUTRON_JOB_INFERENCE =3D 0, +}; + +/** + * struct drm_neutron_inference_job - Inference job descriptor + * + * @tensor_offset: Offset of tensor array inside job BO + * @microcode_offset: Microcode offset inside BO + * @tensor_count: Number of valid tensors + * @pad: MBZ + */ +struct drm_neutron_inference_job { + __u32 tensor_offset; + __u32 microcode_offset; + __u32 tensor_count; + __u32 pad[5]; +}; + +/** + * struct drm_neutron_submit_job - Submit a job to Neutron device + * + * @type: Job type, one of enum drm_neutron_job_type + * @bo_handle: BO handle for this job + * @inference: Inference job descriptor (when type is DRM_NEUTRON_JOB_INFE= RENCE) + * @reserved: Reserved for future job types + * @syncobj_handle: Handle of syncobj on which user waits for job completi= on + * @pad: MBZ + */ +struct drm_neutron_submit_job { + __u32 type; + __u32 bo_handle; + union { + struct drm_neutron_inference_job inference; + __u32 reserved[8]; + }; + __u32 syncobj_handle; + __u32 pad; +}; + #define DRM_IOCTL_NEUTRON_CREATE_BO \ DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_CREATE_BO, \ struct drm_neutron_create_bo) @@ -72,6 +119,10 @@ struct drm_neutron_sync_bo { DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_SYNC_BO, \ struct drm_neutron_sync_bo) =20 +#define DRM_IOCTL_NEUTRON_SUBMIT_JOB \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_NEUTRON_SUBMIT_JOB, \ + struct drm_neutron_submit_job) + #if defined(__cplusplus) } #endif --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva020.nxp.com (inva020.nxp.com [92.121.34.13]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A63936D4FC; Thu, 26 Feb 2026 13:41:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.13 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113270; cv=none; b=uPcJDmVhnvMG8MDre01/hz+Peiw2h69/VwpBsksLWNBKFiLQwVmHzfsCN8yxGxklqGSLi+l62X10srttdYnXwyF9tQG+oWX4IYyx1UxNj524omCnH8qu4IH59/l/RbWZTd/Xvmzhm1NZfl4VvNfMfdgp3dn7DAaoFfGJlSz/1Ok= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113270; c=relaxed/simple; bh=HFuzMjntYvhOCHE0AaufcFtwUGeRoiICS772u9gKCik=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Rr8JIJiUKBEuhCSUEsuckK7Z1dp0DX2CxQoQBk8bl4XF+f35mn/JS00vPOOjqOIAYyuSYpaWb4wSolVNXtdfHdIzSRFpquXErY+47bDVSFGpWSznMYcgln2aVLPb2cVcliUIflAB43IixCv+NFkpfM3UtK/JKUlnKHrKvlxshzE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.13 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id CF3BA1A254A; Thu, 26 Feb 2026 14:41:06 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id B7E371A253C; Thu, 26 Feb 2026 14:41:06 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 0E9D92035B; Thu, 26 Feb 2026 14:41:05 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:47 +0200 Subject: [PATCH 8/9] accel/neutron: Add logging support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-8-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=7700; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=HFuzMjntYvhOCHE0AaufcFtwUGeRoiICS772u9gKCik=; b=JD6RNGZkvSwooK6itGN7AQtmsv1GIa1vMOZgr3/jI62PEolio3hc8V0Q3ylaLw9iGMO19geVk kkJRmvebtDwCd1nFbpiR2OGbaJpOMgkziFwnjKlIE468r+z2Lb7D0zF X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Expose the Neutron firmware log via debugfs interface. The log resides in internal device memory. Signed-off-by: Ioana Ciocoi-Radulescu --- drivers/accel/neutron/Makefile | 2 + drivers/accel/neutron/neutron_debugfs.c | 34 ++++++++++++++++ drivers/accel/neutron/neutron_debugfs.h | 15 +++++++ drivers/accel/neutron/neutron_device.c | 69 +++++++++++++++++++++++++++++= ++++ drivers/accel/neutron/neutron_device.h | 17 ++++++++ drivers/accel/neutron/neutron_driver.c | 3 ++ 6 files changed, 140 insertions(+) diff --git a/drivers/accel/neutron/Makefile b/drivers/accel/neutron/Makefile index ac6dd576521c..6d5c204460af 100644 --- a/drivers/accel/neutron/Makefile +++ b/drivers/accel/neutron/Makefile @@ -8,3 +8,5 @@ neutron-y :=3D \ neutron_gem.o \ neutron_job.o \ neutron_mailbox.o + +neutron-$(CONFIG_DEBUG_FS) +=3D neutron_debugfs.o diff --git a/drivers/accel/neutron/neutron_debugfs.c b/drivers/accel/neutro= n/neutron_debugfs.c new file mode 100644 index 000000000000..a392286e40b7 --- /dev/null +++ b/drivers/accel/neutron/neutron_debugfs.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright 2025 NXP */ + +#include + +#include "neutron_device.h" +#include "neutron_debugfs.h" + +static ssize_t fw_log_read(struct file *f, char __user *buf, size_t count,= loff_t *pos) +{ + struct neutron_device *ndev =3D file_inode(f)->i_private; + + if (!ndev->log.size) + return 0; + + if (ndev->flags & NEUTRON_BOOTED) + neutron_read_log(ndev, count); + + return simple_read_from_buffer(buf, count, pos, ndev->log.buf, + ndev->log.buf_count); +} + +static const struct file_operations fw_log_fops =3D { + .owner =3D THIS_MODULE, + .read =3D fw_log_read, +}; + +void neutron_debugfs_init(struct neutron_device *ndev) +{ + struct dentry *debugfs_root; + + debugfs_root =3D ndev->base.debugfs_root; + debugfs_create_file("fw_log", 0444, debugfs_root, ndev, &fw_log_fops); +} diff --git a/drivers/accel/neutron/neutron_debugfs.h b/drivers/accel/neutro= n/neutron_debugfs.h new file mode 100644 index 000000000000..7cd4b5af55a6 --- /dev/null +++ b/drivers/accel/neutron/neutron_debugfs.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright 2025 NXP */ + +#ifndef __NEUTRON_DEBUGFS_H__ +#define __NEUTRON_DEBUGFS_H__ + +struct neutron_device; + +#if defined(CONFIG_DEBUG_FS) +void neutron_debugfs_init(struct neutron_device *ndev); +#else +static inline void neutron_debugfs_init(struct neutron_device *ndev) {} +#endif + +#endif /* __NEUTRON_DEBUGFS_H__ */ diff --git a/drivers/accel/neutron/neutron_device.c b/drivers/accel/neutron= /neutron_device.c index 571ec906ad72..a5cedc91ad25 100644 --- a/drivers/accel/neutron/neutron_device.c +++ b/drivers/accel/neutron/neutron_device.c @@ -77,6 +77,70 @@ static void neutron_stop(struct neutron_device *ndev) 100, 100 * USEC_PER_MSEC); } =20 +static void neutron_init_logging(struct neutron_device *ndev) +{ + size_t old_size =3D ndev->log.size; + u32 ringctrl; + + ringctrl =3D readl_relaxed(NEUTRON_REG(ndev, RINGCTRL)); + + ndev->log.base =3D ndev->mem_regions[NEUTRON_MEM_DTCM].va + + NEUTRON_DTCM_BANK1_OFFSET + + FIELD_GET(RINGCTRL_ADDR_MASK, ringctrl); + ndev->log.size =3D FIELD_GET(RINGCTRL_SIZE_MASK, ringctrl) * + RINGCTRL_SIZE_MULT; + + if (ndev->log.size =3D=3D 0) { + dev_info(ndev->dev, "Firmware logging is disabled\n"); + return; + } + + /* If size didn't change, keep using the old buffer */ + if (old_size =3D=3D ndev->log.size) + return; + + devm_kfree(ndev->dev, ndev->log.buf); + ndev->log.buf =3D devm_kmalloc(ndev->dev, ndev->log.size, GFP_KERNEL); + if (!ndev->log.buf) { + ndev->log.size =3D 0; + dev_warn(ndev->dev, "Failed to allocate log buffer, logging is disabled\= n"); + } +} + +/* Read up to count bytes from device log into local buffer */ +void neutron_read_log(struct neutron_device *ndev, size_t count) +{ + size_t bytes, remaining; + u32 head, tail; + + ndev->log.buf_count =3D 0; + + if (!(ndev->flags & NEUTRON_BOOTED) || !ndev->log.size) + return; + + tail =3D readl_relaxed(NEUTRON_REG(ndev, TAIL)); + head =3D readl_relaxed(NEUTRON_REG(ndev, HEAD)); + + if (tail =3D=3D head) + return; + + /* Read from head to end of buffer or tail */ + bytes =3D (head < tail) ? (tail - head) : (ndev->log.size - head); + bytes =3D min(count, bytes); + memcpy_fromio(ndev->log.buf, ndev->log.base + head, bytes); + ndev->log.buf_count =3D bytes; + + /* Read from start of buffer, if it wraps around */ + if (head > tail && bytes < count) { + remaining =3D min(count - bytes, tail); + memcpy_fromio(ndev->log.buf + bytes, ndev->log.base, remaining); + ndev->log.buf_count +=3D remaining; + } + + head =3D (head + ndev->log.buf_count) % ndev->log.size; + writel_relaxed(head, NEUTRON_REG(ndev, HEAD)); +} + static void __iomem *neutron_tcm_da_to_va(struct neutron_device *ndev, u64= da) { struct neutron_mem_region *mem; @@ -158,6 +222,8 @@ int neutron_boot(struct neutron_device *ndev) /* Prepare device to receive jobs */ neutron_mbox_reset_state(ndev); =20 + neutron_init_logging(ndev); + ndev->flags |=3D NEUTRON_BOOTED; =20 return 0; @@ -165,6 +231,9 @@ int neutron_boot(struct neutron_device *ndev) =20 void neutron_shutdown(struct neutron_device *ndev) { + /* Device log becomes unavailable after shutdown, save it */ + neutron_read_log(ndev, ndev->log.size); + neutron_stop(ndev); ndev->flags &=3D ~NEUTRON_BOOTED; } diff --git a/drivers/accel/neutron/neutron_device.h b/drivers/accel/neutron= /neutron_device.h index 0ed72965774d..bf06878ac370 100644 --- a/drivers/accel/neutron/neutron_device.h +++ b/drivers/accel/neutron/neutron_device.h @@ -85,11 +85,26 @@ enum neutron_mem_id { NEUTRON_MEM_MAX }; =20 +/** + * struct neutron_log - Neutron log buffer descriptor + * @base: base address of the log buffer in device memory + * @size: Size of the log buffer + * @buf: Kernel buffer for log data + * @buf_count: Number of bytes available in the kernel buffer + */ +struct neutron_log { + void __iomem *base; + size_t size; + void *buf; + size_t buf_count; +}; + /** * struct neutron_device - Neutron device structure * @base: Base DRM device * @dev: Pointer to underlying device * @mem_regions: Array of memory region descriptors + * @log: Log buffer descriptor * @irq: IRQ number * @clks: Neutron clocks * @num_clks: Number of clocks @@ -107,6 +122,7 @@ struct neutron_device { struct device *dev; =20 struct neutron_mem_region mem_regions[NEUTRON_MEM_MAX]; + struct neutron_log log; =20 int irq; struct clk_bulk_data *clks; @@ -137,5 +153,6 @@ void neutron_shutdown(struct neutron_device *ndev); void neutron_enable_irq(struct neutron_device *ndev); void neutron_disable_irq(struct neutron_device *ndev); void neutron_handle_irq(struct neutron_device *ndev); +void neutron_read_log(struct neutron_device *ndev, size_t bytes); =20 #endif /* __NEUTRON_DEVICE_H__ */ diff --git a/drivers/accel/neutron/neutron_driver.c b/drivers/accel/neutron= /neutron_driver.c index ceae1f7e8359..14b4bc3a79d1 100644 --- a/drivers/accel/neutron/neutron_driver.c +++ b/drivers/accel/neutron/neutron_driver.c @@ -18,6 +18,7 @@ =20 #include "neutron_device.h" #include "neutron_driver.h" +#include "neutron_debugfs.h" #include "neutron_gem.h" #include "neutron_job.h" =20 @@ -168,6 +169,8 @@ static int neutron_probe(struct platform_device *pdev) if (ret) goto free_reserved; =20 + neutron_debugfs_init(ndev); + ret =3D devm_pm_runtime_enable(dev); if (ret) goto free_job; --=20 2.34.1 From nobody Tue Apr 7 15:27:36 2026 Received: from inva021.nxp.com (inva021.nxp.com [92.121.34.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 35BB436E461; Thu, 26 Feb 2026 13:41:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=92.121.34.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113271; cv=none; b=As1QW9uK2Ju3XAHSlGSDv7Hebf0fYe9Nc/siYzZERcCmccHK0E49yReenbxnGVgeunsd9QdZB998iJw7CbJ++13CpuHDK0mviYffkrnlTj8TFku3Y7YrPOiUVGNBXpALwkVo7XqVMGg8bC5yOiPuqIt1t8cAX58/F+3sZFILLaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772113271; c=relaxed/simple; bh=4tUFBDNFe36c+mcDJ/TUmnKANKlcvCmSziTeWU5kAHY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RNz53Bn1vnp6sZsN6OS+P51wnqD4RGOWRY+slr/VCj6AgPBOfA2stdcOeAGD/T4KCRLHigto9pARmSWmCjXDe+6ENEl8rJ1w3Jvoaa9gP7pth6Va+Z02PHbddhWTy8jIvmB9+hNiqx+hxWu/eiZ2FOM+KYcXiVH5kJl3UNpN2TM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; arc=none smtp.client-ip=92.121.34.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id DCC8620151F; Thu, 26 Feb 2026 14:41:07 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id CD44920153B; Thu, 26 Feb 2026 14:41:07 +0100 (CET) Received: from lsv15149.swis.ro-buh01.nxp.com (lsv15149.swis.ro-buh01.nxp.com [10.162.246.145]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 22DA22035B; Thu, 26 Feb 2026 14:41:06 +0100 (CET) From: Ioana Ciocoi-Radulescu Date: Thu, 26 Feb 2026 15:40:48 +0200 Subject: [PATCH 9/9] arm64: dts: imx95: Add Neutron node Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260226-neutron-v1-9-46eccb3bb50a@nxp.com> References: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> In-Reply-To: <20260226-neutron-v1-0-46eccb3bb50a@nxp.com> To: Oded Gabbay , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sumit Semwal , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Frank Li , =?utf-8?q?Christian_K=C3=B6nig?= Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Jiwei Fu , Forrest Shi , Alexandru Taran , Ioana Ciocoi-Radulescu X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772113256; l=1689; i=ruxandra.radulescu@nxp.com; s=20260204; h=from:subject:message-id; bh=4tUFBDNFe36c+mcDJ/TUmnKANKlcvCmSziTeWU5kAHY=; b=i4gdK2fWXKlkKm+telHbxNq04X/A+NXwzbBH9AwpDRh87tKHmqQ+L+rNqeCY6UkJFgGxmeDUF +rSE8KSbNgBBucn0Qom0GzJhb2QD3OLVA/acP8Se/lmSVTQOpR/ywtv X-Developer-Key: i=ruxandra.radulescu@nxp.com; a=ed25519; pk=zoq4b4OYR0c4faAH97xoTxdr6vfR8OvPbS+Cx0XhIBY= X-Virus-Scanned: ClamAV using ClamSMTP Add the node for Neutron NPU. Also add a reserved memory region for allocating Neutron buffers, which have a 1MB alignment constraint. Signed-off-by: Jiwei Fu Signed-off-by: Ioana Ciocoi-Radulescu --- arch/arm64/boot/dts/freescale/imx95.dtsi | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts= /freescale/imx95.dtsi index 55e2da094c88..7a41bb50d650 100644 --- a/arch/arm64/boot/dts/freescale/imx95.dtsi +++ b/arch/arm64/boot/dts/freescale/imx95.dtsi @@ -336,6 +336,19 @@ sram1: sram@204c0000 { #size-cells =3D <1>; }; =20 + reserved-memory { + #address-cells =3D <2>; + #size-cells =3D <2>; + ranges; + + neutron_pool: neutron-pool { + compatible =3D "shared-dma-pool"; + size =3D <0x0 0x8000000>; + alignment =3D <0x0 0x100000>; + reusable; + }; + }; + firmware { scmi { compatible =3D "arm,scmi"; @@ -2181,5 +2194,20 @@ ddr-pmu@4e090dc0 { reg =3D <0x0 0x4e090dc0 0x0 0x200>; interrupts =3D ; }; + + neutron: neutron@4ab00000 { + compatible =3D "nxp,imx95-neutron"; + reg =3D <0x0 0x4ab00000 0x0 0x00000400>, + <0x0 0x4ab10000 0x0 0x00010000>, + <0x0 0x4ab08000 0x0 0x00008000>; + reg-names =3D "regs", "itcm", "dtcm"; + memory-region =3D <&neutron_pool>; + interrupts =3D ; + clocks =3D <&scmi_clk IMX95_CLK_NPU>, + <&scmi_clk IMX95_CLK_NPUAPB>; + clock-names =3D "npu", "npu_apb"; + power-domains =3D <&scmi_devpd IMX95_PD_NPU>; + iommus =3D <&smmu 0xd>; + }; }; }; --=20 2.34.1