stmmac ethernet
阅读原文时间:2021年04月21日阅读:1

学习笔记,网卡驱动

从这里看起stmmac_register_platform
注册一个平台驱动

const struct stmmac_of_data meson_dwmac_data = {
    .setup = meson_dwmac_setup,
    .fix_mac_speed = meson_dwmac_fix_mac_speed,
};

static const struct of_device_id stmmac_dt_ids[] = {
#ifdef CONFIG_DWMAC_MESON
    {.compatible = "amlogic, meson6-dwmac",},
    {.compatible = "amlogic, meson8-rmii-dwmac",},
    {.compatible = "amlogic, meson8m2-rgmii-dwmac",},
    {.compatible = "amlogic, meson8m2-rmii-dwmac",
     .data = &meson_dwmac_data},
    {.compatible = "amlogic, gxbb-rgmii-dwmac",
     .data = &meson_dwmac_data},
    {.compatible = "amlogic, gxbb-rmii-dwmac",
     .data = &meson_dwmac_data},
    {.compatible = "amlogic, meson8b-rgmii-dwmac",},
    {.compatible = "amlogic, meson8b-rmii-dwmac",
     .data = &meson_dwmac_data},
    {.compatible = "amlogic, meson6-rmii-dwmac",
     .data = &meson_dwmac_data},
#endif
#ifdef CONFIG_DWMAC_SUNXI
    {.compatible = "allwinner,sun7i-a20-gmac",
    .data = &sun7i_gmac_data},
#endif
#ifdef CONFIG_DWMAC_STI
    {.compatible = "st,stih415-dwmac",
    .data = &sti_gmac_data},
    {.compatible = "st,stih416-dwmac",
    .data = &sti_gmac_data},
    {.compatible = "st,stid127-dwmac",
    .data = &sti_gmac_data},
#endif
    /* SoC specific glue layers should come before generic bindings */
    {.compatible = "st,spear600-gmac"},
    {.compatible = "snps,dwmac-3.610"},
    {.compatible = "snps,dwmac-3.70a"},
    {.compatible = "snps,dwmac-3.710"},
    {.compatible = "snps,dwmac"},
    { /* sentinel */ }
}


    err = platform_driver_register(&stmmac_pltfr_driver);
struct platform_driver stmmac_pltfr_driver = {
    .probe = stmmac_pltfr_probe,
    .remove = stmmac_pltfr_remove,
    .driver = {
           .name = STMMAC_RESOURCE_NAME,
           .owner = THIS_MODULE,
#ifdef CONFIG_PM
           .shutdown = stmmac_pltfr_shutdown,
#endif
           .pm = &stmmac_pltfr_pm_ops,
           .of_match_table = of_match_ptr(stmmac_dt_ids),
           },
}

stmmac_pltfr_probe探测函数
首先从dtd中获取资源

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pr_debug(“resource:res:%p”,(struct resource*)res);
addr = devm_ioremap_resource(dev, res);
if (IS_ERR(addr))
return PTR_ERR(addr);
pr_info(“****************ethernet base addr is %p\n”, addr);
plat_dat = dev_get_platdata(&pdev->dev);
if (pdev->dev.of_node) {
if (!plat_dat)
plat_dat = devm_kzalloc(&pdev->dev,
sizeof(struct
plat_stmmacenet_data),
GFP_KERNEL);
if (!plat_dat) {
pr_err(“%s: ERROR: no memory”, func);
return -ENOMEM;
}

    ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
    if (ret) {
        pr_err("%s: main dt probe failed", __func__);
        return ret;
    }
}
/* Custom setup (if needed) */
if (plat_dat->setup) {
    plat_dat->bsp_priv = plat_dat->setup(pdev);

    pr_info("%s,%d\n",__FUNCTION__,__LINE__);
    if (IS_ERR(plat_dat->bsp_priv))
        return PTR_ERR(plat_dat->bsp_priv);
}

/* Custom initialisation (if needed) */
if (plat_dat->init) {
    ret = plat_dat->init(pdev, plat_dat->bsp_priv);

    pr_info("%s,%d\n",__FUNCTION__,__LINE__);
    if (unlikely(ret))
        return ret;
}
priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);


中断获取:
    priv->dev->irq = platform_get_irq_byname(pdev, "macirq");

    priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");

    priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
