• 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
    ");                                                      
                                                                                                        
        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
    ");      |    |            
                else                                                                  |    |            
                    fep->ptimer_present = 1;                                          |    |            
            } else                                                                    |    |            
                printk(KERN_ERR "IEEE1588: failed to malloc memory
    ");               |    |            
        }                                                                             |    |            
                                                                                      |    |            
        /* 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?
    ");                                        
            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
    ");                                          |  |                 
            }                                                                      |  |                 
            else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {              |  |                 
                val |= (1 << 8);                                                   |  |                 
                printk("<tony> RMII
    ");                                           |  |                 
            }                                                                      |  |                 
            else {                                                                 |  |                 
                val &= ~(1 << 8);                                                  |  |                 
                printk("<tony> MII
    ");                                            |  |                 
            }                                                                      |  |                 
            //时能10M/100M支持                                                      |  |                 
            /* 10M or 100M */                                                      |  |                 
            if (fep->phy_dev && fep->phy_dev->speed == SPEED_100) {                |  |                 
                val &= ~(1 << 9);                                                  |  |                 
                printk("<tony> 1702, speed:100
    ");                                |  |                 
            }                                                                      |  |                 
            else {                                                                 |  |                 
                val |= (1 << 9);                                                   |  |                 
                printk("<tony> 1706, speed:10
    ");                                 |  |                 
            }                                                                      |  |                 
                                                                                   |  |                 
            /* 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!
    ");                           |  |                 
                }                                                                  |  |                 
            }                                                                      |  |                 
        }                                                                          |  |                 
                                                                                   |  |                 
        /* 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
    ");                       |  |                 
        }                                                                          |  |                 
                                                                                   |  |                 
        /* 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
    ", 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
    ", 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)
    ", 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 "
    ");                                                                         
    }                                                                                                   
  • 相关阅读:
    php1
    element ui
    webpack
    vue-router
    vue实例相关2
    vue实例相关
    js笔记2
    js笔记
    不找工作,你的简历也要更新!
    除了做测试,我们还能做些什么呢?
  • 原文地址:https://www.cnblogs.com/helloworldtoyou/p/5407515.html
Copyright © 2020-2023  润新知