6 분 소요

지난 글에서 벤더 BSP를 우리 캐리어보드에 맞춰 디바이스 트리를 새로 썼다. 컴파일도 되고 역컴파일로 병합 결과도 확인했지만, 실물 보드에서는 한 번도 안 돌려봤다.

이번엔 그걸 올린다. 목표는 두 가지였다.

  • .dtb로 보드를 부팅시킨다
  • 실패해도 원래 상태로 돌아올 수 있어야 한다

두 번째가 더 중요했다. 개발용 보드가 한 대뿐이라 못 쓰게 되면 작업이 멈춘다.


안전망을 먼저 설계한다

보드는 eMMC에서 부팅하고 있었다. 거기 있는 .dtb를 갈아끼우면 가장 간단하지만, 잘못되면 부팅이 안 되고 복구가 번거롭다.

대신 SD 카드로 부팅하는 시스템을 따로 만들기로 했다. STM32MP1은 BOOT0/1/2 핀으로 부팅 소스를 고르는데, 이 보드엔 그 핀들이 DIP 스위치에 물려 있었다.

BOOT0 ──[SW1-1]── R83(1K) ──┐
BOOT1 ──[SW1-2]── R84(1K) ──┼── +3.3V
BOOT2 ──[SW1-3]── R86(1K) ──┘

스위치를 닫으면 3.3V로 풀업돼서 1, 열면 모듈 내부 풀다운으로 0. 회로도와 넷리스트로 확인한 내용이다.

그러면 이런 구도가 된다.

스위치 = SD 부팅   →  SD의 리눅스로 실험     (eMMC는 마운트조차 안 됨)
스위치 = eMMC 부팅 →  원래 시스템으로 복귀

스위치 하나로 되돌아갈 수 있다. 이게 있으면 나머지는 마음 편하게 할 수 있다.

시작 전에 지금 스위치 위치를 사진으로 찍어뒀다. 매뉴얼에 SD 부팅 조합(1,0,1)과 다운로드 조합(0,0,0)은 있는데 eMMC 조합은 없었다. 지금 위치가 곧 eMMC 조합이니, 그게 복귀 경로다.


문서와 배포물이 3년 어긋나 있었다

벤더 매뉴얼의 “TF Card Starter” 절을 폈다.

Flash tool Win32DiskImager-1.0.0-binary
flashlayout_myir-image-full_FlashLayout_sdcard_...-trusted.zip

zip을 받아서 풀고, 나온 이미지를 Win32DiskImager로 SD에 구우라고 되어 있었다. 그런데 zip을 풀었더니 이런 게 나왔다.

