ksz9031的介绍可以回归一下datasheet的介绍,一款很优秀的千兆以太网;
【Datasheet】PHY KSZ9031千兆网络芯片解读
系统版本:Ubuntu18.04-64
编译器版本:gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)
uboot版本:2018.07 -linux4sam_6.0
板子型号:at91sama5d3x-xplained
MCU型号:sama5d36
关注微信公众号,回复“ksz9031驱动”,免费下载ksz9031的驱动源代码。
硬件参考图如下:
1、uboot官网开发板默认配置文件路径
//默认配置路径
u-boot-at91-u-boot-2018.07-at91/configs/sama5d3_xplained_nandflash_defconfig
//指定交叉编译器
$ export CROSS_COMPILE=arm-linux-gnueabi-
//先编译默认配置
$ make sama5d3_xplained_nandflash_defconfig
$ make
2、公共信息配置
//配置文件
include/configs/at91-sama5_common.h
//从Nand Flash启动
#ifdef CONFIG_NAND_BOOT
/* u-boot env in nand flash */
#define CONFIG_ENV_OFFSET 0x140000
#define CONFIG_ENV_OFFSET_REDUND 0x100000
#define CONFIG_ENV_SIZE 0x20000
//从Nand Flash读取内核到内存中,跳转到内核
#if 0 /*Jack*/
#define CONFIG_BOOTCOMMAND "nand read 0x21000000 0x180000 0x80000;" \
"nand read 0x22000000 0x200000 0x600000;" \
"bootz 0x22000000 - 0x21000000"
//修改为通过网络下载内核和设备树文件到内存,同样可以启动跳转到内核,方便开发调试内核使用
#else
#define CONFIG_BOOTCOMMAND "tftp 0x21000000 at91-sama5d3_xplained.dtb;" \
"tftp 0x22000000 zImage;" \
"bootz 0x22000000 - 0x21000000"
#endif
3、开发板配置信息
//开发板配置信息文件
include/configs/sama5d3_xplained.h
//DDR地址
//NAND Flash地址
//NAND FLASH的硬件属性,如果更改了芯片料,这部分要仔细核对参数PMECC
/* SDRAM */
#define CONFIG_NR_DRAM_BANKS 1
#define CONFIG_SYS_SDRAM_BASE 0x20000000
#define CONFIG_SYS_SDRAM_SIZE 0x10000000
#ifdef CONFIG_SPL_BUILD
#define CONFIG_SYS_INIT_SP_ADDR 0x318000
#else
#define CONFIG_SYS_INIT_SP_ADDR
(CONFIG_SYS_SDRAM_BASE + 16 * 1024 - GENERATED_GBL_DATA_SIZE)
#endif
/* NAND flash /
#ifdef CONFIG_CMD_NAND
#define CONFIG_NAND_ATMEL
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x60000000
/ our ALE is AD21 /
#define CONFIG_SYS_NAND_MASK_ALE (1 << 21)
/ our CLE is AD22 */
#define CONFIG_SYS_NAND_MASK_CLE (1 << 22)
#define CONFIG_SYS_NAND_ONFI_DETECTION
#define CONFIG_MTD_DEVICE
#define CONFIG_MTD_PARTITIONS
#endif
/* PMECC & PMERRLOC */
#define CONFIG_ATMEL_NAND_HWECC
#define CONFIG_ATMEL_NAND_HW_PMECC
#define CONFIG_PMECC_CAP 4
#define CONFIG_PMECC_SECTOR_SIZE 512
/* USB */
#ifdef CONFIG_CMD_USB
#define CONFIG_USB_ATMEL
#define CONFIG_USB_ATMEL_CLK_SEL_UPLL
#define CONFIG_USB_OHCI_NEW
#define CONFIG_SYS_USB_OHCI_CPU_INIT
#define CONFIG_SYS_USB_OHCI_REGS_BASE 0x00600000
#define CONFIG_SYS_USB_OHCI_SLOT_NAME “SAMA5D3 Xplained”
#define CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS 2
#endif
#define CONFIG_SYS_LOAD_ADDR 0x22000000 /* load address */
/* SPL */
#define CONFIG_SPL_TEXT_BASE 0x300000
#define CONFIG_SPL_MAX_SIZE 0x18000
#define CONFIG_SPL_BSS_START_ADDR 0x20000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
#define CONFIG_SYS_SPL_MALLOC_START 0x20080000
#define CONFIG_SYS_SPL_MALLOC_SIZE 0x80000
#define CONFIG_SYS_MONITOR_LEN (512 << 10)
#ifdef CONFIG_SD_BOOT
#define CONFIG_SYS_MMCSD_FS_BOOT_PARTITION 1
#define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME “u-boot.img”
#elif CONFIG_NAND_BOOT
#define CONFIG_SPL_NAND_DRIVERS
#define CONFIG_SPL_NAND_BASE
#endif
#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x40000
#define CONFIG_SYS_NAND_5_ADDR_CYCLE
#define CONFIG_SYS_NAND_PAGE_SIZE 0x800
#define CONFIG_SYS_NAND_PAGE_COUNT 64
#define CONFIG_SYS_NAND_OOBSIZE 128
#define CONFIG_SYS_NAND_BLOCK_SIZE 0x20000
#define CONFIG_SYS_NAND_BAD_BLOCK_POS 0x0
#define CONFIG_SPL_GENERATE_ATMEL_PMECC_HEADER
#endif
4、功能裁剪文件
configs/sama5d3_xplained_nandflash_defconfig
//功能的裁剪大部分都在这个配置里面,把需要的功能赋值为"y",
//不用的功能,“# CONFIG_DISPLAY_BOARDINFO is not set”
CONFIG_ARM=y
CONFIG_ARCH_AT91=y
CONFIG_SYS_TEXT_BASE=0x26f00000
CONFIG_TARGET_SAMA5D3_XPLAINED=y
CONFIG_SPL_GPIO_SUPPORT=y
CONFIG_SPL_LIBCOMMON_SUPPORT=y
CONFIG_SPL_LIBGENERIC_SUPPORT=y
CONFIG_SYS_MALLOC_F_LEN=0x2000
CONFIG_SPL_SERIAL_SUPPORT=y
CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
CONFIG_SPL=y
CONFIG_DEBUG_UART_BOARD_INIT=y
CONFIG_DEBUG_UART_BASE=0xffffee00
CONFIG_DEBUG_UART_CLOCK=132000000
CONFIG_DEFAULT_DEVICE_TREE="at91-sama5d3_xplained"
CONFIG_DEBUG_UART=y
CONFIG_ENV_VARS_UBOOT_CONFIG=y //支持环境变量配置
CONFIG_FIT=y
CONFIG_NAND_BOOT=y
CONFIG_BOOTDELAY=3 //启动延时3S
CONFIG_USE_BOOTARGS=y
//启动传参数,我更改了一下,主要功能是添加了一个分区
#CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk mtdparts=atmel_nand:256k(bootstrap)ro,768k(uboot)ro,256K(env_redundant),256k(env),512k(dtb),6M(kernel)ro,-(rootfs) rootfstype=ubifs ubi.mtd=6 root=ubi0:rootfs"
CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk mtdparts=atmel_nand:256k(bootstrap)ro,768k(uboot)ro,256K(env_redundant),256k(env),512k(dtb),6M(kernel)ro,160M(rootfs),-(app) rootfstype=ubifs ubi.mtd=7 root=ubi0:rootfs"
# CONFIG_DISPLAY_BOARDINFO is not set
CONFIG_SPL_NAND_SUPPORT=y
CONFIG_HUSH_PARSER=y
CONFIG_CMD_BOOTZ=y
# CONFIG_CMD_FLASH is not set
# CONFIG_CMD_LOADS is not set
CONFIG_CMD_MMC=y //EMMC功能,SDIO接口,和SD卡是一样的;
CONFIG_CMD_NAND=y
CONFIG_CMD_NAND_TRIMFFS=y //这是一种校验方式,烧录文件系统ubi的时候会用到
CONFIG_CMD_USB=y
CONFIG_CMD_DHCP=y
CONFIG_CMD_MII=y //调试网络驱动的MAC控制器接口
CONFIG_CMD_PING=y //支持ping命令,方便验证
CONFIG_CMD_EXT4=y
CONFIG_CMD_EXT4_WRITE=y
CONFIG_CMD_FAT=y
CONFIG_CMD_UBI=y
CONFIG_OF_CONTROL=y
CONFIG_SPL_OF_CONTROL=y
CONFIG_OF_SPL_REMOVE_PROPS="interrupts interrupt-parent dmas dma-names"
CONFIG_ENV_IS_IN_NAND=y
CONFIG_DM=y
CONFIG_SPL_DM=y
CONFIG_SPL_DM_SEQ_ALIAS=y
CONFIG_CLK=y
CONFIG_SPL_CLK=y
CONFIG_CLK_AT91=y
CONFIG_AT91_UTMI=y
CONFIG_AT91_H32MX=y
CONFIG_DM_GPIO=y
CONFIG_AT91_GPIO=y
CONFIG_DM_MMC=y
CONFIG_GENERIC_ATMEL_MCI=y
CONFIG_DM_ETH=y //支持网络设备树,GPIO的关键配置发现找不到代码,因为是从设备树进行GPIO配置的,相当于参数带入到uboot中,很高级很好玩;
CONFIG_MACB=y //网络驱动的控制器
CONFIG_PINCTRL=y
CONFIG_SPL_PINCTRL=y
CONFIG_PINCTRL_AT91=y
CONFIG_DM_SERIAL=y
CONFIG_DEBUG_UART_ATMEL=y
CONFIG_DEBUG_UART_ANNOUNCE=y
CONFIG_ATMEL_USART=y
CONFIG_TIMER=y
CONFIG_SPL_TIMER=y
CONFIG_ATMEL_PIT_TIMER=y
CONFIG_USB=y
CONFIG_DM_USB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_STORAGE=y
CONFIG_W1=y
CONFIG_W1_GPIO=y
CONFIG_W1_EEPROM=y
CONFIG_W1_EEPROM_DS24XXX=y
CONFIG_FAT_WRITE=y
CONFIG_OF_LIBFDT_OVERLAY=y
5、设备树所在路径:arch/arm/dts/
设备树管脚定义
修改:at91-sama5d3_xplained.dts
macb0: ethernet@f0028000 {
phy-mode = “rgmii”;
#address-cells = <1>;
#size-cells = <0>;
status = “okay”;
ethernet-phy@0 {
reg = <0x0>;
};
};
修改为:
macb0: ethernet@f0028000 {
phy-mode = "mii"; //更改PHY的接口方式
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
ethernet-phy@7 {
reg = <0x7>;
};
};
设备树文件规则很多地方和C很像,比如都可以使用include包含头文件;
6、初始化入口
//在结构体中顺序执行函数
static init_fnc_t init_sequence_r[] = {
/* PPC has a udelay(20) here dating from 2002. Why? */
##ifdef CONFIG_CMD_NET
initr_ethaddr,
#endif
#ifdef CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net, //初始化网络设备,uboot打印读取到的网卡信息
endif
7、网卡的初始化流程,是如何找到对应的网卡芯片的呢,这个是我们今天要聊的重点,我们使用的芯片为micrel公司的ksz9031;
首先,可以进入到net路径下面查看对应的Makefile文件,编译总是需要从make入手的;
7.1 网卡初始化代码讲解
#ifdef CONFIG_CMD_NET
static int initr_net(void)
{
puts("Netjack: ");
eth_initialize(); //初始化网卡入口
#if defined(CONFIG_RESET_PHY_R)
debug(“Reset Ethernet PHY\n”);
reset_phy();
#endif
return 0;
}
#endif
7.2以上就算从main函数,跳转到ksz9031的流程,这还只是万里长征的第一步,真正的技术才刚刚开始;
drivers/net/phy/micrel_ksz90x1.c
drivers/net/phy/phy.c
static struct phy_driver ksz9031_driver = {
.name = "Micrel ksz9031",
.uid = 0x221620, //这个UID非常重要,如果你留心观察了,肯定会发现和
Datasheet中的UID是一致的,uboot是靠着这个UID自动匹配PHY驱动的,可以适配很多种厂家;
.mask = 0xfffff0,
.features = PHY_GBIT_FEATURES,
.config = &ksz9031_config, //具体方法实现函数,初始化配置,基本是修改这块的东西
.startup = &ksz90xx_startup, //启动
.shutdown = &genphy_shutdown, //芯片休眠
.writeext = &ksz9031_phy_extwrite,//发送数据
.readext = &ksz9031_phy_extread, //接收数据
};
//把驱动注册到程序中
int phy_micrel_ksz90x1_init(void)
{
printf(“phy_micrel_ksz90x1_init ksz9031_driver\n”);
phy_register(&ksz9021_driver);
phy_register(&ksz9031_driver);
return 0;
}
//初始化配置,需要把寄存器的值打印出来查看状态。
static int ksz9031_config(struct phy_device *phydev)
{
int ret;
unsigned bmcr;
printf("ksz9031_config name=%s,uid=0x%x\n",phydev->drv->name,phydev->phy_id);
if (ret)
return ret;
ret = ksz9031_center_flp_timing(phydev);
if (ret)
return ret;
/* add an option to disable the gigabit feature of this PHY */
if (env_get("disable_giga")) {
unsigned features;
unsigned bmcr;
printf( "env_get disable_giga \n");
/* disable speed 1000 in features supported by the PHY */
features = phydev->drv->features;
features &= ~(SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full);
phydev->advertising = phydev->supported = features;
/* disable speed 1000 in Basic Control Register */
bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
bmcr &= ~(1 << 6);
bmcr |= (1 << 14);
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr);
/* disable speed 1000 in 1000Base-T Control Register */
phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, 0);
/* start autoneg */
genphy_config_aneg(phydev);
genphy_restart_aneg(phydev);
return 0;
}
#if 0
/* disable speed 1000 in Basic Control Register */
bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
printf("ksz9031_config MII_BMCR=0x%x\n",bmcr);
bmcr &= ~(1 << 6);
bmcr |= (1 << 14);
bmcr = 0x4140;
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr);
#endif
printf(" ksz9031_config->genphy_config \nc");
return genphy_config(phydev);
}
控制寄存器 MII_BMCR 0x00 /* Basic mode control register */
int genphy_config_aneg(struct phy_device *phydev)
{
int result;
int ival = 0;
if (phydev->autoneg != AUTONEG_ENABLE)
return genphy_setup_forced(phydev);
#if 0
//set loopback
//Reg 0
ival |= (1 << 14 );
ival |= (1 << 6 );
ival &= ~(1 << 13 );
ival &= ~(1 << 12 );
ival |= (1 << 8 );
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_ANENABLE & (~(1<<6)));
#endif
printf("genphy_config_aneg ival = 0x%x,test1111.\n",ival);
result = genphy_config_advert(phydev);
if (result < 0) /* error */
{
return result;
}
printf("genphy_config_aneg result=%d test2222.\n",result);
if (result == 0) {
/*
* Advertisment hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
int ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
printf("genphy_config_aneg MII_BMCR, ctl=0x%x\n",ctl);
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
result = 1; /* do restart aneg */
}
/*
* Only restart aneg if we are advertising something different
* than we were before.
*/
if (result > 0)
{
printf("genphy_config_aneg test3333.\n");
result = genphy_restart_aneg(phydev);
}
return result;
}
//设置为非lookback,把数据发出去;
/**
* genphy_config_advert - sanitize and advertise auto-negotiation parameters
* @phydev: target phy_device struct
*
* Description: Writes MII_ADVERTISE with the appropriate values,
* after sanitizing the values to make sure we only advertise
* what is supported. Returns < 0 on error, 0 if the PHY's advertisement
* hasn't changed, and > 0 if it has changed.
*/
static int genphy_config_advert(struct phy_device *phydev)
{
u32 advertise;
int oldadv, adv, bmsr;
int err, changed = 0;
/* Only allow advertising what this PHY supports */
phydev->advertising &= phydev->supported;
advertise = phydev->advertising;
/* Setup standard advertisement */
adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
oldadv = adv;
if (adv < 0)
return adv;
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
ADVERTISE_PAUSE_ASYM);
if (advertise & ADVERTISED_10baseT_Half)
adv |= ADVERTISE_10HALF;
if (advertise & ADVERTISED_10baseT_Full)
adv |= ADVERTISE_10FULL;
if (advertise & ADVERTISED_100baseT_Half)
adv |= ADVERTISE_100HALF;
if (advertise & ADVERTISED_100baseT_Full)
adv |= ADVERTISE_100FULL;
if (advertise & ADVERTISED_Pause)
adv |= ADVERTISE_PAUSE_CAP;
if (advertise & ADVERTISED_Asym_Pause)
adv |= ADVERTISE_PAUSE_ASYM;
if (advertise & ADVERTISED_1000baseX_Half)
adv |= ADVERTISE_1000XHALF;
if (advertise & ADVERTISED_1000baseX_Full)
adv |= ADVERTISE_1000XFULL;
printf("genphy_config_advert MII_ADVERTISE(0x04)adv=0x%x, oldadv=0x%x\n",adv,oldadv);
if (adv != oldadv) {
err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv);
if (err < 0)
return err;
changed = 1;
}
bmsr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
if (bmsr < 0)
return bmsr;
/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
* 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
* logical 1.
*/
if (!(bmsr & BMSR_ESTATEN))
return changed;
/* Configure gigabit if it's supported */
adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
oldadv = adv;
if (adv < 0)
return adv;
adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
if (phydev->supported & (SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full)) {
if (advertise & SUPPORTED_1000baseT_Half)
adv |= ADVERTISE_1000HALF;
if (advertise & SUPPORTED_1000baseT_Full)
adv |= ADVERTISE_1000FULL;
}
printf("genphy_config_advert MII_CTRL1000(0x09)adv=0x%x, oldadv=0x%x\n",adv,oldadv);
if (adv != oldadv)
{
changed = 1;
}
//adv |= (1<<10);
//adv |= (1<<11);
#if 0
//set loopback
adv |= (1<<12);
adv &= ~(1<<11);
printf("genphy_config_advert loopback MII_CTRL1000(0x09)adv=0x%x\n",adv);
#endif
err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, adv);
if (err < 0)
return err;
return changed;
}
//自动适应模式
/**
* genphy_restart_aneg - Enable and Restart Autonegotiation
* @phydev: target phy_device struct
*/
int genphy_restart_aneg(struct phy_device *phydev)
{
int ctl;
ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
if (ctl < 0)
return ctl;
#if 1
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART );
/* Don't isolate the PHY if we're negotiating */
ctl &= ~(BMCR_ISOLATE);
#else
//set loopback
ctl |= (1 << 14 );
ctl |= (1 << 6 );
ctl &= ~(1 << 13 );
ctl &= ~(1 << 12 );
ctl |= (1 << 8 );
#endif
printf("genphy_restart_aneg MII_BMCR ctl = 0x%x\n",ctl);
//ctl |= (1 << 15 ); // RESET PHY ksz9031
ctl = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl);
return ctl;
}
//网速带宽设置,寄存器值可以添加printf打印出来;
int genphy_config(struct phy_device *phydev)
{
int val;
u32 features;
features = (SUPPORTED_TP | SUPPORTED_MII
| SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_BNC);
/* Do we support autonegotiation? */
val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
printf("genphy_config phyaddr=%d,MII_BMSR(%d)=0x%x\n",phydev->addr,MII_BMSR,val);
if (val < 0)
return val;
if (val & BMSR_ANEGCAPABLE)
features |= SUPPORTED_Autoneg;
if (val & BMSR_100FULL)
features |= SUPPORTED_100baseT_Full;
if (val & BMSR_100HALF)
features |= SUPPORTED_100baseT_Half;
if (val & BMSR_10FULL)
features |= SUPPORTED_10baseT_Full;
if (val & BMSR_10HALF)
features |= SUPPORTED_10baseT_Half;
if (val & BMSR_ESTATEN) {
val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS);
printf("genphy_config phyaddr=%d,MII_ESTATUS(%d)=0x%x\n",phydev->addr,MII_ESTATUS,val);
if (val < 0)
return val;
if (val & ESTATUS_1000_TFULL)
features |= SUPPORTED_1000baseT_Full;
if (val & ESTATUS_1000_THALF)
features |= SUPPORTED_1000baseT_Half;
if (val & ESTATUS_1000_XFULL)
features |= SUPPORTED_1000baseX_Full;
if (val & ESTATUS_1000_XHALF)
features |= SUPPORTED_1000baseX_Half;
}
phydev->supported &= features;
phydev->advertising &= features;
genphy_config_aneg(phydev);
return 0;
}
//移植完成后,测试网络OK
//ETH0 网络OK
=> ping 192.168.1.108
ethernet@f0028000: PHY present at 0
ethernet@f0028000: Starting autonegotiation...
ethernet@f0028000: Autonegotiation complete
ethernet@f0028000: link up, 100Mbps full-duplex (lpa: 0xcde1)
macb_phy_init udelay(3000)
Using ethernet@f0028000 device
gmacb send
ff ff ff ff ff ff ee ab c1 d2 e6 c6 08 06 00 01 08 00 06 04 00 01 ee ab c1 d2 e6 c6 c0 a8 01 64 00 00 00 00 00 00 c0 a8 01 6c
_macb_recv paddr=0x2fb6aa80
_macb_recv paddr=0x2fb6aa81
_macb_recv ctrl=0xc00c040
recv length=64
gmacb send
a8 1e 84 e6 e9 e5 ee ab c1 d2 e6 c6 08 00 45 00 00 1c 00 00 40 00 ff 01 f7 bf c0 a8 01 64 c0 a8 01 6c 08 00 f7 ff 00 00 00 00
_macb_recv paddr=0x2fb6ab00
_macb_recv paddr=0x2fb6ab01
_macb_recv ctrl=0xc00c040
recv length=64
_macb_recv paddr=0x2fb6ab80
host 192.168.1.108 is alive
=>
更多linux知识点推荐:
[Linux 底层]U-boot调试命令使用技巧
[Linux 底层]U-boot编译移植
[Linux 底层]bootstrap移植裁剪及编译
[Linux 底层]U-boot烧录脚本介绍SecureCRT
关注微信公众号,回复“ksz9031驱动”,免费下载ksz9031的驱动源代码。