Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/2230919/?format=api
{ "id": 2230919, "url": "http://patchwork.ozlabs.org/api/1.2/patches/2230919/?format=api", "web_url": "http://patchwork.ozlabs.org/project/uboot/patch/d9038d69963976399d8e8e26dbf84b98ee1f82da.1777536451.git.simona.toaca@nxp.com/", "project": { "id": 18, "url": "http://patchwork.ozlabs.org/api/1.2/projects/18/?format=api", "name": "U-Boot", "link_name": "uboot", "list_id": "u-boot.lists.denx.de", "list_email": "u-boot@lists.denx.de", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<d9038d69963976399d8e8e26dbf84b98ee1f82da.1777536451.git.simona.toaca@nxp.com>", "list_archive_url": null, "date": "2026-04-30T08:33:30", "name": "[v4,1/4] imx9: Add support for saving DDR training data to NVM", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "ac0404b82405bd6ab469cfdb53e00a6ad0514bd8", "submitter": { "id": 92829, "url": "http://patchwork.ozlabs.org/api/1.2/people/92829/?format=api", "name": "Simona Toaca (OSS)", "email": "simona.toaca@oss.nxp.com" }, "delegate": { "id": 151988, "url": "http://patchwork.ozlabs.org/api/1.2/users/151988/?format=api", "username": "festevam", "first_name": "Fabio", "last_name": "Estevam", "email": "festevam@gmail.com" }, "mbox": "http://patchwork.ozlabs.org/project/uboot/patch/d9038d69963976399d8e8e26dbf84b98ee1f82da.1777536451.git.simona.toaca@nxp.com/mbox/", "series": [ { "id": 502232, "url": "http://patchwork.ozlabs.org/api/1.2/series/502232/?format=api", "web_url": "http://patchwork.ozlabs.org/project/uboot/list/?series=502232", "date": "2026-04-30T08:33:29", "name": "imx9{4,5,52}: Add Quickboot support", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/502232/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2230919/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2230919/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<u-boot-bounces@lists.denx.de>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=NXP1.onmicrosoft.com header.i=@NXP1.onmicrosoft.com\n header.a=rsa-sha256 header.s=selector1-NXP1-onmicrosoft-com\n header.b=X4z0OQbn;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)", "phobos.denx.de;\n dmarc=none (p=none dis=none) header.from=oss.nxp.com", "phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de", "phobos.denx.de;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=NXP1.onmicrosoft.com header.i=@NXP1.onmicrosoft.com\n header.b=\"X4z0OQbn\";\n\tdkim-atps=neutral", "phobos.denx.de;\n dmarc=none (p=none dis=none) header.from=oss.nxp.com", "phobos.denx.de;\n spf=pass smtp.mailfrom=simona.toaca@oss.nxp.com", "dkim=none (message not signed)\n header.d=none;dmarc=none action=none header.from=oss.nxp.com;" ], "Received": [ "from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g5nHl2Sfxz1yGq\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 18:23:30 +1000 (AEST)", "from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id C3D378466D;\n\tThu, 30 Apr 2026 10:23:20 +0200 (CEST)", "by phobos.denx.de (Postfix, from userid 109)\n id 3D85C84653; Thu, 30 Apr 2026 10:23:19 +0200 (CEST)", "from AS8PR04CU009.outbound.protection.outlook.com\n (mail-westeuropeazlp170110003.outbound.protection.outlook.com\n [IPv6:2a01:111:f403:c201::3])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id E96F884661\n for <u-boot@lists.denx.de>; Thu, 30 Apr 2026 10:23:16 +0200 (CEST)", "from VI0PR04MB11917.eurprd04.prod.outlook.com (2603:10a6:800:306::9)\n by DB8PR04MB7036.eurprd04.prod.outlook.com (2603:10a6:10:12f::21)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9870.20; Thu, 30 Apr\n 2026 08:23:14 +0000", "from VI0PR04MB11917.eurprd04.prod.outlook.com\n ([fe80::a506:3460:d2bc:26e7]) by VI0PR04MB11917.eurprd04.prod.outlook.com\n ([fe80::a506:3460:d2bc:26e7%4]) with mapi id 15.20.9870.016; Thu, 30 Apr 2026\n 08:23:13 +0000" ], "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,\n DKIM_VALID,FORGED_SPF_HELO,RCVD_IN_DNSWL_BLOCKED,SPF_HELO_PASS,\n T_SPF_PERMERROR autolearn=no autolearn_force=no version=3.4.2", "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=GqI0COIPtjw25c0YK80FcGlMk9iqSQpPKgjnwxbS1H4hiVi+AfRaj/scimUPr/z4DvfrXPhdKdWMguI36RikFcQdy+yMhULZFAbzdOtlQLgmI5Y/+hU6s4+EUMgbwHmcbyTIqkOYHxa9xy9p/TxaJtUuBVqco1hVDV/OjoRkgKBivYjnqxWs5sy6SdWzjKAo+vGlK+V/lyiO+TXTFjiPRmy67dm+Ic5hNzsbYP7IOOtXUJYCgsIgNrfQLw8/UNHucGuMn/6JfoTtSuNJctePpvbxbUQQHsNiGrN9KD47BufUFvc3SXNhxFBUda4MmrDtollFyScCN/MzwFJpzgb/QA==", "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector10001;\n 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;\n bh=qnY15AgMaQbhqI4Za7s7oA9yIfoYsBHL4PN0lP1niVQ=;\n b=Iz/K9BBmnx0PNp+005Z7UxgQSAK99LOHP+NMYRNrnfjNaCB396zx2jRb/RZjumPAc49Lu+pWng/Z0n4CfscqfrC+GnZTJUMBCxannI5sN/K5dF6rX1PcdV8EfSIJ+umpjIBMBAwTb0CkjPsLuuHQGfVwpcr44IwnJlT7OvUDjD046KQfGY1vRAuRmlevP+r8M0bBi7Fb6eAqhmTt0zwuLDE9AmovPYyx5PWTByIqx5UAcs5mcqkfZ25rYKyS/BnQJoglTkzj52RyUKD/KWN9TJBG8g+LMOXpUx0K2/CiGECIzTvjl5XXV5YW49wSL7LwrMEfdSmBECGuzZptBwpuGg==", "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=oss.nxp.com; dmarc=pass action=none header.from=oss.nxp.com;\n dkim=pass header.d=oss.nxp.com; arc=none", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=NXP1.onmicrosoft.com;\n s=selector1-NXP1-onmicrosoft-com;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=qnY15AgMaQbhqI4Za7s7oA9yIfoYsBHL4PN0lP1niVQ=;\n b=X4z0OQbnO33tLNGDD77XBFm7zRsr80EPHDNUf533L5wE76ukCxG0aofGh80DCjce4ZWWEY4OijpA4Yf5LpA2WbWguiCbHdKSGGR4DHOxRV40TAxpMnP8Ro1p643g6+XDnvDYeWDNcfmd+5xzlPowujhhgPe85okigOJOpON+TgHs2A+KbKAmC8KUzCvwaT7RmZr0M2iGyl+D1lkdKtOd6N+0iNw3cZL4VNW1xDji0ZiqG7mroAHALLaQc/xhxa9OLQwun6cdQ5u/yBgxIuAckmvFeWsDqhrOnyUvOA5gsk0sYg3CKc18ju7cZJ9yShxCjCD6c04iA5YDwFixoOrenw==", "From": "\"Simona Toaca (OSS)\" <simona.toaca@oss.nxp.com>", "To": "uboot-imx@nxp.com,\n\tu-boot@lists.denx.de", "Cc": "Stefano Babic <sbabic@nabladev.com>, festevam@gmail.com, peng.fan@nxp.com,\n alice.guo@nxp.com, ye.li@nxp.com, simona.toaca@nxp.com,\n viorel.suman@nxp.com, fedor.ross@ifm.com, marex@nabladev.com,\n joao.goncalves@toradex.com, ravi@prevas.dk, ping.bai@nxp.com,\n ji.luo@nxp.com, qijian.guo@nxp.com", "Subject": "[PATCH v4 1/4] imx9: Add support for saving DDR training data to NVM", "Date": "Thu, 30 Apr 2026 11:33:30 +0300", "Message-ID": "\n <d9038d69963976399d8e8e26dbf84b98ee1f82da.1777536451.git.simona.toaca@nxp.com>", "X-Mailer": "git-send-email 2.43.0", "In-Reply-To": "<cover.1777536451.git.simona.toaca@nxp.com>", "References": "<cover.1777536451.git.simona.toaca@nxp.com>", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-ClientProxiedBy": "AM9P250CA0022.EURP250.PROD.OUTLOOK.COM\n (2603:10a6:20b:21c::27) To VI0PR04MB11917.eurprd04.prod.outlook.com\n (2603:10a6:800:306::9)", "MIME-Version": "1.0", "X-MS-Exchange-MessageSentRepresentingType": "1", "X-MS-PublicTrafficType": "Email", "X-MS-TrafficTypeDiagnostic": "VI0PR04MB11917:EE_|DB8PR04MB7036:EE_", "X-MS-Office365-Filtering-Correlation-Id": "3fa4e2c3-e757-4795-a9f9-08dea691b89b", "X-MS-Exchange-SharedMailbox-RoutingAgent-Processed": "True", "X-LD-Processed": "686ea1d3-bc2b-4c6f-a92c-d99c5c301635,ExtAddr", "X-MS-Exchange-SenderADCheck": "1", "X-MS-Exchange-AntiSpam-Relay": "0", "X-Microsoft-Antispam": "BCL:0;\n ARA:13230040|366016|1800799024|19092799006|376014|18002099003|56012099003|22082099003;", "X-Microsoft-Antispam-Message-Info": "\n ihchGrCtiovIBI/LKTKvOUVMK3qvZCu8YWDsfFBVl1TTIA5ae7Lyz4XI80BrHie05+I6bPV+qWHDcwnXp0NEgJIbpyMe5s+3w3FtkWlZNiTtMuzRJybB6DVZyigb31pymTs831gw+UKSzm2rReMQjM9JOBTEUvaR2ws9AVCjaCpGn+DnBQfxiSA5emWKzdm07L/LGgk3Uq8+hXt01h0FlkswSxbNqw/5v/jg21YHl0SE63pLmCUX7VOk2v2kD4wwfimjOpuad15X5AzTD5mi5XcxpRVpca5zjyGkLk2x5sS6VEUwkuLf0+CEEbsTKW2699l+NYpLmz5t/MtAIhsj0EQMSUWX1ixwoMUiUChNnbBWMNcLqPi6wpZRKV76VVs1FOsch38s/oW2oz30g/YieqDT+K4N0pcV0exafHIijvIEAZEUfHrARzjXxVYGtqM4Eg70H5sAkwkQZiD6977kbsj9a2lzCF7WSb2mLgkcLan5uQnbfUkxaLwm58+LPvec52vxmxz4wiNgL8A2GRkwHyMpZ3NchDzlppea6mK5XdekhQysSsi4MULQV/jUPn17QL9UeMlCFDTbm/sbR5bkjUB3oB7PYKDj38ld0C3AmqUCZnFzMrX0rs0p4uuPeZYKp/c86H/fwpYF2b5MsxNbU9gogT7YqFB7Z+LiCRIr1svRH1ZBiwyaKKNosYkyFXCe3n4uLj+vNvoQC0+YML+NAClHTLSjiPqzhOVQ4IX6H4g=", "X-Forefront-Antispam-Report": "CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:;\n IPV:NLI; SFV:NSPM; H:VI0PR04MB11917.eurprd04.prod.outlook.com; PTR:;\n CAT:NONE;\n SFS:(13230040)(366016)(1800799024)(19092799006)(376014)(18002099003)(56012099003)(22082099003);\n DIR:OUT; SFP:1101;", "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1", "X-MS-Exchange-AntiSpam-MessageData-0": "\n DZpY5rwCx9jxlzBt8ULfRDKRHP7ROmXYpiBr7KQZtFUpP9G3LkefFRme/k5IikLXweHPmNO37js5yk3Kv004AmF1srbllLi8ewmjspawykYRsFEB8EBiZhEcdF1c6lCz7a71CUflr0tVt+2XkJhK29dCbqaAMzlI7e65JyYERjhpY3OVmB5zjLuLIjAnWHrMqoeGOI2i6PqIuoWy7qCdIeGeJicophc0tJNLpGgqT8B/WwvuWbpJ3rfVFX72H3EYUnEik0jV7nk475tudNrkpS0sdMtBEAqZVJ2DehwtQXgHmw8pKB9MJt8+ExE7rr9EMil/RM5qhw03H8ayhA7trOWMwMZ3T5cnVVkxJMx1zUD3I0Is8TeLhuI8dY/kdr2Zisb+TMbEn1vPXdnUa+dEVGo/z+J7Vbfu/dftqicwQY8BkenwOaDtr8N4Xn6INy6VtUfrTfqKXTGPTNlQigfXNXOJQmCqNaKkytcnYarNMQqSzHaa3+HlKNtXvKPDXTVLwsnmNjPiGFjR2Zmpfnw5iJeYxLxItpGlZ9zs9mS14kkY3hzwmZcB2c+slAwy6JBmr6jMaKPUobYSCicLnyTJraqlF+svmX5SPKzIg3p0jfBsM/Rk5noK1TE64I2Kt89ff5Xe0pd84Y4N08lmLlGFLfgNGpDPdmCWJovW+kbGxnptU05m3T3LXdbhbE4Y2nu4XKgtaqpdohelv6lecQmSnNrcgVeSQw33RQQJdnNLctD9gBO3OGWGfUHnBOjj7QOa022rDfIzJ9vNky2q1pjaLunrVZ/rFUwIpFglP48RrEwGVJFFBpp6U+8mAWNyJyTXpcj80GA7OxGS+aHFCJak41S/T1Qgs2kihtgCp0DKankSMYXT0XOl8Nv0Yob3QqvR2VCtO/COHpFIL4Bl/wxDztxeXU+mgzeTmpm8OJSFL/3AuZUOTnlQX+Vv7KwRfrxxvuB3PDJxOCk5ycLaQ/tId996IR4L8c9wvLS654hr+Rm3PtDoL88ZHwVwrp2AOwhdFxnMApr3deO9Bh3Sy0NfBjbg6OPR4KxsiZ31Jl21BW23LWY6MCazFXTwMbkGkL1Z9+36c6kfqiUYBlUpyOC6nR4hNu4W6pn7QpkSRo1gcz7xCryVk7M7f9RsNRXQmnTPKwROCXcQYWXnaZ2/Ww7l5R8AsJ2Tqc1dO6Two80TCSlGCzs2p9X4Z+vPnzIFk5HEkus9OSauNqZFCrkvh61qlabOI1nkNa/V8gg+vjkPXlpWokjvQCeaNZHeNjiU50EgLjcDe4Nmad440TS5hHuKjaMfBpWbYJTPRys+mLC0lPIhsrGK7wMxw5fXQIBZ+ZsRCjfTezYOLZ2mtLYZ9tBtHcMyskPsT6h8oU7/HB87plwy0znJxQlGmSqSRF8MyW1yyyzMIa8wOJTJN4rSOuGVDmYNUWuvWfpJEs+4O8nNFqNbBGpOasu6xs82XRHuBRtYAlIbLYktu7YN5ozRwHjXWG1pm5u+C5brAB8OEPtcvH0q9OI+Q9OM2F7laolToIbhSkpBvjo1eg7F8zrveAP1DZfnzF7XKwnyUkKdvwaDxw/0qoYY4Llt7Z4G/QO+GD+S4pyAwgyD3xKGtHxsCWrVo2MdC6oCtGIQyXC+2Vx4OazUSDvx3S0KQ/Djq7BK23F9VzPvxrYAis5WSrF8D9R1zKYR0pJ7NlwNo82hRkv6xGY3wj1O7Sg6MsIcE7JrMG+0FAT6gE61Xq9iZoNygD0xUQ==", "X-OriginatorOrg": "oss.nxp.com", "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 3fa4e2c3-e757-4795-a9f9-08dea691b89b", "X-MS-Exchange-CrossTenant-AuthSource": "VI0PR04MB11917.eurprd04.prod.outlook.com", "X-MS-Exchange-CrossTenant-AuthAs": "Internal", "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "30 Apr 2026 08:23:13.3676 (UTC)", "X-MS-Exchange-CrossTenant-FromEntityHeader": "Hosted", "X-MS-Exchange-CrossTenant-Id": "686ea1d3-bc2b-4c6f-a92c-d99c5c301635", "X-MS-Exchange-CrossTenant-MailboxType": "HOSTED", "X-MS-Exchange-CrossTenant-UserPrincipalName": "\n Xr3m7M6pLcaMDby9PnMwIJlz4zgDyZoUCGwZ5rfE6gG/FMRfQJjfztf8fTHmZlKRGMrdQn8SeRWf6wf2MS2h5g==", "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "DB8PR04MB7036", "X-BeenThere": "u-boot@lists.denx.de", "X-Mailman-Version": "2.1.39", "Precedence": "list", "List-Id": "U-Boot discussion <u-boot.lists.denx.de>", "List-Unsubscribe": "<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>", "List-Archive": "<https://lists.denx.de/pipermail/u-boot/>", "List-Post": "<mailto:u-boot@lists.denx.de>", "List-Help": "<mailto:u-boot-request@lists.denx.de?subject=help>", "List-Subscribe": "<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>", "Errors-To": "u-boot-bounces@lists.denx.de", "Sender": "\"U-Boot\" <u-boot-bounces@lists.denx.de>", "X-Virus-Scanned": "clamav-milter 0.103.8 at phobos.denx.de", "X-Virus-Status": "Clean" }, "content": "From: Simona Toaca <simona.toaca@nxp.com>\n\nDDR training data can be saved to NVM and be available\nto OEI at boot time, which will trigger QuickBoot flow.\n\nU-Boot only checks for data integrity (CRC32), while\nOEI is in charge of authentication when it tries to\nload the data from NVM.\n\nOn iMX95 A0/A1, 'authentication' is done via another\nCRC32. On the other SoCs, authentication is done by\nusing ELE to check the MAC stored in the ddrphy_qb_state\nstructure.\n\nSupported platforms: iMX94, iMX95, iMX952 (using OEI)\nSupported storage types: eMMC, SD, SPI flash.\n\nSigned-off-by: Viorel Suman <viorel.suman@nxp.com>\nSigned-off-by: Ye Li <ye.li@nxp.com>\nSigned-off-by: Simona Toaca <simona.toaca@nxp.com>\n---\n arch/arm/include/asm/arch-imx9/ddr.h | 48 +++-\n arch/arm/include/asm/mach-imx/qb.h | 15 +\n arch/arm/mach-imx/Kconfig | 9 +\n arch/arm/mach-imx/imx9/Makefile | 6 +-\n arch/arm/mach-imx/imx9/qb.c | 403 +++++++++++++++++++++++++++\n arch/arm/mach-imx/imx9/scmi/soc.c | 7 +\n drivers/ddr/imx/imx9/Kconfig | 7 +\n 7 files changed, 492 insertions(+), 3 deletions(-)\n create mode 100644 arch/arm/include/asm/mach-imx/qb.h\n create mode 100644 arch/arm/mach-imx/imx9/qb.c", "diff": "diff --git a/arch/arm/include/asm/arch-imx9/ddr.h b/arch/arm/include/asm/arch-imx9/ddr.h\nindex a8e3f7354c7..bba12369f06 100644\n--- a/arch/arm/include/asm/arch-imx9/ddr.h\n+++ b/arch/arm/include/asm/arch-imx9/ddr.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: GPL-2.0+ */\n /*\n- * Copyright 2022 NXP\n+ * Copyright 2022-2026 NXP\n */\n \n #ifndef __ASM_ARCH_IMX8M_DDR_H\n@@ -100,6 +100,52 @@ struct dram_timing_info {\n \n extern struct dram_timing_info dram_timing;\n \n+/* Quick Boot related */\n+#define DDRPHY_QB_CSR_SIZE\t5168\n+#define DDRPHY_QB_ACSM_SIZE\t(4 * 1024)\n+#define DDRPHY_QB_MSB_SIZE\t0x200\n+#define DDRPHY_QB_PSTATES\t0\n+#define DDRPHY_QB_PST_SIZE\t(DDRPHY_QB_PSTATES * 4 * 1024)\n+\n+/**\n+ * This structure needs to be aligned with the one in OEI.\n+ */\n+struct ddrphy_qb_state {\n+\tu32 crc;\t\t /* Used for ensuring integrity in DRAM */\n+#define MAC_LENGTH\t\t8 /* 256 bits, 32-bit aligned */\n+\tu32 mac[MAC_LENGTH];\t /* For 95A0/1 use mac[0] to keep CRC32 value */\n+\tu8 trained_vrefca_a0;\n+\tu8 trained_vrefca_a1;\n+\tu8 trained_vrefca_b0;\n+\tu8 trained_vrefca_b1;\n+\tu8 trained_vrefdq_a0;\n+\tu8 trained_vrefdq_a1;\n+\tu8 trained_vrefdq_b0;\n+\tu8 trained_vrefdq_b1;\n+\tu8 trained_vrefdqu_a0;\n+\tu8 trained_vrefdqu_a1;\n+\tu8 trained_vrefdqu_b0;\n+\tu8 trained_vrefdqu_b1;\n+\tu8 trained_dramdfe_a0;\n+\tu8 trained_dramdfe_a1;\n+\tu8 trained_dramdfe_b0;\n+\tu8 trained_dramdfe_b1;\n+\tu8 trained_dramdca_a0;\n+\tu8 trained_dramdca_a1;\n+\tu8 trained_dramdca_b0;\n+\tu8 trained_dramdca_b1;\n+\tu16 qb_pll_upll_prog0;\n+\tu16 qb_pll_upll_prog1;\n+\tu16 qb_pll_upll_prog2;\n+\tu16 qb_pll_upll_prog3;\n+\tu16 qb_pll_ctrl1;\n+\tu16 qb_pll_ctrl4;\n+\tu16 qb_pll_ctrl5;\n+\tu16 csr[DDRPHY_QB_CSR_SIZE];\n+\tu16 acsm[DDRPHY_QB_ACSM_SIZE];\n+\tu16 pst[DDRPHY_QB_PST_SIZE];\n+};\n+\n void ddr_load_train_firmware(enum fw_type type);\n int ddr_init(struct dram_timing_info *timing_info);\n int ddr_cfg_phy(struct dram_timing_info *timing_info);\ndiff --git a/arch/arm/include/asm/mach-imx/qb.h b/arch/arm/include/asm/mach-imx/qb.h\nnew file mode 100644\nindex 00000000000..a874c9c5e36\n--- /dev/null\n+++ b/arch/arm/include/asm/mach-imx/qb.h\n@@ -0,0 +1,15 @@\n+/* SPDX-License-Identifier: GPL-2.0+ */\n+/*\n+ * Copyright 2026 NXP\n+ */\n+\n+#ifndef __IMX_QB_H__\n+#define __IMX_QB_H__\n+\n+#include <stdbool.h>\n+\n+bool imx_qb_check(void);\n+int imx_qb(const char *ifname, const char *dev, bool save);\n+void spl_imx_qb_save(void);\n+\n+#endif\ndiff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig\nindex 259f4a4ce99..bb62a0cf2f6 100644\n--- a/arch/arm/mach-imx/Kconfig\n+++ b/arch/arm/mach-imx/Kconfig\n@@ -71,6 +71,15 @@ config CSF_SIZE\n \t Define the maximum size for Command Sequence File (CSF) binary\n \t this information is used to define the image boot data.\n \n+config IMX_QB\n+\tbool \"Support Quickboot flow for Synopsis DDR PHY on iMX platforms\"\n+\tdefault y\n+\tdepends on IMX94 || IMX95 || IMX952\n+\thelp\n+\t Enable the logic for saving DDR training data from volatile\n+\t memory to non-volatile storage. OEI uses the saved data to\n+\t run Quickboot flow and skip re-training the DDR PHY.\n+\n config CMD_BMODE\n \tbool \"Support the 'bmode' command\"\n \tdefault y\ndiff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile\nindex 53cc97c6b47..80b697396ea 100644\n--- a/arch/arm/mach-imx/imx9/Makefile\n+++ b/arch/arm/mach-imx/imx9/Makefile\n@@ -1,6 +1,6 @@\n # SPDX-License-Identifier: GPL-2.0+\n #\n-# Copyright 2022 NXP\n+# Copyright 2022,2026 NXP\n \n obj-y += lowlevel_init.o\n \n@@ -12,4 +12,6 @@ endif\n \n ifneq ($(CONFIG_SPL_BUILD),y)\n obj-y += imx_bootaux.o\n-endif\n\\ No newline at end of file\n+endif\n+\n+obj-$(CONFIG_$(PHASE_)IMX_QB) += qb.o\ndiff --git a/arch/arm/mach-imx/imx9/qb.c b/arch/arm/mach-imx/imx9/qb.c\nnew file mode 100644\nindex 00000000000..1a0a12de3d4\n--- /dev/null\n+++ b/arch/arm/mach-imx/imx9/qb.c\n@@ -0,0 +1,403 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/**\n+ * Copyright 2024-2026 NXP\n+ */\n+#include <dm/device-internal.h>\n+#include <dm/uclass.h>\n+#include <errno.h>\n+#include <imx_container.h>\n+#include <linux/bitfield.h>\n+#include <mmc.h>\n+#include <spi_flash.h>\n+#include <spl.h>\n+#include <stdlib.h>\n+#include <u-boot/crc.h>\n+\n+#include <asm/arch/ddr.h>\n+#include <asm/mach-imx/boot_mode.h>\n+#include <asm/mach-imx/sys_proto.h>\n+\n+#define QB_STATE_LOAD_SIZE SZ_64K\n+\n+#define BLK_DEV\t\t0\n+#define SPI_DEV\t\t1\n+\n+#define IMG_FLAGS_IMG_TYPE_MASK 0xF\n+#define IMG_FLAGS_IMG_TYPE(x) FIELD_GET(IMG_FLAGS_IMG_TYPE_MASK, (x))\n+\n+#define IMG_TYPE_DDR_TDATA_DUMMY 0xD /* dummy DDR training data image */\n+\n+static const struct {\n+\tconst char *ifname;\n+\tconst char *dev;\n+} imx_boot_devs[] = {\n+\t[BOOT_DEVICE_MMC1] = { \"mmc\", \"0\" },\n+\t[BOOT_DEVICE_MMC2] = { \"mmc\", \"1\" },\n+\t[BOOT_DEVICE_SPI] = { \"spi\", \"\" },\n+};\n+\n+static int imx_qb_get_board_boot_device(void)\n+{\n+\tswitch (get_boot_device()) {\n+\tcase SD1_BOOT:\n+\tcase MMC1_BOOT:\n+\t\treturn BOOT_DEVICE_MMC1;\n+\tcase SD2_BOOT:\n+\tcase MMC2_BOOT:\n+\t\treturn BOOT_DEVICE_MMC2;\n+\tcase USB_BOOT:\n+\t\treturn BOOT_DEVICE_BOARD;\n+\tcase QSPI_BOOT:\n+\t\treturn BOOT_DEVICE_SPI;\n+\tdefault:\n+\t\treturn BOOT_DEVICE_NONE;\n+\t}\n+}\n+\n+static int imx_qb_get_boot_dev_str(const char **ifname, const char **dev)\n+{\n+\tint boot_dev;\n+\n+\tif (IS_ENABLED(CONFIG_XPL_BUILD))\n+\t\tboot_dev = spl_boot_device();\n+\telse\n+\t\tboot_dev = imx_qb_get_board_boot_device();\n+\n+\tif (boot_dev == BOOT_DEVICE_NONE || boot_dev == BOOT_DEVICE_BOARD)\n+\t\treturn -EINVAL;\n+\n+\t*ifname = imx_boot_devs[boot_dev].ifname;\n+\t*dev = imx_boot_devs[boot_dev].dev;\n+\n+\treturn 0;\n+}\n+\n+bool imx_qb_check(void)\n+{\n+\tstruct ddrphy_qb_state *qb_state;\n+\tu32 size, crc;\n+\n+\t/**\n+\t * Ensure CRC is not empty, the reason is that\n+\t * the data is invalidated after first save run\n+\t * or after it is overwritten.\n+\t */\n+\tqb_state = (struct ddrphy_qb_state *)CONFIG_QB_SAVED_STATE_BASE;\n+\tsize = sizeof(struct ddrphy_qb_state) - sizeof(qb_state->crc);\n+\tcrc = crc32(0, (u8 *)qb_state->mac, size);\n+\n+\tif (!qb_state->crc || crc != qb_state->crc)\n+\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static int imx_qb_get_blk_boot_part(const char * const ifname,\n+\t\t\t\t const char * const dev,\n+\t\t\t\t struct blk_desc **bdesc)\n+{\n+\tstruct udevice *udev;\n+\tstruct disk_partition info;\n+\tstruct mmc *mmc;\n+\tint part;\n+\tint ret;\n+\n+\tif (!IS_ENABLED(CONFIG_XPL_BUILD))\n+\t\treturn blk_get_device_part_str(ifname, dev, bdesc, &info, 1);\n+\n+\t/**\n+\t * SPL does not have access to part_get_info,\n+\t * so get the partition manually. Currently only\n+\t * supporting MMC devices.\n+\t */\n+\tret = blk_get_device_by_str(ifname, dev, bdesc);\n+\n+\tif (ret < 0)\n+\t\treturn -ENODEV;\n+\n+\tif ((*bdesc)->uclass_id != UCLASS_MMC)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tudev = dev_get_parent((*bdesc)->bdev);\n+\tmmc = mmc_get_mmc_dev(udev);\n+\n+\tif (IS_SD(mmc) || mmc->part_config == MMCPART_NOAVAILABLE)\n+\t\treturn 0;\n+\n+\tpart = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);\n+\n+\tif (part == EMMC_BOOT_PART_BOOT1 || part == EMMC_BOOT_PART_BOOT2)\n+\t\treturn part;\n+\n+\treturn 0;\n+}\n+\n+static ulong imx_qb_get_boot_device_offset(void *dev, int dev_type)\n+{\n+\tstruct blk_desc *bdesc;\n+\n+\tswitch (dev_type) {\n+\tcase BLK_DEV:\n+\t\tbdesc = dev;\n+\n+\t\t/* eMMC boot partition */\n+\t\tif (bdesc->hwpart)\n+\t\t\treturn CONTAINER_HDR_EMMC_OFFSET;\n+\n+\t\treturn CONTAINER_HDR_MMCSD_OFFSET;\n+\tcase SPI_DEV:\n+\t\treturn CONTAINER_HDR_QSPI_OFFSET;\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static int imx_qb_parse_container(void *addr, u64 *qb_data_off)\n+{\n+\tstruct container_hdr *phdr;\n+\tstruct boot_img_t *img_entry;\n+\tu32 img_type, img_end;\n+\tint i;\n+\n+\tphdr = addr;\n+\tif (phdr->tag != 0x87 || (phdr->version != 0x0 && phdr->version != 0x2))\n+\t\treturn -EINVAL;\n+\n+\timg_entry = addr + sizeof(struct container_hdr);\n+\tfor (i = 0; i < phdr->num_images; i++) {\n+\t\timg_type = IMG_FLAGS_IMG_TYPE(img_entry->hab_flags);\n+\t\tif (img_type == IMG_TYPE_DDR_TDATA_DUMMY && img_entry->size == 0) {\n+\t\t\t/* Image entry pointing to DDR Training Data */\n+\t\t\t*qb_data_off = img_entry->offset;\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\timg_end = img_entry->offset + img_entry->size;\n+\t\tif (i + 1 < phdr->num_images) {\n+\t\t\timg_entry++;\n+\t\t\tif (img_end + QB_STATE_LOAD_SIZE == img_entry->offset) {\n+\t\t\t\t/* hole detected */\n+\t\t\t\t*qb_data_off = img_end;\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+static int imx_qb_get_dev_qbdata_offset(void *dev, int dev_type, ulong offset,\n+\t\t\t\t\tu64 *qbdata_offset)\n+{\n+\tstruct blk_desc *bdesc;\n+\tu8 *buf;\n+\tulong count;\n+\tint ret;\n+\n+\tbuf = malloc(CONTAINER_HDR_ALIGNMENT);\n+\tif (!buf)\n+\t\treturn -ENOMEM;\n+\n+\tswitch (dev_type) {\n+\tcase BLK_DEV:\n+\t\tbdesc = dev;\n+\n+\t\tcount = blk_dread(bdesc,\n+\t\t\t\t offset / bdesc->blksz,\n+\t\t\t\t CONTAINER_HDR_ALIGNMENT / bdesc->blksz,\n+\t\t\t\t buf);\n+\t\tif (count == 0) {\n+\t\t\tprintf(\"Read container image from MMC/SD failed\\n\");\n+\t\t\tret = -EIO;\n+\t\t\tgoto imx_qb_get_dev_qbdata_offset_exit;\n+\t\t}\n+\t\tbreak;\n+\tcase SPI_DEV:\n+\t\tif (!CONFIG_IS_ENABLED(SPI)) {\n+\t\t\tret = -EOPNOTSUPP;\n+\t\t\tgoto imx_qb_get_dev_qbdata_offset_exit;\n+\t\t}\n+\n+\t\tret = spi_flash_read_dm(dev, offset,\n+\t\t\t\t\tCONTAINER_HDR_ALIGNMENT, buf);\n+\t\tif (ret) {\n+\t\t\tprintf(\"Read container header from SPI failed\\n\");\n+\t\t\tret = -EIO;\n+\t\t\tgoto imx_qb_get_dev_qbdata_offset_exit;\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tprintf(\"Support for device %d not enabled\\n\", dev_type);\n+\t\tret = -EOPNOTSUPP;\n+\t\tgoto imx_qb_get_dev_qbdata_offset_exit;\n+\t}\n+\n+\tret = imx_qb_parse_container(buf, qbdata_offset);\n+\n+imx_qb_get_dev_qbdata_offset_exit:\n+\tfree(buf);\n+\n+\treturn ret;\n+}\n+\n+static int imx_qb_get_qbdata_offset(void *dev, int dev_type,\n+\t\t\t\t u64 *qbdata_offset)\n+{\n+\tu64 cont_offset;\n+\tint ret, i;\n+\n+\tcont_offset = imx_qb_get_boot_device_offset(dev, dev_type);\n+\n+\tfor (i = 0; i < 3; i++) {\n+\t\tret = imx_qb_get_dev_qbdata_offset(dev, dev_type, cont_offset,\n+\t\t\t\t\t\t qbdata_offset);\n+\t\tif (ret == 0) {\n+\t\t\t(*qbdata_offset) += cont_offset;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tcont_offset += CONTAINER_HDR_ALIGNMENT;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int imx_qb_blk(const char * const ifname,\n+\t\t const char * const dev, bool save)\n+{\n+\tstruct blk_desc *bdesc;\n+\tu64 offset;\n+\tu64 load_size;\n+\tint part, orig_part;\n+\tint ret;\n+\n+\tpart = imx_qb_get_blk_boot_part(ifname, dev, &bdesc);\n+\n+\tif (part < 0) {\n+\t\tprintf(\"Failed to find %s %s\\n\", ifname, dev);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\torig_part = bdesc->hwpart;\n+\n+\tret = blk_dselect_hwpart(bdesc, part);\n+\tif (ret && ret != -EMEDIUMTYPE) {\n+\t\tprintf(\"Failed to select hwpart, ret %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = imx_qb_get_qbdata_offset(bdesc, BLK_DEV, &offset);\n+\tif (ret) {\n+\t\tprintf(\"get_qbdata_offset failed, ret = %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\toffset /= bdesc->blksz;\n+\tload_size = QB_STATE_LOAD_SIZE / bdesc->blksz;\n+\n+\tif (save) {\n+\t\t/* QB data is stored in DDR -> can use it as buf */\n+\t\tret = blk_dwrite(bdesc, offset, load_size,\n+\t\t\t\t (const void *)CONFIG_QB_SAVED_STATE_BASE);\n+\t} else {\n+\t\t/* erase */\n+\t\tret = blk_derase(bdesc, offset, load_size);\n+\t}\n+\n+\tif (!ret) {\n+\t\tprintf(\"Failed to write to block device\\n\");\n+\t\treturn -EIO;\n+\t}\n+\n+\t/* Return to original partition */\n+\tret = blk_dselect_hwpart(bdesc, orig_part);\n+\tif (ret && ret != -EMEDIUMTYPE) {\n+\t\tprintf(\"Failed to select hwpart, ret %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int imx_qb_spi(bool save)\n+{\n+\tstruct udevice *flash;\n+\tu64 offset;\n+\tint ret;\n+\n+\tif (!CONFIG_IS_ENABLED(SPI)) {\n+\t\tprintf(\"SPI not enabled\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tret = uclass_first_device_err(UCLASS_SPI_FLASH, &flash);\n+\tif (ret) {\n+\t\tprintf(\"SPI flash not found.\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tret = imx_qb_get_qbdata_offset(flash, SPI_DEV, &offset);\n+\tif (ret) {\n+\t\tprintf(\"get_qbdata_offset failed, ret = %d\\n\", ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = spi_flash_erase_dm(flash, offset, QB_STATE_LOAD_SIZE);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!save)\n+\t\treturn 0;\n+\n+\t/* QB data is stored in DDR -> can use it as buf */\n+\tret = spi_flash_write_dm(flash, offset,\n+\t\t\t\t QB_STATE_LOAD_SIZE,\n+\t\t\t\t (const void *)CONFIG_QB_SAVED_STATE_BASE);\n+\n+\treturn ret;\n+}\n+\n+int imx_qb(const char *ifname, const char *dev, bool save)\n+{\n+\tint ret;\n+\n+\tret = 0;\n+\n+\t/* Try to use boot device */\n+\tif (!strcmp(ifname, \"auto\"))\n+\t\tret = imx_qb_get_boot_dev_str(&ifname, &dev);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (save && !imx_qb_check())\n+\t\treturn -EINVAL;\n+\n+\tif (!strcmp(ifname, \"spi\"))\n+\t\tret = imx_qb_spi(save);\n+\telse\n+\t\tret = imx_qb_blk(ifname, dev, save);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!save)\n+\t\treturn 0;\n+\n+\t/**\n+\t * invalidate qb_state mem so that at next boot\n+\t * the check function will fail and save won't happen\n+\t */\n+\tmemset((void *)CONFIG_QB_SAVED_STATE_BASE, 0,\n+\t sizeof(struct ddrphy_qb_state));\n+\n+\treturn 0;\n+}\n+\n+void spl_imx_qb_save(void)\n+{\n+\t/* Save QB data on current boot device */\n+\tif (imx_qb(\"auto\", \"\", true))\n+\t\tprintf(\"QB save failed\\n\");\n+}\ndiff --git a/arch/arm/mach-imx/imx9/scmi/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c\nindex 47e8fc247df..ab0f7394c9e 100644\n--- a/arch/arm/mach-imx/imx9/scmi/soc.c\n+++ b/arch/arm/mach-imx/imx9/scmi/soc.c\n@@ -310,6 +310,13 @@ static struct mm_region imx9_mem_map[] = {\n \t\t.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |\n \t\t\t PTE_BLOCK_NON_SHARE |\n \t\t\t PTE_BLOCK_PXN | PTE_BLOCK_UXN\n+\t}, {\n+\t\t/* QB data */\n+\t\t.virt = CONFIG_QB_SAVED_STATE_BASE,\n+\t\t.phys = CONFIG_QB_SAVED_STATE_BASE,\n+\t\t.size = 0x200000UL,\t/* 2M */\n+\t\t.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |\n+\t\t\t PTE_BLOCK_OUTER_SHARE\n \t}, {\n \t\t/* empty entry to split table entry 5 if needed when TEEs are used */\n \t\t0,\ndiff --git a/drivers/ddr/imx/imx9/Kconfig b/drivers/ddr/imx/imx9/Kconfig\nindex b953bca4f06..7b3dbf53dff 100644\n--- a/drivers/ddr/imx/imx9/Kconfig\n+++ b/drivers/ddr/imx/imx9/Kconfig\n@@ -29,4 +29,11 @@ config SAVED_DRAM_TIMING_BASE\n \t after DRAM is trained, need to save the dram related timming\n \t info into memory for low power use.\n \n+config QB_SAVED_STATE_BASE\n+\thex \"Define the base address for saved QuickBoot state\"\n+\tdefault 0x8fe00000\n+\thelp\n+\t Once DRAM is trained, the resulted training info is\n+\t saved into memory in order to be reachable from U-Boot.\n+\n endmenu\n", "prefixes": [ "v4", "1/4" ] }