系统版本: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”,免费获取数驱动文件,里面有寄存器打印添加,可直观对比官网库的更改。官网核心板原理图文件;