Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ git diff --diff-filter=M v2022.10 HEAD # the mainline files we patched
| SoC: PLL and DDR bring-up, timer, reset, DRAM size, SDHCI glue | `arch/arm/mach-xm/` (`ARCH_XM`; `TARGET_XM510`/`XM530`/`XM550`; register map in `include/mach/xm.h`) |
| Boards: `board_init`, `misc_init_r` → `openipc_helper()`, `board_eth_init` with the PHY reset / MAC glue | `board/xiongmai/{xm510,xm530}/`. XM550 uses the xm530 board with `XM_DDR3` |
| Memory map, console, timer | `include/configs/{xm510,xm530}.h`, each ending in `#include "openipc-common.h"` |
| SPI NAND on the v2 controller (XM530/550): raw NAND, full-ID table, on-die ECC status through `ecc.read_page` | `drivers/mtd/nand/raw/xmsfc_nand.c` (`NAND_XMSFC`) |
| SPI NOR controller, v1 (XM510) and v2 (XM530/550), non-DM | `drivers/mtd/spi/xmsfc.c`. It provides `spi_flash_probe()` and fills `mtd->_read/_write/_erase`, so mainline's non-DM `spi_flash_*()` wrappers, `sf` and env-in-SPI-flash work without the SPI and spi-nor layers |
| OpenIPC boot layer | `common/openipc.c` and `include/openipc.h` (copied from `openipc-hisilicon`, NAND gate rekeyed to `CONFIG_ENV_IS_IN_NAND`), `include/configs/openipc-common.h` (XM-only rewrite, same env commands) |
| Kconfig fragments and the build | `openipc/`, `build.sh` |
Expand All @@ -50,6 +51,7 @@ git diff --diff-filter=M v2022.10 HEAD # the mainline files we patched

- `arch/arm/{Kconfig,Makefile}`: the `ARCH_XM` hook.
- `common/{Kconfig,Makefile}`: `OPENIPC`, `PRODUCT_SOC`, `PRODUCT_SOCMODEL`.
- `drivers/mtd/nand/raw/{Kconfig,Makefile}`: the `NAND_XMSFC` hook.
- `drivers/mtd/spi/{Kconfig,Makefile}`, `cmd/Kconfig` (`CMD_SF`) and
`env/Kconfig` (`ENV_IS_IN_SPI_FLASH`): accept `XMSFC_SPI_NOR`.
- `drivers/net/Kconfig`: `NETDEVICES` selects `DM_ETH` only when `DM` is on.
Expand Down Expand Up @@ -94,17 +96,46 @@ position-independent fixups. The only RAM before DDR is a 4 KiB SRAM at
driver restores it (`xmsfc_bus_mode()`) after every v2 operation, and only
ever runs after relocation, from DDR.

## SPI NAND (XM530/XM550): built on a guess

Xiongmai never shipped a NAND U-Boot from this SDK. There is a
`CONFIG_SYS_USE_SPI_NAND` block in the vendor `xm530.h`, but no board target,
and nothing documents how the boot ROM starts from SPI NAND.

**What the NAND image assumes:**

