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 <cursoragent@cursor.com>
This commit is contained in:
2026-06-05 05:22:22 +03:00
commit 359c107d36
11 changed files with 839 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# build artifacts
.build/
.venv/
output/u-boot-custom.bin
# macOS
.DS_Store
+203
View File
@@ -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 (см. заголовок файла).
Executable
+129
View File
@@ -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 "$@"
View File
@@ -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"
@@ -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 <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/leds/common.h>
/ {
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 = <LED_COLOR_ID_RED>;
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 = <&reg_dcdc2>;
status = "okay";
};
&cpu1 {
cpu-supply = <&reg_dcdc2>;
status = "okay";
};
&cpu2 {
cpu-supply = <&reg_dcdc2>;
status = "okay";
};
&cpu3 {
cpu-supply = <&reg_dcdc2>;
status = "okay";
};
&mmc0 {
vmmc-supply = <&reg_dldo2>;
broken-cd;
bus-width = <4>;
status = "okay";
};
&mmc1 {
vmmc-supply = <&reg_dldo2>;
vqmmc-supply = <&reg_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 = <&reg_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";
};
+31
View File
@@ -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 */
+15
View File
@@ -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);
}
+122
View File
@@ -0,0 +1,122 @@
commit 3e78f8f407a0a0e7b50aa7eaa6f2f579f35e9837
Author: Jernej Skrabec <jernej.skrabec@gmail.com>
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 <jernej.skrabec@gmail.com>
[Andre: use macro-defined offsets to fix build on older SoCs]
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
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 */
+93
View File
@@ -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."
+30
View File
@@ -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 "Готово."