网卡驱动9-linux内核3.0.8的mdio_bus\phy_device\phy_driver
来源:互联网 发布:alphago软件 编辑:程序博客网 时间:2024/04/29 11:41
上次说了MII
还有RMII GMII RGMII、SGMII等,
GMII:
与MII接口相比,GMII的数据宽度由4位变为8位, 发送参考时钟GTX_CLK和接收参考时钟RX_CLK的频率均为125MHz(1000Mbps/8=125MHz)。支持MII模式。这个GMII可用于1000M网。
RGMII:
由于GMII线太多,RGMII把数据位宽变为4位,在时钟的上升沿和下降沿都采样数据。
本人现在用的1000M网卡用的急速RGMII接口。同时支持MII。
我当前用的SOC集成了MAC,有两个MAC,支持MII和RGMII,一个MDIO接口。
我的硬件是有两个网口,一个MAC对应一个PHY,一个MDIO总线上接两个PHY。一个PHY地址ID为1,另一个为2。内核驱动STMicroelectronics 公司的stmmac/,版本linux-3.0.8
看一下stmmac的源码,可以看出phy管理用的不是我们上篇说的mii.c。而是drivers/net/phy/下的东西。
我们关心的代码是phy.c phy_device.c mdio_bus.c 还有include /linux/phy.h
这些东西的使用和上次的mii.c使用比较的话,会给人一种总线、设备、驱动的概念。不是想上次的mii就是提供mdio读写去操作mii接口。
Bus:
struct mii_bus {const char *name;char id[MII_BUS_ID_SIZE];void *priv;int (*read)(struct mii_bus *bus, int phy_id, int regnum);int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);int (*reset)(struct mii_bus *bus);/* * A lock to ensure that only one thing can read/write * the MDIO bus at a time */struct mutex mdio_lock;struct device *parent;enum {MDIOBUS_ALLOCATED = 1,MDIOBUS_REGISTERED,MDIOBUS_UNREGISTERED,MDIOBUS_RELEASED,} state;struct device dev;/* list of all PHYs on bus */struct phy_device *phy_map[PHY_MAX_ADDR];/* PHY addresses to be ignored when probing */u32 phy_mask;/* * Pointer to an array of interrupts, each PHY's * interrupt at the index matching its address */int *irq;};在驱动中,我们要面对的接口。现在看看stmmac中对它的使用。在stmmac_mdio.c中,看一些关键代码:stmmac_mii_bus = mdiobus_alloc();//动态分配if (stmmac_mii_bus == NULL)return -ENOMEM;irqlist = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);//分配phy irq表if (irqlist == NULL) {err = -ENOMEM;goto irqlist_alloc_fail;}/* Assign IRQ to phy at address phy_addr */if (priv->phy_addr != -1)//我当前的stmmac源码没有支持phy中断irqlist[priv->phy_addr] = priv->phy_irq;/*可以到此去看看如何加一个phy,可能要代理http://www.stlinux.com/kernel/network/phy-howto */ //下面检测时钟是否正常,主要是获取总线时钟,再计算toe时钟,是否合法。tnkclk = mdio_clk_init();priv->plat->clk_csr = get_clk_csr(tnkclk);if (priv->plat->clk_csr == -1) {pr_err("Can not get mdio clk.\n");goto bus_register_fail;}stmmac_mii_bus->name = "STMMAC MII Bus";stmmac_mii_bus->read = &stmmac_mdio_read;//读stmmac_mii_bus->write = &stmmac_mdio_write;//写stmmac_mii_bus->reset = &stmmac_mdio_reset;// 复位snprintf(stmmac_mii_bus->id, MII_BUS_ID_SIZE, "%x", priv->plat->bus_id);//stmmac中是1stmmac_mii_bus->priv = ndev;stmmac_mii_bus->irq = irqlist;stmmac_mii_bus->phy_mask = priv->phy_mask;//这里设为了0,没有地址被忽略stmmac_mii_bus->parent = priv->device;err = mdiobus_register(stmmac_mii_bus);//注册总线。if (err != 0) {pr_err("%s: Cannot register as MDIO bus\n", stmmac_mii_bus->name);goto bus_register_fail;} 在mdiobus_register()中,有一段phy扫面程序for (i = 0; i < PHY_MAX_ADDR; i++) {if ((bus->phy_mask & (1 << i)) == 0) {struct phy_device *phydev;phydev = mdiobus_scan(bus, i);if (IS_ERR(phydev)) {err = PTR_ERR(phydev);goto error;}}}
Mdiobus_scan最终会去读:
MII_PHYSID1,MII_PHYSID2
这个在上一篇的MII寄存器上说过。
获得的id会存入structphy_device的phy_id。这个structphy_device会被存入bus的phy_map里面。
系统启动打印就是:
可以看到地址1和地址2的phy active.
还有IRQ -6其实是- ENXIO:No such device or address
我们在看看write read reset
Stmmac有一个GMII地址寄存器和数据寄存器
Read:
把要读的phy中的地址寄存器的值写入stmmac的GMII地址寄存器
等待地址寄存器第0位(busy)为0
读GMII数据寄存器,获得phy的相关设置值。
Write:
把要写的phy中的地址寄存器的值写入stmmac的GMII地址寄存器
把数据写入GMII数据寄存器
等待地址寄存器第0位(busy)为0
Reset:
当前就是把stmmac的GMII地址寄存器清零
现在我就当已经为内核提供bus完毕。
Device:
这个在mdiobus_scan时已经注册了,通过get_phy_device()获取phy的id,再把搜索到设备时会做phy_device_register动作。我们不具体看了。注册完之后,device_phy_id和driver_phy_id进程匹配,这就是device找到driver的过程。
从上面可以看出,device在驱动中是通过mii标准的寄存器去获取id,然后去匹配的。我们自己的驱动代码不需要太多的关心。也就是关心一下设备的address。
Driver:
在内核启动的时候已经注册了一个叫genphy_driver的驱动。
假设你的phy驱动需要一些特别的操作,你可以注册自己的driver,phy下有很多,如davicom.c/realtek.c等
你看一下realtek.c的驱动,对应的id是0x001cc912,我的板子用的是realtek的,id是0x001cc915,是rtl8211eg。所以我的板子没有RTL821x的phy_driver。用的就是genphy_driver。
不过genphy_driver的id和mask是
.phy_id =0xffffffff,
.phy_id_mask = 0xffffffff,
我们看一下mdio_bus中的match是
static int mdio_bus_match(struct device *dev, struct device_driver *drv){ struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); return ((phydrv->phy_id & phydrv->phy_id_mask) == (phydev->phy_id & phydrv->phy_id_mask));}
这样不能与我的phy匹配。
不过我们不是靠这个机制。而是stmmac_main.c中stmmac_init_phy->phy_connect->phy_connect_direct->phy_attach_direct,看下面的代码;
/* Assume that if there is no driver, that it doesn't * exist, and we should use the genphy driver. */if (NULL == d->driver) {d->driver = &genphy_driver.driver;//如果为空就赋值为genphy_driver。err = d->driver->probe(d);if (err >= 0)err = device_bind_driver(d);if (err)return err;}
如果你要写自己的驱动,你需要看看structphy_driver结构体,里面有很多的特别函数指针需要我们去实体化。我就不看了
- 网卡驱动9-linux内核3.0.8的mdio_bus\phy_device\phy_driver
- Linux内核学习笔记之网卡驱动的详细分析
- linux内核对网卡驱动多队列的支持
- linux-2.6.35内核移植—网卡驱动的添加
- linux-2.6.35内核移植—网卡驱动的添加
- linux-2.6.35内核移植—网卡驱动的移植
- linux内核对网卡驱动多队列的支持
- 内核linux-3.0移植到fl2440-----添加DM9000网卡驱动
- 网卡驱动8-MII接口以及linux内核对MII的支持
- Linux内核---网卡驱动的详细分析 让你的网卡飞起来!
- 学习内核---Linux网卡驱动分析
- 学习内核---Linux网卡驱动分析
- linux内核编程之网卡驱动
- LINUX内核升级 - 更新网卡驱动
- Android下Linux内核网卡驱动移植
- Linux网卡驱动-内核网络分层分析
- linux内核-dm9000网卡驱动添加
- 【驱动】网卡驱动·linux内核网络分层结构
- UIWindow和UIScreen讲解
- Linux内核中_IO,_IOR,_IOW,_IOWR宏的用法与解析
- ASIHTTPRequest加载web请求
- iOS 真机调试
- ios如何判断当前网络的运营商
- 网卡驱动9-linux内核3.0.8的mdio_bus\phy_device\phy_driver
- 有道云笔记导入到印象笔记无法同步的问题解决
- 1588分析和实现总纲
- clojure读取文件->转换Map
- NGUI控件说明(中文) UIPlayAnimation
- mybatis 从入门到精通(三)
- 笔记本建立wifi热点
- SAP 寻找增强点的方法
- redis操作list小记