系统版本: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

图片

原理设计,电源是通过MCU控制的,RST也是通过MCU控制的;外部晶振50M;这样做的好处是,MCU复位之后,PHY芯片一定会被复位,防止PHY由于EMC问题死机,软件复位不了,可以通过电源让芯片强制下电,从而恢复工作;

一、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

            macb1: ethernet@f802c000 {
                phy-mode = "rmii";
                #address-cells = <1>;
                #size-cells = <0>;
                status = "okay";

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

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

内核打印信息
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有问题

四、文件系统启动后,通过sys系统查看网卡驱动,使用cat命令查看对应的值,发现PHY的ID依然是全FF;

root@sama5d3-xplained:/sys/bus/mdio_bus/drivers/Generic PHY# ls
bind  f0028000.ethernet-ffffffff:07  f802c000.ethernet-ffffffff:01  uevent  unbind

root@sama5d3-xplained:/sys/bus/mdio_bus/drivers# cat Generic\ PHY/f802c000.ethernet-ffffffff\:01/phy_id 
0x00000000    //没有读取到硬件ID
root@sama5d3-xplained:/sys/bus/mdio_bus/drivers# cat Generic\ PHY/f802c000.ethernet-ffffffff\:01/phy_interface 
rmii
root@sama5d3-xplained:/sys/bus/mdio_bus/drivers# 

五、设备树配置正确,为什么读取不到ID呢,这时候想起会不会是RST脚的电平是可控的,会不会默认是低,导致PHY不工作呢,电源是不是还没有;

使用万用表测量,发现RST脚还没有高电平。

修改为:电源和RST默认高电平

            macb1: ethernet@f802c000 {
                phy-mode = "rmii";
                #address-cells = <1>;
                #size-cells = <0>;
                pinctrl-0 = <&pinctrl_mcb1_rst >;   //管脚配置
                status = "okay";
                gpios = <0
                    &pioC   18 GPIO_ACTIVE_HIGH //电源
                    &pioC   31 GPIO_ACTIVE_HIGH //复位脚
                    >;
                ethernet-phy@0 {
                    reg = <0x0>;
                };
            };

编译之后,烧录验证,可以正确识别8081芯片了

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)

六、启动文件系统后,依然无法ping通,发现官网的原理图,外接晶振为25MHz,代码肯定配置也是25M;

验证后发现晶振配置需要更改;

图片

图片

//MACB 外接PHY ksz8081 是50MHZ的时钟
//drivers/net/phy/micrel.c
static int ksz8081_config_init(struct phy_device *phydev)
{
    struct kszphy_priv *priv = phydev->priv;
    const struct kszphy_type *type;
    int regval;

    //50M晶振配置
    regval = phy_read(phydev, MII_KSZPHY_CTRL);
    regval |= KSZ8051_RMII_50MHZ_CLK;
    phy_write(phydev, MII_KSZPHY_CTRL, regval);


    if (!priv)
        return 0;

    type = priv->type;

    if (type->has_broadcast_disable)
        kszphy_broadcast_disable(phydev);

    if (type->has_nand_tree_disable)
        kszphy_nand_tree_disable(phydev);

    return kszphy_config_reset(phydev);
}

ksz8081驱动的入口函数,全部都是以这种函数指针的方式对接;这种好处就是可以一个接口适配所有驱动,对架构非常方便。

static struct phy_driver ksphy_driver[] = {
 {
    .phy_id     = PHY_ID_KSZ8081,
    .name       = "Micrel KSZ8081 or KSZ8091",
    .phy_id_mask    = MICREL_PHY_ID_MASK,
    .features   = PHY_BASIC_FEATURES,
    .flags      = PHY_HAS_INTERRUPT,
    .driver_data    = &ksz8081_type,
    .probe      = kszphy_probe,
    .config_init    = ksz8081_config_init,
    .ack_interrupt  = kszphy_ack_interrupt,
    .config_intr    = kszphy_config_intr,
    .get_sset_count = kszphy_get_sset_count,
    .get_strings    = kszphy_get_strings,
    .get_stats  = kszphy_get_stats,
    .suspend    = kszphy_suspend,
    .resume     = kszphy_resume,
},

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

//PHY移植流程
2.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 */ }
};

//device创建,这些都是函数的执行顺序,这些函数名分布到不同的.c文件中;这个顺序是至关重要的。
macb_probe
    macb_mii_init
        mdiobus_register
            device_register
            mdiobus_scan 0~32
                get_phy_device
                    get_phy_id  //获取硬件PHY的ID,用来匹配驱动
                    phy_device_create
                        phy_bus_match //匹配驱动函数
            mdiobus_setup_mdiodev_from_board_info
            mdiobus_create_device
macb_init


//driver创建
MODULE_DEVICE_TABLE(mdio, davicom_tbl);
module_phy_driver(dm91xx_driver);

八、文件系统启动后,依然无法ping通;

怀疑是时序配合问题,把PHY的上电时间提前,直接在外部把可控的电源短接,重启之后,发现可以正常ping通;猜测得到验证;

处理办法,在uboot阶段把电源控制脚设置为高电平;

内核初始化的时候,管脚设置默认状态;

上电初始化PHY gpio
//arch/arm/mach-at91/pm.c
添加代码:

