內核移植
半導體廠商會從 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 lzop
sudo 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.bz2
1.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/zImage
- IMX6ULL EVK開發板對應的設備樹文件:存放路徑為 arch/arm/boot/dts/imx6ull-14x14-evk.dtb
1.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/tftp
cp arch/arm/boot/dts/imx6ull-14x14-evk.dtb /home/andyxi/linux/tftp
最后啟動開發板,進入 uboot命令行模式,輸入如下命令以上兩個文件下載到開發板中并啟動
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 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/configs
cp imx_v7_mfg_defconfig imx_andyxi_emmc_defconfig
2.2 添加開發板對應的設備樹文件
將arch/arm/boot/dts目錄下的 imx6ull-14x14-evk.dts文件重新復制一份并命名為 imx6ull-andyxi-emmc.dts,.dts是設備樹源碼文件,編譯Linux的時候會將其編譯成.dtb文件
cd arch/arm/boot/dts
cp 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/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_andyxi_emmc_defconfig
make 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 zImage
tftp 83000000 imx6ull-andyxi-emmc.dtb
bootz 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_usdhc2_8bit>;
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
bus-width = <8>;
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 = <
MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1
#MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1 #刪除此行
#MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000 #刪除此行
>;
};
在設備樹源碼文件中找到如下代碼段,此處SNVS_TAMPER7/8兩個引腳被設置為了SPI4的功能IO,所以需要刪除
########## imx6ull-andyxi-emmc.dts 代碼段 ##########
spi4 {
compatible = "spi-gpio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi4>;
#pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; #刪除此行
......
......
#cs-gpios = <&gpio5 7 0>; #刪除此行
};
在設備樹源碼文件中找到名為“iomuxc_snvs”節點,在里面添加網絡復位引腳配置信息
########## imx6ull-andyxi-emmc.dts 代碼段 ##########
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
imx6ul-evk {
......
......
# enet1 復位配置
pinctrl_enet1_reset: enet1resetgrp {
fsl,pins = <
# used for enet1 reset #
MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x10B0
>;
};
#enet2 復位配置
pinctrl_enet2_reset: enet2resetgrp {
fsl,pins = <
# used for enet2 reset #
MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x10B0
>;
};
};
};
修改LAN8720的網絡時鐘引腳驅動
在設備樹源碼文件中找到如下代碼段,將ENET1和ENET2的網絡時鐘引腳的電氣屬性值由0x4001b031(默認值)改為0x4001b009
########## imx6ull-andyxi-emmc.dts 代碼段 ##########
pinctrl_enet1: enet1grp {
fsl,pins = <
MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0
MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0
MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0
MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0
MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b009#默認為0x4001b031
>;
};
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 0x4001b009#默認為0x4001b031
>;
};
修改 fec1和 fec2節點的 pinctrl1-0屬性
在設備樹源碼文件中找到“fec1”和“fec2”這兩個節點,修改其中的“pinctrl-0”屬性值,修改后的代碼如下示
########## imx6ull-andyxi-emmc.dts 代碼段 ##########
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_enet1_reset>;
phy-mode = "rmii";
......
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2
&pinctrl_enet2_reset>;
phy-mode = "rmii";
......
};
修改LAN8720的PHY地址
在設備樹源碼文件中找到如下代碼段,設置ENET1的LAN8720A地址(0x0),設置ENET2的LAN8720A地址(0x1),以及其他相關設置,修改后的代碼如下示
########## imx6ull-andyxi-emmc.dts 代碼段 ##########
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1
&pinctrl_enet1_reset>;
phy-mode = "rmii";
phy-handle = <ðphy0>;
phy-reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>;#ENET1復位引腳,低電平有效
phy-reset-duration = <200>; #復位低電平持續時間為200ms
status = "okay";
};
&fec2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2
&pinctrl_enet2_reset>;
phy-mode = "rmii";
phy-handle = <ðphy1>;
phy-reset-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;#ENET2復位引腳,低電平有效
phy-reset-duration = <200>;#復位低電平持續時間為200ms
status = "okay";
mdio {
#address-cells = <1>;
#size-cells = <0>;
ethphy0: ethernet-phy@0 {#ethernet-phy@后面的數字式PHY的地址
compatible = "ethernet-phy-ieee802.3-c22";
#表明PHY芯片是SMSC公司的,內核會找到PHY芯片驅動來驅動LAN8720A
smsc,disable-energy-detect;
reg = <0>; #也表示PHY地址
};
ethphy1: ethernet-phy@1 {#ethernet-phy@后面的數字式PHY的地址
compatible = "ethernet-phy-ieee802.3-c22";
#表明PHY芯片是SMSC公司的,內核會找到PHY芯片驅動來驅動LAN8720A
smsc,disable-energy-detect;
reg = <1>;
};
};
};
修改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內核啟動以后需要根文件系統,如果沒有的話系統會崩潰
-
內核
+關注
關注
3文章
1405瀏覽量
41011 -
Linux
+關注
關注
87文章
11432瀏覽量
212480 -
移植
+關注
關注
1文章
393瀏覽量
28535 -
源碼
+關注
關注
8文章
665瀏覽量
30093 -
開發板
+關注
關注
25文章
5433瀏覽量
101269
發布評論請先 登錄


認識Linux內核移植
如何以低成本將Linux 內核移植到嵌入式處理系統

評論