"); //-->
提起驱动,可能就是开发人员的一个痛点,而对于一个一天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的现象,就要参考我的另一个帖子了。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。