原创 为AM335x移植Linux内核主线代码(36)调试Ethernet之一

2015-2-13 20:45 2341 11 11 分类: MCU/ 嵌入式 文集: Linux Kernel的DTS

之前调试U-Boot的时候,Ethernet花费的时间是最长的,这主要是因为它对电路板的布局要求很高,而且调试需要理解MDIO以及MII/GMII/RMII/RGMII协议之间的区别。有了U-Boot调试的基础,它在Kernel中的移植就不会那么困难啦~

Let's begin.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Step 1:
去掉maria-am335x-common.dts中未被俺的主控板使用的部分,将程序员洁癖发扬到底:
(注意新修改的@@行数是上一步修改之后的基础上的行数,因为懒得记原来的行数了。。。)
将vmmcsd_fixed去掉,因为SD的供电是底板上的+3.3V,和核心板上面的PMIC没有关系;
将clkout2_pin去掉,因为这个引脚悬空了,并没有被使用;
将usb1去掉,因为这里只使用了usb0;

diff a/arch/arm/boot/dts/maria-am335x-common.dts b/arch/arm/boot/dts/maria-am335x-common.dts
--- a/arch/arm/boot/dts/maria-am335x-common.dts
+++ a/arch/arm/boot/dts/maria-am335x-common.dts
@@ -33,7 +33,0 @@

-
-        vmmcsd_fixed: fixedregulator@0 {
-                compatible = "regulator-fixed";
-                regulator-name = "vmmcsd_fixed";
-                regulator-min-microvolt = <3300000>;
-                regulator-max-microvolt = <3300000>;
-        };

@@ -37,1 +37,0 @@
-        pinctrl-0 = <&clkout2_pin>;

@@ -58,6 +58, 0 @@      
 
-        clkout2_pin: pinmux_clkout2_pin {
-                pinctrl-single,pins = <
-                        0x1b4 (PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr1.clkout2 */
-                >;
-        };
-

@@ -153,4 +153,0 @@

- &usb1_phy {
-         status = "okay";
- };
-

@@ -157,5 +157,0 @@

- &usb1 {
-         status = "okay";
-         dr_mode = "host";
- };
-

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Step 2:
编写dts文件中有关Ethernet的部分,包括使用的MII引脚以及MDIO引脚。这里参考之前U-Boot的内容:
标准的MII使用了15个引脚,有些是输入,有些是输出,根据功能相应的进行设置设置。txclk为什么是输入呢?因为MII协议需要的tx时钟是PHY芯片提供的,而不是CPU提供的。
去掉emac1的部分,因为这里只使用了emac0;
另外去掉sleep模式,即使sleep,也认为pinmux不需要变化;
最后将phy_id改为2,这个地址由PHY芯片的地址配置引脚决定,根据实际情况修改即可;

diff a/arch/arm/boot/dts/maria-am335x-common.dts b/arch/arm/boot/dts/maria-am335x-common.dts
--- a/arch/arm/boot/dts/maria-am335x-common.dts
+++ a/arch/arm/boot/dts/maria-am335x-common.dts
@@ -58,37 +54,19 @@

-        cpsw_default: cpsw_default {
-                pinctrl-single,pins = <
-                        /* Slave 1 */
-                        0x110 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxerr.mii1_rxerr */
-                        0x114 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */
-                        0x118 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxdv.mii1_rxdv */
-                        0x11c (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */
-                        0x120 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */
-                        0x124 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */
-                        0x128 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */
-                        0x12c (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_txclk.mii1_txclk */
-                        0x130 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxclk.mii1_rxclk */
-                        0x134 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd3.mii1_rxd3 */
-                        0x138 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd2.mii1_rxd2 */
-                        0x13c (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd1.mii1_rxd1 */
-                        0x140 (PIN_INPUT_PULLUP | MUX_MODE0)    /* mii1_rxd0.mii1_rxd0 */
-                >;
-        };
-
-        cpsw_sleep: cpsw_sleep {
-                pinctrl-single,pins = <
-                        /* Slave 1 reset value */
-                        0x110 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x114 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x118 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x11c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x120 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x124 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x128 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x12c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x130 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x13c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x140 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                >;
-        };
+        cpsw_default: cpsw_default {
+                pinctrl-single,pins = <
+                        0x118 (PIN_INPUT  | MUX_MODE0) /* mii rxdv */
+                        0x114 (PIN_OUTPUT | MUX_MODE0) /* mii txen */
+                        0x110 (PIN_INPUT  | MUX_MODE0) /* mii rxerr */
+                        0x130 (PIN_INPUT  | MUX_MODE0) /* mii rxclk */
+                        0x12C (PIN_INPUT  | MUX_MODE0) /* mii txclk */
+                        0x108 (PIN_INPUT  | MUX_MODE0) /* mii col */
+                        0x10C (PIN_INPUT  | MUX_MODE0) /* mii crs */
+                        0x140 (PIN_INPUT  | MUX_MODE0) /* mii rxd0 */
+                        0x13C (PIN_INPUT  | MUX_MODE0) /* mii rxd1 */
+                        0x138 (PIN_INPUT  | MUX_MODE0) /* mii rxd2 */
+                        0x134 (PIN_INPUT  | MUX_MODE0) /* mii rxd3 */
+                        0x128 (PIN_OUTPUT | MUX_MODE0) /* mii txd0 */
+                        0x124 (PIN_OUTPUT | MUX_MODE0) /* mii txd1 */
+                        0x120 (PIN_OUTPUT | MUX_MODE0) /* mii txd2 */
+                        0x11C (PIN_OUTPUT | MUX_MODE0) /* mii txd3 */
+                >;
+        };

@@ -78,15 +78,6 @@

-        davinci_mdio_default: davinci_mdio_default {
-                pinctrl-single,pins = <
-                        /* MDIO */
-                        0x148 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0)   
-                        0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0)                   
-                >;
-        };
-
-        davinci_mdio_sleep: davinci_mdio_sleep {
-                pinctrl-single,pins = <
-                        /* MDIO reset value */
-                        0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                        0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7)
-                >;
-        };
+        davinci_mdio_default: davinci_mdio_default {
+                pinctrl-single,pins = <
+                        0x148 (PIN_INPUT_PULLUP  | SLEWCTRL_FAST | MUX_MODE0)   /* mdio data */
+                        0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0)                   /* mdio clk */
+                >;
+        };

