Freescale i.MX6 Linux Ethernet Driver驱动源码分析(一)
如需转载,请注明转载地址:最近需要在Freescale i.MX6上移植Ethernet AVB的内核patch,Ethernet AVB的Wiki:http://en.wikipedia.org/wiki/Audio_Video_Bridging,而Freescale原来已经在kernel 3.0.35 LTIB 4.0.0的基础上提供了patch:https://community.freescale.com/docs/DOC-95578,现在需要做的是把kernel 3.0.35上的patch移植到yocto kernel-3.10.17_ga上,第一次听上去就感觉像是代码的复制粘贴,不过首要的问题是Ethernet Driver都没有看过,还谈何移植,所以索性把Ethernet Driver也学习一遍,Google了一段时间,找到了几份不错的学习资料,特此共享之:
Manual》文档可以从官网上下到,它会给你一些简要的介绍。这对于很多初学者来说还是非常友好的。首先找到源码的位置:drviers/net/fec.c对应还有一个fec.h作头文件,同时现在大多数的MPU还集成了支持FEC1588的硬件控制器,从硬件上支持一些对时间有要求的应用,比如Ethernet AVB等等。这里以 3.0.35内核目前最新release的 LTIB 4.1.0环境的内核为参考。
static int __initfec_enet_module_init ( void ){printk ( KERN_INFO "FEC Ethernet Driver\n" );return platform_driver_register (& fec_driver );}static void __exitfec_enet_cleanup ( void ){platform_driver_unregister (& fec_driver );}module_exit ( fec_enet_cleanup );module_init ( fec_enet_module_init );MODULE_LICENSE ( "GPL" );
static int fec_mac_addr_setup ( char * mac_addr ){char * ptr , * p = mac_addr ;unsigned long tmp ;int i = 0 , ret = 0 ;while ( p && (* p ) && i < 6 ) {ptr = strchr ( p , ':' );if ( ptr )* ptr ++ = '\0' ;if ( strlen ( p )) {ret = strict_strtoul ( p , 16 , & tmp );if ( ret < 0 || tmp > 0xff )break ;macaddr [ i ++] = tmp ;}p = ptr ;}return 0 ;}__setup ( "fec_mac=" , fec_mac_addr_setup );
static struct platform_driver fec_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &fec_pm_ops, #endif }, .id_table = fec_devtype, .probe = fec_probe, .remove = __devexit_p(fec_drv_remove), };
/* Functions marked as __devexit may be discarded at kernel link time, depending on config options. Newer versions of binutils detect references from retained sections to discarded sections and flag an error. Pointers to __devexit functions must use __devexit_p(function_name), the wrapper will insert either the function_name or NULL, depending on the config options. */ #if defined(MODULE) || defined(CONFIG_HOTPLUG) #define __devexit_p(x) x #else #define __devexit_p(x) NULL #endif
static struct platform_device_id fec_devtype [] = {{. name = "enet" ,. driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_BUG_TKT168103 ,},{. name = "fec" ,. driver_data = 0 ,},{. name = "imx28-fec" ,. driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |FEC_QUIRK_BUG_TKT168103 ,},{ }};
static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); }
/** initialize __mach_desc_MX6Q_SABRESD data structure.*/MACHINE_START ( MX6Q_SABRESD , "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board" )/* Maintainer: Freescale Semiconductor, Inc. */. boot_params = MX6_PHYS_OFFSET + 0x100 ,. fixup = fixup_mxc_board ,. map_io = mx6_map_io ,. init_irq = mx6_init_irq ,. init_machine = mx6_sabresd_board_init ,. timer = & mx6_sabresd_timer ,. reserve = mx6q_sabresd_reserve ,MACHINE_END
void __init imx6_init_fec ( struct fec_platform_data fec_data ){fec_get_mac_addr ( fec_data . mac );if (! is_valid_ether_addr ( fec_data . mac ))random_ether_addr ( fec_data . mac );if ( cpu_is_mx6sl ())imx6sl_add_fec (& fec_data );elseimx6q_add_fec (& fec_data );}
struct fec_platform_data {int (* init ) ( struct phy_device *);int (* power_hibernate ) ( struct phy_device *);phy_interface_t phy ;unsigned char mac [ ETH_ALEN ];int gpio_irq ;};
static struct fec_platform_data fec_data __initdata = {. init = mx6q_sabresd_fec_phy_init ,. phy = PHY_INTERFACE_MODE_RGMII ,. gpio_irq = MX6_ENET_IRQ ,};
extern const struct imx_fec_data imx6q_fec_data __initconst ;#define imx6q_add_fec ( pdata ) \imx_add_fec (& imx6q_fec_data , pdata )
#ifdef CONFIG_SOC_IMX6Qconst struct imx_fec_data imx6q_fec_data __initconst =imx_fec_data_entry_single ( MX6Q , "enet" );const struct imx_fec_data imx6sl_fec_data __initconst =imx_fec_data_entry_single ( MX6DL , "fec" );#endif
#define imx_fec_data_entry_single ( soc , _devid ) \{ \. iobase = soc ## _FEC_BASE_ADDR, \.irq = soc ## _INT_FEC, \.devid = _devid, \}
struct platform_device * __init imx_add_fec (const struct imx_fec_data * data ,const struct fec_platform_data * pdata ){struct resource res [] = {{. start = data -> iobase ,. end = data -> iobase + SZ_4K - 1 ,. flags = IORESOURCE_MEM ,}, {. start = data -> irq ,. end = data -> irq ,. flags = IORESOURCE_IRQ ,},};if (! fuse_dev_is_available ( MXC_DEV_ENET ))return ERR_PTR (- ENODEV );return imx_add_platform_device_dmamask ( data -> devid , 0 ,res , ARRAY_SIZE ( res ),pdata , sizeof (* pdata ), DMA_BIT_MASK ( 32 ));}
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 );if (! r )return - ENXIO ;r = request_mem_region ( r -> start , resource_size ( r ), pdev -> name );if (! r )return - EBUSY ;
/* Init network device */ndev = alloc_etherdev ( sizeof ( struct fec_enet_private ));if (! ndev ) {ret = - ENOMEM ;goto failed_alloc_etherdev ;}
SET_NETDEV_DEV(ndev, &pdev->dev);
定义如下:
/* Set the sysfs physical device reference for the network logical device * if set prior to registration will cause a symlink during initialization. */ #define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))
/* setup board info structure */fep = netdev_priv ( ndev );
fep -> hwp = ioremap ( r -> start , resource_size ( r ));fep -> pdev = pdev ;if (! fep -> hwp ) {ret = - ENOMEM ;goto failed_ioremap ;}
platform_set_drvdata(pdev, ndev);
接着是对pdata的数据进行解析:pdata = pdev->dev.platform_data;
if ( pdata )fep -> phy_interface = pdata -> phy ;
if (pdata->gpio_irq > 0) { gpio_request(pdata->gpio_irq, "gpio_enet_irq"); gpio_direction_input(pdata->gpio_irq); irq = gpio_to_irq(pdata->gpio_irq); ret = request_irq(irq, fec_enet_interrupt, IRQF_TRIGGER_RISING, pdev->name, ndev); if (ret) goto failed_irq; } else { /* This device has up to three irqs on some platforms */ for (i = 0; i < 3; i++) { irq = platform_get_irq(pdev, i); if (i && irq < 0) break; ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev); if (ret) { while (--i >= 0) { irq = platform_get_irq(pdev, i); free_irq(irq, ndev); } goto failed_irq; } } }
static struct fec_platform_data fec_data __initdata = {. init = mx6q_sabresd_fec_phy_init ,. phy = PHY_INTERFACE_MODE_RGMII ,. gpio_irq = MX6_ENET_IRQ ,};
if (enet_to_gpio_6)/* Make sure the IOMUX_OBSRV_MUX1 is set to ENET_IRQ. */mxc_iomux_set_specialbits_register (IOMUX_OBSRV_MUX1_OFFSET ,OBSRV_MUX1_ENET_IRQ ,OBSRV_MUX1_MASK );elsefec_data . gpio_irq = - 1 ;imx6_init_fec ( fec_data );
static int __init set_enet_irq_to_gpio ( char * p ){enet_to_gpio_6 = true ;return 0 ;}early_param ( "enet_gpio_6" , set_enet_irq_to_gpio );
fep -> clk = clk_get (& pdev -> dev , "fec_clk" );if ( IS_ERR ( fep -> clk )) {ret = PTR_ERR ( fep -> clk );goto failed_clk ;}fep -> mdc_clk = clk_get (& pdev -> dev , "fec_mdc_clk" );if ( IS_ERR ( fep -> mdc_clk )) {ret = PTR_ERR ( fep -> mdc_clk );goto failed_clk ;}clk_enable ( fep -> clk );
ret = fec_enet_init ( ndev );if ( ret )goto failed_init ;
ret = fec_enet_mii_init ( pdev );if ( ret )goto failed_mii_init ;
if ( fec_ptp_malloc_priv (&( fep -> ptp_priv ))) {if ( fep -> ptp_priv ) {fep -> ptp_priv -> hwp = fep -> hwp ;ret = fec_ptp_init ( fep -> ptp_priv , pdev -> id );if ( ret )printk ( KERN_WARNING "IEEE1588: ptp-timer is unavailable\n" );elsefep -> ptimer_present = 1 ;} elseprintk ( KERN_ERR "IEEE1588: failed to malloc memory\n" );}
/* Carrier starts down, phylib will bring it up */netif_carrier_off ( ndev );clk_disable ( fep -> clk );INIT_DELAYED_WORK (& fep -> fixup_trigger_tx , fixup_trigger_tx_func );
ret = register_netdev ( ndev );if ( ret )goto failed_register ;