• 正文
    • 一、網卡
    •  
    • 二、DM9000
    •  
    • 三、SROM 控制器
    •  
    • 四、DM9000A驅動分析
  • 相關推薦
申請入駐 產業(yè)圖譜

從0學ARM-網卡DM9000詳解-基于uboot

2021/05/13
1064
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

從0學arm系列繼續(xù)更新兩篇,這是第一篇,下一篇是uboot中的網絡協(xié)議棧詳解。之前系列請參考所有文章合集【墻裂建議收藏】:《從0學ARM》

一、網卡

1. 概念

網卡是一塊被設計用來允許計算機在計算機網絡上進行通訊的計算機硬件。由于其擁有MAC地址,因此屬于OSI模型的第2層。它使得用戶可以通過電纜或無線相互連接。

每一個網卡都有一個被稱為MAC地址的獨一無二的48位串行號,它被寫在卡上的一塊ROM中。在網絡上的每一個計算機都必須擁有一個獨一無二的MAC地址。沒有任何兩塊被生產出來的網卡擁有同樣的地址。這是因為電氣電子工程師協(xié)會(IEEE)負責為網絡接口控制器(網卡)銷售商分配唯一的MAC地址。

網卡上面裝有處理器存儲器(包括RAM和ROM)。網卡和局域網之間的通信是通過電纜或雙絞線以串行傳輸方式進行的。而網卡和計算機之間的通信則是通過計算機主板上的I/O總線并行傳輸方式進行。

因此,網卡的一個重要功能就是要進行串行/并行轉換。由于網絡上的數據率和計算機總線上的數據率并不相同,因此在網卡中必須裝有對數據進行緩存的存儲芯片。

網卡以前是作為擴展卡插到計算機總線上的,但是由于其價格低廉而且以太網標準普遍存在,大部分新的計算機都在主板上集成了網絡接口。

這些主板或是在主板芯片中集成了以太網的功能,或是使用一塊通過PCI (或者更新的PCI-Express總線)連接到主板上的廉價網卡。

除非需要多接口或者使用其它種類的網絡,否則不再需要一塊獨立的網卡。甚至更新的主板可能含有內置的雙網絡(以太網)接口。

2. 主要功能

1、數據的封裝與解封 發(fā)送時將上一層傳遞來的數據加上首部和尾部,成為以太網的幀。接收時將以太網的幀剝去首部和尾部,然后送交上一層

2、鏈路管理 主要是通過CSMA/CD(Carrier Sense Multiple Access with Collision Detection ,帶沖突檢測的載波監(jiān)聽多路訪問)協(xié)議來實現

3、數據編碼與譯碼 即曼徹斯特編碼與譯碼。其中曼徹斯特碼,又稱數字雙向碼、分相碼或相位編碼(PE),是一種常用的二元碼線路編碼方式之一,被物理層使用來編碼一個同步位流的時鐘和數據。在通信技術中,用來表示所要發(fā)送比特 流中的數據與定時信號所結合起來的代碼。常用在以太網通信,列車總線控制,工業(yè)總線等領域。

3. 分類

  1. 按總線接口類型分 按網卡的總線接口類型來分一般可分bai為ISA接口網卡、PCI接口網卡以及在服務器上使用的PCI-X總線接口類型的網卡,筆記本電腦所使用的網卡是PCMCIA接口類型的。
  • (1)ISA總線網卡(2)PCI總線網卡(3)PCI-X總線網卡(4)PCMCIA總線網卡(5)USB總線接口網卡
  1. 按網絡接口劃分 除了可以按網卡的總線接口類型劃分外,我們還可以按網卡的網絡接口類型來劃分。網卡最終是要與網絡進行連接,所以也就必須有一個接口使網線通過它與其它計算機網絡設備連接起來。不同的網絡接口適用于不同的網絡類型,目前常見的接口主要有以太網的RJ-45接口、細同軸電纜BNC接口和粗同軸電AUI接口、FDDI接口、ATM接口等。而且有的網卡為了適用于更廣泛的應用環(huán)境,提供了兩種或多種類型的接口,如有的網卡會同時提供RJ-45、BNC接口或AUI接口。
  • (1)RJ-45接口網卡(2)BNC接口網卡(3)AUI接口網卡(4)FDDI接口網卡(5)ATM接口網卡
  1. 按帶寬劃分 隨著網絡技術的發(fā)展,網絡帶寬也在不斷提高,但是不同帶寬的網卡所應用的環(huán)境也有所不同,目前主流的網卡主要有10Mbps網卡、100Mbps以太網卡、10Mbps/100Mbps自適應網卡、1000Mbps千兆以太網卡四種。
  • (1)10Mbps網卡(2)100Mbps網卡(3)10Mbps/100Mbps網卡(4)1000Mbps以太網卡

 

二、DM9000

DM9000芯片是DAVICOM公司生產,DM9000A 是一款完全集成的、性價比高、引腳數少、帶有通用處理器接口的單芯片快速以太網控制器。

一個 10/100M PHY 和 4K 雙字的 SRAM 。它是出于低功耗和高性能目的設計的,其 IO 端口支持 3.3V 與 5V 容限值。

DM9000A 為適應各種處理器,提供了 8 位、16 位數據接口訪問內部存儲器。

DM9000A物理協(xié)議層接口完全支持使用 10MBps 下 3 類、4 類、5 類非屏蔽雙絞線和 100MBps 下 5類非屏蔽雙絞線。這是完全遵照 IEEE 802.3u 標準。

它的自動協(xié)商功能將自動完成 DM9000AE配置以使其發(fā)揮出最佳性能。

它還支持 IEEE 802.3x 全雙工流量控制。

1. 模塊圖

圖1 DM9000內部結構框架

 

EEPROM Interface接口用于存放mac地址,Internal SRAM用于存放收發(fā)數據,MII部分把MAC部分與PHY部分連接起來通信,AUTO-MDIX用于自適應10/100M網絡,在物理層上,MAC在PHY之下。

2. 引腳分析

