目录
1、设置 I.MX6U-ALPHA 开发板工作在 792MHz
1.Linux内核获取
Linux 官网为 https://www.kernel.org,
2.内核初次编译
编译内核之前需要先在
ubuntu
上安装
lzop
库,否则内核编译会失败!命令如下:
sudo apt-get install lzop
解压完成以后的 Linux 源码根目录如图 35.2.1 所示:
新建编译脚本内容如下:
1 # ! /bin/sh2 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - distclean3 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - imx_v7_defconfig4 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - menuconfig5 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - all - j16
Linux
的编译过程基本和
uboot
一样,都要先执行“
make xxx_defconfig
”来配置
一下,然后在执行“
make
”进行编译。如果需要使用图形界面配置的话就执行“
make menuconfig
”。
编译完成以后就会在
arch/arm/boot
这个目录下生成一个叫做
zImage
的文件,
zImage
就是
我们要用的
Linux
镜像文件。另外也会在
arch/arm/boot/dts
下生成很多
.dtb
文件,这些
.dtb
就是
设备树文件。
编译
Linux
内核的时候可能会提示“
recipe for target ‘arch/arm/boot/compressed/piggy.lzo’
failed”,原因是没有安装 lzop 库,输入如下命令安装 lzop 库,即可解决:
sudo apt-get install lzop
3.内核顶层Makefile介绍
3.1版本号
示例代码 35.5.1 顶层 Makefile 代码段1 VERSION = 42 PATCHLEVEL = 13 SUBLEVEL = 154 EXTRAVERSION =
3.2、MAKEFLAGS 变量
示例代码 35.5.2 顶层 Makefile 代码段16 MAKEFLAGS += - rR -- include - dir = $ ( CURDIR )
3.3、命令输出
Linux
编译的时候也可以通过“
V=1
”来输出完整的命令,这个和
uboot
一样,相关代码如
下所示;
示例代码 35.5.3 顶层 Makefile 代码段69 ifeq ( "$(origin V)" , "command line" )70 KBUILD_VERBOSE = $ ( V )71 endif72 ifndef KBUILD_VERBOSE73 KBUILD_VERBOSE = 074 endif7576 ifeq ( $ ( KBUILD_VERBOSE ), 1 )77 quiet =78 Q =79 else80 quiet = quiet_81 Q = @82 endif
3.4、静默输出
Linux
编译的时候使用“
make -s
”就可实现静默编译,编译的时候就不会打印任何的信息,
同
uboot
一样,相关代码如下:
示例代码 35.5.4 顶层 Makefile 代码段87 ifneq ( $ ( filter 4. %, $ ( MAKE_VERSION )),) # make - 488 ifneq ( $ ( filter % s , $ ( firstword x$ ( MAKEFLAGS ))),)89 quiet = silent_90 endif91 else # make - 3.8x92 ifneq ( $ ( filter s % - s %, $ ( MAKEFLAGS )),)93 quiet = silent_94 endif95 endif9697 export quiet Q KBUILD_VERBOSE
3.5、设置编译结果输出目录
Linux
编译的时候使用“
O=xxx
”即可将编译产生的过程文件输出到指定的目录中,相关代
码如下:
示例代码 35.5.5 顶层 Makefile 代码段116 ifeq ( $ ( KBUILD_SRC ),)117118 # OK , Make called in directory where kernel src resides119 # Do we want to locate output files in a separate directory ?120 ifeq ( "$(origin O)" , "command line" )121 KBUILD_OUTPUT := $ ( O )122 endif
3.6代码检查
Linux
也支持代码检查,使用命令“
make C=1
”使能代码检查,检查那些需要重新编译的
文件。“
make C=2
”用于检查所有的源码文件,顶层
Makefile
中的代码如下:
示例代码 35.5.6 顶层 Makefile 代码段172 ifeq ( "$(origin C)" , "command line" )173 KBUILD_CHECKSRC = $ ( C )174 endif175 ifndef KBUILD_CHECKSRC176 KBUILD_CHECKSRC = 0177 endif
3. 7、模块编译
Linux
允许单独编译某个模块,使用命令“
make M=dir
”即可,旧语法“
make SUBDIRS=dir
”
也是支持的。顶层
Makefile
中的代码如下:
示例代码 35.5.7 顶层 Makefile 代码段179 # Use make M = dir to specify directory of external module to build180 # Old syntax make ... SUBDIRS = $PWD is still supported181 # Setting the environment variable KBUILD_EXTMOD take precedence182 ifdef SUBDIRS183 KBUILD_EXTMOD ?= $ ( SUBDIRS )184 endif185186 ifeq ( "$(origin M)" , "command line" )187 KBUILD_EXTMOD := $ ( M )188 endif189190 # If building an external module we do not care about the all : rule191 # but instead _all depend on modules192 PHONY += all193 ifeq ( $ ( KBUILD_EXTMOD ),)194 _all : all195 else196 _all : modules197 endif198199 ifeq ( $ ( KBUILD_SRC ),)200 # building in the source tree201 srctree := .202 else203 ifeq ( $ ( KBUILD_SRC )/, $ ( dir $ ( CURDIR )))204 # building in a subdirectory of the source tree205 srctree := ..206 else207 srctree := $ ( KBUILD_SRC )208 endif209 endif210 objtree := .211 src := $ ( srctree )212 obj := $ ( objtree )213214 VPATH := $ ( srctree ) $ ( if $ ( KBUILD_EXTMOD ),: $ ( KBUILD_EXTMOD ))215216 export srctree objtree VPATH
外部模块编译过程和
uboot
也一样,最终导出
srctree
、
objtree
和
VPATH
这三个变量的值,
其中
srctree=.
,也就是当前目录,
objtree
同样为“
.
”。
3.8、设置目标架构和交叉编译器
同
uboot
一样,
Linux
编译的时候需要设置目标板架构
ARCH
和交叉编译器
CROSS_COMPILE
,
在顶层
Makefile
中代码如下:
示例代码 35.5.8 顶层 Makefile 代码段252 ARCH?= $ ( SUBARCH )253 CROSS_COMPILE ?= $ ( CONFIG_CROSS_COMPILE : "%" =%)
为了方便,一般直接修改顶层
Makefile
中的
ARCH
和
CROSS_COMPILE
,直接将其设置
为对应的架构和编译器,比如本教程将
ARCH
设置为为
arm
,
CROSS_COMPILE
设置为
arm-
linux-gnueabihf-
,如下所示:
示例代码 35.5.9 顶层 Makefile 代码段252 ARCH?= arm253 CROSS_COMPILE ?= arm-linux-gnueabihf-
设置好以后我们就可以使用如下命令编译
Linux
了:
make xxx_defconfig //使用默认配置文件配置 Linuxmake menuconfig //启动图形化配置界面make -j16 //编译 Linux
3.9、交叉编译工具变量设置
示例代码 35.5.11 顶层 Makefile 代码段353 AS = $ ( CROSS_COMPILE ) as354 LD = $ ( CROSS_COMPILE ) ld355 CC = $ ( CROSS_COMPILE ) gcc356 CPP = $ ( CC ) - E357 AR = $ ( CROSS_COMPILE ) ar358 NM = $ ( CROSS_COMPILE ) nm359 STRIP = $ ( CROSS_COMPILE ) strip360 OBJCOPY = $ ( CROSS_COMPILE ) objcopy361 OBJDUMP = $ ( CROSS_COMPILE ) objdump
LA、LD、CC 等这些都是交叉编译器所使用的工具。
4.内核移植
1.修改nxp顶层Makefile
修改顶层
Makefile
,直接在顶层
Makefile
文件里面定义
ARCH
和
CROSS_COMPILE
这两
个的变量值为
arm
和
arm-linux-gnueabihf-
,
2. 配置并编译 Linux 内核
和
uboot
一样,在编译
Linux
内核之前要先配置
Linux
内核。每个板子都有其对应的默认
配 置 文 件 , 这 些 默 认 配 置 文 件 保 存 在
arch/arm/configs
目录中。
imx_v7_defconfig
和
imx_v7_mfg_defconfig
都可作为
I.MX6ULL EVK
开发板所使用的默认配置文件。但是这里建议
使用
imx_v7_mfg_defconfig
这个默认配置文件,首先此配置文件默认支持
I.MX6UL
这款芯片,
而且重要的一点就是此文件编译出来的
zImage
可以通过
NXP
官方提供的
MfgTool
工具烧写!!
imx_v7_mfg_defconfig
中的“
mfg
”的意思就是
MfgTool
。
进入到
Ubuntu
中的
Linux
源码根目录下,执行如下命令配置
Linux
内核:
make clean//第一次编译 Linux 内核之前先清理一下make imx_v7_mfg_defconfig //配置 Linux 内核make -j16 //编译 Linux 内核
Linux
内核编译完成以后会在
arch/arm/boot
目录下生成
zImage
镜像文件,如果使用设备树
的话还会在
arch/arm/boot/dts
目录下开发板对应的
.dtb(
设备树
)
文件,比如
imx6ull-14x14-evk.dtb
就是
NXP
官方的
I.MX6ULL EVK
开发板对应的设备树文件。至此我们得到两个文件:
①、
Linux
内核镜像文件:
zImage
。
②、
NXP
官方
I.MX6ULL EVK
开发板对应的设备树文件:
imx6ull-14x14-evk.dtb
。
5.在 Linux 中添加自己的开发板
1.添加开发板默认配置文件
将
arch/arm/configs
目 录 下 的
imx_v7_mfg_defconfig
重 新 复 制 一 份 , 命 名 为
imx_alientek_emmc_defconfig
,命令如下:
cd arch/arm/configscp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig
以后就可以使用如下命令来配置正点原子 EMMC 版开发板对应的 Linux 内核了:
make imx_alientek_emmc_defconfig
2.添加开发板对应的设备树文件
添加适合正点原子
EMMC
版开发板的设备树文件,进入目录
arch/arm/boot/dts
中,复制一
份
imx6ull-14x14-evk.dts
,然后将其重命名为
imx6ull-alientek-emmc.dts
,命令如下:
cd arch/arm/boot/dtscp imx6ull-14x14-evk.dts imx6ull-alientek-emmc.dts
.dts
是设备树源码文件,编译
Linux
的时候会将其编译为
.dtb
文件。
imx6ull-alientek-emmc.dts
创 建 好 以 后 我 们 还 需 要 修 改 文 件
arch/arm/boot/dts/Makefile
, 找 到 “
dtb-
$(CONFIG_SOC_IMX6ULL)
”配置项,在此配置项中加入“
imx6ull-alientek-emmc.dtb
” ,如下
所示:
示例代码 37.3.2.1 arch/arm/boot/dts/Makefile 代码段400 dtb - $ ( CONFIG_SOC_IMX6ULL ) += \401 imx6ull - 14x14 - ddr3 - arm2 . dtb \402 imx6ull - 14x14 - ddr3 - arm2 - adc . dtb \403 imx6ull - 14x14 - ddr3 - arm2 - cs42888 . dtb \404 imx6ull - 14x14 - ddr3 - arm2 - ecspi . dtb \405 imx6ull - 14x14 - ddr3 - arm2 - emmc . dtb \406 imx6ull - 14x14 - ddr3 - arm2 - epdc . dtb \407 imx6ull - 14x14 - ddr3 - arm2 - flexcan2 . dtb \408 imx6ull - 14x14 - ddr3 - arm2 - gpmi - weim . dtb \409 imx6ull - 14x14 - ddr3 - arm2 - lcdif . dtb \410 imx6ull - 14x14 - ddr3 - arm2 - ldo . dtb \411 imx6ull - 14x14 - ddr3 - arm2 - qspi . dtb \412 imx6ull - 14x14 - ddr3 - arm2 - qspi - all . dtb \413 imx6ull - 14x14 - ddr3 - arm2 - tsc . dtb \414 imx6ull - 14x14 - ddr3 - arm2 - uart2 . dtb \415 imx6ull - 14x14 - ddr3 - arm2 - usb . dtb \416 imx6ull - 14x14 - ddr3 - arm2 - wm8958 . dtb \417 imx6ull - 14x14 - evk . dtb \418 imx6ull - 14x14 - evk - btwifi . dtb \419 imx6ull - 14x14 - evk - emmc . dtb \420 imx6ull - 14x14 - evk - gpmi - weim . dtb \421 imx6ull - 14x14 - evk - usb - certi . dtb \422 imx6ull - alientek - emmc . dtb \
第
422
行为“
imx6ull-alientek-emmc.dtb
”,这样编译
Linux
的时候就可以从
imx6ull-alientek
emmc.dts
编译出
imx6ull-alientek-emmc.dtb
文件了。
3.编译脚本
1 # ! /bin/sh2 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - distclean3 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihfimx_alientek_emmc_defconfig4 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - menuconfig5 make ARCH = arm CROSS_COMPILE = arm - linux - gnueabihf - all - j16
4.CPU 主频和网络驱动修改
正点原子
I.MX6U-ALPHA
开发板所使用的
I.MX6ULL
芯片主频都是
792MHz
的,也就是
NXP
官方宣传的
800MHz
版本。后续可能会生产
528MHz
核心板供企业级批量用户,但是开发
板搭配的都是
792MHz
主频的,
1、设置 I.MX6U-ALPHA 开发板工作在 792MHz
进入图
37.4.1.1
所示的命令行以后输入如下命令查看
cpu
信息:
cat /proc/cpuinfo
在图
37.4.1.2
中有
BogoMIPS
这一条,此时
BogoMIPS
为
3.00
,
BogoMIPS
是
Linux
系统中
衡量处理器运行速度的一个“尺子”,处理器性能越强,主频越高,
BogoMIPS
值就越大。
BogoMIPS
只是粗略的计算
CPU
性能,并不十分准确。但是我们可以通过
BogoMIPS
值来大致
的判断当前处理器的性能。在图
37.4.1.2
中并没有看到当前
CPU
的工作频率,那我们就转变另
一种方法查看当前
CPU
的工作频率。进入到目录
/sys/bus/cpu/devices/cpu0/cpufreq
中,此目录下 会有很多文件,如图 37.4.1.3
所示:
此目录中记录了
CPU
频率等信息,这些文件的含义如下:
cpuinfo_cur_freq
:当前
cpu
工作频率,从
CPU
寄存器读取到的工作频率。
cpuinfo_max_freq
:处理器所能运行的最高工作频率
(
单位
: KHz
)。
cpuinfo_min_freq
:处理器所能运行的最低工作频率
(
单位
: KHz
)。
cpuinfo_transition_latency
:处理器切换频率所需要的时间
(
单位
:ns)
。
scaling_available_frequencies
:处理器支持的主频率列表
(
单位
: KHz
)。
scaling_available_governors
:当前内核中支持的所有
governor(
调频
)
类型。
scaling_cur_freq
:保存着
cpufreq
模块缓存的当前
CPU
频率,不会对
CPU
硬件寄存器进
行检查。
scaling_driver
:该文件保存当前
CPU
所使用的调频驱动。
scaling_governor
:
governor(
调频
)
策略,
Linux
内核一共有
5
中调频策略,
①、
Performance
,最高性能,直接用最高频率,不考虑耗电。
②、
Interactive
,一开始直接用最高频率,然后根据
CPU
负载慢慢降低。
③、
Powersave
,省电模式,通常以最低频率运行,系统性能会受影响,一般不会用这个!
④、
Userspace
,可以在用户空间手动调节频率。
⑤、
Ondemand
,定时检查负载,然后根据负载来调节频率。负载低的时候降低
CPU
频率,
这样省电,负载高的时候提高
CPU
频率,增加性能。
scaling_max_freq
:
governor(
调频
)
可以调节的最高频率。
cpuinfo_min_freq
:
governor(
调频
)
可以调节的最低频率。
stats 目录下给出了CPU 各种运行频率的统计情况,比如 CPU 在各频率下的运行时间以及
变频次数。
使用如下命令查看当前
CPU
频率:
cat cpuinfo_cur_freq
当前
CPU
支持
198MHz
、
396MHz
、
528Mhz
和
792MHz
四种频率切换,其中调 频策略为 ondemand
,也就是定期检查负载,然后根据负载情况调节
CPU
频率。因为当前我们 开发板并没有做什么工作,因此 CPU
频率降低为
198MHz
以省电。如果开发板做一些高负载的 工作,比如播放视频等操作那么 CPU
频率就会提升上去。查看
stats
目录下的
time_in_state
文 件可以看到 CPU
在各频率下的工作时间,命令如下:
cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state
假如我们想让
CPU
一直工作在
792MHz
那该怎么办?很简单, 配置 Linux
内核,将调频策略选择为
performance
。或者修改
imx_alientek_emmc_defconfig
文件, 此文件中有下面几行:
41 CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND = y42 CONFIG_CPU_FREQ_GOV_POWERSAVE = y43 CONFIG_CPU_FREQ_GOV_USERSPACE = y44 CONFIG_CPU_FREQ_GOV_INTERACTIVE = y
第
41
行,配置
ondemand
为默认调频策略。
第
42
行,使能
powersave
策略。
第
43
行,使能
userspace
策略。
第
44
行,使能
interactive
策略。
将
第
41
行屏蔽掉,然后在
44
行后面添加:
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
修改完成以后重新编译
Linux
内核,编译之前先清理一下工程!因为我们重新修改过默认
配置文件了,编译完成以后使用新的
zImage
镜 像 文 件 重新 启动
Linux
。
我们再来看一下如何通过图形化界面配置
Linux
内核的
CPU
调频策略,输入“
make
menuconfig
”打开
Linux
内核的图形化配置界面,如图
37.4.1.8
所示:
进入以下路径
CPU Power Management-> CPU Frequency scaling-> Default CPUFreq governor
选择“
performance
”即可,选择以后退出图形化配置界面,然后编译
Linux
内核,一定不要清理工程!否则的话我们刚刚的设置就会被清理掉。
2、超频至 700MHz
超频设置其实很简单,修改一下设备树文件
arch/arm/boot/dts/imx6ull.dtsi
即可,打开
imx6ull.dtsi
,找到下面代码:
示例代码 37.4.1.3 imx6ull.dtsi 文件代码段54 cpu0 : cpu@0 {55 compatible = "arm,cortex-a7" ;56 device_type = "cpu" ;57 reg = < 0 >;58 clock - latency = < 61036 >; /* two CLK32 periods */59 operating - points = <60 /* kHz uV */61 996000 127500062 792000 122500063 528000 117500064 396000 102500065 198000 95000066 >;67fsl , soc - operating - points = <68/* KHz uV */69996000 117500070792000 117500071528000 117500072396000 117500073198000 117500074 >;
修改以后代码如下:
56 device_type = "cpu" ;57 reg = < 0 >;58 clock - latency = < 61036 >; /* two CLK32 periods */59 operating - points = <60 /* kHz uV */61 996000 127500062 792000 122500063 696000 122500064 528000 117500065 396000 102500066 198000 95000067 >;68fsl , soc - operating - points = <69/* KHz uV */70996000 117500071792000 117500072696000 117500073528000 117500074396000 117500075198000 117500076 >;
第
63
行,加入了“
696000 1225000
”,这个就是
696MHz
的支持。
第
72
行,加入了“
696000 1175000
”,也是对
696MHz
的支持。
修改好以后保存,并且编译设备树,在
Linux
内核源码根目录下输入如下命令编译设备树:
make dtbs
3.使能 8 线 EMMC 驱动
修改方法很简单,直接修改设备树即可,打开文 件 imx6ull-alientek-emmc.dts
,找到如下所示内容:
734 & usdhc2 {735 pinctrl - names = "default" ;736 pinctrl - 0 = <& pinctrl_usdhc2 >;737 non - removable ;738 status = "okay" ;739 };
只需要将其改为如下代码即可:
734 & usdhc2 {735 pinctrl - names = "default" , "state_100mhz" , "state_200mhz" ;736 pinctrl - 0 = <& pinctrl_usdhc2_8bit >;737 pinctrl - 1 = <& pinctrl_usdhc2_8bit_100mhz >;738 pinctrl - 2 = <& pinctrl_usdhc2_8bit_200mhz >;739 bus - width = < 8 >;740 non - removable ;741 status = "okay" ;742 };
修改完成以后保存一下
imx6ull-alientek-emmc.dts
,然后使用命令“
make dtbs
”重新编译一
下设备树,编译完成以后使用新的设备树重启
Linux
系统即可。
4.修改网络驱动
正点原子开发板的网络和
NXP
官方的网络硬件上 不同,网络 PHY
芯片由
KSZ8081
换为了
LAN8720A
,两个网络
PHY
芯片的复位
IO
也不同。 所以 Linux
内核自带的网络驱动是驱动不起来
I.MX6U-ALPHA
开发板上的网络的,需要做修 改。
1
、修改
LAN8720
的复位以及网络时钟引脚驱动
打开设备树文件
imx6ull-alientek-emmc.dts,找到如下代码:
584 pinctrl_spi4 : spi4grp {585 fsl , pins = <586MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1587MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1588MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1589MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000590 >;591 };
第
588
和
589
行就是初始化
SNVS_TAMPER7
和
SNVS_TAMPER8
这两个 引脚的,不过看样子好像是作为了 SPI4
的
IO
,这不是我们想要的,所以将
588
和
589
这两行 删除掉!删除掉以后继续在 imx6ull-alientek-emmc.dts
中找到如下所示代码:
125 spi4 {126 compatible = "spi-gpio" ;127 pinctrl - names = "default" ;128 pinctrl - 0 = <& pinctrl_spi4 >;129 pinctrl - assert - gpios = <& gpio5 8 GPIO_ACTIVE_LOW >;......133 cs - gpios = <& gpio5 7 0 >;
第
129
行,设置
GPIO5_IO08
为
SPI4
的一个功能引脚
(
我也不清楚具体作为什么功能用
)
,
而
GPIO5_IO08
就是
SNVS_TAMPER8
的
GPIO
功能引脚。
第
133
行,设置
GPIO5_IO07
作为
SPI4
的片选引脚,而
GPIO5_IO07
就是
SNVS_TAMPER7
的
GPIO
功能引脚。
现在我们需要
GPIO5_IO07
和
GPIO5_IO08
分别作为
ENET1
和
ENET2
的复位引脚,而不
是
SPI4
的什么功能引脚,因此将示例代码
37.4.3.2
中的第
129
行和第
133
行处的代码删除掉!!
否则会干扰到网络复位引脚!
在
imx6ull-alientek-emmc.dts
里面找到名为“
iomuxc_snvs
”的节点
(
就是直接搜索
)
,然后在
此节点下添加网络复位引脚信息,添加完成以后的“
iomuxc_snvs
”的节点内容如下:
1 & iomuxc_snvs {2 pinctrl - names = "default_snvs" ;3 pinctrl - 0 = <& pinctrl_hog_2 >;4 imx6ul - evk {5....../* 省略掉其他 */4344/*enet1 reset zuozhongkai*/45pinctrl_enet1_reset : enet1resetgrp {46fsl , pins = <47/* used for enet1 reset */48MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B049>;50};5152/*enet2 reset zuozhongkai*/53pinctrl_enet2_reset : enet2resetgrp {54fsl , pins = <55/* used for enet2 reset */56MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B057>;58};59 };60 };
第
1
行,
imx6ull-alientek-emmc.dts
文件中
iomuxc_snvs
节点。
第
45~50
行,
ENET1
网络复位引脚配置信息。
第
53~58
行,
ENET2
网络复位引脚配置信息。
最后还需要修改一下
ENET1
和
ENET2
的网络时钟引脚配置,继续在
imx6ull-alientek
emmc.dts
中找到如下所示代码:
309 pinctrl_enet1 : enet1grp {310 fsl , pins = <311 MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN0x1b0b0312 MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER0x1b0b0313 MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0314 MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0315 MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN0x1b0b0316 MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0317 MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0318 MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009319 >;320 };321322 pinctrl_enet2 : enet2grp {323 fsl , pins = <324 MX6UL_PAD_GPIO1_IO07__ENET2_MDC0x1b0b0325 MX6UL_PAD_GPIO1_IO06__ENET2_MDIO0x1b0b0326 MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN0x1b0b0327 MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER0x1b0b0328 MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0329 MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0330 MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN0x1b0b0331 MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0332 MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0333 MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b009334 >;335 };
第
318
和
333
行,分别为
ENET1
和
ENET2
的网络时钟引脚配置信息,将这两个引脚的电
气属性值改为
0x4001b009
,原来默认值为
0x4001b031
。
修改完成以后记得保存一下
imx6ull-alientek-emmc.dts
,网络复位以及时钟引脚驱动就修改
好了。
2
、修改
fec1
和
fec2
节点的
pinctrl-0
属性
在
imx6ull-alientek-emmc.dts
文件中找到名为“
fec1
”和“
fec2
”的这两个节点,修改其中的
“
pinctrl-0
”属性值,修改以后如下所示:
1 & fec1 {2 pinctrl - names = "default" ;3pinctrl - 0 = <& pinctrl_enet14& pinctrl_enet1_reset >;5phy - mode = "rmii" ;......9status = "okay" ;10 };1112 & fec2 {13pinctrl - names = "default" ;14pinctrl - 0 = <& pinctrl_enet215 & pinctrl_enet2_reset >;16phy - mode = "rmii" ;......36 };
第
3~4
行,修改后的
fec1
节点“
pinctrl-0
”属性值。
第
14~15
行,修改后的
fec2
节点“
pinctrl-0
”属性值。
3
、修改
LAN8720A
的
PHY
地址
ENET1
的
LAN8720A
地址为
0x0
,
ENET2
的
LAN8720A 地址为 0x1
。在
imx6ull-alientek-emmc.dts
中找到如下代码:
171 & fec1 {172 pinctrl - names = "default" ;......175 phy - handle = <& ethphy0 >;176 status = "okay" ;177 };178179 & fec2 {180 pinctrl - names = "default" ;......183 phy - handle = <& ethphy1 >;184 status = "okay" ;185186 mdio {187 #address - cells = < 1 >;188 #size - cells = < 0 >;189190 ethphy0 : ethernet - phy@0 {191 compatible = "ethernet-phy-ieee802.3-c22" ;192 reg = < 2 >;193 };194195 ethphy1 : ethernet - phy@1 {196 compatible = "ethernet-phy-ieee802.3-c22" ;197 reg = < 1 >;198 };199 };200 };
第
171~177
行,
ENET1
对应的设备树节点。
第
179~200
行,
ENET2
对应的设备树节点。但是第
186~198
行的
mdio
节点描述了
ENET1
和
ENET2
的
PHY
地址信息。将示例代码
37.4.3.6
改为如下内容:
171 & fec1 {172 pinctrl - names = "default" ;173 pinctrl - 0 = <& pinctrl_enet1174 & pinctrl_enet1_reset >;175 phy - mode = "rmii" ;176 phy - handle = <& ethphy0 >;177 phy - reset - gpios = <& gpio5 7 GPIO_ACTIVE_LOW >;178 phy - reset - duration = < 200 >;179 status = "okay" ;180 };181182 & fec2 {183 pinctrl - names = "default" ;184 pinctrl - 0 = <& pinctrl_enet2185 & pinctrl_enet2_reset >;186 phy - mode = "rmii" ;187 phy - handle = <& ethphy1 >;188 phy - reset - gpios = <& gpio5 8 GPIO_ACTIVE_LOW >;189 phy - reset - duration = < 200 >;190 status = "okay" ;191192 mdio {193 #address - cells = < 1 >;194 #size - cells = < 0 >;195196 ethphy0 : ethernet - phy@0 {197 compatible = "ethernet-phy-ieee802.3-c22" ;198 smsc , disable - energy - detect ;199 reg = < 0 >;200 };201202 ethphy1 : ethernet - phy@1 {203 compatible = "ethernet-phy-ieee802.3-c22" ;204 smsc , disable - energy - detect ;205 reg = < 1 >;206 };207 };208 };
第
177
和
178
行,添加了
ENET1
网络复位引脚所使用的
IO
为
GPIO5_IO07
,低电平有效。
复位低电平信号持续时间为
200ms
。
第
188
和
189
行,
ENET2
网络复位引脚所使用的
IO
为
GPIO5_IO08
,同样低电平有效,持
续时间同样为
200ms
。
第
198
和
204
行,“
smsc,disable-energy-detect
”表明
PHY
芯片是
SMSC
公司的,这样
Linux
内核就会找到
SMSC
公司的
PHY
芯片驱动来驱动
LAN8720A
。
第
196
行,注意“
ethernet-phy@
”后面的数字是
PHY
的地址,
ENET1
的
PHY
地址为
0
,
所以“
@
”后面是
0(
默认为
2)
。
第
199
行,
reg
的值也表示
PHY
地址,
ENET1
的
PHY
地址为
0
,所以
reg=0
。
第
202
行,
ENET2
的
PHY
地址为
1
,因此“
@
”后面为
1
。
第
205
行,因为
ENET2
的
PHY
地址为
1
,所以
reg=1
。
至此,
LAN8720A
的
PHY
地址就改好了,保存一下
imx6ull-alientek-emmc.dts
文件。然后
使用“
make dtbs
”命令重新编译一下设备树。
3
、修改
fec_main.c
文件
要 在
I.MX6ULL
上 使 用
LAN8720A
, 需 要 修 改 一 下
Linux
内 核 源 码 , 打 开
drivers/net/ethernet/freescale/fec_main.c
,找到函数
fec_probe
,在
fec_probe
中加入如下代码:
3438 static int3439 fec_probe ( struct platform_device * pdev )3440 {3441 struct fec_enet_private * fep ;3442 struct fec_platform_data * pdata ;3443 struct net_device * ndev ;3444 int i , irq , ret = 0 ;3445 struct resource * r ;3446 const struct of_device_id * of_id ;3447 static int dev_id ;3448 struct device_node * np = pdev -> dev . of_node , * phy_node ;3449 int num_tx_qs ;3450 int num_rx_qs ;34513452 /* 设置 MX6UL_PAD_ENET1_TX_CLK 和 MX6UL_PAD_ENET2_TX_CLK3453 * 这两个 IO 的复用寄存器的 SION 位为 1 。3454 */3455 void __iomem * IMX6U_ENET1_TX_CLK ;3456 void __iomem * IMX6U_ENET2_TX_CLK ;34573458 IMX6U_ENET1_TX_CLK = ioremap ( 0X020E00DC , 4 );3459 writel ( 0X14 , IMX6U_ENET1_TX_CLK );34603461 IMX6U_ENET2_TX_CLK = ioremap ( 0X020E00FC , 4 );3462 writel ( 0X14 , IMX6U_ENET2_TX_CLK );3463......3656 return ret ;3657 }
第
3455~3462
就是新加入的代码,如果要在
I.MX6ULL
上使用
LAN8720A 就需要设置ENET1
和
ENET2
的
TX_CLK
引脚复位寄存器的
SION
位为
1
。
4
、配置
Linux
内核,使能
LAN8720
驱动
输入命令“
make menuconfig
”,打开图形化配置界面,选择使能
LAN8720A
的驱动,路径
如下:
-> Device Drivers-> Network device support-> PHY Device support and infrastructure-> Drivers for SMSC PHYs
图
37.4.3.1
中选择将“
Drivers for SMSC PHYs
”编译到
Linux
内核中,因此“
<>
”里面变
为了“
*
”。
LAN8720A
是
SMSC
公司出品的,因此勾选这个以后就会编译
LAN8720
驱动,配
置好以后退出配置界面,然后重新编译一下
Linux
内核。
5
、修改
smsc.c
文件
在测试
NFS
挂载文件系统的时候发现文件系统挂载成功率很低!老是提示
NFS
服务器找不
到,三四次就有一次挂载失败!很折磨人。
NFS
挂载就是通过网络来挂载文件系统,这样做的
好处就是方便我们后续调试
Linux
驱动。既然老是挂载失败那么可以肯定的是网络驱动有问题,
网络驱动分两部分:内部
MAC+
外部
PHY
,内部
MAC
驱动是由
NXP
提供的,一般不会出问
题,否则的话用户早就给
NXP
反馈了。而且我用
NXP
官方的开发板测试网络是一直正常的,
但是
NXP
官方的开发板所使用的
PHY
芯片为
KSZ8081
。所以只有可能是外部
PHY
,也就是
LAN8720A
的驱动可能出问题了。鉴于
LAN8720A
有“前车之鉴”,那就是在
uboot
中需要对
LAN8720A
进行一次软复位,要设置
LAN8720A
的
BMCR(
寄存器地址为
0)
寄存器
bit15
为
1
。
所以我猜测,在
Linux
中也需要对
LAN8720A
进行一次软复位。
首先需要找到
LAN8720A
的驱动文件,
LAN8720A
的驱动文件是
drivers/net/phy/smsc.c
,
在此文件中有个叫做
smsc_phy_reset
的函数,看名字都知道这是
SMSC PHY
的复位函数,因
此,
LAN8720A
肯定也会使用到这个复位函数,修改此函数的内容,修改以后的
smsc_phy_reset
函数内容如下所示:
1 static int smsc_phy_reset ( struct phy_device * phydev )2 {3int err , phy_reset ;4int msec = 1 ;5struct device_node * np ;6int timeout = 50000 ;7if ( phydev -> addr == 0 ) /* FEC1 */ {8np = of_find_node_by_path ( "/soc/aips-bus@02100000/ethernet@02188000" );9if ( np == NULL ) {10return - EINVAL ;11}12}1314if ( phydev -> addr == 1 ) /* FEC2 */ {15np = of_find_node_by_path ( "/soc/aips-bus@02000000/ethernet@020b4000" );16if ( np == NULL ) {17return - EINVAL ;18}19}2021err = of_property_read_u32 ( np , "phy-reset-duration" , & msec );22/* A sane reset duration should not be longer than 1s */23if (! err && msec > 1000 )24msec = 1 ;25phy_reset = of_get_named_gpio ( np , "phy-reset-gpios" , 0 );26if (! gpio_is_valid ( phy_reset ))27return ;2829gpio_direction_output ( phy_reset , 0 );30gpio_set_value ( phy_reset , 0 );31msleep ( msec );32gpio_set_value ( phy_reset , 1 );3334int rc = phy_read ( phydev , MII_LAN83C185_SPECIAL_MODES );35if ( rc < 0 )36return rc ;3738/* If the SMSC PHY is in power down mode, then set it39 * in all capable mode before using it.40 */41if (( rc & MII_LAN83C185_MODE_MASK ) ==MII_LAN83C185_MODE_POWERDOWN ) {4243/* set "all capable" mode and reset the phy */44rc |= MII_LAN83C185_MODE_ALL ;45phy_write ( phydev , MII_LAN83C185_SPECIAL_MODES , rc );46}4748phy_write ( phydev , MII_BMCR , BMCR_RESET );49/* wait end of reset (max 500 ms) */5051do {52udelay ( 10 );53if ( timeout -- == 0 )54return - 1 ;55rc = phy_read ( phydev , MII_BMCR );56} while ( rc & BMCR_RESET );57return 0 ;58 }
第
7~12
行,获取
FEC1
网卡对应的设备节点。
第
14~19
行,获取
FEC2
网卡对应的设备节点。
第
21
行,从设备树中获取“
phy-reset-duration
”属性信息,也就是复位时间。
第
25
行,从设备树中获取“
phy-reset-gpios
”属性信息,也就是复位
IO
。
第
29~32
行,设置
PHY
的复位
IO
,复位
LAN8720A
。
第
41~48
行,以前的
smsc_phy_reset
函数会判断
LAN8720
是否处于
Powerdown
模式,只
有处于
Powerdown
模式的时候才会软复位
LAN8720
。这里我们将软复位代码移出来,这样每
次调用
smsc_phy_reset
函数
LAN8720A
都会被软复位。
最后我们还需要在
drivers/net/phy/smsc.c
文件中添加两个头文件,因为修改后的
smsc_phy_reset
函数用到了
gpio_direction_output
和
gpio_set_value
这两个函数,需要添加的头
文件如下所示:
#include <linux/of_gpio.h>#include <linux/io.h>
保存修改后的图形化配置文件
在修改网络驱动的时候我们通过图形界面使能了
LAN8720A
的驱动,使能以后会在
.config
中存在如下代码:
CONFIG_SMSC_PHY=y
打开 drivers/net/phy/Makefile,有如下代码:
11 obj - $ ( CONFIG_SMSC_PHY ) += smsc . o
当
CONFIG_SMSC_PHY=y
的时候就会编译
smsc.c
这个文件,
smsc.c
就是
LAN8720A
的驱
动文件。但是当我们执行“
make clean
”清理工程以后
.config
文件就会被删除掉,因此我们所有
原子哥在线教学
:www.yuanzige.com
论坛
:www.openedv.com
978
I.MX6U
嵌入式
Linux
驱动开发指南
的配置内容都会丢失,结果就是前功尽弃,一“删”回到解放前!所以我们在配置完图形界面
以后经过测试没有问题,就必须要保存一下配置文件。保存配置的方法有两个。
1
、直接另存为
.config
文件
既然图形化界面配置后的配置项保存在
.config
中,那么就简单粗暴,直接将
.config
文件另
存为
imx_alientek_emmc_defconfig
,然后其复制到
arch/arm/configs
目录下,替换以前的
imx_alientek_emmc_defconfig
。这样以后执行“
make imx_alientek_emmc_defconfig
”重新配置
Linux
内核的时候就会使用新的配置文件,默认就会使能
LAN8720A
的驱动。
2
、通过图形界面保存配置文件
相比于第
1
种直接另存为
.config
文件,第
2
种方法就很“文雅”了,在图形界面中保存配
置文件,在图形界面中会有“
< Save >
”选项,如图
37.4.4.1
所示:
通过键盘的“→”键,移动到“
< Save >
”选项,然后按下回车键,打开文件名输入对话框,
如图
37.4.4.2
所示:
在图
37.4.4.2
中输入要保存的文件名,可以带路径,一般是相对路径
(
相对于
Linux
内核源
码 根目 录
)
。 比如 我们要 将新 的配 置文 件保存 到目 录
arch/arm/configs
下 , 文件 名为
imx_alientek_emmc_defconfig
,也就是用新的配置文件替换掉老的默认配置文件。那么我们在图
37.4.4.2
中输入“
arch/arm/configs/imx_alientek_emmc_defconfig
”即可,如图
37.4.4.3
所示:
设置好文件名以后选择下方的“
< Ok >
”按钮,保存文件并退出。退出以后再打开imx_alientek_emmc_defconfig 文件,就会在此文件中找到“
CONFIG_SMSC_PHY=y
”这一行,
如图
37.4.4.4
所示:
同样的,使用“
make imx_alientek_emmc_defconfig
”重新配置
Linux
内核的时候,
LAN8720A
的驱动就会使能,并被编译进
Linux
镜像文件
zImage
中。
6.总结
①、在
Linux
内核中查找可以参考的板子,一般都是半导体厂商自己做的开发板。
②、编译出参考板子对应的
zImage
和
.dtb
文件。
③、使用参考板子的
zImage
文件和
.dtb
文件在我们所使用的板子上启动
Linux
内核,看能
否启动。
④、如果能启动的话就万事大吉,如果不能启动那就悲剧了,需要调试
Linux
内核。不过
一般都会参考半导体官方的开发板设计自己的硬件,所以大部分情况下都会启动起来。启动
Linux
内核用到的外设不多,一般就
DRAM(Uboot
都初始化好的
)
和串口。作为终端使用的串口
一般都会参考半导体厂商的
Demo
板。
⑤、修改相应的驱动,像
NAND Flash
、
EMMC
、
SD
卡等驱动官方的
Linux
内核都是已经
提供好了,基本不会出问题。重点是网络驱动,因为
Linux
驱动开发一般都要通过网络调试代
码,所以一定要确保网络驱动工作正常。如果是处理器内部
MAC+
外部
PHY
这种网络方案的
话,一般网络驱动都很好处理,因为在
Linux
内核中是有外部
PHY
通用驱动的。只要设置好复
位引脚、
PHY
地址信息基本上都可以驱动起来。
⑥、
Linux
内核启动以后需要根文件系统,如果没有根文件系统的话肯定会崩溃,所以确定
Linux
内核移植成功以后就要开始根文件系统的构建。