stmmac ethernet

来源:互联网 发布:java变量怎么初始化 编辑:程序博客网 时间:2024/06/07 06:26

学习笔记,网卡驱动
流程图

从这里看起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);}
0 0
原创粉丝点击