Linux内核(六)[ RK3568 ] 手动/自动调整千兆网口延时TX RX

文章目录

    • 背景
    • 手动测试RX TX的延时值
      • 节点确认
      • 使用方法
      • 扫描delayline窗口效果图
      • 代码解析
        • 设备树配置
        • 相关结构体
    • 自动扫描延时
      • 驱动代码解析
      • 扫描函数解析

背景

最近在调试RK系列的网口,出现网口丢包很严重,或者获取不到IP(手动获取ip也无法ping通外网和内网)等。出现这类问题很大可能是MAC或PHY的延时出现问题,造成收发数据丢包。这时手动调整PHY芯片寄存器收发延时值或者MAC延时值(设备树节点里TX/RX值)很麻烦。我在测试过程中出现由于PCB的原因造成每块板子的延时值不同,需要按照每块板子进行网口延时调整,这样工作量就很大。
为了解决这类问题,RK代码添加回环测试进行手动测试RX/TX延时值,并且集成自动扫描延时值的功能,大大方便调试。(原理:固定PHY芯片的延时(可以将收发延时关掉),调整MAC延时)接下来讲解下相关代码以及使用方法。

手动测试RX TX的延时值

代码实现部分都在 drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c 文件
Kernel-4.4 和 Kernel-3.10版本,测试RX TX 补丁
调整延时值TX/RX补丁资料
Kernel-4.19和之后的版本本身已经包含这部分代码
注:代码需要根据实际的phy进行调整(不通用),不通用的地方代码里有解析

节点确认

新固件(内核)会生成几个sysfs节点,在/sys/devices/platform/fe010000.ethernet/目录下生成几个节点:
在这里插入图片描述

使用方法

测试前需要拔掉网线

扫描delayline窗口效果图

Linux内核(六)[ RK3568 ] 手动/自动调整千兆网口延时TX RX_第1张图片
Linux内核(六)[ RK3568 ] 手动/自动调整千兆网口延时TX RX_第2张图片

代码解析

设备树配置

   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";
    clock_in_out = "output";

    snps,reset-gpio = <&gpio2 RK_PC5 GPIO_ACTIVE_LOW>;            //RK_PB1    RK_PC5
    snps,reset-active-low;
    snps,reset-delays-us = <0 30000 150000>;

    assigned-clocks = <&cru SCLK_GMAC0_RX_TX>, <&cru SCLK_GMAC0>;
    assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>, <&cru CLK_MAC0_2TOP>;
    assigned-clock-rates = <0>, <125000000>;

    pinctrl-names = "default";
    pinctrl-0 = <&gmac0_miim
             &gmac0_tx_bus2
             &gmac0_rx_bus2
             &gmac0_rgmii_clk
             &gmac0_rgmii_bus>;

    tx_delay = <0x00>;            // TX 发送延时,便于测试
    rx_delay = <0x00>;            // RX 接受延时,便于测试

    phy-handle = <&rgmii_phy0>;
    status = "disabled";
};

相关结构体

路径:drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c

// lb私有数据
struct dwmac_rk_lb_priv {
    struct dma_desc *dma_tx;
    dma_addr_t dma_tx_phy;
    struct sk_buff *tx_skbuff;
    dma_addr_t tx_skbuff_dma;
    unsigned int tx_skbuff_dma_len;

    struct dma_desc *dma_rx ____cacheline_aligned_in_smp;
    dma_addr_t dma_rx_phy;
    struct sk_buff *rx_skbuff;
    dma_addr_t rx_skbuff_dma;
    u32 rx_tail_addr;
    u32 tx_tail_addr;

    unsigned int dma_buf_sz;        // dma mtu大小
    unsigned int buf_sz;            // RX 接受FIFO大小

    int type;          // 回环测试类型(LOOPBACK_TYPE_GMAC LOOPBACK_TYPE_PHY)  drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c  phy_lb_scan_store
    int speed;         // 测试速度(10 100 1000) drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c  phy_lb_scan_store
    struct dwmac_rk_packet_attrs *packet;

    unsigned int actual_size;
    int scan;           // 扫描标志位  drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c  phy_lb_scan_store
    int sysfs;          // 系统文件标志位  drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c  phy_lb_scan_store
    u32 id;
    int tx;
    int rx;
    int final_tx;
    int final_rx;
};

