STM32MP157系统移植(TF-A,U-Boot,Linux)

作者:zzssdd2
E-mail:zzssdd2@foxmail.com

〇 环境搭建

主机系统:Ubuntu 20.04.3 LTS
MPU型号:STM32MP157DAA1
参考官方板:STM32MP157D-EV1(STM32MP157D-ED1)
STM32MP1 Developer Package SDK : STM32MP15-Ecosystem-v2.1.0 release
STM32MP1 Developer Package SOURCES : STM32MP15-Ecosystem-v2.1.0 release

  1. 资源下载

    在官网地址STM32MP1 OpenSTLinux Developer Package下载开发包资源。

  2. SDK安装

    将下载的开发包资源en.SDK-x86_64-stm32mp1-openstlinux-5-4-dunfell-mp1-20-11-12.tar_v2.1.0.xzen.SOURCES-stm32mp1-openstlinux-5-4-dunfell-mp1-20-11-12.tar_v2.1.0.xz拷贝到Ubuntu下。

    • 解压SDK

      $ tar xvfJ en.SDK-x86_64-stm32mp1-openstlinux-5-4-dunfell-mp1-20-11-12.tar_v2.1.0.xz
      
    • 赋予SDK安装脚本可执行权限

      $ chmod +x stm32mp1-openstlinux-5.4-dunfell-mp1-20-11-12/sdk/st-image-weston-openstlinux-weston-stm32mp1-x86_64-toolchain-3.1-openstlinux-5.4-dunfell-mp1-20-11-12.sh
      
    • 执行安装命令

      $ ./stm32mp1-openstlinux-5.4-dunfell-mp1-20-11-12/sdk/st-image-weston-openstlinux-weston-stm32mp1-x86_64-toolchain-3.1-openstlinux-5.4-dunfell-mp1-20-11-12.sh -d <安装目录绝对路径>/SDK
      

      过程如下:

      $ ./stm32mp1-openstlinux-5.4-dunfell-mp1-20-11-12/sdk/st-image-weston-openstlinux-weston-stm32mp1-x86_64-toolchain-3.1-openstlinux-5.4-dunfell-mp1-20-11-12.sh -d <安装目录绝对路径>/SDK 
      ST OpenSTLinux - Weston - (A Yocto Project Based Distro) SDK installer version 3.1-openstlinux-5.4-dunfell-mp1-20-11-12
      =======================================================================================================================
      You are about to install the SDK to "<安装目录绝对路径>/SDK". Proceed [Y/n]? Y
      Extracting SDK....................................................................................................................................................................................................................done
      Setting it up...done
      SDK has been successfully set up and is ready to be used.
      Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g.
       $ . <安装目录绝对路径>/SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
      
      
    • SDK环境设置

      $ source <安装目录绝对路径>/SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
      
    • 确认SDK环境变量

      $ echo $ARCH
      arm
      
      $ echo $CROSS_COMPILE
      arm-ostl-linux-gnueabi-
      
      # 在bash下运行该命令,实测zsh执行报错
      $ $CC --version
      arm-ostl-linux-gnueabi-gcc (GCC) 9.3.0
      Copyright (C) 2019 Free Software Foundation, Inc.
      This is free software; see the source for copying conditions.  There is NO
      warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      
      $ echo $OECORE_SDK_VERSION
      3.1-openstlinux-5.4-dunfell-mp1-20-11-12
      
  3. 解压源码

    $ tar xvfJ en.SOURCES-stm32mp1-openstlinux-5-4-dunfell-mp1-20-11-12.tar_v2.1.0.xz
    
  4. SD卡分区

    因为是使用SD卡作为启动设备来验证,所以需要事先对SD卡进行分区(我的SD卡设备在Ubuntu下是sdb)。

    格式化当前分区:

    sudo sgdisk -Z -go /dev/sdb
    

    创建新分区:

    $ sudo sgdisk --resize-table=128 -a 1 \
    -n 1:34:545      -c 1:fsbl1 \
    -n 2:546:1057    -c 2:fsbl2 \
    -n 3:1058:5153   -c 3:ssbl \
    -n 4:5154:136225 -c 4:bootfs \
    -n 5:136226:     -c 5:rootfs \
    -A 4:set:2 \
    -p /dev/sdb -g
    

    查看当前SD卡分区情况:

    $ sudo sgdisk -p /dev/sdb
    Disk /dev/sdb: 61157376 sectors, 29.2 GiB
    Model: Multi-Card
    Sector size (logical/physical): 512/512 bytes
    Disk identifier (GUID): CE89FBB7-8211-45AE-8F5A-C6601A2EC29E
    Partition table holds up to 128 entries
    Main partition table begins at sector 2 and ends at sector 33
    First usable sector is 34, last usable sector is 61157342
    Partitions will be aligned on 2-sector boundaries
    Total free space is 0 sectors (0 bytes)
    
    Number  Start (sector)    End (sector)  Size       Code  Name
       1              34             545   256.0 KiB   8300  fsbl1
       2             546            1057   256.0 KiB   8300  fsbl2
       3            1058            5153   2.0 MiB     8300  ssbl
       4            5154          136225   64.0 MiB    8300  bootfs
       5          136226        61157342   29.1 GiB    8300  rootfs
    

    关于sgdisk工具更多信息使用命令sgdisk --help 查看

一 TF-A移植

tf-a-stm32mp-2.2.r2-r0目录下的README.HOW_TO.txt文件描述了源码解压、打补丁、编译等操作

1.0 组织源码

# 解压
$ tar xvfz tf-a-stm32mp-2.2.r2-r0.tar.gz
# 进入解压后的源码目录
$ cd tf-a-stm32mp-2.2.r2
# git管理
$ test -d .git || git init . && git add . && git commit -m "tf-a source code" && git gc
# 创建开发分支
$ git checkout -b develop
# 打补丁
$ for p in `ls -1 ../*.patch`; do patch -p1 < $p; done

1.1 添加自己的开发板