@@ -201,5 +201,0 @@

- &cpsw_emac1 {
-         phy_id = <&davinci_mdio>, <1>;
-         phy-mode = "mii";
- };
-

@@ -201,13 +201,11 @@

- &mac {
-        pinctrl-names = "default", "sleep";
-        pinctrl-0 = <&cpsw_default>;
-        pinctrl-1 = <&cpsw_sleep>;
-        status = "okay";
- };
-
- &davinci_mdio {
-        pinctrl-names = "default", "sleep";
-        pinctrl-0 = <&davinci_mdio_default>;
-        pinctrl-1 = <&davinci_mdio_sleep>;
-        status = "okay";
- };
+ &mac {
+        pinctrl-names = "default";
+        pinctrl-0 = <&cpsw_default>;
+        status = "okay";
+ };
+
+ &davinci_mdio {
+        pinctrl-names = "default";
+        pinctrl-0 = <&davinci_mdio_default>;
+        status = "okay";
+ };

@@ -197,2 +197,2 @@

-        phy_id = <&davinci_mdio>, <0>;
-        phy-mode = "mii";
+        phy_id = <&davinci_mdio>, <2>;
+        phy-mode = "mii";

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Step 3:
make menuconfig时去掉多余的选项,添加对realtek的支持:

Device Drivers -> Network device support -> Ethernet driver support
留下 Texas Instruments (TI) devices 即可;
Device Drivers -> Network device support -> PHY Device support and infrastructure
留下 Drivers for Realtek PHYs 即可;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Step 4:
这一步是非常痛苦的,因为需要很多很多。。。很多很多。。。。很多的打印信息。
之前调试U-Boot的时候,发现绘制的Ethernt PHY测试板是存在布线问题的(当然也可能是由于MII信号使用杜邦线和主控板相连的原因),导致它只能工作在10Mbps速率上,而无法达到100Mbps。

鉴于调试Linux Kernel也是使用这块Ethernet PHY测试板进行调试,因此需要让Kernel的驱动程序也工作在10Mbps。设置速率的过程也是学习Ethernet驱动的过程,只是Linux Kernel的Ethernet驱动比U-Boot的要复杂太多,因此hope us做好了充分的心理准备。

MDIO的驱动在drivers/net/ethernet/ti/davinci_mdio.c中,这个相对简单,它是一个platform设备,在probe函数里很容易就能读取mdio各种各样的寄存器值,当然要注意dts文件中需设置实际的phy_id值,也就是PHY芯片的地址。还有drivers/net/ethernet/ti/cpsw.c文件,里面是cpsw外设的初始化。

