[linux kernel] 内核下ksz9031驱动调试踩过的坑

系统版本:Ubuntu18.04-64

编译器版本:gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1)

uboot版本:2018.07 -linux4sam_6.0

板子型号:at91sama5d3x-xplained

MCU型号:sama5d36

内核中调试驱动,和uboot中会有些区别,因为内核启动过程是顺序启动的,硬件上电后,外部的器件要快速做好准备工作,内核代码初始化到这个器件如果器件没有完成上电复位,很可能会驱动失败,需要硬件和软件时序同步,这是非常重要的一点。

【Datasheet】PHY KSZ9031千兆网络芯片解读

[Linux 底层]U-boot ksz9031网络驱动调试

一、PHY在内核配置中需要使能对应的芯片厂商驱动Micrel公司PHY;

make menuconfig

Device Drivers  --->
     [*] Network device support  ---> 
        [*]   Ethernet driver support  --->
        -*-   MDIO bus device drivers  ---- //MDIO控制器读取PHY寄存器
        -*-   PHY Device support and infrastructure  --->
            <*>   Micrel PHYs       //Micrel公司的ksz9031和ksz8081


二、修改设备树文件,硬件配置语言,所有的硬件相关信息都需要从设备树中获取。这是新内核的特性。

//arch/arm/boot/dts/at91-sama5d3_xplaint.dts

            macb0: ethernet@f0028000 {
                phy-mode = "rgmii";
                #address-cells = <1>;
                #size-cells = <0>;
                status = "okay";

                ethernet-phy@0 {
                    reg = <0x0>;
                };
            };

三、把编译出的zImage烧录进板子,查看打印信息

CAN device driver interface
at91_can f000c000.can: device registered (reg_base=(ptrval), irq=42)
libphy: MACB_mii_bus: probed
Generic PHY f0028000.ethernet-ffffffff:01: attached PHY driver [Generic PHY] (mii_bus:phy_addr=f0028000.ethernet-ffffffff:01, irq=POLL)
macb f0028000.ethernet eth0: Cadence GEM rev 0x00020119 at 0xf0028000 irq 43 (ee:ab:c1:d2:e6:c6)
macb f802c000.ethernet: invalid hw address, using random
libphy: MACB_mii_bus: probed
Micrel KSZ8081 or KSZ8091 f802c000.ethernet-ffffffff:01: attached PHY driver [Micrel KSZ8081 or KSZ8091] (mii_bus:phy_addr=f802c000.ethernet-ffffffff:01, irq=49)
macb f802c000.ethernet eth1: Cadence MACB rev 0x0001010c at 0xf802c000 irq 44 (36:d7:c3:e6:2a:c7)
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver5d36

其中,Generic PHY f0028000.ethernet-ffffffff:01;获取到得ID为全FF,硬件地址01;这里获取芯片的ID错误了,可能出错的点有MDIO配置错误,或者设备树配置错误;

不用太担心,可以根据打印的信息,查找对应的代码,添加上一些标识在里面;比如:macb f0028000.ethernet: macb_probe phy_interface=2.接口方式,可以搜索出对应的代码,然后添加Jack标识,再次烧录,即可打印出添加的信息;

linux-at91-linux-4.19-at91\drivers\net\ethernet\cadence\macb_main.c

static int macb_probe(struct platform_device *pdev)函数添加打印,

使用dev_info,用调试串口把信息打印出来,这是调试的必备手段;

图片

我们下面把PHY的接口方式,PHY ID都打印出来,看看和设备树是否能够对应上;

2、内核的网卡驱动找不到对应的PHY驱动
内核打印信息
macb f0028000.ethernet: Jack macb_probe phy_interface=2.
macb f0028000.ethernet: macb_mii_init name=f0028000.ethernet,id=ffffffff
libphy: MACB_mii_bus: probed
Generic PHY f0028000.ethernet-ffffffff:07: attached PHY driver [Generic PHY] (mii_bus:phy_addr=f0028000.ethernet-ffffffff:07, irq=POLL)
macb f0028000.ethernet eth0: Cadence GEM rev 0x00020119 at 0xf0028000 irq 46 (ee:ab:c1:d2:e6:c6)
macb f802c000.ethernet: invalid hw address, using random
macb f802c000.ethernet: Jack macb_probe phy_interface=7.
macb f802c000.ethernet: macb_mii_init name=f802c000.ethernet,id=ffffffff
libphy: MACB_mii_bus: probed
Generic PHY f802c000.ethernet-ffffffff:01: attached PHY driver [Generic PHY] (mii_bus:phy_addr=f802c000.ethernet-ffffffff:01, irq=POLL)
macb f802c000.ethernet eth1: Cadence MACB rev 0x0001010c at 0xf802c000 irq 47 (06:50:95:f8:63:dc)

