前面基于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; }上面这些做完后,已经能够正确初始化网卡了,看一下启动信息( 注 意 : N e t : 后 面 跟 着 的 那 一 句 是 我 加 的 调 试 信 息 \color{red}注意:Net:后面跟着的那一句是我加的调试信息 注意:Net:后面跟着的那一句是我加的调试信息): 可以看到网卡已经初始化成功,本来想ping一下测试网络通不通的,但是发现没有ping命令,通过menuconfig将ping命令添加上:
Command line interface ---> [*] Network commands ---> [*] ping添加上ping命令后,再试一次,这次能够成功ping通网络了: 欢迎扫码关注我的微信公众号