继续上一节内容,不知道你有没有注意到,启动u-boot后,运行有一行信息:
输出Flash信息这一部分代码是位于board_init_r阶段,执行initr_net()函数的输出结果。
我们开发板上搭载了型号为DM9000网卡芯片,这一节我们将会介绍u-boot如何支持我们的DM9000网卡,这样我们后续就可以通过网络命令下载程序。
一、DM9000介绍
关于DM9000的相关内容可以先看<Mini2440裸机开发之DM9000>这篇文章。
二、DM9000支持
2.1 宏定义
在include/configs/smdk2440.h文件有网卡相关的定义,如下:
/*
* Hardware drivers
*/
#define CONFIG_CS8900 /* we have a CS8900 on-board */
#define CONFIG_CS8900_BASE 0x19000300
#define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */
可以看到u-boot默认的网卡是cs8900。
2.2 分析启动信息
在u-boot启动时,我们打印调试信息:
Net: Initial value for argc=3
Final value for argc=3
CS8900-0
Error: CS8900-0 address not set.
我们定位到 initr_net(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在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
*/
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;
}
其中eth_devices、eth_current定义如下:
static struct eth_device *eth_devices;
struct eth_device *eth_current;
struct eth_device {
char name[16];
unsigned char enetaddr[6];
phys_addr_t iobase;
int state;
int (*init)(struct eth_device *, bd_t *);
int (*send)(struct eth_device *, void *packet, int length);
int (*recv)(struct eth_device *);
void (*halt)(struct eth_device *);
#ifdef CONFIG_MCAST_TFTP
int (*mcast)(struct eth_device *, const u8 *enetaddr, u8 set);
#endif
int (*write_hwaddr)(struct eth_device *);
struct eth_device *next;
int index;
void *priv;
};
2.3 eth_common_init(net/eth_common.c)
void eth_common_init(void)
{
bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
miiphy_init();
#endif
#ifdef CONFIG_PHYLIB
phy_init();
#endif
}
BOOTSTAGE_ID_NET_ETH_START、BOOTSTAGE_ID_COUNT为枚举类型enum bootstage_id其中的一个元素。这个枚举类型定义了u-boot在启动中的各个阶段,并使用record记录运行到每个阶段时一些信息,比如当前boot时间、id、flags等信息。
BOOTSTAGE_ID_NET_ETH_START = 64,
BOOTSTAGE_ID_COUNT = BOOTSTAGE_ID_USER + CONFIG_BOOTSTAGE_USER_COUNT,
static struct bootstage_record record[BOOTSTAGE_ID_COUNT] = { {1} };
bootstage_mark在common/bootstage.c中定义:
static inline ulong bootstage_mark(enum bootstage_id id)
{
show_boot_progress(id);
return 0;
}
show_boot_progress函数为空。
2.4 board_eth_init(board/samsung/smdk2440/smdk2440.c)
#ifdef CONFIG_CMD_NET #在include/generated/autoconf.h定义
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
return rc;
}
#endif
这里根据定义的宏CONFIG_CS8900,执行cs8900_initialize初始化CS8900网卡。
2.5 cs8900_initialize(drivers/net/cs8900.c)
int cs8900_initialize(u8 dev_num, int base_addr)
{
struct eth_device *dev;
struct cs8900_priv *priv;
dev = malloc(sizeof(*dev));
if (!dev) {
return 0;
}
memset(dev, 0, sizeof(*dev));
priv = malloc(sizeof(*priv));
if (!priv) {
free(dev);
return 0;
}
memset(priv, 0, sizeof(*priv));
priv->regs = (struct cs8900_regs *)base_addr;
dev->iobase = base_addr;
dev->priv = priv;
dev->init = cs8900_init;
dev->halt = cs8900_halt;
dev->send = cs8900_send;
dev->recv = cs8900_recv;
/* Load MAC address from EEPROM */
cs8900_get_enetaddr(dev);
sprintf(dev->name, "%s-%hu", CS8900_DRIVERNAME, dev_num);
eth_register(dev);
return 0;
}
这里我们不介绍这部分代码。我们仅仅关注drivers/net路径下的Makefile文件:
obj-$(CONFIG_CS8900) += cs8900.o
cs8900是否编译取决于宏CONFIG_CS8900。因此我们如果想支持DM9000网卡,只需要进行部分代码修改即可。
2.6 代码修改
修改include/configs/smdk2440.h,把原先的cs8900的宏取消,添加dm9000的宏:
#if 0
#define CONFIG_CS8900 /* we have a CS8900 on-board */
#define CONFIG_CS8900_BASE 0x19000300
#define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */
#else
#define CONFIG_DRIVER_DM9000
#define CONFIG_DM9000_BASE 0x20000000 //这三个是新加的
#define DM9000_IO CONFIG_DM9000_BASE //这三个是新加的
#define DM9000_DATA (CONFIG_DM9000_BASE + 4) //这三个是新加的
#endif
网卡属于内存设备,它的片选由写入的地址范围决定。
从DM9000的文档中可以知道知DM9000的cmd线(即LADDR2线)为0时,数据线做数据寄存器,为1时数据线做地址寄存器。
因为DM9000的片选是bank4,我们不难从S3C2440的文档中得知其地址是0x20000000开始的,既然LADDR2线控制着数据线的类型,我们只需改变LADDR2的值便可。所以推出DM9000的数据寄存器地址是0x20000000,地址寄存器的地址是0x20000004。
修改board/samsung/smdk2440/smdk2440.c:
#ifdef CONFIG_CMD_NET #在include/generated/autoconf.h定义
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
#endif
修改board/samsung/smdk2440/lowlevel_init.S,只需要在那里修改bank4的内容即可:
.long 0x00000740; //BANKCON4
查看drivers/net路径下的Makefile文件,有这么一行:
obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
dm9000x是否编译取决于宏CONFIG_DRIVER_DM9000。
三、编译下载运行
3.1 编译
make distclean
make smdk2440_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux- V=1
3.2 下载到NOR FALSH
我们再次下载运行,输出如下:
initcall: 0005da94
U-Boot 2016.05 (Nov 29 2021 - 23:20:03 +0800)
initcall: 0000f1b4
U-Boot code: 00000000 -> 0007F07C BSS: -> 000CE970
initcall: 000003f8
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
initcall: 0000f408
DRAM: initcall: 000011f0
initcall: 0000f358
Monitor len: 000CE970
Ram size: 04000000
Ram top: 34000000
initcall: 0000ef90
initcall: 0000f15c
TLB table from 33ff0000 to 33ff4000
initcall: 0000efa8
initcall: 0000f110
Reserving 826k for U-Boot at: 33f21000
initcall: 0000f0e4
Reserving 4160k for malloc() at: 33b11000
initcall: 0000f30c
Reserving 80 Bytes for Board Info at: 33b10fb0
initcall: 0000efb0
initcall: 0000f0b0
Reserving 168 Bytes for Global Data at: 33b10f08
initcall: 0000f038
initcall: 0000efe4
initcall: 0000efb8
initcall: 0000f3f8
initcall: 0000f284
RAM Configuration:
Bank #0: 30000000 64 MiB
DRAM: 64 MiB
initcall: 0000f01c
New Stack Pointer is: 33b10ee0
initcall: 0000f248
initcall: 0000f1e0
Relocation Offset is: 33f21000
Relocating to 33f21000, new gd at 33b10f08, sp at 33b10ee0
initcall: 33f30484
initcall: 33f3048c
initcall: 0000f670 (relocated to 33f30670)
WARNING: Caches not enabled
initcall: 0000f4a4 (relocated to 33f304a4)
initcall: 0000f4cc (relocated to 33f304cc)
initcall: 0000f654 (relocated to 33f30654)
using memory 0x33b11000-0x33f21000 for malloc()
initcall: 0000f4d4 (relocated to 33f304d4)
initcall: 0000f460 (relocated to 33f30460)
initcall: 0000f640 (relocated to 33f30640)
initcall: 00001224 (relocated to 33f22224)
dram_bank_mmu_setup: bank: 0
initcall: 0001829c (relocated to 33f3929c)
initcall: 0000f630 (relocated to 33f30630)
initcall: 0000f598 (relocated to 33f30598)
Now running in RAM - U-Boot at: 33f21000
initcall: 0000f4dc (relocated to 33f304dc)
initcall: 0000f5b4 (relocated to 33f305b4)
Flash: fwc addr 00000000 cmd f0 00f0 16bit x 16 bit
fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit
fwc addr 00005554 cmd 55 0055 16bit x 16 bit
fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit
fwc addr 00000000 cmd f0 00f0 16bit x 16 bit
JEDEC PROBE: ID 1 2249 0
Found JEDEC Flash: AMD AM29LV160DB
unlock address index 1
unlock addresses are 0x555/0x2aa
erase_region_count = 1 erase_region_size = 16384
erase_region_count = 2 erase_region_size = 8192
erase_region_count = 1 erase_region_size = 32768
erase_region_count = 31 erase_region_size = 65536
flash_protect ON: from 0x00000000 to 0x00089A33
protect on 0
protect on 1
protect on 2
protect on 3
protect on 4
protect on 5
protect on 6
protect on 7
protect on 8
protect on 9
protect on 10
protect on 11
flash_protect ON: from 0x00070000 to 0x0007FFFF
protect on 10
2 MiB
initcall: 0000f57c (relocated to 33f3057c)
NAND: board_nand_init()
end of nand_init
hwcontrol(): 0xff 0x83
hwcontrol(): 0xffffffff 0x81
dev_ready
hwcontrol(): 0x90 0x83
hwcontrol(): 0x00 0x85
hwcontrol(): 0xffffffff 0x81
dev_ready
hwcontrol(): 0x90 0x83
hwcontrol(): 0x00 0x85
hwcontrol(): 0xffffffff 0x81
dev_ready
hwcontrol(): 0x90 0x83
hwcontrol(): 0x40 0x85
hwcontrol(): 0xffffffff 0x81
dev_ready
hwcontrol(): 0xffffffff 0x80
0 MiB
initcall: 0000f54c (relocated to 33f3054c)
*** Warning - bad CRC, using default environment
Destroy Hash Table: 33f9a890 table = 00000000
Create Hash Table: N=75
INSERT: table 33f9a890, filled 1/79 rv 33b11238 ==> name="bootdelay" value="5"
INSERT: table 33f9a890, filled 2/79 rv 33b110f8 ==> name="baudrate" value="115200"
INSERT: table 33f9a890, filled 3/79 rv 33b110a8 ==> name="ipaddr" value="10.0.0.110"
INSERT: table 33f9a890, filled 4/79 rv 33b11260 ==> name="serverip" value="10.0.0.1"
INSERT: table 33f9a890, filled 5/79 rv 33b114f4 ==> name="netmask" value="255.255.255.0"
INSERT: free(data = 33b11008)
INSERT: done
initcall: 0000f474 (relocated to 33f30474)
initcall: 0001835c (relocated to 33f3935c)
initcall: 0000f53c (relocated to 33f3053c)
initcall: 00015fb0 (relocated to 33f36fb0)
In: serial
Out: serial
Err: serial
Initial value for argc=3
Final value for argc=3
Initial value for argc=3
Final value for argc=3
Initial value for argc=3
Final value for argc=3
initcall: 00000c1c (relocated to 33f21c1c)
initcall: 0000f52c (relocated to 33f3052c)
initcall: 0000f50c (relocated to 33f3050c)
initcall: 0000f4f0 (relocated to 33f304f0)
Net: Initial value for argc=3
Final value for argc=3
dm9000
Error: dm9000 address not set.
initcall: 0000f4e4 (relocated to 33f304e4)
### main_loop entered: bootdelay=5
### main_loop: bootcmd="<UNDEFINED>"
SMDK2440 #
报告有错误:dm9000 address not set,MAC地址未设置。对着打印信息查看代码,可以知道打印此条信息的代码是eth_write_hwaddr这个函数,这个函数在eth_initlize中调用:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
int eth_number)
{
unsigned char env_enetaddr[6];
int ret = 0;
eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
if (!is_zero_ethaddr(env_enetaddr)) {
if (!is_zero_ethaddr(dev->enetaddr) &&
memcmp(dev->enetaddr, env_enetaddr, 6)) {
printf("\nWarning: %s MAC addresses don't match:\n",
dev->name);
printf("Address in SROM is %pM\n",
dev->enetaddr);
printf("Address in environment is %pM\n",
env_enetaddr);
}
memcpy(dev->enetaddr, env_enetaddr, 6);
} else if (is_valid_ethaddr(dev->enetaddr)) {
eth_setenv_enetaddr_by_index(base_name, eth_number,
dev->enetaddr);
} else if (is_zero_ethaddr(dev->enetaddr)) {
#ifdef CONFIG_NET_RANDOM_ETHADDR
net_random_ethaddr(dev->enetaddr);
printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
dev->name, eth_number, dev->enetaddr);
#else
printf("\nError: %s address not set.\n",
dev->name);
return -EINVAL;
#endif
}
if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
if (!is_valid_ethaddr(dev->enetaddr)) {
printf("\nError: %s address %pM illegal value\n",
dev->name, dev->enetaddr);
return -EINVAL;
}
ret = dev->write_hwaddr(dev);
if (ret)
printf("\nWarning: %s failed to set MAC address\n",
dev->name);
}
return ret;
}
上面定义了一个宏 CONFIG_NET_RANDOM_ETHADDR ,如果定义了此宏的话就会随机分配网卡物理地址,否则就打印错误信息,我们并不需要此宏。自己定义物理地址,从代码流程看,网卡地址是直接写进环境变量中的,然后再读取环境变量,这个时候就需要看看设置环境变量的地方了。
环境变量的设置在 common/board_r.c的hash table中,initr_env函数,initr_env 会调用 env_relocate() ,env_relocate() 调用 set_default_env 函数,set_default_env 函数中有一个结构体 default_environment ,这里面定义了默认的参数,进去看看一看就知道里面全部定义的是默认的环境变量参数,其中也有网络的:
#ifdef CONFIG_ETHPRIME
"ethprime=" CONFIG_ETHPRIME "\0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" __stringify(CONFIG_IPADDR) "\0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" __stringify(CONFIG_SERVERIP) "\0"
这里面没有物理地址的定义,所以我们可以自己定义物理地址,在这里加入下面的内容:
#ifdef CONFIG_ETHADDR
"ethaddr=" __stringify(CONFIG_ETHADDR) "\0"
在 include/configs/smdk2440.h 中加入 CONFIG_ETHADDR的宏 ,这里我们可以根据自己本机上的MAC地址进行定义。
#define CONFIG_ETHADDR 08:00:3e:26:0a:5b
保存编译,下载重新运行,查看结果。
3.3 shell命令
更改mac地址:
set ethaddr 00:0c:29:d3:fe:1d
ping目标主机:
ping 192.168.0.104
如果成功了,试着用网络下载程序试试,在winPC端打开tftp服务器,文件地址写为包含内核的文件地址,在板子上设置服务器ip:
set serverip 192.168.0.104
这个ip一定要和tftp服务器上显示的ip一样,向板子输入命令等待传输:
tftp 30000000 xxx
烧写完成了,启动程序:
bootm 30000000
四、代码下载
Young / s3c2440_project[u-boot-2016.05-nor-flash】
参考文章