/********************************************************************
* 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. Python OS模块标准库的系统接口及操作方法

    Python OS模块标准库的系统接口及操作方法 os.name 返回当前操作系统名,定义了'posix','nt','mac','os2','ce','java'(我使用win7/python3.1 ...

  2. 内核升级修复nfs

    Not starting NFS kernel daemon: no support in current kernel. sudo gedit /etc/init.d/nfs-kernel-serv ...

  3. 快速编译Delphi XE3 项目工程组

    Embarcadero 做了个好事.工程组可以直接使用 MSBuild 进行编译,让发布更简单.在Bin目录中 rsvars.bat 用于设置编译的环境变量,结合使用就可以顺利进行编译. call r ...

  4. 【Google Protocol Buffer】Google Protocol Buffer

    http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Google Protocol Buffer 的使用和原理 Protocol Buffers ...

  5. xfire for web-Service

    1.0 XFire XFire是codeHaus组织提供的一个开源框架,它构建了POJO和SOA之间的桥梁,主要特性就是支持将POJO通过非常简单的方式发布成Web服务,这种处理方式不仅充分发挥了PO ...

  6. dijkstra,bellman-ford,floyd分析比较

    http://www.cnblogs.com/mengxm-lincf/archive/2012/02/11/2346288.html 其实我一直存在疑惑是什么导致dijkstra不能处理负权图? 今 ...

  7. vc2008程序发布指南

    vc2008程序发布指南 2008-05-03 17:46 vc2008开发的程序的发布方式可以有5种方式: 1. 采用静态链接到crt和MFC. 只要你拥有组成程序的所有源代码,你就可以采用这种方式 ...

  8. WAF SSI

    http://www.2cto.com/Article/201405/299154.html

  9. android 64位的so文件 报错

     问题解决了,原因是因为豌豆荚搞了个64位的so文件,然后其他用到so的就必须也要有64位的,把他们的64位的so文件删除了,就OK了...

  10. Linux如何在虚拟机中挂载iso yum源

    首先,将作为源的iso的挂载到系统上. 代码如下: mount -o loop /dev/cdrom /mnt/iso/ 或者 mount -o loop /xxx/xxx.iso /mnt/iso/ ...