commit 359c107d36df4ab479a6d687ec66919e2303437b Author: Артем Чамайкин Date: Fri Jun 5 05:22:22 2026 +0300 Add X96Q LPDDR3 v1.3 custom U-Boot build and eMMC flash tooling. Armbian-compatible U-Boot v2025.01 with eMMC, DTB, and flash fixes for the X96Q TV box. Co-authored-by: Cursor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77ba162 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# build artifacts +.build/ +.venv/ +output/u-boot-custom.bin + +# macOS +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..be23f83 --- /dev/null +++ b/README.md @@ -0,0 +1,203 @@ +# X96Q LPDDR3 v1.3 — кастомный U-Boot + +Кастомная сборка U-Boot для **X96Q TV Box LPDDR3 v1.3** (Allwinner H616/H313, 1 GiB LPDDR3, AXP313A, eMMC boot). + +Проверено: UART 115200, загрузка Armbian с eMMC, `DRAM: 1 GiB`. + +## Структура репозитория + +``` +x96q-uboot/ +├── README.md +├── build.sh # сборка (macOS + Homebrew) +├── patches/ +│ ├── jernejsk-emmc.patch # фикс eMMC на H616 +│ ├── easter-egg.patch # баннер в SPL + Kconfig +│ └── fdtfile-armbian.patch # совместимость DTB с Armbian +├── overlays/ +│ ├── configs/x96_q_lpddr3_v1.3_defconfig +│ └── dts/sun50i-h313-x96q-lpddr3-v1.3.dts +├── output/ +│ └── u-boot-custom.bin # артефакт сборки (gitignore) +└── scripts/ + ├── flash-emmc.sh # образ + U-Boot на eMMC + └── flash-uboot-only.sh # только U-Boot поверх существующего образа +``` + +## Что было сломано и как чинили + +### 1. Неправильный defconfig (DDR3 вместо LPDDR3) + +**Симптом:** `DRAM: not supported` в UART. + +**Причина:** generic `x96q_defconfig` настроен на DDR3, а плата — **LPDDR3 v1.3** с другими таймингами и AXP313. + +**Фикс:** defconfig `x96_q_lpddr3_v1.3_defconfig` из Armbian/NickAlilovic + DTS `sun50i-h313-x96q-lpddr3-v1.3.dts`. + +### 2. eMMC read error в SPL + +**Симптом:** SPL не читает eMMC, BootROM зацикливается или грузит битый bootloader. + +**Причина:** на H616 контроллеру MMC нужен hardware reset карты и FIFO threshold (патч Jernej Skrabec, upstream U-Boot). + +**Фикс:** `patches/jernejsk-emmc.patch` — backport на **v2025.01**. + +### 3. Несовпадение имени DTB (главный баг загрузки) + +**Симптом:** +``` +The file allwinner/sun50i-h313-x96q-lpddr3-v1.3.dtb was not found +ERROR: Did not find a cmdline Flattened Device Tree +``` + +**Причина:** U-Boot из SPL выставляет `fdtfile` по имени **своего** device tree (`…-lpddr3-v1.3.dtb`). Armbian в `/boot/dtb/allwinner/` кладёт только `sun50i-h313-x96q-lpddr3.dtb` (без суффикса v1.3). Ядро и initrd грузятся, DTB — нет. + +**Фикс (три уровня):** +1. **Патч U-Boot** `fdtfile-armbian.patch` — в `misc_init_r()` подменяет `fdtfile` на Armbian-имя. +2. **`flash-emmc.sh`** — после прошивки пишет `fdtfile=sun50i-h313-x96q-lpddr3.dtb` в `armbianEnv.txt` и создаёт symlink `…-v1.3.dtb`. +3. Вручную на работающей системе — то же в `/boot/armbianEnv.txt`. + +> U-Boot DT (`v1.3`) и kernel DT (`lpddr3`) — **разные файлы с разным назначением**. SPL использует свой DTS для DRAM/PMIC; ядро — DTB из образа Armbian. + +### 4. `dd: Operation not permitted` на boot0/boot1 + +**Симптом:** запись в user area проходит, в `mmcblk2boot0` — отказ. + +**Причина:** boot-разделы eMMC по умолчанию read-only (`force_ro=1`). + +**Фикс:** перед `dd`: +```bash +echo 0 > /sys/block/mmcblk2boot0/force_ro +echo 0 > /sys/block/mmcblk2boot1/force_ro +``` +Скрипт `flash-emmc.sh` делает это автоматически. + +### 5. `Card did not respond to voltage select! : -110` на mmc0 + +**Симптом:** при фолбэке boot.scr пытается mmc0 и падает. + +**Причина:** в U-Boot mmc0 — SD-слот, при определённых условиях voltage select не проходит; это **не блокер**, если DTB найден на правильном mmc (SD = mmc1 в нашем случае). + +## Карта MMC + +| Где | Устройство | Назначение | +|-----|------------|------------| +| Linux | `mmcblk0` | SD-карта | +| Linux | `mmcblk2` | eMMC (user area) | +| Linux | `mmcblk2boot0/1` | eMMC boot partitions (по 2 MiB) | +| U-Boot shell | `mmc dev 1` | eMMC | +| U-Boot shell | `mmc dev 2` | то же eMMC (другой индекс) | +| U-Boot autoboot | `mmc1` | откуда грузится boot.scr (SD при вставленной карте) | + +SPL/U-Boot пишется в **три места**: +- user area @ **8 KiB** (`dd seek=8`) — legacy sunxi offset +- **boot0** — основная boot-область eMMC +- **boot1** — зеркало (резерв) + +## Сборка + +### Требования (macOS) + +```bash +brew install aarch64-elf-gcc gnu-sed openssl@3 swig python3 +``` + +### Команда + +```bash +./build.sh +``` + +Скрипт: +1. Клонирует U-Boot `v2025.01` и ARM TF-A в `.build/` +2. Накатывает патчи из `patches/` +3. Копирует defconfig и DTS из `overlays/` +4. Собирает BL31 + U-Boot +5. Кладёт бинарник в `output/u-boot-custom.bin` (~822 KiB) + +### Проверка артефакта + +```bash +strings output/u-boot-custom.bin | grep -E 'binary build|2025.01|lpddr3' +``` + +Ожидаемо: +- `*** binary build ***` +- `U-Boot SPL 2025.01-dirty` +- `sun50i-h313-x96q-lpddr3.dtb` (fdtfile для Armbian) + +## Прошивка eMMC + +### Полная (образ Armbian + U-Boot) + +На приставке (под root, лучше загрузившись с SD): + +```bash +# скопировать на приставку: +# output/u-boot-custom.bin +# scripts/flash-emmc.sh +# Armbian_*.img.xz + +cd /home/binary # или куда положили файлы +sudo ./flash-emmc.sh +# или явно: +sudo ./flash-emmc.sh Armbian_community_26.2.0-trunk.904_X96q_trixie_current_6.18.29_minimal.img.xz +``` + +Скрипт: +1. `xz -dc образ | dd of=/dev/mmcblk2` — запись образа локально (не через SSH pipe) +2. U-Boot → user @ 8 KiB +3. Разблокировка boot0/boot1 +4. U-Boot → boot0 + boot1 +5. Правка `armbianEnv.txt` + symlink DTB + +После прошивки: **вынуть SD**, перезагрузка. + +### Только U-Boot (образ уже на eMMC) + +```bash +sudo ./flash-uboot-only.sh /path/to/u-boot-custom.bin +``` + +## Ожидаемый UART при успешной загрузке + +``` +*** binary build *** +U-Boot 2025.01-dirty (...) binary build +CPU: Allwinner H616 (SUN50I) +Model: hechuang,x96-q LPDDR3 v1.3 +DRAM: 1 GiB +... +U-boot loaded from eMMC +Load fdt: /boot/dtb/allwinner/sun50i-h313-x96q-lpddr3.dtb +``` + +## Патчи + +| Патч | Зачем | +|------|-------| +| `jernejsk-emmc.patch` | reset eMMC + FIFO threshold на H616 | +| `easter-egg.patch` | `CONFIG_X96Q_BINARY_BUILD` — маркер в SPL, идентификация сборки | +| `fdtfile-armbian.patch` | `fdtfile` → имя DTB из Armbian, иначе boot.scr не находит дерево | + +## Восстановление (FEL) + +Если eMMC bootloader битый и SD не помогает: + +1. Зажать кнопку **FEL**, подключить USB (`1f3a:efe8`) +2. FEL + sunxi-tools: wipe boot0/boot1/SPL или прошить свежий `u-boot-custom.bin` + +Подробности — в родительском репозитории FEL (`fel-recover.sh`, `fel-wipe-emmc.sh`). + +## Версии + +| Компонент | Версия | +|-----------|--------| +| U-Boot base | `v2025.01` | +| ARM TF-A BL31 | sun50i_h616 | +| Defconfig | Armbian `x96_q_lpddr3_v1.3` | +| Плата | X96Q LPDDR3 **v1.3** (не путать с DDR3 и не-LPDDR3 ревизиями) | + +## Лицензия + +Патчи и overlay — поверх GPL-2.0+ U-Boot. DTS: GPL-2.0+ OR MIT (см. заголовок файла). diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..cb4d472 --- /dev/null +++ b/build.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# Сборка U-Boot для X96Q LPDDR3 v1.3 (Allwinner H616) +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")" && pwd)" +UBOOT_DIR="$ROOT/.build/u-boot" +ATF_DIR="$ROOT/.build/arm-trusted-firmware" +PATCH_DIR="$ROOT/patches" +OVERLAY_DIR="$ROOT/overlays" +CROSS_BIN="$ROOT/.build/cross-bin" +OUT_DIR="$ROOT/output" +CROSS_COMPILE=aarch64-elf- +UBOOT_TAG=v2025.01 +DEFCONFIG=x96_q_lpddr3_v1.3_defconfig +BL31="$ATF_DIR/build/sun50i_h616/debug/bl31.bin" +OUTPUT="$UBOOT_DIR/u-boot-sunxi-with-spl.bin" +NCPU="$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4)" + +export PATH="$CROSS_BIN:/opt/homebrew/opt/gnu-sed/libexec/gnubin:/opt/homebrew/bin:$PATH" + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || { echo "Missing: $1" >&2; exit 1; } +} + +setup_venv() { + if [[ ! -x "$ROOT/.venv/bin/python3" ]]; then + python3 -m venv "$ROOT/.venv" + "$ROOT/.venv/bin/pip" install -q 'setuptools==69.5.1' + fi + export PATH="$ROOT/.venv/bin:$PATH" +} + +setup_cross_shims() { + mkdir -p "$CROSS_BIN" + for t in objcopy ar ranlib ld nm strip; do + ln -sf "/opt/homebrew/bin/${CROSS_COMPILE}${t}" "$CROSS_BIN/$t" + done +} + +clone_repos() { + mkdir -p "$ROOT/.build" + if [[ ! -d "$UBOOT_DIR/.git" ]]; then + git clone --filter=blob:none --tags https://github.com/u-boot/u-boot.git "$UBOOT_DIR" + fi + if [[ ! -d "$ATF_DIR/.git" ]]; then + git clone --depth=1 https://github.com/ARM-software/arm-trusted-firmware.git "$ATF_DIR" + fi +} + +prepare_uboot_tree() { + local patch applied=0 + ( + cd "$UBOOT_DIR" + git fetch --depth 1 origin tag "$UBOOT_TAG" 2>/dev/null || true + git checkout -f "$UBOOT_TAG" + git clean -fdx + ) + for patch in \ + "$PATCH_DIR"/jernejsk-emmc.patch \ + "$PATCH_DIR"/easter-egg.patch \ + "$PATCH_DIR"/fdtfile-armbian.patch; do + [[ -f "$patch" ]] || continue + if (cd "$UBOOT_DIR" && git apply --check "$patch" 2>/dev/null); then + (cd "$UBOOT_DIR" && git apply "$patch") + applied=$((applied + 1)) + elif (cd "$UBOOT_DIR" && git apply --reverse --check "$patch" 2>/dev/null); then + echo "Already applied: $(basename "$patch")" + else + echo "Failed to apply: $patch" >&2 + exit 1 + fi + done + mkdir -p "$UBOOT_DIR/configs" \ + "$UBOOT_DIR/dts/upstream/src/arm64/allwinner" + cp -f "$OVERLAY_DIR/configs/$DEFCONFIG" \ + "$UBOOT_DIR/configs/$DEFCONFIG" + cp -f "$OVERLAY_DIR/dts/sun50i-h313-x96q-lpddr3-v1.3.dts" \ + "$UBOOT_DIR/dts/upstream/src/arm64/allwinner/sun50i-h313-x96q-lpddr3-v1.3.dts" + echo "U-Boot $UBOOT_TAG + $applied patches + overlay" +} + +build_bl31() { + ( + cd "$ATF_DIR" + gmake CROSS_COMPILE="$CROSS_COMPILE" AR="${CROSS_COMPILE}ar" RANLIB="${CROSS_COMPILE}ranlib" \ + PLAT=sun50i_h616 DEBUG=1 bl31 + ) +} + +build_uboot() { + local openssl_prefix py_ldflags py_cflags + openssl_prefix="$(brew --prefix openssl@3)" + py_ldflags="$(python3-config --ldflags --embed 2>/dev/null || python3-config --ldflags)" + py_cflags="$(python3-config --includes)" + ( + cd "$UBOOT_DIR" + export PATH="$ROOT/.venv/bin:$PATH" + gmake CROSS_COMPILE="$CROSS_COMPILE" BL31="$BL31" "$DEFCONFIG" + gmake CROSS_COMPILE="$CROSS_COMPILE" BL31="$BL31" \ + BINMAN_ALLOW_MISSING=1 \ + HOSTCFLAGS="$py_cflags -I$openssl_prefix/include" \ + HOSTLDFLAGS="$py_ldflags -L$openssl_prefix/lib -lcrypto" \ + -j"$NCPU" + ) +} + +main() { + need_cmd git + need_cmd gmake + need_cmd "${CROSS_COMPILE}gcc" + need_cmd python3 + need_cmd swig + need_cmd brew + + setup_cross_shims + setup_venv + clone_repos + prepare_uboot_tree + build_bl31 + build_uboot + + mkdir -p "$OUT_DIR" + cp -f "$OUTPUT" "$OUT_DIR/u-boot-custom.bin" + echo "U-Boot: $(git -C "$UBOOT_DIR" describe --tags --always)" + echo "Output: $OUT_DIR/u-boot-custom.bin" + ls -la "$OUT_DIR/u-boot-custom.bin" +} + +main "$@" diff --git a/output/.gitkeep b/output/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/overlays/configs/x96_q_lpddr3_v1.3_defconfig b/overlays/configs/x96_q_lpddr3_v1.3_defconfig new file mode 100644 index 0000000..9cf260c --- /dev/null +++ b/overlays/configs/x96_q_lpddr3_v1.3_defconfig @@ -0,0 +1,39 @@ +# Armbian/NickAlilovic x96_q_lpddr3_v1.3 — проверенный LPDDR3 конфиг +CONFIG_ARM=y +CONFIG_ARCH_SUNXI=y +CONFIG_DEFAULT_DEVICE_TREE="allwinner/sun50i-h313-x96q-lpddr3-v1.3" +CONFIG_SPL=y +CONFIG_SUNXI_DRAM_H616_LPDDR3=y +CONFIG_DRAM_CLK=600 +CONFIG_DRAM_SUN50I_H616_DX_ODT=0x06060606 +CONFIG_DRAM_SUN50I_H616_DX_DRI=0x0d0d0d0d +CONFIG_DRAM_SUN50I_H616_CA_DRI=0x00000d0d +CONFIG_DRAM_SUN50I_H616_ODT_EN=0x00000001 +CONFIG_DRAM_SUN50I_H616_TPR0=0x0 +CONFIG_DRAM_SUN50I_H616_TPR2=0x00000000 +CONFIG_DRAM_SUN50I_H616_TPR10=0x002f3359 +CONFIG_DRAM_SUN50I_H616_TPR11=0xaa889967 +CONFIG_DRAM_SUN50I_H616_TPR12=0xeeee8979 +CONFIG_MACH_SUN50I_H616=y +CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_R_I2C_ENABLE=y +CONFIG_SPL_I2C=y +CONFIG_SPL_I2C_SUPPORT=y +CONFIG_SPL_SYS_I2C_LEGACY=y +CONFIG_SYS_I2C_MVTWSI=y +CONFIG_SYS_I2C_SLAVE=0x7f +CONFIG_SYS_I2C_SPEED=100000 +CONFIG_SYS_MONITOR_LEN=786432 +CONFIG_PHY_REALTEK=y +CONFIG_SUN8I_EMAC=y +CONFIG_I2C3_ENABLE=y +CONFIG_AXP313_POWER=y +CONFIG_AXP_DCDC3_VOLT=1200 +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_MUSB_GADGET=y +CONFIG_SUPPORT_EMMC_BOOT=y +CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x40 +# custom additions +CONFIG_X96Q_BINARY_BUILD=y +CONFIG_IDENT_STRING=" binary build" diff --git a/overlays/dts/sun50i-h313-x96q-lpddr3-v1.3.dts b/overlays/dts/sun50i-h313-x96q-lpddr3-v1.3.dts new file mode 100644 index 0000000..5e41e50 --- /dev/null +++ b/overlays/dts/sun50i-h313-x96q-lpddr3-v1.3.dts @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Author: piotr.oniszczuk@gmail.com + * X96Q LPDDR3 v1.3 board + */ + +/dts-v1/; + +#include "sun50i-h616.dtsi" + +#include +#include +#include + +/ { + model = "hechuang,x96-q LPDDR3 v1.3"; + compatible = "hechuang,x96-q", "allwinner,sun50i-h616"; + + aliases { + mmc0 = &mmc0; + mmc1 = &mmc1; + mmc2 = &mmc2; + ethernet1 = &wlan; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led-red { + function = LED_FUNCTION_DISK_ACTIVITY; + color = ; + gpios = <&pio 7 6 GPIO_ACTIVE_HIGH>; /* PH6 */ + linux,default-trigger = "mmc0"; + }; + }; + + reg_vcc5v: vcc5v { + compatible = "regulator-fixed"; + regulator-name = "vcc-5v"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; + + reg_vcc_wifi: reg_vcc_wifi { + compatible = "regulator-fixed"; + regulator-name = "vcc-wifi"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&pio 6 18 GPIO_ACTIVE_HIGH>; /* PG18 WL_REG_ON */ + regulator-always-on; + enable-active-high; + status = "okay"; + }; + + wifi_pwrseq: wifi_pwrseq { + compatible = "mmc-pwrseq-simple"; + clocks = <&rtc CLK_OSC32K_FANOUT>; + clock-names = "ext_clock"; + }; +}; + +&cpu0 { + cpu-supply = <®_dcdc2>; + status = "okay"; +}; + +&cpu1 { + cpu-supply = <®_dcdc2>; + status = "okay"; +}; + +&cpu2 { + cpu-supply = <®_dcdc2>; + status = "okay"; +}; + +&cpu3 { + cpu-supply = <®_dcdc2>; + status = "okay"; +}; + +&mmc0 { + vmmc-supply = <®_dldo2>; + broken-cd; + bus-width = <4>; + status = "okay"; +}; + +&mmc1 { + vmmc-supply = <®_dldo2>; + vqmmc-supply = <®_vcc_wifi>; + mmc-pwrseq = <&wifi_pwrseq>; + bus-width = <4>; + non-removable; + status = "okay"; + + wlan: wifi@1 { + reg = <1>; + interrupt-parent = <&pio>; + interrupts = <6 15 IRQ_TYPE_EDGE_RISING>; /* PG15 WL_HOSTWAKE */ + interrupt-names = "host-wake"; + }; +}; + +&mmc2 { + vmmc-supply = <®_dldo2>; + bus-width = <8>; + non-removable; + cap-mmc-hw-reset; + status = "okay"; +}; + +&r_i2c { + status = "okay"; + + axp313a: pmic@36 { + compatible = "x-powers,axp313a"; + reg = <0x36>; + wakeup-source; + + regulators { + reg_dcdc1: dcdc1 { + regulator-always-on; + regulator-min-microvolt = <1160000>; + regulator-max-microvolt = <1160000>; + regulator-name = "vdd-cpu"; + }; + + reg_dcdc2: dcdc2 { + regulator-always-on; + regulator-min-microvolt = <1160000>; + regulator-max-microvolt = <1160000>; + regulator-name = "vdd-gpu-sys"; + }; + + reg_dcdc3: dcdc3 { + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-name = "vdd-dram"; + }; + + reg_aldo1: ldo1 { + regulator-always-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-name = "vcc-sys"; + }; + + reg_dldo2: ldo2 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vcc3v3-ext"; + }; + }; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_ph_pins>; + status = "okay"; +}; diff --git a/patches/easter-egg.patch b/patches/easter-egg.patch new file mode 100644 index 0000000..8256a52 --- /dev/null +++ b/patches/easter-egg.patch @@ -0,0 +1,31 @@ +diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig +index 8065161e6..b92c0402c 100644 +--- a/arch/arm/mach-sunxi/Kconfig ++++ b/arch/arm/mach-sunxi/Kconfig +@@ -1130,6 +1130,12 @@ config BLUETOOTH_DT_DEVICE_FIXUP + The used address is "bdaddr" if set, and "ethaddr" with the LSB + flipped elsewise. + ++config X96Q_BINARY_BUILD ++ bool "Print 'binary build' easter egg on UART in SPL" ++ default n ++ help ++ Print a short banner on the serial console early in SPL boot. ++ + source "board/sunxi/Kconfig" + + endif +diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c +index 701899ee4..7b6e9ee9c 100644 +--- a/arch/arm/mach-sunxi/board.c ++++ b/arch/arm/mach-sunxi/board.c +@@ -470,6 +470,9 @@ void board_init_f(ulong dummy) + + spl_init(); + preloader_console_init(); ++#ifdef CONFIG_X96Q_BINARY_BUILD ++ puts("*** binary build ***\n"); ++#endif + + #if CONFIG_IS_ENABLED(I2C) && CONFIG_IS_ENABLED(SYS_I2C_LEGACY) + /* Needed early by sunxi_board_init if PMU is enabled */ diff --git a/patches/fdtfile-armbian.patch b/patches/fdtfile-armbian.patch new file mode 100644 index 0000000..d522368 --- /dev/null +++ b/patches/fdtfile-armbian.patch @@ -0,0 +1,15 @@ +diff --git a/board/sunxi/board.c b/board/sunxi/board.c +--- a/board/sunxi/board.c ++++ b/board/sunxi/board.c +@@ -826,6 +826,10 @@ int misc_init_r(void) + char *prefix = IS_ENABLED(CONFIG_ARM64) ? "allwinner/" : ""; + char str[64]; + + snprintf(str, sizeof(str), "%s%s.dtb", prefix, spl_dt_name); ++#ifdef CONFIG_X96Q_BINARY_BUILD ++ /* Armbian ships sun50i-h313-x96q-lpddr3.dtb, not the v1.3 suffix */ ++ snprintf(str, sizeof(str), "%ssun50i-h313-x96q-lpddr3.dtb", prefix); ++#endif + env_set("fdtfile", str); + } + diff --git a/patches/jernejsk-emmc.patch b/patches/jernejsk-emmc.patch new file mode 100644 index 0000000..ec13049 --- /dev/null +++ b/patches/jernejsk-emmc.patch @@ -0,0 +1,122 @@ +commit 3e78f8f407a0a0e7b50aa7eaa6f2f579f35e9837 +Author: Jernej Skrabec +Date: Sun Mar 9 07:12:41 2025 +0100 + + sunxi: mmc: Improve reset procedure + + Cards should always be reset and threshold set. This fixes eMMC on H616. + + Signed-off-by: Jernej Skrabec + [Andre: use macro-defined offsets to fix build on older SoCs] + Signed-off-by: Andre Przywara + +diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c +index 8f72d758e..951e6acd3 100644 +--- a/drivers/mmc/sunxi_mmc.c ++++ b/drivers/mmc/sunxi_mmc.c +@@ -449,6 +449,26 @@ out: + return error; + } + ++static void sunxi_mmc_reset(void *regs) ++{ ++ /* Reset controller */ ++ writel(SUNXI_MMC_GCTRL_RESET, regs + SUNXI_MMC_GCTRL); ++ udelay(1000); ++ ++ if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) { ++ /* Reset card */ ++ writel(SUNXI_MMC_HWRST_ASSERT, regs + SUNXI_MMC_HWRST); ++ udelay(10); ++ writel(SUNXI_MMC_HWRST_DEASSERT, regs + SUNXI_MMC_HWRST); ++ udelay(300); ++ ++ /* Setup FIFO R/W threshold. Needed on H616. */ ++ writel(SUNXI_MMC_THLDC_READ_THLD(512) | ++ SUNXI_MMC_THLDC_WRITE_EN | ++ SUNXI_MMC_THLDC_READ_EN, regs + SUNXI_MMC_THLDC); ++ } ++} ++ + /* non-DM code here is used by the (ARM) SPL only */ + + #if !CONFIG_IS_ENABLED(DM_MMC) +@@ -496,9 +516,7 @@ static int sunxi_mmc_core_init(struct mmc *mmc) + { + struct sunxi_mmc_priv *priv = mmc->priv; + +- /* Reset controller */ +- writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); +- udelay(1000); ++ sunxi_mmc_reset(priv->reg); + + return 0; + } +@@ -691,9 +709,7 @@ static int sunxi_mmc_probe(struct udevice *dev) + + upriv->mmc = &plat->mmc; + +- /* Reset controller */ +- writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); +- udelay(1000); ++ sunxi_mmc_reset(priv->reg); + + return 0; + } +diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h +index f4ae5a790..718651603 100644 +--- a/drivers/mmc/sunxi_mmc.h ++++ b/drivers/mmc/sunxi_mmc.h +@@ -37,7 +37,9 @@ struct sunxi_mmc { + u32 res0; /* 0x54 reserved */ + u32 a12a; /* 0x58 Auto command 12 argument */ + u32 ntsr; /* 0x5c New timing set register */ +- u32 res1[8]; ++ u32 res1[6]; ++ u32 hwrst; /* 0x78 Hardware Reset */ ++ u32 res5; + u32 dmac; /* 0x80 internal DMA control */ + u32 dlba; /* 0x84 internal DMA descr list base address */ + u32 idst; /* 0x88 internal DMA status */ +@@ -46,7 +48,8 @@ struct sunxi_mmc { + u32 cbda; /* 0x94 */ + u32 res2[26]; + #if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2) +- u32 res3[17]; ++ u32 thldc; /* 0x100 Threshold control */ ++ u32 res3[16]; + u32 samp_dl; + u32 res4[46]; + #endif +@@ -57,6 +60,7 @@ struct sunxi_mmc { + #define SUNXI_MMC_CLK_ENABLE (0x1 << 16) + #define SUNXI_MMC_CLK_DIVIDER_MASK (0xff) + ++#define SUNXI_MMC_GCTRL 0x000 + #define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0) + #define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1) + #define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2) +@@ -123,6 +127,10 @@ struct sunxi_mmc { + + #define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31) + ++#define SUNXI_MMC_HWRST 0x078 ++#define SUNXI_MMC_HWRST_ASSERT (0x0 << 0) ++#define SUNXI_MMC_HWRST_DEASSERT (0x1 << 0) ++ + #define SUNXI_MMC_IDMAC_RESET (0x1 << 0) + #define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1) + #define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7) +@@ -133,6 +141,12 @@ struct sunxi_mmc { + #define SUNXI_MMC_COMMON_CLK_GATE (1 << 16) + #define SUNXI_MMC_COMMON_RESET (1 << 18) + ++#define SUNXI_MMC_THLDC 0x100 ++#define SUNXI_MMC_THLDC_READ_EN (0x1 << 0) ++#define SUNXI_MMC_THLDC_BSY_CLR_INT_EN (0x1 << 1) ++#define SUNXI_MMC_THLDC_WRITE_EN (0x1 << 2) ++#define SUNXI_MMC_THLDC_READ_THLD(x) (((x) & 0xfff) << 16) ++ + #define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7) + + #endif /* _SUNXI_MMC_H */ diff --git a/scripts/flash-emmc.sh b/scripts/flash-emmc.sh new file mode 100755 index 0000000..79c7279 --- /dev/null +++ b/scripts/flash-emmc.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# Прошивка eMMC на X96Q — запускать на приставке под root +# Usage: sudo ./flash-emmc.sh [образ.img.xz] +set -euo pipefail + +DIR="$(cd "$(dirname "$0")" && pwd)" +UBOOT="${UBOOT:-${DIR}/../output/u-boot-custom.bin}" +IMG_XZ="${1:-}" + +if [[ $EUID -ne 0 ]]; then + echo "Запустите: sudo $0 [образ.img.xz]" + exit 1 +fi + +if [[ -z "$IMG_XZ" ]]; then + shopt -s nullglob + imgs=( "$DIR"/*.img.xz ) + if [[ ${#imgs[@]} -eq 0 ]]; then + echo "Нет .img.xz в $DIR" + exit 1 + elif [[ ${#imgs[@]} -eq 1 ]]; then + IMG_XZ="${imgs[0]}" + else + echo "Несколько образов — укажите явно:" + ls -lh "${imgs[@]}" + exit 1 + fi +fi + +[[ -f "$IMG_XZ" ]] || { echo "Нет файла: $IMG_XZ"; exit 1; } +[[ -f "$UBOOT" ]] || { echo "Нет $UBOOT"; exit 1; } + +EMMC=/dev/mmcblk2 +BOOT0=/dev/mmcblk2boot0 +BOOT1=/dev/mmcblk2boot1 + +[[ -b "$EMMC" ]] || { echo "eMMC не найден: $EMMC"; exit 1; } + +unlock_boot() { + local ro + for part in boot0 boot1; do + ro="/sys/block/mmcblk2${part}/force_ro" + [[ -w "$ro" ]] || continue + echo 0 >"$ro" + echo "mmcblk2${part}: force_ro=$(cat "$ro")" + done +} + +echo "=== eMMC flash (X96Q) ===" +echo "Образ: $IMG_XZ ($(du -h "$IMG_XZ" | cut -f1))" +echo "U-Boot: $UBOOT" +echo "Цель: $EMMC + $BOOT0 + $BOOT1" +lsblk -o NAME,SIZE,TYPE,MOUNTPOINT 2>/dev/null | grep -E 'mmcblk|NAME' || lsblk + +echo "[1/4] Запись образа на $EMMC ..." +xz -dc "$IMG_XZ" | dd of="$EMMC" bs=4M status=progress conv=fsync +sync + +echo "[2/4] U-Boot → user area @ 8 KiB ..." +dd if="$UBOOT" of="$EMMC" bs=1024 seek=8 conv=fsync + +echo "Разблокировка boot0/boot1 (force_ro) ..." +unlock_boot + +echo "[3/4] U-Boot → boot0 ..." +dd if="$UBOOT" of="$BOOT0" bs=1024 conv=fsync + +echo "[4/4] U-Boot → boot1 ..." +dd if="$UBOOT" of="$BOOT1" bs=1024 conv=fsync +sync + +# Armbian DTB: в образе sun50i-h313-x96q-lpddr3.dtb, не v1.3 +MNT="/tmp/emmc-fix" +mkdir -p "$MNT" +if mount "${EMMC}p1" "$MNT"; then + ENV="${MNT}/boot/armbianEnv.txt" + DTB="${MNT}/boot/dtb/allwinner" + if [[ -f "$ENV" ]]; then + if grep -q '^fdtfile=' "$ENV"; then + sed -i 's/^fdtfile=.*/fdtfile=sun50i-h313-x96q-lpddr3.dtb/' "$ENV" + else + echo 'fdtfile=sun50i-h313-x96q-lpddr3.dtb' >>"$ENV" + fi + fi + if [[ -d "$DTB" && -f "${DTB}/sun50i-h313-x96q-lpddr3.dtb" ]]; then + ln -sf sun50i-h313-x96q-lpddr3.dtb "${DTB}/sun50i-h313-x96q-lpddr3-v1.3.dtb" + fi + sync + umount "$MNT" +fi +rmdir "$MNT" 2>/dev/null || true + +echo "Готово. Выключите, выньте SD, включите — загрузка с eMMC." diff --git a/scripts/flash-uboot-only.sh b/scripts/flash-uboot-only.sh new file mode 100755 index 0000000..c66e7dc --- /dev/null +++ b/scripts/flash-uboot-only.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Только U-Boot на eMMC (без перезаписи образа) — запускать на приставке под root +set -euo pipefail + +DIR="$(cd "$(dirname "$0")" && pwd)" +UBOOT="${1:-${DIR}/../output/u-boot-custom.bin}" + +if [[ $EUID -ne 0 ]]; then + echo "Запустите: sudo $0 [u-boot-custom.bin]" + exit 1 +fi + +[[ -f "$UBOOT" ]] || { echo "Нет файла: $UBOOT"; exit 1; } + +EMMC=/dev/mmcblk2 +BOOT0=/dev/mmcblk2boot0 +BOOT1=/dev/mmcblk2boot1 + +echo "=== U-Boot only → $EMMC ===" +echo "Бинарник: $UBOOT" + +echo 0 >/sys/block/mmcblk2boot0/force_ro +echo 0 >/sys/block/mmcblk2boot1/force_ro + +dd if="$UBOOT" of="$EMMC" bs=1024 seek=8 conv=fsync +dd if="$UBOOT" of="$BOOT0" bs=1024 conv=fsync +dd if="$UBOOT" of="$BOOT1" bs=1024 conv=fsync +sync + +echo "Готово."