直接参考【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81
本文仅作为个人笔记使用,方便进一步记录自己的实践总结。
上一章节我们详细的分析了 uboot 的启动流程,对 uboot 有了一个初步的了解。前两章我们都是使用的正点原子提供的 uboot,本章我们就来学习如何将 NXP 官方的 uboot 移植到正点原子的 I.MX6ULL 开发板上,学习如何在 uboot 中添加我们自己的板子。
在 Linux 开发中,U-Boot 移植时需要调通以下关键功能:
基本硬件初始化功能
正确识别并初始化目标板的 CPU 架构和型号,设置 CPU 的工作频率、寄存器等参数,确保 CPU 能够正常工作。例如,对于 ARM 架构的 CPU,要正确配置其工作模式、时钟频率等。设置和初始化系统的时钟源,为目标板上的各种外设和 CPU 提供准确的时钟信号。这涉及到对时钟芯片或相关电路的配置,以确保各个部件能够在正确的时钟频率下协同工作。
内存初始化
检测和初始化目标板的内存系统,包括内存的大小、类型和读写时序等。建立内存映射,为后续的代码加载和数据存储提供基础。比如,初始化 DRAM 控制器,使其能够正常读写内存。
存储设备访问功能
根据目标板的 Flash 类型(如 NAND Flash、Nor Flash、eMMC 等),实现对应的驱动,能够对 Flash 进行读写操作,包括读取 Flash 中的 U-Boot 配置文件、内核镜像和文件系统等数据,以及将修改后的数据写回 Flash。如果目标板支持 SD 卡启动或数据存储,需要实现 SD 卡的驱动,以便能够从 SD 卡加载 U-Boot 或其他相关数据。
网络通信功能
实现以太网驱动程序,能够通过目标板的以太网接口与其他设备进行通信。这对于通过网络下载内核镜像、文件系统以及进行远程调试等操作非常重要。支持基本的网络协议,如 IP、TCP、UDP 等,以便能够进行网络数据传输和通信。例如,实现 Ping 命令、TFTP 客户端等功能,方便与外部网络进行交互。
调试输出功能
通过目标板的串口将调试信息输出到终端或主机上,方便开发人员查看系统的启动过程、错误信息以及运行时的状态。这是最常用的调试手段之一,可以通过串口输出的信息来判断 U-Boot 的运行情况和问题所在。
实现日志记录功能,将重要的事件和信息记录下来,以便后续分析和排查问题。日志可以记录到存储设备中,也可以输出到串口或其他输出设备上。
环境变量管理功能
能够将环境变量存储在合适的介质(如 Flash、EEPROM 等)中,并在启动时正确读取这些环境变量。环境变量用于存储一些系统配置信息和启动参数,如启动命令、网络设置等。提供设置和更新环境变量的方法,以便用户可以根据实际需求修改环境变量的值。这可以通过命令行接口或配置文件等方式实现。
启动加载功能
实现常见的启动命令,如
bootcmd
命令用于设置启动命令,bootz
或bootm
命令用于启动内核镜像等。这些命令是 U-Boot 引导 Linux 内核或其他操作系统的关键。能够从指定的存储设备(如 Flash、SD 卡、网络等)加载内核镜像、设备树文件和根文件系统等,并将它们加载到内存中的合适位置,为操作系统的启动做好准备。
(如果uboot本身也支持设备树)设备树解析功能
能够正确识别和解析设备树文件的格式和结构,从设备树中获取目标板的硬件信息,如各种外设的连接方式、地址空间、中断号等。实现对设备树节点的有效遍历和匹配机制,以便准确找到与目标板硬件相关的节点信息,并根据这些信息进行相应的硬件初始化和配置。
总的来说,在Linux开发中进行U-Boot移植时,需要细致地调通上述一系列关键功能,以确保U-Boot能够在目标硬件平台上稳定、可靠地运行,为Linux内核和其他软件的启动提供坚实的基础。
uboot的最终目标就是为了能够启动linux内核,所以,并不太关心其他不相关的外设功能。
查找 NXP 官方的开发板默认配置文件
uboot 的移植并不是说我们完完全全的从零开始将 uboot 移植到我们现在所使用的开发板或者开发平台上。这个对于我们来说基本是不可能的,这个工作一般是半导体厂商做的,半导体厂商负责将 uboot 移植到他们的芯片上,因此半导体厂商都会自己做一个开发板,这个开发板就叫做原厂开发板,比如大家学习 STM32的时候听说过的discover 开发板就是ST自己做的。
半导体厂商会将 uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个 uboot 发布出去,这就是大家常说的原厂 BSP 包。我们一般做产品的时候就会参考原厂的开发板做硬件,然后在原厂提供的 BSP 包上做修改,将 uboot 或者 linux kernel 移植到我们的硬件上。这个就是uboot 移植的一般流程:
①、在 uboot 中找到参考的开发平台,一般是原厂的开发板。
②、参考原厂开发板移植 uboot 到我们所使用的开发板上。
正点原子的 I.MX6ULL 开发板参考的是 NXP 官方的 I.MX6ULL EVK 开发板做的硬件,因此我们在移植 uboot 的时候就可以以 NXP 官方的 I.MX6ULL EVK 开发板为蓝本。
本章我们是将 NXP 官方的 uboot 移植到正点原子的 I.MX6ULL 开发板上,NXP 官方的uboot 放到了开发板光盘中,路径为:1、例程源码->4、NXP 官方原版 Uboot 和 Linux->ubootimx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。将 uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2 发送到 Ubuntu中并解压,然后创建 VSCode 工程。
在移植之前,我们先编译一下 NXP 官方 I.MX6ULL EVK 开发板对应的 uboot,首先是配置uboot,configs 目录下有很多跟 I.MX6UL/6ULL 有关的配置如图 33.1.1.1 所示,
从图 33.1.1.1 可以看出有很多的默认配置文件,其中以 mx6ul 开头的是 I.MX6UL 芯片的,mx6ull 开头的是 I.MX6ULL 开发板的。I.MX6UL/6ULL 有 9x9mm 和 14x14mm 两种尺寸的,所以我们可以看到会有mx6ull_9x9和mx6ull_14x14开头的默认配置文件。我们使用的是14x14mm的芯片,所以关注 mx6ull_14x14 开头的默认配置文件。正点原子的 I.MX6ULL 有 EMMC 和NAND 两个版本的,因此我们最终只需要关注 mx6ull_14x14_evk_emmc_defconfig 和mx6ull_14x14_evk_nand_defconfig 这两个配置文件就行了。本章我们讲解 EMMC 版本的移植(NAND 版本移植很多类似),所以使用 mx6ull_14x14_evk_emmc_defconfig 作为默认配置文件。
编译 NXP 官方开发板对应的 uboot
找到 NXP 官方 I.MX6ULL EVK 开发板对应的默认配置文件以后就可以编译一下,使用如下命令编译 uboot:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_evk_emmc_defconfig make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
编译完成以后结果如图 33.1.2.1 所示:
从图 33.1.2.1 可以看出,编译成功。我们在编译的时候需要输入 ARCH 和 CORSS_COMPILE这两个变量的值,这样太麻烦了。我们可以直接在顶层 Makefile 中直接给 ARCH 和CORSS_COMPILE 赋值,修改如图 33.1.2.2 所示:
图 33.1.2.2 中的 250、251 行就是直接给 ARCH 和 CROSS_COMPILE 赋值,这样我们就可以使用如下简短的命令来编译 uboot 了:
make mx6ull_14x14_evk_emmc_defconfig make V=1 -j16
如果既不想修改 uboot 的顶层 Makefile,又想编译的时候不用输入那么多,那么就直接创建个 shell 脚本就行了,shell 脚本名为 mx6ull_14x14_emmc.sh,然后在 shell 脚本里面输入如下内容:
示例代码 33.1.2.1 mx6ull_14x14_emmc.sh 文件 1 #!/bin/bash 2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean 3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf mx6ull_14x14_evk_emmc_defconfig 4 make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
记得给 mx6ull_14x14_emmc.sh 这个文件可执行权限,使用 mx6ull_14x14_emmc.sh 脚本编译 uboot 的时候每次都会清理一下工程,然后全部重新编译,编译的时候直接执行这个脚本就行了,命令如下:
./mx6ull_14x14_evk_emmc.sh
编译完成以后会生成 u-boot.bin、u-boot.imx 等文件,但是这些文件是 NXP 官方 I.MX6ULL EVK 开发板。能不能用到正点原子的 I.MX6ULL 开发板上呢?试一下不就知道了!
烧写验证与驱动测试
将 imxdownload 软件拷贝到 uboot 源码根目录下,然后使用 imxdownload 软件将 u-boot.bin烧写到 SD 卡中,烧写命令如下:
chmod 777 imxdownload //给予 imxdownload 可执行权限 ./imxdownload u-boot.bin /dev/sdd //烧写到 SD 卡中,不能烧写到/dev/sda 或 sda1 里面
烧写完成以后将 SD 卡插入 I.MX6U-ALPHA 开发板的 TF 卡槽中,最后设置开发板从 SD卡启动。打开 SecureCRT,设置好开发板所使用的串口并打开,复位开发板,SecureCRT 接收到如下图 33.1.3.1 所示信息:
从图 33.1.3.1 可以看出,uboot 启动正常,虽然我们用的是 NXP 官方 I.MX6ULL 开发板的uboot,但是在正点原子的 I.MX6ULL 开发板上是可以正常启动的。而且 DRAM 识别正确,为512MB,如果用的 NAND 版本的核心版的话 uboot 启动会失败!因为 NAND 核心版用的 256MB的 DRAM。
1、SD 卡和 EMMC 驱动检查
检查一下 SD 卡和 EMMC 驱动是否正常,使用命令 mmc list 列出当前的 MMC 设备,结果如图 33.1.3.2 所示:
从图 33.1.3.2 可以看出当前有两个 MMC 设备,检查每个 MMC 设备信息,先检查 MMC 设备 0,输入如下命令:
mmc dev 0 mmc info
结果如图 33.1.3.3 所示:
从图 33.1.3.3 可以看出,mmc 设备 0 是 SD 卡,SD 卡容量为 14.8GB,这个和我所使用的SD 卡信息相符,说明 SD 卡驱动正常。再来检查 MMC 设备 1,输入如下命令:
mmc dev 1 mmc info
结果如图 33.1.3.4 所示:
从图 33.1.3.4 可以看出,mmc 设备 1 为 EMMC,容量为 7.3GB,说明 EMMC 驱动也成功,SD 卡和 EMMC 的驱动都没问题。
实战情况如下:
可见,FSL_SDHC,也就是SD卡,无法识别。
这块不知道是驱动的问题,还是SD卡本身的问题。
把SD卡拔下来重新插入,再上电,就好了。
难道是SD卡接触不良?
2、LCD 驱动检查
如果 uboot 中的 LCD 驱动正确的话,启动 uboot 以后 LCD 上应该会显示出 NXP 的 logo,如下图 33.1.3.5 所示:
如果你用的不是正点原子的 4.3 寸 480x272 分辨率的屏幕的话,那么 LCD 就不会显示33.1.3.5 所示 logo 界面。因为 NXP 官方 I.MX6ULL 开发板的屏幕就是 4.3 寸 480x272 分辨率的,所以 uboot 默认 LCD 驱动是 4.3 寸 480x272 分辨率的。如果使用其他分辨率的 LCD 就需要修改 LCD 驱动,这里我们先不修改 LCD 驱动了,稍后我们在讲解如何修改 uboot 中的 LCD驱动,我们只需要记得,uboot 的 LCD 需要修改就行了。
3、网络驱动
uboot 启动的时候提示“Board Net Initialization Failed”和“No ethernet found.”这两行,说明网络驱动也有问题,正常情况下应该是如图 33.1.3.6 所示提示:
现在没有图 33.1.3.6 中的信息,那更别说 ping 一下 ubuntu 主机了,说明当前 uboot 的网络部驱动也是有问题的,这是因为正点原子开发板的网络芯片复位引脚和 NXP 官方开发板不一样,因此需要修改驱动。
总结一下 NXP 官方 I.MX6ULL EVK 开发板的 uboot 在正点原子 EMMC 版本 I.MX6ULL开发板上的运行情况:
①、uboot 启动正常,DRAM 识别正确,SD 卡和 EMMC 驱动正常。
②、uboot 里面的 LCD 驱动默认是给 4.3 寸 480x272 分辨率的,如果使用的其他分辨率的屏幕需要修改驱动。
③、网络不能工作,识别不出来网络信息,需要修改驱动。
接下来我们要做的工作如下:
①、前面我们一直使用着 NXP 官方开发板的 uboot 配置,接下来需要在 uboot 中添加我们自己的开发板,也就是正点原子的 I.MX6ULL 开发板。
②、解决 LCD 驱动和网络驱动的问题。
在 U-Boot 中添加自己的开发板
NXP 官方 uboot 中默认都是 NXP 自己的开发板,虽说我们可以直接在官方的开发板上直接修改,使 uboot 可以完整的运行在我们的板子上。但是从学习的角度来讲,这样我们就不能了解到 uboot 是如何添加新平台的。接下来我们就参考 NXP 官方的 I.MX6ULL EVK 开发板,学习如何在 uboot 中添加我们的开发板或者开发平台。
添加开发板默认配置文件
先在 configs 目录下创建默认配置文件,复制 mx6ull_14x14_evk_emmc_defconfig,然后重命名为 mx6ull_alientek_emmc_defconfig,命令如下:
cd configs cp mx6ull_14x14_evk_emmc_defconfig mx6ull_alientek_emmc_defconfig
然后将文件 mx6ull_alientek_emmc_defconfig 中的内容改成下面的:
示例代码 33.2.1.1 mx6ull_alientek_emmc_defconfig 文件 1 CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK" 2 CONFIG_ARM=y 3 CONFIG_ARCH_MX6=y 4 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y 5 CONFIG_CMD_GPIO=y
可以看出,mx6ull_alientek_emmc_defconfig 基本和 mx6ull_14x14_evk_emmc_defconfig 中的内容一样,只是第 1 行和第 4 行做了修改。
添加开发板对应的头文件
在 目 录 include/configs 下 添 加 I.MX6ULL-ALPHA 开 发 板 对 应 的 头 文 件 , 复 制include/configs/mx6ullevk.h,并重命名为 mx6ull_alientek_emmc.h,命令如下:
cp include/configs/mx6ullevk.h include/configs/mx6ull_alientek_emmc.h
拷贝完成以后将:
#ifndef __MX6ULLEVK_CONFIG_H #define __MX6ULLEVK_CONFIG_H
改为:
#ifndef __MX6ULL_ALIENTEK_EMMC_CONFIG_H #define __MX6ULL_ALIENTEK_EMMC_CONFIG_H
mx6ull_alientek_emmc.h 里面有很多宏定义,这些宏定义基本用于配置 uboot,也有一些I.MX6ULL 的配置项目。如果我们自己要想使能或者禁止 uboot 的某些功能,那就在mx6ull_alientek_emmc.h 里面做修改即可。mx6ull_alientek_emmc.h 里面的内容比较多,去掉一些用不到的配置,精简后的内容如下:
1 /* 2 * Copyright (C) 2016 Freescale Semiconductor, Inc. 3 * 4 * Configuration settings for the Freescale i.MX6UL 14x14 EVK board. 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 #ifndef __MX6ULL_ALEITENK_EMMC_CONFIG_H 9 #define __MX6ULL_ALEITENK_EMMC_CONFIG_H 10 11 12 #include <asm/arch/imx-regs.h> 13 #include <linux/sizes.h> 14 #include "mx6_common.h" 15 #include <asm/imx-common/gpio.h> 16 ...... 28 29 #define is_mx6ull_9x9_evk() CONFIG_IS_ENABLED(TARGET_MX6ULL_9X9_EVK) 30 31 #ifdef CONFIG_TARGET_MX6ULL_9X9_EVK 32 #define PHYS_SDRAM_SIZE SZ_256M 33 #define CONFIG_BOOTARGS_CMA_SIZE "cma=96M " 34 #else 35 #define PHYS_SDRAM_SIZE SZ_512M 36 #define CONFIG_BOOTARGS_CMA_SIZE "" 37 /* DCDC used on 14x14 EVK, no PMIC */ 38 #undef CONFIG_LDO_BYPASS_CHECK 39 #endif 40 41 /* SPL options */ 42 /* We default not support SPL 43 * #define CONFIG_SPL_LIBCOMMON_SUPPORT 44 * #define CONFIG_SPL_MMC_SUPPORT 45 * #include "imx6_spl.h" 46 */ 47 48 #define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG 49 50 #define CONFIG_DISPLAY_CPUINFO 51 #define CONFIG_DISPLAY_BOARDINFO 52 53 /* Size of malloc() pool */ 54 #define CONFIG_SYS_MALLOC_LEN (16 * SZ_1M) 55 56 #define CONFIG_BOARD_EARLY_INIT_F 57 #define CONFIG_BOARD_LATE_INIT 58 59 #define CONFIG_MXC_UART 60 #define CONFIG_MXC_UART_BASE UART1_BASE 61 62 /* MMC Configs */ 63 #ifdef CONFIG_FSL_USDHC 64 #define CONFIG_SYS_FSL_ESDHC_ADDR USDHC2_BASE_ADDR 65 66 /* NAND pin conflicts with usdhc2 */ 67 #ifdef CONFIG_SYS_USE_NAND 68 #define CONFIG_SYS_FSL_USDHC_NUM 1 69 #else 70 #define CONFIG_SYS_FSL_USDHC_NUM 2 71 #endif 72 #endif 73 74 /* I2C configs */ 75 #define CONFIG_CMD_I2C 76 #ifdef CONFIG_