(#:表示低電平有效)

 

開發(fā)板FS4412的網卡DM9000A連接到了SROM控制器,下面我們分析數據線、地址線和信號線連接

1) SD0~15

SD0~15: 16位數據線連接到引腳BUF_B_Xm0DATA[0:15],由CMD引腳決定訪問類型。

可見數據和地址線都連接到了SOC的XM0上。

數據線和信號線對應的SROMC的引腳如上圖。

 

2)  CMD

 dm9000       外圍電路             轉換電路         soc 
  CMD--------BUF_B_Xm0ADDR2--------Xm0ADDR2-----Xm0ADDR2

 

如下圖所示:CMD:      命令線,當CMD為高,表示SD 傳輸的是數據,CMD為低表示傳輸的是地址,接在exynos4412的BUF_B_Xm0ADDR2上,可見CMD復用了地址線Xm0ADDR2引腳。

 

 

3)  IOR#、IOW#

 dm9000       外圍電路         轉換電路    soc 
  IOR--------BUF_Xm0OEn--------Xm0OEn-----Xm0OEn
  IOW--------BUF_Xm0WEn--------Xm0WEn-----Xm0WEn  

 

 

4) CS#

 dm9000       外圍電路         轉換電路    soc 
  CS--------BUF_Xm0cs1--------Xm0cs1-----Xm0CSn1

 

CS#:片選,放在exynos4412的Bank1的片選上面,內存基地址是0x05000000。

 

我們的DM9000A是放在exynos4412的Bank1(0X05000000)的片選上面。

而DM9000的CMD引腳接在Bank1的LADDR2上面

讀寫DM9000A的地址CMD拉低, 此時向0X05000000地址上讀寫的數據便是DM9000A的內部寄存器地址

讀寫DM9000A的數據CMD拉高,此時向0X05000000+4地址上讀寫的數據便是DM9000A的數據

設置exynos4412的bank1的硬件位寬,時序,因為不同的硬件,涉及的數據收發(fā)都不同。

 

5)  INT#

中斷線DM9000_IRQ通過U8轉接到引腳XEINT6

由上圖可知中斷引腳INT,接在exynos4412的GPX0_6腳上。uboot中的DM9000A的驅動沒有用到中斷。

 

3. 復用GPIO引腳

XM0引腳復用了GPIO引腳,所以需要初始化對應的GPIO引腳來使能SROMC。

1) GPY0CON

2) GPY1CON

3)GPY3CON

 

4) GPY5CON

 

5) GPY6CON

 

三、SROM 控制器

1. 概念

SROM是高速存儲器,Cache技術就是通過在DROM和CPU之間插入一小塊SROM來減小CPU和存儲之間的速度差異的。

本篇參考開發(fā)板FS4412,DM9000掛接到exynos 4412的SROM控制器上

EXYNOS 4412包含了SROM控制器,特性如下:

  • 外部 8/16位 NOR Flash/PROM/ SRAM memory.4組內存,每塊內存最多16 MB

首先我們要初始化 exynos4412的 SROM 控制器,設置總線寬度和相關時序。

針對 SROM 控制器的每一個 bank 只有2 個寄存器:SROM_BW 和 SROM_BC。

2. SROM_BW

在 SROM_BW 寄存器中,我們只關心與 bank1 相關的域。

上面分析過, DM9000A 的 16 根數據線全部接在 exynos 4412的數據線上,所以 DataWidth1 設置為 1; DM9000A 的地址是按字節(jié)存取的,所以 AddrMode1 設置為 1; 通過查看原理圖,沒有使用 Xm0WAITn和 Xm0BEn 引腳; 所以 WaitEnable1 和 ByteEnable1 均設置為 0。

SROM_BW[7:4]=0x3

 

3. SROM_BC1

SROM 控制器讀時序和 DM9000A 的讀時序主要通過SROM_BCn控制寄存器設置。

設置這些時序之前,首先來看DM9000A芯片手冊時序圖和exynos4412的時序圖

詳盡時序分析:,內存控制器使用HCLK作為時鐘,在HCLK為100MHz時,1個clock大約為10ns。信號值的設定如下:

信號 含義 最低時間(ns)
Tacs 地址發(fā)出后等多長時間發(fā)片選, DM9000AE 中 CS 和 CMD(地址)同時發(fā)出,所以 Tacs最低為0ns 0
Tcos 發(fā)出片選信號后等多長時間發(fā)出讀使能信號(nOW、 IOR),在 DM9000A 的時序圖上對應 T1,最小為 0 0
Tacc 讀使能信號持續(xù)時間,access cycle ,讀寫使能后,多久才能訪問數據,在 DM9000A 的時序圖上對應 T2 10
Tcoh 當DM9000A的寫信號取消后,數據線上的數據還需要至少3ns才消失(nOE讀寫取消后,片選需要維持多長時間)在 DM9000A 的時序圖中對應 T4 3
Tcah 片選結束后,地址保存時間, DM9000A 中CS和cmd同時結束,所以 Tcah=0 0
Tacp 頁模式,不管 0
PMC 頁模式,不管 0

從DM9000A的讀寫時序圖中可以看出,T2+T6實際上構成了DM9000A的一個訪問周期,因此還需要滿足:Tacs + Tcos + Tacc + Tcoh + Tcah>= T2+T6,最終使用下面的表達式來表達:(Tacs >= 0 && Tacs <= 4) && (Tcos >= 0 && Tcos <= 4) && (Tacc >= 1 && Tacc <= 14 ) && (Tcoh >=1 && Tcoh <= 4 )

寄存器SROM_BCn (n = 0 to 3)定義如下:

故設置參考值為:

#define DM9000_Tacs     (0x1)   // address set-up
#define DM9000_Tcos     (0x1)   // chip selection set-up
#define DM9000_Tacc     (0x5)   // access cycle
#define DM9000_Tcoh     (0x1)   // chip selection hold
#define DM9000_Tah      (0xC)   // address holding time
#define DM9000_Tacp     (0x9)   // page mode access cycle
#define DM9000_PMC      (0x1)   // normal(1data)page mode configuration

 