arm-trusted-firmware/          부트로더 조각
bootloader/                    U-Boot 조각
flashlayout_.../*.tsv          CubeProgrammer 설정
myir-image-full-...ext4        1.15GB  파일시스템 조각
st-image-bootfs-...ext4          64MB  파일시스템 조각

통짜 디스크 이미지가 없다. Win32DiskImager에 넣을 게 없었다.

벤더 사이트를 직접 열어보니 답이 있었다.

  날짜
매뉴얼 (V2.1) 2021-04-10
사이트 파일들 2024-07-30

3년 사이에 배포 방식이 바뀌어 있었다. 예전엔 zip 하나로 묶었는데, 지금은 그 내용물을 폴더에 펼쳐서 개별 다운로드하게 바뀌었다. 그리고 그 안에 완성본이 따로 있었다.

FlashLayout_sdcard_stm32mp157c-ya157c-512d-v3-trusted.raw    1.50GB

확장자가 .raw였다. 매뉴얼은 .img라고 불렀지만 같은 것이다 — Yocto 툴체인이 붙이는 이름과 매뉴얼의 표현이 다를 뿐. Win32DiskImager에서 파일 형식을 *.*로 바꾸면 보인다.

전에 누군가 이 폴더에서 파일을 받아뒀는데 1.5GB짜리 완성본만 빼고 받아둔 상태였다. 조각들만 있으니 “이걸로 뭘 하라는 거지” 싶었던 것이다.

문서가 낡았을 때, 문서를 의심하기보다 파일 날짜를 먼저 보면 빠르다.


이 보드엔 USB 콘솔이 없었다

이미지를 굽고 스위치를 1,0,1로 맞췄다. 이제 콘솔이 필요한데 — USB 케이블을 꽂아도 시리얼 포트가 안 잡혔다.

BOM을 뒤졌다.

CP2102 / FT232 / CH340 / PL2303   →  0건

USB-UART 브릿지 칩이 아예 없다. 넷리스트로 콘솔 경로를 따라가 보니 이렇게 나갔다.

PG11 (UART4_TX) ──DEBUG_TX──► U6 SP3232 ──► RS232 레벨 ──► J3 커넥터
PB2  (UART4_RX) ◄─DEBUG_RX── U6 SP3232 ◄── RS232 레벨 ◄──

SP3232RS232 레벨 변환기(±12V)다. USB가 아니라 정통 시리얼로 나간다. 흔히 쓰는 USB-TTL 모듈(3.3V)을 여기 꽂으면 레벨이 안 맞는다.

개발보드를 쓰다 양산 보드로 넘어오면 이런 게 바뀌어 있다. 콘솔이 어떻게 나오는지는 보드마다 확인해야 한다.


SSH가 거부당했다

콘솔이 붙고 SD에서 부팅됐다. 커널 버전을 확인했다.

root@myir:~# uname -r
5.4.31

내 소스 트리와 정확히 같다. 커널을 새로 빌드할 필요 없이 .dtb만 복사하면 된다는 뜻이라 반가웠다.

USB 이더넷도 올라와 있었다.

inet 192.168.7.2/24 brd 192.168.7.255 scope global usb0

scp로 보내려 했더니 거부당했다.

Unable to negotiate ... no matching host key type found. Their offer: ssh-rsa

보드의 SSH 서버가 2021년산이라 ssh-rsa(SHA-1) 호스트키만 제공하는데, 요즘 OpenSSH 클라이언트는 이걸 기본으로 거부한다. 옵션 두 개로 풀린다.

scp -O \
  -o HostKeyAlgorithms=+ssh-rsa \
  -o PubkeyAcceptedAlgorithms=+ssh-rsa \
  stm32mp157a-sc6-v1.dtb root@192.168.7.2:/boot/

+가 중요하다. “기존 목록에 추가“라는 뜻이고, 빼면 그것만 쓰겠다는 뜻이 돼서 다른 데서 막힌다.

매번 치기 번거로워서 ~/.ssh/config에 넣었다.

Host 192.168.7.2
    User root
    HostKeyAlgorithms +ssh-rsa
    PubkeyAcceptedAlgorithms +ssh-rsa
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

StrictHostKeyChecking no보드를 다시 구울 때마다 호스트키가 바뀌기 때문이다. 개발 보드에서는 이게 현실적이다.


extlinux.conf가 두 개였다

여기서 제일 오래 걸렸다.

부팅 설정을 찾았더니 두 개가 나왔다.

root@myir:~# find /boot -name "extlinux.conf"
/boot/mmc0_extlinux/extlinux.conf
/boot/mmc1_extlinux/extlinux.conf

SD로 부팅했으니 mmc0 쪽이겠거니 하고 거기에 우리 항목을 추가했다. 재부팅.

root@myir:~# cat /proc/device-tree/model
MYIR YA157C v2

안 바뀌었다. 벤더 dtb 그대로다.

부팅 로그를 처음부터 읽었더니 답이 있었다.

Boot over mmc0!
Scanning mmc 0:4...
Found /mmc0_extlinux/stm32mp157c-ya157c-v2_extlinux.conf
Retrieving file: /mmc0_extlinux/stm32mp157c-ya157c-v2_extlinux.conf
726 bytes read in 38 ms

U-Boot이 읽은 건 extlinux.conf가 아니라 stm32mp157c-ya157c-v2_extlinux.conf 였다. 보드 이름이 앞에 붙은 파일이 같은 폴더에 따로 있었고, U-Boot은 그쪽을 우선한다.

내가 find로 찾을 때 -name "extlinux.conf"로 정확히 일치하는 것만 찾아서 이 파일이 안 걸렸다.

그리고 로그에 메뉴도 찍혀 있었다.

Select the boot mode
1:      stm32mp157c-ya157c-v2
2:      stm32mp157c-ya157c-hdmi-v2
3:      stm32mp157c-ya157c-lcd-v2
Enter choice: 2:        stm32mp157c-ya157c-hdmi-v2
Retrieving file: /stm32mp157c-ya157c-hdmi-v2.dtb

기본값이 HDMI 버전으로 잡혀 있어서 그걸로 뜨고 있었던 것이다.

dtb가 안 바뀌면 파일을 의심하기 전에 U-Boot이 무슨 파일을 읽었는지부터 보자. 로그가 Retrieving file:로 다 찍어준다.

올바른 파일에 항목을 덧붙였다. 기존 세 개는 그대로 두고 맨 뒤에만 추가했다.

cat >> /boot/mmc0_extlinux/stm32mp157c-ya157c-v2_extlinux.conf << 'EOF'

LABEL sc6
        KERNEL /uImage
        FDT /stm32mp157a-sc6-v1.dtb
        INITRD /uInitrd
        APPEND root=PARTUUID=e91c4e10-... rootwait rw console=ttySTM0,115200
EOF

FDT로 파일을 직접 지정한 게 요점이다. 벤더 항목들은 FDTDIR /를 쓰는데, 그건 U-Boot이 환경변수 fdtfile로 파일명을 정해 그 폴더에서 찾는 방식이다. 우리 파일 이름을 U-Boot이 알 리가 없으니 직접 지정해야 한다.

DEFAULT는 건드리지 않았다. 그러면 아무것도 안 눌렀을 때 원래대로 부팅되니, 롤백이 기본 동작이 된다.

2초는 너무 짧다

TIMEOUT 20

단위가 1/10초라 2초다. 시리얼 콘솔에서 2초 안에 메뉴를 고르는 건 사실상 불가능하다. 10초로 늘렸다.

sed -i 's/^TIMEOUT .*/TIMEOUT 100/' /boot/mmc0_extlinux/stm32mp157c-ya157c-v2_extlinux.conf

