赞
踩
我适配的设备使用的是 RJ45 网口,phy 和 soc 之间的接口为 RGMII;
linux 版本:4.1.15
soc:imx6q
1. 检查电源;
VDDL = DVDDL = AVDDL = 1.1V;由 32 脚 LX 输出;
AVDD33 检查是否为 3.3V 供电;
DVDD_RGMII 电压是否符合下表,由 36、37 脚来选择电压;
2. 检查时钟;
检查晶振能否正常输出 25Mhz 的信号;
3. 检查接线;
MDI、RGMII、MDIO、MDC 等是否正确连接;
4. 检查 phy 地址;
28、17 脚没有外部上拉的情况下,默认使用的是内部下拉,即值为 0;
1. phy id
phy 匹配过程大致如下,可以在 get_phy_device 函数里面判断是否正确读取 phy id;
- fec_probe
- fec_enet_mii_init
- of_mdiobus_register Loop over the child nodes and register a phy_device for each one
- of_mdiobus_register_phy
- get_phy_device ---get_phy_id(phy_device_create匹配phy_driver_register) ---- phy_device_create
- phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
可以在 u-boot 处,进入命令行模式,用 mii info 命令来查看是否正确获取 phy id,如果没有获取,多半是 phy 硬件有问题;
正常情况下,在 u-boot 时候,可以获取两个 phy id,一个广播地址 00,一个 phy 地址;
对于 YT8531S 来说,操作广播地址与操作 phy 地址没差别;
硬件复位后,需要延时 100 ms,才能进行 MDIO 读写;
在 drivers/net/ethernet/freescale/fec_main.c 的 fec_reset_phy 函数尾部加一个 100ms 延时;
- static void fec_reset_phy(struct platform_device *pdev)
- {
- int err, phy_reset;
- int msec = 1;
- struct device_node *np = pdev->dev.of_node;
-
- if (!np)
- return;
-
- err = of_property_read_u32(np, "phy-reset-duration", &msec);
- /* A sane reset duration should not be longer than 1s */
- if (!err && msec > 1000)
- msec = 1;
-
- phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
- if (!gpio_is_valid(phy_reset))
- return;
-
- err = devm_gpio_request_one(&pdev->dev, phy_reset,
- GPIOF_OUT_INIT_LOW, "phy-reset");
- if (err) {
- dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
- return;
- }
- msleep(msec);
- gpio_set_value(phy_reset, 1);
- msleep(100);
- }
2. ENET_TX_CLK
对于 imx6q,使用 RGMII 的时候,还需要一个 125M 的时钟;
将 phy 的外部寄存器 0xA012 改为 0x50 即可;
3. dts 和 driver
fec 节点如下:
- &fec {
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_enet>;
- phy-mode = "rgmii";
- phy-reset-gpios = <&gpio1 25 GPIO_ACTIVE_LOW>;
- phy-reset-duration = <2>;
- fsl,magic-packet;
- status = "okay";
- };
-
- pinctrl_enet: enetgrp {
- fsl,pins = <
- MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
- MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
- MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
- MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
- MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
- MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
- MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
- MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
- MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
- MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
- MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
- MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
- MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
- MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
- MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
-
- /* Phy reset */
- MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x000b0
- >;
motorcomm.c 修改如下:
- /*
- * drivers/net/phy/motorcomm.c
- *
- * Driver for Motorcomm PHYs
- *
- * Author: Leilei Zhao <leilei.zhao@motorcomm.com>
- *
- * Copyright (c) 2019 Motorcomm, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * Support : Motorcomm Phys:
- * Giga phys: yt8511, yt8521
- * 100/10 Phys : yt8512, yt8512b, yt8510
- * Automotive 100Mb Phys : yt8010
- * Automotive 100/10 hyper range Phys: yt8510
- */
-
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/phy.h>
- #include <linux/motorcomm_phy.h>
- #include <linux/of.h>
- #include <linux/clk.h>
- #ifndef LINUX_VERSION_CODE
- #include <linux/version.h>
- #else
- #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
- #endif
- /*for wol, 20210604*/
- #include <linux/netdevice.h>
-
- #include "yt8614-phy.h"
-
- /**** configuration section begin ***********/
-
- /* if system depends on ethernet packet to restore from sleep, please define this macro to 1
- * otherwise, define it to 0.
- */
- #define SYS_WAKEUP_BASED_ON_ETH_PKT 0
-
- /* to enable system WOL of phy, please define this macro to 1
- * otherwise, define it to 0.
- * NOTE: this macro will need macro SYS_WAKEUP_BASED_ON_ETH_PKT to set to 1
- */
- #define YTPHY_ENABLE_WOL 0
-
- /* some GMAC need clock input from PHY, for eg., 125M, please enable this macro
- * by degault, it is set to 0
- */
- #define GMAC_CLOCK_INPUT_NEEDED 0
-
-
- #define YT8521_PHY_MODE_FIBER 1 //fiber mode only
- #define YT8521_PHY_MODE_UTP 2 //utp mode only
- #define YT8521_PHY_MODE_POLL 3 //fiber and utp, poll mode
-
- /* please make choice according to system design
- * for Fiber only system, please define YT8521_PHY_MODE_CURR 1
- * for UTP only system, please define YT8521_PHY_MODE_CURR 2
- * for combo system, please define YT8521_PHY_MODE_CURR 3
- */
- #define YT8521_PHY_MODE_CURR 2
-
- /**** configuration section end ***********/
-
-
- /* no need to change below */
-
- #if (YTPHY_ENABLE_WOL)
- #undef SYS_WAKEUP_BASED_ON_ETH_PKT
- #define SYS_WAKEUP_BASED_ON_ETH_PKT 1
- #endif
-
- /* workaround for 8521 fiber 100m mode */
- static int link_mode_8521 = 0; //0: no link; 1: utp; 32: fiber. traced that 1000m fiber uses 32.
- static int link_mode_8614[4] = {0}; //0: no link; 1: utp; 32: fiber. traced that 1000m fiber uses 32.
-
- /* for multiple port phy, base phy address */
- static unsigned int yt_mport_base_phy_addr = 0xff; //0xff: invalid;
- static unsigned int yt_mport_base_phy_addr_8614 = 0xff; //0xff: invalid;
-
-
- static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
- {
- int ret;
- int val;
-
- ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
- if (ret < 0)
- return ret;
-
- val = phy_read(phydev, REG_DEBUG_DATA);
-
- return val;
- }
-
- static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
- {
- int ret;
-
- ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
- if (ret < 0)
- return ret;
-
- ret = phy_write(phydev, REG_DEBUG_DATA, val);
-
- return ret;
- }
-
-
- int yt8521_soft_reset(struct phy_device *phydev)
- {
- int ret;
- int val;
-
- ytphy_write_ext(phydev, 0xa000, 0);
- ret = genphy_soft_reset(phydev);
- if (ret < 0)
- return ret;
-
- ytphy_write_ext(phydev, 0xa000, 2);
- ret = genphy_soft_reset(phydev);
- if (ret < 0) {
- ytphy_write_ext(phydev, 0xa000, 0);
- return ret;
- }
-
- ret = ytphy_write_ext(phydev, 0xA012, 0x50);
- if (ret < 0)
- return ret;
-
- return 0;
- }
-
- #if (YTPHY_ENABLE_WOL)
- static int ytphy_switch_reg_space(struct phy_device *phydev, int space)
- {
- int ret;
-
- if (space == YTPHY_REG_SPACE_UTP) {
- ret = ytphy_write_ext(phydev, 0xa000, 0);
- } else {
- ret = ytphy_write_ext(phydev, 0xa000, 2);
- }
-
- return ret;
- }
-
- static int ytphy_wol_en_cfg(struct phy_device *phydev, ytphy_wol_cfg_t wol_cfg)
- {
- int ret = 0;
- int val = 0;
-
- val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG);
- if (val < 0)
- return val;
-
- if (wol_cfg.enable) {
- val |= YTPHY_WOL_CFG_EN;
-
- if (wol_cfg.type == YTPHY_WOL_TYPE_LEVEL) {
- val &= ~YTPHY_WOL_CFG_TYPE;
- val &= ~YTPHY_WOL_CFG_INTR_SEL;
- } else if (wol_cfg.type == YTPHY_WOL_TYPE_PULSE) {
- val |= YTPHY_WOL_CFG_TYPE;
- val |= YTPHY_WOL_CFG_INTR_SEL;
-
- if (wol_cfg.width == YTPHY_WOL_WIDTH_84MS) {
- val &= ~YTPHY_WOL_CFG_WIDTH1;
- val &= ~YTPHY_WOL_CFG_WIDTH2;
- } else if (wol_cfg.width == YTPHY_WOL_WIDTH_168MS) {
- val |= YTPHY_WOL_CFG_WIDTH1;
- val &= ~YTPHY_WOL_CFG_WIDTH2;
- } else if (wol_cfg.width == YTPHY_WOL_WIDTH_336MS) {
- val &= ~YTPHY_WOL_CFG_WIDTH1;
- val |= YTPHY_WOL_CFG_WIDTH2;
- } else if (wol_cfg.width == YTPHY_WOL_WIDTH_672MS) {
- val |= YTPHY_WOL_CFG_WIDTH1;
- val |= YTPHY_WOL_CFG_WIDTH2;
- }
- }
- } else {
- val &= ~YTPHY_WOL_CFG_EN;
- val &= ~YTPHY_WOL_CFG_INTR_SEL;
- }
-
- ret = ytphy_write_ext(phydev, YTPHY_WOL_CFG_REG, val);
- if (ret < 0)
- return ret;
-
- return 0;
- }
-
- static void ytphy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
- {
- int val = 0;
-
- wol->supported = WAKE_MAGIC;
- wol->wolopts = 0;
-
- val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG);
- if (val < 0)
- return;
-
- if (val & YTPHY_WOL_CFG_EN)
- wol->wolopts |= WAKE_MAGIC;
-
- return;
- }
-
- static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
- {
- int ret, pre_page, val;
- ytphy_wol_cfg_t wol_cfg;
- struct net_device *p_attached_dev = phydev->attached_dev;
-
- memset(&wol_cfg, 0, sizeof(ytphy_wol_cfg_t));
- pre_page = ytphy_read_ext(phydev, 0xa000);
- if (pre_page < 0)
- return pre_page;
-
- /* Switch to phy UTP page */
- ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP);
- if (ret < 0)
- return ret;
-
- if (wol->wolopts & WAKE_MAGIC) {
-
- /* Enable the WOL interrupt */
- val = phy_read(phydev, YTPHY_UTP_INTR_REG);
- val |= YTPHY_WOL_INTR;
- ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val);
- if (ret < 0)
- return ret;
-
- /* Set the WOL config */
- wol_cfg.enable = 1; //enable
- wol_cfg.type = YTPHY_WOL_TYPE_PULSE;
- wol_cfg.width = YTPHY_WOL_WIDTH_672MS;
- ret = ytphy_wol_en_cfg(phydev, wol_cfg);
- if (ret < 0)
- return ret;
-
- /* Store the device address for the magic packet */
- ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR2,
- ((p_attached_dev->dev_addr[0] << 8) |
- p_attached_dev->dev_addr[1]));
- if (ret < 0)
- return ret;
- ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR1,
- ((p_attached_dev->dev_addr[2] << 8) |
- p_attached_dev->dev_addr[3]));
- if (ret < 0)
- return ret;
- ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR0,
- ((p_attached_dev->dev_addr[4] << 8) |
- p_attached_dev->dev_addr[5]));
- if (ret < 0)
- return ret;
- } else {
- wol_cfg.enable = 0; //disable
- wol_cfg.type = YTPHY_WOL_TYPE_MAX;
- wol_cfg.width = YTPHY_WOL_WIDTH_MAX;
- ret = ytphy_wol_en_cfg(phydev, wol_cfg);
- if (ret < 0)
- return ret;
- }
-
- /* Recover to previous register space page */
- ret = ytphy_switch_reg_space(phydev, pre_page);
- if (ret < 0)
- return ret;
-
- return 0;
- }
- #endif /*(YTPHY_ENABLE_WOL)*/
-
- static int yt8521_config_init(struct phy_device *phydev)
- {
- int ret;
- int val;
-
- phydev->irq = PHY_POLL;
-
- ytphy_write_ext(phydev, 0xa000, 0);
- ret = genphy_config_init(phydev);
- if (ret < 0)
- return ret;
-
- /* disable auto sleep */
- val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
- if (val < 0)
- return val;
-
- val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
-
- ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
- if (ret < 0)
- return ret;
-
- /* enable RXC clock when no wire plug */
- ret = ytphy_write_ext(phydev, 0xa000, 0);
- if (ret < 0)
- return ret;
-
- val = ytphy_read_ext(phydev, 0xc);
- if (val < 0)
- return val;
- val &= ~(1 << 12);
- ret = ytphy_write_ext(phydev, 0xc, val);
- if (ret < 0)
- return ret;
-
- //printk("8521 init call out...\n");
- return ret;
- }
-
- static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp)
- {
- int speed_mode, duplex;
- int speed = SPEED_UNKNOWN;
-
- //printk("8521 status adjust call in...\n");
- duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT;
- speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT;
- switch (speed_mode) {
- case 0:
- if (is_utp)
- speed = SPEED_10;
- break;
- case 1:
- speed = SPEED_100;
- break;
- case 2:
- speed = SPEED_1000;
- break;
- case 3:
- break;
- default:
- speed = SPEED_UNKNOWN;
-
- break;
- }
-
- phydev->speed = speed;
- phydev->duplex = duplex;
-
- //printk("8521 status adjust call out,regval=0x%04x,mode=%s,speed=%dm...\n", val,is_utp?"utp":"fiber", phydev->speed);
- return 0;
- }
-
- int yt8521_aneg_done (struct phy_device *phydev)
- {
- //printk("phy..YT8521 AN_done callin,speed=%dm,linkmoded=%d\n", phydev->speed,link_mode_8521);
-
- if ((32 == link_mode_8521) && (SPEED_100 == phydev->speed))
- {
- return 1/*link_mode_8521*/;
- }
-
- return genphy_aneg_done(phydev);
- }
-
- static int yt8521_read_status(struct phy_device *phydev)
- {
- int ret;
- volatile int val;
- volatile int link;
- int link_utp = 0;
-
- /* reading UTP */
- ret = ytphy_write_ext(phydev, 0xa000, 0);
- if (ret < 0)
- return ret;
-
- val = phy_read(phydev, REG_PHY_SPEC_STATUS);
- if (val < 0)
- return val;
-
- link = val & (BIT(YT8521_LINK_STATUS_BIT));
- if (link) {
- link_utp = 1;
- link_mode_8521 = 1;
- yt8521_adjust_status(phydev, val, 1);
- } else {
- link_utp = 0;
- }
-
- if (link_utp) {
- phydev->link = 1;
- } else {
- phydev->link = 0;
- link_mode_8521 = 0;
- }
-
- if (link_utp) {
- ytphy_write_ext(phydev, 0xa000, 0);
- }
-
- //printk("8521 read status call out, link=%d, linkmode=%d\n", phydev->link, link_mode_8521 );
-
- return 0;
- }
-
- int yt8521_suspend(struct phy_device *phydev)
- {
- #if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
- int value;
-
- #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
- mutex_lock(&phydev->lock);
- #else
- /* no need lock in 4.19 */
- #endif
-
- ytphy_write_ext(phydev, 0xa000, 0);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
-
- ytphy_write_ext(phydev, 0xa000, 2);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
-
- ytphy_write_ext(phydev, 0xa000, 0);
-
- #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
- mutex_unlock(&phydev->lock);
- #else
- /* no need lock/unlock in 4.19 */
- #endif
- #endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
-
- return 0;
- }
-
- int yt8521_resume(struct phy_device *phydev)
- {
- #if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
- int value;
-
- #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
- mutex_lock(&phydev->lock);
- #else
- /* no need lock/unlock in 4.19 */
- #endif
-
- ytphy_write_ext(phydev, 0xa000, 0);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
-
- ytphy_write_ext(phydev, 0xa000, 2);
- value = phy_read(phydev, MII_BMCR);
- phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
-
- ytphy_write_ext(phydev, 0xa000, 0);
-
- #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
- mutex_unlock(&phydev->lock);
- #else
- /* no need lock/unlock in 4.19 */
- #endif
- #endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
-
- return 0;
- }
-
- static struct phy_driver ytphy_drvs[] = {
- {
- /* same as 8521 */
- .phy_id = PHY_ID_YT8531S,
- .name = "YT8531S Ethernet",
- .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
- .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES,
- .flags = PHY_POLL,
- .soft_reset = yt8521_soft_reset,
- .config_aneg = genphy_config_aneg,
- .aneg_done = yt8521_aneg_done,
- .config_init = yt8521_config_init,
- .read_status = yt8521_read_status,
- .suspend = yt8521_suspend,
- .resume = yt8521_resume,
- #if (YTPHY_ENABLE_WOL)
- .get_wol = &ytphy_get_wol,
- .set_wol = &ytphy_set_wol,
- #endif
- }
- };
-
- module_phy_driver(ytphy_drvs);
-
- MODULE_DESCRIPTION("Motorcomm PHY driver");
- MODULE_AUTHOR("Leilei Zhao");
- MODULE_LICENSE("GPL");
-
- static struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
- { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8531S, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8531, MOTORCOMM_PHY_ID_MASK },
- { PHY_ID_YT8618, MOTORCOMM_MPHY_ID_MASK },
- { PHY_ID_YT8614, MOTORCOMM_MPHY_ID_MASK_8614 },
- { }
- };
-
- MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。