网口驱动相关结构体
// 不同平台的mac操作函数集结构体
struct rk_gmac_ops {
void (*set_to_rgmii)(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay);
void (*set_to_rmii)(struct rk_priv_data *bsp_priv);
void (*set_to_sgmii)(struct rk_priv_data *bsp_priv);
void (*set_to_qsgmii)(struct rk_priv_data *bsp_priv);
void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed);
void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed);
void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv);
};
// mac资源数据
struct stmmac_resources {
void __iomem *addr;
const char *mac;
int wol_irq;
int lpi_irq;
int irq;
};
struct plat_stmmacenet_data {
int bus_id;
int phy_addr;
int interface;
struct stmmac_mdio_bus_data *mdio_bus_data;
struct device_node *phy_node;
struct device_node *mdio_node;
struct stmmac_dma_cfg *dma_cfg;
int clk_csr;
int has_gmac;
int enh_desc;
int tx_coe;
int rx_coe;
int bugged_jumbo;
int pmt;
int force_sf_dma_mode;
int force_thresh_dma_mode;
int riwt_off;
int max_speed;
int maxmtu;
int multicast_filter_bins;
int unicast_filter_entries;
int tx_fifo_size;
int rx_fifo_size;
u32 rx_queues_to_use;
u32 tx_queues_to_use;
u8 rx_sched_algorithm;
u8 tx_sched_algorithm;
struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES];
struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
void (*fix_mac_speed)(void *priv, unsigned int speed);
int (*init)(struct platform_device *pdev, void *priv);
void (*exit)(struct platform_device *pdev, void *priv);
void (*get_eth_addr)(void *priv, unsigned char *addr);
struct mac_device_info *(*setup)(void *priv);
void *bsp_priv;
struct clk *stmmac_clk;
struct clk *pclk;
struct clk *clk_ptp_ref;
unsigned int clk_ptp_rate;
unsigned int clk_ref_rate;
struct reset_control *stmmac_rst;
struct stmmac_axi *axi;
int has_gmac4;
bool has_sun8i;
bool tso_en;
int mac_port_sel_speed;
bool en_tx_lpi_clockgating;
int has_xgmac;
};
设备树配置与解析
// RK3568设备树配置
gmac0: ethernet@fe2a0000 {
compatible = "rockchip,rk3568-gmac", "snps,dwmac-4.20a";
reg = <0x0 0xfe2a0000 0x0 0x10000>;
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_wake_irq";
rockchip,grf = <&grf>;
clocks = <&cru SCLK_GMAC0>, <&cru SCLK_GMAC0_RX_TX>,
<&cru SCLK_GMAC0_RX_TX>, <&cru CLK_MAC0_REFOUT>,
<&cru ACLK_GMAC0>, <&cru PCLK_GMAC0>,
<&cru SCLK_GMAC0_RX_TX>, <&cru CLK_GMAC0_PTP_REF>,
<&cru PCLK_XPCS>;
clock-names = "stmmaceth", "mac_clk_rx",
"mac_clk_tx", "clk_mac_refout",
"aclk_mac", "pclk_mac",
"clk_mac_speed", "ptp_ref",
"pclk_xpcs";
resets = <&cru SRST_A_GMAC0>;
reset-names = "stmmaceth";
snps,mixed-burst;
snps,tso;
snps,axi-config = <&gmac0_stmmac_axi_setup>;
snps,mtl-rx-config = <&gmac0_mtl_rx_setup>;
snps,mtl-tx-config = <&gmac0_mtl_tx_setup>;
status = "disabled";
mdio0: mdio {
compatible = "snps,dwmac-mdio";
};
gmac0_stmmac_axi_setup: stmmac-axi-config {
snps,wr_osr_lmt = <4>;
snps,rd_osr_lmt = <8>;
snps,blen = <0 0 0 0 16 8 4>;
};
gmac0_mtl_rx_setup: rx-queues-config {
snps,rx-queues-to-use = <1>;
queue0 {};
};
gmac0_mtl_tx_setup: tx-queues-config {
snps,tx-queues-to-use = <1>;
queue0 {};
};
};
&gmac0 {
phy-mode = "rgmii"; // rgmii或rmii pinctrl-0必须与之相匹配
clock_in_out = "output"; // output 时钟由MAC输入给PHY input与之相反
snps,reset-gpio = <&gpio2 RK_PC5 GPIO_ACTIVE_LOW>; // 用于复位PHY的GPIO
snps,reset-active-low; // 复位PHY的GPIO低电平有效
snps,reset-delays-us = <0 30000 150000>; // 复位PHY之前延时0ms 拉低维持的时间为30ms 拉高后延时150ms
assigned-clocks = <&cru SCLK_GMAC0_RX_TX>, <&cru SCLK_GMAC0>; // MAC时钟源
assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>, <&cru CLK_MAC0_2TOP>; // MAC父时钟源
assigned-clock-rates = <0>, <125000000>; // MAC时钟频率
pinctrl-names = "default";
pinctrl-0 = <&gmac0_miim // 设置PHY的各个引脚
&gmac0_tx_bus2
&gmac0_rx_bus2
&gmac0_rgmii_clk
&gmac0_rgmii_bus>;
tx_delay = <0x25>; // 收发延时 只有在rgmii模式下才需要配置该属性
rx_delay = <0x15>;
phy-handle = <&rgmii_phy0>; // 与mdio总线相连
status = "disabled";
};
&mdio0 {
rgmii_phy0: phy@2 { // 连接某个phy 以及phy硬件地址
compatible = "ethernet-phy-ieee802.3-c22"; // mdio和PHY之间的通信协议为22
reg = <0x2>; // phy硬件地址与与硬件上的phy地址相对应
};
};
// 电压域配置 查看原理图phy是从哪个io接出来的 vccio6 其电压为3.3V
&pmu_io_domains {
status = "okay";
pmuio1-supply = <&vcc3v3_pmu>;
pmuio2-supply = <&vcc3v3_pmu>;
vccio1-supply = <&vccio_acodec>;
vccio3-supply = <&vccio_sd>;
vccio4-supply = <&vcc_3v3>;
vccio5-supply = <&vcc_3v3>;
vccio6-supply = <&vcc_3v3>;
vccio7-supply = <&vcc_3v3>;
};
驱动流程详解
通过上面的设备树匹配了平台设备。路径:
kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
static const struct of_device_id rk_gmac_dwmac_match[] = {
{ .compatible = "rockchip,px30-gmac", .data = &px30_ops },
{ .compatible = "rockchip,rk1808-gmac", .data = &rk1808_ops },
{ .compatible = "rockchip,rk3128-gmac", .data = &rk3128_ops },
{ .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops },
{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
{ .compatible = "rockchip,rk3308-mac", .data = &rk3308_ops },
{ .compatible = "rockchip,rk3328-gmac", .data = &rk3328_ops },
{ .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops },
{ .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops },
{ .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops },
{ .compatible = "rockchip,rk3568-gmac", .data = &rk3568_ops },
{ .compatible = "rockchip,rv1108-gmac", .data = &rv1108_ops },
{ .compatible = "rockchip,rv1126-gmac", .data = &rv1126_ops },
{ }
};
MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);
static struct platform_driver rk_gmac_dwmac_driver = {
.probe = rk_gmac_probe,
.remove = rk_gmac_remove,
.driver = {
.name = "rk_gmac-dwmac",
.pm = &rk_gmac_pm_ops,
.of_match_table = rk_gmac_dwmac_match,
},
};
module_platform_driver(rk_gmac_dwmac_driver);
// RK3568 date操作函数集
rk_gmac_probe
-> of_device_get_match_data // 返回的是匹配的of_device_id的 rk_gmac_ops *data
-> stmmac_get_platform_resources(pdev, &stmmac_res); // 利用设备树中资源给stmmac_res赋值
-> stmmac_res->irq = platform_get_irq_byname(pdev, "macirq"); // 名字和设备树上的对应
-> stmmac_res->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
-> stmmac_res->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
-> platform_get_resource(pdev, IORESOURCE_MEM, 0); // 得到IORESOURCE_MEM资源
-> stmmac_res->addr = devm_ioremap_resource(&pdev->dev, res); // 映射后填充stmmac_res
-> plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); // 填充plat_stmmacenet_data *plat_dat
-> *mac = of_get_mac_address(np); // mac地址
-> plat->interface = of_get_phy_mode(np); // phy接口
-> of_property_read_u32(np, "max-speed", &plat->max_speed) // 最大速度
......
-> plat_dat->fix_mac_speed = rk_fix_speed;
-> plat_dat->get_eth_addr = rk_get_eth_addr;
-> plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data); // 分配填充bsp_priv然后放到plat_dat->bsp_priv
-> bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL); // 分配结构体
-> bsp_priv->phy_iface = of_get_phy_mode(dev->of_node); // 接口类型
-> of_property_read_string(dev->of_node, "clock_in_out", &strings);
-> of_property_read_u32(dev->of_node, "tx_delay", &value);
-> of_property_read_u32(dev->of_node, "rx_delay", &value);
-> rk_gmac_clk_init(plat_dat); // 时钟配置,填充到plat_dat中
-> rk_gmac_powerup(plat_dat->bsp_priv);
-> stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
-> ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv),MTL_MAX_TX_QUEUES,MTL_MAX_RX_QUEUES); // 分配一个net_device *ndev
-> stmmac_set_ethtool_ops(ndev); // 分配钩子函数
-> dev_set_drvdata(device, priv->dev); // 把 priv->dev == device->driver_data
-> stmmac_verify_args(); // 一些驱动参数
-> stmmac_hw_init(priv); // 初始化mac并得到能力
-> stmmac_mdio_register(ndev); // 注册mdio_bus
-> mdiobus_alloc // 申请一个mii_bus
-> new_bus->read = &stmmac_mdio_read; // 读写复位函数
-> new_bus->write = &stmmac_mdio_write;
-> new_bus->reset = &stmmac_mdio_reset;
-> of_mdiobus_register(new_bus, mdio_node); // 注册mdio_bus
-> mdiobus_register(mdio);
-> __mdiobus_register
-> device_register(&bus->dev); // 注册设备文件
-> bus->reset(bus); // 总线复位
-> mdiobus_scan(bus, i); // 循环扫描phy
-> phydev = get_phy_device(bus, addr, false); // 创建phy设备
-> get_phy_id
-> phy_device_register(phydev); // 注册phy设备
-> bus->state = MDIOBUS_REGISTERED; // 标志已经注册
-> register_netdev(ndev); // 注册ndev
-> dwmac_rk_create_loopback_sysfs(&pdev->dev); // 相关系统文件
小结
RK平台PHY驱动与NXP的整体框架对比大部分一致。
原作者:Bazinga bingo