嵌入式Linux学习笔记
學習正點原子《【正點原子】I.MX6U嵌入式Linux驅動開發指南V1.5.2.pdf》個人筆記
常用操作&相關知識
壓縮解壓
解壓.tar.bz2
tar -vxjf linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2
壓縮.tar.bz2
tar -vcjf alientek_uboot.tar.bz2 alientek_uboot
編譯
將前面編譯出來的 led.o 文件鏈接到 0X87800000 這個地址,使用如下命令:
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
上述命令中-Ttext 就是指定鏈接地址,“-o”選項指定鏈接生成的 elf 文件名,這里我們命名為 led.elf。上述命令執行完以后就會在工程目錄下多一個 led.elf 文件
led.elf 文件也不是我們最終燒寫到 SD 卡中的可執行文件,我們要燒寫的.bin 文件,因此還需要將 led.elf 文件轉換為.bin 文件,這里我們就需要用到 arm-linux-gnueabihf-objcopy 這個工具了。
arm-linux-gnueabihf-objcopy 更像一個格式轉換工具,我們需要用它將 led.elf 文件轉換為led.bin 文件,命令如下:
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin
上述命令中,“-O”選項指定以什么格式輸出,后面的“binary”表示以二進制格式輸出,選項“-S”表示不要復制源文件中的重定位信息和符號信息,“-g”表示不復制源文件中的調試信息。
大多數情況下我們都是用 C 語言寫試驗例程的,有時候需要查看其匯編代碼來調試代碼,因此就需要進行反匯編,一般可以將 elf 文件反匯編,比如如下命令:
arm-linux-gnueabihf-objdump -D led.elf > led.dis
上述代碼中的“-D”選項表示反匯編所有的段,反匯編完成以后就會在當前目錄下出現一個名為 led.dis 文件
可以打開 led.dis 文件看一下,看看是不是匯編代碼
從圖 可以看出 led.dis 里面是匯編代碼,而且還可以看到內存分配情況。在0X87800000 處就是全局標號_start,也就是程序開始的地方。通過 led.dis 這個反匯編文件可以明顯的看出我們的代碼已經鏈接到了以 0X87800000 為起始地址的區域。
makefile
objs := start.o main.oledc.bin:$(objs)arm-linux-gnueabihf-ld -Timx6ul.lds -o ledc.elf $^arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis%.o:%.sarm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<%.o:%.Sarm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<%.o:%.carm-linux-gnueabihf-gcc -Wall -nostdlib -c -O2 -o $@ $<clean:rm -rf *.o ledc.bin ledc.elf ledc.dis第 1 行定義了一個變量 objs, objs 包含著要生成 ledc.bin 所需的材料: start.o 和 main.o,也就是當前工程下的 start.s 和 main.c 這兩個文件編譯后的.o 文件。這里要注意 start.o 一定要放到最前面!因為在后面鏈接的時候 start.o 要在最前面,因為 start.o 是最先要執行的文件!
第 3 行就是默認目標,目的是生成最終的可執行文件 ledc.bin, ledc.bin 依賴 start.o 和 main.o如果當前工程沒有 start.o 和 main.o 的時候就會找到相應的規則去生成 start.o 和 main.o。比如start.o 是 start.s 文件編譯生成的,因此會執行第 8 行的規則。
第 4 行是使用 arm-linux-gnueabihf-ld 進行鏈接,鏈接起始地址是 0X87800000,但是這一行用到了自動變量“$^”,“$^”的意思是所有依賴文件的集合,在這里就是 objs 這個變量的值:start.o 和 main.o。鏈接的時候 start.o 要鏈接到最前面,因為第一行代碼就是 start.o 里面的,因此這一行就相當于:
arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf start.o main.o
第 5 行使用 arm-linux-gnueabihf-objcopy 來將 ledc.elf 文件轉為 ledc.bin,本行也用到了自動變量“$@”,“$@”的意思是目標集合,在這里就是“ledc.bin”,那么本行就相當于:
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.bin
第 6 行使用 arm-linux-gnueabihf-objdump 來反匯編,生成 ledc.dis 文件。
第 8~15 行就是針對不同的文件類型將其編譯成對應的.o 文件,其實就是匯編.s(.S)和.c 文件,比如 start.s 就會使用第 8 行的規則來生成對應的 start.o 文件。第 9 行就是具體的命令,這行也用到了自動變量“$@”和“$<”,其中“$<”的意思是依賴目標集合的第一個文件。比如start.s 要編譯成 start.o 的話第 8 行和第 9 行就相當于:
第 17 行就是工程清理規則,通過命令“make clean”就可以清理工程。
KERNELDIR := /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek CURRENT_PATH := $(shell pwd) obj-m := chrdevbase.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean第 1 行, KERNELDIR 表示開發板所使用的 Linux 內核源碼目錄,使用絕對路徑,大家根據自己的實際情況填寫即可。
第 2 行, CURRENT_PATH 表示當前路徑,直接通過運行“pwd”命令來獲取當前所處路徑。
第 3 行, obj-m 表示將 chrdevbase.c 這個文件編譯為 chrdevbase.ko 模塊。
第 8 行,具體的編譯命令,后面的 modules 表示編譯模塊, -C 表示將當前的工作目錄切換到指定目錄中,也就是 KERNERLDIR 目錄。 M 表示模塊源碼目錄,“make modules”命令中加入 M=dir 以后程序會自動到指定的 dir 目錄中讀取模塊的源碼并將其編譯為.ko 文件。
Makefile 編寫好以后輸入“make”命令編譯驅動模塊
鏈接腳本
SECTIONS{. = 0X10000000;.text : {*(.text)}. = 0X30000000;.data ALIGN(4) : { *(.data) }.bss ALIGN(4) : { *(.bss) } }第 1 行我們先寫了一個關鍵字“SECTIONS”,后面跟了一個大括號,這個大括號和第 7 行的大括號是一對,這是必須的??雌饋砭透?C 語言里面的函數一樣。
第 2 行對一個特殊符號“.”進行賦值,“.”在鏈接腳本里面叫做定位計數器,默認的定位計數器為 0。我們要求代碼鏈接到以0X10000000 為起始地址的地方,因此這一行給“.”賦值0X10000000,表示以 0X10000000 開始,后面的文件或者段都會以0X10000000 為起始地址開始鏈接。
第 3 行的“.text”是段名,后面的冒號是語法要求,冒號后面的大括號里面可以填上要鏈接到“.text”這個段里面的所有文件,“*(.text)”中的“*”是通配符,表示所有輸入文件的.text段都放到“.text”中。
第 4 行,我們的要求是數據放到 0X30000000 開始的地方,所以我們需要重新設置定位計數器“.”,將其改為 0X30000000。如果不重新設置的話會怎么樣?假設“.text”段大小為 0X10000,那么接下來的.data 段開始地址就是0X10000000+0X10000=0X10010000,這明顯不符合我們的要求。所以我們必須調整定位計數器為 0X30000000。
第 5 行跟第 3 行一樣,定義了一個名為“.data”的段,然后所有文件的“.data”段都放到這里面。但是這一行多了一個“ALIGN(4)”,這是什么意思呢?這是用來對“.data”這個段的起始地址做字節對齊的, ALIGN(4)表示 4 字節對齊。也就是說段“.data”的起始地址要能被 4 整除,一般常見的都是 ALIGN(4)或者 ALIGN(8),也就是 4 字節或者 8 字節對齊。
第 6 行定義了一個“.bss”段,所有文件中的“.bss”數據都會被放到這個里面,“.bss”數據就是那些定義了但是沒有被初始化的變量。
上面就是鏈接腳本最基本的語法格式,我們接下來就按照這個基本的語法格式來編寫我們本試
驗的鏈接腳本,我們本試驗的鏈接腳本要求如下:
①、鏈接起始地址為 0X87800000。
②、 start.o 要被鏈接到最開始的地方,因為 start.o 里面包含這第一個要執行的命令。
根據要求,在 Makefile 同目錄下新建一個名為“imx6ul.lds”的文件,然后在此文件里面輸入如下所示代碼:
第 2 行設置定位計數器為0X87800000,因為我們的鏈接地址就是0X87800000。
第5行設置鏈接到開始位置的文件為start.o,因為 start.o 里面包含著第一個要執行的指令,所以一定要鏈接到最開始的地方。
第 6 行是 main.o這個文件,其實可以不用寫出來,因為 main.o 的位置就無所謂了,可以由編譯器自行決定鏈接位置。
在第 11、 13 行有“__bss_start”和“__bss_end”這兩個東西?這個是什么呢?“__bss_start”和“__bss_end”是符號,第 11、 13 這兩行其實就是對這兩個符號進行賦值,其值為定位符“.”,這兩個符號用來保存.bss 段的起始地址和結束地址。前面說了.bss 段是定義了但是沒有被初始化的變量,我們需要手動對.bss 段的變量清零的,因此我們需要知道.bss 段的起始和結束地址,這樣我們直接對這段內存賦 0 即可完成清零。通過第 11、 13 行代碼, .bss 段的起始地址和結束地址就保存在了“__bss_start”和“__bss_end”中,我們就可以直接在匯編或者 C 文件里面使用這兩個符號。
arm-linux-gnueabihf-ld -Ttext 0X87800000 -o ledc.elf $^
改為:
arm-linux-gnueabihf-ld -Timx6ul.lds -o ledc.elf $^
其實就是將-T 后面的 0X87800000 改為 imx6ul.lds,表示使用 imx6ul.lds 這個鏈接腳本文件。
內核目錄
linux內核編譯過程
make xxx_defconfig命令配置 Linux 的時候如下兩行命令會執行腳本scripts/Makefile.build:
@make -f ./scripts/Makefile.build obj=scripts/basic @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig %_defconfig: scripts/kconfig/conf @ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig%_defconfig依賴scripts/kconfig/conf,所以會編譯scripts/kconfig/conf.c生成conf這個軟件。
此軟件就會將%_defconfig 中的配置輸出到.config 文件中,最終生成 Linux kernel 根目錄下的.config 文件。
使用命令“make xxx_defconfig”配置好 Linux 內核以后就可以使用“make”或者“make all”命令進行編譯。頂層 Makefile 有如下代碼:
125 PHONY := _all 126 _all: ...... 192 PHONY += all 193 ifeq ($(KBUILD_EXTMOD),) 194 _all: all 195 else 196 _all: modules 197 endif ...... 608 all: vmlinux第 126 行, _all 是默認目標,如果使用命令“make”編譯 Linux 的話此目標就會被匹配。
第 193 行,如果 KBUILD_EXTMOD 為空的話 194 行的代碼成立。
第 194 行,默認目標_all 依賴 all。
第 608 行,目標 all 依賴 vmlinux,所以接下來的重點就是 vmlinux!
要分析 Linux 啟動流程,同樣需要先編譯一下 Linux 源碼,因為有很多文件是需要編譯才會生成的。首先分析 Linux 內核的連接腳本文件 arch/arm/kernel/vmlinux.lds,通過鏈接腳本可以找到 Linux 內核的第一行程序是從哪里執行的。 vmlinux.lds 中有如下代碼:
492 OUTPUT_ARCH(arm) 493 ENTRY(stext) 494 jiffies = jiffies_64;第 493 行的 ENTRY 指明了了 Linux 內核入口,入口為 stext, stext 定義在文件arch/arm/kernel/head.S 中 , 因 此 要 分 析 Linux 內 核 的 啟 動 流 程 , 就 得 先 從 文 件arch/arm/kernel/head.S 的 stext 處開始分析。
linux內核移植步驟
1、設置目標架構和交叉編譯器
同 uboot一樣, Linux編譯的時候需要設置目標板架構ARCH 和交叉編譯器 CROSS_COMPILE,在頂層 Makefile 中代碼如下:
ARCH ?= $(SUBARCH) CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)為了方便,一般直接修改頂層 Makefile 中的 ARCH 和 CROSS_COMPILE,直接將其設置為對應的架構和編譯器,比如本教程將 ARCH 設置為為 arm, CROSS_COMPILE 設置為 armlinux-gnueabihf-,如下所示:
ARCH ?= arm CROSS_COMPILE ?= arm-linux-gnueabihf-設置好以后我們就可以使用如下命令編譯 Linux 了:
make xxx_defconfig //使用默認配置文件配置 Linux make menuconfig //啟動圖形化配置界面 make -j16 //編譯 Linux內核啟動測試
在上一小節我們已經得到了 NXP 官方 I.MX6ULL EVK 開發板對應的 zImage 和 imx6ull-14x14-evk.dtb 這兩個文件。這兩個文件能不能在正點原子的 I.MX6U-ALPHA EMMC 版開發板上啟動呢?測試一下不就知道了,在測試之前確保 uboot 中的環境變量 bootargs 內容如下:
console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw
將上一小節編譯出來的 zImage 和 imx6ull-14x14-evk.dtb 復制到 Ubuntu 中的 tftp 目錄下,因為我們要在 uboot 中使用 tftp 命令將其下載到開發板中,拷貝命令如下:
拷貝完成以后就可以測試了,啟動開發板,進入 uboot 命令行模式,然后輸入如下命令將zImage 和 imx6ull-14x14-evk.dtb 下載到開發板中并啟動:
tftp 80800000 zImage tftp 83000000 imx6ull-14x14-evk.dtb bootz 80800000 - 83000000Linux 內核啟動以后是需要根文件系統的,根文件系統存在哪里是由 uboot 的 bootargs 環境變量指定 , bootargs 會傳遞給 Linux 內核作為命令行參數 。 比如上面設置root=/dev/mmcblk1p2,也就是說根文件系統存儲在/dev/mmcblk1p2 中,也就是 EMMC 的分區 2中。這是因為正點原子的 EMMC 版本開發板出廠的時候已經 EMMC 的分區 2 中燒寫好了根文件系統,所以設置 root=/dev/mmcblk1p2。
在 Linux 中添加自己的開發板
通過編譯 NXP 官方 I.MX6ULL EVK 開發板對應的 Linux 內核,發現其可以在正點原子的 EMMC 版本開發板啟動,所以我們就參考 I.MX6ULL EVK 開發板的設置,在 Linux 內核中添加正點原子的 I.MX6U-ALPHA 開發板。
添加開發板默認配置文件
將 arch/arm/configs 目 錄 下 的 imx_v7_mfg_defconfig 重 新 復 制 一 份 , 命 名 為imx_alientek_emmc_defconfig,命令如下:
cd arch/arm/configs cp imx_v7_mfg_defconfig imx_alientek_emmc_defconfig以后 imx_alientek_emmc_defconfig 就是正點原子的 EMMC 版開發板默認配置文件了。
以后就可以使用如下命令來配置正點原子 EMMC 版開發板對應的 Linux 內核了:
make imx_alientek_emmc_defconfig
添加開發板對應的設備樹文件
添加適合正點原子 EMMC 版開發板的設備樹文件,進入目錄 arch/arm/boot/dts 中,復制一份 imx6ull-14x14-evk.dts,然后將其重命名為 imx6ull-alientek-emmc.dts,命令如下:
cd arch/arm/boot/dts cp 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”
這樣編譯 Linux 的時候就可以從 imx6ull-alientekemmc.dts 編譯出 imx6ull-alientek-emmc.dtb 文件了。
編譯測試
可以創建一個編譯腳本 ,imx6ull_alientek_emmc.sh,腳本內容如下:
1 #!/bin/sh 2 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean 3 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihfimx_alientek_emmc_defconfig 4 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig 5 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16第 2 行,清理工程。
第 3 行,使用默認配置文件 imx_alientek_emmc_defconfig 來配置 Linux 內核。
第 4 行,打開 Linux 的圖形配置界面,如果不需要每次都打開圖形配置界面可以刪除此行。
第 5 行,編譯 Linux。
執行 shell 腳本 imx6ull_alientek_emmc.sh 編譯 Linux 內核, 命令如下:
chmod 777 imx6ull_alientek_emmc.sh //給予可執行權限 ./imx6ull_alientek_emmc.sh //執行 shell 腳本編譯內核編譯完成以后就會在目錄 arch/arm/boot 下生成 zImage 鏡像文件。在 arch/arm/boot/dts 目錄下生成 imx6ull-alientek-emmc.dtb 文件。
CPU 主頻修改
執行cat /proc/cpuinfo
有 BogoMIPS 這一條,此時 BogoMIPS 為 3.00, BogoMIPS 是 Linux 系統中衡量處理器運行速度的一個“尺子”,處理器性能越強,主頻越高, BogoMIPS 值就越大。BogoMIPS 只是粗略的計算 CPU 性能,并不十分準確。但是我們可以通過 BogoMIPS 值來大致的判斷當前處理器的性能。在圖中并沒有看到當前 CPU 的工作頻率,那我們就轉變另一種方法查看當前 CPU 的工作頻率。進入到目錄/sys/bus/cpu/devices/cpu0/cpufreq 中,此目錄下會有很多文件
此目錄中記錄了 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,工作頻率很低!其他的值如下:
可以看出,當前 CPU 支持 198MHz、 396MHz、 528Mhz 和 792000 四種頻率切換,其中調頻策略為 ondemand,也就是定期檢查負載,然后根據負載情況調節 CPU 頻率。因為當前我們開發板并沒有做什么工作,因此 CPU 頻率降低為 198MHz 以省電。如果開發板做一些高負載的工作,比如播放視頻等操作那么 CPU 頻率就會提升上去。
查看 stats 目錄下的 time_in_state 文件可以看到 CPU 在各頻率下的工作時間,命令如下:
cat /sys/bus/cpu/devices/cpu0/cpufreq/stats/time_in_state
從圖中可以看出, CPU 在 198MHz、 396MHz、 528MHz 和 792MHz 都工作過,其中 198MHz 的工作時間最長!假如我們想讓 CPU 一直工作在 792MHz 那該怎么辦?很簡單,配置 Linux 內核,將調頻策略選擇為 performance?;蛘咝薷膇mx_alientek_emmc_defconfig 文件,此文件中有下面幾行:
第 41 行,配置 ondemand 為默認調頻策略。
第 42 行,使能 powersave 策略。
第 43 行,使能 userspace 策略。
第 44 行,使能 interactive 策略。
將第 41 行屏蔽掉,然后在 44 行后面添加:
結果下所示:
41 #CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y 42 CONFIG_CPU_FREQ_GOV_POWERSAVE=y 43 CONFIG_CPU_FREQ_GOV_USERSPACE=y 44 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y 45 CONFIG_CPU_FREQ_GOV_ONDEMAND=y修改完成以后重新編譯 Linux 內核,編譯之前先清理一下工程!因為我們重新修改過默認配置文件了 , 編譯完成以后使用新的 zImage鏡像文件重新 啟動 Linux 。 再 次 查 看/sys/devices/system/cpu/cpu0/cpufreq/ cpuinfo_cur_freq 文件的值,如圖
可以看出,當前 CPU 頻率為 792MHz 了。查看 scaling_governor 文件看一下當前的調頻策略,如圖
可以看出當前的 CPU 調頻策略為 preformance,也就是高性能模式,一直以最高主頻運行。
我們再來看一下如何通過圖形化界面配置 Linux 內核的 CPU 調頻策略,輸入“ make menuconfig”打開 Linux 內核的圖形化配置界面,進入如下路徑:
打開默認調頻策略選擇界面,選擇“performance”。選擇以后退出圖形化配置界面,然后編譯 Linux內核,一定不要清理工程!否則的話我們剛剛的設置就會被清理掉。編譯完成以后使用新的zImage 重啟 Linux,查看當前 CPU 的工作頻率和調頻策略。
我們學習的時候為了高性能,大家可以使用 performance 模式。但是在以后的實際產品開發中,從省電的角度考慮,建議大家使用 ondemand 模式,一來可以省電,二來可以減少發熱。
超頻&修改頻率列表
超頻設置其實很簡單,修改一下設備樹文件 arch/arm/boot/dts/imx6ull.dtsi 即可,打開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 1275000 62 792000 1225000 63 528000 1175000 64 396000 1025000 65 198000 950000 66 >; 67 fsl,soc-operating-points = < 68 /* KHz uV */ 69 996000 1175000 70 792000 1175000 71 528000 1175000 72 396000 1175000 73 198000 1175000 74 >;上面代碼就是設置 CPU 頻率的,第 61-65 行和第 69-73 行就是 I.MX6ULL 所支持的頻率,單位為 KHz,可以看出 I.MX6ULL(視具體型號而定)支持 996MHz、 792MHz、 528MHz、396MHz 和 198MHz。在上一小節中,我們知道 Linux 內核默認支持 198MHz、 396MHz、 528MHz和 792MHz, 如果是 MCIMX6Y2CVM05AB 這顆芯片的話,默認最高只能運行在 528MHz, 我
們在示例代碼中加入針對 696MHz 的支持,修改以后代碼如下:
修改好以后保存,并且編譯設備樹,在 Linux 內核源碼根目錄下輸入如下命令編譯設備樹:
make dtbs
命令“make dtbs”只編譯設備樹文件,也就是將.dts 編譯為.dtb,編譯完成以后使用新的設備 樹 文 件 imx6ull-alientek_emmc.dtb 啟 動 Linux 。 重 啟 以 后 查 看 文 件/sys/devices/system/cpu/cpu0/cpufreq/ scaling_available_frequencies 的內容,如圖
使能8線EMMC驅動
正點原子 EMMC 版本核心板上的 EMMC 采用的 8 位數據線
Linux 內核驅動里面 EMMC 默認是 4 線模式的, 4 線模式肯定沒有 8 線模式的速度快,所以本節我們將 EMMC 的驅動修改為 8 線模式。修改方法很簡單,直接修改設備樹即可,打開文件 imx6ull-alientek-emmc.dts,找到如下所示內容
改為如下代碼即可:
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 系統即可。
修改網絡驅動
因為在后面學習 Linux 驅動開發的時候要用到網絡調試驅動,所以必須要把網絡驅動調試好。在講解 uboot 移植的時候就已經說過了,正點原子開發板的網絡和 NXP 官方的網絡硬件上不同,網絡 PHY 芯片由 KSZ8081 換為了 LAN8720A,兩個網絡 PHY 芯片的復位 IO 也不同。所以 Linux 內核自帶的網絡驅動是驅動不起來 I.MX6U-ALPHA 開發板上的網絡的,需要做修改。
1、修改 LAN8720 的復位以及網絡時鐘引腳驅動
ENET1 復位引腳 ENET1_RST 連接在 I.M6ULL 的 SNVS_TAMPER7 這個引腳上。 ENET2的復位引腳 ENET2_RST 連接在 I.MX6ULL 的 SNVS_TAMPER8 上。打開設備樹文件 imx6ullalientek-emmc.dts,找到如下代碼:
584 pinctrl_spi4: spi4grp { 585 fsl,pins = < 586 MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1 587 MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1 588 MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07 0x70a1 589 MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08 0x80000000 590 >; 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 行, imx6ull-alientek-emmc.dts 文件中 iomuxc_snvs 節點。
第 45~50 行, ENET1 網絡復位引腳配置信息。
第 53~58 行, ENET2 網絡復位引腳配置信息。
最后還需要修改一下 ENET1 和 ENET2 的網絡時鐘引腳配置, 繼續在 imx6ull-alientekemmc.dts 中找到如下所示代碼:
第 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"; 3 pinctrl-0 = <&pinctrl_enet1 4 &pinctrl_enet1_reset>; 5 phy-mode = "rmii"; ...... 9 status = "okay"; 10 }; 11 12 &fec2 { 13 pinctrl-names = "default"; 14 pinctrl-0 = <&pinctrl_enet2 15 &pinctrl_enet2_reset>; 16 phy-mode = "rmii"; ...... 36 };第 3~4 行,修改后的 fec1 節點“pinctrl-0”屬性值。
第 14~15 行,修改后的 fec2 節點“pinctrl-0”屬性值。
3、修改 LAN8720A 的 PHY 地址
在 uboot 移植章節中,我們說過 ENET1 的 LAN8720A 地址為 0x0, ENET2 的 LAN8720A地址為 0x1。在 imx6ull-alientek-emmc.dts 中找到如下代碼:
171 &fec1 { 172 pinctrl-names = "default"; ...... 175 phy-handle = <ðphy0>; 176 status = "okay"; 177 }; 178 179 &fec2 { 180 pinctrl-names = "default"; ...... 183 phy-handle = <ðphy1>; 184 status = "okay"; 185 186 mdio { 187 #address-cells = <1>; 188 #size-cells = <0>; 189 190 ethphy0: ethernet-phy@0 { 191 compatible = "ethernet-phy-ieee802.3-c22"; 192 reg = <2>; 193 }; 194 195 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 改為如下內容:
第 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 int 3439 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; 3451 3452 /* 設置 MX6UL_PAD_ENET1_TX_CLK 和 MX6UL_PAD_ENET2_TX_CLK 3453 * 這兩個 IO 的復用寄存器的 SION 位為 1。 3454 */ 3455 void __iomem *IMX6U_ENET1_TX_CLK; 3456 void __iomem *IMX6U_ENET2_TX_CLK; 3457 3458 IMX6U_ENET1_TX_CLK = ioremap(0X020E00DC, 4); 3459 writel(0X14, IMX6U_ENET1_TX_CLK); 3460 3461 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
圖中選擇將“Drivers for SMSC PHYs”編譯到 Linux 內核中,因此“<>”里面變為了“*”。 LAN8720A 是 SMSC 公司出品的,因此勾選這個以后就會編譯 LAN8720 驅動,配置好以后退出配置界面,然后重新編譯一下 Linux 內核。
5、修改 smsc.c 文件
在修改 smsc.c 文件之前先說點題外話,那就是我是怎么確定要修改 smsc.c 這個文件的。在寫本書之前我并沒有修改過 smsc.c 這個文件,都是使能 LAN8720A 驅動以后就直接使用。但是我在測試 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函數內容如下所示:
6、網絡驅動測試
修改好設備樹和 Linux 內核以后重新編譯一下,得到新的 zImage 鏡像文件和 imx6ullalientek-emmc.dtb 設備樹文件,使用網線將 I.MX6U-ALPHA 開發板的兩個網口與路由器或者電腦連接起來,最后使用新的文件啟動 Linux 內核。啟動以后使用“ifconfig”命令查看一下當前活動的網卡有哪些,結果如圖 37.4.3.2 所示:
從圖 37.4.3.2 可以看出,當前沒有活動的網卡。輸入命令“ifconfig -a”來查看一下開發板中存在的所有網卡,結果如圖 37.4.3.3 所示:
總結
以上是生活随笔為你收集整理的嵌入式Linux学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php-fpm 内存 facebook,
- 下一篇: Cordova入门系列(三)Cordov