From 80c1606d13c2f38cba10e803c0225c3e5a1bdaac Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:18 -0500 Subject: [PATCH 1/9] phy: samsung: Add Exynos USB DRD PHY driver Add DM driver for Exynos USB PHY controllers. For now it only supports Exynos850 SoC. Only UTMI+ (USB 2.0) PHY interface is implemented, as Exynos850 doesn't support USB 3.0. Only two clocks are used for this controller: - phy: bus clock, used for PHY registers access - ref: PHY reference clock (OSCCLK) Ported from Linux kernel: drivers/phy/samsung/phy-exynos5-usbdrd.c Signed-off-by: Sam Protsenko Reviewed-by: Mattijs Korpershoek Reviewed-by: Minkyu Kang Signed-off-by: Minkyu Kang --- MAINTAINERS | 1 + drivers/phy/Kconfig | 9 + drivers/phy/Makefile | 1 + drivers/phy/phy-exynos-usbdrd.c | 386 ++++++++++++++++++++++++++++++++ 4 files changed, 397 insertions(+) create mode 100644 drivers/phy/phy-exynos-usbdrd.c diff --git a/MAINTAINERS b/MAINTAINERS index f914fc54f54..d490b43c57f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -602,6 +602,7 @@ ARM SAMSUNG EXYNOS850 SOC M: Sam Protsenko S: Maintained F: drivers/clk/exynos/clk-exynos850.c +F: drivers/phy/phy-exynos-usbdrd.c F: drivers/pinctrl/exynos/pinctrl-exynos850.c ARM SAMSUNG SOC DRIVERS diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index d3fe90d939e..c297fa03ea7 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -259,6 +259,15 @@ config MT76X8_USB_PHY This PHY is found on MT76x8 devices supporting USB. +config PHY_EXYNOS_USBDRD + bool "Exynos SoC series USB DRD PHY driver" + depends on PHY && CLK + depends on ARCH_EXYNOS + select REGMAP + select SYSCON + help + Enable USB DRD PHY support for Exynos SoC series. + config PHY_MTK_TPHY bool "MediaTek T-PHY Driver" depends on PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index b4d01fc700d..98c1ef8683b 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o obj-$(CONFIG_MT7620_USB_PHY) += mt7620-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o +obj-$(CONFIG_PHY_EXYNOS_USBDRD) += phy-exynos-usbdrd.o obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o obj-$(CONFIG_PHY_NPCM_USB) += phy-npcm-usb.o obj-$(CONFIG_PHY_IMX8MQ_USB) += phy-imx8mq-usb.o diff --git a/drivers/phy/phy-exynos-usbdrd.c b/drivers/phy/phy-exynos-usbdrd.c new file mode 100644 index 00000000000..db5815ed184 --- /dev/null +++ b/drivers/phy/phy-exynos-usbdrd.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Linaro Ltd. + * Sam Protsenko + * + * Samsung Exynos SoC series USB DRD PHY driver. + * Based on Linux kernel PHY driver: drivers/phy/samsung/phy-exynos5-usbdrd.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Offset of PMU register controlling USB PHY output isolation */ +#define EXYNOS_USBDRD_PHY_CONTROL 0x0704 +#define EXYNOS_PHY_ENABLE BIT(0) + +/* Exynos USB PHY registers */ +#define EXYNOS5_FSEL_9MHZ6 0x0 +#define EXYNOS5_FSEL_10MHZ 0x1 +#define EXYNOS5_FSEL_12MHZ 0x2 +#define EXYNOS5_FSEL_19MHZ2 0x3 +#define EXYNOS5_FSEL_20MHZ 0x4 +#define EXYNOS5_FSEL_24MHZ 0x5 +#define EXYNOS5_FSEL_26MHZ 0x6 +#define EXYNOS5_FSEL_50MHZ 0x7 + +/* Exynos850: USB DRD PHY registers */ +#define EXYNOS850_DRD_LINKCTRL 0x04 +#define LINKCTRL_FORCE_QACT BIT(8) +#define LINKCTRL_BUS_FILTER_BYPASS GENMASK(7, 4) + +#define EXYNOS850_DRD_CLKRST 0x20 +#define CLKRST_LINK_SW_RST BIT(0) +#define CLKRST_PORT_RST BIT(1) +#define CLKRST_PHY_SW_RST BIT(3) + +#define EXYNOS850_DRD_SSPPLLCTL 0x30 +#define SSPPLLCTL_FSEL GENMASK(2, 0) + +#define EXYNOS850_DRD_UTMI 0x50 +#define UTMI_FORCE_SLEEP BIT(0) +#define UTMI_FORCE_SUSPEND BIT(1) +#define UTMI_DM_PULLDOWN BIT(2) +#define UTMI_DP_PULLDOWN BIT(3) +#define UTMI_FORCE_BVALID BIT(4) +#define UTMI_FORCE_VBUSVALID BIT(5) + +#define EXYNOS850_DRD_HSP 0x54 +#define HSP_COMMONONN BIT(8) +#define HSP_EN_UTMISUSPEND BIT(9) +#define HSP_VBUSVLDEXT BIT(12) +#define HSP_VBUSVLDEXTSEL BIT(13) +#define HSP_FSV_OUT_EN BIT(24) + +#define EXYNOS850_DRD_HSP_TEST 0x5c +#define HSP_TEST_SIDDQ BIT(24) + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +/** + * struct exynos_usbdrd_phy - driver data for Exynos USB PHY + * @reg_phy: USB PHY controller register memory base + * @clk: clock for register access + * @core_clk: core clock for phy (ref clock) + * @reg_pmu: regmap for PMU block + * @extrefclk: frequency select settings when using 'separate reference clocks' + */ +struct exynos_usbdrd_phy { + void __iomem *reg_phy; + struct clk *clk; + struct clk *core_clk; + struct regmap *reg_pmu; + u32 extrefclk; +}; + +static void exynos_usbdrd_phy_isol(struct regmap *reg_pmu, bool isolate) +{ + unsigned int val; + + if (!reg_pmu) + return; + + val = isolate ? 0 : EXYNOS_PHY_ENABLE; + regmap_update_bits(reg_pmu, EXYNOS_USBDRD_PHY_CONTROL, + EXYNOS_PHY_ENABLE, val); +} + +/* + * Convert the supplied clock rate to the value that can be written to the PHY + * register. + */ +static unsigned int exynos_rate_to_clk(unsigned long rate, u32 *reg) +{ + switch (rate) { + case 9600 * KHZ: + *reg = EXYNOS5_FSEL_9MHZ6; + break; + case 10 * MHZ: + *reg = EXYNOS5_FSEL_10MHZ; + break; + case 12 * MHZ: + *reg = EXYNOS5_FSEL_12MHZ; + break; + case 19200 * KHZ: + *reg = EXYNOS5_FSEL_19MHZ2; + break; + case 20 * MHZ: + *reg = EXYNOS5_FSEL_20MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS5_FSEL_24MHZ; + break; + case 26 * MHZ: + *reg = EXYNOS5_FSEL_26MHZ; + break; + case 50 * MHZ: + *reg = EXYNOS5_FSEL_50MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos850_usbdrd_utmi_init(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + /* + * Disable HWACG (hardware auto clock gating control). This will force + * QACTIVE signal in Q-Channel interface to HIGH level, to make sure + * the PHY clock is not gated by the hardware. + */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= LINKCTRL_FORCE_QACT; + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + /* Start PHY Reset (POR=high) */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg |= CLKRST_PHY_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + + /* Enable UTMI+ */ + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN | + UTMI_DM_PULLDOWN); + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + /* Set PHY clock and control HS PHY */ + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN; + writel(reg, regs_base + EXYNOS850_DRD_HSP); + + /* Set VBUS Valid and D+ pull-up control by VBUS pad usage */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= FIELD_PREP(LINKCTRL_BUS_FILTER_BYPASS, 0xf); + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID; + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL; + writel(reg, regs_base + EXYNOS850_DRD_HSP); + + reg = readl(regs_base + EXYNOS850_DRD_SSPPLLCTL); + reg &= ~SSPPLLCTL_FSEL; + switch (phy_drd->extrefclk) { + case EXYNOS5_FSEL_50MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 7); + break; + case EXYNOS5_FSEL_26MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 6); + break; + case EXYNOS5_FSEL_24MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 2); + break; + case EXYNOS5_FSEL_20MHZ: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 1); + break; + case EXYNOS5_FSEL_19MHZ2: + reg |= FIELD_PREP(SSPPLLCTL_FSEL, 0); + break; + default: + dev_warn(phy->dev, "unsupported ref clk: %#.2x\n", + phy_drd->extrefclk); + break; + } + writel(reg, regs_base + EXYNOS850_DRD_SSPPLLCTL); + + /* Power up PHY analog blocks */ + reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); + reg &= ~HSP_TEST_SIDDQ; + writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); + + /* Finish PHY reset (POR=low) */ + udelay(10); /* required before doing POR=low */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST); + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + udelay(75); /* required after POR=low for guaranteed PHY clock */ + + /* Disable single ended signal out */ + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg &= ~HSP_FSV_OUT_EN; + writel(reg, regs_base + EXYNOS850_DRD_HSP); +} + +static void exynos850_usbdrd_utmi_exit(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + /* Set PHY clock and control HS PHY */ + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN); + reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP; + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + /* Power down PHY analog blocks */ + reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); + reg |= HSP_TEST_SIDDQ; + writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); + + /* Link reset */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg |= CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + udelay(10); /* required before doing POR=low */ + reg &= ~CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); +} + +static int exynos_usbdrd_phy_init(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + int ret; + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + exynos850_usbdrd_utmi_init(phy); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos_usbdrd_phy_exit(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + int ret; + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + exynos850_usbdrd_utmi_exit(phy); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos_usbdrd_phy_power_on(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + int ret; + + dev_dbg(phy->dev, "Request to power_on usbdrd_phy phy\n"); + + ret = clk_prepare_enable(phy_drd->core_clk); + if (ret) + return ret; + + /* Power-on PHY */ + exynos_usbdrd_phy_isol(phy_drd->reg_pmu, false); + + return 0; +} + +static int exynos_usbdrd_phy_power_off(struct phy *phy) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(phy->dev); + + dev_dbg(phy->dev, "Request to power_off usbdrd_phy phy\n"); + + /* Power-off the PHY */ + exynos_usbdrd_phy_isol(phy_drd->reg_pmu, true); + + clk_disable_unprepare(phy_drd->core_clk); + + return 0; +} + +static int exynos_usbdrd_phy_init_clk(struct udevice *dev) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(dev); + unsigned long ref_rate; + int err; + + phy_drd->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(phy_drd->clk)) { + err = PTR_ERR(phy_drd->clk); + dev_err(dev, "Failed to get phy clock (err=%d)\n", err); + return err; + } + + phy_drd->core_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(phy_drd->core_clk)) { + err = PTR_ERR(phy_drd->core_clk); + dev_err(dev, "Failed to get ref clock (err=%d)\n", err); + return err; + } + + ref_rate = clk_get_rate(phy_drd->core_clk); + err = exynos_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (err) { + dev_err(dev, "Clock rate %lu not supported\n", ref_rate); + return err; + } + + return 0; +} + +static int exynos_usbdrd_phy_probe(struct udevice *dev) +{ + struct exynos_usbdrd_phy *phy_drd = dev_get_priv(dev); + int err; + + phy_drd->reg_phy = dev_read_addr_ptr(dev); + if (!phy_drd->reg_phy) + return -EINVAL; + + err = exynos_usbdrd_phy_init_clk(dev); + if (err) + return err; + + phy_drd->reg_pmu = syscon_regmap_lookup_by_phandle(dev, + "samsung,pmu-syscon"); + if (IS_ERR(phy_drd->reg_pmu)) { + err = PTR_ERR(phy_drd->reg_pmu); + dev_err(dev, "Failed to lookup PMU regmap\n"); + return err; + } + + return 0; +} + +static const struct phy_ops exynos_usbdrd_phy_ops = { + .init = exynos_usbdrd_phy_init, + .exit = exynos_usbdrd_phy_exit, + .power_on = exynos_usbdrd_phy_power_on, + .power_off = exynos_usbdrd_phy_power_off, +}; + +static const struct udevice_id exynos_usbdrd_phy_of_match[] = { + { + .compatible = "samsung,exynos850-usbdrd-phy", + }, + { } +}; + +U_BOOT_DRIVER(exynos_usbdrd_phy) = { + .name = "exynos-usbdrd-phy", + .id = UCLASS_PHY, + .of_match = exynos_usbdrd_phy_of_match, + .probe = exynos_usbdrd_phy_probe, + .ops = &exynos_usbdrd_phy_ops, + .priv_auto = sizeof(struct exynos_usbdrd_phy), +}; From ba713dd7d4d8da282562becfefcd974d8344c6f5 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:19 -0500 Subject: [PATCH 2/9] usb: dwc3-generic: Add Exynos850 support The only thing needed from DWC3 glue layer for Exynos850 is to enable USB clocks. The generic glue layer driver already does that. Add Exynos850 dwc3 compatible string to enable support for this chip. Signed-off-by: Sam Protsenko Reviewed-by: Mattijs Korpershoek Signed-off-by: Minkyu Kang --- drivers/usb/dwc3/dwc3-generic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index 21452ad1569..3cda2b74b7e 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -704,6 +704,7 @@ static const struct udevice_id dwc3_glue_ids[] = { { .compatible = "fsl,imx8mp-dwc3", .data = (ulong)&imx8mp_ops }, { .compatible = "fsl,imx8mq-dwc3" }, { .compatible = "intel,tangier-dwc3" }, + { .compatible = "samsung,exynos850-dwusb3" }, { } }; From 34a6f585f0a25f4285122fcade38c59e64c5fd70 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:20 -0500 Subject: [PATCH 3/9] board: samsung: e850-96: Setup serial# env var Setup "serial#" environment variable from the chip ID. The chip ID is read from Exynos850 SoC OTP (One Time Programmable) memory, which acts like an EEPROM and contains unique SoC ID. This "serial#" variable is further used for "fastboot devices" serial number, etc. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.c | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c index 3bbd95201b5..addfe046097 100644 --- a/board/samsung/e850-96/e850-96.c +++ b/board/samsung/e850-96/e850-96.c @@ -4,9 +4,17 @@ * Author: Sam Protsenko */ +#include #include +#include +#include #include "fw.h" +/* OTP Controller base address and register offsets */ +#define EXYNOS850_OTP_BASE 0x10000000 +#define OTP_CHIPID0 0x4 +#define OTP_CHIPID1 0x8 + int dram_init(void) { return fdtdec_setup_mem_size_base(); @@ -17,6 +25,33 @@ int dram_init_banksize(void) return fdtdec_setup_memory_banksize(); } +/* Read the unique SoC ID from OTP registers */ +static u64 get_chip_id(void) +{ + void __iomem *otp_base; + u64 val; + + otp_base = map_sysmem(EXYNOS850_OTP_BASE, 12); + val = readl(otp_base + OTP_CHIPID0); + val |= (u64)readl(otp_base + OTP_CHIPID1) << 32UL; + unmap_sysmem(otp_base); + + return val; +} + +static void setup_serial(void) +{ + char serial_str[17] = { 0 }; + u64 serial_num; + + if (env_get("serial#")) + return; + + serial_num = get_chip_id(); + snprintf(serial_str, sizeof(serial_str), "%016llx", serial_num); + env_set("serial#", serial_str); +} + int board_init(void) { return 0; @@ -26,6 +61,8 @@ int board_late_init(void) { int err; + setup_serial(); + /* * Do this in board_late_init() to make sure MMC is not probed before * efi_init_early(). From c0a8b48b71a7907fe16c278211a3f80b16f56731 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:21 -0500 Subject: [PATCH 4/9] board: samsung: e850-96: Add dfu_alt_info Add 'dfu_alt_info' environment variable which contains: - Linux eMMC partitions ('esp' and 'rootfs') - eMMC Boot Partition A layout, where all the firmware reside It makes it possible to update the bootloader (U-Boot). All sizes in 'dfu_alt_info' are given in 512B blocks (LBA). eMMC size is 58.2 GiB. The eMMC Boot Partition A (mmc0boot0) layout looks like this: boot0 partition (4 MiB) 0x0 +----------------------------------+ | fwbl1 (12 KiB) | 0x18 +----------------------------------+ | epbl (76 KiB) | 0xb0 +----------------------------------+ | bl2 (256 KiB) | 0x2b0 +----------------------------------+ | dram_train (16 KiB) | 0x2d0 +----------------------------------+ | ect_test (50 KiB) | 0x334 +----------------------------------+ | acpm_test (130 KiB) | 0x438 +----------------------------------+ | bootloader (2 MiB) | 0x1438 +----------------------------------+ | el3_mon (256 KiB) | 0x1638 +----------------------------------+ where U-Boot should be flashed into 'bootloader' partition. So U-Boot binary size should be 2 MiB or less. The whole boot0 partition is 4 MiB, but only 2.8 MiB is currently used. With this change, the U-Boot binary can be updated on eMMC like this: => dfu 0 mmc 0 $ dfu-util -D u-boot.bin -a bootloader Looking at E850-96 booting diagram at [1,2], it's easy to see how these binaries are being executed in the same order they are placed in mmc0boot0 area. E.g. fwbl1 is definitely BL1 (software part of Boot ROM). So it's obvious the ROM code just reads the binary from eMMC at 0x0 offset into RAM (SRAM?) and executes it. All mentioned images can be found at [3], as stated in E850-96 U-Boot documentation. 'dram_train', 'ect_test' and 'acpm_test' areas should be ignored -- they are not flashed with real images. [1] doc/board/samsung/e850-96.rst [2] https://docs.u-boot.org/en/latest/board/samsung/e850-96.html [3] https://gitlab.com/Linaro/96boards/e850-96/images/-/tree/master/images Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.env | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/board/samsung/e850-96/e850-96.env b/board/samsung/e850-96/e850-96.env index 5ac76bcef02..c76ea6acdc3 100644 --- a/board/samsung/e850-96/e850-96.env +++ b/board/samsung/e850-96/e850-96.env @@ -7,5 +7,18 @@ pxefile_addr_r=0x8c200000 ramdisk_addr_r=0x8c300000 fdtfile=CONFIG_DEFAULT_FDT_FILE +dfu_alt_info= + rawemmc raw 0 0x747c000 mmcpart 1; + esp part 0 1; + rootfs part 0 2; + fwbl1 raw 0x0 0x18 mmcpart 1; + epbl raw 0x18 0x98 mmcpart 1; + bl2 raw 0xb0 0x200 mmcpart 1; + dram_train raw 0x2b0 0x20 mmcpart 1; + ect_test raw 0x2d0 0x64 mmcpart 1; + acpm_test raw 0x334 0x104 mmcpart 1; + bootloader raw 0x438 0x1000 mmcpart 1; + el3_mon raw 0x1438 0x200 mmcpart 1 + partitions=name=esp,start=512K,size=128M,bootable,type=system; partitions+=name=rootfs,size=-,bootable,type=linux From d15a7b045993167598fa70541ec364ace0a051a9 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:22 -0500 Subject: [PATCH 5/9] board: samsung: e850-96: Add Android partitions Matches downstream Android-Q partition table created by flashing the modified gpt.img [1], with added ESP partition (EFI System Partition). It's an A/B slotted Android partition table, so it's possible to boot Android from this table using Android GBL EFI app. Tested using AOSP/main images for E850-96 with booting via GBL app. [1] https://gitlab.com/Linaro/96boards/e850-96/tools/gpt/-/blob/master/gpt_layout_uboot_q_ab Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.env | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/board/samsung/e850-96/e850-96.env b/board/samsung/e850-96/e850-96.env index c76ea6acdc3..aed7a71046d 100644 --- a/board/samsung/e850-96/e850-96.env +++ b/board/samsung/e850-96/e850-96.env @@ -22,3 +22,34 @@ dfu_alt_info= partitions=name=esp,start=512K,size=128M,bootable,type=system; partitions+=name=rootfs,size=-,bootable,type=linux + +partitions_android=name=esp,start=512K,size=128M,bootable,type=system; +partitions_android+=name=efs,size=20M,uuid=${uuid_gpt_efs}; +partitions_android+=name=env,size=16K,uuid=${uuid_gpt_env}; +partitions_android+=name=kernel,size=30M,uuid=${uuid_gpt_kernel}; +partitions_android+=name=ramdisk,size=26M,uuid=${uuid_gpt_ramdisk}; +partitions_android+=name=dtbo_a,size=1M,uuid=${uuid_gpt_dtbo}; +partitions_android+=name=dtbo_b,size=1M,uuid=${uuid_gpt_dtbo}; +partitions_android+=name=ldfw,size=4016K,uuid=${uuid_gpt_ldfw}; +partitions_android+=name=keystorage,size=8K,uuid=${uuid_gpt_keystorage}; +partitions_android+=name=tzsw,size=1M,uuid=${uuid_gpt_tzsw}; +partitions_android+=name=harx,size=2M,uuid=${uuid_gpt_harx}; +partitions_android+=name=harx_rkp,size=2M,uuid=${uuid_gpt_harx_rkp}; +partitions_android+=name=logo,size=40M,uuid=${uuid_gpt_logo}; +partitions_android+=name=super,size=3600M,uuid=${uuid_gpt_super}; +partitions_android+=name=cache,size=300M,uuid=${uuid_gpt_cache}; +partitions_android+=name=modem,size=100M,uuid=${uuid_gpt_modem}; +partitions_android+=name=boot_a,size=100M,uuid=${uuid_gpt_boot}; +partitions_android+=name=boot_b,size=100M,uuid=${uuid_gpt_boot}; +partitions_android+=name=persist,size=30M,uuid=${uuid_gpt_persist}; +partitions_android+=name=recovery_a,size=40M,uuid=${uuid_gpt_recovery}; +partitions_android+=name=recovery_b,size=40M,uuid=${uuid_gpt_recovery}; +partitions_android+=name=misc,size=40M,uuid=${uuid_gpt_misc}; +partitions_android+=name=mnv,size=20M,uuid=${uuid_gpt_mnv}; +partitions_android+=name=frp,size=512K,uuid=${uuid_gpt_frp}; +partitions_android+=name=vbmeta_a,size=64K,uuid=${uuid_gpt_vbmeta}; +partitions_android+=name=vbmeta_b,size=64K,uuid=${uuid_gpt_vbmeta}; +partitions_android+=name=metadata,size=16M,uuid=${uuid_gpt_metadata}; +partitions_android+=name=dtb_a,size=1M,uuid=${uuid_gpt_dtb}; +partitions_android+=name=dtb_b,size=1M,uuid=${uuid_gpt_dtb}; +partitions_android+=name=userdata,size=-,uuid=${uuid_gpt_userdata} From fcb53c5a694489960702af78bf05264d7c50d850 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:23 -0500 Subject: [PATCH 6/9] configs: e850-96: Increase malloc() pool size "fastboot flash" tries to malloc 8 MiB buffer after receiving data over USB and trying to write it to eMMC. Right now only 8.12 MiB malloc is available for E850-96 overall, which leads to this issue: Malloc failed for: CHUNK_TYPE_RAW Fix it by increasing malloc pool size from 8.12 MiB up to 32 MiB, like it's done in many other boards using fastboot. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- configs/e850-96_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index f0e9ff7c447..375805b3e5c 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -3,7 +3,7 @@ CONFIG_ARCH_CPU_INIT=y CONFIG_ARM_SMCCC=y CONFIG_ARCH_EXYNOS=y CONFIG_TEXT_BASE=0xf8800000 -CONFIG_SYS_MALLOC_LEN=0x81f000 +CONFIG_SYS_MALLOC_LEN=0x2000000 CONFIG_SYS_MALLOC_F_LEN=0x4000 CONFIG_ARCH_EXYNOS9=y CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y From 9472bc2b504603716fe79360e917a1b2aaf456ae Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:24 -0500 Subject: [PATCH 7/9] configs: e850-96: Enable USB gadget and fastboot Now that USB phy and dwc3 glue layer was added for Exynos850, USB gadget is functional on E850-96 board. Enable next features to make it useful: - Exynos850 USB PHY driver (needed for all USB functions) - dwc3 generic driver - USB gadget - CONFIG_DM_USB_GADGET: needed for DWC3 glue layer to instantiate the peripheral driver, i.e. dwc3_generic_peripheral_probe() - USB VID and PID - DFU - Fastboot (including flashing to eMMC boot partitions) As all Exynos firmware binaries (including U-Boot) are contained in eMMC boot partition A (mmc0boot0), because that's where Boot ROM code jumps, it might be useful to be able to flash that area with fastboot. Other more fine grained choices for updating the firmware would be DFU and EFI Capsule Update mechanism. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- configs/e850-96_defconfig | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index 375805b3e5c..96e91959f16 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -15,7 +15,6 @@ CONFIG_SYS_LOAD_ADDR=0x80000000 CONFIG_ENV_OFFSET_REDUND=0x10000 # CONFIG_PSCI_RESET is not set CONFIG_EFI_SET_TIME=y -CONFIG_ANDROID_BOOT_IMAGE=y CONFIG_BOOTSTD_FULL=y CONFIG_DEFAULT_FDT_FILE="exynos850-e850-96.dtb" # CONFIG_DISPLAY_CPUINFO is not set @@ -24,6 +23,7 @@ CONFIG_CMD_BOOTEFI_SELFTEST=y CONFIG_CMD_ABOOTIMG=y CONFIG_CMD_NVEDIT_EFI=y CONFIG_CMD_CLK=y +CONFIG_CMD_DFU=y CONFIG_CMD_GPT=y CONFIG_CMD_MMC=y CONFIG_CMD_EFIDEBUG=y @@ -39,8 +39,18 @@ CONFIG_ENV_RELOC_GD_ENV_ADDR=y CONFIG_ENV_MMC_EMMC_HW_PARTITION=2 CONFIG_NO_NET=y CONFIG_CLK_EXYNOS850=y +CONFIG_DFU_MMC=y +CONFIG_USB_FUNCTION_FASTBOOT=y +CONFIG_FASTBOOT_BUF_ADDR=0x8a000000 +CONFIG_FASTBOOT_BUF_SIZE=0x30000000 +CONFIG_FASTBOOT_FLASH=y +CONFIG_FASTBOOT_FLASH_MMC_DEV=0 +CONFIG_FASTBOOT_MMC_BOOT_SUPPORT=y +CONFIG_FASTBOOT_CMD_OEM_FORMAT=y CONFIG_SUPPORT_EMMC_BOOT=y CONFIG_MMC_DW=y +CONFIG_PHY=y +CONFIG_PHY_EXYNOS_USBDRD=y CONFIG_DM_RTC=y CONFIG_RTC_EMULATION=y CONFIG_SOC_SAMSUNG=y @@ -48,3 +58,11 @@ CONFIG_EXYNOS_PMU=y CONFIG_EXYNOS_USI=y CONFIG_SYSRESET=y CONFIG_SYSRESET_SYSCON=y +CONFIG_USB=y +CONFIG_DM_USB_GADGET=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_GENERIC=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_MANUFACTURER="Samsung" +CONFIG_USB_GADGET_VENDOR_NUM=0x18d1 +CONFIG_USB_GADGET_PRODUCT_NUM=0x0002 From fd76fdc34efd5709bb2cd47e3d099e2cffcb2192 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:25 -0500 Subject: [PATCH 8/9] board: samsung: e850-96: Enable EFI Capsule Update mechanism Make it possible to update E850-96 firmware binaries using EFI Capsule Update mechanism. For example, to update the U-Boot binary, the capsule file can be generated like this: $ ./tools/mkeficapsule --index 4 \ --guid 629578c3-ffb3-4a89-ac0c-611840727779 \ u-boot.bin capsule4.bin The resulting 'capsule4.bin' should be copied to ESP partition (in /boot/efi/EFI/UpdateCapsule/ directory). Then after reboot U-Boot will update the 'bootloader' area in eMMC Boot Partition A (boot0) and remove the capsule file, by EFI boot manager executed as a part of Standard Boot: Applying capsule capsule4.bin succeeded. Reboot after firmware update. The kernel will also expose the ESRT table information via SysFS in /sys/firmware/efi/esrt/entries. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- board/samsung/e850-96/e850-96.c | 40 +++++++++++++++++++++++++++++++++ configs/e850-96_defconfig | 3 +++ include/configs/e850-96.h | 21 +++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/board/samsung/e850-96/e850-96.c b/board/samsung/e850-96/e850-96.c index addfe046097..4e034b9bd3b 100644 --- a/board/samsung/e850-96/e850-96.c +++ b/board/samsung/e850-96/e850-96.c @@ -4,6 +4,7 @@ * Author: Sam Protsenko */ +#include #include #include #include @@ -15,6 +16,45 @@ #define OTP_CHIPID0 0x4 #define OTP_CHIPID1 0x8 +struct efi_fw_image fw_images[] = { + { + .image_type_id = E850_96_FWBL1_IMAGE_GUID, + .fw_name = u"E850-96-FWBL1", + .image_index = 1, + }, + { + .image_type_id = E850_96_EPBL_IMAGE_GUID, + .fw_name = u"E850-96-EPBL", + .image_index = 2, + }, + { + .image_type_id = E850_96_BL2_IMAGE_GUID, + .fw_name = u"E850-96-BL2", + .image_index = 3, + }, + { + .image_type_id = E850_96_BOOTLOADER_IMAGE_GUID, + .fw_name = u"E850-96-BOOTLOADER", + .image_index = 4, + }, + { + .image_type_id = E850_96_EL3_MON_IMAGE_GUID, + .fw_name = u"E850-96-EL3-MON", + .image_index = 5, + }, +}; + +struct efi_capsule_update_info update_info = { + .dfu_string = "mmc 0=" + "fwbl1.img raw 0x0 0x18 mmcpart 1;" + "epbl.img raw 0x18 0x98 mmcpart 1;" + "bl2.img raw 0xb0 0x200 mmcpart 1;" + "bootloader.img raw 0x438 0x1000 mmcpart 1;" + "el3_mon.img raw 0x1438 0x200 mmcpart 1", + .num_images = ARRAY_SIZE(fw_images), + .images = fw_images, +}; + int dram_init(void) { return fdtdec_setup_mem_size_base(); diff --git a/configs/e850-96_defconfig b/configs/e850-96_defconfig index 96e91959f16..e334efd4d5f 100644 --- a/configs/e850-96_defconfig +++ b/configs/e850-96_defconfig @@ -15,6 +15,9 @@ CONFIG_SYS_LOAD_ADDR=0x80000000 CONFIG_ENV_OFFSET_REDUND=0x10000 # CONFIG_PSCI_RESET is not set CONFIG_EFI_SET_TIME=y +CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y +CONFIG_EFI_CAPSULE_ON_DISK=y +CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y CONFIG_BOOTSTD_FULL=y CONFIG_DEFAULT_FDT_FILE="exynos850-e850-96.dtb" # CONFIG_DISPLAY_CPUINFO is not set diff --git a/include/configs/e850-96.h b/include/configs/e850-96.h index 4607b3089b2..63e85332bd8 100644 --- a/include/configs/e850-96.h +++ b/include/configs/e850-96.h @@ -9,4 +9,25 @@ #ifndef __E850_96_H #define __E850_96_H +/* GUIDs for capsule updatable firmware images */ +#define E850_96_FWBL1_IMAGE_GUID \ + EFI_GUID(0x181cd3f2, 0xe375, 0x44d2, 0x80, 0x78, \ + 0x32, 0x21, 0xe1, 0xdf, 0xb9, 0x5e) + +#define E850_96_EPBL_IMAGE_GUID \ + EFI_GUID(0x66c1a54d, 0xd149, 0x415d, 0xaa, 0xda, \ + 0xb8, 0xae, 0xe4, 0x99, 0xb3, 0x70) + +#define E850_96_BL2_IMAGE_GUID \ + EFI_GUID(0x89471c2a, 0x6c8d, 0x4158, 0xac, 0xad, \ + 0x23, 0xd3, 0xb2, 0x87, 0x3d, 0x35) + +#define E850_96_BOOTLOADER_IMAGE_GUID \ + EFI_GUID(0x629578c3, 0xffb3, 0x4a89, 0xac, 0x0c, \ + 0x61, 0x18, 0x40, 0x72, 0x77, 0x79) + +#define E850_96_EL3_MON_IMAGE_GUID \ + EFI_GUID(0xdf5718a2, 0x930a, 0x4916, 0xbb, 0x19, \ + 0x32, 0x13, 0x21, 0x4d, 0x84, 0x86) + #endif /* __E850_96_H */ From f83f2397a1f3ebb3136618332e1549687eb8dd52 Mon Sep 17 00:00:00 2001 From: Sam Protsenko Date: Wed, 9 Jul 2025 17:29:26 -0500 Subject: [PATCH 9/9] doc: samsung: Describe flashing process for E850-96 Now that USB is enabled on the E850-96 board, DFU and fastboot tools are functional and can be used to flash images to eMMC. Update the E850-96 documentation accordingly and describe flashing to User Data Area and Boot HW Partition of eMMC using fastboot and DFU tools. Signed-off-by: Sam Protsenko Signed-off-by: Minkyu Kang --- doc/board/samsung/e850-96.rst | 85 ++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 11 deletions(-) diff --git a/doc/board/samsung/e850-96.rst b/doc/board/samsung/e850-96.rst index 0a7b6fc0c9d..b435fa8b353 100644 --- a/doc/board/samsung/e850-96.rst +++ b/doc/board/samsung/e850-96.rst @@ -43,17 +43,19 @@ Legend: BL31 in terms of ARM boot flow * ``LDFW``: Loadable Firmware -Build Procedure +Unbricking Note --------------- -.. warning:: - At the moment USB is not enabled in U-Boot for this board. Although eMMC is - enabled, you won't be able to flash images over USB (fastboot). So flashing - U-Boot binary **WILL** effectively brick your board. The ``dltool`` [8]_ can - be used then to perform USB boot and flash LittleKernel bootloader binary [7]_ - to unbrick and revive the board. Flashing U-Boot binary might be helpful for - developers or anybody who want to check current state of U-Boot enablement on - E850-96 (which is mostly serial console, eMMC and related blocks). +In case the board is bricked for some reason, the ``dltool`` [8]_ can be used to +unbrick and revive it. This tool performs USB boot, and uploads the LittleKernel +bootloader over USB, which is then being executed on the board. The loaded +bootloader further enters fastboot mode, so that the user can flash the +functional bootloader binary (U-Boot or LittleKernel [7]_) to eMMC using +``fastboot`` tool. Please read the ``dltool`` README file for more details about +the procedure. + +Build Procedure +--------------- Build U-Boot binary from source code (using AArch64 baremetal GCC toolchain): @@ -64,8 +66,9 @@ Build U-Boot binary from source code (using AArch64 baremetal GCC toolchain): make e850-96_defconfig make -Boot E850-96 board into fastboot mode as described in board software doc [9]_, -and flash U-Boot binary into ``bootloader`` eMMC partition: +The original E850-96 board is shipped with LittleKernel-based bootloader flashed +in eMMC. To replace it with U-Boot, boot into fastboot mode (as described in +the board software documentation [9]_), and flash U-Boot binary: .. prompt:: bash $ @@ -74,6 +77,66 @@ and flash U-Boot binary into ``bootloader`` eMMC partition: U-Boot will boot up to the shell. +Flashing +-------- + +User area of eMMC contains GPT partition table (either Linux or Android). Boot +Partition A (``mmc0boot0``) contains all firmware/bootloaders. Boot Partition +B (``mmc0boot1``) contains U-Boot environment. + +First make sure to format eMMC accordingly. Prepare the initial environment: + +.. prompt:: bash => + + env default -f -a + env save + +For Linux, just format eMMC using default ``$partitions`` definitions: + +.. prompt:: bash => + + gpt write mmc 0 $partitions + +For Android, use ``$partitions_android`` instead: + +.. prompt:: bash => + + setenv partitions_linux $partitions + setenv partitions $partitions_android + env save + gpt write mmc 0 $partitions + +In case of Linux, there are two partitions available: ``esp`` (EFI System +Partition) and ``rootfs``. It is recommended to use fastboot to flash images to +those partitions. Enter fastboot mode on your device: + +.. prompt:: bash => + + fastboot usb 0 + +And then flash the images: + +.. prompt:: bash $ + + fastboot flash esp esp.img + fastboot flash rootfs rootfs.img + +To update the firmware, it's easier to use DFU. Enter DFU mode on the board: + +.. prompt:: bash => + + dfu 0 mmc 0 + +To update U-Boot: + +.. prompt:: bash $ + + dfu-util -D u-boot.bin -a bootloader + +It's also possible to use fastboot to flash the whole ``mmc0boot0`` HW +partition, but it's not so straightforward, as one have to prepare the image for +the whole ``boot0`` partition containing all firmware binaries first. + References ----------