前面基于goni這塊板子移植的u-boot并不支持網(wǎng)絡(luò),但是我的開發(fā)板上面是支持網(wǎng)絡(luò)的,所以這里把網(wǎng)絡(luò)支持添加上。
先看一下硬件是怎樣接的:
DM9000屬于類內(nèi)存總線的設(shè)備,被映射到s5pv210的SROM區(qū)域,從圖上可以知道它接在SROM的bank1,CMD接在ADDR2。
從前面的過程分析我們知道,網(wǎng)絡(luò)相關(guān)的初始化在board_init_r階段,有兩個(gè)相關(guān)的函數(shù),下面來看一下:
/*common/board_r.c*/
/*這個(gè)函數(shù)主要的作用是設(shè)置mac地址,但u-boot提供了可以隨機(jī)
生成mac地址的功能,這里可以不用管,只不過要在u-boot配置中
打開隨機(jī)mac地址生成的相關(guān)配置宏,會(huì)面會(huì)提到*/
static int initr_ethaddr(void)
{
bd_t *bd = gd->bd;
/* kept around for legacy kernels only ... ignore the next section */
eth_env_get_enetaddr("ethaddr", bd->bi_enetaddr);
/*
省略
*/
return 0;
}
下面這個(gè)函數(shù)才是真正初始化網(wǎng)卡的:
/*common/board_r.c*/
static int initr_net(void)
{
puts("Net: ");
eth_initialize();
#if defined(CONFIG_RESET_PHY_R)
debug("Reset Ethernet PHY\n");
reset_phy();
#endif
return 0;
}
進(jìn)入eth_initialize函數(shù)看一下具體是怎樣的流程,由于我的板子上面使用的是dm9000網(wǎng)卡,這個(gè)網(wǎng)卡的驅(qū)動(dòng)程序還沒有使用新的u-boot驅(qū)動(dòng)模型,所以看的是下面這個(gè)文件的eth_initialize函數(shù):
/*net/eth_legacy.c*/
int eth_initialize(void)
{
int num_devices = 0;
eth_devices = NULL;
eth_current = NULL;
/*相當(dāng)于空函數(shù)*/
eth_common_init();
/*
* If board-specific initialization exists, call it.
* If not, call a CPU-specific one
*/
/*從這里可以知道是先看board_eth_init函數(shù)有沒有被用戶
定義,如果定義了就執(zhí)行用戶定義的,如果沒有定義就看是否
定義了cpu_eth_init函數(shù),如果定義了就執(zhí)行,沒定義的話
就提示網(wǎng)絡(luò)初始化跳過*/
if (board_eth_init != __def_eth_init) {
if (board_eth_init(gd->bd) < 0)
printf("Board Net Initialization Failed\n");
} else if (cpu_eth_init != __def_eth_init) {
if (cpu_eth_init(gd->bd) < 0)
printf("CPU Net Initialization Failed\n");
} else {
printf("Net Initialization Skipped\n");
}
if (!eth_devices) {
puts("No ethernet found.\n");
bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
} else {
/*后面都是與環(huán)境變量中設(shè)置的一些網(wǎng)絡(luò)相關(guān)的參數(shù)有關(guān),
不重要,這里跳過*/
struct eth_device *dev = eth_devices;
char *ethprime = env_get("ethprime");
bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
do {
if (dev->index)
puts(", ");
printf("%s", dev->name);
if (ethprime && strcmp(dev->name, ethprime) == 0) {
eth_current = dev;
puts(" [PRIME]");
}
if (strchr(dev->name, ' '))
puts("\nWarning: eth device name has a space!"
"\n");
eth_write_hwaddr(dev, "eth", dev->index);
dev = dev->next;
num_devices++;
} while (dev != eth_devices);
eth_current_changed();
putc('\n');
}
return num_devices;
}
這里順便分析一下為什么說用戶定義了board_eth_init函數(shù)就執(zhí)行用戶定義的呢,看一下下面的源碼:
static int __def_eth_init(bd_t *bis)
{
return -1;
}
/*主要原因就在這里,為board_eth_init函數(shù)起了一個(gè)別名函數(shù),并
且聲明成了弱屬性,所以一旦用戶定義了board_eth_init同名函數(shù),
那board_eth_init函數(shù)和__def_eth_init函數(shù)的函數(shù)地址就不一樣
了,通過判斷函數(shù)地址就能知道是否應(yīng)該調(diào)用用戶定義的那個(gè)函數(shù)*/
int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
從上面的分析可以知道最終網(wǎng)卡相關(guān)的初始化就是在board_eth_init或者cpu_eth_init函數(shù),進(jìn)入board_eth_init函數(shù)可以看到,里面初始了一塊名叫SMC911X的網(wǎng)卡:
/*board/samsung/common/board.c*/
/*此文件并沒有被編譯,因?yàn)镸akefile里面有一個(gè)宏沒有打開,這里
只是用來參考*/
int board_eth_init(bd_t *bis)
{
#ifdef CONFIG_SMC911X
/*
省略
*/
/*可以看到最終這里調(diào)用了對(duì)應(yīng)網(wǎng)卡的初始化程序*/
return smc911x_initialize(0, base_addr);
#endif
return 0;
}
下面我們就仿照著修改成dm9000的初始化程序。
先仿照其它板子添加如下的配置宏:
/*include/configs/s5p_goni.h*/
/* DM9000 */
/*根據(jù)dm9000驅(qū)動(dòng)程序同級(jí)目錄的Makefile得知,需要先定義這個(gè)
宏才能編譯dm9000相關(guān)的程序*/
#define CONFIG_DRIVER_DM9000
/*根據(jù)手冊(cè)內(nèi)存映射篇得知SROM bank1的起始地址就是這*/
#define CONFIG_DM9000_BASE 0x88000000
/*硬件上dm9000的cmd引腳接的ADDR2引腳,當(dāng)CMD引腳為0時(shí)傳送的
地址,為1時(shí)傳送的數(shù)據(jù),
那么地址:CONFIG_DM9000_BASE + (0 << 2),
數(shù)據(jù):CONFIG_DM9000_BASE + (1 << 2)*/
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 0x4)
/*我這個(gè)板子沒有外掛eeprom來存儲(chǔ)mac地址,所以要定義這個(gè)宏*/
#define CONFIG_DM9000_NO_SROM 1
/*sromc bank1*/
#define CONFIG_ENV_SROM_BANK 1
上面的內(nèi)容添加后,我們還需要修改u-boot支持網(wǎng)絡(luò),通過menuconfig勾選上如下的配置:
[*] Networking support ---->
--- Networking support
[*] Random ethaddr if unset
[ ] NetConsole support
[ ] Support IP datagram reassembly
(1468) TFTP block size (NEW)
我們單獨(dú)在board/samsung/goni/目錄下新建一個(gè)eth_init_config.c文件用于初始化配置網(wǎng)卡,并且修改該目錄Makefile讓此文件可以被編譯進(jìn)u-boot:
obj-y := goni.o onenand.o
obj-y += lowlevel_init.o
obj-y += ddr_init.o
obj-y += copy_image_to_mem.o
# 讓其依賴于dm9000
obj-$(CONFIG_DRIVER_DM9000) += eth_init_config.o
然后修改網(wǎng)卡初始化相關(guān)的代碼:
/*首先開啟sromc的時(shí)鐘*/
volatile u32 *CLK_GATE_IP1 = (volatile u32 *)(S5PC110_CLOCK_BASE + 0x464);
*CLK_GATE_IP1 |= (0x1 << 26);
配置sromc 的bw和bc寄存器:
/*配置sromc bank1*/
u32 srom_bank;
u32 srom_bw_conf;
u32 srom_bc_conf;
srom_bank = CONFIG_ENV_SROM_BANK;
/*配置bw寄存器,沒有WaitEnable和ByteEnable信號(hào),配置
數(shù)據(jù)寬度為16bit,地址模式為byte*/
srom_bw_conf = SMC_DATA16_WIDTH(srom_bank) | SMC_BYTE_ADDR_MODE(1);
/*時(shí)序配置參數(shù)參考dm9000的手冊(cè)*/
srom_bc_conf = SMC_BC_TACS(0x0) | SMC_BC_TCOS(0x0) | SMC_BC_TACC(0x2)
| SMC_BC_TCOH(0x0) | SMC_BC_TAH(0x0)
| SMC_BC_TACP(0x0) | SMC_BC_PMC(0x0);
s5p_config_sromc(srom_bank, srom_bw_conf, srom_bc_conf);
return dm9000_initialize(bis);
看一下時(shí)序設(shè)置的相關(guān)參數(shù)計(jì)算:
從上圖可以知道sromc位于PSYS域,最大時(shí)鐘頻率133MHz,換算一下每個(gè)時(shí)鐘周期就是約7.5ns。
下面再看一下dm9000的讀時(shí)序圖,關(guān)于時(shí)序的配置參數(shù),這里讀和寫配置成一樣的,以讀為例:
Tacs是片選前的地址建立時(shí)間(也就相當(dāng)于是一個(gè)保持時(shí)間),由于CS和CMD是同時(shí)的,所以設(shè)置為0;
Tcos是操作(讀/寫)使能前的片選建立時(shí)間,這個(gè)也就對(duì)應(yīng)T1,手冊(cè)給的最小值0ns,我們也設(shè)置為0;
Tacc這個(gè)是操作時(shí)間,對(duì)應(yīng)T2,手冊(cè)給的最小10ns,我們?cè)O(shè)置為2個(gè)時(shí)鐘周期也就是15ns了;
Tcoh是操作結(jié)束后片選還要保持多久,對(duì)應(yīng)T5,手冊(cè)給的最小值0ns,我們也設(shè)置為0;
Tcah是去片選之后地址線還要保持多久,由于CS和CMD是同時(shí)結(jié)束的,所以設(shè)置為0;
Tacp和PMC是頁模式才有的,設(shè)置為默認(rèn)值。
完整的初始化源碼如下:
/*board/samsung/goni/eth_init_config.c*/
#include <configs/s5p_goni.h>
#include <asm/arch/sromc.h>
#include <asm-generic/u-boot.h>
#include <common.h>
#include <netdev.h>
int board_eth_init(bd_t *bis)
{
#ifdef CONFIG_DRIVER_DM9000
/*首先開啟sromc的時(shí)鐘*/
volatile u32 *CLK_GATE_IP1 = (volatile u32 *)(S5PC110_CLOCK_BASE + 0x464);
*CLK_GATE_IP1 |= (0x1 << 26);
/*配置sromc bank1*/
u32 srom_bank;
u32 srom_bw_conf;
u32 srom_bc_conf;
srom_bank = CONFIG_ENV_SROM_BANK;
/*配置bw寄存器,沒有WaitEnable和ByteEnable信號(hào),配置
數(shù)據(jù)寬度為16bit,地址模式為byte*/
srom_bw_conf = SMC_DATA16_WIDTH(srom_bank) | SMC_BYTE_ADDR_MODE(1);
/*時(shí)序配置參數(shù)參考dm9000的手冊(cè)*/
srom_bc_conf = SMC_BC_TACS(0x0) | SMC_BC_TCOS(0x0) | SMC_BC_TACC(0x2)
| SMC_BC_TCOH(0x0) | SMC_BC_TAH(0x0)
| SMC_BC_TACP(0x0) | SMC_BC_PMC(0x0);
s5p_config_sromc(srom_bank, srom_bw_conf, srom_bc_conf);
return dm9000_initialize(bis);
#endif
return 0;
}
上面這些做完后,已經(jīng)能夠正確初始化網(wǎng)卡了,看一下啟動(dòng)信息():
可以看到網(wǎng)卡已經(jīng)初始化成功,本來想ping一下測(cè)試網(wǎng)絡(luò)通不通的,但是發(fā)現(xiàn)沒有ping命令,通過menuconfig將ping命令添加上:
Command line interface --->
[*] Network commands --->
[*] ping
添加上ping命令后,再試一次,這次能夠成功ping通網(wǎng)絡(luò)了:
歡迎掃碼關(guān)注我的微信公眾號(hào)