根据所参考的官方开发板创建自己开发板的设备树文件

$ cp ./fdts/stm32mp157d-ed1.dts ./fdts/stm32mp157d-custom.dts
$ cp ./fdts/stm32mp15xx-edx.dtsi ./fdts/stm32mp157d-custom.dtsi

1.2 修改stm32mp157d-custom.dts文件

#include "stm32mp15xx-edx.dtsi"
改为:
#include "stm32mp157d-custom.dtsi"


model = "STMicroelectronics STM32MP157D eval daughter";
compatible = "st,stm32mp157d-ed1", "st,stm32mp157";
改为:
model = "STMicroelectronics STM32MP157D custom board";
compatible = "st,stm32mp157d-custom", "st,stm32mp157";

/* 我手上板子使用的是USART1,官方板子是UART4 */
aliases {
	serial0 = &uart4;
};
改为:
aliases {
	serial0 = &usart1;
};

1.3 修改stm32mp157d-custom.dtsi文件

1.3.1 修改输出串口

&uart4 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart4_pins_a>;
	status = "okay";
};
改为:
&usart1 {
	pinctrl-names = "default";
	pinctrl-0 = <&usart1_pins_a>;
	status = "okay";
};

1.3.2 修改外部晶振

官方板子使用的是第一种方案的有源晶振,我手上板子使用的是第三种方案的无源晶振。所以需要将以下内容删除或注释掉:

&clk_hse {
	st,digbypass;
};

1.3.3 修改SD卡接口


上面是官方板子和我板子的SD卡电路图。可以看到有的功能我的板子是没有的,所以需要将设备树中sdmmc1节点做一些删减:

&sdmmc1 {
	pinctrl-names = "default";
	pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>;
	disable-wp;
	st,sig-dir;
	st,neg-edge;
	st,use-ckin;
	bus-width = <4>;
	vmmc-supply = <&vdd_sd>;
	sd-uhs-sdr12;
	sd-uhs-sdr25;
	sd-uhs-sdr50;
	sd-uhs-ddr50;
	sd-uhs-sdr104;
	status = "okay";
};
修改后:
&sdmmc1 {
	pinctrl-names = "default";
	pinctrl-0 = <&sdmmc1_b4_pins_a>;
	disable-wp;
	st,neg-edge;
	bus-width = <4>;
	vmmc-supply = <&vdd_sd>;
	status = "okay";
};

注:我手上板子的PMIC和官方板子一致,所以无需修改(虽然市面上一些教学开发板采用分立电源方案,但是落实到产品设计个人认为使用官方推荐方案更好。电源这种重要部件的钱能不省就不要省,毕竟有些坑能不踩就不要去踩)

1.4 修改stm32mp15-pinctrl.dtsi文件

在文件stm32mp15-pinctrl.dtsi中的pinctrl_z节点下添加usart1_pins_a节点:

&pinctrl_z {
	......
	usart1_pins_a: usart1-0 {
		pins1 {
			pinmux = <STM32_PINMUX('Z', 7, AF7)>; /* USART1_TX */
			bias-disable;
			drive-push-pull;
			slew-rate = <0>;
		};
		pins2 {
			pinmux = <STM32_PINMUX('Z', 6, AF7)>; /* USART1_RX */
			bias-pull-up;
		};
	};
};

注意:一定要添加在pinctrl_z节点下面,因为USART1使用的是端口Z的引脚

1.5 编译TF-A

创建编译脚本编译tf-a源码,脚本内容如下:

#!/bin/bash
# SDK环境设置
source <安装目录绝对路径>/SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
# 编译前清除上一次编译
make -f $PWD/../Makefile.sdk clean
# 编译命令
make -f $PWD/../Makefile.sdk TFA_DEVICETREE=stm32mp157d-custom TF_A_CONFIG="trusted serialboot" ELF_DEBUG_ENABLE='1' all  

赋予脚本可执行权限:

$ chmod +x build.sh 

执行编译脚本./build.sh等待编译完成后在tf-a源码上层目录build/trusted下生成镜像tf-a-stm32mp157d-custom-trusted.stm32

1.6 烧写验证

将前面完成分区的SD卡连接到Ubuntu,然后进入镜像存放目录后执行如下命令进行烧写:

# 烧写fsbl1
$ sudo dd if=tf-a-stm32mp157d-custom-trusted.stm32 of=/dev/sdb1 bs=1M conv=fdatasync
# 烧写fsbl2
$ sudo dd if=tf-a-stm32mp157d-custom-trusted.stm32 of=/dev/sdb2 bs=1M conv=fdatasync

启动模式调整为SD卡启动:

BOOT Mode BOOT2 BOOT1 BOOT0
Cortex_M4 1 0 0
SD_Card 1 0 1
NOR 0 0 1
EMMC 0 1 0
并行NAND 0 1 1
串行NAND 1 1 1
USB/UART 1 1 0
USB/UART 0 0 0

启动后输出如下:

NOTICE:  CPU: STM32MP157DAA Rev.Z
NOTICE:  Model: STMicroelectronics STM32MP157D custom board
INFO:    Reset reason (0x15):
INFO:      Power-on Reset (rst_por)
INFO:    PMIC version = 0x21
INFO:    Using SDMMC
INFO:      Instance 1
INFO:    Boot used partition fsbl1
NOTICE:  BL2: v2.2-r2.0(debug):d8a87d6-dirty
NOTICE:  BL2: Built : 15:45:09, Feb  8 2022
INFO:    Using crypto library 'stm32_crypto_lib'
INFO:    BL2: Doing platform setup
INFO:    RAM: DDR3-DDR3L 32bits 533000Khz
INFO:    Memory size = 0x40000000 (1024 MB)
INFO:    BL2 runs SP_MIN setup
INFO:    BL2: Loading image id 4
INFO:    Loading image id=4 at address 0x2ffeb000
INFO:    Image id=4 loaded: 0x2ffeb000 - 0x2ffff000
INFO:    BL2: Loading image id 5
INFO:    Loading image id=5 at address 0xc0100000
WARNING: Failed to determine the size of the image id=5 (-12)
ERROR:   BL2: Failed to load image (-12)

