From patchwork Tue Jun 4 14:27:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukas Funke X-Patchwork-Id: 1943429 X-Patchwork-Delegate: monstr@monstr.eu Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=weidmueller.onmicrosoft.com header.i=@weidmueller.onmicrosoft.com header.a=rsa-sha256 header.s=selector1-weidmueller-onmicrosoft-com header.b=jVlWTW9F; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4VttJR1WqCz20Tb for ; Wed, 5 Jun 2024 00:29:11 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 30E87884A7; Tue, 4 Jun 2024 16:28:01 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=weidmueller.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=weidmueller.onmicrosoft.com header.i=@weidmueller.onmicrosoft.com header.b="jVlWTW9F"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id BD124883F5; Tue, 4 Jun 2024 16:27:59 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,FORGED_SPF_HELO,SPF_HELO_PASS,T_SCC_BODY_TEXT_LINE, T_SPF_PERMERROR autolearn=no autolearn_force=no version=3.4.2 Received: from EUR04-HE1-obe.outbound.protection.outlook.com (mail-he1eur04on20717.outbound.protection.outlook.com [IPv6:2a01:111:f403:260f::717]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 139CD884DD for ; Tue, 4 Jun 2024 16:27:56 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=weidmueller.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=Lukas.Funke-oss@weidmueller.com ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SlFkmXBFBEjtlCWFfSsY0qMqdCoqgppdD4a3x7LXHLOTZaqT4p0kPk53tkDuu5twe7/Dvs6y77DIxuOzRMrLXPehaThEO1ER9uu0ca+4lnKACQBAsO48euqUZopC6rh8yC5v+1wobNMqfEkqolDXTunIOvc+7tmOfLoOOk1UgazFhV+bd6GS2N53hdYwYWcdQJtSmnSL/hY02wrDfS9kTrQptswcRGTWP6oJnCm4Nxkdp0BeDu/BXqwYNVrJz+o0604neLeb/eqy5f40PbBKE7yjJ3NmIbM6qK8bTIEbNuIHQUoMONorIjrE3Kx0saOg+Lj8VKtJ4VPKeWbI7H8bYQ== 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-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=asc2GV8/JO329NTTlqPD+VwENZNepsnEf03tfbJt/Pg=; b=iQ1sZhpom5pQ+aVgunNrViAECMZkZQq5lOXkgc9Pk6qQoKrcMdN5YwpPMCDPQ/lUjDhnTlS29bx20DDmrVIYT8OlUOmuA/MbZYvYNt1m3cbe90evqhD2EyKmDQaoamSgq12txYpd1SXrN9l5JK9EH+RTDuwhE9l6c8OieiacauE5/Mk/hxBobpP/7n7mZGA2OJpRwROVtOWKXzv/EaaqsnIgkt7PW7mZEjse4Sjl3Zwec3pGfX0Hr7O/pX7hJBFfcnJkQc/OisQSZg0sdXH5JUdryQrlUJ7/dKEQozxY/32jlVGlBsBjC6WGMuNh7KYTCSCmhlGmXJOg6oJIMXzFEQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=weidmueller.com; dmarc=pass action=none header.from=weidmueller.com; dkim=pass header.d=weidmueller.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=weidmueller.onmicrosoft.com; s=selector1-weidmueller-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=asc2GV8/JO329NTTlqPD+VwENZNepsnEf03tfbJt/Pg=; b=jVlWTW9FquGrc5lo3A/NRhgDX1OuGTXwHP9Gz5T7b73XSnbjtQPbhFzIUPrEeWKy64Zy8iKf9mNrbFEkgngG1YN02SGuI1IlT+Aku9mHV7MKZxaholHrvxA9qfystmatEdHpYCUP8eWNgl+PxxeU8x9DC4ezSm+WGh3dkUtPIOg= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=weidmueller.com; Received: from AS2PR08MB8431.eurprd08.prod.outlook.com (2603:10a6:20b:55a::18) by AM8PR08MB5827.eurprd08.prod.outlook.com (2603:10a6:20b:1da::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7633.24; Tue, 4 Jun 2024 14:27:54 +0000 Received: from AS2PR08MB8431.eurprd08.prod.outlook.com ([fe80::aeb:1738:d59a:cee6]) by AS2PR08MB8431.eurprd08.prod.outlook.com ([fe80::aeb:1738:d59a:cee6%7]) with mapi id 15.20.7633.021; Tue, 4 Jun 2024 14:27:54 +0000 From: lukas.funke-oss@weidmueller.com To: u-boot@lists.denx.de Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Stefan Roese , Michal Simek , Lukas Funke , Alexander Gendin , Bin Meng , Caleb Connolly , Heinrich Schuchardt , Jonas Karlman , Peng Fan , Simon Glass , Tom Rini , Wan Yee Lau Subject: [PATCH v3 7/7] drivers: misc: Add driver to access ZynqMP efuses Date: Tue, 4 Jun 2024 16:27:41 +0200 Message-Id: <20240604142741.425307-8-lukas.funke-oss@weidmueller.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20240604142741.425307-1-lukas.funke-oss@weidmueller.com> References: <20240604142741.425307-1-lukas.funke-oss@weidmueller.com> X-ClientProxiedBy: FR4P281CA0308.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:f6::12) To AS2PR08MB8431.eurprd08.prod.outlook.com (2603:10a6:20b:55a::18) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AS2PR08MB8431:EE_|AM8PR08MB5827:EE_ X-MS-Office365-Filtering-Correlation-Id: d602ac2e-b03b-4754-dc75-08dc84a2854d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; ARA:13230031|366007|376005|52116005|7416005|1800799015|38350700005; X-Microsoft-Antispam-Message-Info: =?utf-8?q?k4Atms9oH8BPD/YrCWhSqyXPGezJ86c?= =?utf-8?q?TlzlLJFY3rMfL97VthUwgp+qeD3ma4+NLlwlTh9gNN4edScTx+HlDeH77BG0WqWUk?= =?utf-8?q?mz+VD8O5SGf8VBH3kpaRViTtfKPvqGaMR+4sO71hWPXDheJwiCUT5FFSpr08w8Kp2?= =?utf-8?q?sIUVoeo798+AdosxssMEIjrbBKTbYc7Uy8EeONg5gV89JMVeRsPvXLcbxHfWWjMPv?= =?utf-8?q?VKoaM9BjpAqocsEhrwabvBC979mhe9nM7biIh6BWfqZisQBZIm7E6BkxbSGmRp7N3?= =?utf-8?q?gmbNcxnTJUfMtvcOEsCtZ9d98CS36yeS9JdITEq86Ip4+nET1pFS8N6BbwwXCV4qz?= =?utf-8?q?7WIqG6E1Q238XICGMZ7+JZjHzDLsDqBrIVnCYDos17Nn5cKpsnq5rVj9voM8Lz72G?= =?utf-8?q?Qcb3z516188rYQubDVOqXxYvkiks+dTSUzFWUgTrPRixVOM8gSioviRKB9bXdh1lR?= =?utf-8?q?zWWkf768XGy5e0NHX9g+Vgl0pZIzPsVwta2Linl7gP+S0CxKRGSz/O1SwYBowXrWT?= =?utf-8?q?iUi47ABeH2BfqT2gY2A+vTA5iy0UwhY28Yr+z03bsu9pltyfJVKB8o0Lq6VMgxz1F?= =?utf-8?q?nyafW3hk72aJo6FE5zaBfWkboZmJ/59rmHNa4Yd3q6RPW/q31F3ktx/y1G+10C/bf?= =?utf-8?q?nwmWEx4UCZKLOA/MqcPo9+yprtmAEETStW73y1ofyXKbg/4yenLcov6LiIwA5ZXP/?= =?utf-8?q?ymcUUUKDqsFXLrPw87px1QzXfOsylRMNV0GD30rgc+bXK/Pc4581hrqS4UEwwGw+x?= =?utf-8?q?gv1a/btBN5OiCFZtwcQcJYOzpe/iu2MskJpqx9MU6mGvKe96mfzRy7lV8cmuXFfcP?= =?utf-8?q?1CxwQkaoCeaXc3U4+XuC2ZokZpzl+VH0gsLnc8lGu6RHQxXuN5VVX+LS0A+In+DgS?= =?utf-8?q?w/HTokqfl+BUz3CkmQ35uI2NglUSLgUI0qVijpCqvOdoWfrblj4wm/aLzGhj0y5gb?= =?utf-8?q?O8ipkFCow52jpVKA36z//iCFu08Axd7ZltJ5spHOHSI0Cbh6B7SeiqjXQligwu90j?= =?utf-8?q?2+7D1sHrdxK5Yl7mT+hPfY9YS5VLMMV1br9YglMlN94Uo05YyvS1HpWQRMMckwFLz?= =?utf-8?q?F5dKHevN7jIRxBgVX4/WF9zu8N+dTprb45CVzb9yd8hBRDROCes0nhWjbF8xgVxWp?= =?utf-8?q?zkf4LKAuYOlRGxzJ9txfhlQVnmAiOC0sCXW3iW3CxDgOi5qTJrDQCTZ1Cg3z+YXdG?= =?utf-8?q?wBqJcZr6DreyHBBUBejKSR5hLENCaqZ2dlvThvvsQjU2JjB99Woreo8PvSefkVbtS?= =?utf-8?q?IyG9frPxKmKUnJm6ES766KGQE6cJbeENiPN0rpOPNcQrw4/w92I6FWVw=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AS2PR08MB8431.eurprd08.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(366007)(376005)(52116005)(7416005)(1800799015)(38350700005); DIR:OUT; SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?Zv1At4SrfN82Btpys7koQu3lrjam?= =?utf-8?q?4CY7cWIBhL3ZXCszjgWutytTKpe8a1eZcUo2wHzH9Hdi8PEnaUNnHS95KXfbzBC/p?= =?utf-8?q?zvyP9LuG32oeWdnm2IEqTa6vkxStLujoAXNFgt4BoBFhwymVWiMvL/FlOgzwUIx20?= =?utf-8?q?BjH0X9nxaQ0kaIhsE9+sYoRxItDi7/a2uwkq+29cTBRBn417lAxUwUC14IUaYZ8wJ?= =?utf-8?q?yBCSoMbkXqZx8MmuDZyKPo8O1O2bQinwWCek4gIn556iFEBg/IbyxcrmifVpEIdzF?= =?utf-8?q?e4lAaJ2qNVyE6gP2Qd4KIW+nTPdRMbXsxM0LjnNWlMiop/uNQyzlcA9PfYWQcSHfK?= =?utf-8?q?u/hxlaHBWGk/txpJs9GeLooVYGEByjbGk1k32mTXdfd6+VlEyvUMadWUmcDIcTzLy?= =?utf-8?q?PgUSswlwZbGVVcB4aSB6+lUrsdW4b1irhMateVggWSLD01xKbbmaIpYc/QCFkNqZu?= =?utf-8?q?AZ+W9wxEuU85Ehf9gJxnGHVCPd8fJhY1oXGMjrGw5alQaD7ngg6c3XLe4RmPFUjKr?= =?utf-8?q?oIpZ5RgwWx/1AnImMK2XHAbe2FP8+HVGy48ax5JyzHYblaELThU/Axd762rfSD+tu?= =?utf-8?q?ZSnKzpsJC0yBwWLZrCVqvupjVsqvWpaq1qrFdyhT7wPgB8Dl7tga2VMBsMnHrW94o?= =?utf-8?q?pJ4cxbljU8s3Oc7U4K8tliIAfGLrYO1GyD+phEDHTNOj4zPmiUF27vUrHz3e8ewln?= =?utf-8?q?uwehu7ma/mlwNrGT3I0u/JUaKnvOEoloQ1nRr50fMDbmlkSIvJBE9T36OMGqdizCX?= =?utf-8?q?w8o8hsG88StatASrVhFZAlIwgRcucJVHH/h5nUESEagYzgUQnBLywNDZEppfa9B1R?= =?utf-8?q?c5PY6louXpaYQvvLOPCuaGf2KgRi/n6/HD6tEIXvReKX/p97yhlPtcj/fYv4JCUAL?= =?utf-8?q?/KsL2iqJLvzfkx0j9zWyDnLUE7bv5bVEBeZ2LOVykFLMg7kyNwTkL64MJSqEaKoIH?= =?utf-8?q?qKA8lvXcQaza2XGMCg+J9uJJEfQ55vDoRr+Bv959d9VDrKzbXprVEWrtJHSlDi6cz?= =?utf-8?q?nV6NZFSirF4sgRu9jJ3XptLrYgYg/fHkUHFzwO4L3fKcw3C9cc9FmllUSC7vuhTxr?= =?utf-8?q?ed7NTwVcplP4VcssIab8h9P7LhqrH2MUU8FdsZPizGvVISsvEbQRXCs5rcwbucvmy?= =?utf-8?q?8wFNiKLt0JzqId0vd7EKocmycHftY6ychO1k06f/SBo6kx9wL/GjiiOq9jbq+wuhE?= =?utf-8?q?ZA/rGGodRoVABAOoPWC9IbjIzqwQ64tPQLIFv3aN0RdYHvCU0fUYQAsp0roReVn5W?= =?utf-8?q?rkdX9NaohoFxYCVH6oa4Cnk0Kzd7khFkKJA/4M25fDqAehu0K5JIAXkDSRfywlvnd?= =?utf-8?q?rjoqEIBLH+POpbtuXSGF1gTep86XehO5/xM6Zd0Gn4QjumMDH3mG7ix/1Vb7RY7KK?= =?utf-8?q?9x/P7KMt0im6abvcxPUCmlMDSGKgQofXeMKSSq+1Eio1/c1gEi+9WaS2uppbNYr5s?= =?utf-8?q?yV5fZvgnjXuFeG9VAZA5xrn1rzLZqRBYkYmSyl6mVSJ+uovGHM/Ht5K4lFO8ZioB1?= =?utf-8?q?0fjEZloFKfnZSlqvMG5ZjwGJqotN45gN6g=3D=3D?= X-OriginatorOrg: weidmueller.com X-MS-Exchange-CrossTenant-Network-Message-Id: d602ac2e-b03b-4754-dc75-08dc84a2854d X-MS-Exchange-CrossTenant-AuthSource: AS2PR08MB8431.eurprd08.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Jun 2024 14:27:53.6878 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: e4289438-1c5f-4c95-a51a-ee553b8b18ec X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: crz1+lJ94bF2sfE8uVFV5RMEpKXkouHiH0f82R9sNuoNz3DyGJ50nVWXGKaaf6v/OJV3HXj8no3O5VZAD40UUw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM8PR08MB5827 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Lukas Funke Add driver to access ZynqMP efuses. This is a u-boot port of [1]. Note: Accessing eFuses requires eFuse access to be enabled in the underlying PMU firmware. [1] https://lore.kernel.org/all/20240224114516.86365-8-srinivas.kandagatla@linaro.org/ Signed-off-by: Lukas Funke --- Changes in v3: - Align ZynqMP eFuse driver with Linux kernel - Adapt versal, versal-net and zynqmp to use common chip-id function - Enable CMD_FUSE and ZYNQMP_EFUSE for zynqmp_virt and zynqmp_kria defconfig - Use 'dev_err' instead 'log_msg_ret' if possible Changes in v2: - Drop vendor specific fuse cmd, use existing fuse cmd - Minor code refactoring (reverse x-mas tree) drivers/misc/Kconfig | 8 + drivers/misc/Makefile | 1 + drivers/misc/zynqmp_efuse.c | 360 ++++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 drivers/misc/zynqmp_efuse.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6009d55f400..c07f50c9a76 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -298,6 +298,14 @@ config FSL_SEC_MON Security Monitor can be transitioned on any security failures, like software violations or hardware security violations. +config ZYNQMP_EFUSE + bool "Enable ZynqMP eFUSE Driver" + depends on ZYNQMP_FIRMWARE + help + Enable access to Zynq UltraScale (ZynqMP) eFUSEs thought PMU firmware + interface. ZnyqMP has 256 eFUSEs where some of them are security related + and cannot be read back (i.e. AES key). + choice prompt "Security monitor interaction endianess" depends on FSL_SEC_MON diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e53d52c47b3..68ba5648eab 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_ESM_K3) += k3_esm.o obj-$(CONFIG_ESM_PMIC) += esm_pmic.o obj-$(CONFIG_SL28CPLD) += sl28cpld.o obj-$(CONFIG_SPL_SOCFPGA_DT_REG) += socfpga_dtreg.o +obj-$(CONFIG_ZYNQMP_EFUSE) += zynqmp_efuse.o diff --git a/drivers/misc/zynqmp_efuse.c b/drivers/misc/zynqmp_efuse.c new file mode 100644 index 00000000000..d12de7494d2 --- /dev/null +++ b/drivers/misc/zynqmp_efuse.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek + * + * (C) Copyright 2024 Weidmueller Interface GmbH + * Lukas Funke + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SILICON_REVISION_MASK 0xF +#define P_USER_0_64_UPPER_MASK GENMASK(31, 16) +#define P_USER_127_LOWER_4_BIT_MASK GENMASK(3, 0) +#define WORD_INBYTES 4 +#define SOC_VER_SIZE 0x4 +#define EFUSE_MEMORY_SIZE 0x177 +#define UNUSED_SPACE 0x8 +#define ZYNQMP_NVMEM_SIZE (SOC_VER_SIZE + UNUSED_SPACE + \ + EFUSE_MEMORY_SIZE) +#define SOC_VERSION_OFFSET 0x0 +#define EFUSE_START_OFFSET 0xC +#define EFUSE_END_OFFSET 0xFC +#define EFUSE_PUF_START_OFFSET 0x100 +#define EFUSE_PUF_MID_OFFSET 0x140 +#define EFUSE_PUF_END_OFFSET 0x17F +#define EFUSE_NOT_ENABLED 29 + +/* + * efuse access type + */ +enum efuse_access { + EFUSE_READ = 0, + EFUSE_WRITE +}; + +/** + * struct xilinx_efuse - the basic structure + * @src: address of the buffer to store the data to be write/read + * @size: read/write word count + * @offset: read/write offset + * @flag: 0 - represents efuse read and 1- represents efuse write + * @pufuserfuse:0 - represents non-puf efuses, offset is used for read/write + * 1 - represents puf user fuse row number. + * + * this structure stores all the required details to + * read/write efuse memory. + */ +struct xilinx_efuse { + u64 src; + u32 size; + u32 offset; + enum efuse_access flag; + u32 pufuserfuse; +}; + +/** + * struct efuse_map_entry - offset and length of zynqmp fuses + * @offset: offset of efuse to be read/write + * @length: length of efuse + */ +struct efuse_map_entry { + u32 offset; + u32 length; +}; + +struct efuse_map_entry zynqmp_efuse_table[] = { + {0x000, 0x04}, /* soc revision */ + {0x00c, 0x0c}, /* SoC DNA */ + {0x020, 0x04}, /* efuse-usr0 */ + {0x024, 0x04}, /* efuse-usr1 */ + {0x028, 0x04}, /* efuse-usr2 */ + {0x02c, 0x04}, /* efuse-usr3 */ + {0x030, 0x04}, /* efuse-usr4 */ + {0x034, 0x04}, /* efuse-usr5 */ + {0x038, 0x04}, /* efuse-usr6 */ + {0x03c, 0x04}, /* efuse-usr7 */ + {0x040, 0x04}, /* efuse-miscusr */ + {0x050, 0x04}, /* efuse-chash */ + {0x054, 0x04}, /* efuse-pufmisc */ + {0x058, 0x04}, /* efuse-sec */ + {0x05c, 0x04}, /* efuse-spkid */ + {0x060, 0x30}, /* efuse-aeskey */ + {0x0a0, 0x30}, /* ppk0-hash */ + {0x0d0, 0x30}, /* ppk1-hash */ + {0x100, 0x7f}, /* pufuser */ +}; + +static int zynqmp_efuse_get_length(u32 offset, u32 *base_offset, u32 *len) +{ + struct efuse_map_entry *fuse; + int i; + + for (i = 0; i < ARRAY_SIZE(zynqmp_efuse_table); ++i) { + fuse = &zynqmp_efuse_table[i]; + if (offset >= fuse->offset && + offset < fuse->offset + fuse->length) { + *base_offset = fuse->offset; + *len = fuse->length; + return 0; + } + } + + return -ENOENT; +} + +static int zynqmp_efuse_access(void *context, unsigned int offset, + void *val, size_t bytes, enum efuse_access flag, + unsigned int pufflag) +{ + struct udevice *dev = context; + struct xilinx_efuse *efuse; + ulong dma_addr; + ulong dma_buf; + size_t words = bytes / WORD_INBYTES; + int ret; + int value; + char *data; + + if (bytes % WORD_INBYTES != 0) { + dev_err(dev, "Bytes requested should be word aligned\n"); + return -EOPNOTSUPP; + } + + if (pufflag == 0 && offset % WORD_INBYTES) { + dev_err(dev, "Offset requested should be word aligned\n"); + return -EOPNOTSUPP; + } + + if (pufflag == 1 && flag == EFUSE_WRITE) { + memcpy(&value, val, bytes); + if ((offset == EFUSE_PUF_START_OFFSET || + offset == EFUSE_PUF_MID_OFFSET) && + value & P_USER_0_64_UPPER_MASK) { + dev_err(dev, "Only lower 4 bytes are allowed to be programmed in P_USER_0 & P_USER_64\n"); + return -EOPNOTSUPP; + } + + if (offset == EFUSE_PUF_END_OFFSET && + (value & P_USER_127_LOWER_4_BIT_MASK)) { + dev_err(dev, "Only MSB 28 bits are allowed to be programmed for P_USER_127\n"); + return -EOPNOTSUPP; + } + } + + efuse = dma_alloc_coherent(sizeof(struct xilinx_efuse), &dma_addr); + if (!efuse) + return -ENOMEM; + + data = dma_alloc_coherent(bytes, &dma_buf); + if (!data) { + ret = -ENOMEM; + goto efuse_data_fail; + } + + if (flag == EFUSE_WRITE) { + memcpy(data, val, bytes); + efuse->flag = EFUSE_WRITE; + } else { + efuse->flag = EFUSE_READ; + } + + efuse->src = dma_buf; + efuse->size = words; + efuse->offset = offset; + efuse->pufuserfuse = pufflag; + + flush_dcache_range((ulong)efuse, (ulong)efuse + + roundup(sizeof(struct xilinx_efuse), ARCH_DMA_MINALIGN)); + flush_dcache_range((ulong)data, (ulong)data + + roundup(sizeof(struct xilinx_efuse), ARCH_DMA_MINALIGN)); + + zynqmp_pm_efuse_access(dma_addr, (u32 *)&ret); + if (ret != 0) { + if (ret == EFUSE_NOT_ENABLED) { + dev_err(dev, "efuse access is not enabled\n"); + ret = -EOPNOTSUPP; + } else { + dev_err(dev, "Error in efuse read %x\n", ret); + ret = -EPERM; + } + goto efuse_access_err; + } + + if (flag == EFUSE_READ) + memcpy(val, data, bytes); +efuse_access_err: + dma_free_coherent(data); +efuse_data_fail: + dma_free_coherent(efuse); + + return ret; +} + +static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct udevice *dev = context; + int ret; + int pufflag = 0; + int idcode; + int version; + + if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET) + pufflag = 1; + + switch (offset) { + /* Soc version offset is zero */ + case SOC_VERSION_OFFSET: + if (bytes != SOC_VER_SIZE) + return -EOPNOTSUPP; + + ret = zynqmp_pm_get_chipid((u32 *)&idcode, (u32 *)&version); + if (ret < 0) + return ret; + + dev_dbg(dev, "Read chipid val %x %x\n", idcode, version); + *(int *)val = version & SILICON_REVISION_MASK; + break; + /* Efuse offset starts from 0xc */ + case EFUSE_START_OFFSET ... EFUSE_END_OFFSET: + case EFUSE_PUF_START_OFFSET ... EFUSE_PUF_END_OFFSET: + ret = zynqmp_efuse_access(context, offset, val, + bytes, EFUSE_READ, pufflag); + break; + default: + *(u32 *)val = 0xDEADBEEF; + ret = 0; + break; + } + + return ret; +} + +static int zynqmp_nvmem_write(void *context, + unsigned int offset, void *val, size_t bytes) +{ + int pufflag = 0; + + if (offset < EFUSE_START_OFFSET || offset > EFUSE_PUF_END_OFFSET) + return -EOPNOTSUPP; + + if (offset >= EFUSE_PUF_START_OFFSET && offset <= EFUSE_PUF_END_OFFSET) + pufflag = 1; + + return zynqmp_efuse_access(context, offset, + val, bytes, EFUSE_WRITE, pufflag); +} + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + u32 base_offset, offset, len; + struct udevice *dev; + u8 buf[128]; /* maximal size of efuse is 0x7f (pufuse) */ + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_DRIVER_GET(zynqmp_efuse), &dev); + if (ret) + return log_msg_ret("uclass_drv", ret); + + ret = zynqmp_efuse_get_length(word, &base_offset, &len); + if (ret) { + dev_err(dev, "Invalid eFuse word offset\n"); + return ret; + } + + if (len > sizeof(buf)) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + + ret = misc_read(dev, base_offset, (void *)buf, len); + if (ret) { + dev_err(dev, "Cannot read eFuse @%u: %d", word, ret); + return ret; + } + + offset = word - base_offset; + *val = ((u32 *)buf)[offset]; + + return 0; +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + u32 base_offset, len; + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_DRIVER_GET(zynqmp_efuse), &dev); + if (ret) + return log_msg_ret("uclass_drv", ret); + + ret = zynqmp_efuse_get_length(word, &base_offset, &len); + if (ret) { + dev_err(dev, "Invalid eFuse word offset\n"); + return ret; + } + + if (word != base_offset || len != sizeof(val)) + return -EINVAL; + + ret = misc_write(dev, word, (void *)&val, sizeof(val)); + if (ret) { + dev_err(dev, "Cannot write eFuse @%u: %d", word, ret); + return ret; + } + + return 0; +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + /* sense is not supported */ + return -ENOSYS; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + /* overriding is not supported */ + return -ENOSYS; +} + +static int zynqmp_nvmem_read_compat(struct udevice *dev, int offset, + void *val, int bytes) +{ + return zynqmp_nvmem_read((void *)dev, offset, val, bytes); +} + +static int zynqmp_nvmem_write_compat(struct udevice *dev, int offset, + const void *val, int bytes) +{ + return zynqmp_nvmem_write((void *)dev, offset, (void *)val, bytes); +} + +static const struct udevice_id zynqmp_efuse_match[] = { + { .compatible = "xlnx,zynqmp-nvmem-fw", }, + { /* sentinel */ }, +}; + +static const struct misc_ops zynqmp_efuse_ops = { + .read = zynqmp_nvmem_read_compat, + .write = zynqmp_nvmem_write_compat, +}; + +U_BOOT_DRIVER(zynqmp_efuse) = { + .name = "zynqmp_efuse", + .id = UCLASS_MISC, + .of_match = zynqmp_efuse_match, + .ops = &zynqmp_efuse_ops, +};