注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

蒙奇D小豌豆的博客

蒙奇D小豌豆的学习记录

 
 
 

日志

 
 

通过I2c总线实现net_device对phy的状态管理(link,spead状态的读取)  

2010-04-06 22:46:34|  分类: others |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 

环境: Powerpc  mpc8572

1  代码跟踪:

gfar_enet_open---------init_phy()---------phy_connect()(net_device与phy绑定)------

a.phy_attach()(根据phy的id在mdio_bus(phydey是挂载在其上面的可以根据dts看出来)上面查找到  phy__device,并给phy_device指定驱动并绑定,phydev->attached_dev =net_device)  

b phydev->adjust_link = handler(adjust_link)(当phy状态发生变化时回调该函数)

c .phy_start_machine()

 

void phy_start_machine(struct phy_device *phydev,

                   void (*handler)(struct net_device *))

{

         phydev->adjust_state = handler;

                                                                                                                                                   

         INIT_WORK(&phydev->state_queue,phy_state_machine);//将phy_state_machine加入工作队列

 

         init_timer(&phydev->phy_timer);        /初始化 并开启phy_timer.

         phydev->phy_timer.function = &phy_timer;

         phydev->phy_timer.data = (unsigned long) phydev;

         mod_timer(&phydev->phy_timer, jiffies + HZ);

}

static void phy_timer(unsigned long data)

{

         struct phy_device *phydev = (struct phy_device *)data;

 

         schedule_work(&phydev->state_queue);

 

}//phy_timer中调用工作队列从而调用phy_state_machine。

phy_state_machine用于读取phy的状态,我们就是要通过i2c驱动实现面里的接口。

 

2.i2c驱动

 

 

static void __iomem *sfp_select;

static void __iomem *sfp_led_ctl;

 

 

 

/*   

         i2c phy read interface

         return a 16-bit unsigned word received from device

*/

int m1111_phy_read(struct i2c_client *client, u8 addr)

{

         int val;

         unsigned short ret;

 

         val = i2c_smbus_read_word_data(client,addr);

         if( val < 0 )

                   return val;

 

         /* Conversion from big-endian to little-endian */

         ret = ( ( val >> 8) & 0x00ff ) | ( ( val << 8) & 0xff00);

         return ret;

}

 

/*

         i2c phy write interface

         16-bit word being written

*/

int m1111_phy_write(struct i2c_client *client, u8 addr, u16 value)

{

         u16 tmp;

        

         /* Conversion from big-endian to little-endian */

         tmp = ( ( value >> 8) & 0x00ff ) | ( ( value<< 8) & 0xff00);

         return i2c_smbus_write_word_data(client,addr,tmp);

}

 

int m1111_config_aneg_done(struct i2c_client *client)

{

         int retval;

 

         retval = m1111_phy_read(client, M1111_BMSR);

 

         return ( retval < 0 ) ? retval : ( retval & M1111_BMSR_ANEGCOMPLETE );

}

 

static int m1111_update_link(struct i2c_client *client, struct phy_device *phydev)

{

         int status;

         int link;

         unsigned short tmp;

        

         /* do a fake read */

         status = m1111_phy_read(client, M1111_BMSR);

         if(status < 0)

                   return status;

 

         /* read link and auto-negotiation status */

         status = m1111_phy_read(client, M1111_BMSR);

         if(status < 0)

                   return status;

 

         if(status & M1111_BMSR_LSTATUS) {

                   phydev->link = 1;

         }else{

                   phydev->link = 0;

         }

 

         /* phy has link we turn led green */

         if( phydev->link ){

                   tmp = in_8(sfp_led_ctl);

 

                   if(!strcmp(phydev->attached_dev->name,"eth0"))

                            tmp = (tmp & 0xfc) | 0x02;

                   else if( !strcmp(phydev->attached_dev->name,"eth1"))

                            tmp = ( tmp & 0xf3 ) | 0x08;

                   else if( !strcmp(phydev->attached_dev->name,"eth2"))

                            tmp = ( tmp & 0xcf ) | 0x20;

                   else

                            tmp = (tmp & 0x3f ) | 0x80;

 

                   out_8(sfp_led_ctl, tmp);

         }

         /* phy link down we turn led red */

         else{

                   tmp = in_8(sfp_led_ctl);

 

                   if(!strcmp(phydev->attached_dev->name,"eth0"))

                            tmp = (tmp & 0xfc) | 0x01;

                   else if( !strcmp(phydev->attached_dev->name,"eth1"))

                            tmp = ( tmp & 0xf3 ) | 0x04;

                   else if( !strcmp(phydev->attached_dev->name,"eth2"))

                            tmp = ( tmp & 0xcf ) | 0x10;

                   else

                            tmp = (tmp & 0x3f ) | 0x40;

 

                   out_8(sfp_led_ctl, tmp);

         }

        

         return status;

        

}

 