由于此时还没有u-boot,所以会报无法加载镜像的错误。

二 U-BOOT移植

u-boot-stm32mp-2020.01.r2-r0目录下的README.HOW_TO.txt文件描述了源码解压、打补丁、编译等操作

2.0 组织源码

# 解压
$ tar xfvz u-boot-stm32mp-2020.01.r2-r0.tar.gz
# 进入解压后的源码目录
$ cd u-boot-stm32mp-2020.01.r2 
# git管理
$ test -d .git || git init . && git add . && git commit -m "U-Boot source code" && git gc
# 创建开发分支
$ git checkout -b develop
# 打补丁
$ for p in `ls -1 ../*.patch`; do patch -p1 < $p; done

2.1 添加自己的开发板

$ cp arch/arm/dts/stm32mp157d-ed1.dts arch/arm/dts/stm32mp157d-custom.dts
$ cp arch/arm/dts/stm32mp15xx-edx.dtsi  arch/arm/dts/stm32mp157d-custom.dtsi
# stm32mp157d-ed1-u-boot.dtsi文件直接引用的stm32mp157a-ed1-u-boot.dtsi文件
$ cp arch/arm/dts/stm32mp157a-ed1-u-boot.dtsi arch/arm/dts/stm32mp157d-custom-u-boot.dtsi

2.2 修改文件适配平台

2.2.1 修改stm32mp157d-custom.dts文件

#include "stm32mp15xx-edx.dtsi"
改为
#include "stm32mp157d-custom.dtsi"
    

model = "STMicroelectronics STM32MP157D eval daughter";
compatible = "st,stm32mp157d-ed1", "st,stm32mp157";
改为
model = "STMicroelectronics STM32MP157D custom board";
compatible = "st,stm32mp157d-custom", "st,stm32mp157"; 


/* 我手上板子使用的是USART1,官方板子是UART4 */
aliases {
    serial0 = &uart4;
};
改为:
aliases {
    serial0 = &usart1;
};

2.2.2 修改stm32mp157d-custom.dtsi文件

2.2.2.1 修改输出串口

&uart4 {
	pinctrl-names = "default", "sleep", "idle";
	pinctrl-0 = <&uart4_pins_a>;
	pinctrl-1 = <&uart4_sleep_pins_a>;
	pinctrl-2 = <&uart4_idle_pins_a>;
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
};
改为:
&usart1 {
	pinctrl-names = "default", "sleep", "idle";
	pinctrl-0 = <&usart1_pins_a>;
	pinctrl-1 = <&usart1_sleep_pins_a>;
	pinctrl-2 = <&usart1_idle_pins_a>;
	/delete-property/dmas;
	/delete-property/dma-names;
	status = "okay";
};

2.2.2.2 修改SD卡接口

前面移植TF-A是有说到SD卡接口与官方板子的差异,修改如下:

&sdmmc1 {
	pinctrl-names = "default", "opendrain", "sleep";
	pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>;
	pinctrl-1 = <&sdmmc1_b4_od_pins_a &sdmmc1_dir_pins_a>;
	pinctrl-2 = <&sdmmc1_b4_sleep_pins_a &sdmmc1_dir_sleep_pins_a>;
	cd-gpios = <&gpiog 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
	disable-wp;
	st,sig-dir;
	st,neg-edge;
	st,use-ckin;
	bus-width = <4>;
	vmmc-supply = <&vdd_sd>;
	vqmmc-supply = <&sd_switch>;
	sd-uhs-sdr12;
	sd-uhs-sdr25;
	sd-uhs-sdr50;
	sd-uhs-ddr50;
	sd-uhs-sdr104;
	status = "okay";
};
改为:
&sdmmc1 {
	pinctrl-names = "default", "opendrain", "sleep";
	pinctrl-0 = <&sdmmc1_b4_pins_a>;
	pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
	pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;
    /* SD卡的CD引脚连接的是PE8 */
	cd-gpios = <&gpioe 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
	disable-wp;
	st,neg-edge;
	bus-width = <4>;
	vmmc-supply = <&vdd_sd>;
	status = "okay";
};

2.2.2.3 添加以太网接口(KSZ9031)

在文件末添加以下内容(参考stm32mp15xx-evx.dtsiDocumentation/devicetree/bindings/net/ethernet-phy.yaml文件):

&ethernet0 {
	status = "okay";
	pinctrl-0 = <&ethernet0_rgmii_pins_a>;
	pinctrl-1 = <&ethernet0_rgmii_sleep_pins_a>;
	pinctrl-names = "default", "sleep";
	phy-mode = "rgmii-id";
	max-speed = <1000>;
	phy-handle = <&phy0>;
	nvmem-cells = <&ethernet_mac_address>;
	nvmem-cell-names = "mac-address";

	mdio0 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "snps,dwmac-mdio";
		phy0: ethernet-phy@0 {
			/* PHY_ID_KSZ9031		0x00221620 */
			compatible = "ethernet-phy-id0022.1620";
			reset-gpios = <&gpiog 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
			reset-assert-us = <1000>;
			reset-deassert-us = <2000>;
			reg = <0>;
		};
	};
};

2.2.2.4 添加USB外设接口

修改如下(参考stm32mp15xx-evx.dtsi文件):

添加usb_phy_tuning节点到根节点

usb_phy_tuning: usb-phy-tuning {
    st,hs-dc-level = <2>;
    st,fs-rftime-tuning;
    st,hs-rftime-reduction;
    st,hs-current-trim = <15>;
    st,hs-impedance-trim = <1>;
    st,squelch-level = <3>;
    st,hs-rx-offset = <2>;
    st,no-lsfs-sc;
};

接下来修改如下所示内容

&usbotg_hs {
	vbus-supply = <&vbus_otg>;
};