保存平台数据:
    platform_set_drvdata(pdev, priv->dev);

接下来看重要的函数,主要用来初化网络数据结构体成员,初始化,注册
    priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);

/**
 * stmmac_dvr_probe
 * @device: device pointer
 * @plat_dat: platform data pointer
 * @addr: iobase memory address
 * Description: this is the main probe function used to
 * call the alloc_etherdev, allocate the priv structure.
 */
struct stmmac_priv *stmmac_dvr_probe(struct device *device,
                     struct plat_stmmacenet_data *plat_dat,
                     void __iomem *addr)
{
    int ret = 0;
    struct net_device *ndev = NULL;
    struct stmmac_priv *priv;
    //申请结构体
    ndev = alloc_etherdev(sizeof(struct stmmac_priv));
    if (!ndev)
        return NULL;
       //设置父设备
    SET_NETDEV_DEV(ndev, device);
    //取出私有数据
    priv = netdev_priv(ndev);
    priv->device = device;
    priv->dev = ndev;
     //通用的设置
    ether_setup(ndev);
     //供ethtool使用,它是一个实用工具
    stmmac_set_ethtool_ops(ndev);
    priv->pause = pause;
    priv->plat = plat_dat;
    priv->ioaddr = addr;
    priv->dev->base_addr = (unsigned long)addr;

    /* Verify driver arguments */
    stmmac_verify_args();

    /* Override with kernel parameters if supplied XXX CRS XXX
     * this needs to have multiple instances
     */
    if ((phyaddr >= 0) && (phyaddr <= 31))
        priv->plat->phy_addr = phyaddr;
//时钟
#ifdef CONFIG_DWMAC_MESON
    priv->stmmac_clk = devm_clk_get(priv->device, "ethclk81");
pr_info("%s,%d\n",__FUNCTION__,__LINE__);

#else
    priv->stmmac_clk = devm_clk_get(priv->device, STMMAC_RESOURCE_NAME);
pr_info("%s,%d\n",__FUNCTION__,__LINE__);

#endif
    if (IS_ERR(priv->stmmac_clk)) {
        dev_warn(priv->device, "%s: warning: cannot get CSR clock\n",
             __func__);
        ret = PTR_ERR(priv->stmmac_clk);
        goto error_clk_get;
    }
    clk_prepare_enable(priv->stmmac_clk);
#ifdef CONFIG_DWMAC_MESON
    priv->stmmac_rst = devm_reset_control_get(priv->device,
                          "ethpower");
#else
    priv->stmmac_rst = devm_reset_control_get(priv->device,
                          STMMAC_RESOURCE_NAME);

#endif
    if (IS_ERR(priv->stmmac_rst)) {
        if (PTR_ERR(priv->stmmac_rst) == -EPROBE_DEFER) {
            ret = -EPROBE_DEFER;
            goto error_hw_init;
        }
        dev_info(priv->device, "no reset control found\n");
        priv->stmmac_rst = NULL;
    }
    if (priv->stmmac_rst)
        reset_control_deassert(priv->stmmac_rst);

    //硬件初始化,主要是mac
    /* Init MAC and get the capabilities */
    ret = stmmac_hw_init(priv);
    if (ret)
        goto error_hw_init;
//操作函数,.ndo_start_xmit 成员用于上层向底层发送数据时调用
    ndev->netdev_ops = &stmmac_netdev_ops;

    ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
                NETIF_F_RXCSUM;
    ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
    ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
#ifdef STMMAC_VLAN_TAG_USED
    /* Both mac100 and gmac support receive VLAN tag detection */
    ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
#endif
    priv->msg_enable = netif_msg_init(debug, default_msg_level);

    if (flow_ctrl)
        priv->flow_ctrl = FLOW_AUTO;    /* RX/TX pause on */

    /* Rx Watchdog is available in the COREs newer than the 3.40.
     * In some case, for example on bugged HW this feature
     * has to be disable and this can be done by passing the
     * riwt_off field from the platform.
     */
    if ((priv->synopsys_id >= DWMAC_CORE_3_50) && (!priv->plat->riwt_off)) {
        priv->use_riwt = 1;
        pr_debug(" Enable RX Mitigation via HW Watchdog Timer\n");
    }

    //用NAPI实现,IRQ+POLL(SOFTIRQ)
    netif_napi_add(ndev, &priv->napi, stmmac_poll, 64);

    spin_lock_init(&priv->lock);
    spin_lock_init(&priv->tx_lock);
    //注册核心结构体
    ret = register_netdev(ndev);
    if (ret) {
        pr_err("%s: ERROR %i registering the device\n", __func__, ret);
        goto error_netdev_register;
    }
#ifdef CONFIG_DWMAC_MESON
    priv->stmmac_clk = clk_get(priv->device, "ethclk81");
#else
    priv->stmmac_clk = clk_get(priv->device, STMMAC_RESOURCE_NAME);
#endif
    if (IS_ERR(priv->stmmac_clk)) {
        pr_warn("%s: warning: cannot get CSR clock\n", __func__);
        goto error_clk_get;
    }

    /* If a specific clk_csr value is passed from the platform
     * this means that the CSR Clock Range selection cannot be
     * changed at run-time and it is fixed. Viceversa the driver'll try to
     * set the MDC clock dynamically according to the csr actual
     * clock input.
     */
    if (!priv->plat->clk_csr)
        stmmac_clk_csr_set(priv);
    else
        priv->clk_csr = priv->plat->clk_csr;

    stmmac_check_pcs_mode(priv);

    if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
        priv->pcs != STMMAC_PCS_RTBI) {
        //MII连接MAC与PHY,扫描PHY设备,从寄存器读出PHY_ID,默认寄存器的值为ffffffff,这里模块为20142014
 /*PHY的读写接口
 new_bus->read = &stmmac_mdio_read;
 new_bus->write = &stmmac_mdio_write;
 new_bus->reset = &stmmac_mdio_reset;*/
        /* MDIO bus Registration */
        ret = stmmac_mdio_register(ndev);

        pr_info("%s,%d\n",__FUNCTION__,__LINE__);
        if (ret < 0) {
            pr_debug("%s: MDIO bus (id: %d) registration failed",
                 __func__, priv->plat->bus_id);
            goto error_mdio_register;
        }
    }
