新闻  |   论坛  |   博客  |   在线研讨会
AM335x双网口RGMII&RMII调试笔记
asd7893361 | 2015-04-29 09:56:59    阅读:95730   发布文章

提起驱动,可能就是开发人员的一个痛点,而对于一个一天linux书都没有看过的小白来说,可想而知。在经历了将近一个月的痛苦磨折之后,在终于能够在屏幕上ping出那一串串数据时,心中的欣喜喷涌而出,好多话想说,却又不知道该如何说起。

首先一点,linux作为一个以开源互助的理念而成长起来的操作系统,却被某些人变成了赚钱的工具,当一个技术讨论群里只剩下吹牛的和让你交学费的“大神”们时,它早已经脱离了本该的面貌!退出21ic-BeagleBone交流群时,心中实在是一种解脱。

而要特别感谢的,首先是美丽聪慧善良的MariannaZhu师姐:http://bbs.****.com/BLOG_mariabrook_2002381-1.HTM ,她的帖子详细而又深刻,给了很多初学者以引导和启发,而其本人也非常和蔼而乐于助人;其次是AM335x & BeagleBone 133361074群里的三江zhengsj.dianzi@163.com,而他同样的是热心助人的技术大牛,给了我最关键的引导。当然,在学习的过程中还有很多其它的伙伴给了我帮助,在此表示感谢!

------------------------------------------------------------------------------------------------------

首先要调试设备,就要找到对应的设备驱动,如果驱动不正确,那么无论如何去配置,也都是没有办法的。在本例中,需要配置出双网口,根据TI的AM335x CPSW (Ethernet) Driver's Guide,双网口需要配置为Dual EMAC模式(在Device Drivers à Network device support à Ethernet driver support à TI CPSW Switch Support)。

但是,在实际配置过程中,我打开了menuconfig,但是却根本没有找到这个选项!使用查找功能也没有找到Dual EMAC这个词,这时,可以进入到ethernet driver的TI文件夹中,打开kconfig文件,查看里面是否有Dual EMAC这个选项,这个基于你编译时所使用的配置文件,一个更简单粗暴的方式,就是把这些配置信息写到kconfig文件里去,然后再用menuconfig去配置,当然简单这样的配置,往往无法是无法配置出双网口的。

要做的,第一,打开驱动文件,也就是cpsw.c,检查里面是否有双网口相关的内容,如果没有,那么要不就换内核,要不就自己写。由于自己做的板子开始是基于bbb 3.8.13的内核进行开发的,而这个内核的驱动源码里是没有双网口的,因此,我选择换内核(有志者可以换驱动,但是我改了两天,错误越来越多,最后不得已放弃)。

几经查找,所幸找了一个3.12的BBB的内核,附上源码:http://wiki.beyondlogic.org/index.php?title=BeagleBoneBlack_Building_Kernel

而在这里源码里,就很幸运的有了双网口相关的驱动,static int cpsw_probe_dual_emac(struct platform_device *pdev, struct cpsw_priv *priv),而首先使用bbb的默认配置(因为做设计的板子的基本部分都是按照BBB来做的),再一步步在menuconfig中配置出双网口,这次是自动有Dual EMAC选项的,但是,除了要配置这外,还有重要的一点就是配置VLAN support:

这个是在Networking support à Networking options中选择的。

       配置完内核,第二步就是要配置设备树文件,在u-boot中,所有的pinmux配置是在board à TI à mux.c中完成的,但是在3.1以后的内核版本中,所有设备的定义都是在设备树中。对本例,进入设备树文件夹,/home/chenguang/kernel/kernel/arch/arm/boot/dts,对设备树文件am335x-bone-common.dtsi和am335x-boneblack.dts进行修改,(一般来说不主张修改第一个文件,但是由于自己只用到这一个板子,所以关系不大)。[hid][/

首先,添加网口2的pinmux,我所用的板子使用了RGMII1 port和RMII2 port,故配置如下:

cpsw_default: cpsw_default {

              pinctrl-single,pins = <

                     /* Slave 1 */

                     0x114 (0x00 | 0x02)      /* mii1_txen.rgmii1_tctl, (PIN_OUTPUT_PULLDOWN | MUX_MODE2)*/

                     0x118 (0x20 | 0x02)      /* mii1_rxdv.rgmii1_rctl (PIN_INPUT_PULLDOWN | MUX_MODE2)*/

                     0x11c (0x00 | 0x02)       /* mii1_txd3.rgmii1_td3 (PIN_OUTPUT_PULLDOWN | MUX_MODE2)*/

                     0x120 (0x00 | 0x02)     /* mii1_txd2.rgmii1_td2 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) */

                     0x124 (0x00 | 0x02)      /* mii1_txd1.rgmii1_td1 (PIN_OUTPUT_PULLDOWN | MUX_MODE2)*/

                     0x128 (0x00 | 0x02)      /* mii1_txd0.rgmii1_td0 (PIN_OUTPUT_PULLDOWN | MUX_MODE2)*/

                     0x12c (0x00 | 0x02)      /* mii1_txclk.rgmii1_tclk (PIN_OUTPUT_PULLDOWN | MUX_MODE2)*/

                     0x130 (0x20 | 0x02)      /* mii1_rxclk.rgmii1_rclk (PIN_INPUT_PULLDOWN | MUX_MODE2) */

                     0x134 (0x20 | 0x02)      /* mii1_rxd3.rgmii1_rd3 (PIN_INPUT_PULLDOWN | MUX_MODE2)*/

                     0x138 (0x20 | 0x02)      /* mii1_rxd2.rgmii1_rd2 (PIN_INPUT_PULLDOWN | MUX_MODE2)*/

                     0x13c (0x20 | 0x02)      /* mii1_rxd1.rgmii1_rd1 (PIN_INPUT_PULLDOWN | MUX_MODE2)*/

                     0x140 (0x20 | 0x02)      /* mii1_rxd0.rgmii1_rd0 (PIN_INPUT_PULLDOWN | MUX_MODE2)*/

 

                     /* Slave 2 */

                     0x70 (0x20 | 0x03)     /* gpmc_WAIT0.rmii2_crs_dv, P0[30](PIN_INPUT_PULLDOWN | MUX_MODE3)*/

                     0x74 (0x00 | 0x03) /* gpmc_WPN.rmii2_rxerr, P0[31](PIN_OUTPUT_PULLDOWN | MUX_MODE3)*/

                     0x40 (0x00 | 0x03) /* gpmc_a0.rmii2_txen, P1[16] (PIN_OUTPUT_PULLDOWN | MUX_MODE3)*/

                     0x50 (0x00 | 0x03) /* gpmc_a4.rmii2_td1, P1[20](PIN_OUTPUT_PULLDOWN | MUX_MODE3)*/

                     0x54 (0x00 | 0x03) /* gpmc_a5.rmii2_td0, P1[21](PIN_OUTPUT_PULLDOWN | MUX_MODE3)*/

                     0x108 (0x20 | 0x01)      /* mii1_col.rmii2_REFCLK, P3[0](PIN_INPUT_PULLDOWN | MUX_MODE1)*/

                     0x68 (0x20 | 0x03) /* gpmc_a10.rmii2_rd1, P1[26](PIN_INPUT_PULLDOWN | MUX_MODE3)*/

                     0x6c (0x20 | 0x03) /* gpmc_a11.rmii2_rd0, P1[27] (PIN_INPUT_PULLDOWN | MUX_MODE3)*/

                    

                     //0x110 (0x10 | 0x07)    /* gmii1_rxer.rmii2_mux_ctl, P3[2](PIN_OUTPUT_PULLUP | MUX_MODE7)*/

              >;

       };

至于Cpsw_sleep,本例中可以不管。第二步,在mac配置中,添加dual_emmc:

&cpsw_emac0 {

       phy_id = <&davinci_mdio>, <0>;

       dual_emac_res_vlan = <2>;

       phy-mode = "rgmii";

};

 

&cpsw_emac1 {

       phy_id = <&davinci_mdio>, <31>;

       dual_emac_res_vlan = <3>;

       phy-mode = "rmii";

};

 

&mac {

       pinctrl-names = "default", "sleep";

       pinctrl-0 = <&cpsw_default>;

       pinctrl-1 = <&cpsw_sleep>;

       dual_emac;

       status = "okay";

};

至于cpsw_emac中的phy-mode及dual_emac_res_vlan配置,感觉没有什么用(后面会讲如何被它坑),但最好还是加上。同时还要非常注意的一点,在配置时,要避免不同的设备使用了相同的端口

]

       配置完这些功能,就可以编译内核,并且把它更新到你的板子里去,但这个时候,往往会发现使用ifconfig查看网络设备还是只有一个,这个时候不要沮丧,这个只是被激活的网络端口,需要查看真正的网口设备和驱动,查看方法:

查看系统设备:ls /sys/bus/platform/devices

或者查看系统驱动:ls /sys/bus/platform/drivers

再进一步,查看网络设备驱动:/sys/bus/platform/drivers/cpsw/4a100000.ethernet/net

从这里可以看到,实际的以太网设备是有两个的,只是有一个的名字变成了rename3,而我们使用ifconfig查看到的网络设备,实际是在文件系统中被进行配置的,使用:vim /etc/networks/interfaces就可以看到网口的配置信息,包括网口名称,网口ip模式等等参数。

打开文件,没有这个rename3的网口配置,而我在网口测试时,使用ifup eth1就无法打开这个网口了。

       现在修改interface的配置,添加名为rename3的网络:

使用静态地址,这样可以加快系统启动的速度。保存重启,再次查看网设备,终于激动的看到了两个网口的信息:

但是,到这里,离成功还只走了一半,能够通信才能说明网口成功了,很不幸,我的第一次测试以失败告终。

       再继续走之前,先讲一个插曲,由于开始的时候无法确定第二个网口无法配置出来的原因,所以根据启动时的输出信息,猜测是因为usb网口占用了一个MAC口:

可以看出,板子启动时检测到了两个MACID,而usb网口用掉其中的MAC2,