&usbphyc_port0 {
	phy-supply = <&vdd_usb>;
};

&usbphyc_port1 {
	phy-supply = <&vdd_usb>;
};

修改后:

&usbh_ehci {
	phys = <&usbphyc_port0>;
	status = "okay";
};

&usbotg_hs {
	pinctrl-0 = <&usbotg_hs_pins_a>;
	pinctrl-names = "default";
	phys = <&usbphyc_port1 0>;
	phy-names = "usb2-phy";
	status = "okay";
};

&usbphyc {
	status = "okay";
};

&usbphyc_port0 {
	phy-supply = <&vdd_usb>;
	st,phy-tuning = <&usb_phy_tuning>;
};

&usbphyc_port1 {
	phy-supply = <&vdd_usb>;
	st,phy-tuning = <&usb_phy_tuning>;
};

2.2.2.5 修改LED接口

st官方板ev1设计了两个led作为heartbeaterror指示灯,分别连接在PD9PA13

若自己板子有设计指示灯可以改为自己的LED接口,若没有则可以注释或删除这些节点,我这块板子LED接口如图:

修改stm32mp157d-custom.dtsi文件:

led {
    compatible = "gpio-leds";
    led-blue {
        label = "heartbeat";
        /* 改为自己开发板的led引脚 */
        gpios = <&gpiod 1 GPIO_ACTIVE_HIGH>;
        linux,default-trigger = "heartbeat";
        default-state = "off";
    };
};

修改stm32mp157d-custom-u-boot.dtsi文件:

led {
    led-red {
        label = "error";
        /* 改为自己开发板的led引脚 */
        gpios = <&gpiod 15 GPIO_ACTIVE_LOW>;
        default-state = "off";
        status = "okay";
    };
};

2.2.3 修改stm32mp157d-custom-u-boot.dtsi文件

2.2.3.1 HSE和输出串口

/* 删除或注释下面内容 */
&clk_hse {
	st,digbypass;
};

/* uart4节点下添加usart1 */
&usart1 {
	u-boot,dm-pre-reloc;
};

&usart1_pins_a {
	u-boot,dm-pre-reloc;
	pins1 {
		u-boot,dm-pre-reloc;
	};
	pins2 {
		u-boot,dm-pre-reloc;
		/* pull-up on rx to avoid floating level */
		bias-pull-up;
	};
};

2.2.3.2 去除无关引脚

官方ev1开发板使用了两个按键用于进入fastbootstm32prog模式,这两个按键分别连接在PA13PA14端口上面。

我手上的板子没有这个设计,所以需要做如下修改:

/* 删除或注释掉下面内容 */
st,fastboot-gpios = <&gpioa 13 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
st,stm32prog-gpios = <&gpioa 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;

这里的修改也和前面的添加USB外设接口有关系,若修改USB接口后不改这里则启动U-Boot会进入fastboot(提示Enter fastboot!)

2.2.4 修改stm32mp15-pinctrl.dtsi文件

pinctrl_z节点下添加usart1引脚相关节点如下:

&pinctrl_z {
	......
	usart1_pins_a: usart1-0 {
		pins1 {
			pinmux = <STM32_PINMUX('Z', 7, AF7)>; /* USART1_TX */
			bias-disable;
			drive-push-pull;
			slew-rate = <0>;
		};
        pins2 {
            pinmux = <STM32_PINMUX('Z', 6, AF7)>; /* USART1_RX */
            bias-disable;
		};
	};

	usart1_idle_pins_a: usart1-idle-0 {
		pins1 {
            pinmux = <STM32_PINMUX('Z', 7, ANALOG)>; /* USART1_TX */
		};
		pins2 {
			pinmux = <STM32_PINMUX('Z', 6, AF7)>; /* USART1_RX */
			bias-disable;
		};
	};

	usart1_sleep_pins_a: usart1-sleep-0 {
		pins {
			pinmux = <STM32_PINMUX('Z', 7, ANALOG)>, /* USART1_TX */
                <STM32_PINMUX('Z', 6, ANALOG)>; /* USART1_RX */
		};
	};
};

2.2.5 添加RGB-LCD接口

U-Boot中添加LCD接口后使用STM32CubeProgrammer软件烧写系统时会在LCD上显示烧写过程。

我板子上有一块5寸rgb接口显示屏,添加rgb-lcd接口需要对stm32mp157d-custom.dts文件做如下修改:

1、在根节点添加以下内容:

panel_backlight: panel-backlight {
    compatible = "gpio-backlight";
    gpios = <&gpiod 13 GPIO_ACTIVE_HIGH>;
    default-on;
    status = "okay";
};

panel_rgb: panel-rgb {
    compatible = "simple-panel";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&ltdc_pins_b>;
    backlight = <&panel_backlight>;
    status = "okay";

    port {
        rgb_in: endpoint {
            remote-endpoint = <&ltdc_ep0_out>;
        };
    };

    display-timings {
        native-mode = <&timing0>; 		/* 时序信息 */
        timing0: 800x480 { 			/* 5 寸 800*480 分辨率 */
            clock-frequency = <30000000>; 	/* LCD 像素时钟,单位 Hz */
            hactive = <800>; 			/* LCD X 轴像素个数 */
            vactive = <480>; 			/* LCD Y 轴像素个数 */
            hfront-porch = <40>; 		/* LCD hfp 参数 */
            hback-porch = <88>; 		/* LCD hbp 参数 */
            hsync-len = <0>; 			/* LCD hspw 参数 */
            vback-porch = <32>; 		/* LCD vbp 参数 */
            vfront-porch = <13>; 		/* LCD vfp 参数 */
            vsync-len = <3>; 			/* LCD vspw 参数 */
        };
    };
};

2、在根节点外追加ltdc节点内容如下:

&ltdc {
    status = "okay";
    pinctrl-names = "default";
    port {
        #address-cells = <1>;
        #size-cells = <0>;

        ltdc_ep0_out: endpoint@0 {
            reg = <0>;
            remote-endpoint = <&rgb_in>;
        };
    };
};

