board: samsung: add support for Samsung Exynos mobile device boards

Add support for a generic platform which intends to support multiple
boards powered by ARMv8 Samsung Exynos SoCs. Some important features
include:
 * Fastboot: This is present to provide an open alternative to Samsung's
   proprietary Odin protocol. The board file configures certain features
   for fastboot, such as a dynamically allocated fastboot buffer, and
   standardized (lowercase) partition aliases.
 * EFI: Kernel image can be loaded from an EFI partition. This
   adopts a standard booting process, which multiple OS distributions
   can rely on.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
This commit is contained in:
Kaustabh Chakraborty 2025-10-24 22:58:27 +05:30 committed by Minkyu Kang
parent ff3b4e9936
commit ed9ba442ee
10 changed files with 467 additions and 1 deletions

View File

@ -2,7 +2,7 @@ if ARCH_EXYNOS
config BOARD_COMMON config BOARD_COMMON
def_bool y def_bool y
depends on !TARGET_SMDKV310 && !TARGET_ARNDALE && !TARGET_E850_96 depends on !TARGET_SMDKV310 && !TARGET_ARNDALE && !TARGET_EXYNOS_MOBILE && !TARGET_E850_96
config SPI_BOOTING config SPI_BOOTING
bool bool
@ -252,6 +252,14 @@ config TARGET_E850_96
endchoice endchoice
endif endif
config TARGET_EXYNOS_MOBILE
bool "Samsung Exynos Generic Boards (for mobile devices)"
select ARM64
select BOARD_EARLY_INIT_F
select CLK_EXYNOS
select LINUX_KERNEL_IMAGE_HEADER
select OF_CONTROL
config SYS_SOC config SYS_SOC
default "exynos" default "exynos"
@ -277,5 +285,6 @@ source "board/samsung/smdk5420/Kconfig"
source "board/samsung/espresso7420/Kconfig" source "board/samsung/espresso7420/Kconfig"
source "board/samsung/axy17lte/Kconfig" source "board/samsung/axy17lte/Kconfig"
source "board/samsung/e850-96/Kconfig" source "board/samsung/e850-96/Kconfig"
source "board/samsung/exynos-mobile/Kconfig"
endif endif

View File

@ -0,0 +1,18 @@
if TARGET_EXYNOS_MOBILE
config ENV_SOURCE_FILE
default "exynos-mobile"
config LNX_KRNL_IMG_TEXT_OFFSET_BASE
default TEXT_BASE
config SYS_BOARD
default "exynos-mobile"
config SYS_CONFIG_NAME
default "exynos-mobile"
config SYS_VENDOR
default "samsung"
endif # TARGET_EXYNOS_MOBILE

View File

@ -0,0 +1,6 @@
Exynos Generic Boards (for mobile devices)
M: Kaustabh Chakraborty <kauschluss@disroot.org>
S: Maintained
F: board/samsung/exynos-mobile/
F: configs/exynos-mobile_defconfig
F: include/configs/exynos-mobile.h

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
obj-y := exynos-mobile.o

View File

