STMicro ethernet driver note¶
Source File and Config¶
stmmac
source code files introduction.
interface to upper layer:
stmmac_main.o:
stmmac_netdev_ops
stmmac_ethtool.o:
stmmac_ethtool_ops
internal operations:
dwmac:
stmmac_ops
,stmmac_dma_ops
dwmac_lib.o
dwmac1000: dwmac1000_core.o dwmac1000_dma.o
dwmac100: dwmac100_core.o dwmac100_dma.o
norm_desc.o, enh_desc.o: dma descriptor,
stmmac_desc_ops
ring_mode.o, chain_mode.o:
stmmac_mode_ops
other: stmmac_mdio.o, mmc_core.o, stmmac_hwtstamp.o, stmmac_ptp.o
Kconfig options:
bus:
stmmac_pci.c | stmmac_platform.c
stmmac_platform.c (struct platform_driver stmmac_pltfr_driver)
dwmac:
dwmac-meson.c | dwmac-sunxi.c | dwmac-sti.c
header files
stmmac.h, common.h
descs.h, descs_com.h (dma descriptor)
dwmac100.h, dwmac1000.h, dwmac_dma.h
am_eth_reg.h, mmc.h, stmmac_ptp.h
Data Structure¶
4 ops in mac_device_info
:
struct mac_device_info
- stmmac_ops *mac; // dwmac*.c
- stmmac_dma_ops *dma; // dwmac*.c
- stmmac_desc_ops *desc; // *_desc.c
- stmmac_mode_ops *mode; // *_mode.c
e.g. priv->hw->mac->set_umac_addr()
use stmmac_ops dwmac1000_ops->set_umac_addr() at dwmac1000_core.c
struct stmmac_priv
, which is stmmac’s private_data of netdev
.:
void __iomem* ioaddr; // MMIO region base address
struct mac_device_info* hw; // 4 ops
struct plat_stmmacenet_data* plat; // struct device->platform_data
Control Flow¶
stmmac driver setting mac address¶
// in stmmac_main.c
stmmac_hw_setup()
=> priv->hw->mac->set_umac_addr() = dwmac1000_ops.set_umac_addr() = dwmac1000_set_umac_addr()
=> stmmac_set_mac_addr()
stmmac MMIO region¶
以下以 stmmac_set_mac_addr()
使用到的 MMIO region 為例子
1. MMIO region 透過 platform_get_resource()
得到, 並透過 ioremap()
轉成 virtual address 給 driver 使用.
// driver use MMIO region by accessing priv->ioaddr
stmmac_pltfr_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
addr = devm_ioremap_resource(dev, res);
...
stmmac_dvr_probe(..., void __iomem *addr)
priv->ioaddr = addr;
2. platform_get_resource()
透過 device tree 獲得 hard-coded 的 MMIO region address. platform_device
適用這套 API.
// platform_get_resource() get resource from Device Tree
// in arch/arm64/boot/dts/meson64_odroidc2.dts
ethmac: ethernet@0xc9410000{
compatible = "amlogic, gxbb-rgmii-dwmac";
reg = <0x0 0xc9410000 0x0 0x10000
0x0 0xc8834540 0x0 0x8>
...
}
platform_get_resource(pdev, IORESOURCE_MEM, 0): start=0xc9410000, size=0x10000
platform_get_resource(pdev, IORESOURCE_MEM, 1): start=0xc8834540, size=0x8
p.s. stmmac_set_mac_addr()
計算 MMIO offset 的方式
// MMIO offset
// in dwmac1000.h
GMAC_ADDR_HIGH(reg) = 0x40 + reg*8 if reg <= 15;
GMAC_ADDR_LOW(reg) = 0x44 + reg*8 if reg <= 15;
net_device_ops/NAPI transfer and receive¶
driver transfer data to device.
ndo_start_xmit()
receive data from parameter sk_buff
and transfer to dma_desc
?
data:
priv->cur_tx priv->dma_tx[entry].des2: dma_map_single(), skb_frag_dma_map() priv->tx_skbuff_dma[entry].buf: alias of priv->dma_tx[entry].des2 priv->tx_skbuff[entry]
functions:
priv->hw->dma ops: enable_dma_transmission(priv->ioaddr); priv->hw->desc ops: prepare_tx_desc(), set_tx_owner(), close_tx_desc() skb_frag_dma_map(): dma_map_page() from ``sk_buff's`` page.
所以, 有幾個重點的 dependency 先處理
dma mapping API: dma_map_single(), dma_map_page()
sk_buff intro:
stmmac hw datasheet? 我想看 TX/RX Ring 的 data structure
driver receive data from device.
stmmac_poll()
other net_device_ops¶
ndo_ioctl
stmmac_hwtstamp_ioctl()
phy_mii_ioctl(): forward to PHY
ioctl()
ndo_open, ndo_stop
ndo_poll_controller
ndo_tx_timeout: stmmac_tx_err(priv);
ndo_set_config: not supported (
-EOPNOTSUPP
)ndo_set_mac_address = NULL
ndo_change_mtu: dev->mtu = new_mtu; netdev_update_features(dev);
ndo_fix_features: check flags
ndo_set_rx_mode: priv->hw->mac->set_filter(dev, priv->synopsys_id);
more than net_device_ops
, recieve need interrupt
stmmac_interrupt()
=>stmmac_dma_interrupt()
: 應該是 interrupt handlerpriv->hw->dma->dma_interrupt()
確認是否 handle_tx/rx, 或者 transmit error. (handle_tx/rx 對應到 dwmac DMA 的 normal interrupt 的其中兩個種類)如果需要 handle_tx/rx, 則 trigger
__napi_schedule()
Other Info¶
platform bus abstraction¶
stmmac_platform.c
implement a platform driver stmmac_pltfr_driver
.
This driver is a wrapper of stmmac_driver
.
stmmac_main.c
registers this platform driver (stmmac_pltfr_driver
) at kernel module initialization.
stmmac_pltfr_driver
implements platform driver ops (ops of struct platform_driver
), like probe, remove, driver.shutdown, driver.pm_ops.
stmmac_pltfr_driver
’s ops depend on stmmac_driver
ops, just do some customization.
customizations:
MMIO region:
platform_get_resource()
,ioremap()
3 IRQs:
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");
struct device
’s platform_datastmmac_probe_config_dt()
platdata_copy_from_machine_data(device, plat);
setup_mac_addr(pdev, mac);
plat_stmmacenet_data->setup()
plat_stmmacenet_data->init()
dwmac-meson.c¶
implements 2 functions setup()
and fix_mac_speed()
setup functions to device’s platform_data plat_stmmacenet_data
:
platdata_copy_from_machine_data(const struct of_device_id *device, struct plat_stmmacenet_data *plat){
if (device->data) {
const struct stmmac_of_data *data = device->data;
plat->setup = data->setup;
plat->fix_mac_speed = data->fix_mac_speed;
...
}
}
usage about these functions:
1. priv->plat->fix_mac_speed()
2. custom setup function in platform_driver initialization.
stmmac_pltfr_probe() => stmmac_probe_config_dt() => platdata_copy_from_machine_data()
struct platform_device *pdev;
struct plat_stmmacenet_data *plat_dat = dev_get_platdata(&pdev->dev);
...
// custom setup function
plat_dat->setup()
dwmac DMA¶
source codes: dwmac_lib.c, dwmac_dma.h
DMA registers
CSR: control and status registers.
dwmac_dma.h
: DMA CSR Mapping(MMIO), fromDMA_BUS_MODE(0x1000)
toDMA_HW_FEATURE(0x1058)
. (CSR0 to CSR8, and … CSR?)CSR1: enable dma transmission
CSR7: mask interrupt and record interrupt type. (
DMA_INTR_ENA_*
)DMA status register (CSR5):
DMA_STATUS_*
DMA control register (CSR6):
DMA_CONTROL_*
DMA interrupt:
DMA_INTR_ENA_*
normal interrupt: 5 kinds, from NIE to ERE
abnormal interrupt: 10 kinds, from AIE to TSE