瑞芯微Rockchip开发者社区
直播中

石玉兰

8年用户 1526经验值
私信 关注
[经验]

RK3568 PHY驱动框架解析

网口驱动相关结构体

// 不同平台的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); // 相关系统文件

小结

RK平台PHY驱动与NXP的整体框架对比大部分一致。

原作者:Bazinga bingo

更多回帖

发帖
×
20
完善资料,
赚取积分