4. SROM初始化

u-boot 已經自帶了 DM9000系列網卡的驅動,在 u-boot 源碼中的 driver/net/dm9000x.c 的有一段說明:

       06/03/2008 Remy Bohmer 
   - Fixed the driver to work with DM9000A.
     (check on ISR receive status bit before reading the
     FIFO as described in DM9000 programming guide and
     application notes)
   - Added autodetect of databus width.
   - Made debug code compile again.
   - Adapt eth_send such that it matches the DM9000*
     application notes. Needed to make it work properly
     for DM9000A.
   - Adapted reset procedure to match DM9000 application
     notes (i.e. double reset)
   - some minor code cleanups
   These changes are tested with DM9000{A,EP,E} together
   with a 200MHz Atmel AT91SAM9261 core

可見,2008年Remy Bohmer已經為 DM9000A 添加了驅動,但是我們仍然需要針對板子做一些修改。

前一章我們針對參考的fs4412開發(fā)板移植了DM9000A的驅動,下面我們來詳細分析DM9000A驅動程序

分析驅動涉及到以下幾個文件:

arch/arm/lib/board.c
board/samsung/origen/origen.c
drivers/net/Dm9000x.c
drivers/net/Dm9000x.h
include/config_cmd_default.h
include/configs/origen.h
include/net.h
net/eth.c

 

5. 宏定義

在include/configs/origen.h中需要定義DM9000A基地址和編譯的宏。其中最重要的幾個宏如下:

名稱 說明
CONFIG_DM9000_BASE DM9000A 的基地址 0x05000000
DM9000_IO DM9000A 的 INDEX 端口地址 CONFIG_DM9000_BASE
DM9000_DATA DM9000A 的 DATA 端口地址 (CONFIG_DM9000_BASE + 4)
CONFIG_DRIVER_DM9000 Makefile中用于控制dm9000驅動是否編譯 1
CONFIG_DM9000_USE_16BIT DM9000A數據寬度  
CONFIG_DM9000_NO_SROM 表示沒有使用SROM 1

其中DM9000_DATA 定義為基地址+0x4,剛好把 Xm0ADDR2 拉高,即把 CMD 拉高。

查看文件drivers/net/Makefile:

 

從 Makefile 得知,要把 DM9000A 的驅動編譯進 u-boot中,需要定義 CONFIG_DRIVER_DM9000 這個宏。

宏定義如下:

#ifdef CONFIG_CMD_NET
#define CONFIG_NET_MULTI
#define CONFIG_DRIVER_DM9000      1
#define CONFIG_DM9000_BASE        0x05000000 
#define DM9000_IO           CONFIG_DM9000_BASE
#define DM9000_DATA         (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT
#define CONFIG_DM9000_NO_SROM     1
#define CONFIG_ETHADDR      11:22:33:44:55:66
#define CONFIG_IPADDR       192.168.6.187
#define CONFIG_SERVERIP           192.168.6.186
#define CONFIG_GATEWAYIP          192.168.6.1
#define CONFIG_NETMASK      255.255.255.0
#endif

除此以外我們還需要添加一些 u-boot 的命令,比如 ping 命令用來檢查網絡是否通暢,tftp用來下載文件。

uboot通過宏來控制是否編譯這些命令,include/configs/origen.h定義了一些宏,但是有的是undefine,我們要打開它們。

/* Command definition*/
#include 

#define CONFIG_CMD_PING
#define CONFIG_CMD_ELF
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MMC
#define CONFIG_CMD_FAT
#define CONFIG_CMD_NET
#undef CONFIG_CMD_NFS
#define CONFIG_CMD_HELLO
#define CONFIG_CMD_LEDA

除此之外頭文件:u-boot-2013.01/include/config_cmd_all.h 也列出了一些可用的命令。

#define CONFIG_CMD_BDI  /* bdinfo   */
#define CONFIG_CMD_BOOTD /* bootd   */
#define CONFIG_CMD_CONSOLE /* coninfo   */
#define CONFIG_CMD_ECHO  /* echo arguments  */
#define CONFIG_CMD_EDITENV /* editenv   */
#define CONFIG_CMD_FPGA  /* FPGA configuration Support */
#define CONFIG_CMD_IMI  /* iminfo   */
#define CONFIG_CMD_ITEST /* Integer (and string) test */
#ifndef CONFIG_SYS_NO_FLASH
#define CONFIG_CMD_FLASH /* flinfo, erase, protect */
#define CONFIG_CMD_IMLS  /* List all found images */
#endif
#define CONFIG_CMD_LOADB /* loadb   */
#define CONFIG_CMD_LOADS /* loads   */
#define CONFIG_CMD_MEMORY /* md mm nm mw cp cmp crc base loop mtest */
#define CONFIG_CMD_MISC  /* Misc functions like sleep etc*/
#define CONFIG_CMD_NET  /* bootp, tftpboot, rarpboot */
#define CONFIG_CMD_NFS  /* NFS support   */
#define CONFIG_CMD_RUN  /* run command in env variable */
#define CONFIG_CMD_SAVEENV /* saveenv   */
#define CONFIG_CMD_SETGETDCR /* DCR support on 4xx  */
#define CONFIG_CMD_SOURCE /* "source" command support */
#define CONFIG_CMD_XIMG  /* Load part of Multi Image */

 

6. 初始化srom

在arch/arm/lib/board.c的函數board_init_r中有如下代碼:

void board_init_r(gd_t *id, ulong dest_addr)
{
 ……
 board_init(); /* Setup chipselects */
 ……
}

函數board_init()定義在board/samsung/origen/origen.c中,我們在該函數中添加了初始化srom代碼:

int board_init(void)
{
 gpio1 = (struct exynos4_gpio_part1 *) EXYNOS4_GPIO_PART1_BASE;
 gpio2 = (struct exynos4_gpio_part2 *) EXYNOS4_GPIO_PART2_BASE;

 gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL);