// 接口模式设置   根据设备树phy-mode = "rgmii"定义
typedef enum {
    PHY_INTERFACE_MODE_NA,
    PHY_INTERFACE_MODE_INTERNAL,
    PHY_INTERFACE_MODE_MII,
    PHY_INTERFACE_MODE_GMII,
    PHY_INTERFACE_MODE_SGMII,
    PHY_INTERFACE_MODE_TBI,
    PHY_INTERFACE_MODE_REVMII,
    PHY_INTERFACE_MODE_RMII,
    PHY_INTERFACE_MODE_RGMII,
    PHY_INTERFACE_MODE_RGMII_ID,
    PHY_INTERFACE_MODE_RGMII_RXID,
    PHY_INTERFACE_MODE_RGMII_TXID,
    PHY_INTERFACE_MODE_RTBI,
    PHY_INTERFACE_MODE_SMII,
    PHY_INTERFACE_MODE_XGMII,
    PHY_INTERFACE_MODE_MOCA,
    PHY_INTERFACE_MODE_QSGMII,
    PHY_INTERFACE_MODE_TRGMII,
    PHY_INTERFACE_MODE_1000BASEX,
    PHY_INTERFACE_MODE_2500BASEX,
    PHY_INTERFACE_MODE_RXAUI,
    PHY_INTERFACE_MODE_XAUI,
    /* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */
    PHY_INTERFACE_MODE_10GKR,
    PHY_INTERFACE_MODE_MAX,
} phy_interface_t;

路径: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,
    },
};

static const struct net_device_ops stmmac_netdev_ops = {
    .ndo_open = stmmac_open,
    .ndo_start_xmit = stmmac_xmit,
    .ndo_stop = stmmac_release,
    .ndo_change_mtu = stmmac_change_mtu,
    .ndo_fix_features = stmmac_fix_features,
    .ndo_set_features = stmmac_set_features,
    .ndo_set_rx_mode = stmmac_set_rx_mode,
    .ndo_tx_timeout = stmmac_tx_timeout,
    .ndo_do_ioctl = stmmac_ioctl,
    .ndo_setup_tc = stmmac_setup_tc,
    .ndo_select_queue = stmmac_select_queue,
#ifdef CONFIG_NET_POLL_CONTROLLER
    .ndo_poll_controller = stmmac_poll_controller,
#endif
    .ndo_set_mac_address = stmmac_set_mac_address,
};

// include/linux/device.h
#define DEVICE_ATTR_RW(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
    
// include/linux/sysfs.h
#define __ATTR_RO(_name) {                      \
    .attr   = { .name = __stringify(_name), .mode = 0444 },     \
    .show   = _name##_show,                     \
}
#define __ATTR_WO(_name) {                      \
    .attr   = { .name = __stringify(_name), .mode = 0200 },     \
    .store  = _name##_store,                    \
}
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
 
// drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
rk_gmac_probe
    -> stmmac_dvr_probe
        -> ndev->netdev_ops = &stmmac_netdev_ops;    // 网络设备的操作函数
    -> dwmac_rk_create_loopback_sysfs        // 初始化系统下的文件创建
        -> device_create_file(device, &dev_attr_rgmii_delayline);  // 创建rgmii_delayline
            -> static DEVICE_ATTR_RW(rgmii_delayline);
        -> device_create_file(device, &dev_attr_mac_lb);           // 创建mac_lb
            -> static DEVICE_ATTR_WO(mac_lb);
        -> device_create_file(device, &dev_attr_phy_lb);           // 创建phy_lb
            -> static DEVICE_ATTR_WO(phy_lb);
        -> device_create_file(device, &dev_attr_phy_lb_scan);      // 创建phy_lb_scan
            -> static DEVICE_ATTR_WO(phy_lb_scan);

