u-boot2020.04移植(支持网络)

前面基于goni这块板子移植的u-boot并不支持网络,但是我的开发板上面是支持网络的,所以这里把网络支持添加上。
先看一下硬件是怎样接的:

dm9000网卡硬件原理图
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时钟

/*首先开启sromc的时钟*/
	volatile u32 *CLK_GATE_IP1 = (volatile u32 *)(S5PC110_CLOCK_BASE + 0x464);

	*CLK_GATE_IP1 |= (0x1 << 26);

sromc bw寄存器
配置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);

看一下时序设置的相关参数计算:

s5pv210时钟域
从上图可以知道sromc位于PSYS域,最大时钟频率133MHz,换算一下每个时钟周期就是约7.5ns。
下面再看一下dm9000的读时序图,关于时序的配置参数,这里读和写配置成一样的,以读为例:

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:后面跟着的那一句是我加的调试信息 ):


可以看到网卡已经初始化成功,本来想ping一下测试网络通不通的,但是发现没有ping命令,通过menuconfig将ping命令添加上:

Command line interface  --->
	[*] Network commands  ---> 
		[*]   ping

添加上ping命令后,再试一次,这次能够成功ping通网络了:

欢迎扫码关注我的微信公众号
漫长当下

猜你喜欢

转载自blog.csdn.net/a1598025967/article/details/107123007