I.MX6 Ethernet MAC (ENET) MAC Address hacking
/********************************************************************* * I.MX6 Ethernet MAC (ENET) MAC Address hacking * 说明: * Lee对相关的一些代码进行了修改,所以这边跟一下这边相关的设置原理, * 主要是为了知道MAC address在Uboot、Linux kernel阶段的的设置,获取的 * 工作机制。 * * 2016-7-27 深圳 南山平山村 曾剑锋 ********************************************************************/ 参考文档: Freescale i.MX6 Linux Ethernet Driver驱动源码分析 http://blog.163.com/thinki_cao/blog/static/8394487520146308450620/ cat lib_arm/board.c void start_armboot (void) { ...... stdio_init (); /* get the devices list going. */ ---------+ ...... | eth_initialize(gd->bd); ----------------*-+ ...... | | } | | | | int stdio_init (void) <-----------------------------+ | { | ...... | #ifdef CONFIG_LCD | drv_lcd_init (); ------------+ | #endif | | ...... | | | | return (0); | | } | | | | int drv_lcd_init (void) <------------+ | { | ...... | lcd_init (lcd_base); /* LCD initialization */ --------+ | ...... | | } | | | | static int lcd_init (void *lcdbase) <--------+ | { | ...... | lcd_enable (); --------+ | ...... | | } | | | | #ifdef CONFIG_LCD | | void lcd_enable(void) <--------+ | { | ...... | setenv("ethaddr",buffer); | ...... | } | | int eth_initialize(bd_t *bis) <----------------------+ { ...... /* Try board-specific initialization first. If it fails or isn't * present, try the cpu-specific initialization */ if (board_eth_init(bis) < 0) cpu_eth_init(bis); -------+ | ...... | } | | | int cpu_eth_init(bd_t *bis) <------+ { int rc = -ENODEV; #if defined(CONFIG_MXC_FEC) rc = mxc_fec_initialize(bis); -------+ | /* Board level init */ | enet_board_init(); | | #endif | return rc; | } | | int mxc_fec_initialize(bd_t *bis) <-------+ { struct eth_device *dev; int i; unsigned char ethaddr[6]; for (i = 0; i < sizeof(fec_info) / sizeof(fec_info[0]); i++) { #ifdef CONFIG_ARCH_MMU int j; #endif dev = (struct eth_device *)memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof *dev); if (dev == NULL) hang(); memset(dev, 0, sizeof(*dev)); sprintf(dev->name, "FEC%d", fec_info[i].index); dev->priv = &fec_info[i]; ----------+ dev->init = fec_init; | dev->halt = fec_halt; | dev->send = fec_send; | dev->recv = fec_recv; | ...... | | if (fec_get_hwaddr(dev, ethaddr) == 0) { ------*-----+ printf("got MAC address from IIM: %pM\n", ethaddr); | | memcpy(dev->enetaddr, ethaddr, 6); | | fec_set_hwaddr(dev); ------*-----*-+ } | | | } | | | | | | return 1; | | | } | | | | | | struct fec_info_s fec_info[] = { <----------+ | | { | | | 0, /* index */ | | | CONFIG_FEC0_IOBASE, /* io base */ --------+-*-----*-*-+ CONFIG_FEC0_PHY_ADDR, /* phy_addr */ | | | | | 0, /* duplex and speed */ | | | | | 0, /* phy name */ | | | | | 0, /* phyname init */ | | | | | 0, /* RX BD */ | | | | | 0, /* TX BD */ | | | | | 0, /* rx Index */ | | | | | 0, /* tx Index */ | | | | | 0, /* tx buffer */ | | | | | #ifdef CONFIG_ARCH_MMU | | | | | { 0 }, /* rx buffer */ | | | | | #endif | | | | | 0, /* initialized flag */ | | | | | }, | | | | | }; | | | | | | | | | | /* FEC private information */ | | | | | struct fec_info_s { <----------*-+ | | | int index; | | | | u32 iobase; | | | | int phy_addr; | | | | int dup_spd; | | | | char *phy_name; | | | | int phyname_init; | | | | cbd_t *rxbd; /* Rx BD */ | | | | cbd_t *txbd; /* Tx BD */ | | | | uint rxIdx; | | | | uint txIdx; | | | | char *txbuf; | | | | #ifdef CONFIG_ARCH_MMU | | | | char *rxbuf[PKTBUFSRX]; | | | | #endif | | | | int initialized; | | | | struct fec_info_s *next; | | | | }; | | | | | | | | #define CONFIG_FEC0_IOBASE ENET_BASE_ADDR <------------+ | | | #define ENET_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x8000) | | | #define AIPS2_OFF_BASE_ADDR (ATZ2_BASE_ADDR + 0x80000) | | | #define ATZ2_BASE_ADDR AIPS2_ARB_BASE_ADDR | | | #define AIPS2_ARB_BASE_ADDR 0x02100000 | | | | | | extern int fec_get_mac_addr(unsigned char *mac); | | | static int fec_get_hwaddr(struct eth_device *dev, unsigned char *mac) <-+ | | { | | #ifdef CONFIG_GET_FEC_MAC_ADDR_FROM_IIM | | fec_get_mac_addr(mac); ----------+ | | | | | return 0; | | | #else | | | return -1; | | | #endif | | | } | | | | | | #ifdef CONFIG_GET_FEC_MAC_ADDR_FROM_IIM | | | int fec_get_mac_addr(unsigned char *mac) <----------+ | | { | | #if 0 | | unsigned int value; | | | | value = readl(OCOTP_BASE_ADDR + HW_OCOTP_MACn(0)); | | mac[5] = value & 0xff; | | mac[4] = (value >> 8) & 0xff; | | mac[3] = (value >> 16) & 0xff; | | mac[2] = (value >> 24) & 0xff; | | value = readl(OCOTP_BASE_ADDR + HW_OCOTP_MACn(1)); | | mac[1] = value & 0xff; | | mac[0] = (value >> 8) & 0xff; | | #else | | eth_getenv_enetaddr("ethaddr", mac); ----------+ | | #endif | | | return 0; | | | } | | | #endif | | | | | | static int fec_set_hwaddr(struct eth_device *dev) <----|-----------+ | { | | uchar *mac = dev->enetaddr; | | struct fec_info_s *info = dev->priv; | | volatile fec_t *fecp = (fec_t *)(info->iobase); | | | | writel(0, &fecp->iaur); | | writel(0, &fecp->ialr); | | writel(0, &fecp->gaur); | | writel(0, &fecp->galr); | | | | /* | | * Set physical address | | */ V | writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], ------*-+ &fecp->palr); | | writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, &fecp->paur); | | | | return 0; | | } | | | | | | cat arch/arm/mach-mx6/board-mx6q_sabresd.c | | MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Boa|"|) /* 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 | | | | | | /*! | | | * Board specific initialization. | | | */ | | | static void __init mx6_sabresd_board_init(void) <---------+ | | { | | ...... | | imx6_init_fec(fec_data); | | ...... | | | } | | | V | | 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); | | | else | | | imx6q_add_fec(&fec_data); | | | } | | | | | | 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_IMX6Q | | | const 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 | | | | | | | | 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)); | | | } | | | | | | #define imx_fec_data_entry_single(soc, _devid) \ <----+ | | { \ | | .iobase = soc ## _FEC_BASE_ADDR, \ --------+ | | .irq = soc ## _INT_FEC, \ | | | .devid = _devid, \ | | | } | | | | | | #define MX6DL_FEC_BASE_ADDR ENET_BASE_ADDR <-------+------+ | #define ENET_BASE_ADDR (AIPS2_OFF_BASE_ADDR+0x8000) | #define AIPS2_OFF_BASE_ADDR (ATZ2_BASE_ADDR + 0x80000) | #define ATZ2_BASE_ADDR AIPS2_ARB_BASE_ADDR | #define AIPS2_ARB_BASE_ADDR 0x02100000 | | cat drivers/net/fec.c | static int __init | fec_enet_module_init(void) | { | printk(KERN_INFO "FEC Ethernet Driver\n"); | | return platform_driver_register(&fec_driver); ------+ | } | | | | static void __exit | | fec_enet_cleanup(void) | | { | | platform_driver_unregister(&fec_driver); | | } | | | | module_exit(fec_enet_cleanup); | | module_init(fec_enet_module_init); | | | | MODULE_LICENSE("GPL"); | | | | 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), | | }; | | | | static int __devinit | | fec_probe(struct platform_device *pdev) <-----+ | { | ...... | fep->hwp = ioremap(r->start, resource_size(r)); ---------------+ | ...... | | ret = fec_enet_init(ndev); ------+ | | if (ret) | | | goto failed_init; | | | ...... | | | } | | | | | | static int fec_enet_init(struct net_device *ndev) <-----+ | | { | | ...... | | /* Get the Ethernet address */ | | fec_get_mac(ndev); ----------------+ | | ...... | | | } | | | | | | static void __inline__ fec_get_mac(struct net_device *ndev) <-----+ | | { | | struct fec_enet_private *fep = netdev_priv(ndev); | | struct fec_platform_data *pdata = fep->pdev->dev.platform_data; | | unsigned char *iap, tmpaddr[ETH_ALEN]; | | | | /* | | * try to get mac address in following order: | | * | | * 1) module parameter via kernel command line in form | | * fec.macaddr=0x00,0x04,0x9f,0x01,0x30,0xe0 | | */ | | iap = macaddr; | | | | /* | | * 2) from flash or fuse (via platform data) | | */ | | if (!is_valid_ether_addr(iap)) { | | #ifdef CONFIG_M5272 | | if (FEC_FLASHMAC) | | iap = (unsigned char *)FEC_FLASHMAC; | | #else | | if (pdata) | | memcpy(iap, pdata->mac, ETH_ALEN); | | #endif | | } | | | | /* | | * 3) FEC mac registers set by bootloader | | */ | | if (!is_valid_ether_addr(iap)) { | | *((unsigned long *) &tmpaddr[0]) = | | be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW)); <----------+-----+ *((unsigned short *) &tmpaddr[4]) = be16_to_cpu(readl(fep->hwp + FEC_ADDR_HIGH) >> 16); iap = &tmpaddr[0]; } memcpy(ndev->dev_addr, iap, ETH_ALEN); /* Adjust MAC if using macaddr */ if (iap == macaddr) ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->pdev->id; }