为Exynos4412移植2022版U-Boot——支持DM9000AEP网卡
此篇博文是在
为Exynos4412移植了2022版U-Boot-2022.01-rc4的基础上
,添加DM9000AEP网卡驱动。
一、DM9000基地址设置原理
电路连接图
从上面可以看出DM9000的地址总线就一根,它不像CS8900那样地址总线和数据总线都齐全。而这里只有一根地址线(CMD),16跟数据线,所以可以确定位宽为16位。而地址线为什么只有一根,这是DM9000决定的,看手册可以知道CPU总线只访问它的两个地址:CMD管脚为0时,数据线送的是DM9000的寄存器地址;CMD管脚为1时,数据线上送的是16位的寄存器数据。因此,对DM9000的操作至少需要两步:先写地址,再写(读)数据。他不像其他内存总线那样直接把数据写到地址传输一次就可以了,而他要传输两次。
基地址的确定
由于DM9000的引脚电平与Exynos4412的不一致,所以中间需要电平转换电路。这也是tiny4412不用DM9000而使用DM9621的原因。
dm9000 外围电路 转换电路 soc
#CS--------Xm0CSn1_LV--------Xm0CSn1-----Xm0CSn1
由电路图可以看出,把DM9000的#CS接到Exynos4412的nGCS1引脚上,表明DM9000连接到SDRAM的Bank1 memory。由Exynos4412用户手册中的Memory Map可以得到,DM9000的端口基址为0x05000000,
寄存器地址的确定
由参考博文和,我们可以得知,DM9000寄存器地址就是基地址。所以,需要宏定义
#define CONFIG_DM9000_BASE 0x05000000
#define DM9000_IO CONFIG_DM9000_BASE
数据地址的确定
dm9000 外围电路 转换电路 soc
#CMD-------Xm0ADDR2_LV-----Xm0ADDR2-----Xm0ADDR2
由电路图可以看出,DM9000的CMD引脚接到Exynos4412的ADDR2,由CMD引脚的高低电平决定地址口和数据口。那么,ADDR2为0时,访问的就是地址口,所以地址LADDR3-LADDR0 = 0000时,即0x05000000 ;ADDR2为1时,地址LADDR3~LADDR0 = 0100时,访问的就是数据口,所以数据口的地址即 0x05000004。所以,需要宏定义
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
其他引脚
INT---->XEINT7 IOR---->Xm0OEn IOW---->Xm0WEn
二、修改u-boot-2022.01-rc4源代码支持DM9000AEP
0、添加裸驱动原理分析
要把USB、nandflash、DM9000的裸驱动编译进U-Boot,一是要写入MCU相关寄存器正确的数值以初始化,二是要实现输入和输出等功能。这些都需要调用函数。那么,我们需要做的就是把这些函数找出来,然后对应开发板的硬件型号做修改,然后通过修改Makefile把该函数源文件编译进U-Boot。 对于DM9000A,我发现common/board_r.c中有board_init_r()函数,该函数中定义了数组函数指针init_sequence_r[],该数组保存的是启动U-Boot时需要执行的函数指针。其中有initr_net()函数指针,该函数会调用eth_initialize()。 eth_initialize()函数在/net/eth_legacy.c中实现。eth_legacy.c中定义了弱函数__def_eth_init(struct bd_info *bis)。该弱函数可以有两种实现,分别是cpu_eth_init(struct bd_info *bis)或者board_eth_init(struct bd_info *bis)。eth_initialize()函数会调用这三个函数。
static int __def_eth_init(struct bd_info *bis)
{
return -1;
}
int cpu_eth_init(struct bd_info *bis) __attribute__((weak, alias("__def_eth_init")));
int board_eth_init(struct bd_info *bis) __attribute__((weak, alias("__def_eth_init")));
而cpu_eth_init和board_eth_init这两种函数都没有具体定义。原因很简单,U-Boot的编写者并不知道开发板使用的是哪种网卡。也就是说,DM9000的裸驱动需要移植者在这里实现。很多Samsung的板子使用board_eth_init()函数实现。
1、修改board/samsung/cbt4412/cbt4412.h
查看文件drivers/net/Makefile:
obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
从 Makefile 得知,如果要把 DM9000A 的驱动编译进 u-boot中,就需要定义 CONFIG_DRIVER_DM9000 这个宏。 除此以外我们还需要添加一些 u-boot 的命令,比如 ping 命令用来检查网络是否通畅,tftp用来下载文件。
定义宏有2种方式,一是在configs/cbt4412_defconfig中定义宏,二是在board/samsung/cbt4412/cbt4412.h中定义宏。很悲剧的是,我在configs/cbt4412_defconfig中添加CONFIG_DRIVER_DM9000=y,但是不起作用。虽然drivers/net/Makefile中存在obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o,但是不能生成dm9000x.o目标文件。无奈,我只有在board/samsung/cbt4412/cbt4412.h定义宏CONFIG_DRIVER_DM9000,以及环境参数等宏定义,如下:
#ifdef CONFIG_CMD_NET
#define CONFIG_DRIVER_DM9000 y
#define CONFIG_DM9000_BASE 0x05000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT
#define CONFIG_DM9000_NO_SROM 1
#define CONFIG_ETHADDR 11:22:33:44:55:66
#define CONFIG_IPADDR 192.168.1.8
#define CONFIG_SERVERIP 192.168.1.7
#define CONFIG_GATEWAYIP 192.168.1.1
#define CONFIG_NETMASK 255.255.255.0
#define EXYNOS4412_SROMC_BASE 0X12570000
#endif
DM9000的基地址、寄存器地址、数据地址在上文中以及分析过了。 #define EXYNOS4412_SROMC_BASE 0X12570000定义的是SROMC的SFR寄存器地址。可以在Exynos4412用户手册中查到。
2、新建arch/arm/mach-exynos/cbt4412_init.c
假设当前路径是arch/arm/mach-exynos/,不知道为什么我不能调用drivers/net/目录下dm9000x.c中的函数,提示错误:undefined reference to `dm9000_initialize’。(如果有看客知道如何修改Makefile或者Makefile.build或者scripts/kconfig或者scripts/Makefile.spl或者其他方法,请留言不吝赐教,谢谢!在cbt4412_init.c中声明extern int dm9000_initialize(struct bd_info *bis)或许可以。)无奈之下,我只好把dm9000x.c和dm9000x.h拷贝到arch/arm/mach-exynos/路径下,并把初始化和裸驱动函数写入cbt4412_init.c。本小节的代码都写在此源文件中。
(1)头文件和函数声明
#include <asm/system.h>
#include <init.h>
#include "configs/cbt4412.h"
#include "dm9000x.h"
void dm9000aep_pre_init(void);
void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf);
int board_eth_init(struct bd_info *bis);
extern int dm9000_initialize(struct bd_info *bis);
(2)SROM 控制器读时序和 DM9000A 的读时序参数
SROM 控制器读时序和 DM9000A 的读时序主要通过SROM_BCn控制寄存器设置。具体分析过程可以参考这个文献的3. SROM_BC1部分。
#ifdef CONFIG_CMD_NET
#define DM9000_Tacs (0x1)
#define DM9000_Tcos (0x1)
#define DM9000_Tacc (0x5)
#define DM9000_Tcoh (0x1)
#define DM9000_Tah (0xC)
#define DM9000_Tacp (0x9)
#define DM9000_PMC (0x1)
#endif
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_TARGET_CBT4412
struct exynos_sromc {
unsigned int bw;
unsigned int bc[6];
};
DECLARE_GLOBAL_DATA_PTR的宏定义在arch/arm/include/asm/global_data.h中。
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
gd_t的定义在include/asm-generic/global_data.h,是名为global_data的结构体。
typedef struct global_data gd_t;
(3)初始化SROM
控制SROM本质上是配置SROM的寄存器。具体分析过程可以参考这个文献的3. SROM_BC1部分。 这里的关键是定义了int board_eth_init(struct bd_info *bis)函数。那么,board_r函数在执行的过程中会调用该函数,也就实现了DM9000的初始化。
#ifdef CONFIG_CMD_NET
int board_eth_init(struct bd_info *bis)
{
int rc = 0;
#ifdef CONFIG_DRIVER_DM9000
dm9000aep_pre_init();
rc = dm9000_initialize(bis);
printascii("dm9000_initialize(bis) was runned\r\n");
#endif
#if defined(CONFIG_RESET_PHY_R)
printascii("Reset Ethernet PHY\r\n");
reset_phy();
#endif
return rc;
}
#endif
static void dm9000aep_pre_init(void)
{
unsigned char smc_bank_num = 1;
unsigned int smc_bw_conf=0;
unsigned int smc_bc_conf=0;
writel(0x00220020, 0x11000000 + 0x120);
writel(0x00002222, 0x11000000 + 0x140);
writel(0x22222222, 0x11000000 + 0x180);
writel(0x0000FFFF, 0x11000000 + 0x188);
writel(0x22222222, 0x11000000 + 0x1C0);
writel(0x0000FFFF, 0x11000000 + 0x1C8);
writel(0x22222222, 0x11000000 + 0x1E0);
writel(0x0000FFFF, 0x11000000 + 0x1E8);
smc_bw_conf &= ~(0xf<<4);
smc_bw_conf |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
smc_bc_conf = ((DM9000_Tacs << 28)
| (DM9000_Tcos << 24)
| (DM9000_Tacc << 16)
| (DM9000_Tcoh << 12)
| (DM9000_Tah << 8)
| (DM9000_Tacp << 4)
| (DM9000_PMC));
exynos_config_sromc(smc_bank_num,smc_bw_conf,smc_bc_conf);
}
static void exynos_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
{
unsigned int tmp;
struct exynos_sromc *srom = (struct exynos_sromc *)(EXYNOS4412_SROMC_BASE);
tmp = srom->bw;
tmp &= ~(0xF << (srom_bank * 4));
tmp |= srom_bw_conf;
srom->bw = tmp;
srom->bc[srom_bank] = srom_bc_conf;
}
也有博文把裸驱动写入board/samsung/cbt4412/cbt4412.c,同样存在不能调用其他路径下函数的问题。如有解决者,请留言告知不吝赐教!谢谢!
3、修改arch/arm/mach-exynos/lowlevel_init.c
在arch/arm/mach-exynos/lowlevel_init.c中调用dm9000aep_pre_init()。此处也可以不修改。这样做只是为了提醒看官,此处修改并不能把网卡的驱动编译进U-Boot,因为此时还只是为U-Boot做准备而没有跳转到U-Boot。
@@ -223,6 +224,7 @@ int do_lowlevel_init(void)
#ifdef CONFIG_TARGET_CBT4412
exynos_pinmux_config(PERIPH_ID_UART0, PINMUX_FLAG_NONE);
+ dm9000aep_pre_init();
#else
exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE);
#endif
基本的初始化代码完成了,但是还要修改和编译相关的部分代码。
4、修改./Makefile
为了制定交叉编译工具链版本,修改顶层目录下的Makefile。在适当位置添加 ARCH ?=arm 以及CROSS_COMPILE ?= arm-none-linux-gnueabihf-
@@ -6,6 +6,7 @@ SUBLEVEL =
EXTRAVERSION = -rc4
NAME =
+ARCH ?= arm
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
@@ -274,6 +275,13 @@ ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
+ifeq (arm,$(ARCH))
+CROSS_COMPILE ?= arm-none-linux-gnueabihf-
+endif
+
+export ARCH CROSS_COMPILE
+
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
5、修改arch/arm/mach-exynos/Makefile
在arch/arm/mach-exynos/Makefile添加目标文件cbt4412_init.o和dm9000x.o。s5p_gpio.c也是从drivers/gpio/s5p_gpio.c复制到arch/arm/mach-exynos/的。
@@ -8,7 +8,10 @@ obj-$(CONFIG_CPU_V7A) += clock.o pinmux.o power.o system.o
obj-$(CONFIG_ARM64) += mmu-arm64.o
obj-$(CONFIG_EXYNOS5420) += sec_boot.o
+ifdef CONFIG_TARGET_CBT4412
obj-$(CONFIG_S5P) += s5p_gpio.o
+obj-y += cbt4412_init.o
+obj-y += dm9000x.o
+endif
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_EXYNOS5) += clock_init_exynos5.o
6、修改arch/arm/Kconfig
DM_ETH与DM9000x.c驱动是不兼容的,所以在arch/arm/Kconfig中删除此配置选项。如果select DM_ETH,那么编译的就是/net/eth-uclass.c,而不是/net/eth-legacy.c。
@@ -679,7 +679,6 @@ config ARCH_EXYNOS
select DM
select DM_GPIO
select DM_I2C
- select DM_ETH
select DM_KEYBOARD
select DM_SERIAL
select DM_SPI
三、编译、烧写、运行
插入SD卡。 由于修改了顶层Makefile文件,所以在终端中直接运行下列命令,就可以将新的U-Boot烧写到SD卡中。 烧写的原理和步骤可参考博文的第八部分。
make distclean && make cbt4412_defconfig && make
cd sd_fuse/tiny4412/
sudo ./sd_fusing.sh /dev/sdb
U-Boot启动结果如下:
U-Boot 2022.01-rc4 (Jan 11 2022 - 09:45:22 +0800) for CBT4412
CPU: Exynos4412 @ 1.4 GHz
Model: CBt4412 board based on Exynos4412
DRAM: 1 GiB
WARNING: Caches not enabled
MMC:
Loading Environment from MMC... MMC Device 2 not found
*** Warning - No MMC card found, using default environment
Net: dm9000
Error: dm9000 address not set.
Hit any key to stop autoboot: 0
No MMC device available
Couldn't find partition mmc 0
Can't set block device
Wrong Image Format for bootm command
ERROR: can't get kernel image!
CBT4412
提示
Net: dm9000 Error: dm9000 address not set.
表明以上修改是成功的。后续继续修改初始化代码实现网卡功能。
|