@ -0,0 +1,287 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Samsung Exynos Generic Board Source (for mobile devices)
*
* Copyright (c) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
*/
#include <asm/armv8/mmu.h>
#include <blk.h>
#include <bootflow.h>
#include <ctype.h>
#include <dm/ofnode.h>
#include <env.h>
#include <errno.h>
#include <init.h>
#include <linux/sizes.h>
#include <lmb.h>
#include <part.h>
#include <stdbool.h>
DECLARE_GLOBAL_DATA_PTR;
#define lmb_alloc(size, addr) \
lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, addr, size, LMB_NONE)
struct exynos_board_info {
const char *name;
const char *chip;
const u64 *const dram_bank_bases;
char serial[64];
int (*const match)(struct exynos_board_info *);
const char *match_model;
const u8 match_max_rev;
};
/*
* The memory mapping includes all DRAM banks, along with the
* peripheral block, and a sentinel at the end. This is filled in
* dynamically.
*/
static struct mm_region exynos_mem_map[CONFIG_NR_DRAM_BANKS + 2] = {
{
/* Peripheral MMIO block */
.virt = 0x10000000UL,
.phys = 0x10000000UL,
.size = 0x10000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN,
},
};
struct mm_region *mem_map = exynos_mem_map;
/*
* This array is used for matching the models and revisions with the
* devicetree used by U-Boot. This allows a single U-Boot to work on
* multiple devices.
*
* Entries are kept in lexicographical order of board SoCs, followed by
* board names.
*/
static struct exynos_board_info exynos_board_info_match[] = {
};
static void exynos_parse_dram_banks(const struct exynos_board_info *board_info,
const void *fdt_base)
{
u64 mem_addr, mem_size = 0;
u32 na, ns, i, j;
int offset;
if (fdt_check_header(fdt_base) < 0)
return;
/* #address-cells and #size-cells as defined in the fdt root. */
na = fdt_address_cells(fdt_base, 0);
ns = fdt_size_cells(fdt_base, 0);
fdt_for_each_subnode(offset, fdt_base, 0) {
if (strncmp(fdt_get_name(fdt_base, offset, NULL), "memory", 6))
continue;
for (i = 0; ; i++) {
mem_addr = fdtdec_get_addr_size_fixed(fdt_base, offset,
"reg", i, na, ns,
&mem_size, false);
if (mem_addr == FDT_ADDR_T_NONE)
break;
if (!mem_size)
continue;
for (j = 0; j < CONFIG_NR_DRAM_BANKS; j++) {
if (board_info->dram_bank_bases[j] != mem_addr)
continue;
mem_map[j + 1].phys = mem_addr;
mem_map[j + 1].virt = mem_addr;
mem_map[j + 1].size = mem_size;
mem_map[j + 1].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_INNER_SHARE;
break;
}
}
}
}
static int exynos_fastboot_setup(void)
{
struct blk_desc *blk_dev;
struct disk_partition info = {0};
char buf[128];
phys_addr_t addr;
int offset, i, j;
/* Allocate and define buffer address for fastboot interface. */
if (lmb_alloc(CONFIG_FASTBOOT_BUF_SIZE, &addr)) {
log_err("%s: failed to allocate fastboot buffer\n", __func__);
return -ENOMEM;
}
env_set_hex("fastboot_addr_r", addr);
blk_dev = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
if (!blk_dev) {
log_err("%s: required mmc device not available\n", __func__);
return -ENODEV;
}
strcpy(buf, "fastboot_partition_alias_");
offset = strlen(buf);
for (i = 1; i < CONFIG_EFI_PARTITION_ENTRIES_NUMBERS; i++) {
if (part_get_info(blk_dev, i, &info))
continue;
/*
* The partition name must be lowercase (stored in buf[]),
* as is expected in all fastboot partitions ...
*/
strlcpy(buf + offset, info.name, sizeof(buf) - offset);
for (j = offset; buf[j]; j++)
buf[j] = tolower(buf[j]);
if (!strcmp(buf + offset, info.name))
continue;
/*
* ... However, if that isn't the case, a fastboot
* partition alias must be defined to establish it.
*/
env_set(buf, info.name);
}
return 0;
}
int board_fit_config_name_match(const char *name)
{
struct exynos_board_info *board_info;
char buf[128];
unsigned int i;
int ret;
/*
* Iterate over exynos_board_info_match[] to select the
* appropriate board info struct. If not found, exit.
*/
for (i = 0; i < ARRAY_SIZE(exynos_board_info_match); i++) {
board_info = exynos_board_info_match + i;
snprintf(buf, sizeof(buf), "%s-%s", board_info->chip,
board_info->name);
if (!strcmp(name, buf))
break;
}
if (i == ARRAY_SIZE(exynos_board_info_match))
return -1;
/*
* Execute match logic for the target board. This is separated
* as the process may be different for multiple boards.
*/
ret = board_info->match(board_info);
if (ret)
return ret;
/*
* Store the correct board info struct in gd->board_type to
* allow other functions to access it.
*/
gd->board_type = (ulong)board_info;
log_debug("%s: device detected: %s\n", __func__, name);
return 0;
}
int timer_init(void)
{
ofnode timer_node;
/*
* In a lot of Exynos devices, the previous bootloader does not
* set CNTFRQ_EL0 properly. However, the timer node in
* devicetree has the correct frequency, use that instead.
*/
timer_node = ofnode_by_compatible(ofnode_null(), "arm,armv8-timer");
gd->arch.timer_rate_hz = ofnode_read_u32_default(timer_node,
"clock-frequency", 0);
return 0;
}
int board_early_init_f(void)
{
const struct exynos_board_info *board_info;
if (!gd->board_type)
return -ENODATA;
board_info = (const struct exynos_board_info *)gd->board_type;
exynos_parse_dram_banks(board_info, gd->fdt_blob);
/*
* Some devices have multiple variants based on the amount of
* memory and internal storage. The lowest bank base has been
* observed to have the same memory range in all board variants.
* For variants with more memory, the previous bootloader should
* overlay the devicetree with the required extra memory ranges.
*/
exynos_parse_dram_banks(board_info, (const void *)get_prev_bl_fdt_addr());
return 0;
}
int dram_init(void)
{
unsigned int i;
/* Select the largest RAM bank for U-Boot. */
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
if (gd->ram_size < mem_map[i + 1].size) {
gd->ram_base = mem_map[i + 1].phys;
gd->ram_size = mem_map[i + 1].size;
}
}
return 0;
}
int dram_init_banksize(void)
{
unsigned int i;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
gd->bd->bi_dram[i].start = mem_map[i + 1].phys;
gd->bd->bi_dram[i].size = mem_map[i + 1].size;
}
return 0;
}
int board_init(void)
{
return 0;
}
int misc_init_r(void)
{
const struct exynos_board_info *board_info;
char buf[128];
if (!gd->board_type)
return -ENODATA;
board_info = (const struct exynos_board_info *)gd->board_type;
env_set("platform", board_info->chip);
env_set("board", board_info->name);
if (strlen(board_info->serial))
env_set("serial#", board_info->serial);
/* EFI booting requires the path to correct dtb, specify it here. */
snprintf(buf, sizeof(buf), "exynos/%s-%s.dtb", board_info->chip,
board_info->name);
env_set("fdtfile", buf);
return exynos_fastboot_setup();
}

