Linux下smi/mdio总线驱动
Linux下smi/mdio总线驱动 韩大卫@吉林师范大学 MII(媒体独立接口), 是IEEE802.3定义的以太网行业标准接口, smi是mii中的标准管理接口, 有两跟管脚, mdio 和mdc ,用来现实双向的数据输入/输出和时钟同步。mdio主要作用用来配置/读取phy的寄存器, 实现监控作用。 Smi总线也就是mdio总线。 以mips 架构的caium octeon 处理器为例介绍mdio总线的驱动。 内核代码 drivers/net/phy/mdio-octeon.c static int __init octeon_mdiobus_mod_init(void)
{
// 同uart,usb,spi,i2c等总线一样, mdio作为platform驱动注册到内核
return platform_driver_register(&octeon_mdiobus_driver);
} static struct platform_driver octeon_mdiobus_driver = {
.driver = {
.name = "mdio-octeon",
.owner = THIS_MODULE,
.of_match_table = octeon_mdiobus_match,
},
.probe = octeon_mdiobus_probe,
.remove = __exit_p(octeon_mdiobus_remove),
}; 内核根据of_match_table 找到了octeon-3860-mdio 的驱动文件, static struct of_device_id octeon_mdiobus_match[] = {
{
.compatible = "cavium,octeon-3860-mdio", },
{},
};
MODULE_DEVICE_TABLE(of, octeon_mdiobus_match); 该驱动说明支持符合”canium,octeon-3860-mdio”规范接口的操作。 进入probe() static int __init octeon_mdiobus_probe(struct platform_device *pdev)
{
/*probe() 总体思想即填充一个struct octeon_mdiobus的数据结构,
最后将此数据结构作为pdev的私有成员。octeon_mdiobus 定义为:
struct octeon_mdiobus {
//struct mii_bus 是linux定义mii总线的通用数据结构。
struct mii_bus *mii_bus;
u64 register_base;
resource_size_t mdio_phys;
resource_size_t regsize;
enum octeon_mdiobus_mode mode;
int phy_irq[PHY_MAX_ADDR];
};
octeon_mdiobus_mode 定义:
enum octeon_mdiobus_mode {
UNINIT = 0,
C22, // IEEE802.3-2005
的条款22.2.4, 22.3.4
C45 //条款45.不用的条款使用不同的数据帧结构。
};
*/
struct octeon_mdiobus *bus; struct resource *res_mem;
union cvmx_smix_en smi_en;
int err = -ENOENT; //为platfrom设备pdev 的私有数据分配内存,长度struct octeon_mdiobus的长度
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM; /*
获取io内存地址资源的描述。此资源的描述记录在uboot的设备树源文件dts里。
关于该描述信息,参考最后附录。
*/
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res_mem == NULL) {
dev_err(&pdev->dev, "found no memory resource\n");
err = -ENXIO;
goto fail_region;
} //获取了io内存地址的首地址及长度的描述后, 按此描述向系统申请相应资源。
bus->mdio_phys = res_mem->start;
bus->regsize = resource_size(res_mem);
if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize, res_mem->name)) {
dev_err(&pdev->dev, "request_mem_region failed\n");
goto fail_region;
} /*
申请成功后, 使用ioremap将这个片io内存地址映射出来,以便交与应用层使用,
register_basae 即为这个映射后的地址基地址,通过操作这个地址, 就可以实现在用户层操作smi的寄存器了。
/*
bus->register_base = (u64)ioremap(bus->mdio_phys, bus->regsize); //为mii_bus数据结构分配内存
bus->mii_bus = mdiobus_alloc(); if (!bus->mii_bus)
goto fail_mdio_alloc; smi_en.u64 = 0;
smi_en.s.en = 1; /*
cvmx_write_csr 是cavium octeon 处理器提供write寄存器的API
#define SMI_EN 0x20 这是寄存器的其基地址上的偏移值
第一个参数是目的寄存器地址, 第二个参数是要write的数值
*/
cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64); /* mii_bus 数据结构定义如下, read/write 的函数指针
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); struct mutex mdio_lock;
struct device *parent;
enum {
MDIOBUS_ALLOCATED = 1,
MDIOBUS_REGISTERED,
MDIOBUS_UNREGISTERED,
MDIOBUS_RELEASED,
} state;
struct device dev; struct phy_device *phy_map[PHY_MAX_ADDR];
u32 phy_mask;
int *irq;
}; */ // 将bus 保存为mii_bus的私有数据
bus->mii_bus->priv = bus; //定义mii_bus的中断号
bus->mii_bus->irq = bus->phy_irq; //mii_bus的总线名称
bus->mii_bus->name = "mdio-octeon";
snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
bus->mii_bus->parent = &pdev->dev; //填充mii_bus read/write的实现函数。
bus->mii_bus->read = octeon_mdiobus_read; bus->mii_bus->write = octeon_mdiobus_write; //将bus作为platfrom 的私有数据
dev_set_drvdata(&pdev->dev, bus); //向内核注册属于octeon 的mii总线
err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node); if (err)
goto fail_register; dev_info(&pdev->dev, "Version " DRV_VERSION "\n"); /*
将mii_bus 保存在一个全局指针数组里, 定义在arch/mips/cavium-octeon/setup.c
struct mii_bus *octeon_mdiobuses[4];
EXPORT_SYMBOL(octeon_mdiobuses);
*/
octeon_mdiobuses[octeon_mdiobus_bus2unit(bus)] = bus->mii_bus; return 0;
} mdio 工作大郅流程: 发送一个2bit的开始标识码和一个2bit的operate标志,该operate 标志在C22和C45里有不同定义。发送一个5bit 的phy 设备地址和5bitPHY寄存器地址。 再空闲MDIO需要2个时钟的访问时间。 MDIO串行读出/写入16bit的寄存器数据。 结束后MDIOMDIO进入高阻状态。 C22下的数据帧格式: st op phyaddr regaddr ta data 01 phy_op 5bit地址 5bit地址 2bit访问时间 16bit读写数据 C22 下的op :10 : write, 01 : read C45数据帧格式: st op phyaddr type ta addr/data 00 phy_op 5bit地址 5bit类型 2bit访问时间 16bit寄存器地址/数据 两者主要差异在op处, C45的op段: 00=address
01=write
,11=read
,10=post-read-increment-address 当op 为00 时, 这个数据帧传入的指定的16bit寄存器地址, 最大地址63336.
当op 为 01/11 时, 这个数据帧才是具体write/read 操作。 因此, 在c45 条款下, 完成一次真正的I/O 操作要使用两个数据帧。 另外,当 op 为 10 时, 含义是当本次读操作结束后, 将寄存器地址加1, 适于与遍历所有的寄存器。 Octeon 对该数帧的定义是: union cvmx_smix_cmd {
uint64_t u64;
struct cvmx_smix_cmd_s {
uint64_t reserved_18_63 : 46;
//保留
uint64_t phy_op : 2; //phy_op
uint64_t reserved_13_15 : 3;
uint64_t phy_adr : 5; //phy芯片地址
uint64_t reserved_5_7 : 3;
uint64_t reg_adr : 5; //寄存器地址
}
…
} struct cvmx_smix_cmd_s 为uint64_t 大小, 即8个字节。 static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
{
struct octeon_mdiobus *p = bus->priv;
union cvmx_smix_cmd smi_cmd;
union cvmx_smix_rd_dat smi_rd; //如果C22条款, read操作时op为 01.
unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
int timeout = 1000; //寄存器是否满足是否有C45 标志
if (regnum & MII_ADDR_C45) { /*
如果是C45条款, 要先发送一个数据帧将寄存器地址写入, 第二个数据帧才是read/write操作
octeon_mdiobus_c45_addr() 函数完成第一个数据帧的作用。
*/
int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
if (r < 0)
return r; //将regnum处理后封装到smi_cmd里。
regnum = (regnum >> 16) & 0x1f; //C45条款下read操作时op为11
op = 3; /* MDIO_CLAUSE_45_READ */
} else {
//C22条款下, 只需要将mdio配置为C22模式即可。
octeon_mdiobus_set_mode(p, C22);
} smi_cmd.u64 = 0;
smi_cmd.s.phy_op = op; /* MDIO_CLAUSE_22_READ */
smi_cmd.s.phy_adr = phy_id;
smi_cmd.s.reg_adr = regnum; /*
由于smi_cmd 是联合体, 将smi_cmd.u64的数值传给函数, 寄存器即可解析出op, phy_id, regnum等参数。
*/
cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64); do { //read之前等待1000个时钟周期
__delay(1000); // 读取到寄存器的数值,保存到u64中。
smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT);
} while (smi_rd.s.pending && --timeout); //如果数据有效, 发送寄存器数值中去掉头部信息的部分, 即u64中的有效载荷。
if (smi_rd.s.val)
return smi_rd.s.dat; else
return -EIO;
} smi 的write 操作原理同上。 附录: dts 里smi总线io地址资源描述 Dts里的描述是根据cavium octeon datasheet来写的, cavium octeon 关于smi 寄存器地址的定义是:
smi0从 0x0001180000001800到0x0001180000001828 smi 从0x0001180000001900
到0x0001180000001920 所以在描述reg地址范围时, 要适当大于这个范围, 但不能跟其他寄存器地址冲突。
smi0: mdio@1180000001800 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00001800 0x0 0x40>;
..
} 关于compatible 的描述, "cavium,octeon-3860-mdio"; cavium 表示了该smi0总线可以兼容“cavium, octeon-3860-mdio”设备, 内核启动后, 会根据这个描述寻找对应的驱动。 smi1: mdio@1180000001900 {
compatible = "cavium,octeon-3860-mdio";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x11800 0x00001900 0x0 0x40>;
};
Linux下smi/mdio总线驱动的更多相关文章
- Linux 下smi/mdio总线通信
Linux 下smi/mdio总线通信 韩大卫@吉林师范大学 下面代码描述了在用户层访问smi/mdio总线, 读写phy芯片寄存器的通用代码.Linux内核2.6以上通用. 将下面代码编译后,将可执 ...
- Linux下的USB总线驱动(一)
版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 一.USB理论 1. USB概念概述 USB1.0版本速度1.5Mbps(低速USB) USB1 ...
- 如何在Linux下写无线网卡的驱动【转】
转自:http://www.crifan.com/files/doc/docbook/linux_wireless/release/html/linux_wireless.html 版本:v0.3 H ...
- Linux内核中SPI总线驱动分析
本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...
- 在linux下安装某个硬件驱动到方法
东西很简单,几句话就能说清除. 使用lsipc检查你需要安装到硬件,记住硬件到关键型号,去搜索引擎搜索linux下的驱动文件 对文件进行安装简单的解压后基本上是 ./configure &&a ...
- linux下安装编译网卡驱动的方法
安装linux操作系统后发现没有网卡驱动,表现为 system → Administration → Network下Hardware列表为空. 以下为安装编译网卡驱动的过程,本人是菜鸟,以下是我从网 ...
- linux驱动基础系列--Linux下Spi接口Wifi驱动分析
前言 本文纯粹的纸上谈兵,我并未在实际开发过程中遇到需要编写或调试这类驱动的时候,本文仅仅是根据源码分析后的记录!基于内核版本:2.6.35.6 .主要是想对spi接口的wifi驱动框架有一个整体的把 ...
- linux下Pl353 NAND Flash驱动分析
linux的NAND Flash驱动位于drivers/mtd/nand子文件夹下: nand_base.c-->定义通用的nand flash基本操作函数,如读写page,可自己重写这些函数 ...
- linux下的块设备驱动(一)
块设备的驱动比字符设备的难,这是因为块设备的驱动和内核的联系进一步增大,但是同时块设备的访问的几个基本结构和字符还是有相似之处的. 有一句话必须记住:对于存储设备(硬盘~~带有机械的操作)而言,调整读 ...
随机推荐
- ASP.NET页面跳转
总结一下跳转方式: <a>标签 <a href=”home.aspx”></a> HyperLink控件 Asp.net 服务器端控件 属性NavigateUrl指 ...
- 一些CSS命名规则
一些CSS命名规则 头:header 内容:content/containe 尾:footer 导航:nav 侧栏:sidebar 栏目:column 页面外围控制整体布局宽度:wrapper 左右中 ...
- Python 正则表达式应用【转载】
将从正则表达式开始讲Python的标准库.正则表达式是文字处理中常用的工具,而且不需要额外的系统知识或经验.我们会把系统相关的包放在后面讲解. 正则表达式(regular expression)主要功 ...
- CeontOS7安装ansible
安装方法一. 第一步:安装epel rpm -ivh http://mirror.pnl.gov/epel/7/x86_64/e/epel-release-7-5.noarch.rpm 第二步:安装a ...
- HTML5学习笔记之客户端存储数据方法:localStorage(),sessionStorage()
HTML5提供了两种在客户端存储数据的新方法: localStorage():没有时间限制的数据存储 sessionStorage():针对一个session的数据存储 下面的一个例子用localSt ...
- Oracle_系统和对象权限管理
授予系统权限: GRANT { system_privilege | role } [,{ system_privilege | role }]... ... TO {user | role | PU ...
- Python文件或目录操作的常用函数
◆ os.listdir(path) Return a list containing the names of the entries in the directory given by path. ...
- XX秘籍
第一课 一个男人在他妻子洗完澡后准备进浴室洗澡.这时,门铃响了. 妻子迅速用浴巾裹住自己冲到门口. 当她打开门时,邻居鲍勃站在那儿. 在她开口前,鲍勃说,“你如果把浴巾拿掉,我给你800美元.” 想了 ...
- Page.ClientScript.RegisterStartupScript 与 Page.ClientScript.RegisterClientScriptBlock 之间的区别
Page.ClientScript.RegisterClientScriptBlock 在页面紧跟<form>之后,整个页面未完全加载完成. Page.ClientScript.Regis ...
- java 笔试
单例设计模式: public class Singliton { //no new private Singliton (){ } static Singliton ins = null; publi ...