u-boot 移植 --->5、友善之臂Tiny210底板王网卡驱动移植
网卡芯片的工作原理
DM9000AE具有以下主要性能:
①48管脚的LQFP封装,管脚少体积小;
②支持8/16位数据总线;
③适用于10Base-T和100Base-T,10/100M自适应,适应不同的网络速度要求
④内置16KB的SRAM,用于收发缓冲,降低对主处理器的速度要求;
⑤支持IP /TCP /UDP加速,减轻了CPU负担,提高网络速度;⑥支持Back pressure半双工流量控制,与IEEE802.3u兼容,支持IEEE802.3x全双工流量控制;
⑦20ns响应时间,2.5V/3.3V低功耗。下图是DM9000网卡芯片常用在嵌入式网络系统上的结构框图。
数据支持8位和16位,不同模式下的PIN脚定义是不相同额,就拿16bit数据位宽来说。如下
其中RX和TX和LED是到隔离变压器的,X1和2为工作时钟晶振接入。CS为片选脚,默认低电平有效,可通过内部EEPROM配置。SD为数据脚,INT为接受到数据并校验正确后的中断信号,一般接到处理器的中断脚上。EE打头的脚是用来操作内部eeprom的。其中32脚为CMD脚,他是控制数据交换的主要脚,SD脚负责数据传输。通过手册可以知道CPU总线只访问它的两个地址,CMD管脚为0时,数据线送的是DM9000的寄存器地址,CMD管脚为1时,数据线上送的是16位的寄存器数据,所以对DM9000的操作至少需要两步:先写地址,再写(读)数据。这样CPU和他的数据交换原理就清楚了。下面是我的硬件的原理图
这里看到他将这个芯片接在了SOC内存接口0上,通过三星的手册知道芯片的这个接口可以支持访问外部8/16bit的 norflash、SRAM等,共支持6个bank。由片选脚的编号可以知道网卡被安排在bank1上,所以上面芯片内存map图,知道地址为88000000~8fffffff。中断脚接在了芯片的外部中断7脚上。知道这些信息就可以修改Uboot中的代码以使我们的板子支持这个网卡芯片了。
代码修改
先直接通过menuconfig使能Networking support 并选择DM9000网卡驱动,烧写后启动提示(这个图是我当时调试参考的博主的博客中的因为自己当时调试忘记截图了。。。)
根据提示信息找打打印这一段信息的代码位置在board_r.c这个文件中,可见我通过menuconfig启用网络实际上就是在配置文件中增加CONFIG_CMD_NET 这个宏
#ifdef CONFIG_CMD_NET 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; } #endif
继续向下看里面的各个函数内部的实现
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 */ 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 = getenv("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; }
这里主要是u-boot的为了增加他的通用性和兼容所以内部机制复杂了一些,首先判断板级和CPU级的网络配置接口是否定义,具体的处理就是如果板级和cpu级的配置接口都在则优先执行board的网络初始化,通过打印内容可以很清楚的看到实际上我们现在是都未定义所以执行最后的else分支打印了上面的log信息。所以我们需要自己实现两者其中之一,移植大法就是参考已有的,通过在u-boot代码中找到我们自己的硬件比较相似的现成驱动来参考,全局搜索最后选择使用三星的smdk100他使用了网卡所以可以借鉴他的实现
int board_eth_init(bd_t *bis) { int rc = 0; #ifdef CONFIG_SMC911X rc = smc911x_initialize(0, CONFIG_SMC911X_BASE); #endif return rc; }
发现他使用的网卡是SMC911x系列的和我们的并不相同,还是找参考找和我手上相同的网卡的初始化驱动代码来参考,最后找到u-boot内部自带自带的dm9000x.c这个驱动文件,他就是实现好了网卡的初始化而且这个驱动封装的非常容易移植,通用的的部分内部实现不通用的部分留给移植实现如下
int dm9000_initialize(bd_t *bis) { struct eth_device *dev = &(dm9000_info.netdev); /* Load MAC address from EEPROM */ dm9000_get_enetaddr(dev); dev->init = dm9000_init; dev->halt = dm9000_halt; dev->send = dm9000_send; dev->recv = dm9000_rx; strcpy(dev->name, "dm9000"); eth_register(dev); return 0; }
这里我在我的板级文件s5pc210.c的文件中增加这个接口内容如下同时还需要通过menuconfig使能dm9000x的驱动:
int board_eth_init(bd_t *bis) { int rc = 0; #ifdef CONFIG_DRIVER_DM9000 rc = dm9000_initialize(bis); #endif return rc; }
接下来还要继续实现一下具体的不通用的部分的部分,因为SMC911x和DM9000的工作原理类似就如同我前面介绍的一样,他们都是作为ROM实现接在CPU存储器控制总线上的所以当然也需要初始化CPU的存储器控制接口,这也是参考smdk100进行修改最后如下
static void dm9000_pre_init(void) { u32 smc_bw_conf, smc_bc_conf; /* gpio configuration GPK0CON */ gpio_cfg_pin(S5PC110_GPIO_MP010 + CONFIG_ENV_SROM_BANK, S5P_GPIO_FUNC(2)); /* Ethernet needs bus width of 16 bits */ smc_bw_conf = SMC_DATA16_WIDTH(CONFIG_ENV_SROM_BANK) | SMC_BYTE_ADDR_MODE(CONFIG_ENV_SROM_BANK)| SMC_WAIT_ENABLE(CONFIG_ENV_SROM_BANK) | SMC_BYTE_ENABLE(CONFIG_ENV_SROM_BANK); smc_bc_conf = SMC_BC_TACS(0x0) | SMC_BC_TCOS(0x1) | SMC_BC_TACC(0x3) | SMC_BC_TCOH(0x1) | SMC_BC_TAH(0x0) | SMC_BC_TACP(0x0) | SMC_BC_PMC(0x0); /* Select and configure the SROMC bank */ s5p_config_sromc(CONFIG_ENV_SROM_BANK, smc_bw_conf, smc_bc_conf); }
其中主要修改索对应的BANK,IO接口和硬件接口时序时间控制参数相关的内容,然后在板级初始化文件中调用完成硬件的提前初始化,从而保证后面DM9000网卡驱动工作时的硬件基础
int board_init(void) { #ifdef CONFIG_CMD_NET dm9000_pre_init(); #endif /* Set Initial global variables */ gd->bd->bi_arch_number = MACH_TYPE_S5PV210; gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; return 0; }
其实当时调试的时候我还在好奇dm9000的驱动是怎么和具体额硬件操作关联起来的,最后再看前面原理的时候就焕然大悟了,因为DM9000是作为ROM挂接在CPU的存储器总线上的,自然由CPU内部总线直接访问到网卡,而需要知道的就是网卡芯片作为ROM到底映射在了内存接口总线上的哪一个地址空间这是dm9000网卡的内部寄存器相关的就直接映射到CPU的存储总线地址空间了,这地址映射这一部分就是在dm9000_pre_init()处理接口中完成的这一部分涉及具体CPU的存储总线接口的初始化和工作原理,所以需要先了解这部分内容才是理解这部分的关键。
然后重新编译烧录SD卡启动如下(图片同上唯一的不同是我当时为出现地址为设置的提示,因为在板级文件中我指定了MAC地址)
u-boot的调试过程网络驱动的移植十分重要,他可以大大的简化后面的调试移植内核的工作,要不然就是需要修改编译一次内核就的插拔SD卡拷贝新的内核镜像文件到SD在重新启动,移植完了网络驱动在通过配置u-boot就支持ftp从而配合交叉编译的机器搭建tfp服务器后就很方便的可以从网络加载内核镜像进行调试移植内核和驱动了。这篇博客写作中间简短了一段时间所以效果不太好,但是总是要做完所以这里就强行结束了。不过在当时移植的时候是遇到了不少坑的,以后长点心及时记录不然 坑复坑。这里需要感谢一个博主是他的博客给力我很大的帮助从移植u-boot和Linux内核即部分驱动的开发。他的主页链接:https://blog.csdn.net/qq_16777851,感谢!
参考:https://blog.csdn.net/qq_16777851/article/details/81843354