zoukankan      html  css  js  c++  java
  • 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.0 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.0/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 0;                                       |               |  | |
        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    = 0,                                     |       | |  | |
            .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
    ", | | |  | |
                        new_driver->name, retval);                      | | |  | |
                                                                        | | |  | |
                return retval;                                          | | |  | |
            }                                                           | | |  | |
                                                                        | | |  | |
            pr_debug("%s: Registered new driver
    ", new_driver->name);  | | |  | |
                                                                        | | |  | |
            return 0;                                                   | | |  | |
        }                                                               | | |  | |
        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 = 0;                                                  | |  | |
                                                                          | |  | |
            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 < 0)                                                    |  | |
                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 < 0)                                                |  | |
                    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 0;                                                       |  | |
        }                                                                   |  | |
                                                                            |  | |
        int genphy_config_aneg(struct phy_device *phydev)        <----------+  | |
        {                                                                      | |
            int result;                                                        | |
                                                                               | |
            printk("zengjf check postion at %s.
    ", __func__);                 | |
                                                                               | |
            if (AUTONEG_ENABLE != phydev->autoneg)                             | |
                return genphy_setup_forced(phydev);                            | |
                                                                               | |
            result = genphy_config_advert(phydev);                             | |
                                                                               | |
            if (result < 0) /* error */                                        | |
                return result;                                                 | |
                                                                               | |
            if (result == 0) {                                                 | |
                /* 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 < 0)                                                   | |
                    return ctl;                                                | |
                                                                               | |
                if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))            | |
                    result = 1; /* do restart aneg */                          | |
            }                                                                  | |
                                                                               | |
            /* Only restart aneg if we are advertising something different     | |
             * than we were before.     */                                     | |
            if (result > 0)                                                    | |
                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 = 0;                                                       |
                                                                                 |
            /* 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 < 0)                                               |
                        return lpagb;                                            |
                                                                                 |
                    adv = phy_read(phydev, MII_CTRL1000);                        |
                                                                                 |
                    if (adv < 0)                                                 |
                        return adv;                                              |
                                                                                 |
                    lpagb &= adv << 2;                                           |
                }                                                                |
                                                                                 |
                lpa = phy_read(phydev, MII_LPA);                                 |
                                                                                 |
                if (lpa < 0)                                                     |
                    return lpa;                                                  |
                                                                                 |
                adv = phy_read(phydev, MII_ADVERTISE);                           |
                                                                                 |
                if (adv < 0)                                                     |
                    return adv;                                                  |
                                                                                 |
                lpa &= adv;                                                      |
                                                                                 |
                phydev->speed = SPEED_10;                                        |
                phydev->duplex = DUPLEX_HALF;                                    |
                phydev->pause = phydev->asym_pause = 0;                          |
                                                                                 |
                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 ? 1 : 0;                 |
                    phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;           |
                }                                                                |
            } else {                                                             |
                int bmcr = phy_read(phydev, MII_BMCR);                           |
                if (bmcr < 0)                                                    |
                    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 = 0;                          |
            }                                                                    |
                                                                                 |
            return 0;                                                            |
        }                                                                        |
        EXPORT_SYMBOL(genphy_read_status);                                       |
                                                                                 |
                                                                                 |
        static int smsc_phy_config_init(struct phy_device *phydev)     <---------+
        {
            printk("zengjf check position %s.
    ", __func__);
        #if 0   // 这段代码会导致PHY无法自动识别到网线插入
            int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
            if (rc < 0)
                return rc;
        
            /* Enable energy detect mode for this SMSC Transceivers */
            rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
                       rc | MII_LAN83C185_EDPWRDOWN);
            if (rc < 0)
                return rc;
        #endif
        
            return smsc_phy_ack_interrupt (phydev);
        }
  • 相关阅读:
    阻止事件冒泡
    移动端开发
    angular6 管道多参数传输 Pipe
    在div上添加小三角
    angular6 使用@Input() @Output()
    angular6 想要获取页面某些事件 如 点击 window宽高等等
    如何将项目添加到git上
    使用vue-cli 搭建element-admin后台
    bootstrap4 常用样式类名 (供自己参考)
    键盘按下 keyCode 的值
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/5237412.html
Copyright © 2011-2022 走看看