赞
踩
RTL8306SD 芯片支持VLAN功能,这颗芯片比较老了,据说已经停产,最新的RTL8306E,但坑的是,realtek的技术支持,没用过这个芯片,无法提供技术支持和资料,网上资料太少,最终是在某论坛里找到了一些api驱动资料和说明,datasheet 规格书看了一遍又一遍,慢慢清晰起来。由于先前对这个芯片不了解,直接参考了原理图,导致设计有些错误。调试过程也走了不少弯路,在此记录。
涉及的代码部分已经上传在我的资源里,需要的同学可以自行下载。
先来看一下框图:
该芯片有5路 phy接口,其中第5口 phy 具有复用功能。 另外有两路 mii/rmii 接口,可以通过不同的配置工作在不同模式下。 这里的配置有点复杂。经过这几天调试,基本摸清楚了。
接下来说一下可以配置为哪些模式。
1.先说一下 phy4(第五个phy), phy4可以与 mac4 一起工作在 UTP模式,什么是UTP模式??就是正常的工作模式 和上面几路phy 一致。除此外 phy4还可以配置为独立的phy,通过mii/rmii 接外部 mac 如图。此时 phy4 与其他phy是独立的,不互通。
2. 再来说一下 MAC4 和 MAC5. 他们都可以配置为:
1. PHY MODE MII
2. MAC MODE MII
3. RMII 模式
上面两种模式我还不太确定具体接什么,但可以确定的是,一种模式是接外置phy的,另一种接 cpu的。我需要用的是RMII模式。
我的需求是 MAC4 PHY4 工作在 UTP模式,MAC5工作在RMII模式。 对应配置表。 是01000,付上配置表。
调试过程如下。
1.硬件部分调试
板子回来后,上电,cpu能正常启动,但是没识别出网卡。
1. 按以往经验判断,应该是MDIO总线没通讯成功。示波器查了波形,芯片果然没响应。
2. 先查 晶振,复位,3.3V电源,1.8V电源。挨个查了一遍。都正常。插上网线,灯不闪。
3. 这样判断 内部逻辑电路可能没启动,通常phy部分,只要插上网线灯就会亮。再次仔细检查电源,发现有个AVDD18接错了,接到了GND上。飞线大法后,插上网线指示灯已经可以闪了,说明内部电路已经开始工作。 奇怪的是这路AVDD接错,芯片没烧,没任何反应,说明这个AVDD18 在芯片内部是独立供电的。 找到第1个坑
4. 重启板子后,发现网卡已经识别出来,说明MDIO已经通讯成功。 测试了4个网口,相互联通的。
5. 但是网络一直不通,没有数据包。把模式配置为 01000后,查了波形。发现RMII没有任何波形。refclock也没有。印象中refclock方向可以配置,翻了datasheet, 81脚需要拉低,82脚才会对外输出refclock。第2个坑
6. 再次飞线,refclock已经有了,示波器量了,标准的50M,说明已经工作在rmii模式了,如果是MII模式,refclock是25M.
7.但数据还是不通,量RMII TX是低电平, RX是高电平。有点疑问。正常情况没有数据通讯的时候应该都是低电平。可能数据线接错了。 这里引入一个新的知识点,也是在这次调试中了解的。 有种接法叫 mac to mac without phy,也叫 fixed-link。顾名思义,是一个固定连接,用于两个有mac控制器的cpu 直连,不通过phy。 这样可以省去2个phy 和 网络变压器等外部器件。由于直连,没有MDIO通讯,所以如果要直连,底层网卡驱动需要调整。MAC直连的时候,需要交叉连接 如下图。
TXD0 | RXD0 |
TXD1 | RXD1 |
TX_EN | RX_DV |
我们的板子接错了。由于芯片的rmii可配置的模式太多,同一个引脚可以配置为tx或rx. 导致误导了我们硬件工程师。实际上同一个引脚的rx或tx。是站在不通角度而言的。在phy 模式的tx,就是 mac模式的rx,数据方向都是一致的。第3个坑
8. 依然是飞线大法,之后再查,发现已经有波形了8306的tx0,tx1,tx_en都有波形输出,但是还是收不到数据。接收数据包是0
难道是时序不对,请出逻辑分析仪来一探,结果逻辑分析仪太楼,最高只能测100M,而RMII是50M的数据。100M就100M吧勉强能用,按照采样定理,回顾一下:采样定理,又称香农采样定理,奈奎斯特采样定理,只要采样频率大于或等于有效信号最高频率的两倍,采样值就可以包含原始信号的所有信息,被采样的信号就可以不失真地还原成原始信号。
在100M采样的时候只支持3个通道,好吧,3个就3个,大不了分组采,因为数据包相同的,我网线接了一个只ping 一个固定地址板子。1组 refclock,txd0, tx_en. 2组 refclock txd1 tx_en.
看波形应该没问题,怀疑tx和rx时序格式可能不对。但是MAC能与MAC直连,肯定说明格式是一致的。
换了一个方法调试的这块板子,ping 外面,ping肯定是不通的,但能知道对方是否收到数据。简单的来说,就看对方数据包是否增加了,高级点就用抓包工具,我用了tcpdump,发现能收到板子发出的数据包。 更加确信了 tx与rx的格式是一致的。 tx_en就是对方rx_dv。那为什么 MAC过来的数据收不到呢? 又查了一遍RMII接口,发现漏了一个rx_err 信号,非常重要的引脚,RX还有一个 RX_ERR 信号。这个怎么接?RX_ERR 这个信号是可选的,但调试被它卡了2天的时间。 这个信号的作用是 phy芯片通知cpu 检测到载波错误,高电平有效。这时cpu就会丢弃掉接收的数据。 第4个坑
9.飞线 直接拉rx_err 引脚到gnd,再次测试,已经能ping通了。测试了与其他网口都能互通,到此芯片调的差不多了。
到目前为止 rtl8306的几路网口都已经联通了,但还没有工作在vlan模式。根据芯片资料描述,要工作在vlan模式,需要把93脚拉低。
但是这个引脚拉低后,网络就不通了。根据判断,硬件连接这时肯定没问题了,应该是需要软件控制。
2.软件部分调试
这个芯片支持vlan功能,vlan功能是通过寄存器控制的,规格书上写的清清楚楚。但是坑的是datasheet上,故意隐去了关于vlan部分的寄存器描述,只保留了基本的phy寄存器描述。 还好在某论坛找到了api驱动资料。这样其实不必关心内部寄存器了,只要会用api接口,就可以控制vlan功能。vlan功能只是这个芯片多种功能中的其中一种,我们这里只要用到vlan就行了。
寄存器通过MDIO总线控制,我的系统运行环境是linux。官方驱动更像是运行在单片机上的驱动类型。所以我决定把驱动做成一个应用层的工具,通过网卡驱动的ioctl接口读写mdio。通过这个工具来控制vlan配置。
主要代码如下: 可以通过这个工具调用2个设置vlan的主要函数
- void vlan_config()
- {
- rtk_vlan_init();
- }
-
- void printhelp()
- {
- printf("vlanmgr V0.1 \n\n");
- printf("Usage: vlanmgr [-i|-v|-p|-s] ... \n");
- printf(" -i Init the vlan chip\n");
- printf(" -v Set vlan\n");
- printf(" Format: vlanmgr -v vid mbmsk(hex) untagmsk(hex) \n");
- printf(" eg: vlanmgr -v 1 3f 00 \n");
- printf(" -p Set portPvid\n");
- printf(" Format: vlanmgr -p port pvid prio \n");
- printf(" -s show the vlan setting\n");
- }
-
- void enablePortlink()
- {
- rtk_port_mac_ability_t pPortability;
- pPortability.forcemode = 1;
- pPortability.nway = 0;
- pPortability.speed = PORT_SPEED_100M;
- pPortability.link = 1;
- pPortability.duplex = PORT_HALF_DUPLEX;
- pPortability.rxpause = 0;
- pPortability.txpause = 0;
-
- rtk_port_macForceLinkExt0_set(MODE_EXT_RMII,&pPortability);
- }
-
- int main(int argc,char *argv[])
- {
-
- if(mdio_open("eth0") == 0)
- {
- printf(" mdio_open fail \n");
- return 0;
- }
-
- if(argc > 1)
- {
- if(strcmp(argv[1],"help")==0 || strcmp(argv[1],"-h")==0)
- {
- printhelp();
- }
- else if(strcmp(argv[1],"-i")==0)
- {
- printf(" vlan chip init\n");
- rtk_switch_init();
- vlan_config();
-
- enablePortlink();
- }
- else if(strcmp(argv[1],"-s")==0)
- {
- rtk_portmask_t mbrmsk, untagmsk;
- rtk_fid_t fid;
- rtk_vlan_t pvid;
- rtk_pri_t prio;
- int i;
- for(i=1;i<6;i++)
- {
- rtk_vlan_get(i,&mbrmsk,&untagmsk,&fid);
- printf(" vlan %d mbrmsk[%02X] untagmsk[%02X]\n",i,mbrmsk.bits[0],untagmsk.bits[0]);
- }
-
- for(i=0;i<6;i++)
- {
- rtk_vlan_portPvid_get(i,&pvid,&prio);
- printf(" port %d pvid[%d] prio[%d]\n",i,pvid,prio);
- }
- }
- else if(strcmp(argv[1],"-v")==0)
- {
- int vid;
- char cmmsk,cumsk;
- rtk_portmask_t mbrmsk;
- rtk_portmask_t untagmsk;
- rtk_api_ret_t ret;
- if(argc < 5)
- {
- printf(" vlan set param error!\n");
- return 0;
- }
- vid = atoi(argv[2]);
- common_string_to_hex(argv[3],&cmmsk,1);
- common_string_to_hex(argv[4],&cumsk,1);
-
- mbrmsk.bits[0] = cmmsk;
- untagmsk.bits[0] = cumsk;
-
- ret = rtk_vlan_set(vid,mbrmsk,untagmsk,0);
- if(ret != RT_ERR_OK)
- printf(" vlan set error[%d]! vid[%d] mbrmsk[%02X] untagmsk[%02X]\n",ret,vid,mbrmsk.bits[0],untagmsk.bits[0]);
- else
- printf(" vlan set OK, vid[%d] mbrmsk[%02X] untagmsk[%02X]\n",vid,mbrmsk.bits[0],untagmsk.bits[0]);
- }
- else if(strcmp(argv[1],"-p")==0)
- {
- int port;
- int pvid;
- int prio;
- rtk_api_ret_t ret;
- if(argc < 5)
- {
- printf(" set port Pvid param error!\n");
- return 0;
- }
- port = atoi(argv[2]);
- pvid = atoi(argv[3]);
- prio = atoi(argv[4]);
- ret = rtk_vlan_portPvid_set(port,pvid,prio);
- if(ret != RT_ERR_OK)
- printf(" port Pvid set error[%d]! port[%d] pvid[%02X] prio[%02X]\n",ret,port,pvid,prio);
- else
- printf(" vlan set OK, port[%d] pvid[%02X] prio[%02X]\n",port,pvid,prio);
- }
-
- }
- else
- {
- printhelp();
- }
-
-
- return 1;
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这个芯片有6个phy,地址分别是 0,1,2,3,4,5,6。 默认网卡驱动初始化的时候,扫描mdio总线,把所有找到的phy都加入phymap中,但是只会取第一个phy作为网卡的phy。这样的话,不是我们要的效果,网线的状态是跟随第一个网口的。所以驱动要调整一下。
再内核中找到 网卡驱动 nuc970_ether0.c, 源码稍后补上。只改一处即可,把第一个phy改 phy_map[6]。
- static int nuc970_mii_setup(struct net_device *dev)
- {
- ...
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- ether->mii_bus->irq[i] = PHY_POLL;
- //ether->mii_bus->irq[1] = ?? write me after the irq number is known
-
- if (mdiobus_register(ether->mii_bus)) {
- dev_err(&pdev->dev, "mdiobus_register() failed\n");
- goto out2;
- }
-
- #if 0
-
- phydev = phy_find_first(ether->mii_bus);
- #else
- phydev = ether->mii_bus->phy_map[6]; //强制设置为第六个phy
- #endif
- if(phydev == NULL) {
- err = -ENODEV;
- dev_err(&pdev->dev, "phy_find_first() failed\n");
- goto out3;
- }
- printk("###################################\n");
- printk("######## phy_find_first id = %d\n",phydev->addr);
- dev_info(&pdev->dev, "######## phy_find_first id = %d\n",phydev->addr);
- //phydev = &phydev[5];
- //dev_info(&pdev->dev, "######## chg phydev to id = %d\n",phydev->addr);
-
- //printk("######## chg phydev to id = %d\n",phydev->addr);
- printk("###################################\n");
-
- ...
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
最后是工具的用法:
参数说明:
-i 初始化RTL8306SD芯片,初始化之后网络才通。初始化之后所有网口在一个vlan分组,所有网口互通。
-v 设置vlan 分组。 需要3个参数,1vid,2 mbrmask,3.untagmsk。
vid:表示vlan的编号 从1开始。
mbrmask:分组掩码: 用于配置vlan分组中有哪些端口。
例如:port0,port1。在一组。对应的mask 就是 0x03(二进制0000 0011)
port3,port5在一组,对应mbr mask是 0x14 (二进制 0001 0100)
untagmsk:设为00即可。
-p 设置端口的Pvid,作用是确定这个端口属于第几个vlan分组。
3个参数分别是 端口号(0开始),pvid编号(1开始对应vlan),优先级。
例如:port0 属于第1个vlan分组: vlanmgr -p 0 1 0
-s 查看当前vlan配置。
初始化之后查看vlan分组如下图, 所有端口在一个分组vlan1中。
配置举例:
当前板子port5是连cpu的端口,port4是内置网口,port0-3是对外网口。cpu端口需要同时能连通2个vlan分组。
假设划分:port4,port5互通。 port0-3,port5也互通。port4,port0-3 相互之间不通。
需要划分3个vlan分组:如下
vlan配置
vlan id | 成员 | mbrmask |
1 | port 4,port5 | 0x30(0011 0000) |
2 | port0-3,port5 | 0x27(0010 0111) |
3 | port 0-5 | 0x3F (0011 1111) |
配置每个port0,属于哪个vlan分组。
pvid配置
端口号 | pvid | 说明 |
0 | 2 | port0-3 处于第2个vlan分组,成员是 port0-3 还有 port5. |
1 | 2 | |
2 | 2 | |
3 | 2 | |
4 | 1 | port4,属于第一个vlan分组,成员是port4,port5 |
5 | 3 | port5,属于第3个vlan分组,成员是全部端口。 |
命令如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。