2.2.6 修改arch/arm/dts/Makefile文件

CONFIG_STM32MP15x下添加自己平台的设备树stm32mp157d-custom.dtb

dtb-$(CONFIG_STM32MP15x) += \
	stm32mp157a-avenger96.dtb \
	stm32mp157a-dk1.dtb \
	stm32mp157a-ed1.dtb \
	stm32mp157a-ev1.dtb \
	stm32mp157c-dk2.dtb \
	stm32mp157c-ed1.dtb \
	stm32mp157c-ev1.dtb \
	stm32mp157d-dk1.dtb \
	stm32mp157d-ed1.dtb \
	stm32mp157d-ev1.dtb \
	stm32mp157f-dk2.dtb \
	stm32mp157f-ed1.dtb \
	stm32mp157f-ev1.dtb \
	stm32mp15xx-dhcom-pdk2.dtb \
	stm32mp157d-custom.dtb

2.3 修改stm32mp15_trusted_defconfig文件

1、为了能够在U-boot终端设置MAC地址等参数,需要在文件末追加以下内容:

CONFIG_ENV_OVERWRITE=y

否则无法更改环境变量,以设置以太网MAC为例报错信息如下:

STM32MP> setenv ethaddr 2c:de:34:8c:5b:e5
## Error: Can't overwrite "ethaddr"
## Error inserting "ethaddr" variable, errno=1

2、使能boot命令。官方uboot配置文件并没有开启boot命令,需要在文件添加以下内容来开启:

CONFIG_CMD_BOOTD=y

2.4 编译U-boot

创建编译脚本内容如下:

#!/bin/bash

# SDK环境设置
source <安装目录绝对路径>/SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
# 编译前清除上一次编译
make -f $PWD/../Makefile.sdk clean
# 编译命令
make -f $PWD/../Makefile.sdk all UBOOT_CONFIGS=stm32mp15_trusted_defconfig,trusted,u-boot.stm32 DEVICE_TREE=stm32mp157d-custom

赋予脚本执行权限:

$ chmod +x build.sh

执行编译脚本./build.sh等待编译完成后会在U-Boot源码上级目录build-trusted下生成镜像u-boot-stm32mp157d-custom-trusted.stm32

注意:编译时可能会遇到如下报错:

Error: You must add new CONFIG options using Kconfig
The following new ad-hoc CONFIG options were detected:
CONFIG_UBIFS_SILENCE_MSG

Please add these via Kconfig instead. Find a suitable Kconfig
file and add a 'config' or 'menuconfig' option.

这是Makefile执行到scripts/check-config.sh脚本时发生错误,有如下两种解决方法:

方法1:屏蔽U-Boot根目录Makefile文件中如下内容,不执行该有序检查操作

quiet_cmd_cfgcheck = CFGCHK  $2
cmd_cfgcheck = $(srctree)/scripts/check-config.sh $2 \
		$(srctree)/scripts/config_whitelist.txt $(srctree)

方法2:把报错信息中的CONFIG_UBIFS_SILENCE_MSG插入到文件scripts/config_whitelist.txt中,注意一定要按照字母排序放到对应位置,如下

CONFIG_UBIBLOCK
CONFIG_UBIFS_SILENCE_MSG
CONFIG_UBIFS_VOLUME

2.5 烧写验证

将前面移植烧写过TF-A的SD卡连接到Ubuntu,然后进入镜像存放目录后执行如下命令进行烧写:

$ sudo dd if=u-boot-stm32mp157d-custom-trusted.stm32 of=/dev/sdb3 conv=fdatasync

插入SD卡并设置BOOT模式为SD卡启动(101)后可以看到在TF-A信息加载完成后会接着加载U-Boot输出信息:

NOTICE:  CPU: STM32MP157DAA Rev.Z
NOTICE:  Model: STMicroelectronics STM32MP157D custom board
INFO:    Reset reason (0x15):
INFO:      Power-on Reset (rst_por)
INFO:    PMIC version = 0x21
INFO:    Using SDMMC
INFO:      Instance 1
INFO:    Boot used partition fsbl1
NOTICE:  BL2: v2.2-r2.0(debug):d8a87d6-dirty
NOTICE:  BL2: Built : 15:45:09, Feb  8 2022
INFO:    Using crypto library 'stm32_crypto_lib'
INFO:    BL2: Doing platform setup
INFO:    RAM: DDR3-DDR3L 32bits 533000Khz
INFO:    Memory size = 0x40000000 (1024 MB)
INFO:    BL2 runs SP_MIN setup
INFO:    BL2: Loading image id 4
INFO:    Loading image id=4 at address 0x2ffeb000
INFO:    Image id=4 loaded: 0x2ffeb000 - 0x2ffff000
INFO:    BL2: Loading image id 5
INFO:    Loading image id=5 at address 0xc0100000
INFO:    STM32 Image size : 899871
INFO:    Image id=5 loaded: 0xc0100000 - 0xc01dbb1f
WARNING: Skip signature check (header option)
NOTICE:  ROTPK is not deployed on platform. Skipping ROTPK verification.
NOTICE:  BL2: Booting BL32
INFO:    Entry point address = 0x2ffeb000
INFO:    SPSR = 0x1d3
NOTICE:  SP_MIN: v2.2-r2.0(debug):d8a87d6-dirty
NOTICE:  SP_MIN: Built : 15:45:14, Feb  8 2022
INFO:    ARM GICv2 driver initialized
INFO:    Set calibration timer to 60 sec
INFO:    stm32mp IWDG1 (12): Secure
INFO:    ETZPC: CRYP1 (9) could be non secure
INFO:    SP_MIN: Initializing runtime services
INFO:    SP_MIN: Preparing exit to normal world


U-Boot 2020.01-stm32mp-r2 (Feb 09 2022 - 01:26:51 +0800)