全是id=ffffffff,怀疑是MDIO有问题
这里只匹配了Ksz8081的驱动;

phy_interface = 2;代表是MII接口

图片

四、继续添加打印信息,PHY外部最多可挂载32个PHY设备,内核是如何做识别的呢,使用for循环扫描的,如果读取到ID不为全FF,则认为是有效的设备,会对该设备进行驱动配对;再往前添加一些打印信息,看看扫描的过程是如何的;

libphy: mdiobus_scan i= 0
libphy: get_phy_device addr=0,get_phy_id=-1,
libphy: mdiobus_scan i= 1
libphy: get_phy_device addr=1,get_phy_id=-1,
libphy: mdiobus_scan i= 2
libphy: get_phy_device addr=2,get_phy_id=-1,
libphy: mdiobus_scan i= 3
libphy: get_phy_device addr=3,get_phy_id=-1,
libphy: mdiobus_scan i= 4
libphy: get_phy_device addr=4,get_phy_id=-1,
libphy: mdiobus_scan i= 5
libphy: get_phy_device addr=5,get_phy_id=-1,
libphy: mdiobus_scan i= 6
libphy: get_phy_device addr=6,get_phy_id=-1,
libphy: mdiobus_scan i= 7
libphy: get_phy_device addr=7,get_phy_id=-1,
libphy: mdiobus_scan i= 8
libphy: get_phy_device addr=8,get_phy_id=-1,
libphy: mdiobus_scan i= 9
libphy: get_phy_device addr=9,get_phy_id=-1,
libphy: mdiobus_scan i= 10
libphy: get_phy_device addr=10,get_phy_id=-1,
libphy: mdiobus_scan i= 11
libphy: get_phy_device addr=11,get_phy_id=-1,
libphy: mdiobus_scan i= 12
libphy: get_phy_device addr=12,get_phy_id=-1,
libphy: mdiobus_scan i= 13
libphy: get_phy_device addr=13,get_phy_id=-1,
libphy: mdiobus_scan i= 14
libphy: get_phy_device addr=14,get_phy_id=-1,
libphy: mdiobus_scan i= 15
libphy: get_phy_device addr=15,get_phy_id=-1,
libphy: mdiobus_scan i= 16
libphy: get_phy_device addr=16,get_phy_id=-1,
libphy: mdiobus_scan i= 17
libphy: get_phy_device addr=17,get_phy_id=-1,
libphy: mdiobus_scan i= 18
libphy: get_phy_device addr=18,get_phy_id=-1,
libphy: mdiobus_scan i= 19
libphy: get_phy_device addr=19,get_phy_id=-1,
libphy: mdiobus_scan i= 20
libphy: get_phy_device addr=20,get_phy_id=-1,
libphy: mdiobus_scan i= 21
libphy: get_phy_device addr=21,get_phy_id=-1,
libphy: mdiobus_scan i= 22
libphy: get_phy_device addr=22,get_phy_id=-1,
libphy: mdiobus_scan i= 23
libphy: get_phy_device addr=23,get_phy_id=-1,
libphy: mdiobus_scan i= 24
libphy: get_phy_device addr=24,get_phy_id=-1,
libphy: mdiobus_scan i= 25
libphy: get_phy_device addr=25,get_phy_id=-1,
libphy: mdiobus_scan i= 26
libphy: get_phy_device addr=26,get_phy_id=-1,
libphy: mdiobus_scan i= 27
libphy: get_phy_device addr=27,get_phy_id=-1,
libphy: mdiobus_scan i= 28
libphy: get_phy_device addr=28,get_phy_id=-1,
libphy: mdiobus_scan i= 29
libphy: get_phy_device addr=29,get_phy_id=-1,
libphy: mdiobus_scan i= 30
libphy: get_phy_device addr=30,get_phy_id=-1,
libphy: mdiobus_scan i= 31
libphy: get_phy_device addr=31,get_phy_id=-1,
libphy: Fixed MDIO Bus: probed
CAN device driver interface
at91_can f000c000.can: device registered (reg_base=(ptrval), irq=45)
macb f0028000.ethernet: Jack macb_probe phy_interface=2.
macb f0028000.ethernet: Jack macb_is_gem gem_ethtool_ops
macb f0028000.ethernet: Jack macb_mdc_clk_div =0x100000
macb f0028000.ethernet: Jack macb_dbw =0x300000,bp->phy_interface=2
libphy: MACB_mii_bus: probed
libphy: get_phy_device addr=0,get_phy_id=25278624,
Generic PHY f0028000.ethernet-ffffffff:00: attached PHY driver [Generic PHY] (mii_bus:phy_addr=f0028000.ethernet-ffffffff:00, irq=POLL)
macb f0028000.ethernet eth0: Cadence GEM rev 0x00020119 at 0xf0028000 irq 46 (ee:ab:c1:d2:e6:c6)
macb f802c000.ethernet: invalid hw address, using random
macb f802c000.ethernet: Jack macb_probe phy_interface=7.
macb f802c000.ethernet: Jack macb_is_gem macb_ethtool_ops
macb f802c000.ethernet: Jack macb_mdc_clk_div =0xc00
macb f802c000.ethernet: Jack macb_dbw =0xc00,bp->phy_interface=7

