程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

Mini2440之uboot移植之实践DM9000支持(三)

继续上一节内容,不知道你有没有注意到,启动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中调用:

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;
}
View Code

上面定义了一个宏  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】

参考文章

[1]移植uboot第七步:支持DM9000

[2]mini2440_dm9000网卡驱动

[3]u-boot移植(十二)---代码修改---支持DM9000网卡

posted @ 2021-11-29 23:54  大奥特曼打小怪兽  阅读(577)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步