/********************************************************************
* OK335xS LAN8710 phy driver hacking
* 说明:
* 本文主要是对OK335xS中的phy的驱动进行代码跟踪,并解决当前遇到
* LAN8710上电后插入网线,会导致LAN8710无法自动握手,Link灯不亮,内核
* 也检测不到LAN8710有状态发生了改变,最终问题定位于LAN8710的驱动初
* 始化部分,本文解决办法选择注释掉对应的内容就行了。
*
* 2016-3-3 深圳 南山平山村 曾剑锋
*******************************************************************/ 一、make menuconfig 配置:
.config - Linux/arm 3.2. Kernel Configuration
──────────────────────────────────────────────────────────────────────────────
┌───────────────── PHY Device support and infrastructure ─────────────────┐
│ Arrow keys navigate the menu. <Enter> selects submenus --->. │
│ Highlighted letters are hotkeys. Pressing <Y> includes, <N> excludes, │
│ <M> modularizes features. Press <Esc><Esc> to exit, <?> for Help, </> │
│ for Search. Legend: [*] built-in [ ] excluded <M> module < > │
│ ┌────^(-)─────────────────────────────────────────────────────────────┐ │
│ │ < > Drivers for Davicom PHYs │ │
│ │ < > Drivers for Quality Semiconductor PHYs │ │
│ │ < > Drivers for the Intel LXT PHYs │ │
│ │ < > Drivers for the Cicada PHYs │ │
│ │ < > Drivers for the Vitesse PHYs │ │
│ │ <*> Drivers for SMSC PHYs │ │
│ │ < > Drivers for Broadcom PHYs │ │
│ │ < > Drivers for ICPlus PHYs │ │
│ │ < > Drivers for Realtek PHYs │ │
│ │ < > Drivers for National Semiconductor PHYs │ │
│ └────v(+)─────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ <Select> < Exit > < Help > │
└─────────────────────────────────────────────────────────────────────────┘ 二、linux-3.2./drivers/net/phy/smsc.c 跟踪:
static struct phy_driver lan8710_driver = { <---------+
/* OUI=0x00800f, Model#=0x0f */ |
.phy_id = 0x0007c0f0, |
// mask导致phy_id=0x0007c0f1也行的 |
.phy_id_mask = 0xfffffff0, |
.name = "SMSC LAN8710/LAN8720", |
|
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
| SUPPORTED_Asym_Pause), |
.flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, |
|
/* basic functions */ |
.config_aneg = genphy_config_aneg, --------*------+
.read_status = genphy_read_status, --------*------*--+
.config_init = smsc_phy_config_init, --------*------*--*-+
| | | |
/* IRQ related */ | | | |
.ack_interrupt = smsc_phy_ack_interrupt, | | | |
.config_intr = smsc_phy_config_intr, | | | |
| | | |
.suspend = genphy_suspend, | | | |
.resume = genphy_resume, | | | |
| | | |
.driver = { .owner = THIS_MODULE, } | | | |
}; | | | |
| | | |
static int __init smsc_init(void) <---------+ | | | |
{ | | | | |
int ret; | | | | |
| | | | |
ret = phy_driver_register (&lan83c185_driver); | | | | |
if (ret) | | | | |
goto err1; | | | | |
| | | | |
ret = phy_driver_register (&lan8187_driver); | | | | |
if (ret) | | | | |
goto err2; | | | | |
| | | | |
ret = phy_driver_register (&lan8700_driver); | | | | |
if (ret) | | | | |
goto err3; | | | | |
| | | | |
ret = phy_driver_register (&lan911x_int_driver);| | | | |
if (ret) | | | | |
goto err4; | | | | |
| | | | |
ret = phy_driver_register (&lan8710_driver); | -----+ | | |
if (ret) | | | |
goto err5; | | | |
| | | |
return ; | | | |
err5: | | | |
phy_driver_unregister (&lan911x_int_driver); | | | |
err4: | | | |
phy_driver_unregister (&lan8700_driver); | | | |
err3: | | | |
phy_driver_unregister (&lan8187_driver); | | | |
err2: | | | |
phy_driver_unregister (&lan83c185_driver); | | | |
err1: | | | |
return ret; | | | |
} | | | |
| | | |
static void __exit smsc_exit(void) | | | |
{ | | | |
phy_driver_unregister (&lan8710_driver); | | | |
phy_driver_unregister (&lan911x_int_driver); | | | |
phy_driver_unregister (&lan8700_driver); | | | |
phy_driver_unregister (&lan8187_driver); | | | |
phy_driver_unregister (&lan83c185_driver); | | | |
} | | | |
| | | |
MODULE_DESCRIPTION("SMSC PHY driver"); | | | |
MODULE_AUTHOR("Herbert Valerio Riedel"); | | | |
MODULE_LICENSE("GPL"); | | | |
| | | |
module_init(smsc_init); -----------+ | | |
module_exit(smsc_exit); | | |
| | |
static struct mdio_device_id __maybe_unused smsc_tbl[] = { | | |
{ 0x0007c0a0, 0xfffffff0 }, | | |
{ 0x0007c0b0, 0xfffffff0 }, | | |
{ 0x0007c0c0, 0xfffffff0 }, | | |
{ 0x0007c0d0, 0xfffffff0 }, | | |
{ 0x0007c0f0, 0xfffffff0 }, | | |
{ } | | |
}; | | |
| | |
MODULE_DEVICE_TABLE(mdio, smsc_tbl); | | |
| | |
| | |
| | |
static int __init phy_init(void) | | |
{ | | |
int rc; | | |
| | |
rc = mdio_bus_init(); ------------------+ | | |
if (rc) | | | |
return rc; | | | |
| | | |
rc = phy_driver_register(&genphy_driver); ---*-----+ | | |
if (rc) | | | | |
mdio_bus_exit(); | | | | |
| | | | |
return rc; | | | | |
} | | | | |
| | | | |
static void __exit phy_exit(void) | | | | |
{ | | | | |
phy_driver_unregister(&genphy_driver); | | | | |
mdio_bus_exit(); | | | | |
} | | | | |
| | | | |
subsys_initcall(phy_init); | | | | |
module_exit(phy_exit); | | | | |
| | | | |
struct bus_type mdio_bus_type = { <-------+ | | | | |
.name = "mdio_bus", | | | | | |
.match = mdio_bus_match, ------*-*-+ | | | |
.pm = MDIO_BUS_PM_OPS, | | | | | | |
}; | | | | | | |
EXPORT_SYMBOL(mdio_bus_type); | | | | | | |
| | | | | | |
int __init mdio_bus_init(void) <-----------*-+ | | | | |
{ | | | | | |
int ret; | | | | | |
| | | | | |
ret = class_register(&mdio_bus_class); | | | | | |
if (!ret) { | | | | | |
ret = bus_register(&mdio_bus_type); -----+ | | | | |
if (ret) | | | | |
class_unregister(&mdio_bus_class); | | | | |
} | | | | |
| | | | |
return ret; | | | | |
} | | | | |
| | | | |
void mdio_bus_exit(void) | | | | |
{ | | | | |
class_unregister(&mdio_bus_class); | | | | |
bus_unregister(&mdio_bus_type); | | | | |
} | | | | |
| | | | |
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)); | | | |
} | | | |
| | | |
static struct phy_driver genphy_driver = { <---------+ | | |
.phy_id = 0xffffffff, | | | |
.phy_id_mask = 0xffffffff, | | | |
.name = "Generic PHY", | | | |
.config_init = genphy_config_init, ------*-------+ | | |
.features = , | | | | |
.config_aneg = genphy_config_aneg, ------*-------*-+ | |
.read_status = genphy_read_status, ------*-------*-*--+ |
.suspend = genphy_suspend, | | | | |
.resume = genphy_resume, | | | | |
.driver = {.owner= THIS_MODULE, }, | | | | |
}; | | | | |
| | | | |
int phy_driver_register(struct phy_driver *new_driver) <--+ | | | |
{ | | | |
int retval; | | | |
| | | |
new_driver->driver.name = new_driver->name; | | | |
new_driver->driver.bus = &mdio_bus_type; | | | |
new_driver->driver.probe = phy_probe; -----------+ | | | |
new_driver->driver.remove = phy_remove; | | | | |
| | | | |
retval = driver_register(&new_driver->driver); | | | | |
| | | | |
if (retval) { | | | | |
printk(KERN_ERR "%s: Error %d in registering driver\n", | | | | |
new_driver->name, retval); | | | | |
| | | | |
return retval; | | | | |
} | | | | |
| | | | |
pr_debug("%s: Registered new driver\n", new_driver->name); | | | | |
| | | | |
return ; | | | | |
} | | | | |
EXPORT_SYMBOL(phy_driver_register); | | | | |
| | | | |
static int phy_probe(struct device *dev) <-----------+ | | | |
{ | | | |
struct phy_device *phydev; | | | |
struct phy_driver *phydrv; | | | |
struct device_driver *drv; | | | |
int err = ; | | | |
| | | |
phydev = to_phy_device(dev); | | | |
| | | |
/* Make sure the driver is held. | | | |
* XXX -- Is this correct? */ | | | |
drv = get_driver(phydev->dev.driver); | | | |
phydrv = to_phy_driver(drv); | | | |
phydev->drv = phydrv; | | | |
| | | |
/* Disable the interrupt if the PHY doesn't support it */ | | | |
if (!(phydrv->flags & PHY_HAS_INTERRUPT)) | | | |
phydev->irq = PHY_POLL; | | | |
| | | |
mutex_lock(&phydev->lock); | | | |
| | | |
/* Start out supporting everything. Eventually, | | | |
* a controller will attach, and may modify one | | | |
* or both of these values */ | | | |
phydev->supported = phydrv->features; | | | |
phydev->advertising = phydrv->features; | | | |
| | | |
| | | |
/* Set the state to READY by default */ | | | |
phydev->state = PHY_READY; | | | |
| | | |
if (phydev->drv->probe) | | | |
err = phydev->drv->probe(phydev); | | | |
| | | |
mutex_unlock(&phydev->lock); | | | |
| | | |
return err; | | | |
| | | |
} | | | |
| | | |
static int genphy_config_init(struct phy_device *phydev) <-------+ | | |
{ | | |
int val; | | |
u32 features; | | |
| | |
/* For now, I'll claim that the generic driver supports | | |
* all possible port types */ | | |
features = (SUPPORTED_TP | SUPPORTED_MII | | |
| SUPPORTED_AUI | SUPPORTED_FIBRE | | | |
SUPPORTED_BNC); | | |
| | |
/* Do we support autonegotiation? */ | | |
val = phy_read(phydev, MII_BMSR); | | |
| | |
if (val < ) | | |
return val; | | |
| | |
if (val & BMSR_ANEGCAPABLE) | | |
features |= SUPPORTED_Autoneg; | | |
| | |
if (val & BMSR_100FULL) | | |
features |= SUPPORTED_100baseT_Full; | | |
if (val & BMSR_100HALF) | | |
features |= SUPPORTED_100baseT_Half; | | |
if (val & BMSR_10FULL) | | |
features |= SUPPORTED_10baseT_Full; | | |
if (val & BMSR_10HALF) | | |
features |= SUPPORTED_10baseT_Half; | | |
| | |
if (val & BMSR_ESTATEN) { | | |
val = phy_read(phydev, MII_ESTATUS); | | |
| | |
if (val < ) | | |
return val; | | |
| | |
if (val & ESTATUS_1000_TFULL) | | |
features |= SUPPORTED_1000baseT_Full; | | |
if (val & ESTATUS_1000_THALF) | | |
features |= SUPPORTED_1000baseT_Half; | | |
} | | |
| | |
phydev->supported = features; | | |
phydev->advertising = features; | | |
| | |
return ; | | |
} | | |
| | |
int genphy_config_aneg(struct phy_device *phydev) <----------+ | |
{ | |
int result; | |
| |
printk("zengjf check postion at %s.\n", __func__); | |
| |
if (AUTONEG_ENABLE != phydev->autoneg) | |
return genphy_setup_forced(phydev); | |
| |
result = genphy_config_advert(phydev); | |
| |
if (result < ) /* error */ | |
return result; | |
| |
if (result == ) { | |
/* Advertisement hasn't changed, but maybe aneg was never on to| |
* begin with? Or maybe phy was isolated? */ | |
int ctl = phy_read(phydev, MII_BMCR); | |
| |
if (ctl < ) | |
return ctl; | |
| |
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) | |
result = ; /* do restart aneg */ | |
} | |
| |
/* Only restart aneg if we are advertising something different | |
* than we were before. */ | |
if (result > ) | |
result = genphy_restart_aneg(phydev); | |
| |
return result; | |
} | |
EXPORT_SYMBOL(genphy_config_aneg); | |
| |
int genphy_read_status(struct phy_device *phydev) <----------+ |
{ |
int adv; |
int err; |
int lpa; |
int lpagb = ; |
|
/* Update the link, but return if there |
* was an error */ |
err = genphy_update_link(phydev); |
if (err) |
return err; |
|
if (AUTONEG_ENABLE == phydev->autoneg) { |
if (phydev->supported & (SUPPORTED_1000baseT_Half |
| SUPPORTED_1000baseT_Full)) { |
lpagb = phy_read(phydev, MII_STAT1000); |
|
if (lpagb < ) |
return lpagb; |
|
adv = phy_read(phydev, MII_CTRL1000); |
|
if (adv < ) |
return adv; |
|
lpagb &= adv << ; |
} |
|
lpa = phy_read(phydev, MII_LPA); |
|
if (lpa < ) |
return lpa; |
|
adv = phy_read(phydev, MII_ADVERTISE); |
|
if (adv < ) |
return adv; |
|
lpa &= adv; |
|
phydev->speed = SPEED_10; |
phydev->duplex = DUPLEX_HALF; |
phydev->pause = phydev->asym_pause = ; |
|
if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { |
phydev->speed = SPEED_1000; |
|
if (lpagb & LPA_1000FULL) |
phydev->duplex = DUPLEX_FULL; |
} else if (lpa & (LPA_100FULL | LPA_100HALF)) { |
phydev->speed = SPEED_100; |
|
if (lpa & LPA_100FULL) |
phydev->duplex = DUPLEX_FULL; |
} else |
if (lpa & LPA_10FULL) |
phydev->duplex = DUPLEX_FULL; |
|
if (phydev->duplex == DUPLEX_FULL){ |
phydev->pause = lpa & LPA_PAUSE_CAP ? : ; |
phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? : ; |
} |
} else { |
int bmcr = phy_read(phydev, MII_BMCR); |
if (bmcr < ) |
return bmcr; |
|
if (bmcr & BMCR_FULLDPLX) |
phydev->duplex = DUPLEX_FULL; |
else |
phydev->duplex = DUPLEX_HALF; |
|
if (bmcr & BMCR_SPEED1000) |
phydev->speed = SPEED_1000; |
else if (bmcr & BMCR_SPEED100) |
phydev->speed = SPEED_100; |
else |
phydev->speed = SPEED_10; |
|
phydev->pause = phydev->asym_pause = ; |
} |
|
return ; |
} |
EXPORT_SYMBOL(genphy_read_status); |
|
|
static int smsc_phy_config_init(struct phy_device *phydev) <---------+
{
printk("zengjf check position %s.\n", __func__);
#if 0 // 这段代码会导致PHY无法自动识别到网线插入
int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
if (rc < )
return rc; /* Enable energy detect mode for this SMSC Transceivers */
rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
rc | MII_LAN83C185_EDPWRDOWN);
if (rc < )
return rc;
#endif return smsc_phy_ack_interrupt (phydev);
}