CPU: STM32MP157DAA Rev.Z
Model: STMicroelectronics STM32MP157D custom board
Board: stm32mp1 in trusted mode (st,stm32mp157d-custom)
DRAM:  1 GiB
Clocks:
- MPU : 800 MHz
- MCU : 208.878 MHz
- AXI : 266.500 MHz
- PER : 24 MHz
- DDR : 533 MHz
WDT:   Started with servicing (32s timeout)
NAND:  0 MiB
MMC:   STM32 SD/MMC: 0, STM32 SD/MMC: 1
Loading Environment from MMC... OK
In:    serial
Out:   serial
Err:   serial
invalid MAC address in OTP 00:00:00:00:00:00
Net:
Error: ethernet@5800a000 address not set.
No ethernet found.

Hit any key to stop autoboot:  0
STM32MP>

报错提示没有设置网卡MAC地址,在U-Boot模式下设置MAC后重启即可解决问题:

# 设置
STM32MP> setenv ethaddr D4:B2:C3:A1:C6:F5
# 保存
STM32MP> saveenv
Saving Environment to MMC... Writing to MMC(0)... OK
# 重启
STM32MP> reset

或者在include/configs/stm32mp1.h文件中添加以太网MAC默认环境变量("ethaddr=D4:B2:C3:A1:C6:F5\0" \)也可以解决该问题:

#define CONFIG_EXTRA_ENV_SETTINGS \
	"bootdelay=1\0" \
	"ethaddr=D4:B2:C3:A1:C6:F5\0" \
	"kernel_addr_r=0xc2000000\0" \
	"fdt_addr_r=0xc4000000\0" \
	"scriptaddr=0xc4100000\0" \
	"pxefile_addr_r=0xc4200000\0" \
	"splashimage=0xc4300000\0"  \
	"ramdisk_addr_r=0xc4400000\0" \
	"altbootcmd=run bootcmd\0" \
	"env_check=" \
		"env exists env_ver || env set env_ver ${ver};" \
		"if env info -p -d -q; then env save; fi;" \
		"if test \"$env_ver\" != \"$ver\"; then" \
		" echo \"*** Warning: old environment ${env_ver}\";" \
		" echo '* set default: env default -a; env save; reset';" \
		" echo '* update current: env set env_ver ${ver}; env save';" \
		"fi;\0" \
	STM32MP_BOOTCMD \
	STM32MP_ANDROID \
	PARTS_DEFAULT \
	BOOTENV \
	"boot_net_usb_start=true\0"

三、LINUX移植

linux-stm32mp-5.4.56-r0目录下的README.HOW_TO.txt文件描述了源码解压、打补丁、编译等操作

3.1 组织源码

# 解压源码
$ $ tar xvfJ linux-5.4.56.tar.xz 
# 进入源码目录
$ cd linux-5.4.56
# git管理
$ test -d .git || git init . && git add . && git commit -m "Linux source code" && git gc
# 创建开发分支
$ git checkout -b develop
# 打补丁
$ for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
# 绕过SHA1内核版本号生成
$ echo "" > .scmversion
# 创建编译输出目录
$ mkdir -p ../build
# 在编译输出目录下生成.config文件
$ make ARCH=arm O="$PWD/../build" multi_v7_defconfig fragment*.config
# 注入补丁文件完善.config配置文件-1
$ for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config $f; done
# 注入补丁文件完善.config配置文件-2
$ yes '' | make ARCH=arm oldconfig O="$PWD/../build"
# 复制配置文件
$ cp ../build/.config ./arch/arm/configs/stm32mp157d_custom_defconfig

注意:make ARCH=arm O="$PWD/../build" multi_v7_defconfig fragment*.config这条命令需要使用bash执行,使用zsh执行会报错如下:

$ make ARCH=arm O="$PWD/../build" multi_v7_defconfig fragment*.config
zsh: no matches found: fragment*.config

若使用zsh则需要补全文件执行,如下:

make ARCH=arm O="$PWD/../build" multi_v7_defconfig fragment-01-multiv7_cleanup.config fragment-02-multiv7_addons.config

3.2 添加自己的开发板

$ cp ./arch/arm/boot/dts/stm32mp157d-ed1.dts ./arch/arm/boot/dts/stm32mp157d-custom.dts
$ cp ./arch/arm/boot/dts/stm32mp15xx-edx.dtsi ./arch/arm/boot/dts/stm32mp157d-custom.dtsi

3.3 修改文件适配平台

3.3.1 修改stm32mp157d-custom.dts文件

参考U-Boot移植2.2.1

3.3.2 修改stm32mp157d-custom.dtsi文件

参考U-Boot移植2.2.2

需要注意&ethernet0节点中以下内容与uboot中有差别:

&ethernet0 {
	......
	pinctrl-1 = <&ethernet0_rgmii_pins_sleep_a>;
	......
};

3.3.3 修改stm32mp15-pinctrl.dtsi文件

pinctrl_z节点下添加usart1相关节点。注意与uboot移植时添加这部分内容有不同之处:USART1_RX引脚需要配置上拉(bias-pull-up)选项,否则linux引导进入文件系统登录时无法输入。uboot移植时是在stm32mp157d-custom-u-boot.dtsi文件中对&usart1_pins_a节点的pins2(USART1_RX引脚)设置了上拉选项。

&pinctrl_z {
	......
	usart1_pins_a: usart1-0 {
		pins1 {
			pinmux = <STM32_PINMUX('Z', 7, AF7)>; /* USART1_TX */
			bias-disable;
			drive-push-pull;
			slew-rate = <0>;
		};
        pins2 {
            pinmux = <STM32_PINMUX('Z', 6, AF7)>; /* USART1_RX */
            bias-pull-up;
		};
	};

	usart1_idle_pins_a: usart1-idle-0 {
		pins1 {
            pinmux = <STM32_PINMUX('Z', 7, ANALOG)>; /* USART1_TX */
		};
		pins2 {
			pinmux = <STM32_PINMUX('Z', 6, AF7)>; /* USART1_RX */
			bias-pull-up;
		};
	};

	usart1_sleep_pins_a: usart1-sleep-0 {
		pins {
			pinmux = <STM32_PINMUX('Z', 7, ANALOG)>, /* USART1_TX */
                <STM32_PINMUX('Z', 6, ANALOG)>; /* USART1_RX */
		};
	};
};