View File

@ -0,0 +1,18 @@
stdin=serial,button-kbd
stdout=serial,vidconsole
stderr=serial,vidconsole
bootdelay=0
bootcmd=bootefi bootmgr; pause; bootmenu
fastbootcmd=echo "Fastboot Mode";
fastboot -l $fastboot_addr_r usb 0
bootmenu_0=Continue Boot=boot
bootmenu_1=Enter Fastboot Mode=run fastbootcmd
bootmenu_2=UEFI Maintenance Menu=eficonfig
bootmenu_3=Reboot=reset
bootmenu_4=Power Off=poweroff
button_cmd_0_name=Volume Down Key
button_cmd_0=bootmenu

View File

@ -0,0 +1,68 @@
CONFIG_ARM=y
CONFIG_SKIP_LOWLEVEL_INIT=y
CONFIG_COUNTER_FREQUENCY=26000000
CONFIG_POSITION_INDEPENDENT=y
CONFIG_ARCH_EXYNOS=y
CONFIG_SYS_MALLOC_LEN=0x2000000
CONFIG_SYS_MALLOC_F_LEN=0x16000
CONFIG_TARGET_EXYNOS_MOBILE=y
CONFIG_NR_DRAM_BANKS=3
CONFIG_SYS_BOOTM_LEN=0x2000000
CONFIG_SYS_LOAD_ADDR=0x80000000
CONFIG_ARMV8_CNTFRQ_BROKEN=y
# CONFIG_PSCI_RESET is not set
CONFIG_BUTTON_CMD=y
CONFIG_SAVE_PREV_BL_FDT_ADDR=y
CONFIG_SAVE_PREV_BL_INITRAMFS_START_ADDR=y
CONFIG_SYS_PBSIZE=1024
CONFIG_BOARD_TYPES=y
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_MISC_INIT_R=y
CONFIG_HUSH_PARSER=y
CONFIG_CMD_BOOTMENU=y
CONFIG_CMD_POWEROFF=y
CONFIG_CMD_FS_GENERIC=y
CONFIG_EFI_PARTITION=y
CONFIG_OF_UPSTREAM=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_BUTTON=y
CONFIG_BUTTON_REMAP_PHONE_KEYS=y
CONFIG_CLK=y
CONFIG_CLK_CCF=y
CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_BUF_ADDR=0xdead0000
CONFIG_FASTBOOT_FLASH=y
CONFIG_FASTBOOT_FLASH_MMC_DEV=0
CONFIG_SYS_I2C_S3C24X0=y
CONFIG_BUTTON_KEYBOARD=y
CONFIG_MISC=y
CONFIG_MMC_BROKEN_CD=y
CONFIG_MMC_IO_VOLTAGE=y
CONFIG_MMC_UHS_SUPPORT=y
CONFIG_MMC_HS400_SUPPORT=y
CONFIG_MMC_DW=y
CONFIG_PHY=y
CONFIG_PHY_EXYNOS_USBDRD=y
CONFIG_PINCTRL=y
CONFIG_DM_PMIC=y
CONFIG_PMIC_S2MPS11=y
CONFIG_DM_REGULATOR=y
CONFIG_DM_REGULATOR_FIXED=y
CONFIG_DM_REGULATOR_S2MPS11=y
CONFIG_SOC_SAMSUNG=y
CONFIG_EXYNOS_PMU=y
CONFIG_SYSRESET=y
CONFIG_SYSRESET_CMD_POWEROFF=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=0x04e8
CONFIG_USB_GADGET_PRODUCT_NUM=0x6602
CONFIG_VIDEO=y
CONFIG_VIDEO_SIMPLE=y
CONFIG_FS_EXT4=y
CONFIG_FS_FAT=y