void m1111_led_red(struct phy_device *phydev)

{

         unsigned short tmp;

 

         tmp = in_8(sfp_led_ctl);

 

         if(!strcmp(phydev->attached_dev->name,"eth0"))

                   tmp = (tmp & 0xfc) | 0x01;

         else if( !strcmp(phydev->attached_dev->name,"eth1"))

                   tmp = ( tmp & 0xf3 ) | 0x04;

         else if( !strcmp(phydev->attached_dev->name,"eth2"))

                   tmp = ( tmp & 0xcf ) | 0x10;

         else

                   tmp = (tmp & 0x3f ) | 0x40;

 

         out_8(sfp_led_ctl, tmp);

 

         phydev->state = PHY_NOLINK;

}

 

static int sfp_phy_compare_id(struct device *dev, void *data)

{

         return strcmp((char *)data, dev->bus_id) ? 0 : 1;

}

 

 

struct i2c_client *get_sfp_i2c_client(void)//或其i2c_client结构

{

         struct bus_type *bus = &i2c_bus_type;

         struct device *d;

 

         struct i2c_client *sfp_phy_client;

        

         d = bus_find_device(bus, NULL, (void *)SFP_PHY0_ID , sfp_phy_compare_id);

         // SFP_PHY0_ID ”1 -0056”后面会介绍

         if(d){

                   sfp_phy_client = dev_get_drvdata(d);

         }else {

                   return ERR_PTR(-ENODEV);

         }

 

         return sfp_phy_client;

        

}

 

int m1111_read_status(struct i2c_client *client, struct phy_device *phydev)//读取状态接口函数

{

         int adv;

         int err;

         int lpa;

         int status = 0;

         unsigned short tmp;

 

 

         if( NULL == client){

                   printk("Error, no client attached! ");

                   return -ENODEV;

         }

         /*

         *      first need to know which port we are operating on, then we write epld reg

         *       to configure the corresponding port

         */

         tmp = in_8(sfp_select);

 

         if(!strcmp(phydev->attached_dev->name,"eth0"))

                   tmp &= 0xfc;

         else if( !strcmp(phydev->attached_dev->name,"eth1"))

                   tmp = ( tmp & 0xfc ) | 0x01;

         else if( !strcmp(phydev->attached_dev->name,"eth2"))

                   tmp = ( tmp & 0xfc ) | 0x02;

         else

                   tmp = (tmp & 0xfc ) | 0x03;

 

         out_8(sfp_select, tmp);

 

         err = m1111_update_link(client, phydev);

         if(err < 0)

                   return err;

        

         status = m1111_phy_read(client, M1111_PHY_STATUS);

        

         if(status < 0)

                   return status;

        

         lpa = m1111_phy_read(client, M1111_LPA);

         if(lpa < 0)

                   return lpa;

 

         adv = m1111_phy_read(client, M1111_ADVERTISE);

         if(adv < 0)

                   return adv;

 

         lpa &= adv;

 

         if(status & M1111_PHY_STATUS_FULLDUPLEX){

                   phydev->duplex= DUPLEX_FULL;

         }else{

                   phydev->duplex = DUPLEX_HALF;

         }

 

         status = status & M1111_PHY_STATUS_SPD_MASK;

         switch (status) {

                   case M1111_PHY_STATUS_1000:

                            phydev->speed= SPEED_1000;

                            break;

 

                   case M1111_PHY_STATUS_100:

                            phydev->speed = SPEED_100;

                            break;

 

                   default:

                            phydev->speed = SPEED_10;

                            break;

         }       

        

         return 0;

}

 

 

 