#ifdef CONFIG_DRIVER_DM9000
 dm9000aep_pre_init();
#endif
 return 0;
}

函數dm9000aep_pre_init用來設置 SROM 控制器。

static void dm9000aep_pre_init(void)
{
 unsigned int tmp;
 unsigned char smc_bank_num = 1;
 unsigned int     smc_bw_conf=0;
 unsigned int     smc_bc_conf=0;

 /* gpio configuration */
 writel(0x00220020, 0x11000000 + 0x120);//GPY0CON
 writel(0x00002222, 0x11000000 + 0x140);//GPY1CON
 /* 16 Bit bus width */
 writel(0x22222222, 0x11000000 + 0x180);//GPY3CON
 writel(0x0000FFFF, 0x11000000 + 0x188);//GPY3PUD
 writel(0x22222222, 0x11000000 + 0x1C0);//GPY5CON
 writel(0x0000FFFF, 0x11000000 + 0x1C8);//GPY5PUD
 writel(0x22222222, 0x11000000 + 0x1E0);//GPY6CON
 writel(0x0000FFFF, 0x11000000 + 0x1E8);//GPY6PUD
               
 smc_bw_conf &= ~(0xf<<4);
 smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
 smc_bc_conf = ((DM9000_Tacs << 28)
   | (DM9000_Tcos << 24)
   | (DM9000_Tacc << 16)
   | (DM9000_Tcoh << 12)
   | (DM9000_Tah << 8)
   | (DM9000_Tacp << 4)
   | (DM9000_PMC));
 exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf);
}
/*
 *  exynos_config_sromc() - select the proper SROMC Bank and configure the
 *  band width control and bank control registers
 *  srom_bank    - SROM
 *  srom_bw_conf  - SMC Band witdh reg configuration value
 *  srom_bc_conf  - SMC Bank Control reg configuration value
 */

void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
 unsigned int tmp;
 struct exynos_sromc *srom = (struct exynos_sromc *)(EXYNOS4412_SROMC_BASE);

 /* Configure SMC_BW register to handle proper SROMC
  * bank */
 tmp = srom->bw;
 tmp &= ~(0xF << (srom_bank * 4));
 tmp |= srom_bw_conf;
 srom->bw = tmp;

 /* Configure SMC_BC
  * register */
 srom->bc[srom_bank] = srom_bc_conf;
}

 

四、DM9000A驅動分析

DM9000A所能支持的功能非常的多,驅動的實現相對比較復雜,搞清楚裸機的網卡驅動,我們再去學習Linux內核的DM9000驅動就相對容易一些。本節(jié)將詳細講解DM9000A網卡的數據的收發(fā)操作的流程。

1. 相關結構體

struct board_info

/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
 u32 runt_length_counter; /* counter: RX length < 64byte */
 u32 long_length_counter; /* counter: RX length > 1514byte */
 u32 reset_counter; /* counter: RESET */
 u32 reset_tx_timeout; /* RESET caused by TX Timeout */
 u32 reset_rx_status; /* RESET caused by RX Statsus wrong */
 u16 tx_pkt_cnt;
 u16 queue_start_addr;
 u16 dbug_cnt;
 u8 phy_addr;
 u8 device_wait_reset; /* device state */
 unsigned char srom[128];
 void (*outblk)(volatile void *data_ptr, int count);
 void (*inblk)(void *data_ptr, int count);
 void (*rx_status)(u16 *RxStatus, u16 *RxLen);
 struct eth_device netdev;
} board_info_t;

static board_info_t dm9000_info;

結構體是用來維護DM9000系列網卡的結構體,所有和網卡DM9000A信息都保存到該結構體中。struct eth_devicestruct board_info中有一個重要的成員 netdev,該成員是uboot提供的標準的統(tǒng)一的網卡設備接口。

struct eth_device {
 char name[16];
 unsigned char enetaddr[6];
 int iobase;
 int state;

 int  (*init) (struct eth_device *, bd_t *);
 int  (*send) (struct eth_device *, void *packet, int length);
 int  (*recv) (struct eth_device *);
 void (*halt) (struct eth_device *);
#ifdef CONFIG_MCAST_TFTP
 int (*mcast) (struct eth_device *, u32 ip, u8 set);
#endif
 int  (*write_hwaddr) (struct eth_device *);
 struct eth_device *next;
 int index;
 void *priv;
};

該結構體維護了操作網卡的回調函數等信息,我們只需要把網口的收發(fā)數據操作封裝到對應的回調函數中,然后注冊到系統(tǒng)即可。

2. 網卡注冊/注銷

進入到arch/arm/lib/board.c 中的 board_init_r 函數:

665 #if defined(CONFIG_CMD_NET)
666     puts("Net:   ");
667     eth_initialize(gd->bd);
668 #if defined(CONFIG_RESET_PHY_R)
669     debug("Reset Ethernet PHYn");
670     reset_phy();
671 #endif

如果定義了 CONFIG_CMD_NET,就調用 eth_initialize(gd->bd)進行網卡初始化。

這個宏在include/config_cmd_default.h 中定義,這個頭文件又被單板配置文件 include/configs/origen.h 所包含。

eth_initialize 函數在 net/eth.c 中定義,下面是該函數部分代碼:

308     /*
309      * If board-specific initialization exists, call it.
310      * If not, call a CPU-specific one
311      */
312     if (board_eth_init != __def_eth_init) {
313         if (board_eth_init(bis) < 0)
314             printf("Board Net Initialization Failedn");
315     } else if (cpu_eth_init != __def_eth_init) {
316         if (cpu_eth_init(bis) < 0)
317             printf("CPU Net Initialization Failedn");
318     } else
319         printf("Net Initialization Skippedn");

這段代碼功能是:如果定義了單板相關的初始化函數就調用它,否則調用 CPU 相關的初始化函數。

其中__def_eth_init 函數,同樣在net/eth.c 中定義