libphy: MACB_mii_bus: probed
libphy: get_phy_device addr=0,get_phy_id=2233697,
Micrel KSZ8081 or KSZ8091 f802c000.ethernet-ffffffff:00: attached PHY driver [Micrel KSZ8081 or KSZ8091] (mii_bus:phy_addr=f802c000.ethernet-ffffffff:00, irq=POLL)
macb f802c000.ethernet eth1: Cadence MACB rev 0x0001010c at 0xf802c000 irq 44 (86:82:71:07:ba:d2)



libphy: MACB_mii_bus: probed

libphy: get_phy_device addr=0,get_phy_id=25278624;其中在地址0上面读取到了PHY的ID,转化为16进制:0xF113644,和手册上面无法对应;但是为什么就是驱动识别不了呢?

更改设备树的PHY芯片地址;

//arch/arm/boot/dts/at91-sama5d3_xplaint.dts
GMAC外部PHY的地址为7,dts中配置的地址为0,地址不匹配,读取不了数据;
            macb0: ethernet@f0028000 {
                phy-mode = "rgmii";
                #address-cells = <1>;
                #size-cells = <0>;
                status = "okay";

                ethernet-phy@0 {
                    reg = <0x0>;
                };
            };

    改为:
            macb0: ethernet@f0028000 {
                phy-mode = "mii";
                #address-cells = <1>;
                #size-cells = <0>;
                status = "okay";

                ethernet-phy@7{
                    reg = <0x7>;
                };
            };
