前面基于goni這塊板子移植的u-boot并不支持網絡,但是我的開發板上面是支持網絡的,所以這里把網絡支持添加上。
先看一下硬件是怎樣接的:
DM9000屬于類內存總線的設備,被映射到s5pv210的SROM區域,從圖上可以知道它接在SROM的bank1,CMD接在ADDR2。
從前面的過程分析我們知道,網絡相關的初始化在board_init_r階段,有兩個相關的函數,下面來看一下:
/*common/board_r.c*/
/*這個函數主要的作用是設置mac地址,但u-boot提供了可以隨機
生成mac地址的功能,這里可以不用管,只不過要在u-boot配置中
打開隨機mac地址生成的相關配置宏,會面會提到*/
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;
}
下面這個函數才是真正初始化網卡的:
/*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;
}
進入eth_initialize函數看一下具體是怎樣的流程,由于我的板子上面使用的是dm9000網卡,這個網卡的驅動程序還沒有使用新的u-boot驅動模型,所以看的是下面這個文件的eth_initialize函數:
/*net/eth_legacy.c*/
int eth_initialize(void)
{
int num_devices = 0;
eth_devices = NULL;
eth_current = NULL;
/*相當于空函數*/
eth_common_init();
/*
* If board-specific initialization exists, call it.
* If not, call a CPU-specific one
*/
/*從這里可以知道是先看board_eth_init函數有沒有被用戶
定義,如果定義了就執行用戶定義的,如果沒有定義就看是否
定義了cpu_eth_init函數,如果定義了就執行,沒定義的話
就提示網絡初始化跳過*/
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 {
/*后面都是與環境變量中設置的一些網絡相關的參數有關,
不重要,這里跳過*/
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函數就執行用戶定義的呢,看一下下面的源碼:
static int __def_eth_init(bd_t *bis)
{
return -1;
}
/*主要原因就在這里,為board_eth_init函數起了一個別名函數,并
且聲明成了弱屬性,所以一旦用戶定義了board_eth_init同名函數,
那board_eth_init函數和__def_eth_init函數的函數地址就不一樣
了,通過判斷函數地址就能知道是否應該調用用戶定義的那個函數*/
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")));
從上面的分析可以知道最終網卡相關的初始化就是在board_eth_init或者cpu_eth_init函數,進入board_eth_init函數可以看到,里面初始了一塊名叫SMC911X的網卡:
/*board/samsung/common/board.c*/
/*此文件并沒有被編譯,因為Makefile里面有一個宏沒有打開,這里
只是用來參考*/
int board_eth_init(bd_t *bis)
{
#ifdef CONFIG_SMC911X
/*
省略
*/
/*可以看到最終這里調用了對應網卡的初始化程序*/
return smc911x_initialize(0, base_addr);
#endif
return 0;
}
下面我們就仿照著修改成dm9000的初始化程序。
先仿照其它板子添加如下的配置宏:
/*include/configs/s5p_goni.h*/
/* DM9000 */
/*根據dm9000驅動程序同級目錄的Makefile得知,需要先定義這個
宏才能編譯dm9000相關的程序*/
#define CONFIG_DRIVER_DM9000
/*根據手冊內存映射篇得知SROM bank1的起始地址就是這*/
#define CONFIG_DM9000_BASE 0x88000000
/*硬件上dm9000的cmd引腳接的ADDR2引腳,當CMD引腳為0時傳送的
地址,為1時傳送的數據,
那么地址:CONFIG_DM9000_BASE + (0 << 2),
數據:CONFIG_DM9000_BASE + (1 << 2)*/
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 0x4)
/*我這個板子沒有外掛eeprom來存儲mac地址,所以要定義這個宏*/
#define CONFIG_DM9000_NO_SROM 1
/*sromc bank1*/
#define CONFIG_ENV_SROM_BANK 1
上面的內容添加后,我們還需要修改u-boot支持網絡,通過menuconfig勾選上如下的配置:
[*] Networking support ---->
--- Networking support
[*] Random ethaddr if unset
[ ] NetConsole support
[ ] Support IP datagram reassembly
(1468) TFTP block size (NEW)
我們單獨在board/samsung/goni/目錄下新建一個eth_init_config.c文件用于初始化配置網卡,并且修改該目錄Makefile讓此文件可以被編譯進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
然后修改網卡初始化相關的代碼:
/*首先開啟sromc的時鐘*/
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信號,配置
數據寬度為16bit,地址模式為byte*/
srom_bw_conf = SMC_DATA16_WIDTH(srom_bank) | SMC_BYTE_ADDR_MODE(1);
/*時序配置參數參考dm9000的手冊*/
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);
看一下時序設置的相關參數計算:
從上圖可以知道sromc位于PSYS域,最大時鐘頻率133MHz,換算一下每個時鐘周期就是約7.5ns。
下面再看一下dm9000的讀時序圖,關于時序的配置參數,這里讀和寫配置成一樣的,以讀為例:
Tacs是片選前的地址建立時間(也就相當于是一個保持時間),由于CS和CMD是同時的,所以設置為0;
Tcos是操作(讀/寫)使能前的片選建立時間,這個也就對應T1,手冊給的最小值0ns,我們也設置為0;
Tacc這個是操作時間,對應T2,手冊給的最小10ns,我們設置為2個時鐘周期也就是15ns了;
Tcoh是操作結束后片選還要保持多久,對應T5,手冊給的最小值0ns,我們也設置為0;
Tcah是去片選之后地址線還要保持多久,由于CS和CMD是同時結束的,所以設置為0;
Tacp和PMC是頁模式才有的,設置為默認值。
完整的初始化源碼如下:
/*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的時鐘*/
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信號,配置
數據寬度為16bit,地址模式為byte*/
srom_bw_conf = SMC_DATA16_WIDTH(srom_bank) | SMC_BYTE_ADDR_MODE(1);
/*時序配置參數參考dm9000的手冊*/
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;
}
上面這些做完后,已經能夠正確初始化網卡了,看一下啟動信息():
可以看到網卡已經初始化成功,本來想ping一下測試網絡通不通的,但是發現沒有ping命令,通過menuconfig將ping命令添加上:
Command line interface --->
[*] Network commands --->
[*] ping
添加上ping命令后,再試一次,這次能夠成功ping通網絡了:
歡迎掃碼關注我的微信公眾號