105  * CPU and board-specific Ethernet initializations.  Aliased function
106  * signals caller to move on
107  */
108 static int __def_eth_init(bd_t *bis)
109 {
110     return -1;
111 }
112 int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
113 int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));

這里用到了 gcc 的弱符號和別名屬性。如果我們沒有定義自己的 board_eth_init 函數,則 board_eth_init 就和__def_eth_init 相同,調用 board_eth_init 就相當于調用__def_eth_init,現在就能明白上面的 if 判斷語句了。

board_eth_init 在board/samsung/origen/origen.c 中定義

264 #ifdef CONFIG_CMD_NET
265 int board_eth_init(bd_t *bis)                                                  
266 {      
267 
268     int rc = 0;
269 #ifdef CONFIG_DRIVER_DM9000
270     rc = dm9000_initialize(bis);                                            
271 #endif                                                                         
272     return rc;                                                              
273 }  
274 #endif

這里通過配置宏來決定調用哪個網卡初始化函數。

我們使用的是 DM9000A,我們先查看下 DM9000A 的驅動源文件drivers/net/DM9000x.c,初始化函數如下:

626 int dm9000_initialize(bd_t *bis)
627 {
628     struct eth_device *dev = &(dm9000_info.netdev);
629 
630     /* Load MAC address from EEPROM */
631     dm9000_get_enetaddr(dev);
632 
633     dev->init = dm9000_init;
634     dev->halt = dm9000_halt;
635     dev->send = dm9000_send;
636     dev->recv = dm9000_rx;
637     sprintf(dev->name, "dm9000");
638 
639     eth_register(dev);
640 
641     return 0;
642 } 

該函數就是 DM9000A 的初始化函數。631行dm9000_get_enetaddr 從 EEPROM 加載MAC地址,

static void dm9000_get_enetaddr(struct eth_device *dev)
{
#if !defined(CONFIG_DM9000_NO_SROM)
 int i;
 for (i = 0; i < 3; i++)
  dm9000_read_srom_word(i, dev->enetaddr + (2 * i));
#endif
}

該函數根據宏CONFIG_DM9000_NO_SROM 來決定是否從EEPROM 加載MAC地址, 參考的板子上的 DM9000A 沒有接 EEPROM,我們在 origen.h 中定義了這個宏,表示不從 EEPROM 加載 MAC地址。

633~636行是將網卡的初始化和收發(fā)數據的函數填充到dev中,用于注冊到系統(tǒng)中:

639行,函數eth_register()的參數是dev,該變量地址其實是dm9000_info.netdev的地址。dm9000_info定義在同一文件下:

108  static board_info_t dm9000_info;

函數eth_register()位于net/eth.c中;

  • 功能:用于注冊網卡到系統(tǒng)中,如果之前網卡設備鏈表為空,則直接復制給全局指針變量eth_devices和eth_current ,如果不為空,則把當前網卡插入到鏈表eth_devices中。
int eth_register(struct eth_device *dev)
{
 struct eth_device *d;
 static int index;

 assert(strlen(dev->name) < sizeof(dev->name));

 if (!eth_devices) {//網卡設備鏈表為空
  eth_current = eth_devices = dev;
  eth_current_changed();
 } else {//找到表尾
  for (d = eth_devices; d->next != eth_devices; d = d->next)
   ;
  d->next = dev;//插入表尾
 }

 dev->state = ETH_STATE_INIT;
 dev->next  = eth_devices;//新的設備指向網卡設備表頭
 dev->index = index++;

 return 0;
}

其中

eth_devices:網卡設備的鏈表 eth_current:用于保存當前使用的網卡

網卡注銷網卡注銷函數eth_unregister() 該函數會將網卡節(jié)點dev從鏈表eth_devices中刪除,并重新設置變量eth_current。

int eth_unregister(struct eth_device *dev)
{
 struct eth_device *cur;

 /* No device */
 if (!eth_devices)
  return -1;

 for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
      cur = cur->next)
  ;

 /* Device not found */
 if (cur->next != dev)
  return -1;

 cur->next = dev->next;

 if (eth_devices == dev)
  eth_devices = dev->next == eth_devices ? NULL : dev->next;

 if (eth_current == dev) {
  eth_current = eth_devices;
  eth_current_changed();
 }

 return 0;
}

 

3. 寄存器

DM9000A 擁有一系列的控制和狀態(tài)寄存器,這些寄存器可以被處理器所訪問,這些寄存器是按字節(jié)對齊的。

所有的 CSRs 在軟件或者硬件復位后都將被置為默認值,除非他們被另外標識。

