从这里看起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);
}