재부팅하고 메뉴에서 4를 눌렀다.


보드가 내 모델명으로 떴다

root@myir:~# cat /proc/device-tree/model
KNV SC6_0 (MYC-YA157C-V2)

내가 DTS에 적은 문자열이다.

가장 궁금했던 것부터

root@myir:~# dmesg | grep -i vrefbuf
  (메시지 없음)

이게 이번 검증의 핵심이었다.

이 보드는 ADC 기준전압을 2.5V로 설계했다. 그런데 기준전압은 STM32 내부 VREFBUF가 만들고, 그 출력이 VREF+ 핀을 직접 구동한다. 만약 SoM 모듈이 VREF+를 3.3V에 묶어놨다면 VREFBUF가 레귤레이션에 실패한다. VREF+는 164핀 커넥터로 안 나와서 모듈 회로도 없이는 확인할 방법이 없었다.

커널 드라이버가 대신 확인해준다.

/* drivers/regulator/stm32-vrefbuf.c */
ret = readl_poll_timeout(base + CSR, val, val & VRR, 650, 10000);
if (ret)
        dev_err(&rdev->dev, "stm32 vrefbuf timed out!\n");

전압 도달 완료 비트를 폴링하다가 10ms 안에 안 되면 에러를 찍는다. 조용하다는 건 2.5V를 정상 확보했다는 뜻이다.

만약 이게 틀렸다면 부팅도 정상, 로그도 깨끗한데 모든 ADC 값만 32% 어긋난다. 그리고 그 채널이 하필 충전 프로토콜의 상태 판정에 쓰이는 채널이었다.