3.3.4 修改arch/arm/boot/dts/Makefile文件

CONFIG_ARCH_STM32下添加自己平台的设备树stm32mp157d-custom.dtb

dtb-$(CONFIG_ARCH_STM32) += \
	stm32f429-disco.dtb \
	stm32f469-disco.dtb \
	stm32f746-disco.dtb \
	stm32f769-disco.dtb \
	stm32429i-eval.dtb \
	stm32746g-eval.dtb \
	stm32h743i-eval.dtb \
	stm32h743i-disco.dtb \
	stm32mp157a-avenger96.dtb \
	stm32mp157a-dk1.dtb \
	stm32mp157d-dk1.dtb \
	stm32mp157c-dk2.dtb \
	stm32mp157f-dk2.dtb \
	stm32mp157c-dk2-a7-examples.dtb \
	stm32mp157c-dk2-m4-examples.dtb \
	stm32mp157f-dk2-a7-examples.dtb \
	stm32mp157f-dk2-m4-examples.dtb \
	stm32mp157a-ed1.dtb \
	stm32mp157c-ed1.dtb \
	stm32mp157d-ed1.dtb \
	stm32mp157f-ed1.dtb \
	stm32mp157a-ev1.dtb \
	stm32mp157c-ev1.dtb \
	stm32mp157d-ev1.dtb \
	stm32mp157f-ev1.dtb \
	stm32mp157c-ev1-a7-examples.dtb \
	stm32mp157c-ev1-m4-examples.dtb \
	stm32mp157f-ev1-a7-examples.dtb \
	stm32mp157f-ev1-m4-examples.dtb \
	stm32mp157d-custom.dtb

3.4 编译Linux

# 清除上次编译
$ make ARCH=arm O="$PWD/../build" distclean
# 生成编译配置选项文件
$ make ARCH=arm O="$PWD/../build" stm32mp157d_custom_defconfig
# Build kernel images (uImage and vmlinux) and device tree (dtbs)
$ make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040 O="$PWD/../build"
# Build kernel module
$ make ARCH=arm modules O="$PWD/../build"
# Generate output build artifacts
$ make ARCH=arm INSTALL_MOD_PATH="$PWD/../build/install_artifact" modules_install O="$PWD/../build"

最后创建一个专门用于存放编译输出项目的目录,将编译完成的镜像和设备树拷贝该目录下:

$ mkdir -p $PWD/../build/install_artifact/boot/
$ cp $PWD/../build/arch/arm/boot/uImage $PWD/../build/install_artifact/boot/
$ cp $PWD/../build/arch/arm/boot/dts/stm32mp157d-custom.dtb $PWD/../build/install_artifact/boot/

3.5 测试验证

3.5.1 Ubuntu上搭建TFTP环境

# 安装tftp相关软件包
$ sudo apt-get install xinetd tftpd tftp
# 在/etc/xinetd.d/目录下创建名为tftp的文件
$ sudo touch /etc/xinetd.d/tftp
# 编辑文件
$ sudo vim /etc/xinetd.d/tftp

内容如下:

service tftp
{
protocol        = udp
port            = 69
socket_type     = dgram
wait            = yes
user            = nobody
server          = /usr/sbin/in.tftpd
server_args     = /tftpboot
disable         = no
}

创建tftpboot文件夹,注意路径及名称要和上面的server_args一致:

$ sudo mkdir /tftpboot
$ sudo chmod -R 777 /tftpboot
$ sudo chown -R nobody /tftpboot

通过xinetd启动tftpd:

$ sudo /etc/init.d/xinetd stop
$ sudo /etc/init.d/xinetd start

至此,tftp服务器已启动并运行

3.5.2 启动验证

首先将内核镜像uImage和设备树stm32mp157d-custom.dtb复制到tftpboot目录下。

开发板进入U-Boot模式,执行以下命令:

# 设置bootcmd使用tftp加载内核&设备树
setenv bootcmd 'tftp c2000000 uImage;tftp c4000000 stm32mp157d-custom.dtb;bootm c2000000 - c4000000'
# 设置ipaddr
setenv ipaddr 192.168.10.128
# 设置serverip
setenv serverip 192.168.10.64
# 设置gatewayip
setenv gatewayip 192.168.10.1
#设置netmask
setenv netmask 255.255.255.0
# 保存环境变量
saveenv
# 重启
reset

由于没有文件系统挂载,启动内核最后会提示Kernel panic错误。

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

3.5.3 Ubuntu上搭建NFS环境

# 安装nfs相关软件包
sudo apt-get install nfs-kernel-server
# 创建nfs共享目录
sudo mkdir /nfsroot
# 修改目录访问权限
sudo chmod 777 /nfsroot
# 修改如下文件
sudo vim /etc/default/nfs-kernel-server

为避免开发板与虚拟机nfs服务版本不同导致挂载失败(Ubuntu的nfs默认为协议V3和协议V4,开发板uboot使用的是V2协议,从而导致uboot不能在nfs服务器中找到文件),做如下修改让Ubuntu中的nfs服务兼容V2协议。

/etc/default/nfs-kernel-server文件内容修改后如下所示:

# Number of servers to start up
RPCNFSDCOUNT="-V 2 8"

# Runtime priority of server (see nice(1))
RPCNFSDPRIORITY=0

# Options for rpc.mountd.
# If you have a port-based firewall, you might want to set up
# a fixed port here using the --port option. For more information, 
# see rpc.mountd(8) or http://wiki.debian.org/SecuringNFS
# To disable NFSv4 on the server, specify '--no-nfs-version 4' here
RPCMOUNTDOPTS="-V 2 --manage-gids"

