Linux内核(五) [ RK3568 ] PHY驱动框架解析 —— MDIO总线

平台:RK3568 内核版本4.19.194

文章目录

    • 网口驱动相关结构体
    • 设备树配置与解析
    • 驱动流程详解

网口驱动相关结构体

// 不同平台的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";
        #address-cells = <0x1>;
        #size-cells = <0x0>;
    };

    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);                             // 相关系统文件

小结
1、RK平台PHY驱动与NXP的整体框架对比大部分一致,具体驱动流程可以参考NXP-phy驱动详解
2、驱动最后调用dwmac_rk_create_loopback_sysfs构建检测MAC端TX/RX延时的相关系统文件,后续会更有关TX/RX延时的代码解析以及自动获取延时的文章

你可能感兴趣的:(Linux内核,linux,phy,网络,nxp,arm)