static void __init at91_phy_gpio_init(void)
{

    ulong phy9031_reset_gpio;                   //ksz9031 reset
    ulong phy8081_reset_gpio;                   //ksz8081 reset
    ulong phy8081_power_gpio;                   //ksz8081 power
    //ulong led_gpio;                           //led

    phy9031_reset_gpio = AT91_PIN_PE9;
    phy8081_reset_gpio = AT91_PIN_PC31;
    phy8081_power_gpio = AT91_PIN_PC18;
    //led_gpio = AT91_PIN_PB12;

    gpio_direction_output(phy9031_reset_gpio, 1);           // setup output 
    gpio_direction_output(phy8081_reset_gpio, 1);       
    gpio_direction_output(phy8081_power_gpio, 1);
    //gpio_direction_output(led_gpio, 1);

    gpio_set_value(phy9031_reset_gpio, 1);              //micrel ksz9031 reset gpio
    gpio_set_value(phy8081_reset_gpio, 1);              //micrel ksz8081 reset gpio
    gpio_set_value(phy8081_power_gpio, 0);              //micrel ksz8081 power gpio
    //gpio_set_value(led_gpio, 0);                  //led on


}

void __init sama5_pm_init(void)
{
    if (!IS_ENABLED(CONFIG_SOC_SAMA5))
        return;

    at91_dt_ramc();
    at91_pm_init(NULL);
    at91_phy_gpio_init();   //PHY gpio init
}

九、在文件系统阶段,使用过程中,发现一个小问题

使用ifconfig eth1 down,然后ifconfig eth1 up之后,网口就不通了;

是由于ksz8081的phy芯片接的是50Mhz晶振;phy芯片复位之后,晶振配置还原成初始值了。

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

    /* 添加时钟信息 */
    ksz8081_clock: ksz8081_clock {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <50000000>;
    };


            macb1: ethernet@f802c000 {
                phy-mode = "rmii";
                #address-cells = <1>;
                #size-cells = <0>;
                pinctrl-0 = <&pinctrl_mcb1_rst >;
                status = "okay";

                gpios = <0
                    &pioC   18 GPIO_ACTIVE_HIGH 
                    &pioC   31 GPIO_ACTIVE_HIGH 
                    >;
                ethernet-phy@0 {
                    reg = <0x0>;
                    clocks = <&ksz8081_clock>;      //外部时钟晶振值50MHZ
                    clock-names = "rmii-ref";       //时钟使用RMII模式 50Mhz
                };
            };

新版内核中,已经有外部晶振通过设备树配置了,3.0内核的时候还是要自己在 代码中实现的;变化日新月异;

在micrel.c中,添加打印验证

//phydev_err(phydev,"Jack rmii_ref_clk_sel probe rate=%d,rmii_ref_clk_sel_25_mhz=%d\n",rate,rmii_ref_clk_sel_25_mhz);
static int kszphy_probe(struct phy_device *phydev)
{
    const struct kszphy_type *type = phydev->drv->driver_data;
    const struct device_node *np = phydev->mdio.dev.of_node;
    struct kszphy_priv *priv;
    struct clk *clk;
    int ret;

    priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    phydev->priv = priv;

    priv->type = type;

    if (type->led_mode_reg) {
        ret = of_property_read_u32(np, "micrel,led-mode",
                &priv->led_mode);
        if (ret)
            priv->led_mode = -1;

        if (priv->led_mode > 3) {
            phydev_err(phydev, "invalid led mode: 0x%02x\n",
                   priv->led_mode);
            priv->led_mode = -1;
        }
    } else {
        priv->led_mode = -1;
    }

    clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref");
    /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
    if (!IS_ERR_OR_NULL(clk)) {
        unsigned long rate = clk_get_rate(clk);
        bool rmii_ref_clk_sel_25_mhz;

        priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
        rmii_ref_clk_sel_25_mhz = of_property_read_bool(np,
                "micrel,rmii-reference-clock-select-25-mhz");

        //phydev_err(phydev,"Jack rmii_ref_clk_sel probe rate=%d,rmii_ref_clk_sel_25_mhz=%d\n",rate,rmii_ref_clk_sel_25_mhz);

        if (rate > 24500000 && rate < 25500000) {
            priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz;
        } else if (rate > 49500000 && rate < 50500000) {
            priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
        } else {
            phydev_err(phydev, "Clock rate out of range: %ld\n",
                   rate);
            return -EINVAL;
        }
    }

    /* Support legacy board-file configuration */
    if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
        priv->rmii_ref_clk_sel = true;
        priv->rmii_ref_clk_sel_val = true;

    }

    return 0;
}

主要文件

D:\VMShare\Kernel2019-09-05\linux-at91-linux-4.19-at91\drivers\net\ethernet\cadence\macb_main.c

D:\VMShare\Kernel2019-09-05\linux-at91-linux-4.19-at91\arch\arm\boot\dts\at91-sama5d3_xplained.dts

D:\VMShare\Kernel2019-09-05\linux-at91-linux-4.19-at91\arch\arm\boot\dts\at91-sama5d3_xplained.dts

D:\VMShare\Kernel2019-09-05\linux-at91-linux-4.19-at91\drivers\net\phy\micrel.c

在这里插入图片描述

更多linux知识点推荐:

[linux kernel] 内核下ksz9031驱动调试

【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底层

关注公众号,回复“内核ksz8081”,免费获取数驱动文件,里面有寄存器打印添加,可直观对比官网库的更改。官网核心板原理图文件;
在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