原创 为AM335x移植Linux内核主线代码(15)关于AM335x的Ethernet下

2014-12-3 10:02 1134 9 9 分类: MCU/ 嵌入式 文集: Linux Kernel的DTS

在理解MDIO的round-robin arbitration以及AR8031的寄存器含义之后,就要返回(11)关于AM335x的Ethernet中的内容中的步骤D,继续之前的代码分析:
================================
D. struct phy_device *phy_connect(struct mii_dev *bus, int addr,
                struct eth_device *dev, phy_interface interface)
                
get_phy_id函数会将PHYID读取回来,在这里就是0x004dd074;
它返回到create_phy_by_mask函数,则会根据上面的值创建phy_device;
它返回到get_phy_device_by_mask函数,return申请到的phy_dev的结构体地址;
它返回到phy_find_by_mask函数,return申请到的phy_dev的结构体地址;
它返回到phy_connect函数,继续往下执行phy_connect_dev函数;

================================
F. void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev)

在此函数中,首先会执行phy_reset函数:
之前观察AR8031寄存器的时候,偏移地址为0的寄存器为Control register;
phy_reset函数会对其最高位RESET进行写操作,从而软件复位AR8031;

然后会使用dev给phydev->dev赋值:
dev是之前在cpsw_register申请的eth_device结构体;
phydev是之前在create_phy_by_mask中申请的phy_device结构体;

将它们的信息分别打印出来:
printf("dev->name = %s, phydev->drv->name = %s.\n", dev->name, phydev->drv->name);
则会显示:
dev->name = cpsw, phydev->drv->name = Generic PHY.

================================
C. int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave)

D会调用E和F;执行完毕D之后,会返回C执行。
C会做两件事情,第一是D包含的PHY connect操作,第二是PHY config操作。

而phy_config函数会调用下面的内容:
__attribute__((weak, alias("__board_phy_config")));
之前在《U-Boot for AM335x (8) spl如何调用u-boot.img》提到了weak关键字的用法,所以这个函数其实是默认什么都不做的,但如果用户在自己的board.c中定义了它,则会执行新定义的内容。否则就执行默认的。

默认__board_phy_config函数是:
static int __board_phy_config(struct phy_device *phydev)
{
        if (phydev->drv->config)
                return phydev->drv->config(phydev);
        return 0;
}

申请phydev的时候,就给它的drv赋值了:
dev->drv = get_phy_driver(dev, interface);
那dev->drv->config究竟是哪个函数呢?
(由于get_phy_driver函数使用了链表,我对链表不太熟悉,因此根据经验猜测)config函数应该是:
int genphy_config(struct phy_device *phydev);

 * 它首先读取了AR8031里的Offset = 0x01的Status Register,此处为0x7949;
 * 判断PHY是否支持auto negotiation,支持的话则features |= SUPPORTED_Autoneg; (N)
 * 判断PHY是否支持100BASE-X Full-Duplex,支持的话则features |= SUPPORTED_100baseT_Full; (Y)
 * 判断PHY是否支持100BASE-X Half-Duplex,支持的话则features |= SUPPORTED_100baseT_Half; (Y)
 * 判断PHY是否支持10Mbs Full-Duplex,支持的话则features |= SUPPORTED_10baseT_Full; (Y)
 * 判断PHY是否支持10Mbs Half-Duplex,支持的话则features |= SUPPORTED_10baseT_Half; (Y)
 * 判断PHY是否有Extended Status Register信息,有的话则读取,此处为0xa000; (Y)
 * 判断PHY是否支持1000BASE-T Full-Duplex,支持的话则features |= SUPPORTED_1000baseT_Full; (Y)
 * 判断PHY是否支持1000BASE-T Half-Duplex,支持的话则features |= SUPPORTED_1000baseT_Half; (N)
 * 判断PHY是否支持1000BASE-X Full Duplex,支持的话则features |= SUPPORTED_1000baseX_Full; (Y)
 * 判断PHY是否支持1000BASE-T Half Duplex,支持的话则features |= SUPPORTED_1000baseX_Half; (N)
 * 得到最终的features = 0x00400fef;
 * 调用genphy_config_aneg(phydev)函数,在此函数中:
 * 读取偏移地址为0x04的Auto-Negotiation Advertisement Register的值,此处为0x1de1;
 * 经过一系列ADVERTISE选项的判断,得出应有的寄存器值为0x11e1;
 * 读取偏移地址为0x09的1000Base-T Control Register,此处为0x0200;
 * 经过一系列ADVERTISE选项的判断,得出应有的寄存器值为0x0200;
 * 执行genphy_restart_aneg(phydev),重启PHY芯片;
 