#ifdef CONFIG_DWMAC_MESON
    gmac_create_sysfs(priv->mii->phy_map[priv->plat->phy_addr],
        priv->ioaddr);
#endif
    return priv;

error_mdio_register:
    unregister_netdev(ndev);
error_netdev_register:
    netif_napi_del(&priv->napi);
error_hw_init:
    clk_disable_unprepare(priv->stmmac_clk);
error_clk_get:
    free_netdev(ndev);

    return NULL;
}

网卡刚起来时是关闭的,要用命令去打开,ifconfig eth0 up
时调用net_device_ops的.ndo_open,这里为stmmac_open

/**
 *  stmmac_open - open entry point of the driver
 *  @dev : pointer to the device structure.
 *  Description:
 *  This function is the open entry point of the driver.
 *  Return value:
 *  0 on success and an appropriate (-)ve integer as defined in errno.h
 *  file on failure.
 */
static int stmmac_open(struct net_device *dev)
{
//获取私用数据
    struct stmmac_priv *priv = netdev_priv(dev);
    int ret;
//检测mac addr
    stmmac_check_ether_addr(priv);

    if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
        priv->pcs != STMMAC_PCS_RTBI) {
        //初始化PHY设备
        ret = stmmac_init_phy(dev);
        if (ret) {
            pr_err("%s: Cannot attach to PHY (error: %d)\n",
                   __func__, ret);
            goto phy_error;
        }
    } else {
        pr_debug("not call stmmac_init_phy\n");
    }
    /* Extra statistics */
    memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats));
    priv->xstats.threshold = tc;

    /* Create and initialize the TX/RX descriptors chains. */
    priv->dma_tx_size = STMMAC_ALIGN(dma_txsize);
    priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize);
    priv->dma_buf_sz = STMMAC_ALIGN(buf_sz);
    pr_debug("open eth0, alloc desc resource\n");
    ret = alloc_dma_desc_resources(priv);
    if (ret < 0) {
        pr_err("%s: DMA descriptors allocation failed\n", __func__);
        goto dma_desc_error;
    }
   /*设置了TX用的定时器回调函数stmmac_init_tx_coalesce,清理发送流程数据*/
    ret = stmmac_hw_setup(dev);
    if (ret < 0) {
        pr_err("%s: Hw setup failed\n", __func__);
        goto init_error;
    }

    if (priv->phydev)
        phy_start(priv->phydev);
