内核移植
半导体厂商会从 linux内核官网下载某个版本,将其移植到自己的 cpu上,测试成功后就会将其开放给该半导体厂商的 cpu开发者。 开发者下载其提供的 linux内核,然后将其移植到自己的产品上。
本文使用 nxp提供的 linux内核源码进行移植,文件名为:
linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
1.nxp官方开发板linux内核编译测试
编译内核之前需要先在ubuntu上安装 lzop库,另外,图形化配置工具还需要 ncurses库支持,安装命令为:
sudo apt-get install lzopsudo apt-get install build-essential sudo apt-get install libncurses5-dev在 ubuntu中新建一个文件夹,然后将 linux内核压缩包拷贝到文件夹中并解压,解压命令为:
tar -vxf linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz21.1 配置并编译内核
编译 linux内核之前要先配置 linux内核。 每个板子都有对应的默认配置文件,保存在“arch/arm/configs”目录中。 imx_v7_defconfig和imx_v7_mfg_defconfig都可以作为 nxp官方开发板 imx6ull evk的默认配置文件,一般使用后者,因为后者编译出来的 zimage可通过 nxp官方提供的 mfgtool工具进行烧写
进入到 ubuntu中的 linux源码根目录下,执行如下命令:
#编译之前先清理make arch=arm cross_compile=arm-linux-gnueabihf- distclean #配置linux内核make arch=arm cross_compile=arm-linux-gnueabihf- imx_v7_mfg_defconfig #编译linux内核make arch=arm cross_compile=arm-linux-gnueabihf- all -j16编译完成后,会得到两个重要文件:
linux内核镜像文件:存放路径为 arch/arm/boot/zimageimx6ull evk开发板对应的设备树文件:存放路径为 arch/arm/boot/dts/imx6ull-14x14-evk.dtb1.2 内核启动测试
将上一节中生成的 zimage和 imx6ull-14x14-evk.dtb这两个文件下载到 imx6u-alpha开发板上进行测试。
首先检查 uboot中的环境变量 bootargs
console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw然后拷贝 zimage和 imx6ull-14x14-evk.dtb这两个文件到 ubuntu的 tftp目录下
cp arch/arm/boot/zimage /home/andyxi/linux/tftpcp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/andyxi/linux/tftp最后启动开发板,进入 uboot命令行模式,输入如下命令以上两个文件下载到开发板中并启动
tftp 80800000 zimagetftp 83000000 imx6ull-14x14-evk.dtbbootz 80800000 - 83000000内核启动后,如果 emmc中存在根文件系统,就可以进入到 linux系统里使用命令进行操作,如下图示
1.3 根文件系统缺失错误
linux内核启动以后是需要根文件系统的,根文件系统存在哪里由 uboot的 bootargs环境变量决定的,bootargs会传递给 linux内核作为命令行参数,比如上一节的 bootargs环境变量值为:
console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw其中“root=/dev/mmcblk1p2”表示根文件系统存储在 /dev/mmcblk1p2中(即emmc的分区2),imx6ul-alpha开发板在出厂时已经将根文件系统烧写到了 emmc的分区2中
如果不设置文件系统路径或者路径设置错误,开发板从网络启动后会提示内核崩溃,vfs(虚拟文件系统)不能挂在文件系统。 下图中为不设置文件系统路径以及后续出现的报错信息
2.在linux内核中添加自已的开发板
2.1 添加开发板默认配置文件
将arch/arm/configs目录下的 imx_v7_mfg_defconfig文件重新复制一份并命名为 imx_andyxi_emmc_defconfig,此后该文件就是自已开发板的默认配置文件了
cd arch/arm/configscp imx_v7_mfg_defconfig imx_andyxi_emmc_defconfig2.2 添加开发板对应的设备树文件
将arch/arm/boot/dts目录下的 imx6ull-14x14-evk.dts文件重新复制一份并命名为 imx6ull-andyxi-emmc.dts,.dts是设备树源码文件,编译linux的时候会将其编译成.dtb文件
cd arch/arm/boot/dtscp imx6ull-14x14-evk.dts imx6ull-andyxi-emmc.dts修改arch/arm/boot/dts目录下的makefile文件,添加开发板设备树文件
########## arch/arm/boot/dts/makefile代码段 ##########dtb-$(config_soc_imx6ull) += \\ imx6ull-14x14-ddr3-arm2.dtb \\ imx6ull-14x14-ddr3-arm2-adc.dtb \\ ...... ...... imx6ull-14x14-evk-usb-certi.dtb \\ imx6ull-andyxi-emmc.dtb \\ ...... ......2.3 编译测试
添加完 imx6ul-alpha emmc开发板后,可以创建一个编译脚本imx6ull_andyxi_emmc.sh
#!/bin/shmake arch=arm cross_compile=arm-linux-gnueabihf- distcleanmake arch=arm cross_compile=arm-linux-gnueabihf- imx_andyxi_emmc_defconfigmake arch=arm cross_compile=arm-linux-gnueabihf- all -j16执行 shell脚本编译 linux内核, 命令如下:
chmod 777 imx6ull_andyxi_emmc.sh #给予可执行权限./imx6ull_andyxi_emmc.sh #执行shell脚本编译内核编译完成后,将 zimage和 imx6ull-andyxi-emmc.dtb文件拷贝至 tftp目录下,然后重启开发板,在 uboot命令模式中使用tftp命令下载这两个文件并启动开发板
tftp 80800000 zimagetftp 83000000 imx6ull-andyxi-emmc.dtbbootz 80800000 – 83000000出现如下图示内容就表示 linux内核启动成功
3.内核网络驱动修改
3.1 使能8线emmc驱动
imx6ul-alpha emmc开发板上的emmc采用8位数据线,而linux内核里的emmc默认是4线模式的,可以通过修改为8线模式来提高运行速度
修改方法:直接修改设备树源码文件 imx6ull-andyxi-emmc.dts,修改完成后使用make dtbs命令重新编译设备树即可
########## 修改后的imx6ull-andyxi-emmc.dts代码段 ##########&usdhc2 { pinctrl-names = default, state_100mhz, state_200mhz; pinctrl-0 = ; pinctrl-1 = ; pinctrl-2 = ; bus-width = ; non-removable; status = okay;};3.2 网络驱动修改
imx6ul-alpha emmc开发板的网络和 nxp官方开发板的网络硬件不同,网络 phy芯片由 ksz8081换为了 lan8720a,两个网络phy芯片的复位io也不同,所以 linux内核自带的网络驱动无法驱动imx6ul-alpha emmc开发板的网络,需要做如下更改
修改lan8720的复位引脚驱动:enet1复位引脚enet1_rst连接在imx6ull的snvs_tamper7引脚上; enet2复位引脚enet_rst连接在imx6ull的snvs_tamper8引脚上
在设备树源码文件中找到如下代码段,此处snvs_tamper7/8两个引脚被初始化为了spi4的io,所以需要删除
########## imx6ull-andyxi-emmc.dts 代码段 ##########pinctrl_spi4: spi4grp { fsl,pins = ;};在设备树源码文件中找到如下代码段,此处snvs_tamper7/8两个引脚被设置为了spi4的功能io,所以需要删除
########## imx6ull-andyxi-emmc.dts 代码段 ##########spi4 { compatible = spi-gpio; pinctrl-names = default; pinctrl-0 = ; #pinctrl-assert-gpios = ; #删除此行 ...... ...... #cs-gpios = ; #删除此行};在设备树源码文件中找到名为“iomuxc_snvs”节点,在里面添加网络复位引脚配置信息
########## imx6ull-andyxi-emmc.dts 代码段 ##########
&iomuxc_snvs {
pinctrl-names = default_snvs;
pinctrl-0 = ;
imx6ul-evk {
......
......
# enet1 复位配置
pinctrl_enet1_reset: enet1resetgrp {
fsl,pins = ;
};
#enet2 复位配置
pinctrl_enet2_reset: enet2resetgrp {
fsl,pins = ;
};
};
};修改lan8720的网络时钟引脚驱动
在设备树源码文件中找到如下代码段,将enet1和enet2的网络时钟引脚的电气属性值由0x4001b031(默认值)改为0x4001b009
########## imx6ull-andyxi-emmc.dts 代码段 ##########pinctrl_enet1: enet1grp { fsl,pins = ;};pinctrl_enet2: enet2grp { fsl,pins = ;};修改 fec1和 fec2节点的 pinctrl1-0属性
在设备树源码文件中找到“fec1”和“fec2”这两个节点,修改其中的“pinctrl-0”属性值,修改后的代码如下示
########## imx6ull-andyxi-emmc.dts 代码段 ##########&fec1 { pinctrl-names = default; pinctrl-0 = ; phy-mode = rmii;...... status = okay;};&fec2 { pinctrl-names = default; pinctrl-0 = ; phy-mode = rmii;......};修改lan8720的phy地址
在设备树源码文件中找到如下代码段,设置enet1的lan8720a地址(0x0),设置enet2的lan8720a地址(0x1),以及其他相关设置,修改后的代码如下示
########## imx6ull-andyxi-emmc.dts 代码段 ##########
&fec1 {
pinctrl-names = default;
pinctrl-0 = ;
phy-mode = rmii;
phy-handle = ;
phy-reset-gpios = ;#enet1复位引脚,低电平有效
phy-reset-duration = ; #复位低电平持续时间为200ms
status = okay;
};
&fec2 {
pinctrl-names = default;
pinctrl-0 = ;
phy-mode = rmii;
phy-handle = ;
phy-reset-gpios = ;#enet2复位引脚,低电平有效
phy-reset-duration = ;#复位低电平持续时间为200ms
status = okay;
mdio {
#address-cells = ;
#size-cells = ;
ethphy0: ethernet-phy@0 {#ethernet-phy@后面的数字式phy的地址
compatible = ethernet-phy-ieee802.3-c22;
#表明phy芯片是smsc公司的,内核会找到phy芯片驱动来驱动lan8720a
smsc,disable-energy-detect;
reg = ; #也表示phy地址
};
ethphy1: ethernet-phy@1 {#ethernet-phy@后面的数字式phy的地址
compatible = ethernet-phy-ieee802.3-c22;
#表明phy芯片是smsc公司的,内核会找到phy芯片驱动来驱动lan8720a
smsc,disable-energy-detect;
reg = ;
};
};
};修改fec_main.c文件
要在 i.mx6ull上使用lan8720a,还需要修改内核源码,打开
drivers/net/ethernet/freescale/fec_main.c,找到函数 fec_probe,在 fec_probe 中加入如下代码
staticintfec_probe(structplatform_device*pdev){structfec_enet_private*fep;structfec_platform_data*pdata;structnet_device*ndev;int i, irq, ret =0;structresource*r;conststructof_device_id*of_id;staticint dev_id;structdevice_node*np = pdev->dev.of_node,*phy_node;int num_tx_qs;int num_rx_qs;/* 设置 mx6ul_pad_enet1_tx_clk 和 mx6ul_pad_enet2_tx_clk * 这两个 io 的复用寄存器的 sion 位为 1,以下为添加的代码 */void __iomem *imx6u_enet1_tx_clk;void __iomem *imx6u_enet2_tx_clk; imx6u_enet1_tx_clk =ioremap(0x020e00dc,4);writel(0x14, imx6u_enet1_tx_clk); imx6u_enet2_tx_clk =ioremap(0x020e00fc,4);writel(0x14, imx6u_enet2_tx_clk);......return ret;}配置linux内核,使能lan8720驱动
输入“make menuconfig”,打开图形化配置解密,选择使能lan8720a的驱动,路径如下:
-> 设备驱动程序 -> 网络设备支持 -> phy 设备支持和基础结构 -> 用于 smsc phy 的驱动程序
修改smsc.c文件
在 linux中对 lan8720a进行一次软复位,找到lan8720a的驱动文件 “drivers/net/phy/smsc.c”,在函数“smsc_phy_reset”中添加lan8720a复位代码,修改后的代码如下
staticintsmsc_phy_reset(structphy_device*phydev){int err, phy_reset;int msec =1;structdevice_node*np;int timeout =50000;if(phydev->addr ==0)/* 获取fec1网卡对应的设备节点 */{ np =of_find_node_by_path(/soc/aips-bus@02100000/ ethernet@02188000); if(np ==null){ return-einval; }}if(phydev->addr ==1)/* 获取fec2网卡对应的设备节点 */{ np =of_find_node_by_path(/soc/aips-bus@02000000/ ethernet@020b4000); if(np ==null){ return-einval; }}//从设备树中获取复位时间 err =of_property_read_u32(np,phy-reset-duration,&msec);/* a sane reset duration should not be longer than 1s */if(!err && msec >1000) msec =1; //从设备树中获取复位io phy_reset =of_get_named_gpio(np,phy-reset-gpios,0); if(!gpio_is_valid(phy_reset)) return; //设置phy的复位io,复位lan8720a gpio_direction_output(phy_reset,0); gpio_set_value(phy_reset,0); msleep(msec); gpio_set_value(phy_reset,1); int rc =phy_read(phydev, mii_lan83c185_special_modes); if(rc <0) return rc; /* if the smsc phy is in power down mode, then set it * in all capable mode before using it.*/ if((rc & mii_lan83c185_mode_mask)== mii_lan83c185_mode_powerdown){ /* set all capable mode and reset the phy */ rc |= mii_lan83c185_mode_all; phy_write(phydev, mii_lan83c185_special_modes, rc); } //未修改之前在上面的函数里面,只有powerdown模式时才会软复位lan8720a //此处将其移出来,这样每次调用smsc_phy_reset函数,lan8720a都会被软复位 phy_write(phydev, mii_bmcr, bmcr_reset); /* wait end of reset (max 500 ms) */ do{ udelay(10); if(timeout--==0) return-1; rc =phy_read(phydev, mii_bmcr); }while(rc & bmcr_reset);return0;}因为smsc_phy_reset函数中用到了gpio_direction_output和gpio_set_value函数,所以需要在“smsc.c”中添加如下头文件
#include #include网络驱动测试:修改好设备树和内核后重新编译,下载并启动开发板后,使用如下步骤进行测试
输入ifconfig -a来查看开发板中存在的网卡(eth0已打开)
输入ifconfig eth1 up命令,可打开eth1(enet1)
使用ifconfig eth1 192.168.10.51命令,配置网卡ip地址,更换网口,成功挂载文件系统后,ping一下ubuntu主机(192.168.10.100),ping成功说明网络驱动修改成功
4.linux内核移植总结
linux内核移植的步骤总结如下:
一般情况下,设计自已的硬件时都会参考半导体厂商官方的开发板在半导体厂商维护的linux内核中查找可以参考的板子(半导体厂商官方开发板)编译出参考板子对应的zimage和.dtb文件,尝试在自已的板子上启动大部分情况下会启动起来,如果不能的话就需要调试linux内核修改相应的驱动,nand/emmc/sd卡等,内核已经提供,重点是网络驱动,需要根据自已的外设phy芯片设置复位引脚、地址信息等linux内核启动以后需要根文件系统,如果没有的话系统会崩溃
AM091037PA-P2功率放大器
基于STM32+华为云IOT设计智能称重系统
利用数据和人工智能分析海洋生态系统中的动态趋势
电路板生产流程
“点亮”异国城市的物联网生态!融入“一带一路”的海尔“朋友圈”
Linux内核移植教程
智能车联网“脱虚向实” 万亿蓝海市场逐渐明朗
华为自研光传输芯片获得重大进展
树莓派推出由RP2040驱动的一体式USB调试套件 价格12美元
ARM芯片将是智能家居设备的主要选择之一
便携式蓝牙音箱DIY图解
小米6最新消息:售价达到10万的亮银版!性能和颜值并驾齐驱的小米6!
让光存在,探索光耦继电器的魔力
灰尘传感器DSM501可以检测出单位体积粒子的个数?
声表面波技术在电缆接头测温中的应用
英飞凌宣布Jochen Hanebeck将接替Reinhard Ploss博士担任首席执行官
LED产生色度漂移的解决方案
浅析DARPA完成吸气式高超声速武器概念
谷歌发布文章回顾2018AI发展
浅谈新能源汽车电驱系统的核心技术