C函数执行完之后会返回B函数,B函数执行完之后则返回A函数。
至此,初始化工作就完成了!

================================
经过设置后的AR8031寄存器值:
config之前   config之后   是否相同
   \|/         \|/       \|/
0x20043100  0x20043100    Y
0x20247949  0x20247949    Y
0x2044004d  0x2044004d    Y
0x2064d074  0x2064d074    Y
0x20841de1  0x208411e1    N
0x20a445e1  0x20a40000    N
0x20c40006  0x20c40004    N
0x20e42001  0x20e42001    Y
0x21040000  0x21040000    Y
0x21240200  0x21240200    Y
0x21440000  0x21440000    Y
0x21640000  0x21640000    Y
0x21840000  0x21840000    Y
0x21a40000  0x21a40000    Y
0x21c40000  0x21c40000    Y
0x21e4a000  0x21e4a000    Y
0x22040862  0x22040862    Y
0x22240010  0x22240010    Y
0x22440000  0x22440000    Y
0x22649000  0x22640000    N
0x2284082c  0x2284082c    Y
0x22a40000  0x22a40000    Y
0x22c404e8  0x22c404e8    Y
0x22e40000  0x22e40000    Y
0x23043200  0x23043200    Y
0x23243000  0x23243000    Y
0x23440000  0x23440000    Y
0x23640600  0x23640600    Y
0x23840000  0x23840000    Y
0x23a40000  0x23a40000    Y
0x23c482ee  0x23c482ee    Y
0x23e48100  0x23e48100    Y

第一条不同:Offset = 0x04
Auto-Negotiation Advertisement Register
0x20841de1 -> 0x208411e1
bit 11: Asymmetric Pause -> No asymmetric pause.
bit 10: MAC PAUSE implemented -> MAC PAUSE not implemented.

第二条不同:Offset = 0x05
Link Partner Ability Register
0x20a445e1 -> 0x20a40000

第三条不同:Offset = 0x06
Auto-Negotiation Expansion Register
0x20c40006 -> 0x20c40004
bit 1: A new page has been received -> No new page.

第四条不同:Offset = 0x14
0x22649000 -> 0x22640000
Smart Speed Register
bit 15-11: 10010B -> 00000B.

MDIO的调试过程告一段落,下面是cpsw的调试:
(没有严谨的过程,调试到哪一步就记录什么~)
================================================================
NOTE1: 设置LOOPBACK:
为了方便调试,首先将PHY芯片设置为LOOPBACK模式,即将TX收到的所有数据都返回到RX上。
设置过程很简单,在函数:
int genphy_restart_aneg(struct phy_device *phydev)
中修改对控制寄存器操作的值:
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART | (1 << 14));

================================
NOTE2: cpsw的初始化过程:
网卡调试,除了MDIO之外,更重要的是cpsw功能,因此这一节观察cpsw功能是如何被U-Boot初始化的。
它主要是由下面这个函数完成:
int eth_initialize(bd_t *bis);

函数具体内容是:
第一步,bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
第二步,miiphy_init();
第三步,phy_init(); (do nothing!)
第四步,eth_env_init(bis); (get zImage!)
第五步,board_eth_init(bis); (user action!)
第六步,bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
第七步,eth_current_changed();

================================
NOTE3: ping命令:
U-Boot支持ping命令,但这个ping命令到底是怎么执行的呢?
观察common/cmd_net.c中的这段程序:

#if defined(CONFIG_CMD_PING)
static int do_ping(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
        if (argc < 2)
                return -1;

        NetPingIP = string_to_ip(argv[1]);
        if (NetPingIP == 0)
                return CMD_RET_USAGE;

        if (NetLoop(PING) < 0) {
                printf("ping failed; host %s is not alive\n", argv[1]);
                return 1;
        }

        printf("host %s is alive\n", argv[1]);

        return 0;
}

U_BOOT_CMD(
        ping,   2,      1,      do_ping,
        "send ICMP ECHO_REQUEST to network host",
        "pingAddress"
);
#endif

而在NetLoop函数里面:
第一步,执行net_init()函数;

第二步,执行eth_is_on_demand_init()函数;
(* 因为逻辑或会从左往右,所以这个函数会被执行;)
(* 因为没有定义CONFIG_NETCONSOLE,因此直接返回1)

第三步,执行eth_halt()函数;
(* 因为eth_current存在,因此halt自身,并且将state置位;)

第四步,执行eth_set_current()函数;

第五步,执行eth_init(bd)函数;
(* cpsw_register()函数调用eth_register()函数;)
(* eth_register()为eth_current结构体指针赋值,值为cpsw_register()中申请的dev;)
(* 所以eth_init()函数调用的eth_current->init()函数,实际上就为cpsw_init()函数;)
(* 这是很关键的一步,它会建立negotiation。)
(* cpsw_init()函数分析: )
(* 1. setbit_and_wait_for_clear32(&priv->regs->soft_reset); 它的作用是操作CPSW_SS SOFT_RESET Register,重启3G logic;)
(* 2. cpsw_ale_enable(priv, 1); 使能ALE(Address Look Engine);)
(* 3. cpsw_ale_clear(priv, 1); 清零ALE的地址表;)
(* 4. cpsw_ale_vlan_aware(priv, 0); 当VLAN没有找到的时候则Flood;)
(* 5. __raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); 设置P0_CPDMA_TX_PRI_MAP Register为0x76543210,设置header packet的优先级;)
(* 6. __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map); 设置P0_CPDMA_RX_CH_MAP Register为0,packets发往的通道号;)
(* 7. __raw_writel(0, &priv->regs->ptype); 设置PTYPE Register的值为0,disable priority elevation and enable statistics on all ports.)
(* 8. __raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en); 使能host port的静态采集;)
(* 9. __raw_writel(0x7, &priv->regs->stat_port_en); 使能所有ports的静态采集;)
(* 10. cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); 设置ALE PORTCTLn寄存器,将Port State设置为Forward。)
(* 11. cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port, ALE_SECURE); )
(* 12. cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port); )
(* 13. cpsw_slave_init(slave, priv);)
(* 14. cpsw_update_link(priv); 本函数会调用cpsw_slave_update_link()函数,后者再调用phy_startup()函数,后者再调用genphy_update_link()函数,此函数会重复读取AR8031的状态寄存器,直到LINK为真,也就是说,Auto-Negotiation成功了,才能接着往下执行!)
(* NOTE: 关于Auto-Negotiation:Auto-Negotiation通过和对端交换一中FLP(Fast Link Pulse)的特殊Frame,里面包含了自己这端可以支持的工作组合方式,对端收到之后和自己可以支持的工作组合方式相比较,选择一种最佳的工作方式。)

此时Auto-Negotiation应该已经完成,但我的板子貌似还有点问题,I think I should dig into the problem of "phyaddr==4"! Anyway, next chapter is about the Auto-Negotiation!等弄明白Auto-Negotiation的原理,再回过头来研究到底phyaddr是不是导致Auto-Negotiation失败的原因!

(NOTE: 当把AR8031四周加焊,发现它的地址就是0,而不是4,说明是虚焊导致了phyaddr的偏移。但既然MDIO可以连接上,说明它的CLK和DATA线并没有问题!究竟虚焊是怎样影响phyaddr的,就不得而知了=_=!)
 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
9
关闭 站长推荐上一条 /3 下一条