之前调试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有关,这里就不多了解了。
文章评论(0条评论)
登录后参与讨论