// drivers/net/ethernet/stmicro/stmmac/dwmac-rk-tool.c
// echo 1000 > phy_lb_scan
phy_lb_scan_store
    -> struct net_device *ndev = dev_get_drvdata(dev);    // 网络设备
    -> struct dwmac_rk_lb_priv *lb_priv;                  // lb私有数据
    -> lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL);   // 申请dwmac_rk_lb_priv结构体大小的空间
    -> ret = kstrtoint(buf, 0, &speed);                   // echo 1000 > phy_lb_scan  (1000赋值speed)
    -> lb_priv->sysfs = 1;                                // 系统文件标志位为1
    -> lb_priv->type = LOOPBACK_TYPE_PHY;                 // 类型回环PHY
    -> lb_priv->speed = speed;                            // 速度设置1000
    -> lb_priv->scan = 1;                                 // 扫描
    -> dwmac_rk_loopback_run(priv, lb_priv);
        -> phy_iface = dwmac_rk_get_phy_interface(priv);  // 获取phy接口  设备树phy-mode = "rgmii"  phy_iface=8  "PHY_INTERFACE_MODE_RGMII"
        -> ndev_up = ndev->flags & IFF_UP;      // 查看网络设备是否正常
        -> ndev->netdev_ops->ndo_stop(ndev);    // stmmac_release 关闭驱动的入口节点
        -> priv->plat->stmmac_rst               
        -> priv->mii->reset
        -> priv->plat->stmmac_rst
        -> usleep_range(100000, 200000);        // 等待phy和控制器准备
        -> dwmac_rk_init                        // 设置DMA FIFO
           -> lb_priv->dma_buf_sz = 1536;         // mtu大小 1500 
           -> lb_priv->buf_sz                  // rx fifo大小
           -> dwmac_rk_alloc_dma_desc_resources   // dma描述符申请
           -> dwmac_rk_init_dma_desc_rings       // 初始化dma描述符
           -> dwmac_rk_init_dma_engine           // dma配置、TX通道配置、RX通道配置
       -> dwmac_rk_set_loopback                   // 设置回环参数以及使能
           -> dwmac_rk_set_phy_loopback           // type = LOOPBACK_TYPE_PHY
               -> dwmac_rk_enable_phy_loopback    // enable = 1  根据传输速度写mac寄存器
                   -> val = mdiobus_read(priv->mii, 2, MII_BMCR);    // 读phy地址2的寄存器0  所有寄存器存在mii.h里,注意:phy的硬件地址一定要对
                   -> mdiobus_write(priv->mii, 2, MII_BMCR, val);    // 设置phy寄存器0为回环模式,写入,这部分每块phy芯片回环值不同需看手册确定是否写入正确
       -> dwmac_rk_loopback_delayline_scan(priv, lb_priv);     // lb_priv->scan=1   
           -> dwmac_rk_delayline_scan               // delayline窗口扫描
               -> 循环发送数据
    -> kfree(lb_priv);                                    // 释放结构体

注:代码里提到的回环测试里val值可以参考AR8035芯片解析

循环发送解析

static int dwmac_rk_loopback_delayline_scan(struct stmmac_priv *priv,
                        struct dwmac_rk_lb_priv *lb_priv)
{
    if (lb_priv->sysfs)
        return dwmac_rk_delayline_scan(priv, lb_priv);
    else
        return dwmac_rk_delayline_scan_cross(priv, lb_priv);
}

// lb_priv->sysfs = 1
#define MAX_DELAYLINE 0x7f
static int dwmac_rk_delayline_scan(struct stmmac_priv *priv,
                   struct dwmac_rk_lb_priv *lb_priv)
{
    int tx, rx, tx_sum, rx_sum, count;
    int tx_mid, rx_mid;
    int ret = -ENXIO;

    tx_sum = 0;
    rx_sum = 0;
    count = 0;
    
    // MAX_DELAYLINE = 0x7f;窗口扫描打印,具体扫描dwmac_rk_loopback_with_identify 
    // 在循环过程中tx范围0x00-0x7f,rx范围0x00-0x7f,组合循环测试
    // mac发数据给phy芯片,phy芯片回复回来的值进行检测对比
    // 根据延时发送接收数据无误成功,则返回0
    // 具体过程会再开篇分析整个循环过程
    for (rx = 0x0; rx <= MAX_DELAYLINE; rx++) {              
        printk(KERN_CONT "RX(0x%02x):", rx);
        for (tx = 0x0; tx <= MAX_DELAYLINE; tx++) {
            if (!dwmac_rk_loopback_with_identify(priv,
                lb_priv, tx, rx)) {
                tx_sum += tx;
                rx_sum += rx;
                count++;
                printk(KERN_CONT "O");
            } else {
                printk(KERN_CONT " ");
            }
        }
        printk(KERN_CONT "\n");
    }
    // 将所有回环测试成功的延时值相加取平均值,作为最后确定的延时值
    if (tx_sum && rx_sum && count) {
        tx_mid = tx_sum / count;
        rx_mid = rx_sum / count;
        if (dwmac_rk_delayline_is_valid(tx_mid, rx_mid)) {
            lb_priv->final_tx = tx_mid;
            lb_priv->final_rx = rx_mid;
            ret = 0;
        }
    }

    if (ret)
        pr_err("\nCan't find suitable delayline\n");
    else
        pr_info("\nFind suitable tx_delay = 0x%02x, rx_delay = 0x%02x\n",
            lb_priv->final_tx, lb_priv->final_rx);

    return ret;
}

自动扫描延时

