imx6 fec分析
/***************************************************************************** * imx6 fec分析 * 本文主要分析imx6的网卡程序,phy使用ar8031。 * * Tony Liu, 2016-4-19, Shenzhen ****************************************************************************/ /* 注册设备 */ kernel/arch/arm/mach-mx6/board_mx6q_sabresd.c 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 | V 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); //获得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); | | | } | | | | | | static int fec_get_mac_addr(unsigned char *mac) <------+ | | { | | unsigned int value; | | | | value = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + HW_OCOTP_MACn(0)); | | value = 0x03040506; | | mac[5] = value & 0xff; | | mac[4] = (value >> 8) & 0xff; | | mac[3] = (value >> 16) & 0xff; | | mac[2] = (value >> 24) & 0xff; | | value = readl(MX6_IO_ADDRESS(OCOTP_BASE_ADDR) + HW_OCOTP_MACn(1)); | | value = 0x0102; | | mac[1] = value & 0xff; | | mac[0] = (value >> 8) & 0xff; | | | | return 0; | | } | | | | #define imx6sl_add_fec(pdata) \ <-----------------------+ | imx_add_fec(&imx6sl_fec_data, pdata) | | | | 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)); | } | | | static struct fec_platform_data fec_data __initdata = { <-----------+ .init = mx6q_sabresd_fec_phy_init, ----------+ .power_hibernate = mx6_sabresd_fec_power_hibernate, | .phy = PHY_INTERFACE_MODE_RGMII, | | #ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO | | .gpio_irq = MX6_ENET_IRQ, | | #endif | | }; | | V | static int mx6_sabresd_fec_power_hibernate(struct phy_device *phydev) | { | unsigned short val; | | /*set AR8031 debug reg 0xb to hibernate power*/ | phy_write(phydev, 0x1d, 0xb); | val = phy_read(phydev, 0x1e); | | val |= 0x8000; | phy_write(phydev, 0x1e, val); | | return 0; | } | | static int mx6q_sabresd_fec_phy_init(struct phy_device *phydev) <-------+ { unsigned short val; gpio_request(SABRESD_FEC_PHY_RESET,"phy-rst"); gpio_direction_output(SABRESD_FEC_PHY_RESET, 0); mdelay(5); gpio_direction_output(SABRESD_FEC_PHY_RESET, 1); mdelay(1); /* Ar8031 phy SmartEEE feature cause link status generates glitch, * which cause ethernet link down/up issue, so disable SmartEEE */ /* ar8031芯片手册中有体积,有三种寄存器,不同的寄存器读取方法不一样。*/ // 1. 0xd确定设备地址, 0xe读取其中的值 phy_write(phydev, 0xd, 0x3); //芯片device address: 3 phy_write(phydev, 0xe, 0x805d); // offset: 0x805D phy_write(phydev, 0xd, 0x4003); //keep the device address val = phy_read(phydev, 0xe); //读取寄存器0x805d 的值 val &= ~(0x1 << 8); //disable smartEEE phy_write(phydev, 0xe, val); /* To enable AR8031 ouput a 125MHz clk from CLK_25M */ phy_write(phydev, 0xd, 0x7); phy_write(phydev, 0xe, 0x8016); phy_write(phydev, 0xd, 0x4007); //不知到为何这里要用0x4007 val = phy_read(phydev, 0xe); //设置时钟125M val &= 0xffe3; val |= 0x18; phy_write(phydev, 0xe, val); // 2. 0x1d确定设备地址, 0x1e读取其中的值 //将要配置的寄存器的值写入0x1d /* introduce tx clock delay */ phy_write(phydev, 0x1d, 0x5); //debug register : 0x05 //从0x1e读取寄存器0x5的值 val = phy_read(phydev, 0x1e); val |= 0x0100; //rgmii tx clock delay enable phy_write(phydev, 0x1e, val); // 3. 一般的寄存器直接对地址进行操作,读取寄存器地址为0x0的值 val = phy_read(phydev, 0x0); if (val & BMCR_PDOWN) phy_write(phydev, 0x0, (val & ~BMCR_PDOWN)); return 0; } // 驱动注册 kernel/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); } | V static struct platform_driver fec_driver = { .driver = { .name = DRIVER_NAME, // "fec" .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) <-----+ { struct fec_enet_private *fep; struct fec_platform_data *pdata; struct net_device *ndev; int i, irq, ret = 0; struct resource *r; 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); /* 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 = pdev->dev.platform_data; if (pdata) fep->phy_interface = pdata->phy; #ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO 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; } } #endif fep->clk = clk_get(&pdev->dev, "fec_clk"); if (IS_ERR(fep->clk)) { ret = PTR_ERR(fep->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"); | | else | | fep->ptimer_present = 1; | | } else | | printk(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; | | | | return 0; | | | | failed_register: | | fec_enet_mii_remove(fep); | | if (fep->ptimer_present) | | fec_ptp_cleanup(fep->ptp_priv); | | kfree(fep->ptp_priv); | | failed_mii_init: | | failed_init: | | clk_disable(fep->clk); | | clk_put(fep->clk); | | failed_clk: | | #ifdef CONFIG_MX6_ENET_IRQ_TO_GPIO | | free_irq(irq, ndev); | | #else | | for (i = 0; i < 3; i++) { | | irq = platform_get_irq(pdev, i); | | if (irq > 0) | | free_irq(irq, ndev); | | } | | #endif | | failed_irq: | | iounmap(fep->hwp); | | failed_ioremap: | | free_netdev(ndev); | | failed_alloc_etherdev: | | release_mem_region(r->start, resource_size(r)); | | | | return ret; | | } | | | | static int fec_enet_mii_init(struct platform_device *pdev) <----------------+ | { | static struct mii_bus *fec0_mii_bus; | struct net_device *ndev = platform_get_drvdata(pdev); | struct fec_enet_private *fep = netdev_priv(ndev); | const struct platform_device_id *id_entry = | platform_get_device_id(fep->pdev); | int err = -ENXIO, i; | | /* | * The dual fec interfaces are not equivalent with enet-mac. | * Here are the differences: | * | * - fec0 supports MII & RMII modes while fec1 only supports RMII | * - fec0 acts as the 1588 time master while fec1 is slave | * - external phys can only be configured by fec0 | * | * That is to say fec1 can not work independently. It only works | * when fec0 is working. The reason behind this design is that the | * second interface is added primarily for Switch mode. | * | * Because of the last point above, both phys are attached on fec0 | * mdio interface in board design, and need to be configured by | * fec0 mii_bus. | */ | if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id) { | /* fec1 uses fec0 mii_bus */ | fep->mii_bus = fec0_mii_bus; | return 0; | } | | fep->mii_timeout = 0; | | /* | * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) | */ | fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), | (FEC_ENET_MII_CLK << 2)) << 1; | | /* set hold time to 2 internal clock cycle */ | if (cpu_is_mx6q() || cpu_is_mx6dl()) | fep->phy_speed |= FEC_ENET_HOLD_TIME; | | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | | fep->mii_bus = mdiobus_alloc(); | if (fep->mii_bus == NULL) { | err = -ENOMEM; | goto err_out; | } | | fep->mii_bus->name = "fec_enet_mii_bus"; | fep->mii_bus->read = fec_enet_mdio_read; | fep->mii_bus->write = fec_enet_mdio_write; | fep->mii_bus->reset = fec_enet_mdio_reset; | snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id + 1); | fep->mii_bus->priv = fep; | fep->mii_bus->parent = &pdev->dev; | | fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); | if (!fep->mii_bus->irq) { | err = -ENOMEM; | goto err_out_free_mdiobus; | } | | for (i = 0; i < PHY_MAX_ADDR; i++) | fep->mii_bus->irq[i] = PHY_POLL; | | if (mdiobus_register(fep->mii_bus)) | goto err_out_free_mdio_irq; | | /* save fec0 mii_bus */ | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | fec0_mii_bus = fep->mii_bus; | | return 0; | | err_out_free_mdio_irq: | kfree(fep->mii_bus->irq); | err_out_free_mdiobus: | mdiobus_free(fep->mii_bus); | err_out: | return err; | } | static int fec_enet_init(struct net_device *ndev) <-------------------------+ { struct fec_enet_private *fep = netdev_priv(ndev); struct bufdesc *cbd_base; struct bufdesc *bdp; int i; /* Allocate memory for buffer descriptors. */ cbd_base = dma_alloc_noncacheable(NULL, BUFDES_SIZE, &fep->bd_dma, GFP_KERNEL); if (!cbd_base) { printk("FEC: allocate descriptor memory failed?\n"); return -ENOMEM; } spin_lock_init(&fep->hw_lock); fep->netdev = ndev; /* Get the Ethernet address */ fec_get_mac(ndev); /* Set receive and transmit descriptor base. */ fep->rx_bd_base = cbd_base; fep->tx_bd_base = cbd_base + RX_RING_SIZE; /* The FEC Ethernet specific entries in the device structure */ ndev->watchdog_timeo = TX_TIMEOUT; ndev->netdev_ops = &fec_netdev_ops; -------------------------+ ndev->ethtool_ops = &fec_enet_ethtool_ops; ----------------------+ | | | fep->use_napi = FEC_NAPI_ENABLE; | | fep->napi_weight = FEC_NAPI_WEIGHT; | | if (fep->use_napi) { | | fec_rx_int_is_enabled(ndev, false); | | netif_napi_add(ndev, &fep->napi, fec_rx_poll, fep->napi_weight); | | } | | | | /* Initialize the receive buffer descriptors. */ | | bdp = fep->rx_bd_base; | | for (i = 0; i < RX_RING_SIZE; i++) { | | | | /* Initialize the BD for every fragment in the page. */ | | bdp->cbd_sc = 0; | | bdp->cbd_bufaddr = 0; | | bdp++; | | } | | | | /* Set the last buffer to wrap */ | | bdp--; | | bdp->cbd_sc |= BD_SC_WRAP; | | | | /* Init transmit descriptors */ | | fec_enet_txbd_init(ndev); | | | | fec_restart(ndev, 0); ---------------------------+ | | | | | return 0; | | | } | | | | | | static void | | | fec_restart(struct net_device *dev, int duplex) <--------+ | | { | | struct fec_enet_private *fep = netdev_priv(dev); | | const struct platform_device_id *id_entry = | | platform_get_device_id(fep->pdev); | | int i, ret; | | u32 val, temp_mac[2], reg = 0; | | | | /* Whack a reset. We should wait for this. */ | | writel(1, fep->hwp + FEC_ECNTRL); | | udelay(10); | | | | /* if uboot don't set MAC address, get MAC address | | * from command line; if command line don't set MAC | | * address, get from OCOTP; otherwise, allocate random | | * address. | | */ | | memcpy(&temp_mac, dev->dev_addr, ETH_ALEN); | | writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); | | writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); | | | | /* Clear any outstanding interrupt. */ | | writel(0xffc00000, fep->hwp + FEC_IEVENT); | | | | /* Reset all multicast. */ | | writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); | | writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); | | #ifndef CONFIG_M5272 | | writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); | | writel(0, fep->hwp + FEC_HASH_TABLE_LOW); | | #endif | | | | /* FIXME: adjust RX FIFO size for performance*/ | | #ifdef CONFIG_ARCH_MX53 | | writel(FEC_RX_FIFO_BR, fep->hwp + FEC_R_FSTART); | | #endif | | | | /* Set maximum receive buffer size. */ | | writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE); | | | | /* Set receive and transmit descriptor base. */ | | writel(fep->bd_dma, fep->hwp + FEC_R_DES_START); | | writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc) * RX_RING_SIZE, | | fep->hwp + FEC_X_DES_START); | | /* Reinit transmit descriptors */ | | fec_enet_txbd_init(dev); | | | | fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; | | fep->cur_rx = fep->rx_bd_base; | | | | /* Reset SKB transmit buffers. */ | | fep->skb_cur = fep->skb_dirty = 0; | | for (i = 0; i <= TX_RING_MOD_MASK; i++) { | | if (fep->tx_skbuff[i]) { | | dev_kfree_skb_any(fep->tx_skbuff[i]); | | fep->tx_skbuff[i] = NULL; | | } | | } | | | | /* Enable MII mode */ | | if (duplex) { | | /* MII enable / FD enable */ | | writel(OPT_FRAME_SIZE | 0x04, fep->hwp + FEC_R_CNTRL); | | writel(0x04, fep->hwp + FEC_X_CNTRL); | | } else { | | /* MII enable / No Rcv on Xmit */ | | writel(OPT_FRAME_SIZE | 0x06, fep->hwp + FEC_R_CNTRL); | | writel(0x0, fep->hwp + FEC_X_CNTRL); | | } | | fep->full_duplex = duplex; | | | | /* Set MII speed */ | | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | | | | /* | | * The phy interface and speed need to get configured | | * differently on enet-mac. | | */ | | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | | val = readl(fep->hwp + FEC_R_CNTRL); | | | | /* MII or RMII */ | | if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII) { | | val |= (1 << 6); | | printk("<tony> RGMII\n"); | | } | | else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { | | val |= (1 << 8); | | printk("<tony> RMII\n"); | | } | | else { | | val &= ~(1 << 8); | | printk("<tony> MII\n"); | | } | | //时能10M/100M支持 | | /* 10M or 100M */ | | if (fep->phy_dev && fep->phy_dev->speed == SPEED_100) { | | val &= ~(1 << 9); | | printk("<tony> 1702, speed:100\n"); | | } | | else { | | val |= (1 << 9); | | printk("<tony> 1706, speed:10\n"); | | } | | | | /* Enable pause frame | | * ENET pause frame has two issues as ticket TKT116501 | | * The issues have been fixed on Rigel TO1.1 and Arik TO1.2 | | */ | | if ((cpu_is_mx6q() && | | (mx6q_revision() >= IMX_CHIP_REVISION_1_2)) || | | (cpu_is_mx6dl() && | | (mx6dl_revision() >= IMX_CHIP_REVISION_1_1))) | | val |= FEC_ENET_FCE; | | | | writel(val, fep->hwp + FEC_R_CNTRL); | | } | | | | if (fep->ptimer_present) { | | /* Set Timer count */ | | ret = fec_ptp_start(fep->ptp_priv); | | if (ret) { | | fep->ptimer_present = 0; | | reg = 0x0; | | } else | | #if defined(CONFIG_SOC_IMX28) || defined(CONFIG_ARCH_MX6) | | reg = 0x00000010; | | #else | | reg = 0x0; | | #endif | | } else | | reg = 0x0; | | | | if (cpu_is_mx25() || cpu_is_mx53() || cpu_is_mx6sl()) { | | if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { | | /* disable the gasket and wait */ | | writel(0, fep->hwp + FEC_MIIGSK_ENR); | | while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) | | udelay(1); | | | | /* | | * configure the gasket: | | * RMII, 50 MHz, no loopback, no echo | | */ | | writel(1, fep->hwp + FEC_MIIGSK_CFGR); | | | | /* re-enable the gasket */ | | writel(2, fep->hwp + FEC_MIIGSK_ENR); | | udelay(10); | | if (!(readl(fep->hwp + FEC_MIIGSK_ENR) & 4)) { | | udelay(100); | | if (!(readl(fep->hwp + FEC_MIIGSK_ENR) & 4)) | | dev_err(&fep->pdev->dev, | | "switch to RMII failed!\n"); | | } | | } | | } | | | | /* ENET enable */ | | val = reg | (0x1 << 1); | | //根据条件设置网络为1G | | /* if phy work at 1G mode, set ENET RGMII speed to 1G */ | | if (fep->phy_dev && (fep->phy_dev->supported & | | (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) && | | fep->phy_interface == PHY_INTERFACE_MODE_RGMII && | | fep->phy_dev->speed == SPEED_1000) { | | val |= (0x1 << 5); //使能1000M模式 | | printk(KERN_WARNING "<tony> 1772 speed:1000\n"); | | } | | | | /* RX FIFO threshold setting for ENET pause frame feature | | * Only set the parameters after ticket TKT116501 fixed. | | * The issue has been fixed on Rigel TO1.1 and Arik TO1.2 | | */ | | if ((cpu_is_mx6q() && | | (mx6q_revision() >= IMX_CHIP_REVISION_1_2)) || | | (cpu_is_mx6dl() && | | (mx6dl_revision() >= IMX_CHIP_REVISION_1_1))) { | | writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM); | | writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL); | | writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM); | | writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL); | | | | /* OPD */ | | writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD); | | } | | | | if (cpu_is_mx6q() || cpu_is_mx6dl()) { | | /* enable endian swap */ | | val |= (0x1 << 8); | | /* enable ENET store and forward mode */ | | writel(0x1 << 8, fep->hwp + FEC_X_WMRK); | | } | | writel(val, fep->hwp + FEC_ECNTRL); | | | | writel(0, fep->hwp + FEC_R_DES_ACTIVE); | | | | /* Enable interrupts we wish to service */ | | if (cpu_is_mx6q() || cpu_is_mx6dl() || cpu_is_mx2() || cpu_is_mx3()) | | val = (FEC_1588_IMASK | FEC_DEFAULT_IMASK); | | else | | val = FEC_DEFAULT_IMASK; | | writel(val, fep->hwp + FEC_IMASK); | | } | | | | static struct ethtool_ops fec_enet_ethtool_ops = { <---------------+ | .get_settings = fec_enet_get_settings, | .set_settings = fec_enet_set_settings, | .get_drvinfo = fec_enet_get_drvinfo, | .get_link = ethtool_op_get_link, | }; | | | static const struct net_device_ops fec_netdev_ops = { <-------------------+ .ndo_open = fec_enet_open, ------------------+ .ndo_stop = fec_enet_close, | .ndo_start_xmit = fec_enet_start_xmit, | .ndo_set_multicast_list = set_multicast_list, | .ndo_change_mtu = eth_change_mtu, | .ndo_validate_addr = eth_validate_addr, | .ndo_tx_timeout = fec_timeout, | .ndo_set_mac_address = fec_set_mac_address, | .ndo_do_ioctl = fec_enet_ioctl, | #ifdef CONFIG_NET_POLL_CONTROLLER | .ndo_poll_controller = fec_enet_netpoll, | #endif | }; | | static int | fec_enet_open(struct net_device *ndev) <-------------------------------+ { struct fec_enet_private *fep = netdev_priv(ndev); struct fec_platform_data *pdata = fep->pdev->dev.platform_data; int ret; if (fep->use_napi) napi_enable(&fep->napi); /* I should reset the ring buffers here, but I don't yet know * a simple way to do that. */ clk_enable(fep->clk); ret = fec_enet_alloc_buffers(ndev); if (ret) return ret; /* Probe and connect to PHY when open the interface */ ret = fec_enet_mii_probe(ndev); ----------------------+ if (ret) { | fec_enet_free_buffers(ndev); | return ret; | } | | phy_start(fep->phy_dev); | netif_start_queue(ndev); | fep->opened = 1; | | ret = -EINVAL; | if (pdata->init && pdata->init(fep->phy_dev)) | return ret; | | return 0; | } | | kernel/drivers/net/fec.c | static int fec_enet_mii_probe(struct net_device *ndev) <------------+ { struct fec_enet_private *fep = netdev_priv(ndev); struct phy_device *phy_dev = NULL; char mdio_bus_id[MII_BUS_ID_SIZE]; char phy_name[MII_BUS_ID_SIZE + 3]; int phy_id; int dev_id = fep->pdev->id; fep->phy_dev = NULL; /* check for attached phy */ for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { if ((fep->mii_bus->phy_mask & (1 << phy_id))) continue; if (fep->mii_bus->phy_map[phy_id] == NULL) continue; if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) continue; if (dev_id--) continue; strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); break; } if (phy_id >= PHY_MAX_ADDR) { printk(KERN_INFO "%s: no PHY, assuming direct connection " "to switch\n", ndev->name); strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); phy_id = 0; } snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, fep->phy_interface); | +--------------------+ if (IS_ERR(phy_dev)) { | printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); | return PTR_ERR(phy_dev); | } | | /* mask with MAC supported features */ | if (cpu_is_mx6q() || cpu_is_mx6dl()) | phy_dev->supported &= PHY_BASIC_FEATURES; | // phy_dev->supported &= PHY_GBIT_FEATURES; | else | phy_dev->supported &= PHY_BASIC_FEATURES; | | /* enable phy pause frame for any platform */ | phy_dev->supported |= ADVERTISED_Pause; | | phy_dev->advertising = phy_dev->supported; | | fep->phy_dev = phy_dev; | fep->link = 0; | fep->full_duplex = 0; | | printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] " | "(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name, | fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev), | fep->phy_dev->irq); | | return 0; | } | | kernel/drivers/net/fec.c | static void fec_enet_adjust_link(struct net_device *ndev) <--------------+ { struct fec_enet_private *fep = netdev_priv(ndev); struct phy_device *phy_dev = fep->phy_dev; struct fec_platform_data *pdata = fep->pdev->dev.platform_data; unsigned long flags; int status_change = 0; spin_lock_irqsave(&fep->hw_lock, flags); /* Prevent a state halted on mii error */ if (fep->mii_timeout && phy_dev->state == PHY_HALTED) { phy_dev->state = PHY_RESUMING; goto spin_unlock; } /* Duplex link change */ if (phy_dev->link) { if (fep->full_duplex != phy_dev->duplex) { fec_restart(ndev, phy_dev->duplex); status_change = 1; } } /* Link on or off change */ if (phy_dev->link != fep->link) { fep->link = phy_dev->link; if (phy_dev->link) { fec_restart(ndev, phy_dev->duplex); if (!fep->tx_full) netif_wake_queue(ndev); } else fec_stop(ndev); status_change = 1; } spin_unlock: spin_unlock_irqrestore(&fep->hw_lock, flags); if (status_change) { if (!phy_dev->link && phy_dev && pdata && pdata->power_hibernate) pdata->power_hibernate(phy_dev); phy_print_status(phy_dev); } | } | | V /drivers/net/phy/phy.c void phy_print_status(struct phy_device *phydev) //这里使每次网线插拔之后会在串口打印输出的内容 { pr_info("PHY: %s - Link is %s", dev_name(&phydev->dev), phydev->link ? "Up" : "Down"); if (phydev->link) printk(KERN_CONT " - %d/%s", phydev->speed, DUPLEX_FULL == phydev->duplex ? "Full" : "Half"); printk(KERN_CONT "\n"); }