# Do you want to start the svcgssd daemon? It is only required for Kerberos
# exports. Valid alternatives are "yes" and "no"; the default is "no".
NEED_SVCGSSD=""

# Options for rpc.svcgssd.
RPCSVCGSSDOPTS="--nfs-version 2,3,4 --debug --syslog"

接下来修改/etc/exports文件:sudo vim /etc/exports

修改后内容如下:

# /etc/exports: the access control list for filesystems which may be exported
#		to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#

/nfsroot *(rw,sync,no_root_squash)

/nfsroot:nfs共享目录(与前面创建的目录一致)

*:允许所有的网络段访问

rw:访问者具有可读写权限

sync:同步缓存

no_root_squash:访问者具有 root 权限。

最后重启nfs服务:sudo /etc/init.d/nfs-kernel-server restart

3.5 Linux挂载nfs文件系统

重启开发板在uboot模式下执行如下命令:

# 设置bootargs
setenv bootargs 'console=ttySTM0,115200 \
root=/dev/nfs \
nfsroot=192.168.10.64:/nfsroot/rootfs,proto=tcp rw \
ip=192.168.10.128:192.168.10.64:192.168.10.1:255.255.255.0::eth0:off'
# 保存环境变量
saveenv
# 重启
reset

setenv bootargs 'console=开发板串口号,波特率
root=挂载方式
nfsroot=nfs服务器ip地址:nfs文件系统路径,proto=传输协议 读写权限
ip=开发板ip地址:nfs服务器ip地址:网关:子网掩码::开发板网口:off'

四 打包镜像

4.1 bootfs镜像

这部分由Linux镜像文件uImage和设备树文件stm32mp157d-custom.dtb构成。在Ubuntu下创建一个bootfs文件夹,将uImage和stm32mp157d-custom.dtb拷贝到该目录下,然后执行如下命令:

# 进入目录
cd bootfs
# 创建磁盘,of磁盘名称,bs磁盘块大小,count磁盘块数量
dd if=/dev/zero of=bootfs.ext4 bs=1M count=64
# 将磁盘格式化为ext4格式
mkfs.ext4 -L bootfs bootfs.ext4
# 创建目录用来挂载上一步制作的bootfs.ext4
sudo mkdir /mnt/bootfs
# 将bootfs.ext4挂载到/mnt/bootfs目录下
sudo mount bootfs.ext4 /mnt/bootfs/
# 将文件拷贝到/mnt/bootfs目录下
sudo cp uImage stm32mp157d-custom.dtb /mnt/bootfs/
# 拷贝完成后取消挂载
sudo umount /mnt/bootfs

完成上述操作后bootfs.ext4文件就打包完成了。

4.2 rootfs镜像

这部分由根文件系统构成,打包方法和前面打包bootfs.ext4操作一样。

# 进入目录
cd rootfs
# 创建磁盘,of磁盘名称,bs磁盘块大小,count磁盘块数量
dd if=/dev/zero of=rootfs.ext4 bs=1M count=512
# 将磁盘格式化为ext4格式
mkfs.ext4 -L rootfs rootfs.ext4
# 创建目录用来挂载上一步制作的rootfs.ext4
sudo mkdir /mnt/rootfs
# 将rootfs.ext4挂载到/mnt/rootfs目录下
sudo mount rootfs.ext4 /mnt/rootfs/
# 进入根文件系统存放目录
cd /nfsroot/rootfs/
# 将文件拷贝到/mnt/rootfs目录下
sudo cp * /mnt/rootfs/ -drf
# 拷贝完成后取消挂载
sudo umount /mnt/rootfs

完成上述操作后rootfs.ext4文件就打包完成了。

4.3 烧写验证

Windows下创建一个image文件夹,然后将tf-a,u-boot,bootfs,rootfs烧写文件拷贝到里面,然后在该文件夹下创建一个tsv文件(STM32CubeProgrammer的烧录脚本)。

FlashLayout_emmc_stm32mp157d-custom-trusted.tsv文件内容如下:

#Opt	Id		Name		Type		Device	Offset		Binary
-		0x01	fsbl1-boot	Binary		none	0x0			tf-a-stm32mp157d-custom-serialboot.stm32
-		0x03	ssbl-boot	Binary		none	0x0			u-boot-stm32mp157d-custom-trusted.stm32
P		0x04	fsbl1		Binary		mmc1	boot1		tf-a-stm32mp157d-custom-trusted.stm32
P		0x05	fsbl2		Binary		mmc1	boot2		tf-a-stm32mp157d-custom-trusted.stm32
PD		0x06	ssbl		Binary		mmc1	0x00080000	u-boot-stm32mp157d-custom-trusted.stm32
P		0x21	boot		System		mmc1	0x00280000	bootfs.ext4
P		0x22	rootfs		FileSystem	mmc1	0x04280000	rootfs.ext4

关于tsv文件各选项含义详见STM32CubeProgrammer flashlayout - stm32mpu (stmicroelectronics.cn)

1、开发板拨码为USB启动(000),使用USB线将开发板USB-OTG接口与Windows连接,然后开发板上电。

2、打开STM32CubeProgrammer软件,查看USB configuration是否有USB信息,没有就点击刷新按钮,确定接口为USB后点击Connect按钮,如图所示:

3、点击Open file选择所使用的tsv文件,注意Binaries path里面的路径为存放烧写文件的路径(Browse按钮选择文件路径),然后点击Download等待烧写完成。

4、烧写完成后开发板拨码为EMMC启动(010),然后进入uboot后依次进行如下操作:

# 设置bootcmd环境变量
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-custom.dtb;bootm c2000000 - c4000000'
# 设置bootargs环境变量
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk1p3 rootwait rw'
# 保存
saveenv
# 重启
reset

至此就实现了从emmc启动系统。

posted @ 2022-07-24 14:23  树·哥  阅读(1935)  评论(0编辑  收藏  举报