[    2.908040] ===> phy[2]: device 4a101000.mdio:02, driver RTL8201CP Ethernet
[    2.915374] davinci_mdio 4a101000.mdio: phy[2]: device 4a101000.mdio:02, drivt
[    2.924903]  0x00001100  0x0000786d  0x00000000  0x00008201
[    2.931203]  0x00000061  0x000045e1  0x00000001  0x00000000
[    2.937508]  0x00000000  0x00000000  0x00000000  0x00000000
[    2.943748]  0x00000000  0x00000000  0x00000000  0x00000000
[    2.950118]  0x0000000c  0x00000080  0x00000000  0x00000023
[    2.956423]  0x00000c59  0x00000406  0x00001100  0x00008560
[    2.962663]  0x00008a7b  0x00000286  0x00000000  0x00000000
[    2.969033]  0x00000000  0x00000000  0x00000000  0x00000000
[    2.976521] cpsw 4a100000.ethernet: Missing slave[1] phy_id property
[    2.983377] =====> is valid_ether_addr enter
[    2.987947] cpsw 4a100000.ethernet: Detected MACID = 20:cd:39:e2:03:96

上面的信息很容易就得到了,因为MDIO和cpsw的设置相对easy,这里就不多记录了~~~~
比较麻烦的地方在于对Ethernet和PHY芯片的配置,如下所示:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~
首先,来关注net/ipv4/af_inet.c这个文件:
它的初始化函数是:
static int __init inet_init(void);

inet_init函数执行了很多的初始化步骤,观察它会发现包含的很多(非常多)的xx_init函数。比如:
rc = proto_register(&tcp_prot, 1);
rc = proto_register(&udp_prot, 1);
arp_init();
ip_init();
tcp_v4_init();
tcp_init();
……

它的ioctl函数是:
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);

inet_ioctl函数执行了很多的设置步骤,观察它会发现包含的很多(非常多)的SIOC*命令选项,比如:
case SIOCGSTAMP:
case SIOCGSTAMPNS:
case SIOCSIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCSIFDSTADDR:
case SIOCSIFPFLAGS:
case SIOCGIFPFLAGS:
case SIOCSIFFLAGS:
……

(Note: 在默认的初始化打印信息里面有这么一句:)
(Configuring network interfaces...)
(打印它之后就是执行各种参数的inet_ioctl函数啦~~)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~
接下来,来关注inet_ioctl函数到底干了什么:
由于这里的inet_ioctl函数被赋值为struct proto_ops方法,找到调用它的函数比较麻烦,所以这里仅仅关注每次它被调用都发生了什么。

第一次执行inet_ioctl函数:
[   17.261257] ================================> inet_ioctl SSSS
[   10.143052] ====> ioctl_num = 1 cmd = 0x8916                                 
[   10.147654] ====> delay 5s.                                                  
[   15.124817] ====> GIFADDR/SIFADDR ......                                     
[   15.128940] ====> devinet_ioctl SSSS                                         
[   15.132685] ====> ifr.ifr_name = lo                                          
[   15.136371] ====> switch (cmd) 1                                             
[   15.139762] ====> switch (cmd) 2                                             
[   15.143237] ====> dev->name = lo                                             
[   15.146644] ====> ifa_address = 0x0100007f                                   
[   15.150933] ====> no IFF_POINTOPOINT                                         
[   15.155428] ====> devinet_ioctl EEEE ret = 0                                 
[   15.159908] =========================> inet_ioctl EEEEEEEEEEEEEE

0x8916被定义为宏SIOCSIFADDR,它会跳转到net/ipv4/devinet.c文件中的devinet_ioctl函数去执行;
它的功能是Set interface address (and family);

ifr.ifr_name = lo;
ifa_address = 0x0100007f;                                 
是不是有点眼熟呢?想起来了不?在终端执行ifconfig会打印网卡信息,lo就是本机地址,它的值是127.0.0.1,也就是0x0100007f倒过来的值。

第二次执行inet_ioctl函数:
[   22.325541] ================================> inet_ioctl SSSS                
[   22.331659] ====> ioctl_num = 2 cmd = 0x8913                                 
[   22.336254] ====> delay 5s.                                                  
[   27.312631] ====> NO VALID SIOC*                                             
[   27.316019] =========================> inet_ioctl EEEEEEEEEEEEEE

0x8913被定义为宏SIOCGIFFLAGS,没这个选项,因此会进入switch的default选项,执行:
sk->sk_prot->ioctl(sk, cmd, arg);
sk和socket有关,这里就不多了解了。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
11
关闭 站长推荐上一条 /3 下一条