于是,我就在内核的配置中禁止掉了usb网口,事后发现usb网口对ethernet口没有影响,但是这种排除干扰的调试方法,在实际中还是有很好的效果的。

       回到网口调试,上一步中虽然配置出了双网口,但却无法ping到另一个计算机,检查了设备树和网络基本配置,这时就只有两种可能了,第一,硬件问题,如果出现了这个问题,那调试起来就相当麻烦;第二,端口配置问题。一般来讲,要使能某项功能,不是单独配置引脚复用就可以实现的,必定有相应的寄存器,本例中第二个网口使用的是RMII接口,而kernel中关于网口模式的选择,除了在设备树中有,其它的地方就再没找到(可能仍然是存在的)。无奈之下,回到u-boot中,在board.c文件中,有一个关于网口的配置,最初第一个网口由MII配置成RGMII,也是在这里实现,再次观察可以发现,里面除了mac地址的配置信息外,还有一个:

              writel((RGMII_MODE_ENABLE | RGMII_INT_DELAY), &cdev->miisel);

              cpsw_slaves[0].phy_if = PHY_INTERFACE_MODE_RGMII;

              cpsw_slaves[1].phy_if = PHY_INTERFACE_MODE_RMII;

通过测试发现,当选择:

writel((RGMII_MODE_ENABLE | RGMII_INT_DELAY), &cdev->miisel); 时,RGMII端口可以正常工作,但RMII端口不能正常工作(配置正确);

当选择:writel(MII_MODE_ENABLE, &cdev->miisel); 时,发现两个端口都不能正常工作。

首先查看writel函数的功能:#define writel(b,addr) ((*(volatile u32 *) (addr)) = (b)),可以大致看出是对内存的操作,而这里应该是配置寄存器。

再看RGMII_MODE_ENABLE的定义:在

/home/chenguang/u-boot/arch/arm/include/asm/arch-am33xx/cpu.h

#define MII_MODE_ENABLE        (GMII1_SEL_MII | GMII2_SEL_MII)

#define RMII_MODE_ENABLE        (GMII1_SEL_RMII | GMII2_SEL_RMII)

#define RGMII_MODE_ENABLE  (GMII1_SEL_RGMII | GMII2_SEL_RGMII)

#define RGMII_INT_DELAY         (RGMII1_IDMODE | RGMII2_IDMODE)

#define RMII_CHIPCKL_ENABLE     (RMII1_IO_CLK_EN | RMII2_IO_CLK_EN)

从这里就可以发现问题,在配置MII端口功能时,系统并没有定义将两个端口(RGMII1和RGMII2)配置成不同模式的选项,再看writel((RGMII_MODE_ENABLE | RGMII_INT_DELAY), &cdev->miisel); 函数,其后面的要写入的地址&cdev->miisel,查找其定义:

static struct ctrl_dev *cdev = (struct ctrl_dev *)CTRL_DEVICE_BASE;

再查看CTRL_DEVICE_BASE定义:

#define CTRL_DEVICE_BASE          0x44E10600

Miisel定义,还是在arch-am33xx/cpu.h中:

这里就可以发现与在网上找的一点资料有了相同之处:

http://www.deyisupport.com/question_answer/dsp_arm/sitara_arm/f/25/t/82706.aspx

在u-boot中使用md命令查看内存信息:

(原)

坑爹的一点,这个寄存器在AM335x的datasheet上找不到,而TI的工作人员也没有给出具体说明,不过从注释可以看出,这个地址就是MII模式选择寄存器,而cpu.h中默认的宏定义却没有我要的选项,没办法,这里就需要自己来重新定义一个配置模式了:#define RGMII_RMII_MODE_ENABLE  (GMII1_SEL_RGMII | GMII2_SEL_RMII)  

这样就可以配置两个网口为不同工作模式了。

配置好后,再次查看寄存器信息:

可以看到,44e10650处的值就发生了变化,再次重启,这个时候,最激动人心的时刻马上就与可以到来了,轻轻的输入ping,一串串整齐的数据就出来了:

       但是,不要高兴的太早,仔细一看,虽然ping成功了,但是却有很多包丢失的现象,最后发现,一般ping的丢包率在30%左右,这在实际中是绝对不允许发生的,继续查找原因。

       到这个时候,事情看起来更像是硬件问题了,但是,怎么能甘心如此放弃?再看配置,看芯片手册,看RMII通信原理……在cpu.h中,可以看到一个关于RMII的宏定义:RMII_CHIPCKL_ENABLE,查看datasheet,可以发现,这个配置的功能就是选择是否启用CPU的RMII接口参考时钟,由于在设计电路时,自己记得在RMII接口上有参考时钟这一部分,并且这个时钟接在了PHY芯片的时钟输出口,那么问题就很明显了,没有配置选择外部才考时钟,cpu使用内部自带时钟,导致参考不一致,丢包就不可以避免了,

到这里,故事就该结束了,配置完端口,配置完时钟,最后一切都跟预想一样了:

至于如何修复在网口驱动列表中出现rename3的现象,就要参考我的另一个帖子了。

 

 

 

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客