這里重要的是物理層PHY receiver,MAC(media access control)層,這里與軟件中的協議棧不同,在硬件上MAC是PHY的下一層。DM9000A將MAC和PHY做到一起,也可以像IIS設備那樣,SOC內有IIS的控制器,而聲卡UDA1341放在片外。網卡當然也有這種設計,它是把PHY的下層MAC放入SOC內,片外的是PHY,當然我暫時還沒見過這種的。DM9000A的輸入是並行的總線,可以和CPU直接IO。而IIS那種需要通過:CPU CORE BUS->I2S控制器->外設。通過對比,應該了解DM9000A怎樣進行IO了吧。其中PHY receiver中的AUTO-MDIX控制網卡的自適應,也就是說它能自動檢測連接的是什么速度的網絡,切換100,10。如果雙方都是自適應的,那就依據誤碼率來選擇。圖中的EEPROM當然存放的是MAC的地址,那么驅動從哪里得到MAC地址的?1)你手工設置的,然后他給你寫入到EEPROM里2)要么就是EEPROM里面應該好了。圖中的MII是一種稱為媒體獨立接口的東西,是為了和PHY連接而設計的。
再來看一下上邊這個DM9000A的原理圖,它的CMD引腳接在CPU的ADDR2上。這里的地址線和數據線是進行復用的。當CMD為0時,SD0~SD15傳的是地址,當CMD為1時,SD0~SD15傳的是數據。
DM9000A的驅動作為platform_driver注冊到內核中,根據Linux設備驅動模型,還應該有一個platform_device,但是源碼里沒有這個,這就需要我們自己來添加了,在arch/arm/plat-s3c24xx/devs.c中,添加:
#include <linux/dm9000.h> /* DM9000 Net Card */ static struct resource s3c_dm9000_resource[] = { [0] = { .start = S3C24XX_PA_DM9000, .end = S3C24XX_PA_DM9000+ 0x3, .flags = IORESOURCE_MEM }, [1]={ .start = S3C24XX_PA_DM9000 + 0x4, //CMD pin is A2 .end = S3C24XX_PA_DM9000 + 0x4 + 0x3, .flags = IORESOURCE_MEM }, [2] = { .start = IRQ_EINT7, .end = IRQ_EINT7, .flags = IORESOURCE_IRQ }, }; static struct dm9000_plat_data s3c_device_dm9000_platdata = { .flags= DM9000_PLATF_16BITONLY, }; struct platform_device s3c_device_dm9000 = { .name= "dm9000", .id= 0, .num_resources= ARRAY_SIZE(s3c_dm9000_resource), .resource= s3c_dm9000_resource, .dev= { .platform_data = &s3c_device_dm9000_platdata, } }; EXPORT_SYMBOL(s3c_device_dm9000);
注意這里第一個IO內存資源的范圍是從S3C24XX_PA_DM9000~ S3C24XX_PA_DM9000+ 0x3,第二個IO內存資源的范圍是從S3C24XX_PA_DM9000 + 0x4~ S3C24XX_PA_DM9000 + 0x4 + 0x3。這相當於兩個32位的寄存器。一個是地址寄存器,一個是數據寄存器。向地址寄存器中寫入0x1相當於選中了offset為1的NCR。向數據寄存器中寫入數據他會自動寫入TX RAM中。什么是TX RAM在DM9000A的驅動框架源碼分析文檔中已經解釋了,這里就不再贅述。在這個文件中添加了平台設備的資源和平台設備。那么這個平台設備又是什么時候注冊進內核的呢?我們繼續往下看。在設備列表添加對DM9000A設備的支持,在arch/arm/mach-s3c2440/mach-smdk2440.c文件中,添加紅色這一行:
static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, &s3c_device_rtc, &s3c_device_dm9000, };
那么這些platform_device是什么時候注冊的呢,看這個文件下邊的代碼,很明了了,在系統初始化的時候會注冊這個設備列表中的每一個設備。
static void __init smdk2440_machine_init(void) { s3c24xx_fb_set_platdata(&smdk2440_fb_info); s3c_i2c0_set_platdata(NULL); platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); smdk_machine_init(); } MACHINE_START(S3C2440, "SMDK2440") /* Maintainer: Ben Dooks <ben@fluff.org> */ .phys_io = S3C2410_PA_UART, .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, .init_irq = s3c24xx_init_irq, .map_io = smdk2440_map_io, .init_machine = smdk2440_machine_init, .timer = &s3c24xx_timer, MACHINE_END
然后修改arch/arm/plat-s3c24xx/include/mach/devs.h,添加:
extern struct platform_device s3c_device_dm9000;
修改arch/arm/mach-s3c2410/include/mach/map.h 文件
- /* DM9000 */
- #define S3C24XX_PA_DM9000 0x20000000
- #define S3C24XX_VA_DM9000 0xE0000000
這里設定了網卡的物理地址和虛擬地址,在arch/arm/mach-s3c2410/mach-smdk2410.c中進行靜態映射,在static struct platform_device *smdk2410_devices[] __initdata中添加
- static struct map_desc smdk2410_iodesc[] __initdata = {
- [0] = {
- .virtual = (unsigned long)S3C24XX_VA_DM9000,
- .pfn = __phys_to_pfn(S3C24XX_PA_DM9000),
- .length = SZ_1M,
- .type = MT_DEVICE,
- },
- };
這樣在程序中訪問S3C24XX_PA_DM9000,S3C24XX_PA_DM9000 + 0x4對應的物理地址,就直接轉換到相應的虛擬地址了。至此DM9000A平台設備的添加和IO地址的靜態映射完成。然后就是修改DM9000A中的MAC地址等了。
修改drivers/net/dm9000.c 文件,增加3行:
- #include <mach/regs-gpio.h>
- #include <mach/irqs.h>
- #include <mach/hardware.h>
修改dm9000_probe函數,添加:
unsigned char mac_addr[]={0x00,0x11,0x22,0x33,0x44,0x55}; static void *bwscon; static void *gpfcon; static void *extint0; static void *intmsk; #define BWSCON (0x48000000) #define GPFCON (0x56000050) #define EXTINT0 (0x56000088) #define INTMSK (0x4A000008) bwscon=ioremap_nocache(BWSCON,0x0000004); gpfcon=ioremap_nocache(GPFCON,0x0000004); extint0=ioremap_nocache(EXTINT0,0x0000004); intmsk=ioremap_nocache(INTMSK,0x0000004); writel(readl(bwscon)|0xc0000,bwscon); writel( (readl(gpfcon) & ~(0x3 << 14)) | (0x2 << 14), gpfcon); writel( readl(gpfcon) | (0x1 << 7), gpfcon); // Disable pull-up writel( (readl(extint0) & ~(0xf << 28)) | (0x4 << 28), extint0); //rising edge writel( (readl(intmsk)) & ~0x80, intmsk);
這里主要設置了總線寬度&等待寄存器,設置相應的GPIO引腳為EXINT7,中斷為上升沿觸發。
在這個函數的最后需要修改:
- if (!is_valid_ether_addr(ndev->dev_addr)) {
- /* try reading from mac */
- mac_src = "chip";
- for (i = 0; i < 6; i++)
- //ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
- ndev->dev_addr[i] = mac_addr[i];
- }
在文件系統中添加網絡配置文件,使系統在開機過程中自動完成對網卡的配置
1)在文件系統/etc/net.conf文件中添加內容
- IPADDR=192.168.0.105
- NETMASK=255.255.255.0
- GATEWAY=192.168.0.1
- MAC=00:11:22:33:44:55
2)在文件系統/sbin/目錄下新建一個可執行的腳本文件net_config,注意文件的權限(可執行)
- #!/bin/sh
- echo Try to bring eth0 interface up ...>/dev/s3c2410_serial0
- if [ -f /etc/net.conf ] ; then
- source /etc/net.conf
- ifconfig eth0 down
- ifconfig eth0 hw ether $MAC
- echo ifconfig eth0 hw ether $MAC >/dev/s3c2410_serial0
- ifconfig eth0 $IPADDR netmask $NETMASK up
- echo ifconfig eth0 $IPADDR netmask $NETMASK up >/dev/s3c2410_serial0
- route add default gw $GATEWAY
- echo add default gw $GATEWAY >/dev/s3c2410_serial0
- else
- ifconfig eth0 hw ether 00:11:22:33:44:55
- ifconfig eth0 192.168.1.105 netmask 255.255.255.0 up
- route add default gw 192.168.0.1
- echo ifconfig eth0 hw ether 00:11:22:33:44:55 >/dev/s3c2410_serial0
- echo ifconfig eth0 192.168.0.105 netmask 255.255.255.0 up >/dev/s3c2410_serial0
- echo route add default gw 192.168.0.1 >/dev/s3c2410_serial0
- fi
- echo Done > /dev/s3c2410_serial0
3)在文件系統/etc/init.d/rcS文件中添加網絡配置語句
- /sbin/ifconfig lo 127.0.0.1 #設置本地回環設備的IP地址
- net_config& #執行上面的net_config文件對網卡進行設置
在文件系統中添加完上述網卡配置信息后重新編譯文件系統下載到開發板,系統上電啟動后就會對網卡自動配置,並執行#ifconfig命令可以看到網卡的配置信息
測試與主機的通信,開發板的IP地址可以在/etc/net.conf中修改
s5pv210網卡驅動移植:http://www.openedv.com/thread-46879-1-1.html
實際上給內核移植DM9000驅動時,修改的代碼不超過到5行,但是為什么這樣改,一定要弄明白。 首先看改的代碼: 因為板子上DM9000接在BANK1位置,所以在Map.h (arch\arm\mach-s5pv210\include\mach) 中添加S5PV210_PA_SROM_BANK1的地址定義
#define S5PV210_PA_SROM_BANK5 0xA8000000 #define S5PV210_PA_SROM_BANK1 0x88000000 ...... 因為u-boot中已經對DM9000已經初始化了,所以要屏蔽掉Mach-smdkv210.c (arch\arm\mach-s5pv210) 中的初始化代碼:
1
2
3
4
5
|
static
void
__init smdkv210_machine_init(
void
)
{
s3c_pm_init();
/*smdkv210_dm9000_init();*/
|
修改platform-device中dm9000的配置,Mach-smdkv210.c (arch\arm\mach-s5pv210)
1
2
3
4
5
6
|
static
struct
resource smdkv210_dm9000_resources[] = {
[0] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK1, 4),
[1] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK1 + 4, 4),
[2] = DEFINE_RES_NAMED(IRQ_EINT(10), 1, NULL, IORESOURCE_IRQ \
| IORESOURCE_IRQ_HIGHLEVEL),
};
|
1
2
3
4
5
6
|
static
struct
resource smdkv210_dm9000_resources[] = {
[0] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK5, 1),
[1] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK5 + 2, 1),
[2] = DEFINE_RES_NAMED(IRQ_EINT(9), 1, NULL, IORESOURCE_IRQ \
| IORESOURCE_IRQ_HIGHLEVEL),
};
|
這里DEFINE_RES_MEM是一個宏
1
2
|
#define DEFINE_RES_MEM(_start, _size) \
DEFINE_RES_MEM_NAMED((_start), (_size), NULL)
|
,DEFINE_RES_MEM(S5PV210_PA_SROM_BANK1, 4)展開后為:
1
2
3
4
5
6
|
{ \
.start = (S5PV210_PA_SROM_BANK1), \
.end = (S5PV210_PA_SROM_BANK1) + (4) - 1, \
.name = (NULL), \
.flags = (IORESOURCE_MEM), \
}
|
首先了解一下linux的platform-deivce驅動,linux 2.6之后的內核用platform_device結構體來描述設備資源,有了platform_device,就可以用platform_add_devices添加設備了,smdkv210_machine_init會調用platform_add_devices來添加所有設備。smdkv210所有的設備用smdkv210_devices數組來表示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
static
struct
platform_device *smdkv210_devices[] __initdata = {
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_fb,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
&s3c_device_hsmmc3,
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_i2c2,
&s3c_device_rtc,
&s3c_device_ts,
&s3c_device_usb_hsotg,
&s3c_device_wdt,
&s5p_device_fimc0,
&s5p_device_fimc1,
&s5p_device_fimc2,
&s5p_device_fimc_md,
&s5p_device_jpeg,
&s5p_device_mfc,
&s5p_device_mfc_l,
&s5p_device_mfc_r,
&s5pv210_device_ac97,
&s5pv210_device_iis0,
&s5pv210_device_spdif,
&samsung_asoc_idma,
&samsung_device_keypad,
&smdkv210_dm9000,
&smdkv210_lcd_lte480wv,
};
|
可見,其中包含了smdkv210_dm9000,該結構體定義如下:
1
2
3
4
5
6
7
8
9
|
static
struct
platform_device smdkv210_dm9000 = {
.name = \"dm9000\",
.id = -1,
.num_resources = ARRAY_SIZE(smdkv210_dm9000_resources),
.resource = smdkv210_dm9000_resources,
.dev = {
.platform_data = &smdkv210_dm9000_platdata,
},
};
|
看到smdkv210_dm9000_resources了吧,所以platform-device使用resource的配置信息來配置設備資源。 回過頭來繼續分析smdkv210_dm9000_resources,這是個resource類型的數組,resource包含start,end,name,flag 4個成員。 因為DM9000是作為SROM(一個外部存儲器)使用的,有2個端口DATA和INDEX,INDEX用來指定地址,DATA用來收發數據。 smdkv210_dm9000_resources的第一組元素就用來定義INDEX端口地址,第二組元素用來定義DATA端口地址,第三組元素用來定義中斷號及中斷類型。明白了這些數據的作用,就可以按照之前移植U-boot網卡驅動的方法修改這些代碼了,即: INDEX端口的start為S5PV210_PA_SROM_BANK1 ,end為S5PV210_PA_SROM_BANK1 + 4,故size為4; DATA的端口start為S5PV210_PA_SROM_BANK1 + 4,SIZE為4。 中斷號為10(根據原理圖)。 在移植NFS根文件系統后,啟動會出現dm9000 dm9000: read wrong id 0x01010101 的錯誤,這是一個小bug,在DM9000.c中讀取ID的函數之前加上延時udelay(150)即可。 udelay(150)添加位置位於 iow(db, DM9000_NCR, NCR_MAC_LBK | NCR_RST); 之后即可,這是因為復位后要延時一小段時間才能正常讀ID。