編號 寄存器 描述 偏移地址 復位后默認值
1 NCR 網絡控制寄存器 00H 00H
2 NSR 網絡狀態(tài)寄存器 01H 00H
3 TCR 發(fā)送控制寄存器 02H 00H
4 TSR I 發(fā)送狀態(tài)寄存器 1 03H 00H
5 TSR II 發(fā)送狀態(tài)寄存器 2 04H 00H
6 RCR 接收控制寄存器 05H 00H
7 RSR 接收狀態(tài)寄存器 06H 00H
8 ROCR 接收溢出計數寄存器 07H 00H
9 BPTR 背壓閾值寄存器 08H 37H
10 FCTR 流控制閾值寄存器 09H 38H
11 FCR TX/RX 流控制寄存器 0AH 00H
12 EPCR EEPROM&PHY 控制寄存器 0BH 00H
13 EPAR EEPROM&PHY 地址寄存器 0CH 40H
14 EPDRL EEPROM&PHY 低字節(jié)數據寄存器 0DH XXH
15 EPDRH EEPROM&PHY 高字節(jié)數據寄存器 0EH XXH
16 WCR 喚醒控制寄存器 0FH 00H
17 PAR 物理地址寄存器 10H~15H 由 EEPROM決定
18 MAR 廣播地址寄存器 16H~1DH XXH
19 GPCR 通用目的控制寄存器(8bit 模式) 1EH 01H
20 GPR 通用目的寄存器 1FH XXH
21 TRPAL TX SRAM 讀指針地址低字節(jié) 22H 00H
22 TRPAH TX SRAM 讀指針地址高字節(jié) 23H 00H
23 RWPAL RX SRAM 寫指針地址低字節(jié) 24H 00H
24 RWPAH RX SRAM 寫指針地址高字節(jié) 25H 0CH
25 VID 廠家 ID 28H~29H 0A46H
26 PID 產品 ID 2AH~2BH 9000H
27 CHIPR 芯片版本 2CH 18H
28 TCR2 發(fā)送控制寄存器 2 2DH 00H
29 OCR 操作控制寄存器 2EH 00H
30 SMCR 特殊模式控制寄存器 2FH 00H
31 ETXCSR 即將發(fā)送控制/狀態(tài)寄存器 30H 00H
32 TCSCR 發(fā)送校驗和控制寄存器 31H 00H
33 RCSCSR 接收校驗和控制狀態(tài)寄存器 32H 00H
34 MRCMDX 內存數據預取讀命令寄存器(地址不加 1) F0H XXH
35 MRCMDX1 內存數據讀命令寄存器(地址不加 1) F1H XXH
36 MRCMD 內存數據讀命令寄存器(地址加 1) F2H XXH
37 MRRL 內存數據讀地址寄存器低字節(jié) F4H 00H
38 MRRH 內存數據讀地址寄存器高字節(jié) F5H 00H
39 MWCMDX 內存數據寫命令寄存器(地址不加 1) F6H XXH
40 MWCMD 內存數據寫命令寄存器(地址加 1) F8H XXH
41 MWRL 內存數據寫地址寄存器低字節(jié) FAH 00H
42 MWRH 內存數據寫地址寄存器高字節(jié) FBH 00H
43 TXPLL TX 數據包長度低字節(jié)寄存器 FCH XXH
44 TXPLH TX 數據包長度高字節(jié)寄存器 FDH XXH
45 ISR 中斷狀態(tài)寄存器 FEH 00H
46 IMR 中斷屏蔽寄存器 FFH 00H

關于默認值的要點(Key to Default) 在下面寄存器描述中,默認欄采用如下形式:


其中

1 該位設為邏輯 1
0 該位設為邏輯 0
X 沒有默認值
P 電源復位恢復默認值
H 硬件復位恢復默認值
S 軟件復位恢復默認值
E 從 EEPROM 得到默認值
T 從捆綁引腳(strap pin)得到默認值

:

RO = 只讀
RW = 可讀可寫
R/C = 可讀/擦除
RW/C1=可讀可寫/通過寫1擦除
WO = 只寫

保留位被隱藏且應寫 0,在讀訪問時保留位沒有定義。

如何讀取 DM9000A 的寄存器 RSR?假設要讀取 DM9000A 的寄存器 RSR(RX Status Register),需要分 2 步:

  1. 向 INDEX 端口寫入 RSR 寄存器的地址(0x06) 條件:nGCS1 信號拉低、 Xm0WEn 信號拉低、 Xm0ADDR2 拉低, 或者說向下面的地址寫數據 0x06從 DATA 端口讀取 RSR 寄存器的值 條件:nGCS1 信號拉低、 Xm0OEn 信號拉低、 Xm0ADDR2 拉高, 或者說從下面的地址讀數據

DM9000A的寄存器很多,但是我們并需要都掌握,我們只需要掌握其中幾個最重要的寄存器的使用即可。

  1. 網絡控制寄存器(NCR)

 

網絡狀態(tài)寄存器(NSR)

在這里插入圖片描述

 

ISR

DAVICOM 指定配置和狀態(tài)寄存器(DSCSR)

 

 

4. 網卡的初始化

網卡的初始化函數入口位于文件net/eth.c下的函數eth_init():