OK335xS LAN8710 phy driver hacking的更多相关文章

  1. OK335xS davinci mdio driver hacking

    /******************************************************************************* * OK335xS davinci m ...

  2. OK335xS pwm buzzer Linux driver hacking

    /**************************************************************************** * OK335xS pwm buzzer L ...

  3. OK335xS knob driver hacking

    /************************************************************************* * OK335xS knob driver hac ...

  4. OK335xS pwm device register hacking

    /************************************************************************* * OK335xS pwm device regi ...

  5. OK335xS I2C device registe hacking

    /*************************************************************************** * OK335xS I2C device re ...

  6. I.MX6 PWM buzzer driver hacking with Demo test

    /***************************************************************************** * I.MX6 PWM buzzer dr ...

  7. OK335xS psplash make-image-header.sh hacking

    /***************************************************************************** * OK335xS psplash mak ...

  8. I.MX6 gpio-keys driver hacking

    /**************************************************************************** * I.MX6 gpio-keys driv ...

  9. I.MX6 bq27441 driver hacking

    /************************************************************************* * I.MX6 bq27441 driver ha ...

随机推荐

  1. Qt+MinGW+OpenCV开发环境在win7系统下的搭建(最新20140423)

    1 搭建环境 (1)联想Y470笔记本电脑,win7操作系统 (2)Qt 5.2.1 Open Source :(Qt Online installer for Window(9MB),即下载页面最上 ...

  2. PAT Ranking (排名)

    PAT Ranking (排名) Programming Ability Test (PAT) is organized by the College of Computer Science and ...

  3. PAT乙级真题1004. 成绩排名 (20)(解题)

    题目: 读入n名学生的姓名.学号.成绩,分别输出成绩最高和成绩最低学生的姓名和学号. 输入格式:每个测试输入包含1个测试用例,格式为 第1行:正整数n 第2行:第1个学生的姓名 学号 成绩 第3行:第 ...

  4. wancms从apache迁移至nginx

    首先解决nginx1.2不支持pathinfo问题,见上一篇博文 其次是数据库的用户名变了之后,修改各种配置,wancms的,ucenter的,bbs的 还有一个是wacms的后台站点管理里面的uc配 ...

  5. EXTJS 4.2 资料 控件之Grid 添加行,编辑行,删除行

    //SiteVariableConfigValue类,创立一个模型类 Ext.define("SiteVariableConfigValue", { extend: "E ...

  6. windows 2008 R2 Activition

    无需破解:Windows Server 2008 R2 至少免费使用 900天 1.首先安装后,有一个180天的试用期. 2.在180天试用期即将结束时,使用下面的评估序列号激活Svr 2008 R2 ...

  7. pageControl指示器和图片放大-b

    小编由于篇幅问题,截取了最后一篇,如果需要看其他的三篇文章,可以去笔者的简书看:http://www.jianshu.com/users/9f3739421d15/latest_articles 另外 ...

  8. 实时数据处理环境搭建flume+kafka+storm:3.kafka安装

    1.  解压  tar -zxvf   2.配置/app/kafka_2.9.2-0.8.1.1/config/server.properties     #标识--     broker.id=0 ...

  9. (转载)shell日志分析常用命令

    shell日志分析常用命令总结 时间:2016-03-09 15:55:29来源:网络 导读:shell日志分析的常用命令,用于日志分析的shell脚本,统计日志中百度蜘蛛的抓取量.抓取最多的页面.抓 ...

  10. @Repository、@Service、@Controller 和 @Component(转)

    鸣谢:http://blog.csdn.net/ye1992/article/details/19971467 @Repository.@Service.@Controller 和 @Componen ...