- the controller's window serves the NAND page data linearly, as in NOR
bus-read mode;
- U-Boot is linked at 0x60001000 (the vendor's NAND `TEXT_BASE`), behind a
4 KiB header;
- the header's first word is `ldr pc, [pc, #-4]` followed by that address, so
it reaches U-Boot whether the ROM starts at the window or the 0x08000000
alias (`build.sh`).

**What it carries:**

- layout `768k(boot),256k(env),-(ubi)`;
- the environment at 0xc0000;
- the kernel at `/boot/uImage` inside the UBIFS `rootfs` volume;
- the same recovery as on `openipc-hisilicon` NAND.

Only qemu-hisilicon's NAND model, which implements the same guess, has run
it. The OpenIPC XM530 kernel has no SPI NAND driver either, so a NAND image
loads and starts the kernel, which then cannot mount its root. Treat NAND as
unverified on silicon, and say so wherever it is offered.

## Build

```sh
bash build.sh # xm510 xm530 xm550; continues past failures, exits non-zero if any failed
bash build.sh xm530 # one or more SoCs (CROSS_COMPILE defaults to arm-linux-gnueabi-)
```

- Output: `output/u-boot-<soc>-nor.bin`, built out of tree in
`output/build-<soc>/`.
- `.config` is `openipc/openipc_config` followed by `openipc/<soc>_config`, then
`olddefconfig`. A later line wins.
- Output: `output/u-boot-<soc>-nor.bin`, plus `-nand.bin` for xm530/xm550,
built out of tree in `output/build-<soc>-<nor|nand>/`.
- `.config` is `openipc/openipc_config`, then `openipc/<soc>_config`, then
`openipc/nand_config` for NAND images, then `olddefconfig`. A later line
wins. The "override" warnings are that layering.
- There are no `configs/*_defconfig` for these targets.
- The host toolchain must match CI (Ubuntu `gcc-arm-linux-gnueabi`). A Docker
image with it works:
Expand All @@ -131,6 +162,13 @@ with the reset vector at the window or alias, exactly as silicon does.
bootloaders leave it, with per-block locks on. `xmsfc_unprotect()` must clear
it before writes land.

- **NAND:** `-global xm-sfc.nand-file=<raw image>`, with no `flash-file`.
- The raw image is pages of 2048 + 64 bytes, U-Boot at page 0, and
`ubinize`'s output at 1 MiB.
- It must reach the prompt, save the env, mount the rootfs and start the
kernel.
- From an erased NAND, TFTP recovery must create the UBI volumes and boot.

**What qemu does not check:** DDR training values, the v2 bus-read restore and
PHY/MDIO timing are acked instantly. A change to those needs a real board.

Expand Down
34 changes: 28 additions & 6 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,43 @@ mkconfig() {
# linked, with no compressed loader in front of it.
build_xm() {
soc=$1
builddir=${BOOT_SRCDIR}/output/build-${soc}
kind=$2
builddir=${BOOT_SRCDIR}/output/build-${soc}-${kind}
make_opts="-C ${BOOT_SRCDIR} O=${builddir} -j$(nproc)"
fragments="openipc/openipc_config openipc/${soc}_config"
[ "${kind}" = nand ] && fragments="${fragments} openipc/nand_config"

rm -rf "${builddir}"
mkconfig "${builddir}" openipc/openipc_config "openipc/${soc}_config" && \
mkconfig "${builddir}" ${fragments} && \
make ${make_opts} olddefconfig </dev/null >/dev/null && \
make ${make_opts} </dev/null && \
cp "${builddir}/u-boot.bin" "output/u-boot-${soc}-nor.bin" || return 1
make ${make_opts} </dev/null || return 1

if [ "${kind}" = nor ]; then
cp "${builddir}/u-boot.bin" "output/u-boot-${soc}-nor.bin" || return 1
else
# SPI NAND: a 4 KiB header, then U-Boot linked at the window + 4 KiB
# (CONFIG_SYS_TEXT_BASE in nand_config). Nothing documents what the
# boot ROM expects here; the header's first word jumps to U-Boot
# wherever the ROM starts it (ldr pc, [pc, #-4] and the address).
text=$(sed -n 's/^CONFIG_SYS_TEXT_BASE=//p' "${builddir}/.config")
python3 - "${builddir}/u-boot.bin" "output/u-boot-${soc}-nand.bin" \
"${text}" <<'PY' || return 1
import struct, sys
src, dst, text = sys.argv[1], sys.argv[2], int(sys.argv[3], 0)
hdr = bytearray(b'\xff' * 0x1000)
hdr[0:8] = struct.pack('<II', 0xe51ff004, text)
open(dst, 'wb').write(bytes(hdr) + open(src, 'rb').read())
Comment on lines +53 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Nand images enter code in the wrong state 🐞 Bug ≡ Correctness

build.sh prepends an ARM-state ldr pc instruction that jumps to the even CONFIG_SYS_TEXT_BASE,
while the linked entry code is built in Thumb state. When the ROM executes that header, the jump
reaches the image entry without switching instruction state, before U-Boot can initialize or
relocate.
Agent Prompt
## Issue description
The NAND header jumps in ARM state to a Thumb-built U-Boot entry, so the image cannot execute its entry code correctly.
## Fix Focus Areas
- build.sh[44-55]
- openipc/openipc_config[6-6]
## Recommended Fix
Make the header explicitly transfer into the instruction state used by the linked entry, and verify the resulting entry sequence for both proposed ROM addresses.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

PY
fi
}

build_soc() {
soc=$1
case "${soc}" in
xm510|xm530|xm550)
build_xm "${soc}" ;;
xm510)
build_xm "${soc}" nor ;;
xm530|xm550)
build_xm "${soc}" nor && build_xm "${soc}" nand ;;
*)
echo "unsupported SoC: ${soc}" >&2
return 1 ;;
Expand Down
9 changes: 9 additions & 0 deletions drivers/mtd/nand/raw/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,15 @@ config NAND_MXIC
help
This selects the Macronix raw NAND controller driver.

config NAND_XMSFC
bool "Xiongmai xmsfc v2 SPI NAND"
depends on ARCH_XM && XM_SFC_V2
select SYS_NAND_SELF_INIT
help
SPI NAND behind the XM530/XM550 flash controller, as a raw NAND
device: the controller runs the SPI NAND commands, the part's
on-die ECC corrects.

config NAND_ZYNQ
bool "Support for Zynq Nand controller"
select SPL_SYS_NAND_SELF_INIT
Expand Down
1 change: 1 addition & 0 deletions drivers/mtd/nand/raw/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_NAND_MXIC) += mxic_nand.o
obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
obj-$(CONFIG_NAND_XMSFC) += xmsfc_nand.o
obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.o
obj-$(CONFIG_CORTINA_NAND) += cortina_nand.o
obj-$(CONFIG_ROCKCHIP_NAND) += rockchip_nfc.o
Expand Down
Loading
Loading