404 int eth_init(bd_t *bis)
405 {
406     struct eth_device *old_current, *dev;
  ……
425     old_current = eth_current;
426     do {
427         debug("Trying %sn", eth_current->name);
428 
429         if (eth_current->init(eth_current, bis) >= 0) {
430             eth_current->state = ETH_STATE_ACTIVE;
431 
432             return 0;
433         }
434         debug("FAILn");
 ……
440 }

429行即調用我們注冊的dm9000A初始化函數,從這也可以看出,整個架構是把網卡的驅動獨立分隔開,與硬件操作相關的代碼由用戶自己填充并注冊到系統(tǒng)中即可,便于擴展。進入dm9000_init():

290 static int dm9000_init(struct eth_device *dev, bd_t *bd)
291 {
292     int i, oft, lnk;
293     u8 io_mode;
294     struct board_info *db = &dm9000_info;
295 
296     DM9000_DBG("%sn", __func__);
297 
298     /* RESET device */
299     dm9000_reset();
300 
301     if (dm9000_probe() < 0)
302         return -1;
303 
304     /* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */
305     io_mode = DM9000_ior(DM9000_ISR) >> 6;
306 
307     switch (io_mode) {
308     case 0x0:  /* 16-bit mode */
309         printf("DM9000: running in 16 bit moden");
310         db->outblk    = dm9000_outblk_16bit;
311         db->inblk     = dm9000_inblk_16bit;
312         db->rx_status = dm9000_rx_status_16bit;
313         break;
314     case 0x01:  /* 32-bit mode */
315         printf("DM9000: running in 32 bit moden");
316         db->outblk    = dm9000_outblk_32bit;
317         db->inblk     = dm9000_inblk_32bit;
318         db->rx_status = dm9000_rx_status_32bit;
319         break;
320     case 0x02: /* 8 bit mode */
321         printf("DM9000: running in 8 bit moden");
322         db->outblk    = dm9000_outblk_8bit;
323         db->inblk     = dm9000_inblk_8bit;
324         db->rx_status = dm9000_rx_status_8bit;
325         break;
326     default:
327         /* Assume 8 bit mode, will probably not work anyway */
328         printf("DM9000: Undefined IO-mode:0x%xn", io_mode);
329         db->outblk    = dm9000_outblk_8bit;
330         db->inblk     = dm9000_inblk_8bit;
331         db->rx_status = dm9000_rx_status_8bit;
332         break;
333     } 
334 
335     /* Program operating register, only internal phy supported */
336     DM9000_iow(DM9000_NCR, 0x0);
337     /* TX Polling clear */
338     DM9000_iow(DM9000_TCR, 0);
339     /* Less 3Kb, 200us */
340     DM9000_iow(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
341     /* Flow Control : High/Low Water */
342     DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
343     /* SH FIXME: This looks strange! Flow Control */
344     DM9000_iow(DM9000_FCR, 0x0);
345     /* Special Mode */
346     DM9000_iow(DM9000_SMCR, 0);
347     /* clear TX status */
348     DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
349     /* Clear interrupt status */
350     DM9000_iow(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
351 
352     printf("MAC: %pMn", dev->enetaddr);
353 
354     /* fill device MAC address registers */
355     for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
356         DM9000_iow(oft, dev->enetaddr[i]);
357     for (i = 0, oft = 0x16; i < 8; i++, oft++)
358         DM9000_iow(oft, 0xff);
359 
360     /* read back mac, just to be sure */
361     for (i = 0, oft = 0x10; i < 6; i++, oft++)
362         DM9000_DBG("%02x:", DM9000_ior(oft));
363     DM9000_DBG("n");
364 
365     /* Activate DM9000 */
366     /* RX enable */
367     DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
368     /* Enable TX/RX interrupt mask */
369     DM9000_iow(DM9000_IMR, IMR_PAR); 
370 
371     i = 0;
372     while (!(dm9000_phy_read(1) & 0x20)) {  /* autonegation complete bit */
373         udelay(1000);
374         i++;
375         if (i == 10000) {
376             printf("could not establish linkn");
377             return 0;
378         }
379     }
380 
381     /* see what we've got */
382     lnk = dm9000_phy_read(17) >> 12;
383     printf("operating at ");
384     switch (lnk) {
385     case 1:
386         printf("10M half duplex ");
387         break;
388     case 2:
389         printf("10M full duplex ");
390         break;
391     case 4:
392         printf("100M half duplex ");
393         break;
394     case 8:
395         printf("100M full duplex ");
396         break;
397     default:
398         printf("unknown: %d ", lnk);
399         break;
400     }
401     printf("moden");
402     return 0;
403 }

299行 函數DM9000_reset()是對dm9000A重置 301行 函數dm9000_probe()分別從寄存器VID、PID讀取廠家ID、產品ID 305行 讀取DM9000A的 ISR寄存器,根據bite[6:7]的值來決定最終從DM9000A中讀取數位數,并將對應的函數設置到db->outblk和db->inblk這兩個變量,最終上層服務想收發(fā)數據就通過這兩個函數,對于16位模式,就分別賦值dm9000_outblk_16bit、dm9000_inblk_16bit;db->rx_status該函數用于從DM9000A中讀取網卡的狀態(tài)信息和數據包的長度,對于16位模式會賦值為dm9000_rx_status_16bit 336~350行 對DM9000A進行初始化配置 355~358行 將mac地址寫入到DM9000A的PAR寄存器 367行 使能數據接收 369行 使能SRAM的讀/寫指針在指針地址超過SRAM的大小時自動跳回起始位置 382行 讀取phy寄存器DSCSR,打印當前網口的帶寬

通過讀 bit[15:12]來看經過自動協(xié)商后選擇的是哪一種模式。網卡自動協(xié)商完成后,結果將被寫到該位。若該位為 1,意味著操作 1 模式是 100M 全雙工模式。

5. 數據的發(fā)送

發(fā)送流程

  1. 清中斷,ISR寄存器bit[1] = 1發(fā)送寫操作,操作MWCMD通過DM9000_DATA寫入數據設置數據幀的長度 TXPLL、TXPLH發(fā)送發(fā)送請求,TCR等待數據發(fā)送完畢,輪訓檢查NSR清中斷,ISR寄存器bit[1] = 1

網卡數據的發(fā)送函數是dm9000_send()

405 /*
406   Hardware start transmission.
407   Send a packet to media from the upper layer.
408 */
409 static int dm9000_send(struct eth_device *netdev, void *packet, int length)
410 {
411     int tmo;
412     struct board_info *db = &dm9000_info;
413 
414     DM9000_DMP_PACKET(__func__ , packet, length);
415 
416     DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
417                                                                                                                                               
418     /* Move data to DM9000 TX RAM */
419     DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
420 
421     /* push the data to the TX-fifo */
422     (db->outblk)(packet, length);
423 
424     /* Set TX length to DM9000 */
425     DM9000_iow(DM9000_TXPLL, length & 0xff);
426     DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
427 
428     /* Issue TX polling command */
429     DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
430 
431     /* wait for end of transmission */
432     tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
433     while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
434         !(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {
435         if (get_timer(0) >= tmo) {
436             printf("transmission timeoutn");
437             break;
438         }
439     }
440     DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
441 
442     DM9000_DBG("transmit donenn");
443     return 0;
444 }

該函數的參數

struct eth_device *netdev:設備
void *packet   :發(fā)送數據包存放的內存的首地址
int length     :發(fā)送的數據包長度

414行 打開debug開關,該行會打印發(fā)送的數據包 416行 使能數據包發(fā)送,將寄存器ISR的bit[1]設置為1 419行 通過寄存器MWCMD寫入一個地址,并向該地址對應的 SRAM 中寫數據。執(zhí)行寫該指令之后,寫指針會根據操作模式(8 位或 16 位)自動增加 1 或 2。422行 調用上一節(jié)db->outblk所賦值的函數將數據包發(fā)送的DM9000A的發(fā)送fifo中 425~426行 將發(fā)送數據包長度寫入到寄存器TXPLL/TXPLH中,這兩個寄存器分別對應低字節(jié)和高字節(jié) 429行 向寄存器TCR的bit[0]寫入1,來請求發(fā)送數據,發(fā)送完畢該位自動清0 432~440行  通過向寄存器ISR的bit[1]寫入1,來清楚發(fā)送標記位

其中發(fā)送函數dm9000_outblk_16bit() 定義如下:

159 static void dm9000_outblk_16bit(volatile void *data_ptr, int count)
160 {
161  int i;
162  u32 tmplen = (count + 1) / 2;
163 
164  for (i = 0; i < tmplen; i++)
165   DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
166 }

164~165行 就是循環(huán)從地址DM9000_DATA讀取數據并存儲到data_ptr執(zhí)行的內存中 此處我們看到每次都是從相同的地址讀取數據,為什么不需要做地址偏移呢?答:寄存器MWCMD已經和我們說的很清楚了,寫該指令之后,指寫指針根據操作模式(8 位或 16 位)增 加 1 或 2。

6. 數據的接收

DM9000A的數據接收

464 static int dm9000_rx(struct eth_device *netdev)
465 {
466     u8 rxbyte, *rdptr = (u8 *) NetRxPackets[0];
467     u16 RxStatus, RxLen = 0;
468     struct board_info *db = &dm9000_info;
469 
470     /* Check packet ready or not, we must check
471        the ISR status first for DM9000A */
472     if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
473         return 0;
474 
475     DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
476 
477     /* There is _at least_ 1 package in the fifo, read them all */
478     for (;;) {
479         DM9000_ior(DM9000_MRCMDX);  /* Dummy read */
480 
481         /* Get most updated data,
482            only look at bits 0:1, See application notes DM9000 */
483         rxbyte = DM9000_inb(DM9000_DATA) & 0x03;
484 
485         /* Status check: this byte must be 0 or 1 */
486         if (rxbyte > DM9000_PKT_RDY) {
487             DM9000_iow(DM9000_RCR, 0x00);   /* Stop Device */
488             DM9000_iow(DM9000_ISR, 0x80);   /* Stop INT request */
489             printf("DM9000 error: status check fail: 0x%xn",
490                 rxbyte);
491             return 0;
492         }
493 
494         if (rxbyte != DM9000_PKT_RDY)
495             return 0; /* No packet received, ignore */
496 
497         DM9000_DBG("receiving packetn");
498 
499         /* A packet ready now  & Get status/length */
500         (db->rx_status)(&RxStatus, &RxLen);
501 
502         DM9000_DBG("rx status: 0x%04x rx len: %dn", RxStatus, RxLen);
503 
504         /* Move data from DM9000 */
505         /* Read received packet from RX SRAM */
506         (db->inblk)(rdptr, RxLen);
507    
508         if ((RxStatus & 0xbf00) || (RxLen < 0x40)
509             || (RxLen > DM9000_PKT_MAX)) {
510             if (RxStatus & 0x100) {
511                 printf("rx fifo errorn");
512             }
513             if (RxStatus & 0x200) {
514                 printf("rx crc errorn");
515             }
516             if (RxStatus & 0x8000) {
517                 printf("rx length errorn");
518             }
519             if (RxLen > DM9000_PKT_MAX) {
520                 printf("rx length too bign");
521                 dm9000_reset();
522             }
523         } else {
524             DM9000_DMP_PACKET(__func__ , rdptr, RxLen);
525 
526             DM9000_DBG("passing packet to upper layern");
527             NetReceive(NetRxPackets[0], RxLen);
528         }
529     }
530     return 0;
531 }

472行 DM9000A的寄存器ISR的bit[0]必須設置為1,否則無法接收數據 475行 將ISR的bit[0]設置為1 479行 讀取寄存器MRCMDX, 以從接收 SRAM 中讀數據;執(zhí)行讀取該指令之后,指向內部 SRAM的讀指針不變。DM9000A 開始預取 SRAM 中數據到內部數據緩沖中 483~494行 從地址DM9000_DATA中讀取數據,從SRAM中讀取的第一個數據的bit[0]必須是1,否則出錯 500行 通過函數指針db->rx_status讀取網卡的狀態(tài)和接收到的數據包的長度 506行 通過函數指針db->inblk從網卡中讀取數據 527行 通過函數NetReceive()提交給上層協(xié)議棧

真正讀取數據的函數是dm9000_inblk_16bit(); 定義如下:

static void dm9000_inblk_16bit(void *data_ptr, int count)
{
 int i;
 u32 tmplen = (count + 1) / 2;

 for (i = 0; i < tmplen; i++)
  ((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA);
}

原理類似于函數dm9000_outblk_16bit,不再重復。

由此可見,要分析DM9000A的數據收發(fā)的原理和流程,就要分析我們注冊網卡的以下幾個函數:

635   dev->send = dm9000_send;
636   dev->recv = dm9000_rx;

310   db->outblk    = dm9000_outblk_16bit;
311   db->inblk     = dm9000_inblk_16bit;

Arm

Arm

ARM公司是一家知識產權(IP)供應商,主要為國際上其他的電子公司提供高性能RISC處理器、外設和系統(tǒng)芯片技術授權。目前,ARM公司的處理器內核已經成為便攜通訊、手持計算設備、多媒體數字消費品等方案的RISC標準。公司1990年11月由Acorn、Apple和VLSI合并而成。

ARM公司是一家知識產權(IP)供應商,主要為國際上其他的電子公司提供高性能RISC處理器、外設和系統(tǒng)芯片技術授權。目前,ARM公司的處理器內核已經成為便攜通訊、手持計算設備、多媒體數字消費品等方案的RISC標準。公司1990年11月由Acorn、Apple和VLSI合并而成。收起

查看更多

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

公眾號『一口Linux』號主彭老師,擁有15年嵌入式開發(fā)經驗和培訓經驗。曾任職ZTE,某研究所,華清遠見教學總監(jiān)。擁有多篇網絡協(xié)議相關專利和軟件著作。精通計算機網絡、Linux系統(tǒng)編程、ARM、Linux驅動、龍芯、物聯(lián)網。原創(chuàng)內容基本從實際項目出發(fā),保持原理+實踐風格,適合Linux驅動新手入門和技術進階。