补丁(通用,在kernel:4.19.194测试okay )
自动扫描延时值补丁资料
注:该补丁重点针对内核版本为4.19和4.19之后的内核,没有CONFIG_DWMAC_RK_AUTO_DELAYLINE自动扫描配置项。可参考此补丁。

开启自动扫描CONFIG_DWMAC_RK_AUTO_DELAYLINE=y
开辟两个空间用于存放TX/RX延时
Linux内核(六)[ RK3568 ] 手动/自动调整千兆网口延时TX RX_第3张图片
Linux内核(六)[ RK3568 ] 手动/自动调整千兆网口延时TX RX_第4张图片

驱动代码解析

 // arch/arm64/configs/rockchip_linux_defconfig
 CONFIG_DWMAC_RK_AUTO_DELAYLINE=y
 
 // drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
 // 烧写的时候判断值有没有效,无效扫描,有效跳过扫描
 #ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE
    if (!priv->delayline_scanned) {
            if (dwmac_rk_get_rgmii_delayline_from_vendor(priv)){
                    priv->delayline_scanned = true;
                    schedule_delayed_work(&priv->scan_dwork, msecs_to_jiffies(8000));
            }else{
                // TX/RX值有效
                priv->delayline_scanned = true;
            }
    }
#endif

// 任务
#ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE
       INIT_DELAYED_WORK(&priv->scan_dwork, stmmac_scan_delayline_dwork);
#endif

// 扫描delayline窗口得到延时值
#ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE
static void stmmac_scan_delayline_dwork(struct work_struct *work)
{
       struct stmmac_priv *priv = container_of(work, struct stmmac_priv,
                                               scan_dwork.work);

       dwmac_rk_search_rgmii_delayline(priv);
};
#endif

// include/linux/soc/rockchip/rk_vendor_storage.h
// 用RKDevInfoWriteTool工具新开两个空间
#define LAN_RGMII_DL_ID         16        // eth0 mac地址
#define EINK_VCOM_ID            17        // eth1 mac地址
#define ETH0_DELAY_ID           18        // 用于存放eth0 TX/RX延时值
#define ETH1_DELAY_ID           19        // 用于存放eth1 TX/RX延时值

#ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE
// 从vendor区获取延时值设置到对应的rgmii中
int dwmac_rk_get_rgmii_delayline_from_vendor(struct stmmac_priv *priv)
{
       int phy_iface = dwmac_rk_get_phy_interface(priv);  // 获取phy接口类型
       unsigned char delayline[2];
       int ret;
       int vendor_id;

        if(!strcmp(priv->dev->name , "eth0"))             // 获取传进来的phy是哪一个,赋值对应的vendor_id 
            vendor_id = ETH0_DELAY_ID;
        else if(!strcmp(priv->dev->name , "eth1"))
            vendor_id = ETH1_DELAY_ID;

       if (phy_iface != PHY_INTERFACE_MODE_RGMII &&
           phy_iface != PHY_INTERFACE_MODE_RGMII_ID)
               return 0;

       memset(delayline, 0x0, sizeof(delayline));       // 为delayline窗口申请空间
       ret = rk_vendor_read(vendor_id, delayline, 2);   // 读vendor_id 里delayline[0], delayline[1]值
       if (ret == 2 &&
           dwmac_rk_delayline_is_valid(delayline[0], delayline[1])) {   // 判断TX/RX值有效,有效范围00-7f
               pr_info("damac rk: read rgmii dl from vendor tx = 0x%02x, rx = 0x%02x\n",
                       delayline[0], delayline[1]);
               dwmac_rk_set_rgmii_delayline(priv, delayline[0], delayline[1]);     // 写入对应的phy的延时值  bsp_priv->tx_delay = tx_delay; bsp_priv->rx_delay = rx_delay;

               return 0;
       }

        return -ERANGE;
}

