From patchwork Wed Nov 18 08:06:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiaying Liang X-Patchwork-Id: 327717 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-21.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6A9D1C64E7A for ; Wed, 18 Nov 2020 08:07:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1F2E02417E for ; Wed, 18 Nov 2020 08:07:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=xilinx.onmicrosoft.com header.i=@xilinx.onmicrosoft.com header.b="e71sdaK+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727360AbgKRIHL (ORCPT ); Wed, 18 Nov 2020 03:07:11 -0500 Received: from mail-mw2nam12on2078.outbound.protection.outlook.com ([40.107.244.78]:1504 "EHLO NAM12-MW2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727201AbgKRIHF (ORCPT ); Wed, 18 Nov 2020 03:07:05 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=aZU+Cnq2UEwMNzAZgBRfD+Al/AjXGjMbGAYZfkmssK7diRdo60jo+i/ti/tDdoOZ7BrLXYWDl8GJh6m5S//l/Gm1Bk3aSN7ABwO/Femo69iWtDUtOcIpNTgJEcJwCM9S6F+RFIEdJSo0NzyXtSGennt/QPqkx6+v4MHlVRKs6avjohd0nbicUc21u8ICW+rWd8H7bmJtqZ7PBWwkqiKGIb/Jhn9UKO1dBPEe40g6dwlFZyrFzxCKN0OxKh+YKi4ZuntBus02eiJ/RuXW/XOh5QVz3Yugmc3Q9DREDS2g+/YQWvlesHyr3S6oCQxoUFdv82dFpHZpTMsTVzScQT5akQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Z5PsmImWtKXNi5tVoX4Deo9kZlaDaVG7MZJ3mMvIgLw=; b=ZgyUsBCCqAHD+NJ5ph3N5dDKSpXZlzjPfhn0b/zwIxdS8R8Dgba35+hMXtERKAJJZRUIO18qgJoy1yfUouetOksoXb7EsmoCIeME0Bg9EWwN5PJmAWm0U4bvgiQq1QF8xluIaWLLgsMNm5n8DSJR//84bNvydlZwmXgQDSh+rKKi4gPWRZ3wIfrBBOZQ4I/8lEgRwdnw/oNyuq/9tDYWI0bHxCpoXjEo4oYWOjmahUQui7OYpEA3Oc29L188d1IUbDEmktjWX1GEdLnjHw1PjLddAqC8VIT39IIyuZqWV4aoK5P0+zw5yxkR77CPutIvhgzLy7WfFhn5ryjIARrnmQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 149.199.62.198) smtp.rcpttodomain=kernel.org smtp.mailfrom=xilinx.com; dmarc=bestguesspass action=none header.from=xilinx.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xilinx.onmicrosoft.com; s=selector2-xilinx-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Z5PsmImWtKXNi5tVoX4Deo9kZlaDaVG7MZJ3mMvIgLw=; b=e71sdaK+8vnqluSXwOJ6npqBkD/wBeVZjiGnBSTMpEQbKBqM1EEJAiqrux+v4OXtFU5GiBK0LQl+3Tgx2H/H16B6Y+aNjW/Ac/2IFOyYT2yc5r5b9olNvW/gDEGHEwaXNJcKMb5OSjAdQScz/DrPGZuRZ8uh96HPygKVYAperuc= Received: from CY4PR22CA0068.namprd22.prod.outlook.com (2603:10b6:903:ae::30) by PH0PR02MB7574.namprd02.prod.outlook.com (2603:10b6:510:55::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3564.25; Wed, 18 Nov 2020 08:06:55 +0000 Received: from CY1NAM02FT052.eop-nam02.prod.protection.outlook.com (2603:10b6:903:ae:cafe::f0) by CY4PR22CA0068.outlook.office365.com (2603:10b6:903:ae::30) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3589.20 via Frontend Transport; Wed, 18 Nov 2020 08:06:55 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 149.199.62.198) smtp.mailfrom=xilinx.com; kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.62.198 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.62.198; helo=xsj-pvapexch02.xlnx.xilinx.com; Received: from xsj-pvapexch02.xlnx.xilinx.com (149.199.62.198) by CY1NAM02FT052.mail.protection.outlook.com (10.152.74.123) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.3564.22 via Frontend Transport; Wed, 18 Nov 2020 08:06:55 +0000 Received: from xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) by xsj-pvapexch02.xlnx.xilinx.com (172.19.86.41) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Wed, 18 Nov 2020 00:06:33 -0800 Received: from smtp.xilinx.com (172.19.127.95) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server id 15.1.1913.5 via Frontend Transport; Wed, 18 Nov 2020 00:06:33 -0800 Envelope-to: michal.simek@xilinx.com, derek.kiernan@xilinx.com, dragan.cvetic@xilinx.com, rajan.vaja@xilinx.com, tejas.patel@xilinx.com, manish.narani@xilinx.com, ravi.patel@xilinx.com, wendy.liang@xilinx.com, hyun.kwon@xilinx.com, robh+dt@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, sumit.semwal@linaro.org, christian.koenig@amd.com, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org Received: from [172.19.2.167] (port=35936 helo=xsjjliang50.xilinx.com) by smtp.xilinx.com with esmtp (Exim 4.90) (envelope-from ) id 1kfIU5-0000Nk-IR; Wed, 18 Nov 2020 00:06:33 -0800 From: Wendy Liang To: , , , , , , , , , , , CC: , , , , , , "Wendy Liang" , Hyun Kwon Subject: [PATCH 2/9] misc: Add Xilinx AI engine device driver Date: Wed, 18 Nov 2020 00:06:13 -0800 Message-ID: <1605686780-17886-3-git-send-email-wendy.liang@xilinx.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> References: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 09924d95-ae25-4ffd-364f-08d88b98ea31 X-MS-TrafficTypeDiagnostic: PH0PR02MB7574: X-Microsoft-Antispam-PRVS: X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-MS-Oob-TLC-OOBClassifiers: OLM:3826; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: WtDsaBUV75y1upPKLjlk2O2Tmvs5rYZ3T86sVgSDSOs1wlS2oPoPmTM4B52QhDSnBo7LClU2JRKPimr1RQCC4AK8Trm5Dy+RJFY4NR9LOWR9Dm2DjHVZli5bCZWgSlnlfRPt2WTsVy/Im2oZ9AbvbMITJL0ogiKxBMFv9ZLM2f9UGVcTii3XbwSBRjTE6BUYhB5b11nc+uA7jZUpB9Rbfclhf9bgcKJ3r2spl/iaTRIUUjWDQGXRF5fR8kyY9kl7u2PcSbuScfX9o3OjCNuTUDeao2iPrEJX9Jz9Hm+ftkU4SbkJq/e+CnIOow0YvpkNYNSQQhVhhRuPmOzovw5p40wqx4TruuC/lWKAvI0Wz9lTiT/3EeMFwo2OhQ06CfT+oLh79gMskgQM3TVZwrEHiJuryV75gC85kuoZdz7n5QzBHhx8HQ68HlroeKnhr9LwhCP4E/DxpFBfg6+AxW6GwR7j7AvVtFbrODFc8yO3UH+NVjaYdkLvo34TwbGq9jI/6n6f7rCXY5+q0xtKFtjKgA== X-Forefront-Antispam-Report: CIP:149.199.62.198; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:xsj-pvapexch02.xlnx.xilinx.com; PTR:unknown-62-198.xilinx.com; CAT:NONE; SFS:(4636009)(396003)(136003)(346002)(376002)(39850400004)(46966005)(107886003)(7416002)(8936002)(26005)(921005)(6666004)(2616005)(82740400003)(186003)(2906002)(5660300002)(8676002)(4326008)(30864003)(6636002)(7696005)(7636003)(47076004)(478600001)(336012)(54906003)(83380400001)(110136005)(966005)(82310400003)(70206006)(426003)(316002)(36756003)(44832011)(70586007)(356005)(9786002)(36906005)(102446001)(579004); DIR:OUT; SFP:1101; X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Nov 2020 08:06:55.2669 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 09924d95-ae25-4ffd-364f-08d88b98ea31 X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.62.198]; Helo=[xsj-pvapexch02.xlnx.xilinx.com] X-MS-Exchange-CrossTenant-AuthSource: CY1NAM02FT052.eop-nam02.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR02MB7574 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Create AI engine device/partition hierarchical structure. Each AI engine device can have multiple logical partitions(groups of AI engine tiles). Each partition is column based and has its own node ID in the system. AI engine device driver manages its partitions. Applications can access AI engine partition through the AI engine partition driver instance. AI engine registers write is moved to kernel as there are registers in the AI engine array needs privilege permission. Signed-off-by: Wendy Liang Signed-off-by: Hyun Kwon --- MAINTAINERS | 8 + drivers/misc/Kconfig | 12 + drivers/misc/Makefile | 1 + drivers/misc/xilinx-ai-engine/Makefile | 11 + drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 115 +++++ drivers/misc/xilinx-ai-engine/ai-engine-dev.c | 448 ++++++++++++++++++ drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 226 ++++++++++ drivers/misc/xilinx-ai-engine/ai-engine-part.c | 498 +++++++++++++++++++++ drivers/misc/xilinx-ai-engine/ai-engine-res.c | 114 +++++ include/uapi/linux/xlnx-ai-engine.h | 107 +++++ 10 files changed, 1540 insertions(+) create mode 100644 drivers/misc/xilinx-ai-engine/Makefile create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-aie.c create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dev.c create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-internal.h create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-part.c create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-res.c create mode 100644 include/uapi/linux/xlnx-ai-engine.h diff --git a/MAINTAINERS b/MAINTAINERS index 5cc595a..40e3351 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19283,6 +19283,14 @@ T: git https://github.com/Xilinx/linux-xlnx.git F: Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml F: drivers/phy/xilinx/phy-zynqmp.c +XILINX AI ENGINE DRIVER +M: Wendy Liang +S: Supported +F: Documentation/devicetree/bindings/soc/xilinx/xlnx,ai-engine.yaml +F: drivers/misc/xilinx-ai-engine/ +F: include/linux/xlnx-ai-engine.h +F: include/uapi/linux/xlnx-ai-engine.h + XILLYBUS DRIVER M: Eli Billauer L: linux-kernel@vger.kernel.org diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fafa8b0..0b8ce4d 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -444,6 +444,18 @@ config XILINX_SDFEC If unsure, say N. +config XILINX_AIE + tristate "Xilinx AI engine" + depends on ARM64 || COMPILE_TEST + help + This option enables support for the Xilinx AI engine driver. + One Xilinx AI engine device can have multiple partitions (groups of + AI engine tiles). Xilinx AI engine device driver instance manages + AI engine partitions. User application access its partitions through + AI engine partition instance file operations. + + If unsure, say N + config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d23231e..2176b18 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI) += habanalabs/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o +obj-$(CONFIG_XILINX_AIE) += xilinx-ai-engine/ diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile new file mode 100644 index 0000000..7827a0a --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for Xilinx AI engine device driver +# + +obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o + +xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \ + ai-engine-dev.o \ + ai-engine-part.o \ + ai-engine-res.o diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c new file mode 100644 index 0000000..319260f --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine driver AIE device specific implementation + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include + +#include "ai-engine-internal.h" + +#define AIE_ARRAY_SHIFT 30U +#define AIE_COL_SHIFT 23U +#define AIE_ROW_SHIFT 18U + +/* + * Registers offsets + */ +#define AIE_SHIMNOC_L2INTR_MASK_REGOFF 0x00015000U +#define AIE_SHIMNOC_L2INTR_INTR_REGOFF 0x00015010U +#define AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF 0x0001d000U +#define AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF 0x0001d13cU +#define AIE_SHIMNOC_AXIMM_REGOFF 0x0001e020U +#define AIE_SHIMPL_L1INTR_MASK_A_REGOFF 0x00035000U +#define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U +#define AIE_SHIMPL_CLKCNTR_REGOFF 0x00036040U +#define AIE_SHIMPL_RESET_REGOFF 0x0003604cU +#define AIE_TILE_CORE_CLKCNTR_REGOFF 0x00036040U + +static const struct aie_tile_regs aie_kernel_regs[] = { + /* SHIM AXI MM Config */ + {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMNOC_AXIMM_REGOFF, + .eoff = AIE_SHIMNOC_AXIMM_REGOFF, + }, + /* SHIM DMA ADDRESS range */ + {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMNOC_DMA_BD0_ADDRLOW_REGOFF, + .eoff = AIE_SHIMNOC_DMA_BD15_PACKET_REGOFF, + }, + /* SHIM 2nd level interrupt controller */ + {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMNOC_L2INTR_MASK_REGOFF, + .eoff = AIE_SHIMNOC_L2INTR_INTR_REGOFF, + }, + /* SHIM 1st level interrupt controller */ + {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) << + AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF, + .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF, + }, + /* SHIM reset Enable */ + {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) << + AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMPL_RESET_REGOFF, + .eoff = AIE_SHIMPL_RESET_REGOFF, + }, + /* SHIM clock control */ + {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) << + AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMPL_CLKCNTR_REGOFF, + .eoff = AIE_SHIMPL_CLKCNTR_REGOFF, + }, + /* Tile clock control */ + {.attribute = AIE_TILE_TYPE_TILE << AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_TILE_CORE_CLKCNTR_REGOFF, + .eoff = AIE_TILE_CORE_CLKCNTR_REGOFF, + }, +}; + +static u32 aie_get_tile_type(struct aie_location *loc) +{ + if (loc->row) + return AIE_TILE_TYPE_TILE; + /* SHIM row */ + if ((loc->col % 4) < 2) + return AIE_TILE_TYPE_SHIMPL; + + return AIE_TILE_TYPE_SHIMNOC; +} + +static const struct aie_tile_operations aie_ops = { + .get_tile_type = aie_get_tile_type, +}; + +/** + * aie_device_init() - Initialize AI engine device struct AIE specific + * properties + * @adev: AI engine device + * @return: 0 for success, negative value for failure. + * + * This function initialize the AI engine device structure device version + * specific elements such as register addressing related array shift, + * column shift, and row shift; AIE specific device operations, device + * columns resource. + */ +int aie_device_init(struct aie_device *adev) +{ + int ret; + + adev->array_shift = AIE_ARRAY_SHIFT; + adev->col_shift = AIE_COL_SHIFT; + adev->row_shift = AIE_ROW_SHIFT; + adev->ops = &aie_ops; + adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs); + adev->kernel_regs = aie_kernel_regs; + + /* Get the columns resource */ + /* Get number of columns from AI engine memory resource */ + ret = aie_resource_initialize(&adev->cols_res, 50); + if (ret) + dev_err(&adev->dev, "failed to initialize columns resource.\n"); + + return ret; +} diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c new file mode 100644 index 0000000..2ab2dc8 --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine device driver + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ai-engine-internal.h" + +#define AIE_DEV_MAX (MINORMASK + 1) + +static dev_t aie_major; +struct class *aie_class; + +static DEFINE_IDA(aie_device_ida); +static DEFINE_IDA(aie_minor_ida); + +/** + * aie_get_partition_fd() - Get AI engine partition file descriptor + * @apart: AI engine partition + * @return: file descriptor for AI engine partition for success, or negative + * value for failure. + * + * This function gets a file descriptor for the AI engine partition. + */ +static int aie_get_partition_fd(struct aie_partition *apart) +{ + struct file *filep; + int ret; + + /* + * We can't use anon_inode_getfd() because we need to modify + * the f_mode flags directly to allow more than just ioctls + */ + ret = get_unused_fd_flags(O_CLOEXEC); + if (ret < 0) + return ret; + + filep = anon_inode_getfile(dev_name(&apart->dev), &aie_part_fops, + apart, O_RDWR); + if (IS_ERR(filep)) { + put_unused_fd(ret); + ret = PTR_ERR(filep); + return ret; + } + filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + fd_install(ret, filep); + + return ret; +} + +/** + * aie_enquire_partitions() - get AI engine partitions information + * @adev: AI engine device + * @query: data struct to store the partition information + * @return: 0 for success, and negative value for failure. + */ +static int aie_enquire_partitions(struct aie_device *adev, + struct aie_partition_query *query) +{ + struct aie_partition *apart; + u32 partition_cnt, i = 0; + int ret; + + if (!query->partitions) { + /* + * If partitions information buffer is NULL. + * It is to get the number of partitions. + */ + query->partition_cnt = 0; + list_for_each_entry(apart, &adev->partitions, node) + query->partition_cnt++; + return 0; + } + + partition_cnt = query->partition_cnt; + if (!partition_cnt) + return 0; + + ret = mutex_lock_interruptible(&adev->mlock); + if (ret) + return ret; + + list_for_each_entry(apart, &adev->partitions, node) { + struct aie_range_args part; + + if (i >= partition_cnt) + break; + part.partition_id = apart->partition_id; + /* + * TBD: check with PLM that if the partition is programmed + * and get the UID of the image which is loaded on the AI + * engine partition. + */ + part.uid = 0; + part.range.start.col = apart->range.start.col; + part.range.start.row = apart->range.start.row; + part.range.size.col = apart->range.size.col; + part.range.size.row = apart->range.size.row; + /* Check if partition is in use */ + part.status = apart->status; + if (copy_to_user((void __user *)&query->partitions[i], &part, + sizeof(part))) { + mutex_unlock(&adev->mlock); + return -EFAULT; + } + i++; + } + mutex_unlock(&adev->mlock); + query->partition_cnt = i; + + return 0; +} + +/** + * aie_get_partition_from_id() - get AI engine partition from id + * @adev: AI engine device + * @partition_id: partition id to check + * @return: partition pointer if partition exists, otherwise, NULL. + * + * This function checks defined partitions with partition id. + * This function expect the caller to lock mlock of @adev. + */ +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev, + u32 partition_id) +{ + struct aie_partition *apart; + + list_for_each_entry(apart, &adev->partitions, node) { + if (apart->partition_id == partition_id) + return apart; + } + + return NULL; +} + +/** + * aie_request_partition() - request AI engine partition + * @adev: AI engine device + * @req: partition request, includes the requested AI engine information + * such as partition node ID and the UID of the image which is + * loaded on the partition. + * @return: partition pointer if partition exists, otherwise, NULL. + * + * This function finds a defined partition which matches the specified + * partition id, request it if it hasn't been requested, and returns it. + */ +struct aie_partition *aie_request_partition(struct aie_device *adev, + struct aie_partition_req *req) +{ + struct aie_partition *apart; + int ret; + + ret = mutex_lock_interruptible(&adev->mlock); + if (ret) + return ERR_PTR(ret); + + apart = aie_get_partition_from_id(adev, req->partition_id); + if (!apart) { + dev_err(&adev->dev, + "request partition %u failed, not exist.\n", + req->partition_id); + mutex_unlock(&adev->mlock); + return ERR_PTR(-EINVAL); + } + /* + * TBD: It will check image UID too to see if the user matches + * what's loaded in the AI engine partition. And check the meta + * data to see which resources used by application. + */ + + ret = mutex_lock_interruptible(&apart->mlock); + if (ret) + return ERR_PTR(ret); + + if (apart->status & XAIE_PART_STATUS_INUSE) { + mutex_unlock(&apart->mlock); + dev_err(&adev->dev, + "request partition %u failed, partition in use.\n", + req->partition_id); + apart = ERR_PTR(-EBUSY); + } else { + /* + * TBD: + * 1. setup NOC AXI MM config to only generate error events + * for slave error and decode error. + * 2. scan to see which tiles have been clock gated. + * + * This needs to be done before the AI engine partition is + * exported for user to access. + */ + apart->status = XAIE_PART_STATUS_INUSE; + mutex_unlock(&apart->mlock); + } + mutex_unlock(&adev->mlock); + + return apart; +} + +static long xilinx_ai_engine_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct aie_device *adev = cdev_to_aiedev(inode->i_cdev); + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case AIE_ENQUIRE_PART_IOCTL: + { + struct aie_partition_query query; + struct aie_partition_query __user *uquery_ptr = argp; + + if (copy_from_user(&query, uquery_ptr, sizeof(query))) + return -EFAULT; + ret = aie_enquire_partitions(adev, &query); + if (ret < 0) + return ret; + if (copy_to_user((void __user *)&uquery_ptr->partition_cnt, + &query.partition_cnt, + sizeof(query.partition_cnt))) + return -EFAULT; + break; + } + case AIE_REQUEST_PART_IOCTL: + { + struct aie_partition_req req; + struct aie_partition *apart; + + if (copy_from_user(&req, argp, sizeof(req))) + return -EFAULT; + apart = aie_request_partition(adev, &req); + if (IS_ERR(apart)) + return PTR_ERR(apart); + ret = aie_get_partition_fd(apart); + if (ret < 0) { + dev_err(&apart->dev, "failed to get fd.\n"); + break; + } + break; + } + default: + dev_err(&adev->dev, "Invalid ioctl command %u.\n", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct file_operations aie_device_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = xilinx_ai_engine_ioctl, +}; + +static void xilinx_ai_engine_release_device(struct device *dev) +{ + struct aie_device *adev = dev_to_aiedev(dev); + + ida_simple_remove(&aie_device_ida, dev->id); + ida_simple_remove(&aie_minor_ida, MINOR(dev->devt)); + cdev_del(&adev->cdev); + aie_resource_uninitialize(&adev->cols_res); +} + +/** + * of_xilinx_ai_engine_part_probe() - probes for AI engine partition nodes + * @adev: AI engine device + * + * This function will probe for children AI engine partition nodes and create + * an AI engine partition instance for each node. + */ +static void of_xilinx_ai_engine_part_probe(struct aie_device *adev) +{ + struct device_node *nc; + + for_each_available_child_of_node(adev->dev.of_node, nc) { + struct aie_partition *apart; + + if (of_node_test_and_set_flag(nc, OF_POPULATED)) + continue; + apart = of_aie_part_probe(adev, nc); + if (IS_ERR(apart)) { + dev_err(&adev->dev, + "Failed to probe AI engine part for %pOF\n", + nc); + of_node_clear_flag(nc, OF_POPULATED); + } + } +} + +static int xilinx_ai_engine_probe(struct platform_device *pdev) +{ + struct aie_device *adev; + struct device *dev; + int ret; + + adev = devm_kzalloc(&pdev->dev, sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + platform_set_drvdata(pdev, adev); + INIT_LIST_HEAD(&adev->partitions); + mutex_init(&adev->mlock); + + adev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!adev->res) { + dev_err(&pdev->dev, "No memory resource.\n"); + return -EINVAL; + } + adev->base = devm_ioremap_resource(&pdev->dev, adev->res); + if (IS_ERR(adev->base)) { + dev_err(&pdev->dev, "no io memory resource.\n"); + return PTR_ERR(adev->base); + } + + /* Initialize AIE device specific instance. */ + ret = aie_device_init(adev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize device instance.\n"); + return ret; + } + + dev = &adev->dev; + device_initialize(dev); + dev->class = aie_class; + dev->parent = &pdev->dev; + dev->of_node = pdev->dev.of_node; + + ret = ida_simple_get(&aie_minor_ida, 0, AIE_DEV_MAX, GFP_KERNEL); + if (ret < 0) + goto free_dev; + dev->devt = MKDEV(MAJOR(aie_major), ret); + ret = ida_simple_get(&aie_device_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto free_minor_ida; + dev->id = ret; + dev_set_name(&adev->dev, "aie%d", dev->id); + + cdev_init(&adev->cdev, &aie_device_fops); + adev->cdev.owner = THIS_MODULE; + ret = cdev_add(&adev->cdev, dev->devt, 1); + if (ret) + goto free_ida; + /* We can now rely on the release function for cleanup */ + dev->release = xilinx_ai_engine_release_device; + + ret = device_add(dev); + if (ret) { + dev_err(&pdev->dev, "device_add failed: %d\n", ret); + put_device(dev); + return ret; + } + + of_xilinx_ai_engine_part_probe(adev); + dev_info(&pdev->dev, "Xilinx AI Engine device(cols=%u) probed\n", + adev->cols_res.total); + return 0; + +free_ida: + ida_simple_remove(&aie_device_ida, dev->id); +free_minor_ida: + ida_simple_remove(&aie_minor_ida, MINOR(dev->devt)); +free_dev: + put_device(dev); + + return ret; +} + +static int xilinx_ai_engine_remove(struct platform_device *pdev) +{ + struct aie_device *adev = platform_get_drvdata(pdev); + struct aie_partition *apart; + + list_for_each_entry(apart, &adev->partitions, node) + aie_part_remove(apart); + + device_del(&adev->dev); + put_device(&adev->dev); + + return 0; +} + +static const struct of_device_id xilinx_ai_engine_of_match[] = { + { .compatible = "xlnx,ai-engine-v1.0", }, + { /* end of table */ }, +}; +MODULE_DEVICE_TABLE(of, xilinx_ai_engine_of_match); + +static struct platform_driver xilinx_ai_engine_driver = { + .probe = xilinx_ai_engine_probe, + .remove = xilinx_ai_engine_remove, + .driver = { + .name = "xilinx-ai-engine", + .of_match_table = xilinx_ai_engine_of_match, + }, +}; + +static int __init xilinx_ai_engine_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&aie_major, 0, AIE_DEV_MAX, "aie"); + if (ret < 0) { + pr_err("aie: failed to allocate aie region\n"); + return ret; + } + + aie_class = class_create(THIS_MODULE, "aie"); + if (IS_ERR(aie_class)) { + pr_err("failed to create aie class\n"); + unregister_chrdev_region(aie_major, AIE_DEV_MAX); + return PTR_ERR(aie_class); + } + + platform_driver_register(&xilinx_ai_engine_driver); + + return 0; +} +postcore_initcall(xilinx_ai_engine_init); + +static void __exit xilinx_ai_engine_exit(void) +{ + platform_driver_unregister(&xilinx_ai_engine_driver); + class_destroy(aie_class); + unregister_chrdev_region(aie_major, AIE_DEV_MAX); +} +module_exit(xilinx_ai_engine_exit); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h new file mode 100644 index 0000000..6a69946 --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Xilinx AI Engine driver internal header + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#ifndef AIE_INTERNAL_H +#define AIE_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Macros for AI engine tile type bitmasks + */ +#define AIE_TILE_TYPE_TILE BIT(0) +#define AIE_TILE_TYPE_SHIMPL BIT(1) +/* SHIM NOC tile includes SHIM PL and SHIM NOC modules */ +#define AIE_TILE_TYPE_SHIMNOC BIT(2) + +/* + * Macros for attribute property of AI engine registers accessed by kernel + * 0 - 7 bits: tile type bits + * 8 - 15 bits: permission bits. If it is 1, it allows write from userspace + */ +#define AIE_REGS_ATTR_TILE_TYPE_SHIFT 0U +#define AIE_REGS_ATTR_PERM_SHIFT 8U +#define AIE_REGS_ATTR_TILE_TYPE_MASK GENMASK(AIE_REGS_ATTR_PERM_SHIFT - 1, \ + AIE_REGS_ATTR_TILE_TYPE_SHIFT) +#define AIE_REGS_ATTR_PERM_MASK GENMASK(15, \ + AIE_REGS_ATTR_PERM_SHIFT) + +/** + * struct aie_tile_regs - contiguous range of AI engine register + * within an AI engine tile + * @soff: start offset of the range + * @eoff: end offset of the range + * @attribute: registers attribute. It uses AIE_REGS_ATTR_* macros defined + * above. + */ +struct aie_tile_regs { + size_t soff; + size_t eoff; + u32 attribute; +}; + +struct aie_device; +struct aie_partition; + +/** + * struct aie_tile_operations - AI engine device operations + * @get_tile_type: get type of tile based on tile operation + * + * Different AI engine device version has its own device + * operation. + */ +struct aie_tile_operations { + u32 (*get_tile_type)(struct aie_location *loc); +}; + +/** + * struct aie_resource - AI engine resource structure + * @bitmap: resource bitmap + * @total: total number of resource + */ +struct aie_resource { + unsigned long *bitmap; + u32 total; +}; + +/** + * struct aie_device - AI engine device structure + * @partitions: list of partitions requested + * @cdev: cdev for the AI engine + * @dev: device for the AI engine device + * @mlock: protection for AI engine device operations + * @base: AI engine device base virtual address + * @res: memory resource of AI engine device + * @kernel_regs: array of kernel only registers + * @ops: tile operations + * @size: size of the AI engine address space + * @array_shift: array address shift + * @col_shift: column address shift + * @row_shift: row address shift + * @cols_res: AI engine columns resources to indicate + * while columns are occupied by partitions. + * @num_kernel_regs: number of kernel only registers range + * @version: AI engine device version + */ +struct aie_device { + struct list_head partitions; + struct cdev cdev; + struct device dev; + struct mutex mlock; /* protection for AI engine partitions */ + void __iomem *base; + struct resource *res; + const struct aie_tile_regs *kernel_regs; + const struct aie_tile_operations *ops; + size_t size; + struct aie_resource cols_res; + u32 array_shift; + u32 col_shift; + u32 row_shift; + u32 num_kernel_regs; + int version; +}; + +/** + * struct aie_partition - AI engine partition structure + * @node: list node + * @adev: pointer to AI device instance + * @range: range of partition + * @mlock: protection for AI engine partition operations + * @dev: device for the AI engine partition + * @partition_id: partition id. Partition ID is the identifier + * of the AI engine partition in the system. + * @status: indicate if the partition is in use + */ +struct aie_partition { + struct list_head node; + struct aie_device *adev; + struct aie_range range; + struct mutex mlock; /* protection for AI engine partition operations */ + struct device dev; + u32 partition_id; + u32 status; +}; + +extern struct class *aie_class; +extern const struct file_operations aie_part_fops; + +#define cdev_to_aiedev(i_cdev) container_of((i_cdev), struct aie_device, cdev) +#define dev_to_aiedev(_dev) container_of((_dev), struct aie_device, dev) +#define dev_to_aiepart(_dev) container_of((_dev), struct aie_partition, dev) + +#define aie_col_mask(adev) ({ \ + struct aie_device *_adev = (adev); \ + GENMASK_ULL(_adev->array_shift - 1, _adev->col_shift); \ + }) + +#define aie_row_mask(adev) ({ \ + struct aie_device *_adev = (adev); \ + GENMASK_ULL(_adev->col_shift - 1, _adev->row_shift); \ + }) + +#define aie_tile_reg_mask(adev) ({ \ + struct aie_device *_adev = (adev); \ + GENMASK_ULL(_adev->row_shift - 1, 0); \ + }) + +/* + * Need to define field get, as AI engine shift mask is not constant. + * Cannot use FIELD_GET() + */ +#define aie_tile_reg_field_get(mask, shift, regoff) ( \ + ((regoff) & (mask)) >> (shift)) + +#define aie_cal_tile_reg(adev, regoff) ( \ + aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff)) + +/** + * aie_cal_regoff() - calculate register offset to the whole AI engine + * device start address + * @adev: AI engine device + * @loc: AI engine tile location + * @regoff_intile: register offset within a tile + * @return: register offset to the whole AI engine device start address + */ +static inline u32 aie_cal_regoff(struct aie_device *adev, + struct aie_location loc, u32 regoff_intile) +{ + return regoff_intile + (loc.col << adev->col_shift) + + (loc.row << adev->row_shift); +} + +/** + * aie_validate_location() - validate tile location within an AI engine + * partition + * @apart: AI engine partition + * @loc: AI engine tile location + * @return: return 0 if it is valid, negative value for errors. + * + * This function checks if the AI engine location is within the AI engine + * partition. + */ +static inline int aie_validate_location(struct aie_partition *apart, + struct aie_location loc) +{ + if (loc.col < apart->range.start.col || + loc.col >= apart->range.start.col + apart->range.size.col || + loc.row < apart->range.start.row || + loc.row >= apart->range.start.row + apart->range.size.row) + return -EINVAL; + + return 0; +} + +int aie_resource_initialize(struct aie_resource *res, int count); +void aie_resource_uninitialize(struct aie_resource *res); +int aie_resource_check_region(struct aie_resource *res, u32 start, + u32 count); +int aie_resource_get_region(struct aie_resource *res, u32 start, + u32 count); +void aie_resource_put_region(struct aie_resource *res, int start, u32 count); + +const struct file_operations *aie_part_get_fops(void); +u8 aie_part_in_use(struct aie_partition *apart); +struct aie_partition *aie_get_partition_from_id(struct aie_device *adev, + u32 partition_id); +struct aie_partition *aie_request_partition(struct aie_device *adev, + struct aie_partition_req *req); +struct aie_partition *of_aie_part_probe(struct aie_device *adev, + struct device_node *nc); +void aie_part_remove(struct aie_partition *apart); + +int aie_device_init(struct aie_device *adev); +#endif /* AIE_INTERNAL_H */ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c new file mode 100644 index 0000000..fc8f9f5 --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine partition driver + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ai-engine-internal.h" + +/** + * aie_cal_loc() - calculate tile location from register offset to the AI + * engine device + * @adev: AI engine device + * @loc: memory pointer to restore returning location information + * @regoff: tile internal register offset + * + * This function returns the tile location. + */ +static void aie_cal_loc(struct aie_device *adev, + struct aie_location *loc, u64 regoff) +{ + loc->col = (u32)aie_tile_reg_field_get(aie_col_mask(adev), + adev->col_shift, regoff); + loc->row = (u32)aie_tile_reg_field_get(aie_row_mask(adev), + adev->row_shift, regoff); +} + +/** + * aie_part_reg_validation() - validate AI engine partition register access + * @apart: AI engine partition + * @offset: AI engine register offset + * @len: len of data to write/read + * @is_write: is the access to write to register + * @return: 0 for success, or negative value for failure. + * + * This function validate if the register to access is within the AI engine + * partition. If it is write access, if the register is writable by user. + */ +static int aie_part_reg_validation(struct aie_partition *apart, size_t offset, + size_t len, u8 is_write) +{ + struct aie_device *adev; + u32 regend32, ttype; + u64 regoff, regend64; + struct aie_location loc; + unsigned int i; + + adev = apart->adev; + if (offset % sizeof(u32)) { + dev_err(&apart->dev, + "Invalid reg off(0x%zx), not 32bit aligned.\n", + offset); + return -EINVAL; + } + + if (len % sizeof(u32)) { + dev_err(&apart->dev, "Invalid reg operation len %zu.\n", len); + return -EINVAL; + } + + regoff = aie_cal_tile_reg(adev, offset); + regend64 = regoff + len; + if (regend64 >= BIT_ULL(adev->row_shift)) { + dev_err(&apart->dev, + "Invalid reg operation len %zu.\n", len); + return -EINVAL; + } + + aie_cal_loc(adev, &loc, offset); + if (aie_validate_location(apart, loc)) { + dev_err(&apart->dev, + "Invalid (%d,%d) out of part(%d,%d),(%d,%d)\n", + loc.col, loc.row, + apart->range.start.col, apart->range.start.row, + apart->range.size.col, apart->range.size.row); + return -EINVAL; + } + + if (!is_write) + return 0; + + regend32 = lower_32_bits(regend64); + ttype = adev->ops->get_tile_type(&loc); + for (i = 0; i < adev->num_kernel_regs; i++) { + const struct aie_tile_regs *regs; + u32 rttype, writable; + + regs = &adev->kernel_regs[i]; + rttype = (regs->attribute & AIE_REGS_ATTR_TILE_TYPE_MASK) >> + AIE_REGS_ATTR_TILE_TYPE_SHIFT; + writable = (regs->attribute & AIE_REGS_ATTR_PERM_MASK) >> + AIE_REGS_ATTR_PERM_SHIFT; + if (!(ttype & rttype)) + continue; + if ((regoff >= regs->soff && regoff <= regs->eoff) || + (regend32 >= regs->soff && regend32 <= regs->eoff)) { + if (!writable) { + dev_err(&apart->dev, + "reg 0x%zx,0x%zx not writable.\n", + offset, len); + return -EINVAL; + } + } + } + + return 0; +} + +/** + * aie_part_write_register() - AI engine partition write register + * @apart: AI engine partition + * @offset: AI engine register offset + * @len: len of data to write + * @data: data to write + * @mask: mask, if it is non 0, it is mask write. + * @return: number of bytes write for success, or negative value for failure. + * + * This function writes data to the specified registers. + * If the mask is non 0, it is mask write. + */ +static int aie_part_write_register(struct aie_partition *apart, size_t offset, + size_t len, void *data, u32 mask) +{ + int ret; + void __iomem *va; + + if (mask && len > sizeof(u32)) { + /* For mask write, only allow 32bit. */ + dev_err(&apart->dev, + "failed mask write, len is more that 32bit.\n"); + return -EINVAL; + } + + /* offset is expected to be relative to the start of the partition */ + offset += aie_cal_regoff(apart->adev, apart->range.start, 0); + ret = aie_part_reg_validation(apart, offset, len, 1); + if (ret < 0) { + dev_err(&apart->dev, "failed to write to 0x%zx,0x%zx.\n", + offset, len); + return ret; + } + + va = apart->adev->base + offset; + if (!mask) { + if (len == sizeof(u32)) + iowrite32(*((u32 *)data), va); + else + memcpy_toio(va, data, len); + } else { + u32 val = ioread32(va); + + val &= ~mask; + val |= *((u32 *)data) & mask; + iowrite32(val, va); + } + + return (int)len; +} + +/** + * aie_part_access_regs() - AI engine partition registers access + * @apart: AI engine partition + * @num_reqs: number of access requests + * @reqs: array of registers access + * @return: 0 for success, and negative value for failure. + * + * This function executes AI engine partition register access requests. + */ +static int aie_part_access_regs(struct aie_partition *apart, u32 num_reqs, + struct aie_reg_args *reqs) +{ + u32 i; + + for (i = 0; i < num_reqs; i++) { + struct aie_reg_args *args = &reqs[i]; + int ret; + + if (args->op != AIE_REG_WRITE) { + dev_err(&apart->dev, + "Invalid register command type: %u.\n", + args->op); + return -EINVAL; + } + ret = aie_part_write_register(apart, + (size_t)args->offset, + sizeof(args->val), + &args->val, args->mask); + if (ret < 0) { + dev_err(&apart->dev, "reg op %u failed: 0x%llx.\n", + args->op, args->offset); + return ret; + } + } + + return 0; +} + +static int aie_part_release(struct inode *inode, struct file *filp) +{ + struct aie_partition *apart = filp->private_data; + int ret; + + /* + * TODO: It will need to reset the SHIM columns and gate the + * tiles of the partition. + */ + ret = mutex_lock_interruptible(&apart->mlock); + if (ret) + return ret; + + apart->status = 0; + mutex_unlock(&apart->mlock); + + return 0; +} + +static const struct vm_operations_struct aie_part_physical_vm_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys, +#endif +}; + +static int aie_part_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct aie_partition *apart = fp->private_data; + struct aie_device *adev = apart->adev; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE; + phys_addr_t addr; + size_t size; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + /* Only allow userspace directly read registers */ + if (vma->vm_flags & VM_WRITE) { + dev_err(&apart->dev, "%s: do not support writable mmap.\n", + __func__); + return -EINVAL; + } + vma->vm_private_data = apart; + vma->vm_ops = &aie_part_physical_vm_ops; + size = apart->range.size.col << adev->col_shift; + if ((vma->vm_end - vma->vm_start) > (size - offset)) { + dev_err(&apart->dev, + "%s: size exceed.\n", __func__); + return -EINVAL; + } + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + /* Calculate the partition address */ + addr = adev->res->start; + addr += apart->range.start.col << adev->col_shift; + addr += apart->range.start.row << adev->row_shift; + addr += offset; + return remap_pfn_range(vma, + vma->vm_start, + addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aie_partition *apart = fp->private_data; + void __user *argp = (void __user *)arg; + long ret; + + switch (cmd) { + case AIE_REG_IOCTL: + { + struct aie_reg_args raccess; + + if (copy_from_user(&raccess, argp, sizeof(raccess))) + return -EFAULT; + + ret = mutex_lock_interruptible(&apart->mlock); + if (ret) + return ret; + + ret = aie_part_access_regs(apart, 1, &raccess); + mutex_unlock(&apart->mlock); + break; + } + default: + dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +const struct file_operations aie_part_fops = { + .owner = THIS_MODULE, + .release = aie_part_release, + .mmap = aie_part_mmap, + .unlocked_ioctl = aie_part_ioctl, +}; + +/** + * aie_part_release_device() - release an AI engine partition instance + * @dev: AI engine partition device + * + * It will be called by device driver core when no one holds a valid + * pointer to @dev anymore. + */ +static void aie_part_release_device(struct device *dev) +{ + struct aie_partition *apart = dev_to_aiepart(dev); + struct aie_device *adev = apart->adev; + int ret; + + ret = mutex_lock_interruptible(&adev->mlock); + if (ret) { + dev_warn(&apart->dev, + "getting adev->mlock is interrupted by signal\n"); + } + + aie_resource_put_region(&adev->cols_res, apart->range.start.col, + apart->range.size.col); + list_del(&apart->node); + mutex_unlock(&adev->mlock); + put_device(apart->dev.parent); +} + +/** + * aie_create_partition() - create AI engine partition instance + * @adev: AI engine device + * @range: AI engine partition range to check. A range describes a group + * of AI engine tiles. + * @return: created AI engine partition pointer for success, and PTR_ERR + * for failure. + * + * This function creates an AI engine partition instance. + * It creates AI engine partition, the AI engine partition device and + * the AI engine partition character device. + */ +static struct aie_partition *aie_create_partition(struct aie_device *adev, + struct aie_range *range) +{ + struct aie_partition *apart; + struct device *dev; + char devname[32]; + int ret; + + ret = mutex_lock_interruptible(&adev->mlock); + if (ret) + return ERR_PTR(ret); + + ret = aie_resource_check_region(&adev->cols_res, range->start.col, + range->size.col); + if (ret != range->start.col) { + dev_err(&adev->dev, "invalid partition (%u,%u)(%u,%u).\n", + range->start.col, range->start.row, + range->size.col, range->size.row); + mutex_unlock(&adev->mlock); + return ERR_PTR(-EINVAL); + } + ret = aie_resource_get_region(&adev->cols_res, range->start.col, + range->size.col); + if (ret != range->start.col) { + dev_err(&adev->dev, "failed to get partition (%u,%u)(%u,%u).\n", + range->start.col, range->start.row, + range->size.col, range->size.row); + mutex_unlock(&adev->mlock); + return ERR_PTR(-EFAULT); + } + mutex_unlock(&adev->mlock); + + apart = devm_kzalloc(&adev->dev, sizeof(*apart), GFP_KERNEL); + if (!apart) + return ERR_PTR(-ENOMEM); + + apart->adev = adev; + memcpy(&apart->range, range, sizeof(*range)); + mutex_init(&apart->mlock); + + /* Create AI engine partition device */ + dev = &apart->dev; + device_initialize(dev); + dev->parent = &adev->dev; + dev->class = aie_class; + dev_set_drvdata(dev, apart); + snprintf(devname, sizeof(devname) - 1, "aiepart_%d_%d", + apart->range.start.col, apart->range.size.col); + dev_set_name(dev, devname); + /* We can now rely on the release function for cleanup */ + dev->release = aie_part_release_device; + ret = device_add(dev); + if (ret) { + dev_err(dev, "device_add failed: %d\n", ret); + put_device(dev); + return ERR_PTR(ret); + } + + ret = mutex_lock_interruptible(&adev->mlock); + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } + + list_add_tail(&apart->node, &adev->partitions); + mutex_unlock(&adev->mlock); + get_device(&adev->dev); + dev_dbg(dev, "created AIE partition device.\n"); + + return apart; +} + +struct aie_partition * +of_aie_part_probe(struct aie_device *adev, struct device_node *nc) +{ + struct aie_partition *apart; + struct aie_range range; + u32 partition_id, regs[4]; + int ret; + + /* Select device driver */ + ret = of_property_read_u32_array(nc, "reg", regs, ARRAY_SIZE(regs)); + if (ret < 0) { + dev_err(&adev->dev, + "probe %pOF failed, no tiles range information.\n", + nc); + return ERR_PTR(ret); + } + range.start.col = regs[0]; + range.start.row = regs[1]; + range.size.col = regs[2]; + range.size.row = regs[3]; + + ret = of_property_read_u32_index(nc, "xlnx,partition-id", 0, + &partition_id); + if (ret < 0) { + dev_err(&adev->dev, + "probe %pOF failed, no partition id.\n", nc); + return ERR_PTR(ret); + } + + ret = mutex_lock_interruptible(&adev->mlock); + if (ret) + return ERR_PTR(ret); + + apart = aie_get_partition_from_id(adev, partition_id); + mutex_unlock(&adev->mlock); + if (apart) { + dev_err(&adev->dev, + "probe failed: partition %u exists.\n", + partition_id); + return ERR_PTR(ret); + } + + apart = aie_create_partition(adev, &range); + if (IS_ERR(apart)) { + dev_err(&adev->dev, + "%s: failed to create part(%u,%u),(%u,%u).\n", + __func__, range.start.col, range.start.row, + range.size.col, range.size.row); + return apart; + } + + of_node_get(nc); + apart->dev.of_node = nc; + apart->partition_id = partition_id; + + dev_info(&adev->dev, + "AI engine part(%u,%u),(%u,%u), id %u is probed successfully.\n", + range.start.col, range.start.row, + range.size.col, range.size.row, apart->partition_id); + + return apart; +} + +/** + * aie_destroy_part() - destroy AI engine partition + * @apart: AI engine partition + * + * This function will remove AI engine partition. + */ +void aie_part_remove(struct aie_partition *apart) +{ + device_del(&apart->dev); + put_device(&apart->dev); +} diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-res.c b/drivers/misc/xilinx-ai-engine/ai-engine-res.c new file mode 100644 index 0000000..36f08bf --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-res.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine device driver + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include + +#include "ai-engine-internal.h" + +/** + * aie_resource_initialize() - initialize AI engine resource + * @res: pointer to AI engine resource + * @count: total number of element of this resource + * @return: 0 for success, negative value for failure. + * + * This function will initialize the data structure for the + * resource. + */ +int aie_resource_initialize(struct aie_resource *res, int count) +{ + if (!res || !count) + return -EINVAL; + res->bitmap = bitmap_zalloc(count, GFP_KERNEL); + if (!res->bitmap) + return -ENOMEM; + res->total = count; + + return 0; +} + +/** + * aie_resource_uninitialize() - uninitialize AI engine resource + * @res: pointer to AI engine resource + * + * This function will release the AI engine resource data members. + */ +void aie_resource_uninitialize(struct aie_resource *res) +{ + res->total = 0; + if (res->bitmap) + bitmap_free(res->bitmap); +} + +/** + * aie_resource_check() - check availability of requested resource + * @res: pointer to AI engine resource to check + * @start: start index of the required resource, it will only be used if + * @continuous is 1. It will check the available resource starting from + * @start + * @count: number of requested element + * @return: start resource id if the requested number of resources are available + * It will return negative value of errors. + * + * This function will check the availability. It will return start resource id + * if the requested number of resources are available. + */ +int aie_resource_check_region(struct aie_resource *res, + u32 start, u32 count) +{ + unsigned long id; + + if (!res || !res->bitmap || !count) + return -EINVAL; + id = bitmap_find_next_zero_area(res->bitmap, res->total, start, + count, 0); + if (id >= res->total) + return -ERANGE; + + return (int)id; +} + +/** + * aie_resource_get_region() - get requested AI engine resource + * @res: pointer to AI engine resource to check + * @count: number of requested element + * @start: start index of the required resource + * @return: start resource id for success, and negative value for failure. + * + * This function check if the requested AI engine resource is available. + * If it is available, mark it used and return the start resource id. + */ +int aie_resource_get_region(struct aie_resource *res, u32 start, u32 count) +{ + unsigned long off; + + if (!res || !res->bitmap || !count) + return -EINVAL; + off = bitmap_find_next_zero_area(res->bitmap, res->total, start, + count, 0); + if (off >= res->total) { + pr_err("Failed to get available AI engine resource.\n"); + return -ERANGE; + } + bitmap_set(res->bitmap, off, count); + + return (int)off; +} + +/** + * aie_resource_put_region() - release requested AI engine resource + * @res: pointer to AI engine resource to check + * @start: start index of the resource to release + * @count: number of elements to release + * + * This function release the requested AI engine resource. + */ +void aie_resource_put_region(struct aie_resource *res, int start, u32 count) +{ + if (!res || !count) + return; + bitmap_clear(res->bitmap, start, count); +} diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h new file mode 100644 index 0000000..acbc781 --- /dev/null +++ b/include/uapi/linux/xlnx-ai-engine.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2020, Xilinx Inc. + */ + +#ifndef _UAPI_AI_ENGINE_H_ +#define _UAPI_AI_ENGINE_H_ + +#include +#include + +enum aie_reg_op { + AIE_REG_WRITE, +}; + +/* AI engine partition is in use */ +#define XAIE_PART_STATUS_INUSE (1U << 0) + +/** + * struct aie_location - AIE location information + * @col: column id + * @row: row id + */ +struct aie_location { + __u32 col; + __u32 row; +}; + +/** + * struct aie_range - AIE range information + * @start: start tile location + * @size: size of the range, number of columns and rows + */ +struct aie_range { + struct aie_location start; + struct aie_location size; +}; + +/** + * struct aie_reg_args - AIE access register arguments + * @op: if this request is to read, write or poll register + * @mask: mask for mask write, 0 for not mask write + * @offset: offset of register to the start of an AI engine partition + * @val: value to write or get + */ +struct aie_reg_args { + enum aie_reg_op op; + __u32 mask; + __u64 offset; + __u32 val; +}; + +/** + * struct aie_range_args - AIE range request arguments + * @partition_id: partition id. It is used to identify the + * AI engine partition in the system. + * @uid: image identifier loaded on the AI engine partition + * @range: range of AIE tiles + * @status: indicate if the AI engine is in use. + * 0 means not in used, otherwise, in use. + */ +struct aie_range_args { + __u32 partition_id; + __u32 uid; + struct aie_range range; + __u32 status; +}; + +/** + * struct aie_partition_query - AIE partition query arguments + * @partition_cnt: number of defined partitions in the system + * @partitions: buffer to store defined partitions information. + */ +struct aie_partition_query { + struct aie_range_args *partitions; + __u32 partition_cnt; +}; + +/** + * struct aie_partition_req - AIE request partition arguments + * @partition_id: partition node id. It is used to identify the AI engine + * partition in the system. + * @uid: image identifier loaded on the AI engine partition + * @meta_data: meta data to indicate which resources used by application. + * @flag: used for application to indicate particular driver requirements + * application wants to have for the partition. e.g. do not clean + * resource when closing the partition. + */ +struct aie_partition_req { + __u32 partition_id; + __u32 uid; + __u64 meta_data; + __u32 flag; +}; + +#define AIE_IOCTL_BASE 'A' + +/* AI engine device IOCTL operations */ +#define AIE_ENQUIRE_PART_IOCTL _IOWR(AIE_IOCTL_BASE, 0x1, \ + struct aie_partition_query) +#define AIE_REQUEST_PART_IOCTL _IOR(AIE_IOCTL_BASE, 0x2, \ + struct aie_partition_req) + +/* AI engine partition IOCTL operations */ +#define AIE_REG_IOCTL _IOWR(AIE_IOCTL_BASE, 0x8, \ + struct aie_reg_args) +#endif From patchwork Wed Nov 18 08:06:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiaying Liang X-Patchwork-Id: 327720 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 629A9C64E90 for ; Wed, 18 Nov 2020 08:07:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0605E21D7A for ; Wed, 18 Nov 2020 08:07:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=xilinx.onmicrosoft.com header.i=@xilinx.onmicrosoft.com header.b="njTMZp3r" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727087AbgKRIGl (ORCPT ); Wed, 18 Nov 2020 03:06:41 -0500 Received: from mail-bn7nam10on2084.outbound.protection.outlook.com ([40.107.92.84]:41249 "EHLO NAM10-BN7-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726875AbgKRIGk (ORCPT ); Wed, 18 Nov 2020 03:06:40 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=luXDpLnGf6rLEqlS/XusZUR/JQRPdSz1Z9FHyEYrAiYsFCxJRkbbGB14x4xoDEGnSsNgCak90H1tbP2nZRDLy05it/FNI1wKSVSpuLFS1YqoxzXaMR7SajKhqsdyHwnyQtbkr1KAEjaOMwHugwQ3tWC51qjxI05jh7+eBlse+JU5UvkYFcQlqQUZgLzoZ5Pm9j1bEswMvbIoIckJZGIDcEo2mCj8zQr64C0QFQqPBszN6rX68gTKTMGA6801/NHp3hV82zC6qeRbdLu6HxgiEceuqnPWwULH/5tHaEUXhKhpRJBB3/3KWa2zvqtt8gq5s6szc70KK9cMairuLw9+6w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ZQK3w4Z/FkNfVNWtLq9BgPbMaDSPwRV0+XDB7KA69/s=; b=HYSeaC4BsujL/5n/xrnDMSh4hfu3CqhlJeTzvNT3DvYCtGnO2Zetto02XQirP3/rnIxXhOXbwdRxlsJ+zlOnM25FNdcTl6IcvM4EMJaKUxwTKjd84EdvVuawBs93zQvFVurdZRnsEzfAeByS7rhMrhjwXVEk0ReL0E8DqGBGqxwXZ3YpevZgJP9zejCaOa+wrWdAuOKnxyE1EcPXKQJhh3H1RnphPmWXK0vHcp39MMjG3a1lEEUJVQT+PB/t4ITJX1TvS3fliHKQA2I8YvPRl/VjvdCzpApnsC8uhfB0zwF3FR5EwwPcvURDegMN1CnJ5uMHifmWeDVKQvqC+ArQVQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 149.199.62.198) smtp.rcpttodomain=kernel.org smtp.mailfrom=xilinx.com; dmarc=bestguesspass action=none header.from=xilinx.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xilinx.onmicrosoft.com; s=selector2-xilinx-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ZQK3w4Z/FkNfVNWtLq9BgPbMaDSPwRV0+XDB7KA69/s=; b=njTMZp3rXq6tsrn7OL0gbnahHNjAVUcPXw69g09CggYGHn2ok0twntBfQvcO61l+Ya8nyfCL9E8Mlhyv9rELKrzCIv8m3fuDyfs02ptOupH+ePG2LbTYRhKYXiLdLWHJbpryFLyTZgUjVk6sRMKu3c9egapuo7cGwAfXjspmhNw= Received: from SN6PR16CA0051.namprd16.prod.outlook.com (2603:10b6:805:ca::28) by MN2PR02MB6816.namprd02.prod.outlook.com (2603:10b6:208:1d9::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3564.25; Wed, 18 Nov 2020 08:06:34 +0000 Received: from SN1NAM02FT051.eop-nam02.prod.protection.outlook.com (2603:10b6:805:ca:cafe::b2) by SN6PR16CA0051.outlook.office365.com (2603:10b6:805:ca::28) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3589.20 via Frontend Transport; Wed, 18 Nov 2020 08:06:34 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 149.199.62.198) smtp.mailfrom=xilinx.com; kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.62.198 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.62.198; helo=xsj-pvapexch01.xlnx.xilinx.com; Received: from xsj-pvapexch01.xlnx.xilinx.com (149.199.62.198) by SN1NAM02FT051.mail.protection.outlook.com (10.152.73.103) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.3564.22 via Frontend Transport; Wed, 18 Nov 2020 08:06:34 +0000 Received: from xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Wed, 18 Nov 2020 00:06:33 -0800 Received: from smtp.xilinx.com (172.19.127.95) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server id 15.1.1913.5 via Frontend Transport; Wed, 18 Nov 2020 00:06:33 -0800 Envelope-to: michal.simek@xilinx.com, derek.kiernan@xilinx.com, dragan.cvetic@xilinx.com, rajan.vaja@xilinx.com, tejas.patel@xilinx.com, manish.narani@xilinx.com, ravi.patel@xilinx.com, wendy.liang@xilinx.com, robh+dt@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, sumit.semwal@linaro.org, christian.koenig@amd.com, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org Received: from [172.19.2.167] (port=35936 helo=xsjjliang50.xilinx.com) by smtp.xilinx.com with esmtp (Exim 4.90) (envelope-from ) id 1kfIU5-0000Nk-Kl; Wed, 18 Nov 2020 00:06:33 -0800 From: Wendy Liang To: , , , , , , , , , , , CC: , , , , , , "Wendy Liang" Subject: [PATCH 3/9] misc: xilinx-ai-engine: Implement AI engine cleanup sequence Date: Wed, 18 Nov 2020 00:06:14 -0800 Message-ID: <1605686780-17886-4-git-send-email-wendy.liang@xilinx.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> References: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 0fd045c5-ed42-405a-7188-08d88b98ddc8 X-MS-TrafficTypeDiagnostic: MN2PR02MB6816: X-Microsoft-Antispam-PRVS: X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-MS-Oob-TLC-OOBClassifiers: OLM:3173; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 8ZyEWO4IVNS4y2kbcsR40IugZpnRn7DdkSCommuyEBWbovW57paLOkLcFocZ3u+832DjYPseyO5jktO8647zfFcRFh54gGKj8aWnhZ4j0SBg15bomHJOfZiYDGyg6d6mbajMv+geOLU+Ex8DPiywmlJeruXTbZYoQ9zMGwOhM2gP91hD82FG3W7IpADuSRz08tmnalS8UAw4D4VQimayE4PIJS3AHugq7hol11cAX5LzfH2nyyD716NIenoTde3ySjj5FCEPe4gRAGf4m3Uml7xUHPxwUBdVhJVUzKTeyDLW9Mwi26DYsR/xqN0jTK+2EBAQ7fp6J6wC3NFdvt0MjIOs9tkvv8KkWemIodxXtX60QUZRLcTO1JfqintJElMce+L6nfBVFS6O3AOGXCH+7ihwGmM4Y4+3ygkI8mxgI1hhGlDWCnvSs+ectLkSGKxe X-Forefront-Antispam-Report: CIP:149.199.62.198; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:xsj-pvapexch01.xlnx.xilinx.com; PTR:unknown-62-198.xilinx.com; CAT:NONE; SFS:(4636009)(346002)(376002)(136003)(396003)(39850400004)(46966005)(36906005)(6636002)(70206006)(7416002)(9786002)(426003)(83380400001)(336012)(44832011)(8676002)(186003)(26005)(8936002)(5660300002)(36756003)(47076004)(82310400003)(478600001)(4326008)(70586007)(356005)(54906003)(6666004)(2906002)(7636003)(107886003)(7696005)(30864003)(921005)(110136005)(82740400003)(2616005)(316002)(102446001); DIR:OUT; SFP:1101; X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Nov 2020 08:06:34.4316 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 0fd045c5-ed42-405a-7188-08d88b98ddc8 X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.62.198]; Helo=[xsj-pvapexch01.xlnx.xilinx.com] X-MS-Exchange-CrossTenant-AuthSource: SN1NAM02FT051.eop-nam02.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR02MB6816 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org When AI engine partition is released, that is if no one is using the AI engine partition, by default, it will cleanup the partition by doing the following: * reset the columns * reset the SHIMs * clear data and program memory * gate all the tiles If user doesn't want the partition is reset when the partition is released, user can set the control flag to indicate not to reset the partition when the user requests the partition. If partition the not to reset partition control flag is set, it will not execute the above cleanup sequence when the partition is released. Signed-off-by: Wendy Liang Reviewed-by: Hyun Kwon --- drivers/misc/xilinx-ai-engine/Makefile | 3 +- drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 92 ++++++++++++++++ drivers/misc/xilinx-ai-engine/ai-engine-dev.c | 2 + drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 34 ++++++ drivers/misc/xilinx-ai-engine/ai-engine-part.c | 7 +- drivers/misc/xilinx-ai-engine/ai-engine-reset.c | 121 +++++++++++++++++++++ include/uapi/linux/xlnx-ai-engine.h | 6 + 7 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-reset.c diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile index 7827a0a..39bec61 100644 --- a/drivers/misc/xilinx-ai-engine/Makefile +++ b/drivers/misc/xilinx-ai-engine/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \ ai-engine-dev.o \ ai-engine-part.o \ - ai-engine-res.o + ai-engine-res.o \ + ai-engine-reset.o diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c index 319260f..36127f0 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c @@ -5,6 +5,9 @@ * Copyright (C) 2020 Xilinx, Inc. */ +#include +#include +#include #include #include "ai-engine-internal.h" @@ -24,9 +27,25 @@ #define AIE_SHIMPL_L1INTR_MASK_A_REGOFF 0x00035000U #define AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF 0x00035050U #define AIE_SHIMPL_CLKCNTR_REGOFF 0x00036040U +#define AIE_SHIMPL_COLRESET_REGOFF 0x00036048U #define AIE_SHIMPL_RESET_REGOFF 0x0003604cU #define AIE_TILE_CORE_CLKCNTR_REGOFF 0x00036040U +/* + * Register masks + */ +#define AIE_SHIMPL_SHIMRST_MASK 0x1U +#define AIE_SHIMPL_COLRST_MASK 0x1U +#define AIE_SHIMPL_CLKCNTR_COLBUF_MASK 0x1U + +/* + * AI engine SHIM reset ID. + * TODO: it should follow the Linux reset framework. The ID should be in the + * device tree. However, as versal resets is not ready, we hardcode it in the + * driver. + */ +#define VERSAL_PM_RST_AIE_SHIM_ID 0xc10405fU + static const struct aie_tile_regs aie_kernel_regs[] = { /* SHIM AXI MM Config */ {.attribute = AIE_TILE_TYPE_SHIMNOC << AIE_REGS_ATTR_TILE_TYPE_SHIFT, @@ -49,6 +68,12 @@ static const struct aie_tile_regs aie_kernel_regs[] = { .soff = AIE_SHIMPL_L1INTR_MASK_A_REGOFF, .eoff = AIE_SHIMPL_L1INTR_BLOCK_NORTH_B_REGOFF, }, + /* SHIM column reset */ + {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) << + AIE_REGS_ATTR_TILE_TYPE_SHIFT, + .soff = AIE_SHIMPL_COLRESET_REGOFF, + .eoff = AIE_SHIMPL_COLRESET_REGOFF, + }, /* SHIM reset Enable */ {.attribute = (AIE_TILE_TYPE_SHIMPL | AIE_TILE_TYPE_SHIMNOC) << AIE_REGS_ATTR_TILE_TYPE_SHIFT, @@ -68,6 +93,16 @@ static const struct aie_tile_regs aie_kernel_regs[] = { }, }; +static const struct aie_single_reg_field aie_col_rst = { + .mask = AIE_SHIMPL_COLRST_MASK, + .regoff = AIE_SHIMPL_COLRESET_REGOFF, +}; + +static const struct aie_single_reg_field aie_col_clkbuf = { + .mask = AIE_SHIMPL_CLKCNTR_COLBUF_MASK, + .regoff = AIE_SHIMPL_CLKCNTR_REGOFF, +}; + static u32 aie_get_tile_type(struct aie_location *loc) { if (loc->row) @@ -79,8 +114,63 @@ static u32 aie_get_tile_type(struct aie_location *loc) return AIE_TILE_TYPE_SHIMNOC; } +/** + * aie_set_shim_reset() - Set AI engine SHIM reset + * @adev: AI engine device + * @range: range of AI engine tiles + * @assert: true to set reset, false to unset reset + */ +static void aie_set_shim_reset(struct aie_device *adev, + struct aie_range *range, bool assert) +{ + u32 c; + u32 val; + struct aie_location loc; + + val = FIELD_PREP(AIE_SHIMPL_SHIMRST_MASK, (assert ? 1 : 0)); + loc.row = 0; + for (c = range->start.col; c < range->start.col + range->size.col; + c++) { + u32 regoff; + + loc.col = c; + regoff = aie_cal_regoff(adev, loc, AIE_SHIMPL_RESET_REGOFF); + iowrite32(val, adev->base + regoff); + } +} + +static int aie_reset_shim(struct aie_device *adev, struct aie_range *range) +{ + int ret; + + /* Enable shim reset of each column */ + aie_set_shim_reset(adev, range, true); + + /* Assert shim reset of AI engine array */ + ret = zynqmp_pm_reset_assert(VERSAL_PM_RST_AIE_SHIM_ID, + PM_RESET_ACTION_ASSERT); + if (ret < 0) { + dev_err(&adev->dev, "failed to assert SHIM reset.\n"); + return ret; + } + + /* Release shim reset of AI engine array */ + ret = zynqmp_pm_reset_assert(VERSAL_PM_RST_AIE_SHIM_ID, + PM_RESET_ACTION_RELEASE); + if (ret < 0) { + dev_err(&adev->dev, "failed to release SHIM reset.\n"); + return ret; + } + + /* Disable shim reset of each column */ + aie_set_shim_reset(adev, range, false); + + return 0; +} + static const struct aie_tile_operations aie_ops = { .get_tile_type = aie_get_tile_type, + .reset_shim = aie_reset_shim, }; /** @@ -104,6 +194,8 @@ int aie_device_init(struct aie_device *adev) adev->ops = &aie_ops; adev->num_kernel_regs = ARRAY_SIZE(aie_kernel_regs); adev->kernel_regs = aie_kernel_regs; + adev->col_rst = &aie_col_rst; + adev->col_clkbuf = &aie_col_clkbuf; /* Get the columns resource */ /* Get number of columns from AI engine memory resource */ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c index 2ab2dc8..38a1ded 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-dev.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dev.c @@ -208,6 +208,8 @@ struct aie_partition *aie_request_partition(struct aie_device *adev, * exported for user to access. */ apart->status = XAIE_PART_STATUS_INUSE; + apart->cntrflag = req->flag; + mutex_unlock(&apart->mlock); } mutex_unlock(&adev->mlock); diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h index 6a69946..2acd34f 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h @@ -53,18 +53,30 @@ struct aie_tile_regs { u32 attribute; }; +/** + * struct aie_single_reg_field - AI engine single field register attribute + * @mask: field mask + * @regoff: register offset of the field + */ +struct aie_single_reg_field { + u32 mask; + u32 regoff; +}; + struct aie_device; struct aie_partition; /** * struct aie_tile_operations - AI engine device operations * @get_tile_type: get type of tile based on tile operation + * @reset_shim: reset shim, it will assert and then release SHIM reset * * Different AI engine device version has its own device * operation. */ struct aie_tile_operations { u32 (*get_tile_type)(struct aie_location *loc); + int (*reset_shim)(struct aie_device *adev, struct aie_range *range); }; /** @@ -87,6 +99,8 @@ struct aie_resource { * @res: memory resource of AI engine device * @kernel_regs: array of kernel only registers * @ops: tile operations + * @col_rst: column reset attribute + * @col_clkbuf: column clock buffer attribute * @size: size of the AI engine address space * @array_shift: array address shift * @col_shift: column address shift @@ -105,6 +119,8 @@ struct aie_device { struct resource *res; const struct aie_tile_regs *kernel_regs; const struct aie_tile_operations *ops; + const struct aie_single_reg_field *col_rst; + const struct aie_single_reg_field *col_clkbuf; size_t size; struct aie_resource cols_res; u32 array_shift; @@ -124,6 +140,8 @@ struct aie_device { * @partition_id: partition id. Partition ID is the identifier * of the AI engine partition in the system. * @status: indicate if the partition is in use + * @cntrflag: partition control flag. e.g. whether to reset columns when + * the partition is released */ struct aie_partition { struct list_head node; @@ -133,6 +151,7 @@ struct aie_partition { struct device dev; u32 partition_id; u32 status; + u32 cntrflag; }; extern struct class *aie_class; @@ -168,6 +187,20 @@ extern const struct file_operations aie_part_fops; aie_tile_reg_field_get(aie_tile_reg_mask(adev), 0, regoff)) /** + * aie_get_field_val() - calculate value of an AI engine register field + * @field: a field in a register + * @val: value of the field + * @return: value of a register field + */ +static inline u32 aie_get_field_val(const struct aie_single_reg_field *field, + u32 val) +{ + long long mask = (long long)field->mask & 0x00000000ffffffff; + + return (val << __bf_shf(mask)) & field->mask; +} + +/** * aie_cal_regoff() - calculate register offset to the whole AI engine * device start address * @adev: AI engine device @@ -221,6 +254,7 @@ struct aie_partition *aie_request_partition(struct aie_device *adev, struct aie_partition *of_aie_part_probe(struct aie_device *adev, struct device_node *nc); void aie_part_remove(struct aie_partition *apart); +int aie_part_clean(struct aie_partition *apart); int aie_device_init(struct aie_device *adev); #endif /* AIE_INTERNAL_H */ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c index fc8f9f5..98f125b 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-part.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c @@ -217,14 +217,12 @@ static int aie_part_release(struct inode *inode, struct file *filp) struct aie_partition *apart = filp->private_data; int ret; - /* - * TODO: It will need to reset the SHIM columns and gate the - * tiles of the partition. - */ ret = mutex_lock_interruptible(&apart->mlock); if (ret) return ret; + aie_part_clean(apart); + apart->status = 0; mutex_unlock(&apart->mlock); @@ -413,7 +411,6 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev, put_device(dev); return ERR_PTR(ret); } - list_add_tail(&apart->node, &adev->partitions); mutex_unlock(&adev->mlock); get_device(&adev->dev); diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-reset.c b/drivers/misc/xilinx-ai-engine/ai-engine-reset.c new file mode 100644 index 0000000..fc0262f7 --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-reset.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine device driver resets implementation + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include +#include + +#include "ai-engine-internal.h" + +/** + * aie_part_set_col_reset() - set AI engine column reset + * @apart: AI engine partition + * @col: column to reset + * @reset: true to assert reset, false to release reset + */ +static void aie_part_set_col_reset(struct aie_partition *apart, u32 col, + bool reset) +{ + struct aie_device *adev = apart->adev; + const struct aie_single_reg_field *col_rst = adev->col_rst; + struct aie_location loc; + u32 regoff, val; + + loc.row = 0; + loc.col = col; + + val = aie_get_field_val(col_rst, (reset ? 1 : 0)); + regoff = aie_cal_regoff(adev, loc, col_rst->regoff); + iowrite32(val, adev->base + regoff); +} + +/** + * aie_part_set_col_clkbuf() - set AI engine column clock buffer + * @apart: AI engine partition + * @col: column to reset + * @enable: true to enable, false to disable + */ +static void aie_part_set_col_clkbuf(struct aie_partition *apart, u32 col, + bool enable) +{ + struct aie_device *adev = apart->adev; + const struct aie_single_reg_field *col_clkbuf = adev->col_clkbuf; + struct aie_location loc; + u32 regoff, val; + + loc.row = 0; + loc.col = col; + + val = aie_get_field_val(col_clkbuf, (enable ? 1 : 0)); + regoff = aie_cal_regoff(adev, loc, col_clkbuf->regoff); + iowrite32(val, adev->base + regoff); +} + +/** + * aie_part_set_cols_reset() - set column reset of every column in a partition + * @apart: AI engine partition + * @reset: bool to assert reset, false to release reset + */ +static void aie_part_set_cols_reset(struct aie_partition *apart, bool reset) +{ + struct aie_range *range = &apart->range; + u32 c; + + for (c = range->start.col; c < range->start.col + range->size.col; + c++) + aie_part_set_col_reset(apart, c, reset); +} + +/** + * aie_part_set_cols_clkbuf() - set column clock buffer of every column in a + * partition + * @apart: AI engine partition + * @enable: true to enable, false to disable + */ +static void aie_part_set_cols_clkbuf(struct aie_partition *apart, bool enable) +{ + struct aie_range *range = &apart->range; + u32 c; + + for (c = range->start.col; c < range->start.col + range->size.col; + c++) + aie_part_set_col_clkbuf(apart, c, enable); +} + +/** + * aie_part_clean() - reset and clear AI engine partition + * @apart: AI engine partition + * @return: 0 for success and negative value for failure + * + * This function will: + * * gate all the columns + * * reset AI engine partition columns + * * reset AI engine shims + * * clear the memories + * * gate all the tiles in a partition. + * + * This function will not validate the partition, the caller will need to + * provide a valid AI engine partition. + */ +int aie_part_clean(struct aie_partition *apart) +{ + struct aie_device *adev = apart->adev; + int ret; + + if (apart->cntrflag & XAIE_PART_NOT_RST_ON_RELEASE) + return 0; + + aie_part_set_cols_clkbuf(apart, false); + aie_part_set_cols_reset(apart, true); + + ret = apart->adev->ops->reset_shim(adev, &apart->range); + if (ret < 0) + return ret; + + aie_part_set_cols_clkbuf(apart, false); + + return 0; +} diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h index acbc781..ed2823c 100644 --- a/include/uapi/linux/xlnx-ai-engine.h +++ b/include/uapi/linux/xlnx-ai-engine.h @@ -16,6 +16,12 @@ enum aie_reg_op { /* AI engine partition is in use */ #define XAIE_PART_STATUS_INUSE (1U << 0) +/* + * AI engine partition control flags + */ +/* Not reset when release AI engine partition */ +#define XAIE_PART_NOT_RST_ON_RELEASE 0x00000001U + /** * struct aie_location - AIE location information * @col: column id From patchwork Wed Nov 18 08:06:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiaying Liang X-Patchwork-Id: 327716 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E9E97C64E8A for ; Wed, 18 Nov 2020 08:07:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9E74E223C7 for ; Wed, 18 Nov 2020 08:07:50 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=xilinx.onmicrosoft.com header.i=@xilinx.onmicrosoft.com header.b="CHWUkB7H" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727294AbgKRIHE (ORCPT ); Wed, 18 Nov 2020 03:07:04 -0500 Received: from mail-bn8nam11on2081.outbound.protection.outlook.com ([40.107.236.81]:13546 "EHLO NAM11-BN8-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727266AbgKRIHE (ORCPT ); Wed, 18 Nov 2020 03:07:04 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=EVsEgGaEYU9nQguZeqjn3lx6ypOnf/JbR7I6fLCh19bohyjdFW2gna0RFyJAy3yvJpbk24QNAFUEyeogDsqp5q7Sn780D9erqkeTFjyauGUg1DSs4tYRceNlLaJmV7ZBFxOPkTXwMQGmEDYrcdTNA4gCVQc45nPO3ZZx9X6Hoox2bbcbW2fg4xBKeSfmwHjmcBkKJJpueZQXD3OKRiBEDrH5d+nFyaq2bp4J0VTNGXtVVC9DquIuvHseQ+O1JtjJVYiKyoig07cDZEh33iWfyc60I6nL/69ZIG1SNvaQ4H9Jy/d1GFmHUK0hM0aGUbFJp9C2r7L0IG1oneUsPORpOQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=U+FUXjcOxngFEfP4VMposQ15PthSjXjRPuA6RQUijAo=; b=dDsPR8j+27i+hfkJcUIX4RBd55eEjlyDjcT4K6cJckOO2UvYgirQ1W1i7X9PAojuCfPoYcdL/fYVb6XRsv59jSY+MtEycObkvX5MqQFjsUnX+o/zE23VHUJpFlsbMWxY7pvGrWDD5PER8qky+CveGoKYYhO7pdy7fZVchYKgu1GfwC/FeOHWxIo6v/9aQziCNKjKJGnytXfiOHyIeQ0oe6la1XevklI/SXWJTffKDtRKqc/P1huDC54PgSwT1nZ/lgwaJRkEr6j2JxU3dRJrbwAqjhJvqFZB/LHRS5CCclB9MUp1IAjD5PSUVxd+LUGF0ndUm27NKAMpZLT81WO+9Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 149.199.62.198) smtp.rcpttodomain=kernel.org smtp.mailfrom=xilinx.com; dmarc=bestguesspass action=none header.from=xilinx.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xilinx.onmicrosoft.com; s=selector2-xilinx-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=U+FUXjcOxngFEfP4VMposQ15PthSjXjRPuA6RQUijAo=; b=CHWUkB7HQJX+o23A+v4NbpjEC9c9jDzjGP1imAv9o7mXoajVH4W2n4gzuVER88eLZyZDZ++OTmjZbkoSZtaj1vdQR2+GsM4zor2S0f2BHPATHSomvXPzUwrmX0RiU+vHPIydnxDHmNvKPUkClYxuABpvg+e5PFEa/N5zfZuTOwE= Received: from SA0PR11CA0100.namprd11.prod.outlook.com (2603:10b6:806:d1::15) by SN4PR0201MB3472.namprd02.prod.outlook.com (2603:10b6:803:48::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3564.28; Wed, 18 Nov 2020 08:06:56 +0000 Received: from SN1NAM02FT048.eop-nam02.prod.protection.outlook.com (2603:10b6:806:d1:cafe::c9) by SA0PR11CA0100.outlook.office365.com (2603:10b6:806:d1::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3589.20 via Frontend Transport; Wed, 18 Nov 2020 08:06:56 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 149.199.62.198) smtp.mailfrom=xilinx.com; kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.62.198 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.62.198; helo=xsj-pvapexch01.xlnx.xilinx.com; Received: from xsj-pvapexch01.xlnx.xilinx.com (149.199.62.198) by SN1NAM02FT048.mail.protection.outlook.com (10.152.72.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.3564.22 via Frontend Transport; Wed, 18 Nov 2020 08:06:55 +0000 Received: from xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Wed, 18 Nov 2020 00:06:33 -0800 Received: from smtp.xilinx.com (172.19.127.95) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server id 15.1.1913.5 via Frontend Transport; Wed, 18 Nov 2020 00:06:33 -0800 Envelope-to: michal.simek@xilinx.com, derek.kiernan@xilinx.com, dragan.cvetic@xilinx.com, rajan.vaja@xilinx.com, tejas.patel@xilinx.com, manish.narani@xilinx.com, ravi.patel@xilinx.com, wendy.liang@xilinx.com, robh+dt@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, sumit.semwal@linaro.org, christian.koenig@amd.com, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org Received: from [172.19.2.167] (port=35936 helo=xsjjliang50.xilinx.com) by smtp.xilinx.com with esmtp (Exim 4.90) (envelope-from ) id 1kfIU5-0000Nk-Ls; Wed, 18 Nov 2020 00:06:33 -0800 From: Wendy Liang To: , , , , , , , , , , , CC: , , , , , , "Wendy Liang" Subject: [PATCH 4/9] misc: xilinx-ai-engine: expose AI engine tile memories to userspace Date: Wed, 18 Nov 2020 00:06:15 -0800 Message-ID: <1605686780-17886-5-git-send-email-wendy.liang@xilinx.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> References: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 83f61d42-2dea-4c1a-d9b1-08d88b98ea95 X-MS-TrafficTypeDiagnostic: SN4PR0201MB3472: X-Microsoft-Antispam-PRVS: X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-MS-Oob-TLC-OOBClassifiers: OLM:2089; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 6vwQo9AqqLsl0fwaVjaU4oMzXoNyGBQTZ/c68A9yRI61lKusmkEkdMO4OASq2bIqGcWLyRgG2wDde4D8cIu0TvcBf7pTzzMs6kYKx5D+iL03ENg1426TpVNjRVNE37BQOHsMNcDuKKGPVI/OJLO0VtyUvDAuROU7EC+eAMVyQbR/TlHjIuEjcXJPjSqR2r0GOBCTxx2+15oErt7c7Y2VuONZxSDXvpHj7L2kutgof9MDqCeZ9v3GjJK6E8qXhTm63RZnK2Pi5gam6ZBQRlLzJ0vQxAJNvM+j8Sn2/H0OKdmh1bX+x9ivbCjl7Ovv6MyYT3jPoHMsfxmLDTj4WQJD1NwxwYj36Ho1wUvlziMhQlTZLGN+CrJpBxVTuu3ZU9JhNevRw7QEgjtbBhRktEseEjpSa7l+x3YZzWpcsGuzidkBIO3z2XmLwmQwcFuIfcRwuvIVLkB52DgdXO8T92unnnL9a9vkPHcPqJXMWp2fPQ8= X-Forefront-Antispam-Report: CIP:149.199.62.198; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:xsj-pvapexch01.xlnx.xilinx.com; PTR:unknown-62-198.xilinx.com; CAT:NONE; SFS:(4636009)(376002)(396003)(39850400004)(346002)(136003)(46966005)(110136005)(6636002)(316002)(82740400003)(54906003)(36756003)(7696005)(2616005)(82310400003)(426003)(2906002)(336012)(8676002)(36906005)(47076004)(26005)(8936002)(7636003)(9786002)(356005)(107886003)(186003)(6666004)(4326008)(921005)(70206006)(44832011)(7416002)(478600001)(70586007)(83380400001)(30864003)(5660300002)(102446001)(461764006); DIR:OUT; SFP:1101; X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Nov 2020 08:06:55.9094 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 83f61d42-2dea-4c1a-d9b1-08d88b98ea95 X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.62.198]; Helo=[xsj-pvapexch01.xlnx.xilinx.com] X-MS-Exchange-CrossTenant-AuthSource: SN1NAM02FT048.eop-nam02.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN4PR0201MB3472 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org There is no concern to have userspace to directly access AI engine program and data memories. It will be much faster to directly copy data to and from these memories from userspace. We choose to use DMA buf for the data and program memory because of the DMA buf features. DMA buf can share the DMA memory between applications and different devices, which can benefit on how to share data with AI engine device in future. There is one DMA buf per type of memory in an AI engine partition. e.g. There is one DMA buf for all the tile core program memories in an AI engine partition. There is another DMA buf for all the tile data memories in an AI engine partition. Signed-off-by: Wendy Liang Reviewed-by: Hyun Kwon --- drivers/misc/xilinx-ai-engine/Makefile | 1 + drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 36 +++ drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 30 +++ drivers/misc/xilinx-ai-engine/ai-engine-mem.c | 274 +++++++++++++++++++++ drivers/misc/xilinx-ai-engine/ai-engine-part.c | 47 ++++ drivers/misc/xilinx-ai-engine/ai-engine-reset.c | 38 +++ include/uapi/linux/xlnx-ai-engine.h | 49 ++++ 7 files changed, 475 insertions(+) create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-mem.c diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile index 39bec61..2dbed42 100644 --- a/drivers/misc/xilinx-ai-engine/Makefile +++ b/drivers/misc/xilinx-ai-engine/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \ ai-engine-dev.o \ + ai-engine-mem.o \ ai-engine-part.o \ ai-engine-res.o \ ai-engine-reset.o diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c index 36127f0..7fce2f00 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c @@ -12,10 +12,14 @@ #include "ai-engine-internal.h" +#define KBYTES(n) ((n) * 1024) + #define AIE_ARRAY_SHIFT 30U #define AIE_COL_SHIFT 23U #define AIE_ROW_SHIFT 18U +#define NUM_MEMS_PER_TILE 2U + /* * Registers offsets */ @@ -114,6 +118,37 @@ static u32 aie_get_tile_type(struct aie_location *loc) return AIE_TILE_TYPE_SHIMNOC; } +static unsigned int aie_get_mem_info(struct aie_range *range, + struct aie_part_mem *pmem) +{ + unsigned int i; + + if (range->start.row + range->size.row <= 1) { + /* SHIM row only, no memories in this range */ + return 0; + } + if (!pmem) + return NUM_MEMS_PER_TILE; + + for (i = 0; i < NUM_MEMS_PER_TILE; i++) { + struct aie_mem *mem = &pmem[i].mem; + + memcpy(&mem->range, range, sizeof(*range)); + if (!mem->range.start.row) { + mem->range.start.row = 1; + mem->range.size.row--; + } + } + /* Setup tile data memory information */ + pmem[0].mem.offset = 0; + pmem[0].mem.size = KBYTES(32); + /* Setup program memory information */ + pmem[1].mem.offset = 0x20000; + pmem[1].mem.size = KBYTES(16); + + return NUM_MEMS_PER_TILE; +} + /** * aie_set_shim_reset() - Set AI engine SHIM reset * @adev: AI engine device @@ -170,6 +205,7 @@ static int aie_reset_shim(struct aie_device *adev, struct aie_range *range) static const struct aie_tile_operations aie_ops = { .get_tile_type = aie_get_tile_type, + .get_mem_info = aie_get_mem_info, .reset_shim = aie_reset_shim, }; diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h index 2acd34f..e84610b 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -67,8 +69,30 @@ struct aie_device; struct aie_partition; /** + * struct aie_part_mem - AI engine partition memory information structure + * @apart: AI engine partition + * @dbuf: dmabuf pointer associated with the memory + * @mem: memory information of a type of memory + * @size: size of the total memories in the partition + * + * This structure is to keep the information of a type of memory in a + * partition. The memory information will be stored in @mem property. + * The following information will be keep: + * * memory start address offset within a tile + * * memory size + * * what tiles contain this type of memory + */ +struct aie_part_mem { + struct aie_partition *apart; + struct dma_buf *dbuf; + struct aie_mem mem; + size_t size; +}; + +/** * struct aie_tile_operations - AI engine device operations * @get_tile_type: get type of tile based on tile operation + * @get_mem_info: get different types of memories information * @reset_shim: reset shim, it will assert and then release SHIM reset * * Different AI engine device version has its own device @@ -76,6 +100,8 @@ struct aie_partition; */ struct aie_tile_operations { u32 (*get_tile_type)(struct aie_location *loc); + unsigned int (*get_mem_info)(struct aie_range *range, + struct aie_part_mem *pmem); int (*reset_shim)(struct aie_device *adev, struct aie_range *range); }; @@ -134,6 +160,7 @@ struct aie_device { * struct aie_partition - AI engine partition structure * @node: list node * @adev: pointer to AI device instance + * @pmems: pointer to partition memories types * @range: range of partition * @mlock: protection for AI engine partition operations * @dev: device for the AI engine partition @@ -146,6 +173,7 @@ struct aie_device { struct aie_partition { struct list_head node; struct aie_device *adev; + struct aie_part_mem *pmems; struct aie_range range; struct mutex mlock; /* protection for AI engine partition operations */ struct device dev; @@ -256,5 +284,7 @@ struct aie_partition *of_aie_part_probe(struct aie_device *adev, void aie_part_remove(struct aie_partition *apart); int aie_part_clean(struct aie_partition *apart); +int aie_mem_get_info(struct aie_partition *apart, unsigned long arg); + int aie_device_init(struct aie_device *adev); #endif /* AIE_INTERNAL_H */ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-mem.c b/drivers/misc/xilinx-ai-engine/ai-engine-mem.c new file mode 100644 index 0000000..5a06bdd0 --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-mem.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine device memory implementation + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include + +#include "ai-engine-internal.h" + +#define aie_cal_reg_goffset(adev, loc, regoff) ({ \ + struct aie_device *_adev = (adev); \ + struct aie_location *_loc = &(loc); \ + (_loc->col << _adev->col_shift) + \ + (_loc->row << _adev->row_shift) + (regoff); \ + }) + +#define aie_cal_reg_pa(adev, loc, regoff) ({ \ + struct aie_device *__adev = (adev); \ + __adev->res->start + aie_cal_reg_goffset(__adev, loc, regoff); \ + }) + +static struct sg_table * +aie_mem_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + /* + * TODO: It is mandatory by DMA buf operation. It is used return + * scatterlist table of an attachment. We don't have the implementation + * for now. And thus it has empty implementation. + */ + (void)attachment; + (void)direction; + dev_warn(attachment->dev, + "AI engine memory map dma buf is not implemented.\n"); + return NULL; +} + +static void aie_mem_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + /* + * TODO: It is mandatory by DMA buf operation. It is used deallocate + * scatterlist table of an attachment. We don't have the implementation + * for now. And thus it has empty implementation. + */ + (void)attachment; + (void)table; + (void)direction; + dev_warn(attachment->dev, + "AI engine memory unmap dma buf is not implemented.\n"); +} + +static int aie_mem_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct aie_part_mem *pmem = dmabuf->priv; + struct aie_mem *mem = &pmem->mem; + struct aie_partition *apart = pmem->apart; + struct aie_location loc; + unsigned long addr = vma->vm_start; + unsigned long offset = vma->vm_pgoff * PAGE_SIZE, moffset = 0; + unsigned long remainder = vma->vm_end - addr; + size_t msize = mem->size; + + if (remainder + offset > pmem->size) + return -EINVAL; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + for (loc.col = mem->range.start.col; + loc.col < mem->range.start.col + mem->range.size.col; loc.col++) { + for (loc.row = mem->range.start.row; + loc.row < mem->range.start.row + mem->range.size.row; + loc.row++) { + unsigned long toffset, len; + phys_addr_t mempa; + int ret; + + remainder = vma->vm_end - addr; + if (!remainder) + return 0; + + if (moffset + msize < offset) { + moffset += msize; + continue; + } + /* + * calculate offset within the tile memory. + * offset is the offset to vma->start. + * moffset is the tile memory start offset to + * vma->start. + */ + toffset = offset - moffset; + len = msize - toffset; + if (len > remainder) + len = remainder; + mempa = aie_cal_reg_pa(apart->adev, loc, + toffset + mem->offset); + + ret = remap_pfn_range(vma, addr, mempa >> PAGE_SHIFT, + len, vma->vm_page_prot); + if (ret) { + dev_err(&apart->dev, + "failed to mmap (%u,%u)memory, remap failed, 0x%pa, 0x%lx.\n", + loc.col, loc.row, &mempa, len); + return ret; + } + addr += len; + offset += len; + moffset += msize; + } + } + return 0; +} + +static void aie_mem_dmabuf_release(struct dma_buf *dmabuf) +{ + struct aie_part_mem *pmem = dmabuf->priv; + + pmem->dbuf = NULL; +} + +static const struct dma_buf_ops aie_mem_dma_buf_ops = { + .map_dma_buf = aie_mem_map_dma_buf, + .unmap_dma_buf = aie_mem_unmap_dma_buf, + .mmap = aie_mem_mmap, + .release = aie_mem_dmabuf_release, +}; + +/** + * aie_mem_create_dmabuf() - creates DMA buffer for AI engine partition + * memories + * @apart: AI engine partition + * @pmem: pointer to the partition memory information + * @mem: pointer to where it store the memory information and DMA buf file + * descriptor for user. + * @return: 0 for success, negative value for failure + * + * This function will create DMA buffer for the AI engine partition memory + * and will store the DMA buffer file descriptor and memory information in + * @mem. + */ +static int aie_mem_create_dmabuf(struct aie_partition *apart, + struct aie_part_mem *pmem, + struct aie_mem *mem) +{ + struct dma_buf *dmabuf; + int ret; + + if (!PAGE_ALIGNED(pmem->mem.size)) { + dev_warn(&apart->dev, + "no dmabuf for mem(0x%zx, 0x%zx), not aligned with page size.\n", + pmem->mem.offset, pmem->mem.size); + return -EINVAL; + } + + dmabuf = pmem->dbuf; + if (!dmabuf) { + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + exp_info.ops = &aie_mem_dma_buf_ops; + exp_info.size = pmem->size; + exp_info.flags = O_RDWR; + exp_info.priv = pmem; + + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + pmem->dbuf = dmabuf; + } + + ret = dma_buf_fd(dmabuf, O_CLOEXEC); + if (ret < 0) { + dev_err(&apart->dev, + "dmabuf creation failed, failed to get fd.\n"); + return ret; + } + memcpy(mem, &pmem->mem, sizeof(*mem)); + mem->fd = ret; + + return 0; +} + +/** + * aie_mem_get_info() - get AI engine memories information + * @apart: AI engine partition + * @arg: argument from user to enquire AI engine partition memory information + * @return: 0 for success, and negative value for failure + * + * This function will get the memories information for the specified AI engine + * partition. It will create DMA buf file descriptors for the memories and + * return the DMA buf file descriptors to users. + * It will create a DMA buffer per type of memories. + * e.g. There will be a DMA buffer for all the tile program memories in the + * partition, and another DMA buffer for all the tile data memories in the + * partition. + * User can first pass num_mems as 0 in the @arg to enquire for how many types + * of memories in this AI engine partition. And then, user can allocate memory + * to keep the information for different types of memories, and then use the + * same enquiry with non-zero num_mems and none NULL pointer to ask for the + * details of the information of all the types of memories in the AI engine + * partition. + */ +int aie_mem_get_info(struct aie_partition *apart, unsigned long arg) +{ + struct aie_mem_args margs; + struct aie_mem *mems; + unsigned int num_mems, i; + int ret; + + if (copy_from_user(&margs, (void __user *)arg, sizeof(margs))) + return -EFAULT; + + num_mems = apart->adev->ops->get_mem_info(&apart->range, NULL); + if (num_mems <= 0) + return -EINVAL; + + if (!margs.num_mems) { + struct aie_mem_args __user *umargs_ptr = (void __user *)arg; + + /* This enquiry is to get the number of types of memories. */ + if (copy_to_user((void __user *)&umargs_ptr->num_mems, + &num_mems, sizeof(num_mems))) + return -EFAULT; + return 0; + } + + if (num_mems != margs.num_mems) { + dev_err(&apart->dev, + "failed to get mem info, invalid num of mems %d,%d.\n", + num_mems, margs.num_mems); + return -EINVAL; + } + if (!margs.mems) { + dev_err(&apart->dev, + "failed to get mem info, mems pointer is NULL.\n"); + return -EINVAL; + } + + mems = kcalloc(num_mems, sizeof(*mems), GFP_KERNEL); + if (!mems) + return -ENOMEM; + + /* + * Create DMA buffer for the memories. + * Each type of memory in the partition has its own DMA buf. + */ + for (i = 0; i < num_mems; i++) { + ret = aie_mem_create_dmabuf(apart, &apart->pmems[i], &mems[i]); + if (ret) + break; + } + if (!ret) { + if (copy_to_user((void __user *)margs.mems, mems, + num_mems * sizeof(mems[0]))) + ret = -EFAULT; + } + + if (ret) { + for (i = 0; i < num_mems; i++) { + if (mems[i].fd) + put_unused_fd(mems[i].fd); + } + } + + kfree(mems); + return ret; +} diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c index 98f125b..4be6d38 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-part.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c @@ -294,6 +294,8 @@ static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) mutex_unlock(&apart->mlock); break; } + case AIE_GET_MEM_IOCTL: + return aie_mem_get_info(apart, arg); default: dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd); ret = -EINVAL; @@ -337,6 +339,41 @@ static void aie_part_release_device(struct device *dev) } /** + * aie_part_create_mems_info() - creates array to store the AI engine partition + * different memories types information + * @apart: AI engine partition + * @return: 0 for success, negative value for failure + * + * This function will create array to store the information of different + * memories types in the partition. This array is stored in @apart->pmems. + */ +static int aie_part_create_mems_info(struct aie_partition *apart) +{ + unsigned int i, num_mems; + + num_mems = apart->adev->ops->get_mem_info(&apart->range, NULL); + if (!num_mems) + return 0; + + apart->pmems = devm_kcalloc(&apart->dev, num_mems, + sizeof(struct aie_part_mem), + GFP_KERNEL); + if (!apart->pmems) + return -ENOMEM; + + apart->adev->ops->get_mem_info(&apart->range, apart->pmems); + for (i = 0; i < num_mems; i++) { + struct aie_mem *mem = &apart->pmems[i].mem; + + apart->pmems[i].apart = apart; + apart->pmems[i].size = mem->size * + mem->range.size.col * + mem->range.size.row; + } + return 0; +} + +/** * aie_create_partition() - create AI engine partition instance * @adev: AI engine device * @range: AI engine partition range to check. A range describes a group @@ -406,6 +443,16 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev, return ERR_PTR(ret); } + /* + * Create array to keep the information of the different types of tile + * memories information of the AI engine partition. + */ + ret = aie_part_create_mems_info(apart); + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } + ret = mutex_lock_interruptible(&adev->mlock); if (ret) { put_device(dev); diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-reset.c b/drivers/misc/xilinx-ai-engine/ai-engine-reset.c index fc0262f7..d35cd8d 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-reset.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-reset.c @@ -86,6 +86,43 @@ static void aie_part_set_cols_clkbuf(struct aie_partition *apart, bool enable) } /** + * aie_part_clear_mems() - clear memories of every tile in a partition + * @apart: AI engine partition + */ +static void aie_part_clear_mems(struct aie_partition *apart) +{ + struct aie_device *adev = apart->adev; + struct aie_part_mem *pmems = apart->pmems; + u32 i, num_mems; + + /* Get the number of different types of memories */ + num_mems = adev->ops->get_mem_info(&apart->range, NULL); + if (!num_mems) + return; + + /* Clear each type of memories in the partition */ + for (i = 0; i < num_mems; i++) { + struct aie_mem *mem = &pmems[i].mem; + struct aie_range *range = &mem->range; + u32 c, r; + + for (c = range->start.col; + c < range->start.col + range->size.col; c++) { + for (r = range->start.row; + r < range->start.row + range->size.row; r++) { + struct aie_location loc; + u32 memoff; + + loc.col = c; + loc.row = r; + memoff = aie_cal_regoff(adev, loc, mem->offset); + memset_io(adev->base + memoff, 0, mem->size); + } + } + } +} + +/** * aie_part_clean() - reset and clear AI engine partition * @apart: AI engine partition * @return: 0 for success and negative value for failure @@ -115,6 +152,7 @@ int aie_part_clean(struct aie_partition *apart) if (ret < 0) return ret; + aie_part_clear_mems(apart); aie_part_set_cols_clkbuf(apart, false); return 0; diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h index ed2823c..5e40d00 100644 --- a/include/uapi/linux/xlnx-ai-engine.h +++ b/include/uapi/linux/xlnx-ai-engine.h @@ -6,6 +6,10 @@ #ifndef _UAPI_AI_ENGINE_H_ #define _UAPI_AI_ENGINE_H_ +#ifndef __KERNEL__ +#include +#endif + #include #include @@ -43,6 +47,32 @@ struct aie_range { }; /** + * struct aie_mem - AIE memory information + * @range: range of tiles of the memory + * @offset: register offset within a tile of the memory + * @size: of a the memory in one tile + * @fd: file descriptor of the memory + */ +struct aie_mem { + struct aie_range range; + size_t offset; + size_t size; + int fd; +}; + +/** + * struct aie_mem_args - AIE memory enquiry arguments + * @num_mems: number of "struct aie_mem" elements + * e.g. two memory information elements, one for tile core memory, + * and the other for tile data memory. + * @mems: array of AI engine memory information elements + */ +struct aie_mem_args { + unsigned int num_mems; + struct aie_mem *mems; +}; + +/** * struct aie_reg_args - AIE access register arguments * @op: if this request is to read, write or poll register * @mask: mask for mask write, 0 for not mask write @@ -110,4 +140,23 @@ struct aie_partition_req { /* AI engine partition IOCTL operations */ #define AIE_REG_IOCTL _IOWR(AIE_IOCTL_BASE, 0x8, \ struct aie_reg_args) +/** + * DOC: AIE_GET_MEM_IOCTL - enquire information of memories in the AI engine + * partition + * This ioctl is used to get the information of all the different types of + * memories in the AI engine partition. Application can get the memories + * information in two steps: + * 1. passing 0 as @num_mems in struct aie_mem_args to enquire the number of + * different memories in the partition, the value will be returned in + * @num_mems. + * 2. passing the number of memories in @num_mems and valid pointer as @mems of + * struct aie_mem_args to store the details information of different + * memories. The driver will create DMA buf for each type of memories, and + * will return the memory addressing information along with the DMA buf file + * descriptors in @mems. + * After getting the memories information, user can use mmap() with the DMA buf + * file descriptor to enable access the memories from userspace. + */ +#define AIE_GET_MEM_IOCTL _IOWR(AIE_IOCTL_BASE, 0x9, \ + struct aie_mem_args) #endif From patchwork Wed Nov 18 08:06:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiaying Liang X-Patchwork-Id: 327719 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B36E9C83008 for ; Wed, 18 Nov 2020 08:07:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5BDD32463F for ; Wed, 18 Nov 2020 08:07:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=xilinx.onmicrosoft.com header.i=@xilinx.onmicrosoft.com header.b="npwlq/qi" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727234AbgKRIG4 (ORCPT ); Wed, 18 Nov 2020 03:06:56 -0500 Received: from mail-co1nam11on2064.outbound.protection.outlook.com ([40.107.220.64]:52897 "EHLO NAM11-CO1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727201AbgKRIGz (ORCPT ); Wed, 18 Nov 2020 03:06:55 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=AkWOm7rJsvK9ULz/MjJflELAdb3skNJ9cZABY8qnI/DmkRfNyiegS1XJi/oCxfcX87sSCa4JGnuk7rRJDGwfqmRNNh6YDgQAHr4M41KfTlV+jlTodUsi8eIuWupMFGIEr23lkU/fMP0rE4OfRc+ifIjzX50ffzaSpm6EXyudyS+LDhi97b01Oe6mr3JyWB9Ix5oyjjHG6bGBXiwIt4KSdajKMDte+k5HF7CMpMFjxY+ybJBw4g98MaMswElKPG2pZdA2+ovXEmkmMakzyIvX5oAUogn8++N9Vf90Lyo+Htx8I50t0CaW1s0qaInj6pbhX3WIkBSF3ObXhJMfmrjV9Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WIoL8cL9tr8UQ3SJA8o91PY4KJ0/n3ZymvIvjTjleos=; b=H71dkd8t6m8Fm4d/nV8StGP7hFFY9TJZp1cbpl0N/twQrIFEv5hDViFaMZ/3saIEXq6RZ7Wxy7MSIT6bcFpBoZ/XSKRDTF2LHIYI6Y4vms/Mphdb4/N6X/pNuWqmjPED2mKe8Q5zhTHk+nUTanecFnzEkV6K+xdya/8ZkCZaHq4afWhXFLqu+RhOA8d6gcAJY32upsQw9NHAePIoqUExvyDgzT1mM4NdQ8OTioLYSxTPySVM0OLIIjOEhi1/7TirPjRDKh/p9MQHkzpK9OzdWAP2YYmms5cQm/m/CJIYQAWDD1QeRYctxBZ+I9B8Pe5VERnYbpv9NSLJGcvpuhwfyg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 149.199.62.198) smtp.rcpttodomain=kernel.org smtp.mailfrom=xilinx.com; dmarc=bestguesspass action=none header.from=xilinx.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xilinx.onmicrosoft.com; s=selector2-xilinx-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=WIoL8cL9tr8UQ3SJA8o91PY4KJ0/n3ZymvIvjTjleos=; b=npwlq/qiarBLHDI76o3bK8qmfrmH/MGqLJDPSYyDdmXnjR2qe2bCLZvynfMS11UgY7/63MDHjuKW0Psk4B0Oyo/lBdr3v/OmueX1je8e/8jm4PEavLik8TSEVjXaFrDEMEJW/7brrBlujVZRaREM0WyXRsx2qoXBoTmCnYgwN78= Received: from CY4PR22CA0060.namprd22.prod.outlook.com (2603:10b6:903:ae::22) by BN6PR02MB3250.namprd02.prod.outlook.com (2603:10b6:405:68::37) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3541.25; Wed, 18 Nov 2020 08:06:45 +0000 Received: from CY1NAM02FT052.eop-nam02.prod.protection.outlook.com (2603:10b6:903:ae:cafe::ac) by CY4PR22CA0060.outlook.office365.com (2603:10b6:903:ae::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3589.20 via Frontend Transport; Wed, 18 Nov 2020 08:06:45 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 149.199.62.198) smtp.mailfrom=xilinx.com; kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.62.198 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.62.198; helo=xsj-pvapexch02.xlnx.xilinx.com; Received: from xsj-pvapexch02.xlnx.xilinx.com (149.199.62.198) by CY1NAM02FT052.mail.protection.outlook.com (10.152.74.123) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.3564.22 via Frontend Transport; Wed, 18 Nov 2020 08:06:45 +0000 Received: from xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) by xsj-pvapexch02.xlnx.xilinx.com (172.19.86.41) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Wed, 18 Nov 2020 00:06:33 -0800 Received: from smtp.xilinx.com (172.19.127.95) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server id 15.1.1913.5 via Frontend Transport; Wed, 18 Nov 2020 00:06:33 -0800 Envelope-to: michal.simek@xilinx.com, derek.kiernan@xilinx.com, dragan.cvetic@xilinx.com, rajan.vaja@xilinx.com, tejas.patel@xilinx.com, manish.narani@xilinx.com, ravi.patel@xilinx.com, wendy.liang@xilinx.com, robh+dt@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, sumit.semwal@linaro.org, christian.koenig@amd.com, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org Received: from [172.19.2.167] (port=35936 helo=xsjjliang50.xilinx.com) by smtp.xilinx.com with esmtp (Exim 4.90) (envelope-from ) id 1kfIU5-0000Nk-N6; Wed, 18 Nov 2020 00:06:33 -0800 From: Wendy Liang To: , , , , , , , , , , , CC: , , , , , , "Wendy Liang" Subject: [PATCH 5/9] misc: xilinx-ai-engine: add setting shim dma bd operation Date: Wed, 18 Nov 2020 00:06:16 -0800 Message-ID: <1605686780-17886-6-git-send-email-wendy.liang@xilinx.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> References: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 537c6a7d-9a05-459a-3dda-08d88b98e42d X-MS-TrafficTypeDiagnostic: BN6PR02MB3250: X-Microsoft-Antispam-PRVS: X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-MS-Oob-TLC-OOBClassifiers: OLM:93; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: LJ2ZVuaaDIDTboaSs6dLoyRwkZsjf8Tj49MdNXesA6PQtB5giglJNczkVnax4I75cH5znI4Knv9rugaWBQYEOiOcwK7p4RnksXshygioGeEAidjc8qPsA4jkHDAAmr3Wud2/4ObXJvPAkQaGs8bwn9LRRp80Hx7ouLogPrDxcfThG7+0xJE9VuqxRwRycu8GZBKCxkr8ZhYvnAPI7nO6PwDcFeUgKA4iY8VSMGxWMHrTK8nQGodMyNhaUQyzLE6YPXb0baLTeLmicSGoUEjVrxCPAuKMUnic++hKxFhQARY8sQYEYtrAP7hhfxshSArgW3skOE4UR/11vpR0/HAmx/3u/XNPWXEScb2zzMffTPy9eTh/wWcIq+K+HT7UckKKAOOdY/V4+1nH7+6UgLNJB6IPlPx3wF6ImUdgYpxMC0lwuBtnCQeVTEoy+6iNbdkM X-Forefront-Antispam-Report: CIP:149.199.62.198; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:xsj-pvapexch02.xlnx.xilinx.com; PTR:unknown-62-198.xilinx.com; CAT:NONE; SFS:(4636009)(396003)(346002)(376002)(136003)(39850400004)(46966005)(36756003)(70206006)(44832011)(70586007)(6666004)(7416002)(7696005)(8676002)(478600001)(4326008)(426003)(8936002)(30864003)(47076004)(9786002)(336012)(82740400003)(36906005)(2616005)(316002)(26005)(2906002)(6636002)(921005)(82310400003)(110136005)(7636003)(83380400001)(5660300002)(54906003)(356005)(107886003)(186003)(102446001); DIR:OUT; SFP:1101; X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Nov 2020 08:06:45.1765 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 537c6a7d-9a05-459a-3dda-08d88b98e42d X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.62.198]; Helo=[xsj-pvapexch02.xlnx.xilinx.com] X-MS-Exchange-CrossTenant-AuthSource: CY1NAM02FT052.eop-nam02.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BN6PR02MB3250 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add operation to set SHIM DMA buffer descriptor. The following operations are added to set the buffer descriptors: * attach DMA buffer which enables AI engine device to access the DMA buffer memory * detach DMA buffer which disables AI engine device to access the DMA buffer memory * set DMA buffer descriptor, which takes buffer descriptor contents pointer, the dmabuf fd, and the offset to the start of dmabuf as as argument. It validates the dmabuf and the offset and size of the buffer. And then it calculates the DMA address of the buffer and set the buffer descriptor content to the hardware DMA buffer descriptor. The main logic to control what's go into the buffer descriptor and which buffer descriptor to use is in the userspace AI engine library. Signed-off-by: Wendy Liang Reviewed-by: Hyun Kwon --- drivers/misc/xilinx-ai-engine/Makefile | 1 + drivers/misc/xilinx-ai-engine/ai-engine-aie.c | 19 + drivers/misc/xilinx-ai-engine/ai-engine-dma.c | 481 +++++++++++++++++++++ drivers/misc/xilinx-ai-engine/ai-engine-internal.h | 45 ++ drivers/misc/xilinx-ai-engine/ai-engine-part.c | 17 + include/uapi/linux/xlnx-ai-engine.h | 43 ++ 6 files changed, 606 insertions(+) create mode 100644 drivers/misc/xilinx-ai-engine/ai-engine-dma.c diff --git a/drivers/misc/xilinx-ai-engine/Makefile b/drivers/misc/xilinx-ai-engine/Makefile index 2dbed42..1b743fa 100644 --- a/drivers/misc/xilinx-ai-engine/Makefile +++ b/drivers/misc/xilinx-ai-engine/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_XILINX_AIE) += xilinx-aie.o xilinx-aie-$(CONFIG_XILINX_AIE) := ai-engine-aie.o \ ai-engine-dev.o \ + ai-engine-dma.o \ ai-engine-mem.o \ ai-engine-part.o \ ai-engine-res.o \ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c index 7fce2f00..19c262d 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-aie.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-aie.c @@ -107,6 +107,24 @@ static const struct aie_single_reg_field aie_col_clkbuf = { .regoff = AIE_SHIMPL_CLKCNTR_REGOFF, }; +static const struct aie_dma_attr aie_shimdma = { + .laddr = { + .mask = 0xffffffffU, + .regoff = 0U, + }, + .haddr = { + .mask = 0xffff0000U, + .regoff = 0x8U, + }, + .buflen = { + .mask = 0xffffffffU, + .regoff = 0x4U, + }, + .bd_regoff = 0x0001d000U, + .num_bds = 16, + .bd_len = 0x14U, +}; + static u32 aie_get_tile_type(struct aie_location *loc) { if (loc->row) @@ -232,6 +250,7 @@ int aie_device_init(struct aie_device *adev) adev->kernel_regs = aie_kernel_regs; adev->col_rst = &aie_col_rst; adev->col_clkbuf = &aie_col_clkbuf; + adev->shim_dma = &aiev1_shimdma; /* Get the columns resource */ /* Get number of columns from AI engine memory resource */ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-dma.c b/drivers/misc/xilinx-ai-engine/ai-engine-dma.c new file mode 100644 index 0000000..007bec4 --- /dev/null +++ b/drivers/misc/xilinx-ai-engine/ai-engine-dma.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AI Engine driver DMA implementation + * + * Copyright (C) 2020 Xilinx, Inc. + */ + +#include "ai-engine-internal.h" +#include +#include +#include +#include +#include +#include +#include + +/** + * struct aie_dmabuf - AI engine dmabuf information + * @attach: dmabuf attachment pointer + * @sgt: scatter/gather table + * @refs: refcount of the attached aie_dmabuf + * @node: list node + */ +struct aie_dmabuf { + struct dma_buf_attachment *attach; + struct sg_table *sgt; + refcount_t refs; + struct list_head node; +}; + +/** + * aie_part_find_dmabuf() - find a attached dmabuf + * @apart: AI engine partition + * @dmabuf: pointer to dmabuf + * @return: pointer to AI engine dmabuf struct of the found dmabuf, if dmabuf + * is not found, returns NULL. + * + * This function scans all the attached dmabufs to see the input dmabuf is + * in the list. if it is attached, return the corresponding struct aie_dmabuf + * pointer. + */ +static struct aie_dmabuf * +aie_part_find_dmabuf(struct aie_partition *apart, struct dma_buf *dmabuf) +{ + struct aie_dmabuf *adbuf; + + list_for_each_entry(adbuf, &apart->dbufs, node) { + if (dmabuf == adbuf->attach->dmabuf) + return adbuf; + } + + return NULL; +} + +/** + * aie_part_get_dmabuf_da_from_off() - get DMA address from offset to a dmabuf + * @apart: AI engine partition + * @dmabuf_fd: dmabuf file descriptor + * @off: offset to the start of a dmabuf + * @len: memory length + * @return: dma address, or 0 if @off or @len is invalid, or if @dmabuf_fd is + * not attached. + * + * This function returns DMA address if has been mapped to a dmabuf which has + * been attached to the AI engine partition. + */ +static dma_addr_t +aie_part_get_dmabuf_da_from_off(struct aie_partition *apart, int dmabuf_fd, + u64 off, size_t len) +{ + struct dma_buf *dbuf = dma_buf_get(dmabuf_fd); + struct aie_dmabuf *adbuf; + + if (IS_ERR(dbuf)) { + dev_err(&apart->dev, + "failed to get dma address, not able to get dmabuf from %d.\n", + dmabuf_fd); + return 0; + } + + adbuf = aie_part_find_dmabuf(apart, dbuf); + dma_buf_put(dbuf); + if (!adbuf) { + dev_err(&apart->dev, + "failed to get dma address, dmabuf %d not attached.\n", + dmabuf_fd); + return 0; + } + + if (off >= dbuf->size || off + len >= dbuf->size) { + dev_err(&apart->dev, + "failed to get dma address from buf %d, off=0x%llx, len=0x%zx.\n", + dmabuf_fd, off, len); + return 0; + } + + return sg_dma_address(adbuf->sgt->sgl) + off; +} + +/** + * aie_part_set_shimdma_bd() - Set the buffer descriptor to AI engine partition + * hardware + * @apart: AI engine partition + * @loc: AI engine tile location + * @bd_id: buffer descriptor ID + * @bd: pointer buffer descriptor content + * @return: 0 for success, negative value for failure + * + * This function sets the specified buffer descriptor content to the + * specified buffer descriptor in the specified AI engine SHIM NOC tile. + */ +static int aie_part_set_shimdma_bd(struct aie_partition *apart, + struct aie_location loc, u32 bd_id, u32 *bd) +{ + const struct aie_dma_attr *shim_dma = apart->adev->shim_dma; + struct aie_location loc_adjust; + u32 i, regoff, intile_regoff; + + intile_regoff = shim_dma->bd_regoff + shim_dma->bd_len * bd_id; + loc_adjust.col = loc.col + apart->range.start.col; + loc_adjust.row = loc.row + apart->range.start.row; + regoff = aie_cal_regoff(apart->adev, loc_adjust, intile_regoff); + + for (i = 0; i < shim_dma->bd_len / (sizeof(*bd)); + i++, regoff += sizeof(*bd)) + iowrite32(bd[i], apart->adev->base + regoff); + return 0; +} + +/** + * aie_part_validate_bdloc() - Validate SHIM DMA buffer descriptor location + * @apart: AI engine partition + * @loc: tile location + * @bd_id: buffer descriptor id + * + * @return: 0 for success, negative value for failure + * + * This function validate the SHIM DMA buffer descriptor base address. + */ +static int aie_part_validate_bdloc(struct aie_partition *apart, + struct aie_location loc, u32 bd_id) +{ + const struct aie_dma_attr *shim_dma = apart->adev->shim_dma; + struct aie_location loc_adjust; + u32 ttype; + + loc_adjust.col = loc.col + apart->range.start.col; + loc_adjust.row = loc.row + apart->range.start.row; + + if (aie_validate_location(apart, loc_adjust) < 0) { + dev_err(&apart->dev, + "invalid loc (%u,%u) in (%u,%u).\n", + loc.col, loc.row, + apart->range.size.col, apart->range.size.row); + return -EINVAL; + } + + ttype = apart->adev->ops->get_tile_type(&loc_adjust); + if (ttype != AIE_TILE_TYPE_SHIMNOC) { + dev_err(&apart->dev, + "failed to set bd, (%u,%u) is not SHIM NOC\n", + loc.col, loc.row); + return -EINVAL; + } + + if (bd_id >= shim_dma->num_bds) { + dev_err(&apart->dev, + "invalid SHIM DMA bd id: %u.\n", bd_id); + return -EINVAL; + } + + return 0; +} + +/** + * aie_part_attach_dmabuf() - Attach dmabuf to an AI engine + * @apart: AI engine partition + * @dbuf: pointer to the DMA buffer to attach + * @return: pointer to AI engine dmabuf structure for success, or error value + * for failure + * + * This function attaches a dmabuf to the specified AI engine partition. + */ +static struct aie_dmabuf *aie_part_attach_dmabuf(struct aie_partition *apart, + struct dma_buf *dbuf) +{ + struct aie_dmabuf *adbuf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; + + attach = dma_buf_attach(dbuf, &apart->dev); + if (IS_ERR(attach)) { + dev_err(&apart->dev, "failed to attach dmabuf\n"); + return ERR_CAST(attach); + } + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + dev_err(&apart->dev, "failed to map dmabuf attachment\n"); + dma_buf_detach(dbuf, attach); + return ERR_CAST(sgt); + } + + if (sgt->nents != 1) { + dma_addr_t next_sg_addr = sg_dma_address(sgt->sgl); + struct scatterlist *s; + unsigned int i; + + for_each_sg(sgt->sgl, s, sgt->nents, i) { + if (sg_dma_address(s) != next_sg_addr) { + dev_err(&apart->dev, + "dmabuf not contiguous\n"); + dma_buf_unmap_attachment(attach, sgt, + attach->dir); + dma_buf_detach(dbuf, attach); + return ERR_PTR(-EINVAL); + } + + next_sg_addr = sg_dma_address(s) + sg_dma_len(s); + } + } + + adbuf = devm_kzalloc(&apart->dev, sizeof(*adbuf), GFP_KERNEL); + if (!adbuf) { + dma_buf_unmap_attachment(attach, sgt, attach->dir); + dma_buf_detach(dbuf, attach); + return ERR_PTR(-ENOMEM); + } + + adbuf->attach = attach; + /* + * dmabuf attachment doesn't always include the sgt, store it in + * AI engine dma buf structure. + */ + adbuf->sgt = sgt; + + refcount_set(&adbuf->refs, 1); + + list_add(&adbuf->node, &apart->dbufs); + return adbuf; +} + +/** + * aie_part_dmabuf_attach_get() - Get reference to an dmabuf attachment + * @adbuf: AI engine partition attached dmabuf + * + * This call will increase the reference count by 1 + */ +static void aie_part_dmabuf_attach_get(struct aie_dmabuf *adbuf) +{ + refcount_inc(&adbuf->refs); +} + +/** + * aie_part_dmabuf_attach_put() - Put reference to an dmabuf attachment + * @adbuf: AI engine partition attached dmabuf + * + * This call will decrease the reference count by 1. If the refcount reaches + * 0, it will detach the dmabuf. + */ +static void aie_part_dmabuf_attach_put(struct aie_dmabuf *adbuf) +{ + struct dma_buf *dbuf; + + if (!refcount_dec_and_test(&adbuf->refs)) + return; + + dbuf = adbuf->attach->dmabuf; + dma_buf_unmap_attachment(adbuf->attach, adbuf->sgt, adbuf->attach->dir); + dma_buf_detach(dbuf, adbuf->attach); + dma_buf_put(dbuf); + list_del(&adbuf->node); +} + +/** + * aie_part_release_dmabufs() - detach all the attached dmabufs from partition + * @apart: AI engine partition + */ +void aie_part_release_dmabufs(struct aie_partition *apart) +{ + struct aie_dmabuf *adbuf, *tmpadbuf; + + list_for_each_entry_safe(adbuf, tmpadbuf, &apart->dbufs, node) { + struct dma_buf *dbuf = adbuf->attach->dmabuf; + + dma_buf_unmap_attachment(adbuf->attach, adbuf->sgt, + adbuf->attach->dir); + dma_buf_detach(dbuf, adbuf->attach); + dma_buf_put(dbuf); + list_del(&adbuf->node); + devm_kfree(&apart->dev, adbuf); + } +} + +/** + * aie_part_attach_dmabuf_req() - Handle attaching dmabuf to an AI engine + * partition request + * @apart: AI engine partition + * @user_args: user AI engine dmabuf argument + * + * @return: 0 for success, negative value for failure + * + * This function attaches a dmabuf to the specified AI engine partition and map + * the attachment. It checks if the dmabuf is already attached, if it is not + * attached, attach it. It returns the number of entries of the attachment to + * the AI engine dmabuf user argument. If user wants to know the sg list, it + * can use AI engine get sg ioctl. + */ +long aie_part_attach_dmabuf_req(struct aie_partition *apart, + void __user *user_args) +{ + struct aie_dmabuf *adbuf; + struct dma_buf *dbuf; + long ret; + int dmabuf_fd = (int)(uintptr_t)user_args; + + dbuf = dma_buf_get(dmabuf_fd); + if (IS_ERR(dbuf)) { + dev_err(&apart->dev, "failed to get dmabuf from %d.\n", + dmabuf_fd); + return PTR_ERR(dbuf); + } + + ret = mutex_lock_interruptible(&apart->mlock); + if (ret) { + dma_buf_put(dbuf); + return ret; + } + + adbuf = aie_part_find_dmabuf(apart, dbuf); + if (!adbuf) + adbuf = aie_part_attach_dmabuf(apart, dbuf); + else + aie_part_dmabuf_attach_get(adbuf); + + mutex_unlock(&apart->mlock); + + if (IS_ERR(adbuf)) { + dev_err(&apart->dev, "failed to attach dmabuf\n"); + dma_buf_put(dbuf); + return PTR_ERR(adbuf); + } + + return 0; +} + +/** + * aie_part_detach_dmabuf_req() - Handle detaching dmabuf from an AI engine + * partition request + * @apart: AI engine partition + * @user_args: user AI engine dmabuf argument + * + * @return: 0 for success, negative value for failure + * + * This function unmaps and detaches a dmabuf from the specified AI engine + * partition. + */ +long aie_part_detach_dmabuf_req(struct aie_partition *apart, + void __user *user_args) +{ + int dmabuf_fd; + struct dma_buf *dbuf; + struct aie_dmabuf *adbuf; + int ret; + + dmabuf_fd = (int)(uintptr_t)user_args; + + dbuf = dma_buf_get(dmabuf_fd); + if (IS_ERR(dbuf)) { + dev_err(&apart->dev, "failed to get dmabuf %d.\n", dmabuf_fd); + return PTR_ERR(dbuf); + } + + ret = mutex_lock_interruptible(&apart->mlock); + if (ret) { + dma_buf_put(dbuf); + return ret; + } + + adbuf = aie_part_find_dmabuf(apart, dbuf); + dma_buf_put(dbuf); + if (!adbuf) { + dev_err(&apart->dev, "failed to find dmabuf %d.\n", dmabuf_fd); + mutex_unlock(&apart->mlock); + return -EINVAL; + } + + aie_part_dmabuf_attach_put(adbuf); + + mutex_unlock(&apart->mlock); + + return 0; +} + +/** + * aie_part_set_dmabuf_bd() - Set AI engine SHIM DMA dmabuf buffer descriptor + * @apart: AI engine partition + * @user_args: user AI engine dmabuf argument + * + * @return: 0 for success, negative value for failure + * + * This function set the user specified buffer descriptor into the SHIM DMA + * buffer descriptor. The buffer descriptor contained in the @user_args has the + * offset to the start of the buffer descriptor. + */ +long aie_part_set_dmabuf_bd(struct aie_partition *apart, + void __user *user_args) +{ + struct aie_device *adev = apart->adev; + const struct aie_dma_attr *shim_dma = adev->shim_dma; + struct aie_dmabuf_bd_args args; + u32 *bd, *tmpbd, len, laddr, haddr, regval; + u64 off; + dma_addr_t addr; + int ret; + + if (copy_from_user(&args, user_args, sizeof(args))) + return -EFAULT; + + ret = aie_part_validate_bdloc(apart, args.loc, args.bd_id); + if (ret) { + dev_err(&apart->dev, "invalid SHIM DMA BD reg address.\n"); + return -EINVAL; + } + + bd = memdup_user((void __user *)args.bd, shim_dma->bd_len); + if (IS_ERR(bd)) + return PTR_ERR(bd); + + regval = bd[shim_dma->buflen.regoff / sizeof(u32)]; + len = aie_get_reg_field(&shim_dma->buflen, regval); + if (!len) { + dev_err(&apart->dev, "no buf length from shim dma bd.\n"); + kfree(bd); + return -EINVAL; + } + + /* Get low 32bit address offset */ + tmpbd = (u32 *)((char *)bd + shim_dma->laddr.regoff); + laddr = *tmpbd & shim_dma->laddr.mask; + /* Get high 32bit address offset */ + tmpbd = (u32 *)((char *)bd + shim_dma->haddr.regoff); + haddr = *tmpbd & shim_dma->haddr.mask; + off = laddr | ((u64)haddr << 32); + + ret = mutex_lock_interruptible(&apart->mlock); + if (ret) { + kfree(bd); + return ret; + } + + /* Get device address from offset */ + addr = aie_part_get_dmabuf_da_from_off(apart, args.buf_fd, off, len); + if (!addr) { + dev_err(&apart->dev, "invalid buffer 0x%llx, 0x%x.\n", + off, len); + mutex_unlock(&apart->mlock); + kfree(bd); + return -EINVAL; + } + + /* Set low 32bit address */ + laddr = lower_32_bits(addr); + tmpbd = (u32 *)((char *)bd + shim_dma->laddr.regoff); + *tmpbd &= ~shim_dma->laddr.mask; + *tmpbd |= aie_get_field_val(&shim_dma->laddr, laddr); + + /* Set high 32bit address */ + haddr = upper_32_bits(addr); + tmpbd = (u32 *)((char *)bd + shim_dma->haddr.regoff); + *tmpbd &= ~shim_dma->haddr.mask; + *tmpbd |= aie_get_field_val(&shim_dma->haddr, haddr); + + ret = aie_part_set_shimdma_bd(apart, args.loc, args.bd_id, bd); + mutex_unlock(&apart->mlock); + if (ret) + dev_err(&apart->dev, "failed to set to shim dma bd.\n"); + + kfree(bd); + return ret; +} diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h index e84610b..bf3a09c 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-internal.h +++ b/drivers/misc/xilinx-ai-engine/ai-engine-internal.h @@ -90,6 +90,24 @@ struct aie_part_mem { }; /** + * struct aie_dma_attr - AI engine DMA attributes structure + * @laddr: low address field attributes + * @haddr: high address field attributes + * @buflen: buffer length field attributes + * @bd_regoff: SHIM DMA buffer descriptors register offset + * @num_bds: number of buffer descriptors + * @bd_len: length of a buffer descriptor in bytes + */ +struct aie_dma_attr { + struct aie_single_reg_field laddr; + struct aie_single_reg_field haddr; + struct aie_single_reg_field buflen; + u32 bd_regoff; + u32 num_bds; + u32 bd_len; +}; + +/** * struct aie_tile_operations - AI engine device operations * @get_tile_type: get type of tile based on tile operation * @get_mem_info: get different types of memories information @@ -127,6 +145,7 @@ struct aie_resource { * @ops: tile operations * @col_rst: column reset attribute * @col_clkbuf: column clock buffer attribute + * @shim_dma: SHIM DMA attribute * @size: size of the AI engine address space * @array_shift: array address shift * @col_shift: column address shift @@ -147,6 +166,7 @@ struct aie_device { const struct aie_tile_operations *ops; const struct aie_single_reg_field *col_rst; const struct aie_single_reg_field *col_clkbuf; + const struct aie_dma_attr *shim_dma; size_t size; struct aie_resource cols_res; u32 array_shift; @@ -159,6 +179,7 @@ struct aie_device { /** * struct aie_partition - AI engine partition structure * @node: list node + * @dbufs: dmabufs list * @adev: pointer to AI device instance * @pmems: pointer to partition memories types * @range: range of partition @@ -172,6 +193,7 @@ struct aie_device { */ struct aie_partition { struct list_head node; + struct list_head dbufs; struct aie_device *adev; struct aie_part_mem *pmems; struct aie_range range; @@ -229,6 +251,20 @@ static inline u32 aie_get_field_val(const struct aie_single_reg_field *field, } /** + * aie_get_reg_field() - get value from a field from a register valuer + * @field: a field in a register + * @regval: register value + * @return: value of a register field + */ +static inline u32 aie_get_reg_field(const struct aie_single_reg_field *field, + u32 regval) +{ + long long mask64 = (long long)field->mask & 0x00000000ffffffff; + + return (regval & field->mask) >> __bf_shf(mask64); +} + +/** * aie_cal_regoff() - calculate register offset to the whole AI engine * device start address * @adev: AI engine device @@ -286,5 +322,14 @@ int aie_part_clean(struct aie_partition *apart); int aie_mem_get_info(struct aie_partition *apart, unsigned long arg); +long aie_part_attach_dmabuf_req(struct aie_partition *apart, + void __user *user_args); +long aie_part_detach_dmabuf_req(struct aie_partition *apart, + void __user *user_args); +long aie_part_set_bd(struct aie_partition *apart, void __user *user_args); +long aie_part_set_dmabuf_bd(struct aie_partition *apart, + void __user *user_args); +void aie_part_release_dmabufs(struct aie_partition *apart); + int aie_device_init(struct aie_device *adev); #endif /* AIE_INTERNAL_H */ diff --git a/drivers/misc/xilinx-ai-engine/ai-engine-part.c b/drivers/misc/xilinx-ai-engine/ai-engine-part.c index 4be6d38..dcfb9ec 100644 --- a/drivers/misc/xilinx-ai-engine/ai-engine-part.c +++ b/drivers/misc/xilinx-ai-engine/ai-engine-part.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -221,6 +222,7 @@ static int aie_part_release(struct inode *inode, struct file *filp) if (ret) return ret; + aie_part_release_dmabufs(apart); aie_part_clean(apart); apart->status = 0; @@ -296,6 +298,12 @@ static long aie_part_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) } case AIE_GET_MEM_IOCTL: return aie_mem_get_info(apart, arg); + case AIE_ATTACH_DMABUF_IOCTL: + return aie_part_attach_dmabuf_req(apart, argp); + case AIE_DETACH_DMABUF_IOCTL: + return aie_part_detach_dmabuf_req(apart, argp); + case AIE_SET_SHIMDMA_DMABUF_BD_IOCTL: + return aie_part_set_dmabuf_bd(apart, argp); default: dev_err(&apart->dev, "Invalid ioctl command %u.\n", cmd); ret = -EINVAL; @@ -422,6 +430,7 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev, return ERR_PTR(-ENOMEM); apart->adev = adev; + INIT_LIST_HEAD(&apart->dbufs); memcpy(&apart->range, range, sizeof(*range)); mutex_init(&apart->mlock); @@ -443,6 +452,10 @@ static struct aie_partition *aie_create_partition(struct aie_device *adev, return ERR_PTR(ret); } + /* Set up the DMA mask */ + dev->coherent_dma_mask = DMA_BIT_MASK(48); + dev->dma_mask = &dev->coherent_dma_mask; + /* * Create array to keep the information of the different types of tile * memories information of the AI engine partition. @@ -521,6 +534,10 @@ of_aie_part_probe(struct aie_device *adev, struct device_node *nc) apart->dev.of_node = nc; apart->partition_id = partition_id; + ret = of_dma_configure(&apart->dev, nc, true); + if (ret) + dev_warn(&apart->dev, "Failed to configure DMA.\n"); + dev_info(&adev->dev, "AI engine part(%u,%u),(%u,%u), id %u is probed successfully.\n", range.start.col, range.start.row, diff --git a/include/uapi/linux/xlnx-ai-engine.h b/include/uapi/linux/xlnx-ai-engine.h index 5e40d00..9080f57 100644 --- a/include/uapi/linux/xlnx-ai-engine.h +++ b/include/uapi/linux/xlnx-ai-engine.h @@ -129,6 +129,21 @@ struct aie_partition_req { __u32 flag; }; +/** + * struct aie_dmabuf_bd_args - AIE dmabuf buffer descriptor information + * @bd: DMA buffer descriptor, within the buffer descriptor, the address field + * will be the offset to the start of the dmabuf + * @buf_fd: DMA buffer handler which is dmabuf file descriptor + * @loc: Tile location relative to the start of a partition + * @bd_id: buffer descriptor id + */ +struct aie_dmabuf_bd_args { + __u32 *bd; + struct aie_location loc; + int buf_fd; + __u32 bd_id; +}; + #define AIE_IOCTL_BASE 'A' /* AI engine device IOCTL operations */ @@ -159,4 +174,32 @@ struct aie_partition_req { */ #define AIE_GET_MEM_IOCTL _IOWR(AIE_IOCTL_BASE, 0x9, \ struct aie_mem_args) +/** + * DOC: AIE_ATTACH_DMABUF_IOCTL - attach a dmabuf to AI engine partition + * + * This ioctl is used to attach a dmabuf to the AI engine partition. AI engine + * partition will return the number of scatter gather list elements of the + * dmabuf. + */ +#define AIE_ATTACH_DMABUF_IOCTL _IOR(AIE_IOCTL_BASE, 0xa, int) + +/** + * DOC: AIE_DETACH_DMABUF_IOCTL - dettach a dmabuf from AI engine partition + * + * This ioctl is used to detach a dmabuf from the AI engine partition + */ +#define AIE_DETACH_DMABUF_IOCTL _IOR(AIE_IOCTL_BASE, 0xb, int) + +/** + * DOC: AIE_SET_SHIMDMA_DMABUF_BD_IOCTL - set buffer descriptor which contains + * dmabuf to SHIM DMA + * + * This ioctl is used to set the buffer descriptor to SHIM DMA. The + * aie_dmabuf_bd_args contains the dmabuf fd and the buffer descriptor contents. + * The address field in the buffer descriptor contents should be the offset to + * the start of the dmabuf. + */ +#define AIE_SET_SHIMDMA_DMABUF_BD_IOCTL _IOW(AIE_IOCTL_BASE, 0x10, \ + struct aie_dmabuf_bd_args) + #endif From patchwork Wed Nov 18 08:06:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiaying Liang X-Patchwork-Id: 327718 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 06565C56202 for ; Wed, 18 Nov 2020 08:07:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 83CA6223C7 for ; Wed, 18 Nov 2020 08:07:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=xilinx.onmicrosoft.com header.i=@xilinx.onmicrosoft.com header.b="ryUmlwrD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727301AbgKRIHF (ORCPT ); Wed, 18 Nov 2020 03:07:05 -0500 Received: from mail-dm6nam11on2053.outbound.protection.outlook.com ([40.107.223.53]:30489 "EHLO NAM11-DM6-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727268AbgKRIHC (ORCPT ); Wed, 18 Nov 2020 03:07:02 -0500 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=jo8vCK768z4ZUxVfmQEjA30DvxcMUOHLdbab2LbhVKrx0J/xPVh3Dk0LvE/LIR0cUZhNkomN2kko2W1U34o1uqghay2zQOtweKFz/pf+RGtX8GRUdPvFT8xTvkEO5O7ccRLth/zTjLHULF79bRsyi4aN+VjIFI2wvUurajpLWSYXJQFaiI81OI36k84QUrTP9KJK/ItqaEbYnbr/nkoXBcrhm9kTlKYW7trubBCPuVN/KzEjVJjxnAnXkQx+N2l/ocY1QEPA8btC+KsgNckDfMRvsDHJHsyBGA3tyPitaB6aYB8fLEGtJ4jxsHRRA0GzQ1v1QDiv/QEJZvhKlH8AVQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=sgFPcwVcdVDA1lISQdnpSALC+Om4hmyoTqUgCLjEox0=; b=j0ein2QxcpkUCi/sLzSIbeIxJUw6zRlh2D4TcuUV4ocjQWWfbf0yOEnME7o8xMedrje/oGss6TXJHsiGEwtrC7x/pdCjKDc9vUKhx9bCYg0rO28d9awPD8oPjPmK0o+IM+T2rnHoW6w+5oI7Cl3lzQ6TFWpUqJp6M73GaeYMYc4+YMB4BcjO8H3i9FUJQq3jUD4pLO8d2VKa5dyaClSxTBvWa10v+7GjiHkY673lnSK57D2S+xCeWQskQvzvBl4VlQU5pilucQ55JU21JWUjIsZoNlY0Y25U/0nmD/LMUUSbp+22jYXrpTkT4CZYnD4Fjk8hOp+0EFOdtG7//0mLGg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 149.199.62.198) smtp.rcpttodomain=kernel.org smtp.mailfrom=xilinx.com; dmarc=bestguesspass action=none header.from=xilinx.com; dkim=none (message not signed); arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xilinx.onmicrosoft.com; s=selector2-xilinx-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=sgFPcwVcdVDA1lISQdnpSALC+Om4hmyoTqUgCLjEox0=; b=ryUmlwrDxOryq4RpmVoRCAFocFEESG4PAEQVL+TQ9bai5z1aG0RSsogvhaU9GDGeBHSpFfRcR2sdrAnMrJmumu4vP0t1f5tbJUkgKyps/i8MmMQbEQzUvnVqrH1GMsVSmglrX9NBGEC00TCM0prgXEX1xE+X5WmUO73U0fGtOtQ= Received: from SA0PR11CA0092.namprd11.prod.outlook.com (2603:10b6:806:d1::7) by DM6PR02MB4265.namprd02.prod.outlook.com (2603:10b6:5:2e::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3564.25; Wed, 18 Nov 2020 08:06:58 +0000 Received: from SN1NAM02FT048.eop-nam02.prod.protection.outlook.com (2603:10b6:806:d1:cafe::68) by SA0PR11CA0092.outlook.office365.com (2603:10b6:806:d1::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3589.20 via Frontend Transport; Wed, 18 Nov 2020 08:06:57 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 149.199.62.198) smtp.mailfrom=xilinx.com; kernel.org; dkim=none (message not signed) header.d=none;kernel.org; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.62.198 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.62.198; helo=xsj-pvapexch01.xlnx.xilinx.com; Received: from xsj-pvapexch01.xlnx.xilinx.com (149.199.62.198) by SN1NAM02FT048.mail.protection.outlook.com (10.152.72.202) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.20.3564.22 via Frontend Transport; Wed, 18 Nov 2020 08:06:56 +0000 Received: from xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Wed, 18 Nov 2020 00:06:33 -0800 Received: from smtp.xilinx.com (172.19.127.95) by xsj-pvapexch01.xlnx.xilinx.com (172.19.86.40) with Microsoft SMTP Server id 15.1.1913.5 via Frontend Transport; Wed, 18 Nov 2020 00:06:33 -0800 Envelope-to: michal.simek@xilinx.com, derek.kiernan@xilinx.com, dragan.cvetic@xilinx.com, rajan.vaja@xilinx.com, tejas.patel@xilinx.com, manish.narani@xilinx.com, ravi.patel@xilinx.com, izhar.ameer.shaikh@xilinx.com, wendy.liang@xilinx.com, robh+dt@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, sumit.semwal@linaro.org, christian.koenig@amd.com, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org Received: from [172.19.2.167] (port=35936 helo=xsjjliang50.xilinx.com) by smtp.xilinx.com with esmtp (Exim 4.90) (envelope-from ) id 1kfIU5-0000Nk-QS; Wed, 18 Nov 2020 00:06:33 -0800 From: Wendy Liang To: , , , , , , , , , , , CC: , , , , , , "Izhar Ameer Shaikh" , Wendy Liang Subject: [PATCH 8/9] firmware: xilinx: Add IOCTL support for AIE ISR Clear Date: Wed, 18 Nov 2020 00:06:19 -0800 Message-ID: <1605686780-17886-9-git-send-email-wendy.liang@xilinx.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> References: <1605686780-17886-1-git-send-email-wendy.liang@xilinx.com> MIME-Version: 1.0 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: e10d6275-e360-4838-29da-08d88b98eb2b X-MS-TrafficTypeDiagnostic: DM6PR02MB4265: X-Microsoft-Antispam-PRVS: X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-MS-Oob-TLC-OOBClassifiers: OLM:7219; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 6kVZJpBpGhGhZo8B/GKzgyHgMIJRRDFAKJCGHyhVEYgTaTTQa8BdogEueB4plsvyN1vtKO4i94lYpPLivUCKs0F/V/ZJ8Ok81atjaqcCJlq8YLNM3SLVL8PjkmRZhRUPTk6TzDknFd4ruua91FCZ1FcMyEKM2mNQkdFXeJObuHFXbLO6vO+lmlwNqNwbvc6bJQ33a2RSOKLT/Thex3S6jrYRTxEx2bAT58tCtywlxjSHDqv86xrzgzN2lkW+vo7sRmLFqMcK1c5pA1ylONKzkorgA/XMnaG8kZQ/dnkufUZZyOrV74oG3UnSqdOyTpDKp6Bcfd4cVTpv/9ZND8PhO4sWlcVupFUgcX1vynvolBFjAE5AVJijPzdNXNCEhLwKvq3LOzlbodWcEcKvpUVYoRwVD6cz2Y5v0v8IGBtcAgz9chO0ojYZbe7yn3ezT3J/ X-Forefront-Antispam-Report: CIP:149.199.62.198; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:xsj-pvapexch01.xlnx.xilinx.com; PTR:unknown-62-198.xilinx.com; CAT:NONE; SFS:(4636009)(136003)(376002)(39850400004)(396003)(346002)(46966005)(426003)(2616005)(82310400003)(44832011)(54906003)(47076004)(5660300002)(7696005)(36906005)(316002)(478600001)(110136005)(336012)(6666004)(921005)(2906002)(26005)(83380400001)(186003)(36756003)(82740400003)(4326008)(8936002)(7416002)(7636003)(6636002)(9786002)(8676002)(70206006)(356005)(70586007)(107886003)(102446001); DIR:OUT; SFP:1101; X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Nov 2020 08:06:56.8889 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: e10d6275-e360-4838-29da-08d88b98eb2b X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.62.198]; Helo=[xsj-pvapexch01.xlnx.xilinx.com] X-MS-Exchange-CrossTenant-AuthSource: SN1NAM02FT048.eop-nam02.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM6PR02MB4265 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Izhar Ameer Shaikh Latching of AIE NPI Interrupts is present in Versal ES1 Silicon Rev, however it has been removed from ES2 rev. As a result on ES1, in order to use the interrupt, a client needs to request PMC to clear/ack the interrupt. Provide an EEMI IOCTL to serve the same purpose. Note that, this will only be applicable for ES1 rev. For ES2 and other non-silicon platforms, this call will essentially be a NOP in the firmware. Signed-off-by: Izhar Ameer Shaikh Signed-off-by: Wendy Liang --- drivers/firmware/xilinx/zynqmp.c | 14 ++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index efb8a66..7a0c6a3 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -702,6 +702,20 @@ int zynqmp_pm_set_boot_health_status(u32 value) } /** + * zynqmp_pm_clear_aie_npi_isr - Clear AI engine NPI interrupt status register + * @node: AI engine node id + * @irq_mask: Mask of AI engine NPI interrupt bit to clear + * + * Return: Returns status, either success or error+reason + */ +int zynqmp_pm_clear_aie_npi_isr(u32 node, u32 irq_mask) +{ + return zynqmp_pm_invoke_fn(PM_IOCTL, node, IOCTL_AIE_ISR_CLEAR, + irq_mask, 0, NULL); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_clear_aie_npi_isr); + +/** * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release) * @reset: Reset to be configured * @assert_flag: Flag stating should reset be asserted (1) or diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 5968df8..b929d57 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -116,6 +116,8 @@ enum pm_ioctl_id { IOCTL_READ_PGGS = 15, /* Set healthy bit value */ IOCTL_SET_BOOT_HEALTH_STATUS = 17, + /* AI engine NPI ISR clear */ + IOCTL_AIE_ISR_CLEAR = 24, }; enum pm_query_id { @@ -357,6 +359,7 @@ int zynqmp_pm_write_pggs(u32 index, u32 value); int zynqmp_pm_read_pggs(u32 index, u32 *value); int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype); int zynqmp_pm_set_boot_health_status(u32 value); +int zynqmp_pm_clear_aie_npi_isr(u32 node, u32 irq_mask); #else static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) { @@ -507,6 +510,11 @@ static inline int zynqmp_pm_set_boot_health_status(u32 value) { return -ENODEV; } + +static int zynqmp_pm_clear_aie_npi_isr(u32 node, u32 irq_mask) +{ + return -ENODEV; +} #endif #endif /* __FIRMWARE_ZYNQMP_H__ */