i.MX6ULL 开发板系统移植与根文件系统构建（一）：U-Boot 的移植
一、开发环境 嵌入式 Linux 开发板：100ASK-6ULL-V11（即：由百问网出品的 i.MX6ULL-PRO）； 系统：Ubuntu 18.04（已配置交叉编译工具链）； 待移植的 U-Boot：由 NXP 官方维护的 imx_v2020.04_5.4.47_2.2.0。 二、下载待移植的 U-Boot 源码 可以使用以下命令： git clone https://github.com/nxp-imx/uboot-imx cd uboot-imx git branch -a git checkout remotes/origin/imx_v2020.04_5.4.47_2.2.0 NXP 官方的项目集合了多个版本的 U-Boot，项目较大，如果多次克隆失败的话，也可以通过韦东山老师的仓库来克隆： git clone https://gitee.com/weidongshan/uboot-imx cd uboot-imx git reset d95408316b25d766d65397df42419b88fda3bbe1 --hard 三、移植 U-Boot 可以尝试使用默认配置来编译，我们这里就不尝试了哈，已知问题有：网卡不能正常驱动、LCD 屏幕参数不适配等等（其实尝试过，但没有截图🤣），我们直接开始移植。 1. 建立单板相关配置 建立单板的配置文件和头文件： cp configs/mx6ull_14x14_evk_emmc_defconfig configs/mx6ull_14x14_angus_emmc_defconfig cp include/configs/mx6ullevk.h include/configs/mx6ullangus.h 修改配置文件mx6ull_14x14_angus_emmc_defconfig的对应项为： CONFIG_TARGET_MX6ULL_14X14_ANGUS=y CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullangus/imximage.cfg" CONFIG_DEFAULT_DEVICE_TREE="imx6ull-14x14-angus-emmc" 修改头文件mx6ullangus.h的对应项为： #ifndef __MX6ULLANGUS_CONFIG_H #define __MX6ULLANGUS_CONFIG_H 同时修改该文件中查找设备树的命令： 添加 board 文件夹： cp board/freescale/mx6ullevk board/freescale/mx6ullangus -r 进入该文件夹后，修改文件名： mv mx6ullevk.c mx6ullangus.c 同时修改其对应内容： #ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG env_set("board_name", "ANGUS"); if (is_mx6ull_9x9_evk()) env_set("board_rev", "9X9"); else env_set("board_rev", "14X14"); if (is_cpu_type(MXC_CPU_MX6ULZ)) { env_set("board_name", "ULZ-EVK"); env_set("usb_net_cmd", "usb start"); } #endif int checkboard(void) { if (is_mx6ull_9x9_evk()) puts("Board: MX6ULL 9x9 EVK\n"); else if (is_cpu_type(MXC_CPU_MX6ULZ)) puts("Board: MX6ULZ 14x14 EVK\n"); else puts("Board: MX6ULL 14x14 ANGUS\n"); return 0; } 修改文件imximage.cfg的对应项为： #ifdef CONFIG_USE_IMXIMG_PLUGIN /*PLUGIN plugin-binary-file IRAM_FREE_START_ADDR*/ PLUGIN board/freescale/mx6ullangus/plugin.bin 0x00907000 #else 修改文件Kconfig为： if TARGET_MX6ULL_14X14_ANGUS || TARGET_MX6ULL_9X9_EVK config SYS_BOARD default "mx6ullangus" config SYS_VENDOR default "freescale" config SYS_CONFIG_NAME default "mx6ullangus" config SYS_TEXT_BASE default 0x87800000 endif 修改文件MAINTAINERS为： MX6ULLANGUS BOARD M: Peng Fan <peng.fan@nxp.com> S: Maintained F: board/freescale/mx6ullangus/ F: include/configs/mx6ullangus.h F: configs/mx6ull_14x14_angus_defconfig F: configs/mx6ull_14x14_angus_plugin_defconfig F: configs/mx6ulz_14x14_angus_defconfig 修改文件Makefile的对应项为： obj-y := mx6ullangus.o 增加设备树文件： cp arch/arm/dts/imx6ull-14x14-evk-emmc.dts arch/arm/dts/imx6ull-14x14-angus-emmc.dts cp arch/arm/dts/imx6ull-14x14-evk.dts arch/arm/dts/imx6ull-14x14-angus.dts cp arch/arm/dts/imx6ul-14x14-evk.dtsi arch/arm/dts/imx6ul-14x14-angus.dtsi cp arch/arm/dts/imx6ul-14x14-evk-u-boot.dtsi arch/arm/dts/imx6ul-14x14-angus-u-boot.dtsi 修改文件imx6ull-14x14-angus-emmc.dts的对应项为： #include "imx6ull-14x14-angus.dts" 修改imx6ull-14x14-angus.dts为 // SPDX-License-Identifier: (GPL-2.0 OR MIT) // // Copyright (C) 2016 Freescale Semiconductor, Inc. /dts-v1/; #include "imx6ull.dtsi" #include "imx6ul-14x14-angus.dtsi" #include "imx6ul-14x14-angus-u-boot.dtsi" / { model = "i.MX6 ULL 14x14 ANGUS Board"; compatible = "fsl,imx6ull-14x14-angus", "fsl,imx6ull"; }; &clks { assigned-clocks = <&clks IMX6UL_CLK_PLL3_PFD2>; assigned-clock-rates = <320000000>; }; 在编译设备树的文件arch/arm/dts/Makefile中添加我们的板子： 在文件arch/arm/mach-imx/mx6/Kconfig添加我们的板子，以支持 U-Boot 的图形化配置： config TARGET_MX6ULL_14X14_ANGUS bool "Support mx6ull_14x14_angus" select BOARD_LATE_INIT select DM select DM_THERMAL select MX6ULL imply CMD_DM source "board/freescale/mx6ullangus/Kconfig" 2. 适配网卡驱动和 LCD 屏幕参数 适配 LAN8720A 网卡 为了支持 U-Boot 从网络启动 Linux 内核，我们需要解决网卡适配的问题，但只需要适配一个网口即可，我们选择较为常用的 ENET2。 查看开发板的原理图和 LAN8720A 的芯片手册可知，ENET2 网卡 PHY 地址是 0x1。 同时，根据 LAN8720A 的芯片手册，我们在使用前需要对其进行硬件复位和软件复位操作，其中硬件复位要保持 nRST 引脚持续拉低一段时间，软件复位要求向 Soft Reset bit of Basic Control Register 写入1（建议多次写入直到其值为1）。 那我们继续修改配置文件configs/mx6ull_14x14_angus_emmc_defconfig，删除以下代码（EVK 开发板的 PHY 芯片的生产厂商是 Micrel）： CONFIG_PHY_MICREL=y CONFIG_PHY_MICREL_KSZ8XXX=y 使能 SMSC 公司的 PHY 驱动（LAN8720A 的生产厂商）： CONFIG_PHY_SMSC=y 修改文件arch/arm/dts/imx6ul-14x14-angus.dtsi的对应项以关闭 ENET1 的初始化、配置 phy-reset-gpios引脚以及设置phy-reset-duration 复位时间（对 LAN8720A 进行硬件复位）： &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet1>; phy-mode = "rmii"; phy-handle = <&ethphy0>; status = "disabled"; }; &fec2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet2>; phy-mode = "rmii"; phy-handle = <&ethphy1>; phy-reset-gpios = <&gpio5 6 GPIO_ACTIVE_LOW>; phy-reset-duration = <30>; status = "okay"; mdio { #address-cells = <1>; #size-cells = <0>; ethphy0: ethernet-phy@2 { reg = <0>; // micrel,led-mode = <1>; clocks = <&clks IMX6UL_CLK_ENET_REF>; clock-names = "rmii-ref"; }; ethphy1: ethernet-phy@1 { reg = <1>; // micrel,led-mode = <1>; clocks = <&clks IMX6UL_CLK_ENET2_REF>; clock-names = "rmii-ref"; }; }; }; pinctrl_enet2: enet2grp { fsl,pins = < MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0 MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0 MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0 MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0 MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0 MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0 MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0 MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0 MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0 MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031 MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x17059 >; }; pinctrl_lcdif_ctrl: lcdifctrlgrp { fsl,pins = < MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79 MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79 MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79 MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79 /* used for lcd reset */ MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x79 MX6ULL_PAD_SNVS_TAMPER6__GPIO5_IO06 0x79 >; }; 修改drivers/net/phy/phy.c对 LAN8720A 进行软件复位（为了减小对其它芯片的影响，我们引入宏命令进行条件编译），由于这里修改的代码比较复杂，我们直接对比： 需要添加的代码如下： #ifdef CONFIG_PHY_SMSC int rc; do{ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); udelay(100); rc=phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); udelay(100); }while(rc&BMCR_RESET); #endif #ifdef CONFIG_PHY_SMSC phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); udelay(1000); #endif 适配 LCD 屏幕参数 修改arch/arm/dts/imx6ul-14x14-angus.dtsi的对应项即可： &lcdif { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_lcdif_dat &pinctrl_lcdif_ctrl>; display = <&display0>; status = "okay"; display0: display@0 { bits-per-pixel = <24>; bus-width = <24>; display-timings { native-mode = <&timing0>; timing0: timing0 { clock-frequency = <51200000>; hactive = <1024>; vactive = <600>; hfront-porch = <160>; hback-porch = <140>; hsync-len = <20>; vback-porch = <20>; vfront-porch = <12>; vsync-len = <3>; hsync-active = <0>; vsync-active = <0>; de-active = <1>; pixelclk-active = <0>; }; }; }; }; 3. 编译、烧写 U-Boot 并启动开发板 编译： make distclean make mx6ull_14x14_angus_emmc_defconfig make 烧写并启动开发板： 可以看出，U-Boot 已经成功在板子上运行起来，观察发现屏幕工作正常。 之后配置网络，以支持后续通过网络下载 Linux 内核及设备树、基于NFS 挂载根文件系统： setenv ipaddr 10.10.10.31 setenv gatewayip 10.10.10.10 setenv netmask 255.255.0.0 setenv serverip 10.10.10.30 saveenv 如果 ENET2 未设置 MAC 地址，可以手动设置一下： setenv eth1addr=00:01:3f:2d:3e:4d saveenv 使用ping命令检验： => ping 10.10.10.30 Using ethernet@20b4000 device host 10.10.10.30 is alive 完工！ 参考资料 [1] https://github.com/nxp-imx/uboot-imx/tree/imx_v2020.04_5.4.47_2.2.0 [2] https://gitee.com/weidongshan/uboot-imx [3] 正点原子【第三期】手把手教你学Linux之系统移植和根文件系统构建篇 [4] i.MX6ULL移植NXP官方uboot2020.04_5.4.47_2.2.0