static int mpc8572_sfp_remove(struct i2c_client *client)

{

         /* release the resources we have requested */

         if(sfp_led_ctl)

                   iounmap(sfp_led_ctl);

        

         if(sfp_select)

                   iounmap(sfp_select);

        

         return 0;

}

 

static int mpc8572_sfp_probe(struct i2c_client *client,

                            const struct i2c_device_id *id)

{

         int rc = 0;

         unsigned short tmp;

         int status;

        

         printk(KERN_INFO "-- %s ",__FUNCTION__);

        

         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C

                                          | I2C_FUNC_SMBUS_BYTE_DATA)) {

                   rc = -ENODEV;

                  

                   goto exit;

         }

         dev_info(&client->dev,"chip found ");

 

         /* set client->dev driver_data */

         i2c_set_clientdata(client, client);

 

         sfp_select = ioremap(SFP_SELECT_REG,1);

         if( NULL == sfp_select){

                   DPRINTK("ioremap failed ");

                   rc= -ENOMEM;

                   goto exit;

         }

 

         sfp_led_ctl = ioremap(SFP_LED_CTL,1);

         if( NULL == sfp_led_ctl) {

                   rc = -ENOMEM;

                   goto exit;

         }

 

 

 

        

static struct i2c_driver mpc8572_sfp_driver = {

         .driver = {

                   .name = "sfp-phy",

         },

         .probe = mpc8572_sfp_probe,

         .remove = mpc8572_sfp_remove,

};

 

int __init mpc8572_sfp_init(void)

{

         return i2c_add_driver(&mpc8572_sfp_driver);

}

 

void __exit mpc8572_sfp_exit(void)

{

         i2c_del_driver(&mpc8572_sfp_driver);

}

 

MODULE_AUTHOR("wenxu@DPI Shanghai University");

MODULE_DESCRIPTION("FTM-C012R-L SFP Phy client drivers");

MODULE_LICENSE("GPL");

 

module_init(mpc8572_sfp_init);

module_exit(mpc8572_sfp_exit);

 

 

3修改mpc8572ds.dts,挂载设备于i2c上,生成dtb文件

在 fsl_soc.c中

static struct i2c_driver_device i2c_devices[] __initdata = {

         {"ricoh,rs5c372a", "rtc-rs5c372", "rs5c372a",},

         {"ricoh,rs5c372b", "rtc-rs5c372", "rs5c372b",},

         {"ricoh,rv5c386",  "rtc-rs5c372", "rv5c386",},

         {"ricoh,rv5c387a", "rtc-rs5c372", "rv5c387a",},

         {"marvell,88e1111","sfp-phy","88e1111",},//此为我们添加的

};

The first entry in the table matches "compatible" property in device tree,

the second one - i2c driver name, and the third one - i2c device name in

the driver id table(

 

在dts的i2c中加上(详见什么是dts及其写法)

 

 

i2c@3100 {

            device_type = "i2c";

            compatible = "fsl-i2c";

            reg = <0x3100 0x100>;

            interrupts = <0x2b 0x2>;

            interrupt-parent = <0x1>;

            dfsrr;

 

            rtc@68 {

                device_type = "rtc";

                compatible = "stm,m41t81";

                reg = <0x68>;

            };

 

            phy@56{

                //device_type = "phy0";//可以不要的

                compatible = "marvell,88e1111";//为上面结构的第一个 所以上面程序里 phy_id  为1 -0056  1指 1号i2c 的56ge 偏移地址

                reg = <0x56>;//phy芯片上i2c的偏移地址

            };

        };4 开机phy状态的变化

enum phy_state {

         PHY_DOWN=0,

         PHY_STARTING,

         PHY_READY,

         PHY_PENDING,

         PHY_UP,

         PHY_AN,

         PHY_RUNNING,

         PHY_NOLINK,

         PHY_FORCING,

         PHY_CHANGELINK,

         PHY_HALTED,

         PHY_RESUMING

};

//当网线连接好时

First_time    PHY_UP,

Second_time    PHY_AN,

Third_time    PHY_RUNNING,

后面 根据网线插拔的状态变化

 

 

  评论这张
 
阅读(2546)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018