作者:彭東林 郵箱:[email protected] QQ:405728433 開發板 TQ2440 + 64MB 內存 + 256MB Nand 軟體 Linux: Linux-4.9 (https://github.com/pengdonglin137/linux-4.9 ) u-b ...
作者:彭東林
QQ:405728433
開發板
TQ2440 + 64MB 內存 + 256MB Nand
軟體
Linux: Linux-4.9 (https://github.com/pengdonglin137/linux-4.9 )
u-boot:U-Boot 2015.04 (http://www.cnblogs.com/pengdonglin137/p/4541705.html 以及 https://github.com/pengdonglin137/u-boot )
busybox:1.25.0
工具鏈:
編譯內核使用的是arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2(下載)
編譯busybox使用的是EABI-4.3.3_EmbedSky_20100610.tar.bz2 (開發板自帶的工具鏈),因爲發現,如果用跟編譯kernel同樣的那個工具鏈的話,啓動init進程啓動會有問題
概述
以往TQ2440上移植Linux內核都沒有支持設備樹,而設備樹纔是潮流,tq2440沒用上設備樹這件事我心裏糾結了很長時間,所以特意花了一天時間搞一搞,同時爲想研究學習設備樹的同志鋪一鋪路。好在Samsung已經做了很多工作,最後我發現,需要我們修改的基本都是設備樹文件,而kernel代碼幾乎不用怎麼大動。
在移植的時候,需要對Linux下的中斷子系統、時鐘子系統有一些認識。
目前這個版本支持:
1. serial0
2. rtc
3. watchdog (如果沒有這個的話,reboot的時候板子不會自動復位)
4. DM9000 (有了這個,就可以用nfs掛載遠程目錄,對於調試工作很有益處)
下麵是下載代碼的鏈接:
git clone [email protected]:pengdonglin137/linux-4.9.git -b tq2440_dt
使用方法:
-
下載代碼後,修改Makefile文件,設置ARCH和CROSS_COMPILE
-
make tq2440_dt_defconfig
-
編譯uImage,然後將uImage拷貝到/tftpboot下: make uImage -j4
-
編譯設備樹,然後將s3c2440-tq2440-dt.dtb拷貝到/tftpboot下:make dtbs
-
製作ramdisk:下載tq2440_ramdisk.tar.gz,解壓後,執行下麵的腳本mk_ramdisk.sh,會生成一個ramdisk.img文件
進入u-boot:註意:後下載的鏡像不要把前面的鏡像覆蓋了
-
下載uImage:tftp 0x30008000 uImage;
-
下載ramdisk:tftp 0x31000000 ramdisk.img;
-
下載設備樹文件:tftp 0x33000000 s3c2440-tq2440-dt.dtb;
-
啓動:bootm 0x30008000 0x31000000 0x33000000
正文
在移植的時候參考了s3c2416的代碼,因爲目前s3c2416採用的就是設備樹,但是畢竟跟s3c2440不同,無法直接使用,需要修改設備樹配置。
在移植初期,kernel啓動的時候會在很多地方卡住,臨時的處理辦法是先把卡住的函數註掉,把出問題的模塊先從內核配置中拿掉。最後,板子起來後,再回頭分析前面模塊被卡住的原因。目前我是在tq2440上面移植的,由於mini2440跟tq2440基本一樣(初期我使用的內核配置文件copy的就是mini2440_defconfig,然後在此基礎上修改),所以理論上使用上面的鏡像也可以將mini2440啓動起來。
下麵的移植記錄不會很全,詳細的代碼改動請參考上面我上傳到github上的代碼。
一、添加設備樹文件,我仿照s3c2416-smdk2416.dts的結構添加了tq2440的設備樹需要的文件,下麵是設備樹的結構
s3c2440-tq2440-dt.dts
----> s3c2440.dtsi
----> s3c24xx.dtsi
----> skeleton.dtsi
----> s3c2440-pinctrl.dtsi
我們大概介紹一下上面的幾個文件:
-
skeleton.dtsi 存放的是一個設備樹必備的一些基本屬性
-
s3c24xx.dtsi 中存放的是整個s3c24xx系列SoC公共的一些屬性,如中斷控制器、串口、看門狗、RTC、I2C控制器等等
-
s3c2440-pinctrl.dtsi 存放的是s3c2440這款SoC中GPIO控制器、外部中斷控制器、引腳複用等信息的配置
-
s3c2440.dtsi 存放的是s3c2440這個SoC跟其他s3c24xx系列不同的一些硬體信息,如clock控制器、串口等等
-
s3c2440-tq2440-dt.dts 存放的是tq2440的硬體信息
設備樹這樣一層層包含的好處是: 在同名節點中,後出現的屬性會覆蓋前面出現的同名屬性,不同的屬性將來會合併到所隸屬的同名的節點下麵。
然後修改arch/arm/boot/dts/Makefile:
1 diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile 2 index c558ba7..28381c0 100644 3 --- a/arch/arm/boot/dts/Makefile 4 +++ b/arch/arm/boot/dts/Makefile 5 @@ -661,7 +661,8 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \ 6 rk3288-veyron-pinky.dtb \ 7 rk3288-veyron-speedy.dtb 8 dtb-$(CONFIG_ARCH_S3C24XX) += \ 9 - s3c2416-smdk2416.dtb 10 + s3c2416-smdk2416.dtb \ 11 + s3c2440-tq2440-dt.dtb 12 dtb-$(CONFIG_ARCH_S3C64XX) += \ 13 s3c6410-mini6410.dtb \ 14 s3c6410-smdk6410.dtb
這樣在make dtbs編譯設備樹的時候就會編譯s3c2440-tq2440-dt.dts,在arch/arm/boot/dts/下生成s3c2440-tq2440-dt.dtb
二、修改Makefile和Kconfig,添加tq2440板子的信息,以便在kernel啓動的時候能夠用從設備樹鏡像中解析到的信息匹配到tq2440板子
-
修改arch/arm/mach-s3c24xx/Kconfig
1 diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig 2 index 4b1690a..5b2e34f 100644 3 --- a/arch/arm/mach-s3c24xx/Kconfig 4 +++ b/arch/arm/mach-s3c24xx/Kconfig 5 @@ -475,6 +475,15 @@ config MACH_MINI2440 6 Say Y here to select support for the MINI2440. Is a 10cm x 10cm board 7 available via various sources. It can come with a 3.5" or 7" touch LCD. 8 9 +config MACH_TQ2440_DT 10 + bool "TQ2440 development board using device tree" 11 + select CLKSRC_OF 12 + select USE_OF 13 + select PINCTRL 14 + select PINCTRL_S3C24XX 15 + help 16 + Say Y here to select support for the TQ2440. 17 + 18 config MACH_NEXCODER_2440 19 bool "NexVision NEXCODER 2440 Light Board" 20 select S3C2440_XTAL_12000000
這樣在make menuconfig的時候,選擇上這個配置。選擇這個配置的時候,CONFIG_CLKSRC_OF/CONFIG_USE_OF/CONFIG_PINCTRL/CONFIG_S3C24XX都會被配置上。
-
修改arch/arm/mach-s3c24xx/Makefile
1 diff --git a/arch/arm/mach-s3c24xx/Makefile b/arch/arm/mach-s3c24xx/Makefile 2 index 8ac2f58..2be494d100644 3 --- a/arch/arm/mach-s3c24xx/Makefile 4 +++ b/arch/arm/mach-s3c24xx/Makefile 5 @@-60,6+60,8@@ obj-$(CONFIG_ARCH_SMDK2410) += mach-smdk2410.o 6 obj-$(CONFIG_MACH_TCT_HAMMER) += mach-tct_hammer.o 7 obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o 8 +obj-$(CONFIG_MACH_TQ2440_DT) += mach-tq2440-dt.o 9 + 10 obj-$(CONFIG_MACH_JIVE) += mach-jive.o 11 obj-$(CONFIG_MACH_SMDK2413) += mach-smdk2413.o 12 obj-$(CONFIG_MACH_VSTMS) += mach-vstms.o
在Kconfig配置上CONFIG_MACH_TQ2440_DT後,在make uImage的時候就會編譯mach-tq2440-dt.c
-
添加arch/arm/mach-s3c24xx/mach-tq2440-dt.c
1 #include <linux/clocksource.h> 2 #include <linux/irqchip.h> 3 #include <linux/serial_s3c.h> 4 #include <asm/mach/arch.h> 5 #include <mach/map.h> 6 #include <plat/cpu.h> 7 #include <plat/pm.h> 8 #include "common.h" 9 static void __init tq2440_dt_map_io(void) 10 { 11 s3c24xx_init_io(NULL, 0); 12 } 13 static void __init tq2440_dt_machine_init(void) 14 { 15 s3c_pm_init(); 16 } 17 static const char *const tq2440_dt_compat[] __initconst = { 18 "samsung,s3c2440", 19 "samsung,tq2440", 20 NULL 21 }; 22 DT_MACHINE_START(TQ2440_DT, "Samsung S3C2440 (Flattened Device Tree)") 23 .dt_compat = tq2440_dt_compat, 24 .map_io = tq2440_dt_map_io, 25 .init_irq = irqchip_init, 26 .init_machine = tq2440_dt_machine_init, 27 MACHINE_END
第11行會對一些常用的內存進行靜態映射。
這裏我們需要註意的是第30行的dt_compat數組,其中的值要跟設備樹中的compatible匹配,如arch/arm/boot/dts/s3c2440-tq2440-dt.dts:
1 /dts-v1/; 2 #include "s3c2440.dtsi" 3 #include <dt-bindings/interrupt-controller/irq.h> 4 #include <dt-bindings/clock/s3c2410.h> 5 / { 6 model = "TQ2440"; 7 compatible = "samsung,s3c2440", "samsung,tq2440"; 8 memory { 9 reg = <0x30000000 0x34000000>; 10 };
如上面的第7行,跟tq2440_dt_compat是相匹配的。
三、打開內核調試開關
如果uboot中設置了bootargs屬性的話,在boot的之前它會修改設備樹鏡像,覆蓋其中chosen節點中的bootargs屬性,爲了便於調試,我在uboot中執行setenv bootargs命令,這樣就可以刪除uboot中bootargs環境變量。
在啓動kernel的時候最煩人的是,uboot列印出"Starting kernel ..."後,整個系統就沒有任何動靜了,此時,就需要打開內核早期的調試log,方法如下:
爲了能夠儘量看到更多內核啓動早期的log,一定要在內核配置文件中把內核早期的log配置打開:
Kernel hacking --->
[*] Kernel low-level debugging functions (read help!)
Kernel low-level debugging port (Use Samsung S3C UART 0 for low-level debug) --->
[*] Early printk
除了上面的配置,還必須在bootargs中添加一個earlyprintk字元串,否則這些log還是列印不出來,此外,建議再在bootargs中添加一個ignore_loglevel參數,防止有些模塊的log由於loglevel的問題無法輸出log
下麵是設備樹(s3c2440-tq2440-dt.dts)中chosen節點的定義:
1 chosen { 2 bootargs = "root=/dev/ram0 rw rootfstype=ext2 console=ttySAC0,115200n8 init=/linuxrc ignore_loglevel earlyprintk"; 3 };
四、剩下的工作就是修改設備樹了
這也是導致kernel無法啓動的原因,當然前期並不確定問題是出在設備樹還是kernel,下麵提示幾個比較關鍵的點。
-
fixed-clock時鐘配置
在移植以前不支持設備樹的內核代碼的時候(https://github.com/pengdonglin137/linux-3-14-y/tree/transplant_to_tq2440 )在mach-tq2440.c中:
1 static void __init tq2440_map_io(void) 2 { 3 s3c24xx_init_io(tq2440_iodesc, ARRAY_SIZE(tq2440_iodesc)); 4 s3c24xx_init_clocks(12000000); 5 s3c24xx_init_uarts(tq2440_uartcfgs, ARRAY_SIZE(tq2440_uartcfgs)); 6 samsung_set_timer_source(SAMSUNG_PWM3, SAMSUNG_PWM4); 7 }
第4行設置了XTI的時鐘是12MKHz,但是有了設備樹後,就不能這樣做了,你可以看看在linux-4.9下麵s3c24xx_init_clocks函數的實現,由於cpu->init_clocks爲NULL,會導致kernel panic。
那爲什麼是12M呢?在TQ2440的核心板上可以看到XTIpll上接了一個12M的晶振:
對應在設備樹中的配置(s3c2440-tq2440-dt.dts)是:
1 clocks { 2 compatible = "simple-bus"; 3 #address-cells = <1>; 4 #size-cells = <0>; 5 xti: oscillator@0 { 6 compatible = "fixed-clock"; 7 reg = <0>; 8 clock-frequency = <12000000>; 9 clock-output-names = "xti"; 10 #clock-cells = <0>; 11 }; 12 };
這個還是比較關鍵的,s3c2416並沒有這個節點,如果這個沒有配置的話,會導致很多問題,如後面的在bootconsole被disable後,由於時鐘問題,串口輸出會出問題,此外,也會導致很多內核除0異常。
-
clock控制器配置
這個也非常關鍵,在某個模塊在get_clk的時候就會用到,此外,在某個節點的屬性中配置了clocks和clock-names屬性的時候也會用到它,如果配置有問題,也會出現很多問題。
對應的設備樹配置(s3c2440.dtsi)如下:
1 clocks: clock-controller@4c000000 { 2 compatible = "samsung,s3c2440-clock"; 3 reg = <0x4c000000 0x20>; 4 #clock-cells = <1>; 5 };
關於clock這部分可以參考內核文檔:Documentation/devicetree/bindings/clock/samsung,s3c2410-clock.txt
串口引用clocks節點(s3c2440-tq2440-dt.dts):
1 serial@50000000 { 2 status = "okay"; 3 clock-names = "uart"; 4 clocks = <&clocks PCLK_UART0>; 5 pinctrl-names = "default"; 6 pinctrl-0 = <&uart0_data>; 7 };
其中PCLK_UART0參考arch/arm/boot/dts/include/dt-bindings/clock/s3c2410.h或者這個clock控制器驅動的實現
-
watchdog配置(s3c2440.dtsi)
這個是在使用時發現執行reboot命令後,系統沒有啓動復位重啓,而是提示進入halt狀態,原因是watchdog的硬體信息不完整而且其status默認是disabled狀態。
1 watchdog: watchdog@53000000 { 2 interrupts = <1 9 27 3>; 3 clocks = <&clocks PCLK>; 4 clock-names = "watchdog"; 5 };
從S3C2440的晶元手冊上面看,看門狗的clock直接接到PCLK上面了,並沒有加什麼開關,所以這裏clocks傳PCLK參數。
然後將watchdog使能的配置最好放到s3c2440-tq2440-dt.dts中,這樣很符合邏輯。
1 &uart0 { 2 status = "okay"; 3 };
標號uart0是在s3c24xx.dtsi中,有了標號,在引用一個節點的時候會很方面。
-
RTC配置(s3c2440.dtsi)
默認這個模塊也是disabled的,需要在s3c2440-tq2440-dt.dts將其使能,同時在s3c2440.dtsi中爲其配置一下時鐘。
-
DM9000配置
先看設備樹配置:
1 srom-cs4@20000000 { 2 compatible = "simple-bus"; 3 #address-cells = <1>; 4 #size-cells = <1>; 5 reg = <0x20000000 0x8000000>; 6 ranges; 7 ethernet@18000000 { 8 compatible = "davicom,dm9000"; 9 reg = <0x20000000 0x2 0x20000004 0x2>; 10 interrupt-parent = <&gpf>; 11 interrupts = <7 IRQ_TYPE_EDGE_RISING>; 12 local-mac-address = [00 00 de ad be ef]; 13 davicom,no-eeprom; 14 }; 15 };
DM9000接到了CS4上面,其地址範圍是:0x20000000 --> 0x28000000
看一下底板原理圖:
這裏關註幾點:
1. EINT7中斷: DM9000會臉道s3c2440的外部中斷7上,配置見第11和12行
2. LADDR2:接到了DM9000的CMD上,這個是用於區分發送給DM9000的數據是地址還是數據,低電平是地址,高電平是數據。LADDR2是第2根地址線(從0算起),所以addr和data的區分就在地址第2位上,所以addr是0x2000_0000,而data是0x2000_0004
3. nLAN_CS2:接到nGCS4上面,同時也接到DM9000的片選信號上面,當s3c2440發出0x2000_0000--->0x2800_0000範圍地址時,該引腳會被拉低,DM9000被選擇
4. reg屬性中的0x2的意思是DM9000工作在16bit模式,具體請參考DM9000的驅動:drivers/net/ethernet/davicom/dm9000.c
此外,在Linux-4.9下的dm9000驅動在tq2440上面並不能很好的工作,丟包嚴重,針對這部分,還是參照以前的修改辦法。
五、根文件系統
目前由於還沒有添加NandFlash的硬體信息,所以目前採用的是ramdisk形式的內存文件系統。
直接使用tq2440_ramdisk.tar.gz就可以了,使用ramdisk後,kernel裏也必須有相應的配置,剛開始就是由於kernel配置不當,導致根文件系統無法正常掛載。
關於這部分可以參考:http://blog.csdn.net/ctthuangcheng/article/details/8555529,這裏把關鍵的地方列出來:
make menuconfig ARCH=arm
- 打開配置菜單,修改兩個配置項,分別是:
a):General setup-->選擇 Initial RAM filesystem and RAM disk...... 項
b):Device Drivers-->Block devices-->選擇 RAM block device support 項
c):並檢查Optimize for size是否被選中,如果沒有則選中,此項優化內核大小,根據需要進行配置。
d):device driver->block device里的一個選項,Default Ramdisk 設置ramdisk的大小.16384
Note:修改Default RAM disk size kbytes選項為(8192)Default RAM disk size kbytes, 之所以修改是因為我們製作的ramdisk是8192KB大小的。如果這個大小和你做的ramdisk不匹配,則啟動時仍然會出現kernel panic內核恐慌,提示ramdisk格式不正確,掛載不上ramdisk。- 進入File systems菜單,選上<*> Second extended fs support
ramdisk是一種記憶體虛擬磁碟技術,實質上並不是一種文件系統,它使用的文件系統是ext2文件系統。
這樣就為內核添加好了ramdisk啟動功能和ramdisk的驅動支持了。
然後在chosen的bootargs中配置添加ramdisk相關的欄位:
bootargs = "root=/dev/ram0 rw rootfstype=ext2 console=ttySAC0,115200n8 init=/linuxrc ignore_loglevel earlyprintk"
結尾
後續會慢慢把tq2440板子上的其他設備也bring up起來,對次感興趣的小夥伴也可以自己嘗試一下,盡享設備樹帶來的樂趣吧!!
最後附上啓動log:
1 U-Boot 2015.04-g5095150 (Dec 21 2015 - 06:17:05) 2 CPUID: 32440001 3 FCLK: 400 MHz 4 HCLK: 100 MHz 5 PCLK: 50 MHz 6 I2C: ready 7 DRAM: 64 MiB 8 WARNING: Caches not enabled 9 Flash: 0 Bytes 10 NAND: 256 MiB 11 In: serial 12 Out: serial 13 Err: serial 14 Net: dm9000 15 Hit any key to stop autoboot: 0 16 TQ2440 # print 17 baudrate=115200 18 bootcmd=run cmd 19 bootdelay=0 20 cmd=tftp 0x30008000 uImage; tftp 0x31000000 ramdisk.img; tftp 0x33000000 dtb; bootm 0x30008000 0x31000000 0x33000000 21 cmd2=root=/dev/nfs rw nfsroot=192.168.2.8:/nfsroot/rootfs init=/linuxrc console=ttySAC0,115200n8 ip=192.168.2.6 22 cmd3=noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200n8 earlyprintk 23 cmd4=nand read 0x30008000 0x200000 0x400000;bootm 0x30008000; 24 cmd5=tftp 0x30008000 uImage; tftp 0x32000000 dtb; bootm 0x30008000 - 0x32000000 25 ethact=dm9000 26 ethaddr=00:0c:29:2a:5c:a5 27 fileaddr=33000000 28 filesize=fc6 29 ipaddr=192.168.2.8 30 netmask=255.255.255.0 31 serverip=192.168.2.6 32 Environment size: 665/524284 bytes 33 TQ2440 # boot 34 dm9000 i/o: 0x20000000, id: 0x90000a46 35 DM9000: running in 16 bit mode 36 MAC: 00:0c:29:2a:5c:a5 37 could not establish link 38 Using dm9000 device 39 TFTP from server 192.168.2.6; our IP address is 192.168.2.8 40 Filename 'uImage'. 41 Load address: 0x30008000 42 Loading: ################################################################# 43 ################################################################# 44 ################################################################# 45 ########################## 46 1.4 MiB/s 47 done 48 Bytes transferred = 3238648 (316af8 hex) 49 dm9000 i/o: 0x20000000, id: 0x90000a46 50 DM9000: running in 16 bit mode 51 MAC: 00:0c:29:2a:5c:a5 52 could not establish link 53 Using dm9000 device 54 TFTP from server 192.168.2.6; our IP address is 192.168.2.8 55 Filename 'ramdisk.img'. 56 Load address: 0x31000000 57 Loading: ################################################################# 58 ################################################################# 59 ###################################### 60 1.4 MiB/s 61 done 62 Bytes transferred = 2465340 (259e3c hex) 63 dm9000 i/o: 0x20000000, id: 0x90000a46 64 DM9000: running in 16 bit mode 65 MAC: 00:0c:29:2a:5c:a5 66 could not establish link 67 Using dm9000 device 68 TFTP from server 192.168.2.6; our IP address is 192.168.2.8 69 Filename 'dtb'. 70 Load address: 0x33000000 71 Loading: # 72 962.9 KiB/s 73 done 74 Bytes transferred = 3944 (f68 hex) 75 ## Booting kernel from Legacy Image at 30008000 ... 76 Image Name: Linux-4.9.0+ 77 Created: 2016-12-31 12:23:03 UTC 78 Image Type: ARM Linux Kernel Image (uncompressed) 79 Data Size: 3238584 Bytes = 3.1 MiB 80 Load Address: 30008000 81 Entry Point: 30008000 82 Verifying Checksum ... OK 83 ## Loading init Ramdisk from Legacy Image at 31000000 ... 84 Image Name: ramdisk 85 Created: 2016-12-31 15:42:22 UTC 86 Image Type: ARM Linux RAMDisk Image (gzip compressed) 87 Data Size: 2465276 Bytes = 2.4 MiB 88 Load Address: 00000000 89 Entry Point: 00000000 90 Verifying Checksum ... OK 91 ## Flattened Device Tree blob at 33000000 92 Booting using the fdt blob at 0x33000000 93 Loading Kernel Image ... OK 94 Loading Ramdisk to 33850000, end 33aa9dfc ... OK 95 Loading Device Tree to 3384c000, end 3384ff67 ... OK 96 Starting kernel ... 97 Uncompressing Linux... done, booting the kernel. 98 [ 0.000000] Booting Linux on physical CPU 0x0 99 [ 0.000000] Linux version 4.9.0+ (pengdonglin@pengdonglin-dell) (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29) ) #26 Sat Dec 31 20:22:58 CST 2016 100 [ 0.000000] CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c000717f 101 [ 0.000000] CPU: VIVT data cache, VIVT instruction cache 102 [ 0.000000] OF: fdt:Machine model: TQ2440 103 [ 0.000000] debug: ignoring loglevel setting. 104 [ 0.000000] bootconsole [earlycon0] enabled 105 [ 0.000000] Memory policy: Data cache writeback 106 [ 0.000000] CPU S3C2440A (id 0x32440001) 107 [ 0.000000] On node 0 totalpages: 16384 108 [ 0.000000] free_area_init_node: node 0