int dwmac_rk_search_rgmii_delayline(struct stmmac_priv *priv)
{
       struct dwmac_rk_lb_priv *lb_priv;
       unsigned char delayline[2];
       int ret;
       int vendor_id;

       lb_priv = kzalloc(sizeof(*lb_priv), GFP_KERNEL);
       if (!lb_priv)
               return -ENOMEM;

        if(!strcmp(priv->dev->name , "eth0"))
            vendor_id = ETH0_DELAY_ID;
        else if(!strcmp(priv->dev->name , "eth1"))
            vendor_id = ETH1_DELAY_ID;

       lb_priv->sysfs = 0;                  // 不以文件系统形式扫描
       lb_priv->type = LOOPBACK_TYPE_PHY;
       lb_priv->speed = LOOPBACK_SPEED1000;
       lb_priv->scan = 1;

       ret = dwmac_rk_loopback_run(priv, lb_priv);     // 循环测试
       if (!ret) {
               delayline[0] = lb_priv->final_tx;
               delayline[1] = lb_priv->final_rx;
               if (rk_vendor_write(vendor_id, delayline, 2))  // 将测试后的值写入vendor区,保存
                       pr_err("damac rk: write rgmii delayline to vendor failed\n");

               /* write tx/rx delayline back if loopback okay */
               dwmac_rk_set_rgmii_delayline(priv, lb_priv->final_tx,  // 回环ok后写入phy延时 bsp_priv->tx_delay = tx_delay; bsp_priv->rx_delay = rx_delay;
                                            lb_priv->final_rx);
       }

       kfree(lb_priv);                          // 释放lb_priv结构体
       return ret;
}
#endif

扫描函数解析

static int dwmac_rk_loopback_delayline_scan(struct stmmac_priv *priv,
                        struct dwmac_rk_lb_priv *lb_priv)
{
    if (lb_priv->sysfs)
        return dwmac_rk_delayline_scan(priv, lb_priv);
    else
        return dwmac_rk_delayline_scan_cross(priv, lb_priv);
}

// lb_priv->sysfs =0
#define SCAN_STEP 0x1              // 扫描步长为1
#define MAX_DELAYLINE 0x7f
#define SCAN_VALID_RANGE 0xA

static int dwmac_rk_delayline_scan_cross(struct stmmac_priv *priv,
                     struct dwmac_rk_lb_priv *lb_priv)
{
    int tx_left, tx_right, rx_up, rx_down;
    int i, j, tx_index, rx_index;
    int tx_mid, rx_mid;

    /* initiation */
    tx_index = SCAN_STEP;
    rx_index = SCAN_STEP;

re_scan:
    /* start from rx based on the experience */
    for (i = rx_index; i <= (MAX_DELAYLINE - SCAN_STEP); i += SCAN_STEP) {
        tx_left = 0;
        tx_right = 0;
        tx_mid = 0;

        for (j = tx_index; j <= (MAX_DELAYLINE - SCAN_STEP);
             j += SCAN_STEP) {
            if (!dwmac_rk_loopback_with_identify(priv,
                lb_priv, j, i)) {
                if (!tx_left)
                    tx_left = j;
                tx_right = j;
            }
        }

        /* look for tx_mid */
        if ((tx_right - tx_left) > SCAN_VALID_RANGE) {
            tx_mid = (tx_right + tx_left) / 2;
            break;
        }
    }

    /* Worst case: reach the end */
    if (i >= (MAX_DELAYLINE - SCAN_STEP))
        goto end;

    rx_up = 0;
    rx_down = 0;

    /* look for rx_mid base on the tx_mid */
    for (i = SCAN_STEP; i <= (MAX_DELAYLINE - SCAN_STEP);
         i += SCAN_STEP) {
        if (!dwmac_rk_loopback_with_identify(priv, lb_priv,
            tx_mid, i)) {
            if (!rx_up)
                rx_up = i;
            rx_down = i;
        }
    }

    if ((rx_down - rx_up) > SCAN_VALID_RANGE) {
        /* Now get the rx_mid */
        rx_mid = (rx_up + rx_down) / 2;
    } else {
        rx_index += SCAN_STEP;
        rx_mid = 0;
        goto re_scan;
    }

    if (dwmac_rk_delayline_is_valid(tx_mid, rx_mid)) {
        lb_priv->final_tx = tx_mid;
        lb_priv->final_rx = rx_mid;

        pr_info("Find suitable tx_delay = 0x%02x, rx_delay = 0x%02x\n",
            lb_priv->final_tx, lb_priv->final_rx);

        return 0;
    }
end:
    pr_err("Can't find suitable delayline\n");
    return -ENXIO;
}

注:
1、遇到手动扫描可以使用(网络性能好),但是自动扫描失败,报没有合适的延时值匹配,可以调整步长#define SCAN_STEP 0x5
2、该自动扫描也可以不用RK工具去烧写,只要在vendor区ID分配没有用的区间即可
3、自动扫描只能在emmc全部擦除之后才能用,在使用的过程中不能插网线。(RK一般是将emmc全部擦除,第一次加载固件时不能插网线,后续可以插网线烧写单个镜像)
4、关于vendor分区,为什么要讲扫描出来的值保存在这里?后续文章会更新

你可能感兴趣的:(Linux内核,linux,phy,mac,arm,c语言)