나머지

root@myir:~# ls /sys/class/pwm/
pwmchip0  pwmchip4  pwmchip8  pwmchip12

root@myir:~# i2cdetect -l
i2c-0   i2c   STM32F7 I2C(0x5c002000)   I2C adapter
i2c-1   i2c   STM32F7 I2C(0x5c009000)   I2C adapter

root@myir:~# ls /sys/class/leds/
bled  dout1  dout2  dout3  dout4  rled

root@myir:~# ls /dev/ttySTM*
/dev/ttySTM0  /dev/ttySTM1  /dev/ttySTM2  /dev/ttySTM3

I2C가 두 개만 나오는 게 맞다. 나머지 둘은 의도적으로 껐다 — 벤더가 켜둔 I2C2가 우리 보드에서는 AC 검출 입력으로 쓰는 핀을 물고 있었다.

시리얼이 4개인 것도 정상이다. UART7에는 블루투스 자식 노드가 달려 있어서 serdev로 직접 붙고 /dev/ttySTM으로는 안 나온다.

lsblk로 eMMC 상태도 확인했다.

mmcblk1      14.9G   /boot /vendor / /usr/local    ← SD (부팅 중)
mmcblk2       7.3G   (마운트 안 됨)                ← eMMC
mmcblk2boot0 31.9M
mmcblk2boot1 31.9M

mmcblk2boot0/boot1eMMC에만 있는 하드웨어 부트 파티션이다. SD 카드에는 없다. 이걸로 둘을 구분할 수 있다.

eMMC는 마운트조차 되지 않았다. 안전망이 의도대로 작동했다.


compatible을 바꾸면 딸려오는 것

로그에 에러가 하나 남아 있었다.

brcmfmac: Direct firmware load for
  brcm/brcmfmac43430-sdio.knv,stm32mp157a-sc6.txt failed with error -2

WiFi 드라이버가 보드의 compatible 문자열로 펌웨어 설정 파일을 찾는다. 나는 DTS에 이렇게 썼다.

compatible = "knv,stm32mp157a-sc6", "myir,stm32mp157c-ya157c-v2", "st,stm32mp157";

가장 앞 문자열이 바뀌었으니 찾는 파일 이름도 바뀌었고, 그런 파일은 없다. 심볼릭 링크 하나면 풀린다.

cd /lib/firmware/brcm
ln -s brcmfmac43430-sdio.<기존이름>.txt "brcmfmac43430-sdio.knv,stm32mp157a-sc6.txt"

사소하지만 compatible을 바꾸면 커널 밖에도 영향이 간다는 걸 보여주는 사례라 적어둔다. 펌웨어 파일명, udev 규칙, 유저 공간 설정 같은 것들이 이 문자열을 참조하고 있을 수 있다.


되돌아보며

이번에 시간을 잡아먹은 것들은 전부 문서와 실물이 어긋난 자리였다.

문서 실제
.zip을 받아 .img를 굽는다 폴더에 펼쳐진 .raw
USB로 콘솔 RS232 (USB-UART 칩 없음)
scp로 복사 레거시 SSH 알고리즘 허용 필요
extlinux.conf <보드이름>_extlinux.conf 우선

하나하나는 별것 아닌데, 모르면 각각 30분씩 사라진다.

그리고 안전망을 먼저 만든 게 옳았다. eMMC를 안 건드리는 구성으로 시작했기 때문에, 위 네 가지를 헤매는 동안에도 “망가뜨리면 어쩌지”를 걱정할 필요가 없었다. 스위치 하나면 원래대로 돌아갔으니까.

브링업에서는 되돌릴 방법을 먼저 확보하고 시작하는 게 결국 더 빠르다.

다음은 PLC 모뎀 드라이버를 모듈로 올리고, DIN/DOUT 극성을 실측으로 확인하는 차례다.

댓글남기기