在linux kerneldriver/net/ethernet/nxpmac目录下的代码是对S5P4418 SOC中的MAC进行初始化,该目录下仍然存在大量的stmmac
其中在nxpmac_main.c中是这个驱动的入口,在这个源文件中存在
module_init(stmmac_init);
module_exit(stmmac_exit);
在stmmac_init函数中调用stmmac_register_platform向kernel注册驱动程序。而stmmac_register_platform函数存在于nxpmac.h,函数实体如下:
extern struct platform_driver stmmac_pltfr_driver;
static inline int stmmac_register_platform(void)
{
int err;
err = platform_driver_register(&stmmac_pltfr_driver);
if (err)
pr_err("stmmac: failed to register the platform driver\n");
return err;
}
static inline void stmmac_unregister_platform(void)
{
platform_driver_unregister(&stmmac_pltfr_driver);
}
进一步追踪stmmac_pltfr_driver变量,在nxpmac_platform.c中发现该变量的定义
struct platform_driver stmmac_pltfr_driver = {
.probe = stmmac_pltfr_probe,
.remove = stmmac_pltfr_remove,
.driver = {
.name = NXPMAC_RESOURCE_NAME,
.owner = THIS_MODULE,
#if CFG_ETHER_LOOPBACK_MODE == 1
#else
.pm = &stmmac_pltfr_pm_ops,
#endif
.of_match_table = of_match_ptr(stmmac_dt_ids),
},
};
对stmmac_pltfr_probe进行分析:首先是获取MAC I/O内存的起始地址和大小(MAC和PHY寄存器的地址资源)res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
然后在内核IO内存request_mem_region(res->start, resource_size(res), pdev->name),申请成功后使用addr = ioremap(res->start, resource_size(res));获取物理地址对应的虚拟地址。
紧接着,驱动程序获取之前注册设备的时候注册的数据plat_dat = dev_get_platdata(&pdev->dev);如果注册设备的时候没有注册platdata,那么驱动程序会为platdata申请内存
if (!plat_dat)
plat_dat = devm_kzalloc(&pdev->dev,
sizeof(struct plat_stmmacenet_data),
GFP_KERNEL);
接下来会调用ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);这个函数的作用是获取注册设备的时候注册的一些配置。但是在NanoPC-T2中,这个函数是空的,不会做任何的事情。
驱动会调用注册设备时候添加的一个init函数
if (plat_dat->init) {
ret = plat_dat->init(pdev);
if (unlikely(ret))
goto out_unmap;
}
static struct plat_stmmacenet_data nxpmac_plat_data = {
#if defined (CONFIG_REALTEK_PHY_RTL8201)
.phy_addr = 3, // 7 for 8211 3 for 8201
.clk_csr = 0xe,
.speed = SPEED_100,
#else
.phy_addr = 7,
.clk_csr = 0x4, // PCLK 150~250 Mhz
.speed = SPEED_1000, // SPEED_1000
#endif
.interface = PHY_INTERFACE_MODE_RGMII,
.autoneg = AUTONEG_ENABLE, //AUTONEG_ENABLE or AUTONEG_DISABLE
.duplex = DUPLEX_FULL,
.pbl = 16, /* burst 16 */
.has_gmac = 1, /* GMAC ethernet */
.enh_desc = 0,
.mdio_bus_data = &nxpmac0_mdio_bus,
.init = &nxpmac_init,
};
在nxpmac_init函数中做如下的配置:
一、时钟设置
1、在S5P4418 SOC中存在一个Clok Generator模块,在这个模块中控制各个IP时钟产生,在nxpmac_init会获取控制GMAC始终控制寄存器的地址
addr = NX_CLKGEN_GetPhysicalAddress(CLOCKINDEX_OF_DWC_GMAC_MODULE);
2、设置GMACCLKGEN0L寄存器的CLKSRCSEL0=4,意味着RX的时钟来自外部。在NanoPC-T2中,RX时钟来自PHY芯片
NX_CLKGEN_SetClockSource( CLOCKINDEX_OF_DWC_GMAC_MODULE, 0, 4);
3、设置GMACCLKGEN0L寄存器的CLKDIV0=0
NX_CLKGEN_SetClockDivisor( CLOCKINDEX_OF_DWC_GMAC_MODULE, 0, 1);
4、设置GMACCLK寄存器的OUTCLKINV0=0(Normal:Falling Edge)
NX_CLKGEN_SetClockOutInv( CLOCKINDEX_OF_DWC_GMAC_MODULE, 0, CFALSE);
5、设置GMACCLKENB寄存器的CLKGENENB=1
NX_CLKGEN_SetClockDivisorEnable( CLOCKINDEX_OF_DWC_GMAC_MODULE, CTRUE);
二、复位控制设置
1、获取复位控制寄存器的物理地址,开始地址在0xC001_2000
2、让MAC IP核复位一下
NX_RSTCON_SetnRST(RESETINDEX_OF_DWC_GMAC_MODULE_aresetn_i, RSTCON_ENABLE);
udelay(100);
NX_RSTCON_SetnRST(RESETINDEX_OF_DWC_GMAC_MODULE_aresetn_i, RSTCON_DISABLE);
udelay(100);
NX_RSTCON_SetnRST(RESETINDEX_OF_DWC_GMAC_MODULE_aresetn_i, RSTCON_ENABLE);
udelay(100);
三、让PHY芯片复位
1、外部的PHY芯片的RESET与S5P4418的一个引脚相连,驱动程序通过让此引脚输出低电平来达到让PHY复位的功能
gpio_request(CFG_ETHER_GMAC_PHY_RST_NUM,"Ethernet Rst pin");
gpio_direction_output(CFG_ETHER_GMAC_PHY_RST_NUM, 1 );
udelay( 100 );
gpio_set_value(CFG_ETHER_GMAC_PHY_RST_NUM, 0 );
udelay( 100 );
gpio_set_value(CFG_ETHER_GMAC_PHY_RST_NUM, 1 );
以上步骤执行完毕后,返回stmmac_pltfr_probe函数执行priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
下一章介绍stmmac_dvr_probe函数的过程。