Linux下smi/mdio总线驱动

摘要:
Linux下的smi/mdio总线驱动Han David@吉林师范大学MII,这是IEEE802.3定义的以太网行业标准接口。smi是mii中的标准管理接口,具有两个引脚,mdio和mdc,用于实现双向数据输入/输出和时钟同步。以mips架构的caiumoton处理器为例,介绍mdio总线的驱动程序。输入probe()statipoint__initocten_mdiobus_probe{/*probe(()的基本思想是填充structocten_mdiobus的数据结构,最后将该数据结构作为pdev的私有成员。octen_mdiobus定义为:structocte_mdiobus{//structmii_bus是linux定义的mii总线的通用数据结构。
Linuxsmimdio总线驱动

韩大卫@吉林师范大学


MII(媒体独立接口), 是IEEE802.3定义的以太网行业标准接口, smimii中的标准管理接口, 有两跟管脚, 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
");
        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
");                                                               
        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 "
");
     
    /*
    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的开始标识码和一个2bitoperate标志,该operate 标志在C22C45里有不同定义。发送一个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 : write01 read

       
C45数据帧格式:

st	op		phyaddr	type		ta			addrdata

00    phy_op	5bit地址	5bit类型	2bit访问时间		16bit寄存器地址/数据

      
两者主要差异在op处, C45op段:

00=address
 01=write
11=read
10=post-read-increment-address   

op 00 时, 这个数据帧传入的指定的16bit寄存器地址, 最大地址63336.
op 01/11 时, 这个数据帧才是具体write/read 操作。 

因此, 在c45 条款下,  完成一次真正的IO 操作要使用两个数据帧。

另外,当 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操作时op01.
    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操作时op11
        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 寄存器地址的定义是:
smi00x00011800000018000x0001180000001828 
 
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总线驱动》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇《Pointer-Generator-Network》centos7.5-docke安装(2020.9.1)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

vCenter线上操作磁盘扩容

以下截图是生产机器,目前是有一块盘,且根分区是/dev/sda3,因为磁盘不足,需要备份的数据要远远超过此时的空间大小;正常情况下,是可以新增硬盘硬盘作为备份 但是作为宿主机下的虚机,因为一些不规范的操作,我们直接在线扩容,导致新增的磁盘空间没有生效 新增之后,我们通过fdisk -l查看,硬盘空间并未变化,此时我们reboot重启生效 我们可以...

Linux逻辑卷管理LVM学习总结

LVM(Logical Volume Manager),它是Linux环境下对磁盘分区进行管理的一种机制,LVM是建立在硬盘和分区之上的一个逻辑层,可以弹性的增加或减小分区的大小,使得磁盘分区管理更具灵活性。LVM的实现示意图: 创建LVM实例: 1、首先创建3个准备组成卷组VG的物理分区,并且把分区格式改为8e。以/dev/sdb1、/dev/sdb2...

网口扫盲二:Mac与Phy组成原理的简单分析

网口扫盲二:Mac与Phy组成原理的简单分析 1. general 下图是网口结构简图.网口由CPU、MAC和PHY三部分组成.DMA控制器通常属于CPU的一部分,用虚线放在这里是为了表示DMA控制器可能会参与到网口数据传输中. 对于上述的三部分,并不一定都是独立的芯片,根据组合形式,可分为下列几种类型: CPU集成MAC与PHY; CPU集成MAC...

物理机(window)安装linux系统

1,需要的资源    1⃣️,centos7的镜像文件 centos7.iso    2⃣️,pe制作软件  https://cn.ultraiso.net/xiazai.html  软碟通 自己去下载就好了 2,开始制作u盘pe  打开安装后的软碟通,然后用这个软件打开centos7.iso  3,写入映像 启动 ---》 写入硬盘映像   最后写入...

git(代码仓库)

第1章 git介绍 1.1 参数: 第2章 git管理一个项目 2.1 图示 2.2 cd /项目路径 2.3 git config --globle user.email  "邮箱地址" 2.4 git confgi --globle user.name  'xiaodong' 2.5 git init 2.6 git log 2.7 git statu...

【转】Android操作系统安全研究系列——键盘记录

作 者: hacknet 以投稿在黑防6月杂志 ****************************************     盗号木马大家都很常见了,但大多数都是在windows平台下的,像阿拉QQ大盗等键盘记录程序都给用户带来不少的麻烦。在android手机越来越普及的今天,盗号木马也将会转移到手机上来,为了更好的防范这种盗号木马,我们首先要...