再次下载尝试,打印识别芯片正常,驱动可以正确匹配;
libphy: MACB_mii_bus: probed
libphy: get_phy_device addr=3,get_phy_id=-1,
mdio_bus f0028000.ethernet-ffffffff: MDIO device at address 3 is missing.
libphy: get_phy_device addr=0,get_phy_id=-1,
libphy: get_phy_device addr=1,get_phy_id=-1,
libphy: get_phy_device addr=2,get_phy_id=-1,
libphy: get_phy_device addr=3,get_phy_id=-1,
libphy: get_phy_device addr=4,get_phy_id=-1,
libphy: get_phy_device addr=5,get_phy_id=-1,
libphy: get_phy_device addr=6,get_phy_id=-1,
libphy: get_phy_device addr=7,get_phy_id=2233890,
libphy: get_phy_device addr=8,get_phy_id=-1,
libphy: get_phy_device addr=9,get_phy_id=-1,
libphy: get_phy_device addr=10,get_phy_id=-1,
libphy: get_phy_device addr=11,get_phy_id=-1,
libphy: get_phy_device addr=12,get_phy_id=-1,
libphy: get_phy_device addr=13,get_phy_id=-1,
libphy: get_phy_device addr=14,get_phy_id=-1,
libphy: get_phy_device addr=15,get_phy_id=-1,
libphy: get_phy_device addr=16,get_phy_id=-1,
libphy: get_phy_device addr=17,get_phy_id=-1,
libphy: get_phy_device addr=18,get_phy_id=-1,
libphy: get_phy_device addr=19,get_phy_id=-1,
libphy: get_phy_device addr=20,get_phy_id=-1,
libphy: get_phy_device addr=21,get_phy_id=-1,
libphy: get_phy_device addr=22,get_phy_id=-1,
libphy: get_phy_device addr=23,get_phy_id=-1,
libphy: get_phy_device addr=24,get_phy_id=-1,
libphy: get_phy_device addr=25,get_phy_id=-1,
libphy: get_phy_device addr=26,get_phy_id=-1,
libphy: get_phy_device addr=27,get_phy_id=-1,
libphy: get_phy_device addr=28,get_phy_id=-1,
libphy: get_phy_device addr=29,get_phy_id=-1,
libphy: get_phy_device addr=30,get_phy_id=-1,
libphy: get_phy_device addr=31,get_phy_id=-1,
Micrel KSZ9031 Gigabit PHY f0028000.ethernet-ffffffff:07: attached PHY driver [Micrel KSZ9031 Gigabit PHY] (mii_bus:phy_addr=f0028000.ethernet-ffffffff:07, irq=POLL)

五、下面梳理一下PHY的驱动加载流程

1、根据dts中的硬件配置,新建device

drivers\net\ethernet\cadence\macb_main.c

    { .compatible = "atmel,sama5d3-gem", .data = &sama5d3_config },
    { .compatible = "atmel,sama5d3-macb", .data = &sama5d3macb_config },

static const struct macb_config sama5d3_config = {
    .caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE
          | MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_JUMBO,
    .dma_burst_length = 16,
    .clk_init = macb_clk_init,
    .init = macb_init,
    .jumbo_max_len = 10240,
};

static const struct of_device_id macb_dt_ids[] = {
    { .compatible = "cdns,at32ap7000-macb" },
    { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config },
    { .compatible = "cdns,macb" },
    { .compatible = "cdns,np4-macb", .data = &np4_config },
    { .compatible = "cdns,pc302-gem", .data = &pc302gem_config },
    { .compatible = "cdns,gem", .data = &pc302gem_config },
    { .compatible = "cdns,sam9x60-macb", .data = &at91sam9260_config },
    { .compatible = "atmel,sama5d2-gem", .data = &sama5d2_config },
    { .compatible = "atmel,sama5d3-gem", .data = &sama5d3_config },
    { .compatible = "atmel,sama5d3-macb", .data = &sama5d3macb_config },
    { .compatible = "atmel,sama5d4-gem", .data = &sama5d4_config },
    { .compatible = "cdns,at91rm9200-emac", .data = &emac_config },
    { .compatible = "cdns,emac", .data = &emac_config },
    { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config},
    { .compatible = "cdns,zynq-gem", .data = &zynq_config },
    { /* sentinel */ }
};

2、device创建

macb_probe//入口函数
macb_mii_init//MII接口初始化
mdiobus_register//MDIO总线注册
device_register//设备注册
mdiobus_scan 0~32//循环扫描32个外部设备
get_phy_device//获取PHY ID
get_phy_id //获取硬件PHY的ID,用来匹配驱动
phy_device_create//PHY设备创建
phy_bus_match //匹配驱动函数
mdiobus_setup_mdiodev_from_board_info//MDIO创建设备
mdiobus_create_device//用于文件系统调试MDIO总线
macb_init//初始化完成

3、Driver创建

MODULE_DEVICE_TABLE(mdio, davicom_tbl);

module_phy_driver(dm91xx_driver);

启动文件系统尝试ping设备,验证驱动是否OK

测试结果为:ksz9031,需要多次插拔网线,才能检测到网线连接;

root@sama5d3-xplained:~# ifconfig
eth0      Link encap:Ethernet  HWaddr ee:ab:c1:d2:e6:c6  
          inet addr:192.168.1.100  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::ecab:c1ff:fed2:e6c6/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:1795 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1076 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:150514 (146.9 KiB)  TX bytes:113046 (110.3 KiB)
          Interrupt:46 Base address:0x8000 