//注册中断请求线IRQ,中断处理函数stmmac_interrupt用于接收DMA数据,配合NAPI处理
    /* Request the IRQ lines */
    ret = request_irq(dev->irq, stmmac_interrupt,
              IRQF_SHARED, dev->name, dev);
    if (unlikely(ret < 0)) {
        pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n",
               __func__, dev->irq, ret);
        goto init_error;
    }

    /* Request the Wake IRQ in case of another line is used for WoL */
    if (priv->wol_irq != dev->irq) {
        ret = request_irq(priv->wol_irq, stmmac_interrupt,
                  IRQF_SHARED, dev->name, dev);
        if (unlikely(ret < 0)) {
            pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n",
                   __func__, priv->wol_irq, ret);
            goto wolirq_error;
        }
    }

    /* Request the IRQ lines */
    if (priv->lpi_irq != -ENXIO) {
        ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED,
                  dev->name, dev);
        if (unlikely(ret < 0)) {
            pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n",
                   __func__, priv->lpi_irq, ret);
            goto lpiirq_error;
        }
    }

    napi_enable(&priv->napi);
    netif_start_queue(dev);

    return 0;

lpiirq_error:
    if (priv->wol_irq != dev->irq)
        free_irq(priv->wol_irq, dev);
wolirq_error:
    free_irq(dev->irq, dev);

init_error:
    free_dma_desc_resources(priv);
dma_desc_error:
    if (priv->phydev)
        phy_disconnect(priv->phydev);
phy_error:
    clk_disable_unprepare(priv->stmmac_clk);

    return ret;
}

看初始化PHY部分,注册了一个回调函数,用于检测网口连接状态
/**
* stmmac_init_phy - PHY initialization
* @dev: net device structure
* Description: it initializes the driver’s PHY state, and attaches the PHY
* to the mac driver.
* Return value:
* 0 on success
*/
static int stmmac_init_phy(struct net_device *dev)
….

phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface);

//显示网络连接状态,刚接上或拨出网线时打印,处理相应环境
phy_print_status(phydev);

中断处理函数
/**
* stmmac_interrupt - main ISR
* @irq: interrupt number.
* @dev_id: to pass the net device pointer.
* Description: this is the main driver interrupt service routine.
* It calls the DMA ISR and also the core ISR to manage PMT, MMC, LPI
* interrupts.
*/
static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
{
struct net_device dev = (struct net_device )dev_id;
struct stmmac_priv *priv = netdev_priv(dev);

if (priv->irq_wake)
    pm_wakeup_event(priv->device, 0);

if (unlikely(!dev)) {
    pr_err("%s: invalid dev pointer\n", __func__);
    return IRQ_NONE;
}

/* To handle GMAC own interrupts */
if (priv->plat->has_gmac) {
    int status = priv->hw->mac->host_irq_status((void __iomem *)
                            dev->base_addr,
                            &priv->xstats);
    if (unlikely(status)) {
        /* For LPI we need to save the tx status */
        if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE)
            priv->tx_path_in_lpi_mode = true;
        if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE)
            priv->tx_path_in_lpi_mode = false;
    }
}

/* To handle DMA interrupts */
stmmac_dma_interrupt(priv);

return IRQ_HANDLED;

}`

/**
 * stmmac_dma_interrupt: DMA ISR
 * @priv: driver private structure
 * Description: this is the DMA ISR. It is called by the main ISR.
 * It calls the dwmac dma routine to understand which type of interrupt
 * happened. In case of there is a Normal interrupt and either TX or RX
 * interrupt happened so the NAPI is scheduled.
 */
static void stmmac_dma_interrupt(struct stmmac_priv *priv)
{
    int status;

    status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats);
    if (likely((status & handle_rx)) || (status & handle_tx)) {
        if (likely(napi_schedule_prep(&priv->napi))) {
            stmmac_disable_dma_irq(priv);
            //加入poll流程
            __napi_schedule(&priv->napi);
        }
    }
    if (unlikely(status & tx_hard_error_bump_tc)) {
        /* Try to bump up the dma threshold on this failure */
        if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) {
            tc += 64;
            priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
            priv->xstats.threshold = tc;
        }
    } else if (unlikely(status == tx_hard_error))
        stmmac_tx_err(priv);
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章