From patchwork Wed Mar 19 20:20:35 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062980 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=iTDwo7xw; 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 4ZJ0Tk2r6Mz1xrn for ; Thu, 20 Mar 2025 07:21:14 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 1DF7C8197D; Wed, 19 Mar 2025 21:21:06 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="iTDwo7xw"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 17B4181C67; Wed, 19 Mar 2025 21:21:03 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx1.sberdevices.ru [37.18.73.165]) (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 2A53A810F4 for ; Wed, 19 Mar 2025 21:20:59 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk01.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 6858110000D; Wed, 19 Mar 2025 23:20:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 6858110000D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415658; bh=H3WE+GS7B6YVabiOijgU/qFvdi56abgkSCgGv3g16sM=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=iTDwo7xw6hyus4AAcjCZUliNdp8PKOIddTy1rzlUMfOkBMxYfa7oQimZE/O0DS/rW fTuNBVe8J23vWyKvz3w8T469BAM9dG+6BLV/XzoHs9JIE8teDZSOED2JXtHaW4bnI8 SsT1I8n2pzplwf9tvZ5YBOORZrvRW9JiqZOLy9eBP38XEtRHtUu6o9hzNVpDYjOLwE HTuBezhj/XrpwvmxJVczUz4UVeZO4wUwLpAeT2bm3L0eg+2KBUlNydMTAbOjL4d13N m8IATH/Vp4biXqkAUwQDteH4oFAK89fqA4DcF40NTuVZ4eYlb511nIxP7l5c1MKs2C R2AqKyFlcRVIA== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:20:58 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , , Vladimir Mitrofanov , Evgeny Bachinin Subject: [PATCH v1 01/11] arch: arm: meson: sm: add commands to reboot device in different modes Date: Wed, 19 Mar 2025 23:20:35 +0300 Message-ID: <20250319202045.1407-2-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; salutedevices.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 17:18:00 #27803216 X-KSMG-AntiVirus-Status: Clean, skipped 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: Vladimir Mitrofanov There are several commands to reboot device in specific mode (cold, normal, etc.), these commands helps to enter flashing mode and set boot source device: 1) MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET This command tells BL1 that it must load BL2 from USB data path. It is used in ADNL protocol logic when burning process is started from the U-Boot command line: when 'adnl' is entered, this command tells BL1 to load BL2 from USB, then board is rebooted. After reboot BL2 and next stage loaders will be loaded from USB, sticky register has value that we are in ADNL mode, thus booted U-Boot enters ADNL mode to start data burning. Used in adnl-based SoC families: a1, s4, c1, с2, sc2, t7, etc. 2) MESON_SMC_CMD_REBOOT_MODE_SET Used in optimus-based SoC families: axg, g12a, g12b, sm1, etc. 3) MESON_SMC_CMD_USB_BOOT_MODE_SET Used in optimus-based SoC families: axg, g12a, g12b, sm1, etc. Signed-off-by: Arseniy Krasnov Signed-off-by: Vladimir Mitrofanov Signed-off-by: Evgeny Bachinin --- arch/arm/include/asm/arch-meson/sm.h | 98 +++++++++++++++++++++++++++- arch/arm/mach-meson/sm.c | 39 +++++++++++ drivers/sm/meson-sm.c | 3 + include/meson/sm.h | 3 + 4 files changed, 142 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/arch-meson/sm.h b/arch/arm/include/asm/arch-meson/sm.h index 4d614955fc2..087f8b4d5fa 100644 --- a/arch/arm/include/asm/arch-meson/sm.h +++ b/arch/arm/include/asm/arch-meson/sm.h @@ -83,7 +83,7 @@ int meson_sm_get_serial(void *buffer, size_t size); */ int meson_sm_get_chip_id(struct meson_sm_chip_id *chip_id); -enum { +enum meson_reboot_mode { REBOOT_REASON_COLD = 0, REBOOT_REASON_NORMAL = 1, REBOOT_REASON_RECOVERY = 2, @@ -134,4 +134,100 @@ int meson_sm_pwrdm_set(size_t index, int cmd); #define meson_sm_pwrdm_on(index) \ meson_sm_pwrdm_set(index, PWRDM_ON) +/* + * Below is the list of SCPI_CMD commands, issued by U-Boot and passed to SCP + * co-processor via SMC to BL31 (TF-A). BL31 routes these commands to SCP + * via mailbox. + * + * Commands instruct the SCP how to set/control POC pins in order to choose + * first boot source, specified by the particular command below. These POC pins + * are readable by BootROM and used to determine the boot sequence. Usually, + * the normal default boot sequence, seen on production device is + * NAND/eMMC -> SD -> USB + * + * Commands below change the first boot source. + * + * NOTE: + * these commands are used along with meson_sm_set_bl1_first_boot_source() on + * Amlogic SoCs where ADNL burning protocol is utilized. + * Refer to meson_sm_set_usb_boot_mode() for the same functionality but on SoCs + * with Optimus burning protocol. + */ + +/** + * define SCPI_CMD_USB_BOOT - sets the first boot source into USB with timeout + * + * Additionally, force the BootROM (BL1) to wait for the Host's enumeration + * with timeout + */ +#define SCPI_CMD_USB_BOOT 0xB0 + +/** + * define SCPI_CMD_USB_UNBOOT - sets the first boot source into USB w/o timeout + * + * Additionally, force the BootROM to wait for the Host's enumeration with + * uninterruptible wait, i.e. forever + */ +#define SCPI_CMD_USB_UNBOOT 0xB1 + +/** + * define SCPI_CMD_SDCARD_BOOT - sets the first boot source into SD card + */ +#define SCPI_CMD_SDCARD_BOOT 0xB2 + +/** + * define SCPI_CMD_CLEAR_BOOT - sets the first boot source into default one + * + * BootROM follows a pre-defined boot sequence stored in SoC ROM code. + */ +#define SCPI_CMD_CLEAR_BOOT 0xB3 + +/** + * meson_sm_set_bl1_first_boot_source - sets the first boot source for BootROM. + * @cmd: command to set first boot source visible to BootROM during boot. + * Use SCPI_CMD_* as commands. + * + * BootROM checks POC-pins (POC0, POC1, POC2) to determine in which order it + * must probe the various methods of booting the system. The order is usually + * pre-defined. But function changes the first boot source of that pre-defined + * order. + * + * @return: zero on success or error code on failure. + */ +int meson_sm_set_bl1_first_boot_source(u64 cmd); + +/** + * meson_sm_reboot - reboots device in a appropriate mode + * All possible modes described in &enum meson_reboot_mode + * + * @mode: reboot mode. + * @return: zero on success or error code on failure. + */ +int meson_sm_reboot(u64 mode); + +/** + * enum meson_usb_boot_mode - modes for meson_sm_set_usb_boot_mode() + * @FORCE_USB_BOOT: Forces BootROM to boot with firmware transferred by PC + * burning tool via USB. This mode has influence despite of the + * previously selected boot source. This mode persists between + * reboots and must be cleared. + * @CLEAR_USB_BOOT: Clears previous mode. + * + * These modes are used in conjunction with meson_sm_set_usb_boot_mode() on + * Amlogic SoCs where Optimus burning protocol is utilized. + */ +enum meson_usb_boot_mode { + CLEAR_USB_BOOT = 1, + FORCE_USB_BOOT = 2, +}; + +/** + * meson_sm_set_usb_boot_mode - sets the mode of usb interface that will be used during boot + * All possible modes described in &enum meson_usb_boot_mode + * + * @mode: reboot mode. + * @return: zero on success or error code on failure. + */ +int meson_sm_set_usb_boot_mode(u64 mode); + #endif /* __MESON_SM_H__ */ diff --git a/arch/arm/mach-meson/sm.c b/arch/arm/mach-meson/sm.c index b1f91ca29ce..53b07e1dfe9 100644 --- a/arch/arm/mach-meson/sm.c +++ b/arch/arm/mach-meson/sm.c @@ -236,6 +236,17 @@ int meson_sm_get_reboot_reason(void) return FIELD_GET(REBOOT_REASON_MASK, reason); } +static int meson_sm_call(u32 smc, struct pt_regs *regs, s32 *retval) +{ + struct udevice *dev; + + dev = meson_get_sm_device(); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + return sm_call(dev, smc, retval, regs); +} + int meson_sm_pwrdm_set(size_t index, int cmd) { struct udevice *dev; @@ -256,3 +267,31 @@ int meson_sm_pwrdm_set(size_t index, int cmd) return err; } + +int meson_sm_set_bl1_first_boot_source(u64 cmd) +{ + struct pt_regs regs = { 0 }; + + regs.regs[1] = cmd; + + return meson_sm_call(MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET, + ®s, NULL); +} + +int meson_sm_reboot(u64 mode) +{ + struct pt_regs regs = { 0 }; + + regs.regs[1] = mode; + + return meson_sm_call(MESON_SMC_CMD_REBOOT_MODE_SET, ®s, NULL); +} + +int meson_sm_set_usb_boot_mode(u64 mode) +{ + struct pt_regs regs = { 0 }; + + regs.regs[1] = mode; + + return meson_sm_call(MESON_SMC_CMD_USB_BOOT_MODE_SET, ®s, NULL); +} diff --git a/drivers/sm/meson-sm.c b/drivers/sm/meson-sm.c index 87eba1486db..f413d8335b3 100644 --- a/drivers/sm/meson-sm.c +++ b/drivers/sm/meson-sm.c @@ -168,8 +168,11 @@ static const struct meson_sm_data meson_sm_gxbb_data = { .cmd = { SET_CMD(MESON_SMC_CMD_EFUSE_READ, 0x82000030), SET_CMD(MESON_SMC_CMD_EFUSE_WRITE, 0x82000031), + SET_CMD(MESON_SMC_CMD_USB_BOOT_MODE_SET, 0x82000043), SET_CMD(MESON_SMC_CMD_CHIP_ID_GET, 0x82000044), + SET_CMD(MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET, 0x82000087), SET_CMD(MESON_SMC_CMD_PWRDM_SET, 0x82000093), + SET_CMD(MESON_SMC_CMD_REBOOT_MODE_SET, 0x84000009), }, }; diff --git a/include/meson/sm.h b/include/meson/sm.h index fbaab1f1ee0..b9621f95cbe 100644 --- a/include/meson/sm.h +++ b/include/meson/sm.h @@ -13,6 +13,9 @@ enum meson_smc_cmd { MESON_SMC_CMD_EFUSE_WRITE, /* write efuse memory */ MESON_SMC_CMD_CHIP_ID_GET, /* readh chip unique id */ MESON_SMC_CMD_PWRDM_SET, /* do command at specified power domain */ + MESON_SMC_CMD_BL1_FIRST_BOOT_SRC_SET, /* setup first boot source for BootROM */ + MESON_SMC_CMD_REBOOT_MODE_SET, /* Reboot device in desired mode */ + MESON_SMC_CMD_USB_BOOT_MODE_SET, /* Run boot from USB device */ MESON_SMC_CMD_COUNT, }; From patchwork Wed Mar 19 20:20:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062986 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=jqdNn4ex; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (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 4ZJ0W259jzz1xrn for ; Thu, 20 Mar 2025 07:22:22 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 4D6E781DD6; Wed, 19 Mar 2025 21:21:17 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="jqdNn4ex"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 89F5E81DE9; Wed, 19 Mar 2025 21:21:08 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (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 4F5398141E for ; Wed, 19 Mar 2025 21:20:59 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id BE65D12000F; Wed, 19 Mar 2025 23:20:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru BE65D12000F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415658; bh=OQyPAOK2ADmMl55epTEU3v6mVEbLzjkaxe9Gd/aD0NM=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=jqdNn4exNta+f17o3AHYiT8TpWwcIse/RwUifTCXk8ArBWblIiozYU+JvSzCh7M89 oXXS/Hb8uXgA78llcZxdWDtvjksTUhNETVJbfF3OJPVJsPbB8G/jB6jDJl2t429v6/ C0mz0rret5EZRLxTuZYSGdOdtAFv3Q8mGgrp+LGcO1F8OT7NC2SJNqHwpn0V8Ptzpy mNC0U3SKmCmhPTfb1ImO8b2LgYW4WSE4NEkeyXZ4OA3AgAOIKmVBCg/xLwZUIuqimc BkI/644/ApqOCEuu2Uc6HYHjEFOJunk9IGAxxy4S21Z5KgpnxCHSYwuJAVH7M3o4Fx xDm3ikUENxZ2Q== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:20:58 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 02/11] arch: arm: meson: use 'meson_sm_call()' in 'meson_sm_pwrdm_set()' Date: Wed, 19 Mar 2025 23:20:36 +0300 Message-ID: <20250319202045.1407-3-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; salutedevices.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 19:45:00 #27804052 X-KSMG-AntiVirus-Status: Clean, skipped 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 Use more generic function in 'meson_sm_pwrdm_set()'. Signed-off-by: Arseniy Krasnov --- arch/arm/mach-meson/sm.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/arm/mach-meson/sm.c b/arch/arm/mach-meson/sm.c index 53b07e1dfe9..0a781e9c47b 100644 --- a/arch/arm/mach-meson/sm.c +++ b/arch/arm/mach-meson/sm.c @@ -249,22 +249,16 @@ static int meson_sm_call(u32 smc, struct pt_regs *regs, s32 *retval) int meson_sm_pwrdm_set(size_t index, int cmd) { - struct udevice *dev; struct pt_regs regs = { 0 }; int err; - dev = meson_get_sm_device(); - if (IS_ERR(dev)) - return PTR_ERR(dev); - regs.regs[1] = index; regs.regs[2] = cmd; - err = sm_call(dev, MESON_SMC_CMD_PWRDM_SET, NULL, ®s); + err = meson_sm_call(MESON_SMC_CMD_PWRDM_SET, ®s, NULL); if (err) pr_err("Failed to %s power domain ind=%zu (%d)\n", cmd == PWRDM_ON ? "enable" : "disable", index, err); - return err; } From patchwork Wed Mar 19 20:20:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062982 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=VRZUViPf; 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 4ZJ0V86vSQz1xrn for ; Thu, 20 Mar 2025 07:21:36 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id B752481D5A; Wed, 19 Mar 2025 21:21:06 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="VRZUViPf"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 3F4D381D3B; Wed, 19 Mar 2025 21:21:05 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx1.sberdevices.ru [37.18.73.165]) (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 BB4AC8193A for ; Wed, 19 Mar 2025 21:20:59 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk01.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 2DB76100012; Wed, 19 Mar 2025 23:20:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 2DB76100012 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415659; bh=rTEv5jRKvBhPuHncn2gTz1h76Na5PC+0ObMbSI3BuB0=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=VRZUViPfHU7YYEAdAOh+7lobNvL8wf8KhdGR6szYmG5PqImc0k09ERbMoXyPznuem 0iLYKvC6myGYEB0E5tl9lx5VMNR1nJvEzytGjLrXd1fj0XtOYwRYIP9iFLUtzLz3Ne P6/VpQUiApq5jOCdEigrrN4D/4J+8WYCLIlV+4Rzkhp68VpwYx3msmSy8A0+KYuSz9 1fZ3UllEpVP5U1X+q00o+UXjFb0CXEuiYgzeUQnht5ZDRhUtSDxkdjAkJxYh/WguVo fvCBWHPXLNudCbSpSpCFHl9WtbDBbXX5GFknZdNbHwFqlIxHJpPPcyVPfHP0+wlq5U IrJS+4+Hp8yiA== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:20:58 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 03/11] usb: gadget: fastboot: make part of USB fastboot code shared Date: Wed, 19 Mar 2025 23:20:37 +0300 Message-ID: <20250319202045.1407-4-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_uf_ne_domains}, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; salutedevices.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean, bases: 2025/03/19 19:47:00 X-KSMG-LinksScanning: Clean, bases: 2025/03/19 19:47:00 X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 17:18:00 #27803216 X-KSMG-AntiVirus-Status: Clean, skipped 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 This is preparation for fastboot-based flashing protocols (such as Amlogic ADNL). When device enters ADNL mode, it becomes "fastboot" device from USB point of view, so to avoid copy-paste of USB part of fastboot protocol, reimplement it as shared code between fastboot and ADNL implementations. Signed-off-by: Arseniy Krasnov --- drivers/usb/gadget/Makefile | 2 +- drivers/usb/gadget/f_fastboot.c | 295 +---------------------- drivers/usb/gadget/f_fastboot_common.c | 320 +++++++++++++++++++++++++ drivers/usb/gadget/f_fastboot_common.h | 71 ++++++ 4 files changed, 403 insertions(+), 285 deletions(-) create mode 100644 drivers/usb/gadget/f_fastboot_common.c create mode 100644 drivers/usb/gadget/f_fastboot_common.h diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4bda224ff1a..6c16ad83f6e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o obj-$(CONFIG_DFU_OVER_USB) += f_dfu.o obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o -obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o f_fastboot_common.o obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o obj-$(CONFIG_USB_FUNCTION_ROCKUSB) += f_rockusb.o obj-$(CONFIG_USB_FUNCTION_ACM) += f_acm.o diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 3c76d42c5ef..9298658b569 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -23,29 +23,7 @@ #include #include #include - -#define FASTBOOT_INTERFACE_CLASS 0xff -#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 -#define FASTBOOT_INTERFACE_PROTOCOL 0x03 - -#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0 (0x0200) -#define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1 (0x0040) -#define TX_ENDPOINT_MAXIMUM_PACKET_SIZE (0x0040) - -#define EP_BUFFER_SIZE 4096 -/* - * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size - * (64 or 512 or 1024), else we break on certain controllers like DWC3 - * that expect bulk OUT requests to be divisible by maxpacket size. - */ - -struct f_fastboot { - struct usb_function usb_function; - - /* IN/OUT EP's and corresponding requests */ - struct usb_ep *in_ep, *out_ep; - struct usb_request *in_req, *out_req; -}; +#include "f_fastboot_common.h" static char fb_ext_prop_name[] = "DeviceInterfaceGUID"; static char fb_ext_prop_data[] = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}"; @@ -66,113 +44,7 @@ static struct usb_os_desc_table fb_os_desc_table = { .os_desc = &fb_os_desc, }; -static inline struct f_fastboot *func_to_fastboot(struct usb_function *f) -{ - return container_of(f, struct f_fastboot, usb_function); -} - -static struct f_fastboot *fastboot_func; - -static struct usb_endpoint_descriptor fs_ep_in = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(64), -}; - -static struct usb_endpoint_descriptor fs_ep_out = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(64), -}; - -static struct usb_endpoint_descriptor hs_ep_in = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), -}; - -static struct usb_endpoint_descriptor hs_ep_out = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512), -}; - -static struct usb_interface_descriptor interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0x00, - .bAlternateSetting = 0x00, - .bNumEndpoints = 0x02, - .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, - .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, - .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, -}; - -static struct usb_descriptor_header *fb_fs_function[] = { - (struct usb_descriptor_header *)&interface_desc, - (struct usb_descriptor_header *)&fs_ep_in, - (struct usb_descriptor_header *)&fs_ep_out, - NULL, -}; - -static struct usb_descriptor_header *fb_hs_function[] = { - (struct usb_descriptor_header *)&interface_desc, - (struct usb_descriptor_header *)&hs_ep_in, - (struct usb_descriptor_header *)&hs_ep_out, - NULL, -}; - -/* Super speed */ -static struct usb_endpoint_descriptor ss_ep_in = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(1024), -}; - -static struct usb_endpoint_descriptor ss_ep_out = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(1024), -}; - -static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = { - .bLength = sizeof(fb_ss_bulk_comp_desc), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, -}; - -static struct usb_descriptor_header *fb_ss_function[] = { - (struct usb_descriptor_header *)&interface_desc, - (struct usb_descriptor_header *)&ss_ep_in, - (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, - (struct usb_descriptor_header *)&ss_ep_out, - (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, - NULL, -}; - -static struct usb_endpoint_descriptor * -fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *ss) -{ - if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER) - return ss; - - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) - return hs; - return fs; -} +static struct fastboot_funcs *fastboot_func; /* * static strings, in UTF-8 @@ -196,179 +68,34 @@ static struct usb_gadget_strings *fastboot_strings[] = { static void rx_handler_command(struct usb_ep *ep, struct usb_request *req); -static void fastboot_complete(struct usb_ep *ep, struct usb_request *req) -{ - int status = req->status; - if (!status) - return; - printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); -} - static int fastboot_bind(struct usb_configuration *c, struct usb_function *f) { - int id; - struct usb_gadget *gadget = c->cdev->gadget; - struct f_fastboot *f_fb = func_to_fastboot(f); - const char *s; - - /* DYNAMIC interface numbers assignments */ - id = usb_interface_id(c, f); - if (id < 0) - return id; - interface_desc.bInterfaceNumber = id; - - /* Enable OS and Extended Properties Feature Descriptor */ - c->cdev->use_os_string = 1; - f->os_desc_table = &fb_os_desc_table; - f->os_desc_n = 1; - f->os_desc_table->if_id = id; - INIT_LIST_HEAD(&fb_os_desc.ext_prop); - fb_ext_prop.name_len = strlen(fb_ext_prop.name) * 2 + 2; - fb_os_desc.ext_prop_len = 10 + fb_ext_prop.name_len; - fb_os_desc.ext_prop_count = 1; - fb_ext_prop.data_len = strlen(fb_ext_prop.data) * 2 + 2; - fb_os_desc.ext_prop_len += fb_ext_prop.data_len + 4; - list_add_tail(&fb_ext_prop.entry, &fb_os_desc.ext_prop); - - id = usb_string_id(c->cdev); - if (id < 0) - return id; - fastboot_string_defs[0].id = id; - interface_desc.iInterface = id; - - f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); - if (!f_fb->in_ep) - return -ENODEV; - f_fb->in_ep->driver_data = c->cdev; - - f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); - if (!f_fb->out_ep) - return -ENODEV; - f_fb->out_ep->driver_data = c->cdev; - - f->descriptors = fb_fs_function; - - if (gadget_is_dualspeed(gadget)) { - /* Assume endpoint addresses are the same for both speeds */ - hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; - hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; - /* copy HS descriptors */ - f->hs_descriptors = fb_hs_function; - } - - if (gadget_is_superspeed(gadget)) { - ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; - ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; - f->ss_descriptors = fb_ss_function; - } - - s = env_get("serial#"); - if (s) - g_dnl_set_serialnumber((char *)s); - - return 0; + return fastboot_common_bind(c, f, &fb_os_desc_table, &fb_os_desc, + (struct usb_string **)&fastboot_string_defs, + &fb_ext_prop); } static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f) { - f->os_desc_table = NULL; - list_del(&fb_os_desc.ext_prop); + fastboot_common_unbind(c, f, &fb_os_desc); memset(fastboot_func, 0, sizeof(*fastboot_func)); } static void fastboot_disable(struct usb_function *f) { - struct f_fastboot *f_fb = func_to_fastboot(f); - - usb_ep_disable(f_fb->out_ep); - usb_ep_disable(f_fb->in_ep); - - if (f_fb->out_req) { - free(f_fb->out_req->buf); - usb_ep_free_request(f_fb->out_ep, f_fb->out_req); - f_fb->out_req = NULL; - } - if (f_fb->in_req) { - free(f_fb->in_req->buf); - usb_ep_free_request(f_fb->in_ep, f_fb->in_req); - f_fb->in_req = NULL; - } -} - -static struct usb_request *fastboot_start_ep(struct usb_ep *ep) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, 0); - if (!req) - return NULL; - - req->length = EP_BUFFER_SIZE; - req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - - memset(req->buf, 0, req->length); - return req; + fastboot_common_disable(f); } static int fastboot_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { - int ret; - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_gadget *gadget = cdev->gadget; - struct f_fastboot *f_fb = func_to_fastboot(f); - const struct usb_endpoint_descriptor *d; - - debug("%s: func: %s intf: %d alt: %d\n", - __func__, f->name, interface, alt); - - d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out); - ret = usb_ep_enable(f_fb->out_ep, d); - if (ret) { - puts("failed to enable out ep\n"); - return ret; - } - - f_fb->out_req = fastboot_start_ep(f_fb->out_ep); - if (!f_fb->out_req) { - puts("failed to alloc out req\n"); - ret = -EINVAL; - goto err; - } - f_fb->out_req->complete = rx_handler_command; - - d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in); - ret = usb_ep_enable(f_fb->in_ep, d); - if (ret) { - puts("failed to enable in ep\n"); - goto err; - } - - f_fb->in_req = fastboot_start_ep(f_fb->in_ep); - if (!f_fb->in_req) { - puts("failed alloc req in\n"); - ret = -EINVAL; - goto err; - } - f_fb->in_req->complete = fastboot_complete; - - ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0); - if (ret) - goto err; - - return 0; -err: - fastboot_disable(f); - return ret; + return fastboot_common_set_alt(f, interface, alt, + rx_handler_command); } static int fastboot_add(struct usb_configuration *c) { - struct f_fastboot *f_fb = fastboot_func; + struct fastboot_funcs *f_fb = fastboot_func; int status; debug("%s: cdev: 0x%p\n", __func__, c->cdev); @@ -515,7 +242,7 @@ static void multiresponse_on_complete(struct usb_ep *ep, struct usb_request *req /* If response is final OKAY/FAIL response disconnect this handler and unset cmd */ if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) { multiresponse_cmd = -1; - fastboot_func->in_req->complete = fastboot_complete; + fastboot_func->in_req->complete = fastboot_common_complete; } } diff --git a/drivers/usb/gadget/f_fastboot_common.c b/drivers/usb/gadget/f_fastboot_common.c new file mode 100644 index 00000000000..9e509503d2d --- /dev/null +++ b/drivers/usb/gadget/f_fastboot_common.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 - 2009 + * Windriver, + * Tom Rix + * + * Copyright 2011 Sebastian Andrzej Siewior + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring + * + * Copyright (c) 2025 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#include +#include "f_fastboot_common.h" + +static struct usb_endpoint_descriptor fs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_endpoint_descriptor fs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), +}; + +static struct usb_interface_descriptor interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = FASTBOOT_INTERFACE_CLASS, + .bInterfaceSubClass = FASTBOOT_INTERFACE_SUB_CLASS, + .bInterfaceProtocol = FASTBOOT_INTERFACE_PROTOCOL, +}; + +static struct usb_descriptor_header *fb_fs_function[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&fs_ep_in, + (struct usb_descriptor_header *)&fs_ep_out, + NULL, +}; + +static struct usb_endpoint_descriptor hs_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor hs_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_descriptor_header *fb_hs_function[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&hs_ep_in, + (struct usb_descriptor_header *)&hs_ep_out, + NULL, +}; + +/* Super speed */ +static struct usb_endpoint_descriptor ss_ep_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_ep_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fb_ss_bulk_comp_desc = { + .bLength = sizeof(fb_ss_bulk_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *fb_ss_function[] = { + (struct usb_descriptor_header *)&interface_desc, + (struct usb_descriptor_header *)&ss_ep_in, + (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, + (struct usb_descriptor_header *)&ss_ep_out, + (struct usb_descriptor_header *)&fb_ss_bulk_comp_desc, + NULL, +}; + +static struct usb_endpoint_descriptor *fastboot_common_ep_desc(struct usb_gadget *g, + struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *ss) +{ + if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER) + return ss; + + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +void fastboot_common_complete(struct usb_ep *ep, struct usb_request *req) +{ + int status = req->status; + + if (!status) + return; + + printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual); +} + +int fastboot_common_bind(struct usb_configuration *c, struct usb_function *f, + struct usb_os_desc_table *fb_os_desc_table, + struct usb_os_desc *fb_os_desc, + struct usb_string **fastboot_string_defs, + struct usb_os_desc_ext_prop *fb_ext_prop) +{ + int id; + struct usb_gadget *gadget = c->cdev->gadget; + struct fastboot_funcs *f_fb = func_to_fastboot_funcs(f); + const char *s; + + /* DYNAMIC interface numbers assignments */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + + interface_desc.bInterfaceNumber = id; + + /* Enable OS and Extended Properties Feature Descriptor */ + c->cdev->use_os_string = 1; + f->os_desc_table = fb_os_desc_table; + f->os_desc_n = 1; + f->os_desc_table->if_id = id; + INIT_LIST_HEAD(&fb_os_desc->ext_prop); + fb_ext_prop->name_len = strlen(fb_ext_prop->name) * 2 + 2; + fb_os_desc->ext_prop_len = 10 + fb_ext_prop->name_len; + fb_os_desc->ext_prop_count = 1; + fb_ext_prop->data_len = strlen(fb_ext_prop->data) * 2 + 2; + fb_os_desc->ext_prop_len += fb_ext_prop->data_len + 4; + list_add_tail(&fb_ext_prop->entry, &fb_os_desc->ext_prop); + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + + fastboot_string_defs[0]->id = id; + interface_desc.iInterface = id; + + f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in); + if (!f_fb->in_ep) + return -ENODEV; + + f_fb->in_ep->driver_data = c->cdev; + + f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out); + if (!f_fb->out_ep) + return -ENODEV; + + f_fb->out_ep->driver_data = c->cdev; + + f->descriptors = fb_fs_function; + + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + /* copy HS descriptors */ + f->hs_descriptors = fb_hs_function; + } + + if (gadget_is_superspeed(gadget)) { + ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress; + ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress; + f->ss_descriptors = fb_ss_function; + } + + s = env_get("serial#"); + if (s) + g_dnl_set_serialnumber((char *)s); + + return 0; +} + +void fastboot_common_unbind(struct usb_configuration *c, + struct usb_function *f, + struct usb_os_desc *fb_os_desc) +{ + f->os_desc_table = NULL; + list_del(&fb_os_desc->ext_prop); +} + +static void __fastboot_common_disable(struct usb_function *f, bool disable_out, bool disable_in) +{ + struct fastboot_funcs *f_fb = func_to_fastboot_funcs(f); + + if (disable_out) + usb_ep_disable(f_fb->out_ep); + + if (disable_in) + usb_ep_disable(f_fb->in_ep); + + if (f_fb->out_req) { + free(f_fb->out_req->buf); + usb_ep_free_request(f_fb->out_ep, f_fb->out_req); + f_fb->out_req = NULL; + } + if (f_fb->in_req) { + free(f_fb->in_req->buf); + usb_ep_free_request(f_fb->in_ep, f_fb->in_req); + f_fb->in_req = NULL; + } +} + +void fastboot_common_disable(struct usb_function *f) +{ + return __fastboot_common_disable(f, true, true); +} + +static struct usb_request *fastboot_common_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return NULL; + + req->length = EP_BUFFER_SIZE; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + memset(req->buf, 0, req->length); + return req; +} + +int fastboot_common_set_alt(struct usb_function *f, + unsigned int interface, unsigned int alt, + void (*rx_handler)(struct usb_ep *ep, struct usb_request *req)) +{ + int ret; + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct fastboot_funcs *f_adnl = func_to_fastboot_funcs(f); + const struct usb_endpoint_descriptor *d; + bool disable_out = false; + bool disable_in = false; + + debug("%s: func: %s intf: %u alt: %u\n", + __func__, f->name, interface, alt); + + d = fastboot_common_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out); + ret = usb_ep_enable(f_adnl->out_ep, d); + if (ret) { + puts("failed to enable out ep\n"); + return ret; + } + + disable_out = true; + + f_adnl->out_req = fastboot_common_start_ep(f_adnl->out_ep); + if (!f_adnl->out_req) { + puts("failed to alloc out req\n"); + ret = -EINVAL; + goto err; + } + + f_adnl->out_req->complete = rx_handler; + + d = fastboot_common_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in); + ret = usb_ep_enable(f_adnl->in_ep, d); + if (ret) { + puts("failed to enable in ep\n"); + goto err; + } + + disable_in = true; + + f_adnl->in_req = fastboot_common_start_ep(f_adnl->in_ep); + if (!f_adnl->in_req) { + puts("failed alloc req in\n"); + ret = -EINVAL; + goto err; + } + f_adnl->in_req->complete = fastboot_common_complete; + + ret = usb_ep_queue(f_adnl->out_ep, f_adnl->out_req, 0); + if (ret) + goto err; + + return 0; +err: + __fastboot_common_disable(f, disable_out, disable_in); + return ret; +} + +struct fastboot_funcs *func_to_fastboot_funcs(struct usb_function *f) +{ + return container_of(f, struct fastboot_funcs, usb_function); +} diff --git a/drivers/usb/gadget/f_fastboot_common.h b/drivers/usb/gadget/f_fastboot_common.h new file mode 100644 index 00000000000..dba7d7003ba --- /dev/null +++ b/drivers/usb/gadget/f_fastboot_common.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2008 - 2009 + * Windriver, + * Tom Rix + * + * Copyright 2011 Sebastian Andrzej Siewior + * + * Copyright 2014 Linaro, Ltd. + * Rob Herring + * + * Copyright (c) 2025 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#ifndef __F_FASTBOOT_COMMON_H_ +#define __F_FASTBOOT_COMMON_H_ +#include + +/* + * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size + * (64 or 512 or 1024), else we break on certain controllers like DWC3 + * that expect bulk OUT requests to be divisible by maxpacket size. + */ +#define EP_BUFFER_SIZE 4096 + +#define FASTBOOT_INTERFACE_CLASS 0xff +#define FASTBOOT_INTERFACE_SUB_CLASS 0x42 +#define FASTBOOT_INTERFACE_PROTOCOL 0x03 + +struct fastboot_funcs { + struct usb_function usb_function; + + /* IN/OUT EP's and corresponding requests */ + struct usb_ep *in_ep, *out_ep; + struct usb_request *in_req, *out_req; +}; + +void fastboot_common_complete(struct usb_ep *ep, struct usb_request *req); + +/* + * Initializes USB function 'f' with provided resources and binds it to the + * USB configuration 'c'. + */ +int fastboot_common_bind(struct usb_configuration *c, struct usb_function *f, + struct usb_os_desc_table *fb_os_desc_table, + struct usb_os_desc *fb_os_desc, + struct usb_string **fastboot_string_defs, + struct usb_os_desc_ext_prop *fb_ext_prop); + +/* Reverse for 'fastboot_bind_common()'. */ +void fastboot_common_unbind(struct usb_configuration *c, + struct usb_function *f, + struct usb_os_desc *fb_os_desc); + +/* + * Setups endpoints, allocates memory and sets callbacks for both IN and + * OUT requests. + */ +int fastboot_common_set_alt(struct usb_function *f, + unsigned int interface, unsigned int alt, + void (*rx_handler)(struct usb_ep *ep, + struct usb_request *req)); + +/* Disables provided USB function 'f'. */ +void fastboot_common_disable(struct usb_function *f); + +/* Returns parent struct of 'f'. */ +struct fastboot_funcs *func_to_fastboot_funcs(struct usb_function *f); + +#endif /* __F_FASTBOOT_COMMON_H_ */ From patchwork Wed Mar 19 20:20:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062984 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=k+7Ut2ag; 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 4ZJ0Vg1wk5z1xrn for ; Thu, 20 Mar 2025 07:22:03 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id AE83B81D0B; Wed, 19 Mar 2025 21:21:16 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="k+7Ut2ag"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id CF910810F2; Wed, 19 Mar 2025 21:21:05 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (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 E9FC88198A for ; Wed, 19 Mar 2025 21:20:59 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 63A06120011; Wed, 19 Mar 2025 23:20:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 63A06120011 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415659; bh=ILk/xCgiNIi3MmSne25n+5S5JV5ESGJWkXGtCFAVYL0=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=k+7Ut2agEW56cq7iIWCah4GBtiK1cnEOvOvH4WKRUKIlodCMp9ZGI/uFrqAiIRsy7 Tt9N/o5z+b09B4lfGkPfGbfJuRMHrA4bATmz1YQk5G1gdCIkL9tnRjIms/u/dEQ++D Rs/mKZcml5GOLVlSwn4r0A1dW+qkA03jjTwRq5Xwlog/jyJRfkIllWCRilnOe6q8To vQPaes5js/oFcBVh2ly9+Dpan+MDud6BgkraVzWqCLE+25pedUXuSCklWjDuLw8i71 OL+mLGwF1JpwLo0ge/eQgNQ6j9JfoVbLESPy31RYQ9+zM2qzh3ultUxJek6g/O+oGa J8r0zRQ+pOSMQ== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:20:59 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 04/11] usb: gadget: amlogic: common code for Amlogic flashing commands Date: Wed, 19 Mar 2025 23:20:38 +0300 Message-ID: <20250319202045.1407-5-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; salutedevices.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 19:45:00 #27804052 X-KSMG-AntiVirus-Status: Clean, skipped 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 Amlogic has Optimus and ADNL protocols to update firmware image. Both are operatable by special commands and such commands use same approaches, so let's implement such code before implementing both protocols. Signed-off-by: Arseniy Krasnov --- cmd/meson/gadget.c | 183 +++++++++++++++++++++++++++++++++++++++++++++ cmd/meson/gadget.h | 28 +++++++ 2 files changed, 211 insertions(+) create mode 100644 cmd/meson/gadget.c create mode 100644 cmd/meson/gadget.h diff --git a/cmd/meson/gadget.c b/cmd/meson/gadget.c new file mode 100644 index 00000000000..6155043ae5a --- /dev/null +++ b/cmd/meson/gadget.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BURN_BOOT_UDELAY (2 * 1000 * 1000) +static unsigned long burn_identify_time_ms; +static unsigned long burn_start_time_ms; + +void amlogic_gadget_set_times(unsigned long start_time, unsigned long identify_time) +{ + burn_start_time_ms = start_time; + burn_identify_time_ms = identify_time; +} + +/* + * This function utilizes serial from inside Amlogic chip_id with one + * distinctive feature that this serial must be in big-endian. + * + * This is needed, because serial string, utilized in BootROM, must match + * the serial string, used inside U-Boot. But BootROM utilizes serial string + * in big-endian: being in BootROM try + * - for new SoCs (a1, s4, etc - those which utilize ADNL protocol): run + * host command 'fastboot devices' + * - for old SoCs (axg, g12a, g12b, etc - those, utilizing optimus protocol): + * send "chipid" command to bootROM + */ +static int amlogic_serial_hex(char serial_str[SM_SERIAL_SIZE * 2 + 1]) +{ + u8 serial[SM_SERIAL_SIZE]; + int ret; + + /* + * ADNL/OPTIMUS uses serial value in big-endian order. + * meson_sm_get_serial() returns exactly such value. + */ + ret = meson_sm_get_serial(serial, sizeof(serial)); + if (ret) + return ret; + + bin2hex(serial_str, serial, sizeof(serial)); + return ret; +} + +int amlogic_gadget_run(const char *mode, int argc, char *const argv[]) +{ + char serial_string[SM_SERIAL_SIZE * 2 + 1] = { 0 }; + unsigned long identify_timeout_ms; + unsigned long start_timeout_ms; + struct udevice *udc; + char *saved_serial; + int ret; + + if (mtd_probe_devices()) { + pr_err("Mtd devices probe failed\n"); + return CMD_RET_FAILURE; + } + + /* + * During ADNL/OPTIMUS burning process, device enters USB gadget mode + * twice: first time when it was stopped in ROM boot, second - + * here, after ROM and BL2 stages are done and we are ready + * to run BL33 (U-boot) stage. From the PC side, client waits + * reentering to USB gadget mode (in BL33 stage). Without delay + * below, PC client waits for USB device forever with the + * following message. + * + * OPTIMUS do not use timeout described below + * MSG[TLS]Waiting for DNL[] with timeout 0 + * + * At the same time, there is no difference (from the PC point + * of view) between USB device state with or w/o this delay, so + * seems this delay is needed only to make PC client work + * correctly due to is internal logic. + */ + udelay(BURN_BOOT_UDELAY); + + ret = amlogic_serial_hex(serial_string); + if (ret) + return CMD_RET_FAILURE; + + saved_serial = env_get("serial#"); + if (saved_serial) { + saved_serial = strdup(saved_serial); + if (!saved_serial) + return CMD_RET_FAILURE; + } + + /* + * Use "force" flag, because 'serial#' is protected + * from both delete and rewrite. This variable is used + * by USB gadget code as serial number of USB device. + */ + ret = run_commandf("env set -f serial# %s", serial_string); + if (ret) { + ret = CMD_RET_FAILURE; + goto exit_ret; + } + + amlogic_gadget_set_times(get_timer(0), 0); + + ret = udc_device_get_by_index(0, &udc); + if (ret) { + pr_err("Failed to get UDC device: %d\n", ret); + ret = CMD_RET_FAILURE; + goto exit_ret; + } + + g_dnl_clear_detach(); + ret = g_dnl_register(mode); + if (ret) { + ret = CMD_RET_FAILURE; + goto exit_ret; + } + + if (!g_dnl_board_usb_cable_connected()) { + pr_err("Usb cable isn't detected\n"); + ret = CMD_RET_FAILURE; + goto exit; + } + + identify_timeout_ms = (argc == 3) ? simple_strtoul(argv[2], NULL, 0) : 0; + start_timeout_ms = (argc >= 2) ? simple_strtoul(argv[1], NULL, 0) : 0; + + while (1) { + if (g_dnl_detach()) + break; + + if (ctrlc()) + break; + + schedule(); + + if (start_timeout_ms && burn_start_time_ms) { + unsigned long start_delta = get_timer(burn_start_time_ms); + + if (start_delta > start_timeout_ms) { + pr_err("Burn init timeout: %lu > %lu\n", + start_delta, start_timeout_ms); + ret = CMD_RET_FAILURE; + goto exit; + } + } + + if (identify_timeout_ms && burn_identify_time_ms) { + unsigned long identify_delta_ms = get_timer(burn_identify_time_ms); + + if (identify_delta_ms > identify_timeout_ms) { + pr_err("Burn identify timeout: %lu > %lu\n", + identify_delta_ms, identify_timeout_ms); + ret = CMD_RET_FAILURE; + goto exit; + } + } + + dm_usb_gadget_handle_interrupts(udc); + } + + ret = CMD_RET_SUCCESS; + +exit: + g_dnl_unregister(); + g_dnl_clear_detach(); + udc_device_put(udc); + +exit_ret: + if (saved_serial) { + env_set("serial#", saved_serial); + free(saved_serial); + } + + return ret; +} diff --git a/cmd/meson/gadget.h b/cmd/meson/gadget.h new file mode 100644 index 00000000000..a3771fb4746 --- /dev/null +++ b/cmd/meson/gadget.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#ifndef __GADGET_H__ +#define __GADGET_H__ + +/** + * amlogic_gadget_set_times() - set timeouts for ADNL/Optimus protocols + * + * @start_time: timeout in ms, until device enters ADNL/Optimus mode + * @identify_time: timeout in ms, until device gets first command from host + */ +void amlogic_gadget_set_times(unsigned long start_time, unsigned long identify_time); + +/** + * amlogic_gadget_run() - switch device to USB gadget mode + * + * @mode: USB function name. + * @argc: number of arguments in 'argv' + * @argv: arguments, passed from command + * Return: 0 (CMD_RET_SUCCESS) if there is no error. + */ +int amlogic_gadget_run(const char *mode, int argc, char *const argv[]); + +#endif /* __GADGET_H__ */ From patchwork Wed Mar 19 20:20:39 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062981 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=nGqTHPNT; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4ZJ0Tz5HMnz1xrn for ; Thu, 20 Mar 2025 07:21:27 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 6B91781B4B; Wed, 19 Mar 2025 21:21:06 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="nGqTHPNT"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 65DDD81D3B; Wed, 19 Mar 2025 21:21:04 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx1.sberdevices.ru [37.18.73.165]) (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 3922181A0E for ; Wed, 19 Mar 2025 21:21:00 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk01.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id A6C55100014; Wed, 19 Mar 2025 23:20:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru A6C55100014 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415659; bh=/2cxCbxHsQ1eMmh7bRhWYOA84MGhtZATSCZoFu6239A=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=nGqTHPNTDJ7omdBx6cHKokgA0EdbUhg8fOzqsov5I5BI5Hp79rLrguYzJahMTiz0u mYeKeDyQ+gAQaavl7cRzq+VkU7GTWqxBFe4gdpQoOhKKJolwbXVBykzVBlLMUOILoa lJOmKdohQFFFZ1hFqJUIa6pFI0Lhp8frAjgQpkejWqiE71bECiHiEVUA2aLP77r96k oAD1f37ogV7Wrj4/d27MokVxxB6qiAOWvN7AjiM8E7wK7vUEf8rCTibuuW24A4cHVR /4QwE/Kn8JqRgjJpQtkBTVpdSDvszngiI/FCRGOjvYiAnyrcNmlhZNHCOUPus5xWjK FQZzNmFMcKOGg== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:20:59 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 05/11] mtd: rawnand: meson: move some defines to dedicated header Date: Wed, 19 Mar 2025 23:20:39 +0300 Message-ID: <20250319202045.1407-6-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; salutedevices.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 17:18:00 #27803216 X-KSMG-AntiVirus-Status: Clean, skipped 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 We need these values and macro to correctly write "bootloader" partition in Optimus protocol, so let's export them to arch-meson header and also provide more detailed information about each macro. Signed-off-by: Arseniy Krasnov --- arch/arm/include/asm/arch-meson/rawnand.h | 38 +++++++++++++++++++++++ drivers/mtd/nand/raw/meson_nand.c | 13 +------- 2 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 arch/arm/include/asm/arch-meson/rawnand.h diff --git a/arch/arm/include/asm/arch-meson/rawnand.h b/arch/arm/include/asm/arch-meson/rawnand.h new file mode 100644 index 00000000000..81bf6ef7e37 --- /dev/null +++ b/arch/arm/include/asm/arch-meson/rawnand.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2025 SaluteDevices, Inc. + */ + +#ifndef __MESON_RAWNAND_H__ +#define __MESON_RAWNAND_H__ + +#include +#include +#include + +/* M2N means 'memory to NAND', e.g. write. */ +#define NFC_CMD_M2N ((0 << 17) | BIT(21)) +/* N2M means 'NAND to memory', e.g. read. */ +#define NFC_CMD_N2M ((1 << 17) | BIT(21)) + +/* + * Macro to create value for read/write command. + * + * 'cmd_dir' - direction, e.g. read or write. + * 'ran' - scrambler mode (enabled or not). + * 'bch' - ECC mode (strength and size). + * 'short_mode' - short mode (enabled or not). + * 'page_size' - size of page. + * 'pages' - number of pages to access. + */ +#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \ + ( \ + (cmd_dir) | \ + ((ran)) | \ + ((bch) << 14) | \ + ((short_mode) << 13) | \ + (((page_size) & 0x7F) << 6) | \ + ((pages) & 0x3F) \ + ) + +#endif diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 28c851f103b..81122315f45 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -34,8 +35,6 @@ #define NFC_CMD_AIL ((2 << 16) | (3 << 20)) #define NFC_CMD_AIH ((3 << 16) | (3 << 20)) #define NFC_CMD_SEED ((8 << 16) | (3 << 20)) -#define NFC_CMD_M2N ((0 << 17) | (2 << 20)) -#define NFC_CMD_N2M ((1 << 17) | (2 << 20)) #define NFC_CMD_RB BIT(20) #define NFC_CMD_SCRAMBLER_ENABLE BIT(19) #define NFC_CMD_SCRAMBLER_DISABLE 0 @@ -61,16 +60,6 @@ #define NFC_REG_PINS 0x30 #define NFC_REG_VER 0x38 -#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \ - ( \ - (cmd_dir) | \ - (ran) | \ - ((bch) << 14) | \ - ((short_mode) << 13) | \ - (((page_size) & 0x7f) << 6) | \ - ((pages) & 0x3f) \ - ) - #define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff)) #define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff)) #define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff)) From patchwork Wed Mar 19 20:20:40 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062983 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=PhkH2gMc; 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) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4ZJ0VS5Wnjz1xrn for ; Thu, 20 Mar 2025 07:21:52 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 6A07E81A0E; Wed, 19 Mar 2025 21:21:16 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="PhkH2gMc"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id A59598197D; Wed, 19 Mar 2025 21:21:05 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (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 85A7481B42 for ; Wed, 19 Mar 2025 21:21:00 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 07FEB120012; Wed, 19 Mar 2025 23:21:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 07FEB120012 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415660; bh=Oaz9kK497lphS7IZ2pMXR0Imi4OVRcRz+na1ZPtEt9c=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=PhkH2gMcMaZ//sjak2bVu254mb92xG4Z4zS3tFfDMdaTJ2WJtFx0wNF8cA60RzNKs 3seMFansTvl8ty6H544C5dToxprbalw+wQHLlaveeP9sECTgrjQfl2JVz9RAk/+h89 1S6DRw9RZFl0XT5iFnJArwyeoQxQvKWu2pcBfR0HMyzpi+6qZ0hbN7+CtCmKo+ok+x EWXg6wZz7muuxkf5/sDwQRkLlWEZtfugAcUsEBPMljsHn60azV0RfYOhOW/Br0DEBl 71KRNje0+I2IPBj0V0jRd13tCt5SI6la4xyCsr4b183P6hsl8WeSCxwoHFQxIazXrR Si4dOXOF3MuUQ== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:20:59 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 06/11] arch: arm: meson: bootloader write support Date: Wed, 19 Mar 2025 23:20:40 +0300 Message-ID: <20250319202045.1407-7-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; salutedevices.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 19:45:00 #27804052 X-KSMG-AntiVirus-Status: Clean, skipped 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 BootROM of Amlogic SoCs that use ADNL/Optimus protocols needs special layout of "bootloader" partition. So let's implement functions that support bootloader writing on such SoCs. This is prerequisite for ADNL/Optimus implementation. We place such functions to 'arch/arm/mach-meson', because this code is also needed by 'fastboot' protocol which can also write "bootloader" partition. Signed-off-by: Arseniy Krasnov --- arch/arm/include/asm/arch-meson/nand.h | 34 +++ arch/arm/include/asm/arch-meson/spinand.h | 43 ++++ arch/arm/mach-meson/Kconfig | 31 +++ arch/arm/mach-meson/Makefile | 4 +- arch/arm/mach-meson/rawnand.c | 291 ++++++++++++++++++++++ arch/arm/mach-meson/spinand.c | 158 ++++++++++++ 6 files changed, 559 insertions(+), 2 deletions(-) create mode 100644 arch/arm/include/asm/arch-meson/nand.h create mode 100644 arch/arm/include/asm/arch-meson/spinand.h create mode 100644 arch/arm/mach-meson/rawnand.c create mode 100644 arch/arm/mach-meson/spinand.c diff --git a/arch/arm/include/asm/arch-meson/nand.h b/arch/arm/include/asm/arch-meson/nand.h new file mode 100644 index 00000000000..1f5a20d237b --- /dev/null +++ b/arch/arm/include/asm/arch-meson/nand.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2023-2024 SaluteDevices, Inc. + */ + +#ifndef __MESON_NAND_H__ +#define __MESON_NAND_H__ + +#define BOOT_LOADER "bootloader" +#define BOOT_BL2 "bl2" +#define BOOT_SPL "spl" +#define BOOT_TPL "tpl" +#define BOOT_FIP "fip" + +#define BL2_COPY_NUM (CONFIG_MESON_BL2_COPY_NUM) +#define BL2_IMAGE_SIZE_PER_COPY (CONFIG_MESON_BL2_IMAGE_SIZE) +#define BL2_TOTAL_PAGES 1024 + +#define TPL_COPY_NUM (CONFIG_MESON_TPL_COPY_NUM) +#define TPL_SIZE_PER_COPY 0x200000 + +#define NAND_RSV_BLOCK_NUM 48 + +/** + * meson_bootloader_write - write 'bootloader' partition to NAND + * according to the required layout. It will + * write BL2, TPL and info pages. + * + * @buf: buffer with BL2 and TPL + * @length: buffer length + * @return: 0 on success, -errno otherwise. + */ +int meson_bootloader_write(unsigned char *buf, size_t length); +#endif diff --git a/arch/arm/include/asm/arch-meson/spinand.h b/arch/arm/include/asm/arch-meson/spinand.h new file mode 100644 index 00000000000..1628c8bd4e0 --- /dev/null +++ b/arch/arm/include/asm/arch-meson/spinand.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2024 SaluteDevices, Inc. + */ + +#ifndef __MESON_SPINAND_H__ +#define __MESON_SPINAND_H__ + +#include +#include +#include +#include +#include +#include + +struct meson_spi_nand_info_page { + char magic[8]; /* Magic header of info page. */ + /* Info page version, +1 when you update this struct. */ + u8 version; /* 1 for now. */ + u8 mode; /* 1 discrete, 0 compact. */ + u8 bl2_num; /* bl2 copy number. */ + u8 fip_num; /* fip copy number. */ + union { + struct { + u8 rd_max; /* spi nand max read io. */ + u8 oob_offset; /* User bytes offset. */ + u8 planes_per_lun; /* number of planes per LUN. */ + u8 rsv; /* Reserved gap. */ + u32 fip_start; /* Start pages. */ + u32 fip_pages; /* Pages per fip. */ + u32 page_size; /* spi nand page size (bytes). */ + u32 page_per_blk; /* Page number per block. */ + u32 oob_size; /* Valid oob size (bytes). */ + u32 bbt_start; /* BBT start pages. */ + u32 bbt_valid; /* BBT valid offset pages. */ + u32 bbt_size; /* BBT occupied bytes. */ + } __packed spinand; /* spi nand. */ + struct { + u32 reserved; + } emmc; + } dev; +} __packed; +#endif diff --git a/arch/arm/mach-meson/Kconfig b/arch/arm/mach-meson/Kconfig index 7570f48e25f..883ee78f49b 100644 --- a/arch/arm/mach-meson/Kconfig +++ b/arch/arm/mach-meson/Kconfig @@ -93,4 +93,35 @@ config SYS_BOARD Based on this option board// will be used. +config MESON_BL2_COPY_NUM + depends on ADNL || FASTBOOT_FLASH + int "Number of BL2 copies written to storage" + default 8 + help + The ADNL / Optimus / fastboot protocol writes several copies of BL2 + bootloader during firmware update process. + +config MESON_TPL_COPY_NUM + depends on ADNL || FASTBOOT_FLASH + int "Number of TPL copies written to storage" + default 4 + help + The ADNL / Optimus / fastboot protocol writes several copies of TPL + bootloader during firmware update process. + +config MESON_BL2_IMAGE_SIZE + depends on ADNL || FASTBOOT_FLASH + hex "Size of BL2 image" + default 0x10000 + help + Size of BL2 image, passed by ADNL / Optimus / fastboot protocols. + +config MESON_BL2_IMAGE_OFFSET + depends on ADNL || FASTBOOT_FLASH + hex "Offset from info page to BL2 image" + default 0x800 + help + For each copy of BL2, this is offset from info page to BL2 image. Used + by Optimus and fastboot protocols. + endif diff --git a/arch/arm/mach-meson/Makefile b/arch/arm/mach-meson/Makefile index 535b0878b91..f63f91dde35 100644 --- a/arch/arm/mach-meson/Makefile +++ b/arch/arm/mach-meson/Makefile @@ -4,6 +4,6 @@ obj-y += board-common.o sm.o board-info.o obj-$(CONFIG_MESON_GX) += board-gx.o -obj-$(CONFIG_MESON_AXG) += board-axg.o +obj-$(CONFIG_MESON_AXG) += board-axg.o rawnand.o obj-$(CONFIG_MESON_G12A) += board-g12a.o -obj-$(CONFIG_MESON_A1) += board-a1.o +obj-$(CONFIG_MESON_A1) += board-a1.o spinand.o diff --git a/arch/arm/mach-meson/rawnand.c b/arch/arm/mach-meson/rawnand.c new file mode 100644 index 00000000000..6b91674200e --- /dev/null +++ b/arch/arm/mach-meson/rawnand.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2024 SaluteDevices, Inc. + */ + +#include +#include +#include +#include +#include +#include + +struct raw_nand_setup { + union { + u32 d32; + struct { + unsigned cmd:22; /* NAND controller cmd to read. */ + unsigned large_page:1; /* Page is large. */ + unsigned no_rb:1; /* No RB. */ + unsigned a2:1; /* A2 cmd enable flag. */ + unsigned reserved25:1; /* Gap. */ + unsigned page_list:1; /* Page list is enabled. */ + unsigned sync_mode:2; /* Always 0 according vendor doc. */ + unsigned size:2; /* Always 0 according vendor doc. */ + unsigned active:1; /* Always 0 according vendor doc. */ + } b; + } cfg; + u16 id; + u16 max; +} __packed; + +struct raw_nand_cmd { + unsigned char type; + unsigned char val; +} __packed; + +struct raw_ext_info { + u32 read_info; + u32 new_type; + u32 page_per_blk; + u32 xlc; + u32 ce_mask; + u32 boot_num; + u32 each_boot_pages; + /* for compatible reason */ + u32 bbt_occupy_pages; + u32 bbt_start_block; +} __packed; + +struct raw_fip_info { + u16 version; + u16 mode; + u32 fip_start; +} __packed; + +struct raw_nand_info_page { + struct raw_nand_setup nand_setup; + unsigned char page_list[16]; + struct raw_nand_cmd retry_usr[32]; + struct raw_ext_info ext_info; + struct raw_fip_info fip_info; + u32 ddrp_start_page; +} __packed; + +static void meson_raw_fill_oobbuf_with_magic(u8 *oobbuf, size_t oobavail) +{ + for (size_t j = 0; j < oobavail; j += 2) { + oobbuf[j] = 0x55; + oobbuf[j + 1] = 0xaa; + } +} + +static int meson_raw_raw_nand_info_page_prepare(struct mtd_info *mtd, void *oobbuf) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd->parent); + struct raw_nand_info_page *p_nand_page0; + struct raw_ext_info *ext_info; + struct raw_nand_setup *nand_setup; + struct raw_fip_info *fip_info; + u32 config_data; + + p_nand_page0 = oobbuf; + memset(p_nand_page0, 0x0, sizeof(struct raw_nand_info_page)); + nand_setup = &p_nand_page0->nand_setup; + ext_info = &p_nand_page0->ext_info; + + config_data = CMDRWGEN(NFC_CMD_N2M, 0, 1, 0, nand_chip->ecc.size >> 3, + nand_chip->ecc.steps); + + config_data = config_data | BIT(23) | BIT(22) | BIT(21); + nand_setup->cfg.d32 = config_data; + nand_setup->id = 0; + nand_setup->max = 0; + + ext_info->read_info = 1; + ext_info->page_per_blk = mtd->erasesize / mtd->writesize; + ext_info->ce_mask = 0x01; + ext_info->xlc = 1; + ext_info->boot_num = TPL_COPY_NUM; + ext_info->each_boot_pages = TPL_SIZE_PER_COPY / mtd->writesize; + ext_info->bbt_occupy_pages = 0x1; + ext_info->bbt_start_block = 0x14; /* Amlogic keeps BBT in hardcoded offset. */ + + p_nand_page0->ddrp_start_page = 0xb00; + + fip_info = &p_nand_page0->fip_info; + fip_info->version = 1; + fip_info->mode = 1; /* Discrete mode. */ + fip_info->fip_start = BL2_TOTAL_PAGES + + (NAND_RSV_BLOCK_NUM * ext_info->page_per_blk); + + return 0; +} + +static int meson_raw_write_info_page(struct mtd_info *mtd, loff_t offset, size_t *actual) +{ + int ret; + struct mtd_oob_ops ops; + u8 *datbuf = (u8 *)calloc(mtd->writesize, sizeof(u8)); + + if (!datbuf) { + pr_err("Failed to allocate data buf for info page\n"); + return -ENOMEM; + } + + meson_raw_raw_nand_info_page_prepare(mtd, datbuf); + + memset(&ops, 0, sizeof(struct mtd_oob_ops)); + ops.len = mtd->writesize; + ops.datbuf = datbuf; + ops.ooblen = mtd->oobavail; + ops.mode = MTD_OPS_AUTO_OOB; + ops.oobbuf = calloc(mtd->oobavail, sizeof(u8)); + + if (!ops.oobbuf) { + pr_err("Failed to allocate OOB buf for info page\n"); + free(datbuf); + return -ENOMEM; + } + + meson_raw_fill_oobbuf_with_magic(ops.oobbuf, mtd->oobavail); + + ret = mtd_write_oob(mtd, offset, &ops); + *actual = ops.retlen; + + free(ops.oobbuf); + free(datbuf); + + return ret; +} + +static int meson_raw_write_bl2(unsigned char *src_data) +{ + int ret; + size_t i; + nand_erase_options_t opts; + struct mtd_info *mtd; + size_t bl2_copy_limit; + struct nand_chip *nand; + size_t oobbuff_size; + struct mtd_oob_ops ops; + size_t src_size = BL2_IMAGE_SIZE_PER_COPY; + loff_t dest = 0; + size_t copy_num = BL2_COPY_NUM; + + pr_info("Writing BL2\n"); + + mtd = get_mtd_device_nm(BOOT_LOADER); + if (IS_ERR_OR_NULL(mtd)) { + pr_err("Failed to get mtd device \"bootloader\" ret: %ld\n", PTR_ERR(mtd)); + return PTR_ERR(mtd); + } + + if (mtd->oobavail & 1) { + pr_err("Invalid OOB available bytes: %u\n", mtd->oobavail); + return -ENODEV; + } + + memset(&opts, 0, sizeof(opts)); + opts.offset = dest; + opts.length = mtd->size; + if (nand_erase_opts(mtd, &opts)) { + pr_err("Failed to erase \"bootloader\"\n"); + return -EIO; + } + + bl2_copy_limit = mtd->size / BL2_COPY_NUM; + + memset(&ops, 0, sizeof(struct mtd_oob_ops)); + ops.len = src_size; + ops.datbuf = src_data; + ops.ooblen = mtd->oobavail * (src_size / mtd->writesize + 1); + ops.mode = MTD_OPS_AUTO_OOB; + + nand = mtd_to_nand(mtd->parent); + oobbuff_size = (mtd->writesize / nand->ecc.size) * ops.ooblen; + + ops.oobbuf = calloc(oobbuff_size, sizeof(u8)); + + if (!ops.oobbuf) { + pr_err("Failed to allocate OOB buf for BL2\n"); + return -ENOMEM; + } + + meson_raw_fill_oobbuf_with_magic(ops.oobbuf, ops.ooblen); + + for (i = 0; i < copy_num; i++) { + size_t actual = 0; + loff_t offset_in_partition = dest + i * bl2_copy_limit; + + if (mtd_block_isbad(mtd, offset_in_partition)) { + pr_info("Skip badblock at %llx\n", offset_in_partition); + continue; + } + + ret = meson_raw_write_info_page(mtd, offset_in_partition, &actual); + if (ret) + goto free_oobbuf; + + offset_in_partition += CONFIG_MESON_BL2_IMAGE_OFFSET + + (actual - mtd->writesize); + ret = mtd_write_oob(mtd, offset_in_partition, &ops); + if (ret) + goto free_oobbuf; + } + +free_oobbuf: + free(ops.oobbuf); + + return ret; +} + +static int meson_raw_write_tpl(unsigned char *src_data, size_t src_size) +{ + int ret; + size_t i; + struct mtd_info *mtd; + nand_erase_options_t opts; + loff_t dest = 0; + size_t copy_num = TPL_COPY_NUM; + + pr_info("Writing TPL\n"); + mtd = get_mtd_device_nm(BOOT_TPL); + + if (IS_ERR_OR_NULL(mtd)) { + pr_err("Failed to get mtd device \"tpl\" ret: %ld\n", PTR_ERR(mtd)); + return PTR_ERR(mtd); + } + + memset(&opts, 0, sizeof(nand_erase_options_t)); + opts.offset = dest; + opts.length = mtd->size; + if (nand_erase_opts(mtd, &opts)) { + pr_err("Failed to erase \"tpl\"\n"); + return -EIO; + } + + for (i = 0; i < copy_num; i++) { + loff_t offset_in_partition = dest + i * TPL_SIZE_PER_COPY; + + ret = nand_write_skip_bad(mtd, offset_in_partition, &src_size, + NULL, mtd->size / TPL_COPY_NUM, src_data, 0); + if (ret) + return ret; + } + + return 0; +} + +int meson_bootloader_write(unsigned char *buf, size_t length) +{ + int ret; + + if (!buf) { + pr_err("Empty buffer is received\n"); + return -EINVAL; + } + + ret = meson_raw_write_bl2(buf); + if (ret) + return ret; + + /* Write FIP. FIP is also called TPL */ + ret = meson_raw_write_tpl(buf + BL2_IMAGE_SIZE_PER_COPY, + length - BL2_IMAGE_SIZE_PER_COPY); + if (ret) + return ret; + + pr_info("\"Bootloader (BL2+TPL)\" is written\n"); + return 0; +} diff --git a/arch/arm/mach-meson/spinand.c b/arch/arm/mach-meson/spinand.c new file mode 100644 index 00000000000..dbad4ba0e9f --- /dev/null +++ b/arch/arm/mach-meson/spinand.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 SaluteDevices, Inc. + */ + +#include +#include +#include +#include +#include + +#define SPINAND_MAGIC "AMLIFPG" +#define SPINAND_INFO_VER 1 + +static void meson_bootloader_setup_info_page(struct meson_spi_nand_info_page *info_page, + struct mtd_info *mtd) +{ + struct nand_device *nand_dev; + u32 page_per_blk; + + nand_dev = mtd_to_nanddev(mtd->parent); + page_per_blk = mtd->erasesize / mtd->writesize; + info_page->version = SPINAND_INFO_VER; + /* DISCRETE only */ + info_page->mode = 1; + info_page->bl2_num = BL2_COPY_NUM; + info_page->fip_num = TPL_COPY_NUM; + info_page->dev.spinand.rd_max = 2; + info_page->dev.spinand.fip_start = BL2_TOTAL_PAGES + NAND_RSV_BLOCK_NUM * page_per_blk; + info_page->dev.spinand.fip_pages = TPL_SIZE_PER_COPY / mtd->writesize; + info_page->dev.spinand.page_size = mtd->writesize; + info_page->dev.spinand.page_per_blk = page_per_blk; + info_page->dev.spinand.oob_size = mtd->oobsize; + info_page->dev.spinand.oob_offset = 0; + info_page->dev.spinand.bbt_start = 0; + info_page->dev.spinand.bbt_valid = 0; + info_page->dev.spinand.bbt_size = 0; + info_page->dev.spinand.planes_per_lun = nand_dev->memorg.planes_per_lun; +} + +static int meson_store_boot_write_bl2(struct mtd_info *mtd, void *buf, unsigned int copy_idx, + struct meson_spi_nand_info_page *info_page) +{ + unsigned int size_per_copy; + loff_t offset; + size_t retlen; + int ret; + + size_per_copy = mtd->writesize * (BL2_TOTAL_PAGES / BL2_COPY_NUM); + offset = mtd->offset + (copy_idx * size_per_copy); + ret = mtd_write(mtd, offset, BL2_IMAGE_SIZE_PER_COPY, &retlen, buf); + if (ret) + return ret; + + /* + * Info page is written directly after each BL2 image. For + * example let: + * BL2_TOTAL_PAGES = 1024 + * BL2_COPY_NUM = 8 + * BL2_IMAGE_SIZE_PER_COPY = 65536 = 0x10000 + * size_per_copy = 2048 * (1024 / 8) = 0x40000 + * + * BL2 image will be written to BL2_COPY_NUM + * locations: + * 0x00000 + * 0x40000 + * 0x80000 + * 0xc0000 + * 0x100000 + * 0x140000 + * 0x180000 + * 0x1c0000 + * + * Info pages will be written to BL2_COPY_NUM + * locations: + * 0x10000 + * 0x50000 + * 0x90000 + * 0xd0000 + * 0x110000 + * 0x150000 + * 0x190000 + * 0x1d0000 + * + * Here is the same, in picture: + * + * BL2_TOTAL_PAGES == 1024 + * BL2_COPY_NUM == 8 + * /---------------------------------------\ + * | | + * + * |AB |AB |AB |AB |AB |AB |AB |AB | + * |____|____|____|____|____|____|____|____| + * ^ ^ + * | | + * |____|__ size_per_copy == 0x40000 + * + * A - is BL2 image. It is BL2_IMAGE_SIZE_PER_COPY == 0x10000 bytes. + * B - is info page. + */ + offset += BL2_IMAGE_SIZE_PER_COPY; + + return mtd_write(mtd, offset, sizeof(*info_page), + &retlen, (u8 *)info_page); +} + +static int meson_store_boot_write_tpl(void *buf, size_t size, unsigned int copy_idx) +{ + loff_t offset; + struct mtd_info *mtd; + loff_t limit; + size_t retlen = 0; + + mtd = get_mtd_device_nm(BOOT_TPL); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + offset = mtd->offset + (copy_idx * TPL_SIZE_PER_COPY); + limit = offset + TPL_SIZE_PER_COPY; + + return nand_write_skip_bad(mtd->parent, offset, &size, &retlen, + limit, buf, 0); +} + +int meson_bootloader_write(unsigned char *buf, size_t length) +{ + struct meson_spi_nand_info_page info_page; + unsigned int copy_idx; + struct mtd_info *mtd; + int ret; + + /* 'buf' contains BL2 and TPL. If there is only BL2 or + * even less - input data is invalid. + */ + if (length <= BL2_IMAGE_SIZE_PER_COPY) + return -EINVAL; + + mtd = get_mtd_device_nm(BOOT_LOADER); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + meson_bootloader_setup_info_page(&info_page, mtd); + + for (copy_idx = 0; copy_idx < BL2_COPY_NUM; copy_idx++) { + ret = meson_store_boot_write_bl2(mtd, buf, copy_idx, &info_page); + if (ret) + return ret; + } + + for (copy_idx = 0; copy_idx < TPL_COPY_NUM; copy_idx++) { + ret = meson_store_boot_write_tpl(buf + BL2_IMAGE_SIZE_PER_COPY, + length - BL2_IMAGE_SIZE_PER_COPY, copy_idx); + if (ret) + return ret; + } + + return 0; +} From patchwork Wed Mar 19 20:20:41 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062985 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=W99Hder+; 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 4ZJ0Vs3B2Jz1xrn for ; Thu, 20 Mar 2025 07:22:13 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 0283881DAB; Wed, 19 Mar 2025 21:21:17 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="W99Hder+"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 5DBC981B42; Wed, 19 Mar 2025 21:21:06 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx1.sberdevices.ru [37.18.73.165]) (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 C1D2A81B4B for ; Wed, 19 Mar 2025 21:21:00 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk01.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 48A67100015; Wed, 19 Mar 2025 23:21:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 48A67100015 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415660; bh=55VQToj4Te6FUgYKTvotsEqGDkJWFu0WJVhvD06nrh4=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=W99Hder+2yTEcMDX/XOQ345qfgaRIdv8QTwop2DVRLtcuen107lfJ+f61XFjhvH6c GY7TmueaNMZk7yd0EQJ7wHpBy4HAQp6QvhY8/MFEcOnbjeWF3m3tweL3MNHY7GdSqS vFW0U2yhi5IsG8epDwYQpLZfr+e5GKxnD68dwTNzjQUfrb1LdLC3iwEbJMY8Eqr6ne 8h6KwsZasDBGYtLA2nZ0+J0ve8AmCYCPwQiLN7B2h174e08P1TakhuZLb4N48/31A+ 9noSTuqH2wEEGDOA38lB7WEkFa+PimwEETCh2qACKxyA2x/N31xrvRGpzbGy34G5YI XEtoISeQZxWzQ== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:21:00 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 07/11] arm: meson: a1: add A1_SYSCTRL_SEC_STICKY_REG2 Date: Wed, 19 Mar 2025 23:20:41 +0300 Message-ID: <20250319202045.1407-8-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; salutedevices.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 17:18:00 #27803216 X-KSMG-AntiVirus-Status: Clean, skipped 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 This register is used by ADNL protocol, to check boot source of current U-boot instance. Signed-off-by: Arseniy Krasnov --- arch/arm/include/asm/arch-meson/a1.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/include/asm/arch-meson/a1.h b/arch/arm/include/asm/arch-meson/a1.h index 86d1a68de8c..16e209ed932 100644 --- a/arch/arm/include/asm/arch-meson/a1.h +++ b/arch/arm/include/asm/arch-meson/a1.h @@ -13,6 +13,12 @@ #define A1_SYSCTRL_ADDR(off) (A1_SYSCTRL_BASE + ((off) << 2)) #define A1_SYSCTRL_SEC_STATUS_REG4 A1_SYSCTRL_ADDR(0xc4) +/* + * Checked during boot. Tells, that previous code, e.g. + * executed before reboot requested to continue boot + * from USB. Used by ADNL protocol. + */ +#define A1_SYSCTRL_SEC_STICKY_REG2 A1_SYSCTRL_ADDR(0xe2) #define A1_SYSCTRL_MEM_SIZE_MASK 0xFFFF0000 #define A1_SYSCTRL_MEM_SIZE_SHIFT 16 From patchwork Wed Mar 19 20:20:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062988 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=cyBfPY38; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (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 4ZJ0WP3KFgz1xrn for ; Thu, 20 Mar 2025 07:22:41 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id F0ABD81E13; Wed, 19 Mar 2025 21:21:17 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="cyBfPY38"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 452048141E; Wed, 19 Mar 2025 21:21:10 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (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 4AA9781C64 for ; Wed, 19 Mar 2025 21:21:01 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id BE049120013; Wed, 19 Mar 2025 23:21:00 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru BE049120013 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415660; bh=57i8ekdB7P3lnVk1tSjHBteB8noW7VQup4RjT34jVaE=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=cyBfPY381jsCVC4cLrbyWh9eVLAbMas4E8D/TB1oPuOwnHAvdGBojAxr1A1IDpsnz R+vkg+Re0OZZcPKXTGLgupz8ARSuEr0HlrUP042XIrwqgUii63fOK/h+9Pl7jc/c65 fKEnpjUUVjKze3J4tM1qU0TyP/kRdfjoGERxtopinrr/OzAThoANu1A0rYuhobw7kq f5GodUZs83skCobMRiuhYpB5VkNlXq6DdbCL2hvS9Z2yxKdFB1XZOIWmhQQoaWfUQB 4SGkzl4damiSha0A2gDXhYH5gnsEAUjXxVwtUu8cxtxB1XqxyUhInK1mEitOt6o4ew F6H7a6HBuBoGQ== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:21:00 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 08/11] usb: gadget: amlogic: implement ADNL protocol Date: Wed, 19 Mar 2025 23:20:42 +0300 Message-ID: <20250319202045.1407-9-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; salutedevices.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 19:45:00 #27804052 X-KSMG-AntiVirus-Status: Clean, skipped 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 This adds support for ADNL (Amlogic DowNLoad) protocol. This protocol is supported by some Amlogic SoCs (a1, s4, c1, с2, sc2, t7, etc) in BootRom code (e.g. it is already supported in "out of the box" mode). It has similar goals as fastboot - upload and update firmware images on the device. Difference is that, it is specific for Amlogic SoCs, and for example it allows to upload and run image starting from ROM boot stage, it also uses SoC specific registers and SMC commands. Signed-off-by: Arseniy Krasnov --- cmd/Kconfig | 7 + cmd/meson/Makefile | 1 + cmd/meson/adnl.c | 27 + drivers/usb/gadget/Kconfig | 2 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/amlogic/Kconfig | 11 + drivers/usb/gadget/amlogic/adnl/Kconfig | 29 + drivers/usb/gadget/amlogic/adnl/Makefile | 4 + drivers/usb/gadget/amlogic/adnl/adnl.h | 124 +++ .../gadget/amlogic/adnl/adnl_buff_manager.c | 316 +++++++ drivers/usb/gadget/amlogic/adnl/adnl_media.c | 235 +++++ .../usb/gadget/amlogic/adnl/adnl_storage.c | 140 +++ drivers/usb/gadget/amlogic/adnl/f_adnl.c | 835 ++++++++++++++++++ 13 files changed, 1732 insertions(+) create mode 100644 cmd/meson/adnl.c create mode 100644 drivers/usb/gadget/amlogic/Kconfig create mode 100644 drivers/usb/gadget/amlogic/adnl/Kconfig create mode 100644 drivers/usb/gadget/amlogic/adnl/Makefile create mode 100644 drivers/usb/gadget/amlogic/adnl/adnl.h create mode 100644 drivers/usb/gadget/amlogic/adnl/adnl_buff_manager.c create mode 100644 drivers/usb/gadget/amlogic/adnl/adnl_media.c create mode 100644 drivers/usb/gadget/amlogic/adnl/adnl_storage.c create mode 100644 drivers/usb/gadget/amlogic/adnl/f_adnl.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 9e8a9c3cb3a..2f195d03848 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1146,6 +1146,13 @@ config CMD_FASTBOOT See doc/android/fastboot.rst for more information. +config CMD_ADNL + bool "ADNL - Amlogic download protocol" + depends on ADNL + help + This command, "adnl", enables the ADNL listen mode, providing + the ability to listen in ADNL mode. + config CMD_FLASH bool "flinfo, erase, protect" default y diff --git a/cmd/meson/Makefile b/cmd/meson/Makefile index ee26c175cfe..1cf984378a3 100644 --- a/cmd/meson/Makefile +++ b/cmd/meson/Makefile @@ -3,3 +3,4 @@ # Copyright (c) 2022, SberDevices. All rights reserved. obj-y += sm.o +obj-$(CONFIG_CMD_ADNL) += gadget.o adnl.o diff --git a/cmd/meson/adnl.c b/cmd/meson/adnl.c new file mode 100644 index 00000000000..13aa1f2113e --- /dev/null +++ b/cmd/meson/adnl.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#include + +#include "gadget.h" + +static int do_adnl(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return amlogic_gadget_run("usb_dnl_adnl", argc, argv); +} + +U_BOOT_LONGHELP(adnl, + "- run as an ADNL USB device\n\n" + "With:\n" + "\t[init timeout]: time between command start and moment when\n" + "\t device enters gadget mode, in ms\n" + "\t[identify timeout]: time between moment when device enters gadget\n" + "\t mode and first 'identify' command from PC, in ms\n"); + +U_BOOT_CMD(adnl, 3, 1, do_adnl, + "ADNL protocol mode", + adnl_help_text); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c815764c2bc..6391ceb9cfa 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -289,6 +289,8 @@ config USBNET_HOST_ADDR endif # USB_ETHER +source "drivers/usb/gadget/amlogic/Kconfig" + endif # USB_GADGET if SPL_USB_GADGET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 6c16ad83f6e..2b6705c52be 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o obj-$(CONFIG_DFU_OVER_USB) += f_dfu.o obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o f_fastboot_common.o +obj-$(CONFIG_ADNL) += f_fastboot_common.o amlogic/adnl/ obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o obj-$(CONFIG_USB_FUNCTION_ROCKUSB) += f_rockusb.o obj-$(CONFIG_USB_FUNCTION_ACM) += f_acm.o diff --git a/drivers/usb/gadget/amlogic/Kconfig b/drivers/usb/gadget/amlogic/Kconfig new file mode 100644 index 00000000000..460dc6fe235 --- /dev/null +++ b/drivers/usb/gadget/amlogic/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2024 SaluteDevices, Inc. + +choice + prompt "Amlogic burning protocols" + optional + +source "drivers/usb/gadget/amlogic/adnl/Kconfig" + +endchoice diff --git a/drivers/usb/gadget/amlogic/adnl/Kconfig b/drivers/usb/gadget/amlogic/adnl/Kconfig new file mode 100644 index 00000000000..21438b716ef --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2023 SaluteDevices, Inc. + +config ADNL + bool "Enable ADNL protocol" + depends on USB_GADGET + depends on MESON_A1 + depends on MTD_SPI_NAND + help + This enables ADNL protocol. + +if ADNL + +config ADNL_BUF_ADDR + hex "Define ADNL buffer address" + help + The ADNL protocol requires a large memory buffer for + downloads. Define this to the starting RAM address to use for + downloaded images. + +config ADNL_BUF_SIZE + hex "Define ADNL buffer size" + help + The ADNL protocol requires a large memory buffer for + downloads. This buffer should be as large as possible for a + platform. Define this to the size of available RAM for ADNL. + +endif diff --git a/drivers/usb/gadget/amlogic/adnl/Makefile b/drivers/usb/gadget/amlogic/adnl/Makefile new file mode 100644 index 00000000000..2b8c933a35d --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +subdir-ccflags-y += -I$(src)/../ -I$(src)/../../ -I$(src)/../../../../../ +obj-y += adnl_buff_manager.o adnl_media.o adnl_storage.o f_adnl.o diff --git a/drivers/usb/gadget/amlogic/adnl/adnl.h b/drivers/usb/gadget/amlogic/adnl/adnl.h new file mode 100644 index 00000000000..38d57282d31 --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/adnl.h @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#ifndef _ADNL_H_ +#define _ADNL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define ADNL_FAIL_STR "FAIL" + +/** + * adnl_store_logic_write() - Write non-boot partition. + * + * Write requested partition. This is kind of wrapper over + * 'nand_write_skip_bad()'. + * + * @name: partition name. + * @off: offset to write data. + * @size: size of buffer. + * @buf: buffer with data to write. + * + * @return: 0 on success, -errno otherwise. + */ +int adnl_store_logic_write(const char *name, loff_t off, size_t size, void *buf); + +/** + * adnl_store_logic_read() - Read non-boot partition. + * + * Read requested partition. This is kind of wrapper over + * 'nand_read_skip_bad()'. + * + * @name: partition name. + * @off: offset to read data from. + * @size: size of buffer. + * @buf: buffer to read to. + * + * @return: 0 on success, -errno otherwise. + */ +int adnl_store_logic_read(const char *name, loff_t off, size_t size, void *buf); + +extern char *fb_response_str; + +#define ADNL_ERR(fmt, ...) pr_err("[ADNL][ERR]: " fmt, ##__VA_ARGS__) +#define ADNL_WARN(fmt, ...) pr_warn("[ADNL][WARN] " fmt, ##__VA_ARGS__) +#define ADNL_DBG(fmt, ...) pr_debug("[ADNL][DBG] " fmt, ##__VA_ARGS__) + +/* This output macro doesn't depend on CONFIG_LOGLEVEL. */ +#define ADNL_MSG(fmt, ...) printf("[ADNL][MSG] " fmt, ##__VA_ARGS__) + +#define RESPONSE_LEN 128 + +#define ADNL_DOWNLOAD_MEM_BASE CONFIG_ADNL_BUF_ADDR +#define ADNL_DOWNLOAD_MEM_SIZE CONFIG_ADNL_BUF_SIZE +#define ADNL_DOWNLOAD_SPARE_SZ (8UL << 10) + +#define ADNL_PART_IMG_FMT_RAW 0xabcd + +#define ADNL_PART_NAME_LEN 32 + +struct adnl_img_common_param { + char part_name[ADNL_PART_NAME_LEN]; + s64 img_sz_total; + s64 part_start_off; +}; + +struct adnl_img_dw_param { + struct adnl_img_common_param common_inf; + int img_fmt; +}; + +union adnl_img_trans_param { + struct adnl_img_common_param common_inf; + struct adnl_img_dw_param download; +}; + +struct adnl_usb_dw_info { + int data_size; + s64 file_offset; + char *data_buf; +}; + +int adnl_buffman_img_init(union adnl_img_trans_param *img_param); +int adnl_buffman_fill_download_info(struct adnl_usb_dw_info **download_inf); +int adnl_buffman_data_complete_download(const struct adnl_usb_dw_info *download_inf); +int adnl_verify_partition_img(const char *verify_alg, const char *origsum_str, + char *ack); + +#define BULK_EP_MPS 512 +#define DWC_BLK_MAX_LEN (8 * BULK_EP_MPS) + +/** + * adnl_bootloader_read_n_verify() - Read boot partition. + * + * Read boot partition. This also checks, that all copies + * of both BL2 and TPL are valid (e.g. same). + * + * @buf: buffer to read to. + * @buf_len: size of buffer. Must be at least 'bootloader_len * 2'. + * @bootloader_len: size of BL2 + TPL images. + * + * @return: 0 on success, -errno otherwise. + */ +int adnl_bootloader_read_n_verify(u8 *buf, size_t buf_len, size_t bootloader_len); +int adnl_media_check_image_size(s64 img_sz, const char *part_name); + +int adnl_mwrite_cmd_parser(unsigned long img_size, unsigned long part_off, + const char *img_format, const char *media, + const char *part_name, char *ack); + +#define ADNL_ERASE_NOTHING 0 +#define ADNL_ERASE_NORMAL 1 +#define ADNL_ERASE_ALL 3 +int adnl_erase_storage(int erase_mode); + +#endif /* _ADNL_H_ */ diff --git a/drivers/usb/gadget/amlogic/adnl/adnl_buff_manager.c b/drivers/usb/gadget/amlogic/adnl/adnl_buff_manager.c new file mode 100644 index 00000000000..9e7a26ceb8e --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/adnl_buff_manager.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adnl.h" + +#define RAW_IMG_TRANSFER_LEN (128 << 10) +#define KB (1UL << 10) +#define MB (KB << 10) + +static struct { + union adnl_img_trans_param img_trans_param; + int data_buf_cap; + char *data_buf; + int inited; +} img_transfer_info; + +static struct adnl_usb_dw_info adnl_usb_dw_info; +static s64 raw_img_file_offset; + +static int adnl_img_verify_sha1sum_other(int img_fmt, sha1_context *ctx, + s64 img_total_len, + char *part, s64 part_base) +{ + unsigned char *verify_buff = (unsigned char *)ADNL_DOWNLOAD_MEM_BASE; + + if (!strncmp(BOOT_LOADER, part, ADNL_PART_NAME_LEN)) { + int ret; + + ret = adnl_bootloader_read_n_verify(verify_buff, ADNL_DOWNLOAD_MEM_SIZE, + img_total_len); + if (ret) + return ret; + + sha1_update(ctx, verify_buff, img_total_len); + return 0; + } else { + size_t verify_buff_len; + size_t this_verify_len; + size_t verify_len; + + if (img_fmt != ADNL_PART_IMG_FMT_RAW) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "unknown image format %x\n", img_fmt); + return -EINVAL; + } + + this_verify_len = 0; + verify_len = 0; + /* We use 2MB buffer for each SHA1 step. + * Seems this is optimal value here. It + * was taken from vendor's bootloader. + */ + verify_buff_len = 2 * MB; + + /* Read partition part by part and update SHA1. */ + for (; verify_len < img_total_len; verify_len += this_verify_len) { + int ret; + + this_verify_len = img_total_len - verify_len; + this_verify_len = (this_verify_len > verify_buff_len) ? + verify_buff_len : this_verify_len; + + ret = adnl_store_logic_read(part, verify_len + part_base, + this_verify_len, verify_buff); + + if (ret) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "store logic read failed\n"); + return ret; + } + + sha1_update(ctx, verify_buff, this_verify_len); + } + } + + return 0; +} + +static int adnl_buffman_img_get_sha1sum(unsigned char *verify_sum) +{ + static sha1_context ctx; + struct adnl_img_common_param *img_common_param; + struct adnl_img_dw_param *img_down_param; + int ret; + + img_down_param = &img_transfer_info.img_trans_param.download; + img_common_param = &img_down_param->common_inf; + + sha1_starts(&ctx); + + ret = adnl_img_verify_sha1sum_other(img_down_param->img_fmt, &ctx, + img_common_param->img_sz_total, + img_common_param->part_name, + img_common_param->part_start_off); + if (ret) + return ret; + + sha1_finish(&ctx, verify_sum); + + return 0; +} + +int adnl_verify_partition_img(const char *verify_alg, const char *origsum_str, + char *ack) +{ + unsigned char gensum[SHA1_SUM_LEN] = { 0 }; + char gensum_str[SHA1_SUM_LEN * 2 + 1]; + int ret; + + ret = strcmp(verify_alg, "sha1sum"); + if (ret) { + fastboot_response(ADNL_FAIL_STR, ack, + "algorithm '%s' unsupported\n", verify_alg); + return -EINVAL; + } + + if (strlen(origsum_str) != SHA1_SUM_LEN * 2) { + fastboot_response(ADNL_FAIL_STR, ack, + "invalid hash len for algo %s\n", verify_alg); + return -EINVAL; + } + + ret = adnl_buffman_img_get_sha1sum(gensum); + if (ret) { + ADNL_ERR("failed to check SHA1: %d", ret); + return ret; + } + + bin2hex(gensum_str, gensum, SHA1_SUM_LEN); + + ret = strncmp(gensum_str, origsum_str, SHA1_SUM_LEN * 2); + if (ret) { + fastboot_response(ADNL_FAIL_STR, ack, "checksum not match %s != %s\n", + gensum_str, origsum_str); + return -EINVAL; + } + + ADNL_MSG("checksum is correct\n"); + return 0; +} + +static int adnl_nand_erase(const char *partition) +{ + struct erase_info ei = { 0 }; + struct mtd_info *mtd; + + mtd = get_mtd_device_nm(partition); + if (IS_ERR(mtd)) + return -ENODEV; + + ei.mtd = mtd; + ei.len = mtd->size; + ei.addr = 0; + + return mtd_erase(mtd, &ei); +} + +int adnl_buffman_img_init(union adnl_img_trans_param *img_param) +{ + struct adnl_img_common_param *common_inf; + char *part_name; + s64 img_size; + int ret; + + common_inf = &img_param->common_inf; + + memcpy(&img_transfer_info.img_trans_param, img_param, + sizeof(union adnl_img_trans_param)); + img_transfer_info.data_buf = (char *)ADNL_DOWNLOAD_MEM_BASE; + img_transfer_info.data_buf_cap = ADNL_DOWNLOAD_MEM_SIZE; + img_transfer_info.inited = 1; + img_size = common_inf->img_sz_total; + part_name = common_inf->part_name; + + ret = adnl_media_check_image_size(img_size, part_name); + if (ret) { + ADNL_ERR("failed to check image size\n"); + return ret; + } + + ADNL_MSG("erasing MTD partition '%s'\n", part_name); + + ret = adnl_nand_erase(part_name); + if (ret) { + ADNL_ERR("erasing MTD partition '%s' failed %i\n", part_name, ret); + return ret; + } + + if (!strncmp(part_name, BOOT_LOADER, ADNL_PART_NAME_LEN)) { + /* + * If passed partition name is "bootloader", then + * erase "tpl" partition also. This is needed, + * because image for "bootloader" partition also + * contains image for "tpl". + */ + part_name = BOOT_TPL; + ret = adnl_nand_erase(part_name); + if (ret) { + ADNL_ERR("erasing MTD partition '%s' failed %i\n", part_name, ret); + return ret; + } + } + + raw_img_file_offset = 0; + + return 0; +} + +static int adnl_buffman_fill_download_info_rawimg(struct adnl_img_dw_param *img_param) +{ + struct adnl_img_common_param *cmn_inf = &img_param->common_inf; + s64 left_len; + + adnl_usb_dw_info.file_offset = raw_img_file_offset; + left_len = cmn_inf->img_sz_total - raw_img_file_offset; + + if (strncmp(BOOT_LOADER, cmn_inf->part_name, ADNL_PART_NAME_LEN)) + /* For non "bootloader" partitions send data with + * smaller blocks - such partitions are significantly + * bigger. + */ + adnl_usb_dw_info.data_size = min_t(s64, left_len, + RAW_IMG_TRANSFER_LEN); + else + adnl_usb_dw_info.data_size = left_len; + + adnl_usb_dw_info.data_buf = (char *)ADNL_DOWNLOAD_MEM_BASE; + + /* For next raw img download. */ + raw_img_file_offset += adnl_usb_dw_info.data_size; + + return 0; +} + +int adnl_buffman_fill_download_info(struct adnl_usb_dw_info **download_inf) +{ + struct adnl_img_dw_param *img_param; + int ret; + + *download_inf = NULL; + + if (!img_transfer_info.inited) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "invalid state %d\n", + img_transfer_info.inited); + return -EINVAL; + } + + img_param = &img_transfer_info.img_trans_param.download; + + if (img_param->img_fmt != ADNL_PART_IMG_FMT_RAW) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "unknown image format %x\n", img_param->img_fmt); + return -EINVAL; + } + + ret = adnl_buffman_fill_download_info_rawimg(img_param); + if (ret) + return ret; + + *download_inf = &adnl_usb_dw_info; + + return ret; +} + +static int adnl_buffman_complete_raw(const struct adnl_usb_dw_info *download_inf, + const char *part_name, + u8 *data_buf, + int this_transfer_len) +{ + /* 'bootloader' partition has special layout on storage, so we + * have special handler to work with this partition. + */ + if (!strncmp(BOOT_LOADER, part_name, ADNL_PART_NAME_LEN)) + return meson_bootloader_write(data_buf, this_transfer_len); + else + return adnl_store_logic_write(part_name, + download_inf->file_offset, + this_transfer_len, data_buf); +} + +int adnl_buffman_data_complete_download(const struct adnl_usb_dw_info *download_inf) +{ + struct adnl_img_common_param *cmn_inf; + struct adnl_img_dw_param *down_inf; + + down_inf = &img_transfer_info.img_trans_param.download; + + if (down_inf->img_fmt != ADNL_PART_IMG_FMT_RAW) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "unknown image format %x\n", down_inf->img_fmt); + return -EINVAL; + } + + cmn_inf = &img_transfer_info.img_trans_param.common_inf; + + return adnl_buffman_complete_raw(download_inf, + cmn_inf->part_name, + download_inf->data_buf, + download_inf->data_size); +} diff --git a/drivers/usb/gadget/amlogic/adnl/adnl_media.c b/drivers/usb/gadget/amlogic/adnl/adnl_media.c new file mode 100644 index 00000000000..ec8b8cd630a --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/adnl_media.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adnl.h" + +static int adnl_store_boot_read_tpl(void *buf, size_t size, unsigned int copy_idx) +{ + size_t retlen; + loff_t offset, limit; + struct mtd_info *mtd; + + mtd = get_mtd_device_nm(BOOT_TPL); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + offset = mtd->offset + (copy_idx * TPL_SIZE_PER_COPY); + limit = offset + TPL_SIZE_PER_COPY; + + return nand_read_skip_bad(mtd->parent, offset, &size, &retlen, limit, buf); +} + +static int adnl_store_boot_read_bl2(void *buf, unsigned int copy_idx) +{ + unsigned int size_per_copy; + struct mtd_info *mtd; + loff_t offset; + size_t length; + + mtd = get_mtd_device_nm(BOOT_LOADER); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + size_per_copy = mtd->writesize * (BL2_TOTAL_PAGES / BL2_COPY_NUM); + offset = mtd->offset + (copy_idx * size_per_copy); + length = BL2_IMAGE_SIZE_PER_COPY; + + return nand_read(mtd->parent, offset, &length, buf); +} + +int adnl_bootloader_read_n_verify(u8 *buf, size_t buf_len, size_t bootloader_len) +{ + unsigned int copy_idx; + int ret; + + /* 'bootloader_len' consists of BL2 and TPL. This function + * reads BL2 to 'buf' and then places TPL directly after it. + * Also this function checks, that all copies of BL2 and TPL + * are same. To do that, we need extra memory in 'buf': first + * copy is read to the start of the buffer, then every next + * copy is placed after the first one and compared to it. + * + * 'buf': | | + * |________________________________| + * ^ ^ + * |________________________________| + * 'buf_len' + * + * + * 1) During BL2 read and checks: + * + * | BL2 1st | | + * |_________|_________________________| + * + * + * | BL2 1st | BL2 Nst | | + * |_________|_________|_______________| + * ^ ^ ^ + * |_________|_________| + * 'memcmp()' + * + * 2) During TPL read and checks: + * + * | BL2 1st | TPL 1st | | + * |_________|_________|_______________| + * + * + * | BL2 1st | TPL 1st | TPL Nst | | + * |_________|_________|_________|_____| + * ^ ^ ^ + * |_________|_________| + * 'memcmp()' + * + * 3) Final state of the buffer: + * + * | BL2 1st | TPL 1st | | + * |_________|_________|_______________| + * ^ ^ + * |___________________| + * 'bootloader_len' + * + * For simple and rude check this is enough: + */ + if ((bootloader_len << 1) > buf_len) + return -EINVAL; + + for (copy_idx = 0; copy_idx < BL2_COPY_NUM; copy_idx++) { + void *data_buf = buf + BL2_IMAGE_SIZE_PER_COPY * !!copy_idx; + + ret = adnl_store_boot_read_bl2(data_buf, copy_idx); + if (ret) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "failed to read part BL2:'%u'\n", copy_idx); + return ret; + } + + /* All copies must be valid. Otherwise - something goes wrong. */ + if (copy_idx) { + if (memcmp(buf, data_buf, BL2_IMAGE_SIZE_PER_COPY)) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "boot part 'BL2':'%u' mismatch\n", + copy_idx); + return -EIO; + } + } + } + + /* Now we place TPL to 'buf'. 'bootloader_len' will be TPL length. */ + buf += BL2_IMAGE_SIZE_PER_COPY; + bootloader_len -= BL2_IMAGE_SIZE_PER_COPY; + + for (copy_idx = 0; copy_idx < TPL_COPY_NUM; copy_idx++) { + void *data_buf = buf + bootloader_len * !!copy_idx; + + ret = adnl_store_boot_read_tpl(data_buf, bootloader_len, copy_idx); + if (ret) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "failed to read part TPL:'%i'\n", copy_idx); + return ret; + } + + /* All copies must be valid. Otherwise - something goes wrong. */ + if (copy_idx) { + if (memcmp(buf, data_buf, bootloader_len)) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "boot part TPL:'%i' mismatch\n", copy_idx); + return -EIO; + } + } + } + + return 0; +} + +static uint64_t adnl_store_part_size(const char *part_name) +{ + struct mtd_info *mtd; + + if (!strcmp(part_name, BOOT_BL2) || + !strcmp(part_name, BOOT_SPL)) { + mtd = get_mtd_device_nm(BOOT_LOADER); + } else if (!strcmp(part_name, BOOT_TPL) || + !strcmp(part_name, BOOT_FIP)) { + mtd = get_mtd_device_nm(BOOT_TPL); + } else { + mtd = get_mtd_device_nm(part_name); + } + + if (IS_ERR_OR_NULL(mtd)) + return 0; + + return mtd->size; +} + +int adnl_media_check_image_size(s64 img_sz, const char *part_name) +{ + u64 part_cap; + + if (!strcmp(BOOT_LOADER, part_name)) { + unsigned int boot_size = BL2_IMAGE_SIZE_PER_COPY + TPL_SIZE_PER_COPY; + + if (img_sz > boot_size) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "image size %lld > boot size %u\n", + img_sz, boot_size); + return -EINVAL; + } + return 0; + } + + part_cap = adnl_store_part_size(part_name); + if (!part_cap) { + ADNL_ERR("failed to get size for part %s\n", part_name); + return -ENODEV; + } + + if (img_sz > part_cap) { + ADNL_ERR("img size %lld > part size %llu\n", img_sz, part_cap); + return -EINVAL; + } + + return 0; +} + +int adnl_mwrite_cmd_parser(unsigned long img_size, unsigned long part_off, + const char *img_format, const char *media, + const char *part_name, char *ack) +{ + union adnl_img_trans_param img_trans_param = { 0 }; + struct adnl_img_dw_param *img_download_param; + struct adnl_img_common_param *common_info; + + if (strcmp(img_format, "normal")) { + fastboot_response(ADNL_FAIL_STR, ack, + "unknown image format %s", img_format); + return -EINVAL; + } + + if (strcmp(media, "store")) { + fastboot_response(ADNL_FAIL_STR, ack, + "unknown media '%s'", media); + return -EINVAL; + } + + img_download_param = &img_trans_param.download; + common_info = &img_download_param->common_inf; + common_info->img_sz_total = img_size; + common_info->part_start_off = part_off; + + img_download_param->img_fmt = ADNL_PART_IMG_FMT_RAW; + strlcpy(common_info->part_name, part_name, ADNL_PART_NAME_LEN); + + return adnl_buffman_img_init(&img_trans_param); +} diff --git a/drivers/usb/gadget/amlogic/adnl/adnl_storage.c b/drivers/usb/gadget/amlogic/adnl/adnl_storage.c new file mode 100644 index 00000000000..99dc4af7627 --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/adnl_storage.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "adnl.h" + +static int adnl_store_get_offset(const char *partname, + loff_t *retoff, loff_t off) +{ + struct mtd_info *mtd_info; + + if (!strcmp(partname, BOOT_BL2)) + mtd_info = get_mtd_device_nm(BOOT_LOADER); + else + mtd_info = get_mtd_device_nm(partname); + + if (IS_ERR_OR_NULL(mtd_info)) + return -ENODEV; + + *retoff = off + mtd_info->offset; + + return 0; +} + +int adnl_store_logic_write(const char *name, loff_t off, size_t size, void *buf) +{ + struct mtd_info *mtd; + size_t retlen; + loff_t offset; + int ret; + + ret = adnl_store_get_offset(name, &offset, off); + if (ret) + return ret; + + mtd = get_mtd_device_nm(name); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + return nand_write_skip_bad(mtd, off, &size, &retlen, mtd->size, buf, 0); +} + +int adnl_store_logic_read(const char *name, loff_t off, size_t size, void *buf) +{ + struct mtd_info *mtd; + size_t retlen; + loff_t offset; + int ret; + + ret = adnl_store_get_offset(name, &offset, off); + if (ret) + return ret; + + mtd = get_mtd_device_nm(name); + if (IS_ERR_OR_NULL(mtd)) + return -ENODEV; + + return nand_read_skip_bad(mtd, off, &size, &retlen, mtd->size, buf); +} + +static bool adnl_must_skip_block(struct mtd_info *mtd, off_t offset) +{ + if (mtd_block_isbad(mtd, offset)) + return true; + + if (mtd_block_isreserved(mtd, offset)) + return true; + + return false; +} + +int adnl_erase_storage(int erase_mode) +{ + switch (erase_mode) { + case ADNL_ERASE_NOTHING: + ADNL_MSG("Erase nothing...\n"); + return 0; + case ADNL_ERASE_ALL: + /* Full erase includes erasing reserved areas: + * 1) "key" area. + * 2) Bad block table. + * 3) Some other blocks, marked as "bad" or "reserved". + * + * Currently it is not supported. + */ + ADNL_MSG("Full erase is not supported, fallback to normal\n"); + /* Fall through */ + case ADNL_ERASE_NORMAL: { + struct mtd_info *mtd; + off_t offset; + + ADNL_MSG("Erase normal...\n"); + /* + * "bootloader" is always present in ADNL, so use it to + * get MTD device for the entire NAND chip. + */ + mtd = get_mtd_device_nm(BOOT_LOADER); + if (!mtd || !mtd->parent) + return -ENODEV; + + mtd = mtd->parent; + + for (offset = 0; offset < mtd->size; offset += mtd->erasesize) { + struct erase_info erase_op = {}; + int ret; + + if (adnl_must_skip_block(mtd, offset)) + continue; + + erase_op.mtd = mtd; + erase_op.addr = offset; + erase_op.len = mtd->erasesize; + + ret = mtd_erase(mtd, &erase_op); + if (ret) { + ADNL_ERR("Can't erase block at %lx\n", + offset); + return ret; + } + } + + ADNL_MSG("Erased ok\n"); + + return 0; + } + default: + ADNL_ERR("Unknown erase mode %d\n", erase_mode); + return -EINVAL; + } +} diff --git a/drivers/usb/gadget/amlogic/adnl/f_adnl.c b/drivers/usb/gadget/amlogic/adnl/f_adnl.c new file mode 100644 index 00000000000..0b772acc4e4 --- /dev/null +++ b/drivers/usb/gadget/amlogic/adnl/f_adnl.c @@ -0,0 +1,835 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 SaluteDevices, Inc. + * Author: Arseniy Krasnov + */ +/* + * Implementation of ADNL protocol. + * + * ADNL is Amlogic protocol for updating firmware images. It + * looks like fastboot, but: + * 1) Works only on Amlogic boards, as it uses SoC specific features in + * its commands (for example special registers, SMC calls, bootROM + * behaviour). + * 2) It uses Amlogic specific image format to update firmware - such + * image contains different images inside - BL2, U-boot and partitions. + * 3) It works only over USB transport. + * 4) Minimal part (to run BL2 and U-boot from memory) is implemented in + * the bootROM code inside Amlogic boards, so this protocol is a good + * tool to "resurrect" boards with for example corrupted bootloader. + * + * There are two possible operating modes for this protocol: + * * Updating from bootROM. + * 1) Device stops booting in the boot ROM and enters USB gadget mode + * introducing itself as ADNL device. + * 2) PC adnl-client sends BL2 and U-boot to the board, which are + * executed in memory and finally U-boot checks sticky registers + * and when it detects that we in ADNL mode, it runs command 'adnl' + * (e.g. without continue booting to the command line or autoboot + * sequence). + * 3) Boards enters USB gadget mode again still introducing itself as + * ADNL device. + * 4) PC application continue ADNL operations (see NOTES#1 for more + * details). + * + * * Updating from U-boot command line. + * 1) U-boot runs 'adnl' command (run from script or entered by user). + * 2) ADNL logic in U-boot tells BL1 not to load BL2 and next loaders + * from storage, but receive it by USB data path. This feature is + * implemented by the special SMC call. + * 3) ADNL logic in U-boot reboots board. + * 4) BL1 receives BL2 and U-boot from USB. Now Goto 2) from list + * above (e.g. 'Updating from bootROM'). + * + * Protocol operation flow: + * + * To start protocol operations PC sends several commands: + * 'getvar identify' - tells PC some hardcoded fw version. + * 'oem setvar burnsteps' - performs writes to sticky registers. + * 'oem disk_initial' - this is suppressed in this implementation by + * always success wrapper. + * 'getvar secureboot' - this is suppressed in this implementation by + * always success wrapper. + * + * After this exchange, stage for data transmission and writing is + * started. Data is transmitted and written in 'per-partition' way. + * + * To send and write each partition the following commands sequence is + * executed: + * + * 1) PC sends 'oem mwrite normal store '. This will + * erase MTD partition and tells ADNL logic on the board to receive + * specified number of bytes and later write all of them to the + * . More, here is size of the entire image. + * + * 2) Devices replies 'OKAY'. + * + * 3) PC sends 'mwrite verify=addsum'. + * + * 4) Device replies 'DATAOUT::', where is number + * of bytes which device is ready to process and is offset in + * image to send data from. Note, here is size of entire image + * only for "bootloader" image, for another images it is 128KB. See + * RAW_IMG_TRANSFER_LEN. + * + * 5) PC sends data. There are no protocol commands in this type of + * packet - just raw data. Size of packet is USB specific and is + * not related to the from 2). + * + * 6) Device replies with empty packet. + * + * 7) Device checks that requested amount of data ( from 4)) is + * received. If no, goto 3) (e.g. continue receiving data from PC). + * If so, device performs simple checksum verification using algorithm + * from 'adnl_csum_special()' (because of parameter 'verify=addsum') from + * 3). Original checksum is transferred in the tail bytes of the last + * USB packet for the current portion of data. If verification is + * successful, device write data to storage and replies 'OKAY'. + * + * 8) If not all bytes from 1) are processed, goto 3) (e.g. receive next + * portion of data from PC starting from next offset in the image). + * + * 9) PC sends 'oem verify '. It means check partition from + * 1) with algorithm , it's hash must be equal to the . + * Currently only 'sha1sum' is supported as . Note, in this step + * device reads whole partition part by part (as verification algo + * allows) from storage and calculates its hash. If hash verification + * is successful, device replies 'OKAY'. + * + * NOTES#2: + * Current implementation has limited set of ADNL features and commands. + * It was tested only on A1 SoC with one image of specific layout/content + * (BL2, TPL, etc.). + * + * NOTES#3: + * "bootloader" partition has special layout: BL2 and TPL are saved as + * several copies of each other with specific alignment. + * + * NOTES#4: + * This implementation requires that data buffer address and size to be + * set in the config file. This buffers will be used for both IN and OUT + * USB data. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adnl.h" +#include "f_fastboot_common.h" +#include "cmd/meson/gadget.h" + +#define ADNL_CMD_GETVAR "getvar:" +#define ADNL_CMD_OEM "oem" +#define ADNL_CMD_DOWNLOAD "download:" +#define ADNL_CMD_UPDATE "update" +#define ADNL_CMD_REBOOT "reboot" +#define ADNL_CMD_MWRITE "mwrite" + +static char adnl_ext_prop_name[] = "DeviceInterfaceGUID"; +static char adnl_ext_prop_data[] = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}"; + +static struct usb_os_desc_ext_prop adnl_ext_prop = { + .type = 1, + .name = adnl_ext_prop_name, /* NUL-terminated Unicode String (REG_SZ) */ + .data = adnl_ext_prop_data, +}; + +/* 16 bytes of "Compatible ID" and "Subcompatible ID" */ +static char adnl_cid[16] = {'W', 'I', 'N', 'U', 'S', 'B'}; +static struct usb_os_desc adnl_os_desc = { + .ext_compat_id = adnl_cid, +}; + +static struct usb_os_desc_table adnl_os_desc_table = { + .os_desc = &adnl_os_desc, +}; + +static struct fastboot_funcs *adnl_func; +static unsigned int download_size; +static unsigned int download_bytes; + +static const char adnl_name[] = "Amlogic DNL"; + +static struct usb_string adnl_string_defs[] = { + [0].s = adnl_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_adnl = { + .language = 0x0409, /* en-us */ + .strings = adnl_string_defs, +}; + +static struct usb_gadget_strings *adnl_strings[] = { + &stringtab_adnl, + NULL, +}; + +static char response_str[RESPONSE_LEN + 1]; +char *fb_response_str = &response_str[4]; + +enum { + MWRITE_DATA_CHECK_ALG_NONE = 0, /* Not need checksum. */ + MWRITE_DATA_CHECK_ALG_ADDSUM, +}; + +struct adnl_mwrite_info { + unsigned int total_bytes; + unsigned int transferred_bytes; + unsigned int data_check_alg; + void *priv; +}; + +static struct adnl_mwrite_info adnl_mwrite_info; +static struct adnl_usb_dw_info *adnl_usb_dw_info; + +static int adnl_tx_write(const char *buffer, unsigned int buffer_size) +{ + struct usb_request *in_req = adnl_func->in_req; + int ret; + + memcpy(in_req->buf, buffer, buffer_size); + in_req->length = buffer_size; + + ret = usb_ep_queue(adnl_func->in_ep, in_req, 0); + if (ret) + ADNL_ERR("error %d on queueing %u bytes from tx buf\n", ret, + buffer_size); + + return 0; +} + +static int adnl_tx_write_str(const char *buffer) +{ + return adnl_tx_write(buffer, strlen(buffer)); +} + +static void adnl_rx_handler_command(struct usb_ep *ep, struct usb_request *req); + +static int dwc_blk_len(int left_sz) +{ + if (left_sz >= DWC_BLK_MAX_LEN) + return DWC_BLK_MAX_LEN; + + if (left_sz >= BULK_EP_MPS) + return (left_sz / BULK_EP_MPS) * BULK_EP_MPS; + + return left_sz; +} + +static unsigned int adnl_csum_special(const void *p_buf, const unsigned int size) +{ + const unsigned int *data = (const unsigned int *)p_buf; + unsigned int word_len = size >> 2; + unsigned int rest = size & 0x3; + unsigned int sum = 0; + + while (word_len--) + sum += *data++; + + if (rest) + sum += (*data) & GENMASK(rest * 8, 0); + + return sum; +} + +#define EP_CMD_LEN_MAX 256 +/* This is input handler for raw image data. */ +static void rx_handler_mwrite(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int transfer_size = req->actual; + char *data_buf = adnl_usb_dw_info->data_buf; + int ret = 0; + + if (req->status) { + ADNL_ERR("Bad status: %d\n", req->status); + return; + } + + adnl_mwrite_info.transferred_bytes += transfer_size; + + /* Check if transfer is done */ + if (adnl_mwrite_info.transferred_bytes == adnl_mwrite_info.total_bytes && + adnl_mwrite_info.data_check_alg > MWRITE_DATA_CHECK_ALG_NONE) { + /* This is one before last packet with data. */ + req->length = 4; /* For rx addsum. */ + req->buf += transfer_size; + + req->buf = (char *)ALIGN((s64)req->buf, 8); + } else if (adnl_mwrite_info.transferred_bytes >= adnl_mwrite_info.total_bytes) { + fastboot_okay(NULL, response_str); + response_str[4] = 0; + + /* Calculate checksum for just transferred part of data. */ + if (adnl_mwrite_info.data_check_alg == MWRITE_DATA_CHECK_ALG_ADDSUM) { + unsigned int data_len = adnl_mwrite_info.total_bytes; + unsigned int gensum = adnl_csum_special(data_buf, data_len); + unsigned int origsum; + + origsum = *(unsigned int *)ALIGN((s64)(data_buf + data_len), 8); + + if (gensum != origsum) { + ADNL_MSG("checksum not match %x != %x\n", gensum, origsum); + fastboot_response(ADNL_FAIL_STR, response_str, + "checksum not match %x != %x\n", + gensum, origsum); + ret = -EINVAL; + } + } + + /* Checksum verification is done, write this part of image. */ + if (!ret) + ret = adnl_buffman_data_complete_download(adnl_usb_dw_info); + + if (ret) + fastboot_fail(NULL, response_str); + else + fastboot_okay(NULL, response_str); + + adnl_tx_write_str(response_str); + ADNL_MSG("response[%d][%s]\n", ret, response_str); + ADNL_DBG("mwrite 0x%x bytes [%s]\n", adnl_mwrite_info.transferred_bytes, + ret ? "FAILED" : "OK"); + /* Set default receive callback for handling ADNL commands. + * We are ready to receive next portion of data or image is + * already transferred. + */ + req->complete = adnl_rx_handler_command; + req->length = EP_CMD_LEN_MAX; + + if (adnl_mwrite_info.priv) + req->buf = (char *)adnl_mwrite_info.priv; + } else { + unsigned int left_len = adnl_mwrite_info.total_bytes - + adnl_mwrite_info.transferred_bytes; + + /* Continue receiving data. */ + req->length = dwc_blk_len(left_len); + req->buf += transfer_size; + } + + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static int adnl_bind(struct usb_configuration *c, struct usb_function *f) +{ + return fastboot_common_bind(c, f, &adnl_os_desc_table, + &adnl_os_desc, + (struct usb_string **)&adnl_string_defs, + &adnl_ext_prop); +} + +static void adnl_unbind(struct usb_configuration *c, struct usb_function *f) +{ + fastboot_common_unbind(c, f, &adnl_os_desc); + memset(adnl_func, 0, sizeof(*adnl_func)); +} + +static void adnl_disable(struct usb_function *f) +{ + fastboot_common_disable(f); +} + +static int adnl_set_alt(struct usb_function *f, + unsigned int interface, unsigned int alt) +{ + amlogic_gadget_set_times(0, get_timer(0)); + return fastboot_common_set_alt(f, interface, alt, adnl_rx_handler_command); +} + +static int adnl_add(struct usb_configuration *c) +{ + struct fastboot_funcs *f_adnl = adnl_func; + int status; + + if (!f_adnl) { + f_adnl = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_adnl)); + if (!f_adnl) + return -ENOMEM; + + adnl_func = f_adnl; + memset(f_adnl, 0, sizeof(*f_adnl)); + } + + g_dnl_set_product("DNL"); + f_adnl->usb_function.name = "f_adnl"; + f_adnl->usb_function.bind = adnl_bind; + f_adnl->usb_function.unbind = adnl_unbind; + f_adnl->usb_function.set_alt = adnl_set_alt; + f_adnl->usb_function.disable = adnl_disable; + f_adnl->usb_function.strings = adnl_strings; + + status = usb_add_function(c, &f_adnl->usb_function); + if (status) { + free(f_adnl); + adnl_func = NULL; + } + + return status; +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_adnl, adnl_add); + +struct cmd_getvar_dispatch_info { + char *cmd; + void (*cb)(char *response, size_t *reply_len); +}; + +static void cb_getvar_identify(char *response, size_t *reply_len) +{ + const char adnl_ver[] = {5, 0, 0, 16, 0, 0, 0, 0}; + + ADNL_MSG("executing identify cmd\n"); + + memcpy(response, adnl_ver, sizeof(adnl_ver)); + *reply_len = sizeof(adnl_ver); + amlogic_gadget_set_times(0, 0); +} + +static int adnl_is_secureboot_enabled(void) +{ + /* TODO: support secureboot. */ + return 0; +} + +static void cb_getvar_secureboot(char *response, size_t *reply_len) +{ + unsigned int secureboot_enable; + + secureboot_enable = adnl_is_secureboot_enabled(); + memcpy(response, &secureboot_enable, sizeof(unsigned int)); + *reply_len = sizeof(unsigned int); +} + +static const struct cmd_getvar_dispatch_info cmd_getvar_dispatch_info[] = { + { + .cmd = "identify", + .cb = cb_getvar_identify, + }, + { + .cmd = "secureboot", + .cb = cb_getvar_secureboot, + }, +}; + +static void cb_getvar(struct usb_request *req) +{ + void (*func_cb)(char *response, size_t *reply_len); + char *cmd = req->buf; + char cmd_buf[RESPONSE_LEN]; + char *response = response_str; + size_t chars_left; + size_t reply_len; + int i; + + strcpy(response, "OKAY"); + chars_left = sizeof(response_str) - strlen(response) - 1; + + memcpy(cmd_buf, cmd, strnlen(cmd, RESPONSE_LEN - 1) + 1); + cmd = cmd_buf; + strsep(&cmd, ":"); + + if (!cmd || cmd[0] == '\0') { + fastboot_response(ADNL_FAIL_STR, response, "missing var\n"); + adnl_tx_write_str(response); + return; + } + + func_cb = NULL; + + for (i = 0; i < ARRAY_SIZE(cmd_getvar_dispatch_info); i++) { + if (!strncmp(cmd_getvar_dispatch_info[i].cmd, cmd, + strlen(cmd_getvar_dispatch_info[i].cmd))) { + func_cb = cmd_getvar_dispatch_info[i].cb; + break; + } + } + + reply_len = 0; + + if (!func_cb) { + ADNL_ERR("unknown variable: %s\n", cmd); + adnl_tx_write_str(ADNL_FAIL_STR "unknown variable"); + } else { + reply_len = chars_left; + func_cb(response_str + 4, &reply_len); + reply_len += 4; + } + + if (!reply_len) + reply_len = strlen(response) + 1; + + adnl_tx_write(response, reply_len); +} + +static int adnl_set_var(const char *name, const char *value) +{ + if (!strcmp("burnsteps", name)) { + unsigned int val = simple_strtoul(value, NULL, 0); + + writel(val, A1_SYSCTRL_SEC_STICKY_REG2); + + return 0; + } + + return -EINVAL; +} + +static void cb_oem_cmd(struct usb_request *req) +{ + char *argv[CONFIG_SYS_MAXARGS + 1]; + char tmp[RESPONSE_LEN + 1]; + char *cmd = req->buf; + char *ack = response_str + 4; + char *cmd_buf = tmp; + int ret, argc; + + ack[0] = '\0'; + memcpy(cmd_buf, cmd, strnlen(cmd, RESPONSE_LEN) + 1); + strsep(&cmd_buf, " "); + response_str[4] = 0; + + argc = cli_simple_parse_line(cmd_buf, argv); + + if (!argc) { + fastboot_fail("oem no command at all", response_str); + ADNL_ERR("%s\n", response_str); + return; + } + + if (!strcmp("mwrite", argv[0])) { + if (argc < 5) { + ret = -EINVAL; + } else { + unsigned long img_size; + unsigned long part_off; + + img_size = simple_strtoul(argv[1], NULL, 0); + part_off = argc > 5 ? simple_strtoul(argv[5], NULL, 0) : 0; + ret = adnl_mwrite_cmd_parser(img_size, part_off, argv[2], + argv[3], argv[4], ack); + } + } else if (!strcmp("verify", argv[0])) { + ret = adnl_verify_partition_img(argv[1], argv[2], ack); + } else if (!strcmp("disk_initial", argv[0])) { + int erase_mode = argc > 1 ? simple_strtoul(argv[1], NULL, 0) : 0; + + ret = adnl_erase_storage(erase_mode); + } else if (!strcmp("save_setting", argv[0])) { + ADNL_MSG("saveenv not implemented\n"); + ret = 0; + } else if (!strcmp("setvar", argv[0])) { + if (argc < 2) + ret = -EINVAL; + else + ret = adnl_set_var(argv[1], argv[2]); + } else { + strsep(&cmd, " "); + ret = run_command(cmd, 0); + if (ret) + fastboot_response(ADNL_FAIL_STR, ack, + "fail in cmd[%s]", cmd); + } + + if (ret) + fastboot_fail(NULL, response_str); + else + fastboot_okay(NULL, response_str); + + adnl_tx_write_str(response_str); + ADNL_MSG("response[%d][%s]\n", ret, response_str); +} + +static void cb_aml_media_write(struct usb_request *req) +{ + char *cmd = req->buf; + const char *field; + int ret; + + ADNL_DBG("cmd cb_mwrite[%s]\n", cmd); + + strsep(&cmd, ":"); + field = cmd; + response_str[4] = 0; + + adnl_mwrite_info.data_check_alg = MWRITE_DATA_CHECK_ALG_NONE; + adnl_mwrite_info.transferred_bytes = 0; + + for (strsep(&cmd, "="); cmd; ) { + if (!strcmp(field, "verify")) { + if (!strcmp("addsum", cmd)) { + adnl_mwrite_info.data_check_alg = MWRITE_DATA_CHECK_ALG_ADDSUM; + } else { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "unknown data check algo '%s'\n", cmd); + fastboot_fail(NULL, response_str); + return; + } + } else { + snprintf(response_str, sizeof(response_str), + ADNL_FAIL_STR "unknown field '%s'\n", field); + ADNL_ERR("%s", response_str); + ret = -EINVAL; + goto _exit; + } + + strsep(&cmd, ","); + strsep(&cmd, "="); + } + + ret = adnl_buffman_fill_download_info(&adnl_usb_dw_info); + if (ret || !adnl_usb_dw_info) { + fastboot_response(ADNL_FAIL_STR, fb_response_str, + "Fail in buffman get, ret %d", ret); + goto _exit; + } + + adnl_mwrite_info.total_bytes = adnl_usb_dw_info->data_size; + ret = 0; + +_exit: + if (ret) { + fastboot_fail(NULL, response_str); + } else if (!adnl_usb_dw_info->data_size) { + fastboot_okay(NULL, response_str); + ADNL_MSG("OK in Partition Image\n"); + } else { + unsigned int left_len; + + snprintf(response_str, sizeof(response_str), + "DATAOUT0x%x:0x%llx", adnl_usb_dw_info->data_size, + adnl_usb_dw_info->file_offset); + req->complete = rx_handler_mwrite; + left_len = adnl_mwrite_info.total_bytes - + adnl_mwrite_info.transferred_bytes; + req->length = dwc_blk_len(left_len); + + if (!adnl_mwrite_info.priv) + adnl_mwrite_info.priv = req->buf; + + req->buf = adnl_usb_dw_info->data_buf; + } + + adnl_tx_write_str(response_str); +} + +static unsigned int rx_bytes_expected(void) +{ + int rx_remain = download_size - download_bytes; + + if (rx_remain < 0) + return 0; + + if (rx_remain > EP_BUFFER_SIZE) + return EP_BUFFER_SIZE; + + return rx_remain; +} + +#define BYTES_PER_DOT 0x20000 +static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int transfer_size = download_size - download_bytes; + unsigned char *buffer = req->buf; + unsigned int buffer_size = req->actual; + unsigned int pre_dot_num, now_dot_num; + + if (req->status) { + ADNL_ERR("Bad status: %d\n", req->status); + return; + } + + if (buffer_size < transfer_size) + transfer_size = buffer_size; + + memcpy((u8 *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes, + buffer, transfer_size); + + pre_dot_num = download_bytes / BYTES_PER_DOT; + download_bytes += transfer_size; + now_dot_num = download_bytes / BYTES_PER_DOT; + + if (pre_dot_num != now_dot_num) { + putc('.'); + + /* 65 is progress bar size, same as in TFTP. */ + if (!(now_dot_num % 65)) + putc('\n'); + } + + /* Check if transfer is done */ + if (download_bytes >= download_size) { + char response[RESPONSE_LEN]; + static char EP_CMD_BUF[EP_CMD_LEN_MAX * 2]; + + /* + * Reset global transfer variable, keep download_bytes because + * it will be used in the next possible flashing command + */ + download_size = 0; + req->complete = adnl_rx_handler_command; + req->length = EP_CMD_LEN_MAX; + req->buf = &EP_CMD_BUF[0]; + + sprintf(response, "OKAY"); + adnl_tx_write_str(response); + + ADNL_MSG("\ndownloading of %u bytes finished\n", download_bytes); + } else { + req->length = rx_bytes_expected(); + + if (req->length < ep->maxpacket) + req->length = ep->maxpacket; + } + + req->actual = 0; + usb_ep_queue(ep, req, 0); +} + +static void cb_download(struct usb_request *req) +{ + char *cmd = req->buf; + char response[RESPONSE_LEN]; + + strsep(&cmd, ":"); + download_size = simple_strtoul(cmd, NULL, 16); + download_bytes = 0; + + if (!download_size) { + fastboot_fail(response, "data invalid size"); + } else if (download_size > ADNL_DOWNLOAD_MEM_SIZE) { + download_size = 0; + fastboot_fail(response, "data too large"); + } else { + sprintf(response, "DATA%08x", download_size); + req->complete = rx_handler_dl_image; + req->buf = (char *)CONFIG_ADNL_BUF_ADDR; + req->length = rx_bytes_expected(); + } + + adnl_tx_write_str(response); +} + +static void adnl_complete_do_reboot(struct usb_ep *ep, struct usb_request *req) +{ + do_reset(NULL, 0, 0, NULL); +} + +static void adnl_complete_do_reboot_bootloader(struct usb_ep *ep, struct usb_request *req) +{ + run_command("reboot bootloader", 0); +} + +static void adnl_complete_do_reboot_bl1usb(struct usb_ep *ep, struct usb_request *req) +{ + /* + * Reboot into bootROM (BL1) with USB as first boot source, + * awaiting forever the host connection. + */ + meson_sm_set_bl1_first_boot_source(SCPI_CMD_USB_UNBOOT); + do_reset(NULL, 0, 0, NULL); +} + +static void cb_reboot(struct usb_request *req) +{ + char *cmd = req->buf; + + ADNL_MSG("reboot '%s'\n", cmd); + + strsep(&cmd, "-"); + + if (!cmd) { + adnl_func->in_req->complete = adnl_complete_do_reboot; + } else if (!strcmp("bootloader", cmd)) { + adnl_func->in_req->complete = adnl_complete_do_reboot_bootloader; + } else if (!strcmp("romusb", cmd)) { + adnl_func->in_req->complete = adnl_complete_do_reboot_bl1usb; + } else { + fastboot_fail("unsupported reboot cmd", response_str); + adnl_tx_write_str(response_str); + return; + } + + adnl_tx_write_str("OKAY"); +} + +struct cmd_dispatch_info { + const char *cmd; + void (*const cb)(struct usb_request *req); +}; + +static const struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = ADNL_CMD_GETVAR, + .cb = cb_getvar, + }, + { + .cmd = ADNL_CMD_OEM, + .cb = cb_oem_cmd, + }, + { + .cmd = ADNL_CMD_DOWNLOAD, + .cb = cb_download, + }, + { + .cmd = ADNL_CMD_UPDATE, + .cb = cb_download, + }, + { + .cmd = ADNL_CMD_REBOOT, + .cb = cb_reboot, + }, + { + .cmd = ADNL_CMD_MWRITE, + .cb = cb_aml_media_write, + }, +}; + +static void adnl_rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + void (*func_cb)(struct usb_request *req); + char *cmdbuf = req->buf; + int i; + + cmdbuf[req->actual] = '\0'; + func_cb = NULL; + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + if (!strncmp(cmd_dispatch_info[i].cmd, cmdbuf, + strlen(cmd_dispatch_info[i].cmd))) { + ADNL_MSG("got cmd '%s'\n", cmd_dispatch_info[i].cmd); + func_cb = cmd_dispatch_info[i].cb; + break; + } + } + + if (!func_cb) { + ADNL_ERR("unknown command: %s\n", cmdbuf); + adnl_tx_write_str(ADNL_FAIL_STR "unknown command"); + } else { + if (req->actual < req->length) { + u8 *buf = (u8 *)req->buf; + + buf[req->actual] = 0; + func_cb(req); + } else { + ADNL_ERR("buffer overflow\n"); + adnl_tx_write_str(ADNL_FAIL_STR "buffer overflow"); + } + } + + if (!req->status) { + *cmdbuf = '\0'; + req->actual = 0; + usb_ep_queue(ep, req, 0); + } +} From patchwork Wed Mar 19 20:20:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062989 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=Mpl82KTc; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (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 4ZJ0Wb3ltDz1xrn for ; Thu, 20 Mar 2025 07:22:51 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 5358481E3E; Wed, 19 Mar 2025 21:21:18 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="Mpl82KTc"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id A40108129F; Wed, 19 Mar 2025 21:21:11 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (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 9507281C84 for ; Wed, 19 Mar 2025 21:21:01 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 06D8D120014; Wed, 19 Mar 2025 23:21:01 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 06D8D120014 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415661; bh=beN8T1cB4+EB2MZsbBmqpWowbJ9IKNEn0swDpJ8PUP0=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=Mpl82KTcakZYx9B/Dz8CBBJZIQCuljuAJj7zFhBGCTilr+n21WqmAD51M/rszAugC VttrqNtvzHtFbjtPhGYS5WGqCeqhr4gYxmGaZLjEGfTALOnv5GqHOaIIqhF9Uf37OK H7CH6BTa9HjWZDnoHKwn3PeJKOSToFGiLhB7tK46Z4kr2rVW7zXtWZWzFUU/qEOmMs /siDvnZVWhlhX8smd8d7XXK6axPGADJh9deYp49F7OWZEqfqf7V3Sd92ojBwp9GF63 Fa7nbEYNRSAd2qpkKYsPYrW8fsRU7MJMaWVwfHjUSbQdPDibww/sZpuwRgh+ELFaoZ 93wcjKdBwVcKA== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:21:00 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , Subject: [PATCH v1 09/11] arm: meson: a1: ADNL protocol support Date: Wed, 19 Mar 2025 23:20:43 +0300 Message-ID: <20250319202045.1407-10-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; salutedevices.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 19:45:00 #27804052 X-KSMG-AntiVirus-Status: Clean, skipped 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 During ADNL protocol operations, BootROM may request to enter ADNL mode after boot. So to support this, let's check for values in sticky registers after boot (such registers could be updated earlier by boot ROM, telling U-boot to enter ADNL mode. Signed-off-by: Arseniy Krasnov --- arch/arm/include/asm/arch-meson/a1.h | 13 +++++++++++ arch/arm/mach-meson/board-a1.c | 32 +++++++++++++++++++++++++++- arch/arm/mach-meson/board-common.c | 17 +++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/arch-meson/a1.h b/arch/arm/include/asm/arch-meson/a1.h index 16e209ed932..4a22aaf5627 100644 --- a/arch/arm/include/asm/arch-meson/a1.h +++ b/arch/arm/include/asm/arch-meson/a1.h @@ -12,6 +12,7 @@ /* SYSCTRL registers */ #define A1_SYSCTRL_ADDR(off) (A1_SYSCTRL_BASE + ((off) << 2)) +#define A1_SYSCTRL_SEC_STATUS_REG1 A1_SYSCTRL_ADDR(0xc1) #define A1_SYSCTRL_SEC_STATUS_REG4 A1_SYSCTRL_ADDR(0xc4) /* * Checked during boot. Tells, that previous code, e.g. @@ -20,7 +21,19 @@ */ #define A1_SYSCTRL_SEC_STICKY_REG2 A1_SYSCTRL_ADDR(0xe2) +#define A1_AO_BOOT_DEVICE 0xF #define A1_SYSCTRL_MEM_SIZE_MASK 0xFFFF0000 #define A1_SYSCTRL_MEM_SIZE_SHIFT 16 +#if CONFIG_IS_ENABLED(ADNL) +/** + * is_tpl_loaded_from_usb - Checks, that this instance of U-boot was + * loaded from USB during firmware update + * process. + * + * returns: true, if so. + */ +int is_tpl_loaded_from_usb(void); +#endif + #endif /* __MESON_A1_H__ */ diff --git a/arch/arm/mach-meson/board-a1.c b/arch/arm/mach-meson/board-a1.c index f848c0f068e..9cc410f1e57 100644 --- a/arch/arm/mach-meson/board-a1.c +++ b/arch/arm/mach-meson/board-a1.c @@ -3,6 +3,7 @@ * (C) Copyright 2023 SberDevices, Inc. */ +#include #include #include #include @@ -23,9 +24,38 @@ void meson_init_reserved_memory(__maybe_unused void *fdt) int meson_get_boot_device(void) { - return -ENOSYS; + return readl(A1_SYSCTRL_SEC_STATUS_REG4) & A1_AO_BOOT_DEVICE; } +#if CONFIG_IS_ENABLED(ADNL) + +static bool is_boot_device_usb(void) +{ + return meson_get_boot_device() == BOOT_DEVICE_USB; +} + +#define MESON_ADNL_BL1_USB_PROTO_DNL_BIT BIT(12) + +static bool is_bl1_usb_protocol_DNL(void) +{ + u32 val = readl(A1_SYSCTRL_SEC_STATUS_REG1); + + return !(val & MESON_ADNL_BL1_USB_PROTO_DNL_BIT); +} + +int is_tpl_loaded_from_usb(void) +{ + if (!is_boot_device_usb()) + return false; + + if (!is_bl1_usb_protocol_DNL()) + return false; + + return true; +} + +#endif + static struct mm_region a1_mem_map[] = { { .virt = 0x00000000UL, diff --git a/arch/arm/mach-meson/board-common.c b/arch/arm/mach-meson/board-common.c index 39774c43049..0624d557e40 100644 --- a/arch/arm/mach-meson/board-common.c +++ b/arch/arm/mach-meson/board-common.c @@ -3,6 +3,7 @@ * (C) Copyright 2016 Beniamino Galvani */ +#include #include #include #include @@ -14,9 +15,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -141,10 +144,24 @@ __weak int meson_board_late_init(void) return 0; } +static void meson_board_try_enter_adnl(void) +{ +#if (IS_ENABLED(CONFIG_CMD_ADNL)) + if (!is_tpl_loaded_from_usb()) + return; + + meson_sm_set_bl1_first_boot_source(SCPI_CMD_CLEAR_BOOT); + + run_command("adnl", 0); +#endif +} + int board_late_init(void) { meson_set_boot_source(); + meson_board_try_enter_adnl(); + return meson_board_late_init(); } From patchwork Wed Mar 19 20:20:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062990 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=UHp+1XqF; 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 4ZJ0Wm2cnNz1xrn for ; Thu, 20 Mar 2025 07:23:00 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 9924281E8C; Wed, 19 Mar 2025 21:21:18 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="UHp+1XqF"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 3038680FF0; Wed, 19 Mar 2025 21:21:13 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx1.sberdevices.ru [37.18.73.165]) (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 BF64781CA8 for ; Wed, 19 Mar 2025 21:21:01 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk01.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 51178100016; Wed, 19 Mar 2025 23:21:01 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 51178100016 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415661; bh=8bFxN2lzYZUQCS/aScPWZo+WE9xH+qLuSWGgA/o5ITk=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=UHp+1XqFmfPU+WKpSJ9WEvi3NxZvZz1r7KHGmczhsBEQ2Yf+tUiiY4vwUOLMrAXih tXLwf8C24tBmSLkcQiTTrBdleiSmPSuytPycoF0gXVY9cF+rJ27jr8WIR+5MHATkt3 EgoaR9KkTn64pYiEmYLVqiQIRogOUEJO71z5Z+SyXC6CCHNyZvYHY7VlrlQqkbwAeb Ztl5dv+2ra78r/HMqVXhTZz1cVBMQE1qvD4hhjEv2C0qw1O9q61dY3o8hU9wH7Ijvo 148YJt4DoO7WnJ7xV7TPzEzP9HbH3P0IvVHuhwJ4EFzWexR7GHZhhTE9cfZ0OXdrW1 DsVC4Z7aMXkBA== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:21:01 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , , Vladimir Mitrofanov Subject: [PATCH v1 10/11] usb: gadget: amlogic: implement Optimus protocol Date: Wed, 19 Mar 2025 23:20:44 +0300 Message-ID: <20250319202045.1407-11-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; salutedevices.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 17:18:00 #27803216 X-KSMG-AntiVirus-Status: Clean, skipped 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: Vladimir Mitrofanov Add Amlogic's Optimus protocol support for USB transport. Signed-off-by: Vladimir Mitrofanov Signed-off-by: Arseniy Krasnov --- cmd/Kconfig | 7 + cmd/meson/Makefile | 1 + cmd/meson/optimus.c | 21 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/amlogic/Kconfig | 1 + drivers/usb/gadget/amlogic/optimus/Kconfig | 12 + drivers/usb/gadget/amlogic/optimus/Makefile | 7 + .../usb/gadget/amlogic/optimus/f_optimus.c | 687 ++++++++++++++++++ .../gadget/amlogic/optimus/optimus_download.c | 188 +++++ .../gadget/amlogic/optimus/optimus_download.h | 86 +++ 10 files changed, 1011 insertions(+) create mode 100644 cmd/meson/optimus.c create mode 100644 drivers/usb/gadget/amlogic/optimus/Kconfig create mode 100644 drivers/usb/gadget/amlogic/optimus/Makefile create mode 100644 drivers/usb/gadget/amlogic/optimus/f_optimus.c create mode 100644 drivers/usb/gadget/amlogic/optimus/optimus_download.c create mode 100644 drivers/usb/gadget/amlogic/optimus/optimus_download.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 2f195d03848..159e52d5298 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1153,6 +1153,13 @@ config CMD_ADNL This command, "adnl", enables the ADNL listen mode, providing the ability to listen in ADNL mode. +config CMD_OPTIMUS + bool "OPTIMUS - Amlogic download protocol" + depends on OPTIMUS + help + This command, "optimus", enables the OPTIMUS listen mode, providing + the ability to communicate with host by OPTIMUS protocol. + config CMD_FLASH bool "flinfo, erase, protect" default y diff --git a/cmd/meson/Makefile b/cmd/meson/Makefile index 1cf984378a3..611d1549736 100644 --- a/cmd/meson/Makefile +++ b/cmd/meson/Makefile @@ -4,3 +4,4 @@ obj-y += sm.o obj-$(CONFIG_CMD_ADNL) += gadget.o adnl.o +obj-$(CONFIG_CMD_OPTIMUS) += gadget.o optimus.o diff --git a/cmd/meson/optimus.c b/cmd/meson/optimus.c new file mode 100644 index 00000000000..97a1435c0ab --- /dev/null +++ b/cmd/meson/optimus.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024 SaluteDevices, Inc. + * Author: Vladimir Mitrofanov + */ + +#include + +#include "gadget.h" + +static int do_optimus(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + return amlogic_gadget_run("usb_dnl_optimus", argc, argv); +} + +U_BOOT_LONGHELP(optimus, + "- run as an OPTIMUS USB device\n\n"); + +U_BOOT_CMD(optimus, 1, 1, do_optimus, + "OPTIMUS protocol mode", optimus_help_text); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 2b6705c52be..f5899a96221 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o obj-$(CONFIG_USB_RENESAS_USBHS) += rcar/ +obj-$(CONFIG_OPTIMUS) += f_fastboot_common.o amlogic/optimus/ ifndef CONFIG_XPL_BUILD obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o diff --git a/drivers/usb/gadget/amlogic/Kconfig b/drivers/usb/gadget/amlogic/Kconfig index 460dc6fe235..5a16fb25998 100644 --- a/drivers/usb/gadget/amlogic/Kconfig +++ b/drivers/usb/gadget/amlogic/Kconfig @@ -7,5 +7,6 @@ choice optional source "drivers/usb/gadget/amlogic/adnl/Kconfig" +source "drivers/usb/gadget/amlogic/optimus/Kconfig" endchoice diff --git a/drivers/usb/gadget/amlogic/optimus/Kconfig b/drivers/usb/gadget/amlogic/optimus/Kconfig new file mode 100644 index 00000000000..6a730670d34 --- /dev/null +++ b/drivers/usb/gadget/amlogic/optimus/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2024 SaluteDevices, Inc. + +config OPTIMUS + bool "Enable OPTIMUS protocol" + depends on USB_GADGET + depends on MESON_AXG + help + This enables usb optimus function protocol + "Optimus" protocol is used to flash Amlogic devices + from bootRom or from U-Boot mode diff --git a/drivers/usb/gadget/amlogic/optimus/Makefile b/drivers/usb/gadget/amlogic/optimus/Makefile new file mode 100644 index 00000000000..f48806414aa --- /dev/null +++ b/drivers/usb/gadget/amlogic/optimus/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += f_optimus_cb.o + +subdir-ccflags-y += -I$(src)/../ -I$(src)/../../ +f_optimus_cb-y += f_optimus.o +f_optimus_cb-y += optimus_download.o diff --git a/drivers/usb/gadget/amlogic/optimus/f_optimus.c b/drivers/usb/gadget/amlogic/optimus/f_optimus.c new file mode 100644 index 00000000000..9912a691301 --- /dev/null +++ b/drivers/usb/gadget/amlogic/optimus/f_optimus.c @@ -0,0 +1,687 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 SaluteDevices, Inc. + * Author: Vladimir Mitrofanov + */ +/* + * OPTIMUS - is Amlogic protocol for updating firmware. + * This protocol is used for flashing devices based on Amlogic SoC. + * Optimus uses specific image format. In this implementation USB + * is used for data transfer. + * + * There are two ways to switch device to flashing mode: + * 1. Enter bootROM flashing mode (how this should be done depends on device) + * 2. Run from TPL (ex. from U-boot) + * + * Flashing steps + * 1. Device has to be switched to Optimus mode (as described earlier). + * 2. Host detects it as "Amlogic DNL" device, by sending identify request. + * 3. Host sends u-boot. BootROM loads it to RAM and executes u-boot. + * Finally u-boot detects that we are in Optimus update process, so it enters + * Optimus mode to continue fw update. + * 4. Now host starts to send protocol commands. + * First host sends some preparation commands like "low_power", "echo 1234" etc. + * The order of this commands depends on host software. + * Host may start flashing from any partition of the image (depends on host) + * 5. Host sends "bootloader_is_old" and "erase_bootloader". Device is preparing + * buffers and storage to receive "bootloader". "Bootloader" consists of two + * parts "bl2" and "tpl". As "tpl" could be used u-boot. + * 6. Host sends "download" command that contains information about full size of + * partition that will be sent. Partition is sending by chunks. Every chunk + * writes to permanent memory. + * "Bootloader" is exception. It should be fully received before writing to storage. + * After getting full "bootloader" device starts flashing it in to permanent memory. + * 7. After sending each partition of firmware Host sends "download get_status". + * 8. For every success operation Device respond with "success" status. + * 9. Host repeats steps form 6 - 8 with other partitions (A, B, ...). + * This partitions are sending by chunks, but in contrast ("bootloader") they + * are transmitting and writing to permanent memory by block of 64KiB (the size + * depends on host software). + * 10. After all partitions are received Host sends "save_setting" to restore + * environment to default state. + * + * NOTES#1: + * Current implementation has limited set of OPTIMUS features and commands. + * It was tested only on A113 SoC with one image of specific layout/content + * (BL2, TPL, etc.). + * + * NOTES#2: + * "bootloader" partition has special layout: BL2 and TPL are saved as + * several copies of each other with specific alignment. + * + * NOTES#3: + * This implementation requires that data buffer address and size to be + * set in the config file. This buffers will be used for both IN and OUT + * USB data. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_fastboot_common.h" +#include "optimus_download.h" + +#define CMD_DOWNLOAD "download" +#define CMD_LOWPOWER "low_power" +#define CMD_BL_IS_OLD "bootloader_is_old" +#define CMD_ERASE_BL "erase_bootloader" +#define CMD_RESET "reset" +#define CMD_UPLOAD "upload" +#define CMD_VERIFY "verify" +#define CMD_DISK_INITIAL "disk_initial" +#define CMD_SAVE_SET "save_setting" +#define CMD_BURN_COMPL "burn_complete" +#define CMD_ECHO_1234 "echo" +#define CMD_SETENV "setenv" +#define CMD_SAVE "save" +#define CMD_RPMB_RESET "rpmb_reset" +#define CMD_AMLMMC "amlmmc" +#define CMD_NAND "nand" +#define CMD_SUB_GETSTATUS "get_status" +#define CMD_SUB_STORE "store" + +#define MAX_RESPONSE_LEN 512 +#define MAX_CMD_LEN 60 +#define SPACE_IN_HEX 0x20 + +#define REQ_WRITE_MEM 0x01 +#define REQ_READ_MEM 0x02 +#define REQ_RUN_IN_ADDR 0x05 +#define REQ_WR_LARGE_MEM 0x11 +#define REQ_RD_LARGE_MEM 0x12 +#define REQ_IDENTIFY_HOST 0x20 +#define REQ_TPL_CMD 0x30 +#define REQ_TPL_STAT 0x31 +#define REQ_BULKCMD 0x34 +#define REQ_WRITE_MEDIA 0x32 +#define REQ_READ_MEDIA 0x33 + +typedef void (*cmd_handler) (const struct usb_request *req); + +static cmd_handler get_handler(const char *cmd); + +static const char opti_name[] = "WorldCup Device"; +static const char resp_success[] = "success"; +/* Pay attention \":\" is in the end. Protocol specific. */ +static const char resp_failed[] = "failed:"; + +static struct usb_os_desc_ext_prop opti_ext_prop = { + .type = 1, /* NULL-terminated Unicode String (REG_SZ) */ + .name = "DeviceInterfaceGUID", + .data = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}", +}; + +/* 16 bytes of "Compatible ID" and "Subcompatible ID" */ +static char opti_cid[16] = {'W', 'I', 'N', 'U', 'S', 'B'}; +static struct usb_os_desc opti_os_desc = { + .ext_compat_id = opti_cid, +}; + +static struct usb_os_desc_table opti_os_desc_table = { + .os_desc = &opti_os_desc, +}; + +static struct fastboot_funcs *opti_func; + +static struct usb_string opti_string_defs[] = { + [0].s = opti_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_opti = { + .language = 0x0409, /* en-us */ + .strings = opti_string_defs, +}; + +static struct usb_gadget_strings *opti_strings[] = { + &stringtab_opti, + NULL, +}; + +/* This function is oriented to send string response of desired size ending by zero bytes */ +static int opti_tx_write(const char *buffer, size_t buffer_size) +{ + int ret; + struct usb_request *in_req = opti_func->in_req; + size_t resp_len = strnlen(buffer, MAX_RESPONSE_LEN); + + if (!buffer_size || buffer_size > MAX_RESPONSE_LEN) + buffer_size = MAX_RESPONSE_LEN; + + resp_len = min_t(size_t, resp_len, buffer_size); + memset(in_req->buf, 0, MAX_RESPONSE_LEN); + memcpy(in_req->buf, buffer, resp_len); + + in_req->length = buffer_size; + + ret = usb_ep_queue(opti_func->in_ep, in_req, 0); + if (ret) + OPTI_ERR("Response with size %zu failed: %d", buffer_size, ret); + + return ret; +} + +static void opti_rx_handler_command(struct usb_ep *ep, struct usb_request *req); + +static int opti_bind(struct usb_configuration *c, struct usb_function *f) +{ + return fastboot_common_bind(c, f, &opti_os_desc_table, + &opti_os_desc, (struct usb_string **)&opti_string_defs, + &opti_ext_prop); +} + +static void opti_unbind(struct usb_configuration *c, struct usb_function *f) +{ + fastboot_common_unbind(c, f, &opti_os_desc); + memset(opti_func, 0, sizeof(*opti_func)); +} + +static void opti_disable(struct usb_function *f) +{ + fastboot_common_disable(f); +} + +static int opti_set_alt(struct usb_function *f, + unsigned int interface, unsigned int alt) +{ + int ret; + struct usb_configuration *c; + + if (interface) + return 0; + + ret = fastboot_common_set_alt(f, interface, alt, opti_rx_handler_command); + + c = f->config; + + if (!c->interface[0]) + return ret; + + for (size_t i = 1; i < MAX_CONFIG_INTERFACES; i++) { + if (!c->interface[i]) + c->interface[i] = c->interface[0]; + } + + return ret; +} + +void complete_tpl_cmd(struct usb_ep *ep, struct usb_request *req) +{ + cmd_handler cb; + + ((char *)req->buf)[req->actual] = '\0'; + cb = get_handler(req->buf); + + if (!cb) { + OPTI_ERR("Unknown command: %.*s", MAX_CMD_LEN, (char *)req->buf); + return; + } + + cb(req); +} + +void complete_write_media(struct usb_ep *ep, struct usb_request *req) +{ + struct optimus_chunk in; + struct optimus_img *img; + + in = *((struct optimus_chunk *)req->buf); + + img = optimus_get_img(); + + img->cur_chunk_meta = in; + + if (optimus_chunk_alloc_buf(img->fsize)) + return; + + opti_func->out_req->length = min_t(u32, in.data_length, EP_BUFFER_SIZE); + opti_func->out_req->complete = opti_rx_handler_command; + + OPTI_MSG_ERASE("Image %s received:%*zd%%%c", img->name, + 4, img->dsize * 100 / img->fsize, SPACE_IN_HEX); + + usb_ep_queue(opti_func->out_ep, opti_func->out_req, 0); +} + +static int optimus_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value; + + u8 resp_buf[MAX_RESPONSE_LEN]; + u8 host_identity[] = {0, 7, 0, 16, 0, 0, 0, 0}; + + switch (ctrl->bRequest) { + case REQ_IDENTIFY_HOST: + value = min_t(u16, len, MAX_RESPONSE_LEN); + memcpy(req->buf, host_identity, value); + break; + case REQ_TPL_CMD: + value = len; + req->complete = complete_tpl_cmd; + break; + case REQ_TPL_STAT: + value = (!len) ? 0 : len - 1; + memset(req->buf, 0, len); + memcpy(req->buf, resp_success, sizeof(resp_success) - 1); + break; + case REQ_BULKCMD: + value = len; + req->complete = complete_tpl_cmd; + break; + case REQ_READ_MEDIA: + value = len; + memset(resp_buf, 0, sizeof(resp_buf)); + opti_tx_write(resp_buf, w_value); + break; + case REQ_WRITE_MEDIA: + /* This is main request to receive image chunks of data */ + value = len; + req->complete = complete_write_media; + break; + case REQ_WRITE_MEM: + case REQ_READ_MEM: + case REQ_WR_LARGE_MEM: + case REQ_RUN_IN_ADDR: + value = len; + memset(req->buf, 0, len); + break; + default: + value = 0; + OPTI_ERR("\nUnknown request: bRequest %#x", ctrl->bRequest); + } + + req->length = value; + req->zero = 0; + + value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL); + if (value < 0) { + OPTI_ERR("Response \"ep0\" failed (%d)", value); + req->status = 0; + } + + return value; +} + +int opti_add(struct usb_configuration *c) +{ + struct fastboot_funcs *f_opti = opti_func; + int status; + + OPTI_MSG("Start usb \"optimus\""); + if (!f_opti) { + f_opti = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_opti)); + if (!f_opti) + return -ENOMEM; + + opti_func = f_opti; + memset(f_opti, 0, sizeof(*f_opti)); + } + + g_dnl_set_product("DNL"); + f_opti->usb_function.name = "f_opti"; + f_opti->usb_function.bind = opti_bind; + f_opti->usb_function.unbind = opti_unbind; + f_opti->usb_function.set_alt = opti_set_alt; + f_opti->usb_function.disable = opti_disable; + f_opti->usb_function.strings = opti_strings; + f_opti->usb_function.setup = optimus_setup; + + status = usb_add_function(c, &f_opti->usb_function); + if (status) { + free(f_opti); + opti_func = NULL; + } + + return status; +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_optimus, opti_add); + +static void cb_download(const struct usb_request *req) +{ + char *cmd[10]; + char *token; + u16 i; + unsigned int img_size; + const char delim[] = {SPACE_IN_HEX, '\x00'}; + struct optimus_img *img; + + /* Finalizing received buffer by \x00 */ + token = req->buf; + token[req->actual] = '\x00'; + + i = 0; + token = strtok(req->buf, delim); + while (token && i < ARRAY_SIZE(cmd)) { + cmd[i] = token; + token = strtok(NULL, delim); + i++; + } + + img = optimus_get_img(); + + if (i >= 1 && !strncmp(cmd[1], CMD_SUB_GETSTATUS, sizeof(CMD_SUB_GETSTATUS) - 1)) { + if (img->dsize == img->fsize) { + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); + OPTI_MSG("Partition \'%s\' is fully received", img->name); + } else { + opti_tx_write(resp_failed, img->cur_chunk_meta.ack_len); + OPTI_MSG("Partition \'%s\' receiving failed", img->name); + } + } else if (i >= 4 && !strcmp(cmd[1], CMD_SUB_STORE)) { + img_size = simple_strtoul(cmd[4], NULL, 10); + /* Init first image chunk by full image name and full image size. */ + optimus_chunk_init(cmd[2], img_size); + } +} + +static void cb_lowpower(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_bl_is_old(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_erase_bl(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_reset(const struct usb_request *req) +{ + meson_sm_set_usb_boot_mode(FORCE_USB_BOOT); + meson_sm_reboot(REBOOT_REASON_NORMAL); +} + +static void cb_upload(const struct usb_request *req) +{ + const char **param_list; + size_t resp_len = 0; + char *req_buf_ch; + + req_buf_ch = (char *)req->buf; + req_buf_ch[MAX_RESPONSE_LEN - 1] = '\0'; + param_list = str_to_list(req_buf_ch); + + if (param_list) { + size_t param_count = 0; + + while (param_list[param_count]) + param_count++; + + if (param_count > 4) + resp_len = simple_strtoul(param_list[4], NULL, 0); + } else { + resp_len = 0x4; + } + + str_free_list(param_list); + + opti_tx_write(resp_success, resp_len); +} + +static void cb_verify(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static bool opti_must_skip_block(struct mtd_info *mtd, off_t offset) +{ + if (mtd_block_isbad(mtd, offset)) + return true; + + if (mtd_block_isreserved(mtd, offset)) + return true; + + return false; +} + +static void cb_disk_initial(const struct usb_request *req) +{ + struct optimus_img *img; + struct mtd_info *mtd; + off_t offset; + + mtd = get_mtd_device_nm("bootloader"); + if (!mtd || !mtd->parent) + return; + + mtd = mtd->parent; + + for (offset = 0; offset < mtd->size; offset += mtd->erasesize) { + struct erase_info erase_op = {}; + int ret; + + if (opti_must_skip_block(mtd, offset)) + continue; + + erase_op.mtd = mtd; + erase_op.addr = offset; + erase_op.len = mtd->erasesize; + + ret = mtd_erase(mtd, &erase_op); + if (ret) { + OPTI_ERR("Can't erase block at %lx\n", + offset); + return; + } + } + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_save_setting(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_burn_complete(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_echo_1234(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_setenv(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + env_set("firstboot", "1"); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_save(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + env_save(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_rpmb_reset(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_amlmmc(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void cb_nand(const struct usb_request *req) +{ + struct optimus_img *img; + + img = optimus_get_img(); + opti_tx_write(resp_success, img->cur_chunk_meta.ack_len); +} + +static void opti_rx_handler_command(struct usb_ep *ep, struct usb_request *req) +{ + enum optimus_chunk_state ret = optimus_chunk_process(req->buf, req->length); + struct optimus_img *img; + + img = optimus_get_img(); + + switch (ret) { + case OPTI_CHUNK_STATE_COMPLETE: + req->complete = NULL; + opti_tx_write("OK!!", img->cur_chunk_meta.ack_len); + break; + case OPTI_CHUNK_STATE_IN_PROGRESS: + req->length = img->req_length; + req->complete = opti_rx_handler_command; + if (!usb_ep_queue(ep, req, 0)) + break; + case OPTI_CHUNK_STATE_ERROR: + req->complete = NULL; + opti_tx_write("FALSE!!", img->cur_chunk_meta.ack_len); + break; + case OPTI_CHUNK_STATE_NEED_INIT: + break; + } +} + +struct cmd_dispatch_info { + const char *cmd; + cmd_handler cb; +}; + +static const struct cmd_dispatch_info cmd_dispatch_info[] = { + { + .cmd = CMD_RESET, + .cb = cb_reset, + }, + { + .cmd = CMD_DOWNLOAD, + .cb = cb_download, + }, + { + .cmd = CMD_LOWPOWER, + .cb = cb_lowpower, + }, + { + .cmd = CMD_BL_IS_OLD, + .cb = cb_bl_is_old, + }, + { + .cmd = CMD_ERASE_BL, + .cb = cb_erase_bl, + }, + { + .cmd = CMD_UPLOAD, + .cb = cb_upload, + }, + { + .cmd = CMD_VERIFY, + .cb = cb_verify, + }, + { + .cmd = CMD_DISK_INITIAL, + .cb = cb_disk_initial, + }, + { + .cmd = CMD_SAVE_SET, + .cb = cb_save_setting, + }, + { + .cmd = CMD_BURN_COMPL, + .cb = cb_burn_complete, + }, + { + .cmd = CMD_ECHO_1234, + .cb = cb_echo_1234, + }, + { + .cmd = CMD_SETENV, + .cb = cb_setenv, + }, + { + .cmd = CMD_SAVE, + .cb = cb_save, + }, + { + .cmd = CMD_RPMB_RESET, + .cb = cb_rpmb_reset, + }, + { + .cmd = CMD_AMLMMC, + .cb = cb_amlmmc, + }, + { + .cmd = CMD_NAND, + .cb = cb_nand, + }, +}; + +static cmd_handler get_handler(const char *cmd) +{ + u32 i; + const char *str_ptr; + + if (!cmd) + return NULL; + + str_ptr = skip_spaces(cmd); + + for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) { + int ret; + + ret = strncmp(str_ptr, cmd_dispatch_info[i].cmd, strlen(cmd_dispatch_info[i].cmd)); + if (!ret) { + OPTI_MSG("In cmd: '%s'", cmd_dispatch_info[i].cmd); + return cmd_dispatch_info[i].cb; + } + } + + return NULL; +} diff --git a/drivers/usb/gadget/amlogic/optimus/optimus_download.c b/drivers/usb/gadget/amlogic/optimus/optimus_download.c new file mode 100644 index 00000000000..c78e39ed23e --- /dev/null +++ b/drivers/usb/gadget/amlogic/optimus/optimus_download.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 SaluteDevices, Inc. + * Author: Vladimir Mitrofanov + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f_fastboot_common.h" +#include "optimus_download.h" + +static struct optimus_img flash_img = { .ptr = NULL }; + +struct optimus_img *optimus_get_img(void) +{ + return &flash_img; +} + +static int optimus_write_normal_image(u32 data_sz, u8 *data) +{ + struct optimus_img *img; + size_t written_length; + size_t length; + struct mtd_info *mtd; + int ret; + + if (!data_sz) + return 0; + + img = optimus_get_img(); + + mtd = get_mtd_device_nm(img->name); + + if (IS_ERR_OR_NULL(mtd)) { + OPTI_ERR("Failed to get mtd device \"%s\" ret: %ld", img->name, PTR_ERR(mtd)); + return PTR_ERR(mtd); + } + + /* Need use intermediate storage to write conversion of types pointers */ + length = data_sz; + + written_length = 0; + ret = nand_write_skip_bad(mtd, img->media_offset, &length, + &written_length, mtd->size, data, 0); + img->media_offset += written_length; + + return ret; +} + +void optimus_chunk_init(const char *name, unsigned int size) +{ + struct optimus_img *img; + + img = optimus_get_img(); + + img->fsize = size; + + memset(img->name, 0, sizeof(img->name)); + strncpy(img->name, name, sizeof(img->name) - 1); + + img->dsize = 0; + img->media_offset = 0; +} + +int optimus_chunk_alloc_buf(unsigned int size) +{ + struct optimus_img *img; + size_t full_size; + + img = optimus_get_img(); + + img->fsize = size; + img->cur_chunk_data_len = 0; + + if (!strcmp(img->name, BOOT_LOADER)) { + img->type = OPTI_IMG_TYPE_BOOTLOADER; + full_size = img->fsize; + } else { + img->type = OPTI_IMG_TYPE_NORMAL; + full_size = img->cur_chunk_meta.data_length; + } + + if (!img->ptr) + img->ptr = (u8 *)malloc(full_size); + + if (!img->ptr) { + OPTI_ERR("No memory for data chunk"); + return -ENOMEM; + } + + return 0; +} + +static void optimus_chunk_free(void) +{ + struct optimus_img *img; + + img = optimus_get_img(); + + free(img->ptr); + img->ptr = NULL; +} + +static unsigned int optimus_csum_special(const void *p_buf, const unsigned int size) +{ + const unsigned int *data = (const unsigned int *)p_buf; + unsigned int word_len = size >> 2; + unsigned int rest = size & 0x3; + unsigned int sum = 0; + + while (word_len--) + sum += *data++; + + if (rest) + sum += (*data) & GENMASK(rest * 8, 0); + + return sum; +} + +enum optimus_chunk_state optimus_chunk_process(const void *data, unsigned int size) +{ + struct optimus_img *img; + int rw_ret; + u32 len_diff; + u32 check_sum; + u32 pos; + + img = optimus_get_img(); + + if (!img->ptr) + return OPTI_CHUNK_STATE_NEED_INIT; + + if (img->type == OPTI_IMG_TYPE_BOOTLOADER) + pos = img->dsize; + else + pos = img->cur_chunk_data_len; + + if (img->ptr + pos + size <= img->ptr + img->fsize) + memcpy(img->ptr + pos, data, size); + else + return OPTI_CHUNK_STATE_ERROR; + + img->dsize += size; + img->cur_chunk_data_len += size; + if (img->cur_chunk_data_len < img->cur_chunk_meta.data_length) { + len_diff = img->cur_chunk_meta.data_length - img->cur_chunk_data_len; + img->req_length = min_t(u32, len_diff, EP_BUFFER_SIZE); + return OPTI_CHUNK_STATE_IN_PROGRESS; + } + + /* All chunk data received. */ + if (img->type == OPTI_IMG_TYPE_BOOTLOADER) + pos = img->dsize - img->cur_chunk_data_len; + else + pos = 0; + + check_sum = optimus_csum_special(img->ptr + pos, img->cur_chunk_data_len); + if (check_sum != img->cur_chunk_meta.chksum) + return OPTI_CHUNK_STATE_ERROR; + + if (img->type == OPTI_IMG_TYPE_BOOTLOADER && size) { + /* Not full bootloader image. Chunk is only part. */ + if (img->dsize < img->fsize) + return OPTI_CHUNK_STATE_COMPLETE; + + /* We write bootloader after getting full image */ + rw_ret = meson_bootloader_write(img->ptr, img->fsize); + optimus_chunk_free(); + if (rw_ret) + return OPTI_CHUNK_STATE_ERROR; + } else { + rw_ret = optimus_write_normal_image((u32)img->cur_chunk_data_len, img->ptr); + optimus_chunk_free(); + if (rw_ret) + return OPTI_CHUNK_STATE_ERROR; + } + + return OPTI_CHUNK_STATE_COMPLETE; +} diff --git a/drivers/usb/gadget/amlogic/optimus/optimus_download.h b/drivers/usb/gadget/amlogic/optimus/optimus_download.h new file mode 100644 index 00000000000..3a8682df045 --- /dev/null +++ b/drivers/usb/gadget/amlogic/optimus/optimus_download.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2024 SaluteDevices, Inc. All rights reserved. + * Author: Vladimir Mitrofanov + */ + +#ifndef __OPTIMUS_DOWNLOAD_H__ +#define __OPTIMUS_DOWNLOAD_H__ + +#define OPTI_PREF "[OPTI][MSG] " +#define ERASE_SEQ "\x1B[2K\r" +#define __OPTI_MSG(prefix, fmt, ...) printf(prefix fmt, ##__VA_ARGS__) + +#define OPTI_MSG(fmt, ...) __OPTI_MSG("\n" OPTI_PREF, fmt, ##__VA_ARGS__) +#define OPTI_MSG_ERASE(fmt, ...) __OPTI_MSG(ERASE_SEQ "" OPTI_PREF, fmt, ##__VA_ARGS__) +#define OPTI_ERR(fmt, ...) pr_err("\n[OPTI][ERR]: " fmt, ##__VA_ARGS__) + +enum optimus_img_type { + OPTI_IMG_TYPE_BOOTLOADER, + OPTI_IMG_TYPE_NORMAL, +}; + +enum optimus_chunk_state { + OPTI_CHUNK_STATE_COMPLETE, + OPTI_CHUNK_STATE_IN_PROGRESS, + OPTI_CHUNK_STATE_NEED_INIT, + OPTI_CHUNK_STATE_ERROR, +}; + +struct optimus_chunk { + u32 retry_times; /* Unused in this version. */ + u32 data_length; /* Length of data in this chunk. */ + u32 seq_num; /* Unused in this version. */ + u32 chksum; /* Checksum (see 'optimus_csum_special()'. */ + u16 chksum_alg; /* Unused in this version. */ + u16 ack_len; /* ACK packet length, expected by sender. */ +} __packed; + +struct optimus_img { + u8 *ptr; /* Data buffer. */ + size_t dsize; /* Downloaded size. */ + size_t fsize; /* Image full size. */ + char name[256]; /* Current partition name. */ + enum optimus_img_type type; /* Current partition type. */ + + struct optimus_chunk cur_chunk_meta; /* Current chunk. */ + u32 cur_chunk_data_len; /* Downloaded size for current chunk. */ + u32 req_length; /* Rest to download, limited by EP buffer. */ + loff_t media_offset; /* Current offset to write this image. */ +}; + +/** + * optimus_get_img() - Get current Optimus image (context). + * + * @return: pointer to Optimus image (context). + */ +struct optimus_img *optimus_get_img(void); + +/** + * optimus_chunk_init() - Initialize "chunk" to receive data. + * + * @name: "chunk" name. + * @size: full size of data to receive. + */ +void optimus_chunk_init(const char *name, unsigned int size); + +/** + * optimus_chunk_alloc_buf() - Alloc buffer inside "chunk" for data. + * + * @size: full size of data. + * + * @return: 0 on success, -errno otherwise. + */ +int optimus_chunk_alloc_buf(unsigned int size); + +/** + * optimus_chunk_process() - Pass new portion of data to "chunk". + * + * @data: new portion of data. + * @size: size of data. + * + * @return: new state of "chunk". + */ +enum optimus_chunk_state optimus_chunk_process(const void *data, unsigned int size); + +#endif /* __OPTIMUS_DOWNLOAD_H__ */ From patchwork Wed Mar 19 20:20:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arseniy Krasnov X-Patchwork-Id: 2062987 X-Patchwork-Delegate: narmstrong@baylibre.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.a=rsa-sha256 header.s=mail header.b=I67JCPKU; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org) Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (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 4ZJ0WC6dFvz1xrn for ; Thu, 20 Mar 2025 07:22:31 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id A7EC881DED; Wed, 19 Mar 2025 21:21:17 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=salutedevices.com header.i=@salutedevices.com header.b="I67JCPKU"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 1A83C8129F; Wed, 19 Mar 2025 21:21:10 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_BLOCKED, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Received: from mx1.sberdevices.ru (mx2.sberdevices.ru [45.89.224.132]) (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 F0D3881CAF for ; Wed, 19 Mar 2025 21:21:01 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=salutedevices.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=avkrasnov@salutedevices.com Received: from p-infra-ksmg-sc-msk02.sberdevices.ru (localhost [127.0.0.1]) by mx1.sberdevices.ru (Postfix) with ESMTP id 8430B120015; Wed, 19 Mar 2025 23:21:01 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.sberdevices.ru 8430B120015 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=salutedevices.com; s=mail; t=1742415661; bh=f4w9RmMeHgiQaN14upAA5pVyCE17/cNa1SQ+rwvBpUA=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=I67JCPKU+Tqvbfp95+phKIqsbjUD8HBBTFEA/mA8goe7/LX6bH6dVkN0tvNFvtRWX MfQB8cl8FiCkLVv+geGdkxZSX5lPp0MYmwe53+rZ2najd1uHUKnmNuXUtwojbT0j9A 50ZTPWr9LHdTLF6fEg7ahn7Em8XjC+K+0hfAgJPEXn7O0OlzBen9JfaN2KNx5H5ykJ R+6fc+J8Km8MryI7K/qgxDFkxkIr4tHsH5iC8aabbWxTcihjlmHUAdB3q1cpKV4lVx pzIcMRI2+SQgHCFs8QEQrezcRh8kaknv++YofaHJf+06gsOaCV01lA6QWRkDN77RPl 8mAxm+7AyeXGg== Received: from smtp.sberdevices.ru (p-exch-cas-a-m1.sberdevices.ru [172.24.201.216]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.sberdevices.ru (Postfix) with ESMTPS; Wed, 19 Mar 2025 23:21:01 +0300 (MSK) From: Arseniy Krasnov To: Tom Rini , Neil Armstrong , Dario Binacchi , Michael Trimarchi , Lukasz Majewski , Mattijs Korpershoek , Marek Vasut , Viacheslav Bocharov , Sumit Garg , Simon Glass , Ilias Apalodimas , Jerome Forissier , Heinrich Schuchardt , Vitaliy Vasylskyy CC: , , , , , Vladimir Mitrofanov Subject: [PATCH v1 11/11] arch: arm: axg: Optimus protocol support Date: Wed, 19 Mar 2025 23:20:45 +0300 Message-ID: <20250319202045.1407-12-avkrasnov@salutedevices.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20250319202045.1407-1-avkrasnov@salutedevices.com> References: <20250319202045.1407-1-avkrasnov@salutedevices.com> MIME-Version: 1.0 X-Originating-IP: [172.28.65.111] X-ClientProxiedBy: p-exch-cas-a-m2.sberdevices.ru (172.24.201.210) To p-exch-cas-a-m1.sberdevices.ru (172.24.201.216) X-KSMG-Rule-ID: 1 X-KSMG-Message-Action: clean X-KSMG-AntiSpam-Lua-Profiles: 191951 [Mar 19 2025] X-KSMG-AntiSpam-Version: 6.1.1.11 X-KSMG-AntiSpam-Envelope-From: avkrasnov@salutedevices.com X-KSMG-AntiSpam-Rate: 0 X-KSMG-AntiSpam-Status: not_detected X-KSMG-AntiSpam-Method: none X-KSMG-AntiSpam-Auth: dkim=none X-KSMG-AntiSpam-Info: LuaCore: 51 0.3.51 68896fb0083a027476849bf400a331a2d5d94398, {Tracking_from_domain_doesnt_match_to}, 127.0.0.199:7.1.2; salutedevices.com:7.1.1; smtp.sberdevices.ru:5.0.1,7.1.1; d41d8cd98f00b204e9800998ecf8427e.com:7.1.1, FromAlignment: s X-MS-Exchange-Organization-SCL: -1 X-KSMG-AntiSpam-Interceptor-Info: scan successful X-KSMG-AntiPhishing: Clean X-KSMG-LinksScanning: Clean X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.0.1.6960, bases: 2025/03/19 19:45:00 #27804052 X-KSMG-AntiVirus-Status: Clean, skipped 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: Vladimir Mitrofanov During Optimus protocol operations, BootROM may request to enter Optimus mode after boot. So to support this, let's check for values in sticky registers after boot (such registers could be updated earlier by boot ROM, telling U-boot to enter Optimus mode). Signed-off-by: Vladimir Mitrofanov Signed-off-by: Arseniy Krasnov --- arch/arm/include/asm/arch-meson/axg.h | 12 ++++++++++++ arch/arm/mach-meson/board-axg.c | 12 ++++++++++++ arch/arm/mach-meson/board-common.c | 15 +++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/arch/arm/include/asm/arch-meson/axg.h b/arch/arm/include/asm/arch-meson/axg.h index 12042de9359..e99cd3aa780 100644 --- a/arch/arm/include/asm/arch-meson/axg.h +++ b/arch/arm/include/asm/arch-meson/axg.h @@ -23,6 +23,7 @@ #define AXG_AO_SEC_GP_CFG3 AXG_AO_ADDR(0x93) #define AXG_AO_SEC_GP_CFG4 AXG_AO_ADDR(0x94) #define AXG_AO_SEC_GP_CFG5 AXG_AO_ADDR(0x95) +#define AXG_AO_SEC_GP_CFG7 AXG_AO_ADDR(0x97) #define AXG_AO_BOOT_DEVICE 0xF #define AXG_AO_MEM_SIZE_MASK 0xFFFF0000 @@ -31,4 +32,15 @@ #define AXG_AO_BL31_RSVMEM_SIZE_SHIFT 16 #define AXG_AO_BL32_RSVMEM_SIZE_MASK 0xFFFF +#if CONFIG_IS_ENABLED(CMD_OPTIMUS) +/** + * is_tpl_loaded_from_usb - Checks, that this instance of U-boot was + * loaded from USB during firmware update + * process. + * + * returns: true, if so. + */ +int is_tpl_loaded_from_usb(void); +#endif + #endif /* __AXG_H__ */ diff --git a/arch/arm/mach-meson/board-axg.c b/arch/arm/mach-meson/board-axg.c index 6535539184c..13eb66dfc21 100644 --- a/arch/arm/mach-meson/board-axg.c +++ b/arch/arm/mach-meson/board-axg.c @@ -4,12 +4,14 @@ * (C) Copyright 2018 Neil Armstrong */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -62,6 +64,16 @@ phys_size_t get_effective_memsize(void) >> AXG_AO_MEM_SIZE_SHIFT) * SZ_1M; } +#if CONFIG_IS_ENABLED(CMD_OPTIMUS) +int is_tpl_loaded_from_usb(void) +{ + const int boot_id = meson_get_boot_device(); + const unsigned int force_usb_boot = readl(AXG_AO_SEC_GP_CFG7); + + return (boot_id == BOOT_DEVICE_USB || (force_usb_boot & BIT(31))); +} +#endif + static struct mm_region axg_mem_map[] = { { .virt = 0x0UL, diff --git a/arch/arm/mach-meson/board-common.c b/arch/arm/mach-meson/board-common.c index 0624d557e40..639c7a64507 100644 --- a/arch/arm/mach-meson/board-common.c +++ b/arch/arm/mach-meson/board-common.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,18 @@ __weak int meson_board_late_init(void) return 0; } +static void meson_board_try_enter_optimus(void) +{ +#if (IS_ENABLED(CONFIG_CMD_OPTIMUS)) + if (!is_tpl_loaded_from_usb()) + return; + + meson_sm_set_usb_boot_mode(CLEAR_USB_BOOT); + + run_command("optimus", 0); +#endif +} + static void meson_board_try_enter_adnl(void) { #if (IS_ENABLED(CONFIG_CMD_ADNL)) @@ -162,6 +175,8 @@ int board_late_init(void) meson_board_try_enter_adnl(); + meson_board_try_enter_optimus(); + return meson_board_late_init(); }