View File

@ -0,0 +1,40 @@
.. SPDX-License-Identifier: GPL-2.0+
.. sectionauthor:: Kaustabh Chakraborty <kauschluss@disroot.org>
Samsung Exynos Generic ARMv8 Boards (for mobile devices)
=======================================================
Overview
--------
This document describes how to build and run U-Boot for Samsung Exynos generic
boards. Boards are expected to boot with a primary bootloader, such as S-BOOT or
S-LK, which hands off control to U-Boot. Presently, only ARMv8 devices are
supported.
The U-Boot image is built with all device tree blobs packed in a single FIT
image. During boot, it uses simple heuristics to detect the target board, and
subsequently the appropriate FDT is selected.
Installation
------------
Building
^^^^^^^^
If a cross-compiler is required, install it and set it up like so:
.. prompt:: bash $
export CROSS_COMPILE=aarch64-linux-gnu-
Then, run the following commands to build U-Boot:
.. prompt:: bash $
make O=.output exynos-mobile_defconfig
make O=.output -j$(nproc)
If successful, the U-Boot binary will be present in ``.output/u-boot.bin``.
Preparation and Flashing
^^^^^^^^^^^^^^^^^^^^^^^^
Since U-Boot supports multiple boards, and devices have different requirements,
this step will vary depending on your target.

View File

@ -8,4 +8,5 @@ Samsung
axy17lte axy17lte
e850-96 e850-96
exynos-mobile
n1 n1

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Samsung Exynos Generic Board Configuration (for mobile devices)
*
* Copyright (C) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
*/
#ifndef __CONFIG_EXYNOS_MOBILE_H
#define __CONFIG_EXYNOS_MOBILE_H
#define CPU_RELEASE_ADDR secondary_boot_addr
#define CFG_SYS_BAUDRATE_TABLE {9600, 115200}
#endif /* __CONFIG_EXYNOS_MOBILE_H */