OK335xS LAN8710 phy driver hacking
- /********************************************************************
- * 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的更多相关文章
- OK335xS davinci mdio driver hacking
/******************************************************************************* * OK335xS davinci m ...
- OK335xS pwm buzzer Linux driver hacking
/**************************************************************************** * OK335xS pwm buzzer L ...
- OK335xS knob driver hacking
/************************************************************************* * OK335xS knob driver hac ...
- OK335xS pwm device register hacking
/************************************************************************* * OK335xS pwm device regi ...
- OK335xS I2C device registe hacking
/*************************************************************************** * OK335xS I2C device re ...
- I.MX6 PWM buzzer driver hacking with Demo test
/***************************************************************************** * I.MX6 PWM buzzer dr ...
- OK335xS psplash make-image-header.sh hacking
/***************************************************************************** * OK335xS psplash mak ...
- I.MX6 gpio-keys driver hacking
/**************************************************************************** * I.MX6 gpio-keys driv ...
- I.MX6 bq27441 driver hacking
/************************************************************************* * I.MX6 bq27441 driver ha ...
随机推荐
- 自动化运维——一键安装MySQL
根据项目需要,前段时间在搞EMM系统各种安装包的自动化部署工作,主要包括一键安装和一键启动\停止功能.总结记录下来,以供后用. 本文主要是自动安装MySQL5.7.11版,Linux版脚本在CentO ...
- oracle的function和procedure返回值给shell
本文演示两个关于如何在shell中调用oracle的function和procedure,并将返回值返回给shell. 1.首在package中创建function和procedure,脚本如下: G ...
- Kinetic使用注意点--ellipse
new Ellipse(config) 参数: config:包含所有配置项的对象. { radius: "半径,可以用数字a.数组[a,b]或对象{x:a,y:b}来表示" } ...
- 【多路复用】I/O多路复用
http://www.tuicool.com/articles/RBvqUz C#下用select方法实现socket服务端
- [译] ASP.NET 生命周期 – ASP.NET 请求生命周期(三)
使用特殊方法处理请求生命周期事件 为了在全局应用类中处理这些事件,我们会创建一个名称以 Application_ 开头,以事件名称结尾的方法,比如 Application_BeginRequest.举 ...
- UITableView自定义单元格
随手笔记: RootViewController代码 #import "RootViewController.h" #import "AddressContact.h&q ...
- Java中的break与continue区别
break跳出当前循环执行循环下面的程序, 如果break出现在嵌套循环的内层循环, 则break语句只会跳出当前层的循环; 当程序执行到continue时时, 则跳过本次循环程序重新回到循环开始继续 ...
- 滤镜简单demo(转,供参考)
NSURL *iamgeUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"default" ...
- Fail-fast的原因及解决方法
[转载]:http://blog.csdn.net/chenssy/article/details/38151189 在JDK的Collection中我们时常会看到类似于这样的话: 例如,ArrayL ...
- PAT-乙级-1044. 火星数字(20)
1044. 火星数字(20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 火星人是以13进制计数的: 地球人的 ...