eth1      Link encap:Ethernet  HWaddr aa:ab:c1:d2:e6:c6  
          inet addr:192.168.2.100  Bcast:192.168.2.255  Mask:255.255.255.0
          inet6 addr: fe80::a8ab:c1ff:fed2:e6c6/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2334 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1797 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:696836 (680.5 KiB)  TX bytes:671982 (656.2 KiB)
          Interrupt:47 Base address:0xc000 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:35 errors:0 dropped:0 overruns:0 frame:0
          TX packets:35 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:3836 (3.7 KiB)  TX bytes:3836 (3.7 KiB)

怀疑是硬件和文件系统状态不同步导致;

4.1 ksz9031,使用外部杜邦线短接rst,使PHY芯片强制复位,看到网口灯正常;

4.2在文件系统中同步硬件状态;ifconfig eth0 down; ifconfig eth0 up; 强制让PHY驱动不工作后,再次使能;

4.3千兆网进入正常状态,多次试验,热插拔都可以正确识别,ping也可以正常通讯;

以上三个步骤做完,发现网卡驱动正常了;这就好 办了,说明我们的猜测是对的;

抱着芯片手册又啃了几遍,发现端倪;

通过代码走查,打印PHY寄存器状态值,MII_ADVERTISE(0x04)寄存器值为0xde1,为对称和不对称暂停功能;链路不能被激活;

//driver/net/phy/phy_device.c

//driver/net/phy/phy_device.c
/**
 * genphy_restart_aneg - Enable and Restart Autonegotiation
 * @phydev: target phy_device struct
 */
int genphy_restart_aneg(struct phy_device *phydev)
{
    int ctl;//,ictl,adv,lpagb,iadv;
    ctl = phy_read(phydev, MII_BMCR);

    //pr_info("jack genphy_restart_aneg MII_BMCR = 0x%x\n",ctl);

    /* Don't isolate the PHY if we're negotiating */
    phy_write(phydev, MII_ADVERTISE, 0x1e1);    //改为无暂停;让芯片正常侦测信号

    ctl = 0;
    ctl = phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
              BMCR_ANENABLE | BMCR_ANRESTART);
    //return phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,
    //        BMCR_ANENABLE  | BMCR_ANRESTART);
/*
    pr_info("genphy_restart_aneg modify MII_BMCR = 0x%x\n",ctl);

    //mdelay(3000);
    ictl = phy_read(phydev, MII_BMSR);
    pr_info("genphy_restart_aneg read MII_BMSR = 0x%x\n",ictl);

    adv = phy_read(phydev, MII_CTRL1000);
    pr_info("genphy_restart_aneg read MII_CTRL1000 = 0x%x\n",adv);

    lpagb = phy_read(phydev, MII_STAT1000);
    pr_info("genphy_restart_aneg read MII_STAT1000 = 0x%x\n",lpagb);

    phy_write(phydev, MII_ADVERTISE, 0x1e1);
    iadv = phy_read(phydev, MII_ADVERTISE);
    pr_info("genphy_restart_aneg read MII_ADVERTISE = 0x%x\n",iadv);
*/
    return ctl;
}

测试之后发现可以正常侦测网络,带载通讯也是正常的,网络驱动调试结束;

更多linux知识点推荐:

【C语言】数据结构链表算法福利视频

【C语言】C语言视频福利

[linux kernel] 内核启动流程梳理

[linux 底层]u-boot EMMC驱动

[linux 底层]u-boot图形化裁剪配置

[Linux 底层]U-boot ksz9031网络驱动调试

[Linux 底层]U-boot调试命令使用技巧

[Linux 底层]U-boot编译移植

[Linux 底层]U-boot烧录脚本介绍SecureCRT

[Linux 底层]bootstrap移植裁剪及编译

[Linux 底层] 平台软件分层介绍

[Linux 驱动] RS485测试程序编写

[Linux 驱动] CAN测试程序编写

推荐阅读:

芯片手册解读 | Linux底层

关注公众号,回复“内核ksz9031”,免费获取数驱动文件,里面有寄存器打印添加,可直观对比官网库的更改。
[linux kernel] 内核下ksz9031驱动调试踩过的坑_第1张图片

你可能感兴趣的:(Linux,linux,kernel,网络)