OK335xS davinci mdio driver hacking

简介: /******************************************************************************* * OK335xS davinci mdio driver hacking * 说明: * 以前一直也想对网卡驱动的工作原理进行跟踪,这次正好有机会,先跟mdio接口部分 * 的代码。
/*******************************************************************************
 *                  OK335xS davinci mdio driver hacking
 * 说明:
 *     以前一直也想对网卡驱动的工作原理进行跟踪,这次正好有机会,先跟mdio接口部分
 * 的代码。
 *
 *                                             2016-3-1 深圳 南山平山村 曾剑锋
 ******************************************************************************/

static struct platform_driver davinci_mdio_driver = {
    .driver = {
        .name     = "davinci_mdio",
        .owner     = THIS_MODULE,
        .pm     = &davinci_mdio_pm_ops,
    },
    .probe = davinci_mdio_probe,                                       ---------+
    .remove = __devexit_p(davinci_mdio_remove),                                 |
};                                                                              |
                                                                                |
static int __init davinci_mdio_init(void)                                       |
{                                                                               |
    return platform_driver_register(&davinci_mdio_driver);                      |
}                                                                               |
device_initcall(davinci_mdio_init);                                             |
                                                                                |
static void __exit davinci_mdio_exit(void)                                      |
{                                                                               |
    platform_driver_unregister(&davinci_mdio_driver);                           |
}                                                                               |
module_exit(davinci_mdio_exit);                                                 |
                                                                                |
MODULE_LICENSE("GPL");                                                          |
MODULE_DESCRIPTION("DaVinci MDIO driver");                                      |
                                                                                |
                                                                                |
static int __devinit davinci_mdio_probe(struct platform_device *pdev)   <-------+
{
    struct mdio_platform_data *pdata = pdev->dev.platform_data;
    struct device *dev = &pdev->dev;
    struct davinci_mdio_data *data;
    struct resource *res;
    struct phy_device *phy;
    int ret, addr;

    data = kzalloc(sizeof(*data), GFP_KERNEL);
    if (!data) {
        dev_err(dev, "failed to alloc device data\n");
        return -ENOMEM;
    }

    data->pdata = pdata ? (*pdata) : default_pdata;

    data->bus = mdiobus_alloc();
    if (!data->bus) {
        dev_err(dev, "failed to alloc mii bus\n");
        ret = -ENOMEM;
        goto bail_out;
    }

    data->bus->name        = dev_name(dev);
    data->bus->read        = davinci_mdio_read,
    data->bus->write    = davinci_mdio_write,
    data->bus->reset    = davinci_mdio_reset,
    data->bus->parent    = dev;
    data->bus->priv        = data;
    snprintf(data->bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);

    pm_runtime_enable(&pdev->dev);
    pm_runtime_get_sync(&pdev->dev);
    data->clk = clk_get(&pdev->dev, "fck");
    if (IS_ERR(data->clk)) {
        data->clk = NULL;
        dev_err(dev, "failed to get device clock\n");
        ret = PTR_ERR(data->clk);
        goto bail_out;
    }

    dev_set_drvdata(dev, data);
    data->dev = dev;
    spin_lock_init(&data->lock);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(dev, "could not find register map resource\n");
        ret = -ENOENT;
        goto bail_out;
    }

    res = devm_request_mem_region(dev, res->start, resource_size(res),
                        dev_name(dev));
    if (!res) {
        dev_err(dev, "could not allocate register map resource\n");
        ret = -ENXIO;
        goto bail_out;
    }

    data->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
    if (!data->regs) {
        dev_err(dev, "could not map mdio registers\n");
        ret = -ENOMEM;
        goto bail_out;
    }

    /* register the mii bus */
    ret = mdiobus_register(data->bus);        -----------------------+
    if (ret)                                                         |
        goto bail_out;                                               |
                                                                     |
    /* scan and dump the bus */                                      |
    for (addr = 0; addr < PHY_MAX_ADDR; addr++) {                    |
        phy = data->bus->phy_map[addr];                              |
        if (phy) {                                                   |
            dev_info(dev, "phy[%d]: device %s, driver %s\n",         |
                 phy->addr, dev_name(&phy->dev),                     |
                 phy->drv ? phy->drv->name : "unknown");             |
        }                                                            |
    }                                                                |
                                                                     |
    return 0;                                                        |
                                                                     |
bail_out:                                                            |
    if (data->bus)                                                   |
        mdiobus_free(data->bus);                                     |
    if (data->clk)                                                   |
        clk_put(data->clk);                                          |
    pm_runtime_put_sync(&pdev->dev);                                 |
    pm_runtime_disable(&pdev->dev);                                  |
                                                                     |
    kfree(data);                                                     |
                                                                     |
    return ret;                                                      |
}                                                                    |
                                                                     |
int mdiobus_register(struct mii_bus *bus)        <-------------------+
{
    int i, err;

    if (NULL == bus || NULL == bus->name ||
            NULL == bus->read ||
            NULL == bus->write)
        return -EINVAL;

    BUG_ON(bus->state != MDIOBUS_ALLOCATED &&
           bus->state != MDIOBUS_UNREGISTERED);

    bus->dev.parent = bus->parent;
    bus->dev.class = &mdio_bus_class;
    bus->dev.groups = NULL;
    dev_set_name(&bus->dev, "%s", bus->id);

    err = device_register(&bus->dev);
    if (err) {
        printk(KERN_ERR "mii_bus %s failed to register\n", bus->id);
        return -EINVAL;
    }

    mutex_init(&bus->mdio_lock);

    if (bus->reset)
        bus->reset(bus);

    for (i = 0; i < PHY_MAX_ADDR; i++) {
        if ((bus->phy_mask & (1 << i)) == 0) {
            struct phy_device *phydev;

            phydev = mdiobus_scan(bus, i);       -----------------------+
            if (IS_ERR(phydev)) {                                       |
                err = PTR_ERR(phydev);                                  |
                goto error;                                             |
            }                                                           |
        }                                                               |
    }                                                                   |
                                                                        |
    bus->state = MDIOBUS_REGISTERED;                                    |
    pr_info("%s: probed\n", bus->name);                                 |
    return 0;                                                           |
                                                                        |
error:                                                                  |
    while (--i >= 0) {                                                  |
        if (bus->phy_map[i])                                            |
            device_unregister(&bus->phy_map[i]->dev);                   |
    }                                                                   |
    device_del(&bus->dev);                                              |
    return err;                                                         |
}                                                                       |
EXPORT_SYMBOL(mdiobus_register);                                        |
                                                                        |
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)  <-------+
{
    struct phy_device *phydev;
    int err;

    phydev = get_phy_device(bus, addr);                         -----------+
    if (IS_ERR(phydev) || phydev == NULL)                                  |
        return phydev;                                                     |
                                                                           |
    err = phy_device_register(phydev);        ---------------+             |
    if (err) {                                               |             |
        phy_device_free(phydev);                             |             |
        return NULL;                                         |             |
    }                                                        |             |
                                                             |             |
    return phydev;                                           |             |
}                                                            |             |
EXPORT_SYMBOL(mdiobus_scan);                                 |             |
                                                             |             |
int phy_device_register(struct phy_device *phydev)   <-------+             |
{                                                                          |
    int err;                                                               |
                                                                           |
    /* Don't register a phy if one is already registered at this           |
     * address */                                                          |
    if (phydev->bus->phy_map[phydev->addr])                                |
        return -EINVAL;                                                    |
    phydev->bus->phy_map[phydev->addr] = phydev;                           |
                                                                           |
    /* Run all of the fixups for this PHY */                               |
    phy_scan_fixups(phydev);                    ------------------+        |
                                                                  |        |
    err = device_register(&phydev->dev);                          |        |
    if (err) {                                                    |        |
        pr_err("phy %d failed to register\n", phydev->addr);      |        |
        goto out;                                                 |        |
    }                                                             |        |
                                                                  |        |
    return 0;                                                     |        |
                                                                  |        |
 out:                                                             |        |
    phydev->bus->phy_map[phydev->addr] = NULL;                    |        |
    return err;                                                   |        |
}                                                                 |        |
EXPORT_SYMBOL(phy_device_register);                               |        |
                                                                  |        |
/* Runs any matching fixups for this phydev */                    |        |
int phy_scan_fixups(struct phy_device *phydev)        <-----------+        |
{                                                                          |
    struct phy_fixup *fixup;                                               |
                                                                           |
    mutex_lock(&phy_fixup_lock);                                           |
    list_for_each_entry(fixup, &phy_fixup_list, list) {                    |
        if (phy_needs_fixup(phydev, fixup)) {                              |
            int err;                                                       |
                                                                           |
            err = fixup->run(phydev);                                      |
                                                                           |
            if (err < 0) {                                                 |
                mutex_unlock(&phy_fixup_lock);                             |
                return err;                                                |
            }                                                              |
        }                                                                  |
    }                                                                      |
    mutex_unlock(&phy_fixup_lock);                                         |
                                                                           |
    return 0;                                                              |
}                                                                          |
                                                                           |
struct phy_device * get_phy_device(struct mii_bus *bus, int addr) <--------+
{
    struct phy_device *dev = NULL;
    u32 phy_id;
    int r;

    r = get_phy_id(bus, addr, &phy_id);           ------------------+
    if (r)                                                          |
        return ERR_PTR(r);                                          |
                                                                    |
    /* If the phy_id is mostly Fs, there is no device there */      |
    if ((phy_id & 0x1fffffff) == 0x1fffffff)                        |
        return NULL;                                                |
                                                                    |
    dev = phy_device_create(bus, addr, phy_id);             --------*----+
                                                                    |    |
    return dev;                                                     |    |
}                                                                   |    |
EXPORT_SYMBOL(get_phy_device);                                      |    |
                                                                    |    |
int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id)  <-------+    |
{                                                                        |
    int phy_reg;                                                         |
                                                                         |
    /* Grab the bits from PHYIR1, and put them                           |
     * in the upper half */                                              |
    phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);                      |
                                                                         |
    if (phy_reg < 0)                                                     |
        return -EIO;                                                     |
                                                                         |
    *phy_id = (phy_reg & 0xffff) << 16;                                  |
                                                                         |
    /* Grab the bits from PHYIR2, and put them in the lower half */      |
    phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);                      |
                                                                         |
    if (phy_reg < 0)                                                     |
        return -EIO;                                                     |
                                                                         |
    *phy_id |= (phy_reg & 0xffff);                                       |
                                                                         |
    return 0;                                                            |
}                                                                        |
EXPORT_SYMBOL(get_phy_id);                                               |
                                                                         |
static struct phy_device* phy_device_create(struct mii_bus *bus,  <------+
                        int addr, int phy_id)
{
    struct phy_device *dev;

    /* We allocate the device, and initialize the
     * default values */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);

    if (NULL == dev)
        return (struct phy_device*) PTR_ERR((void*)-ENOMEM);

    dev->dev.release = phy_device_release;

    dev->speed = 0;
    dev->duplex = -1;
    dev->pause = dev->asym_pause = 0;
    dev->link = 1;
    dev->interface = PHY_INTERFACE_MODE_GMII;

    dev->autoneg = AUTONEG_ENABLE;

    dev->addr = addr;
    dev->phy_id = phy_id;
    dev->bus = bus;
    dev->dev.parent = bus->parent;
    dev->dev.bus = &mdio_bus_type;                            // important
    dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL;
    dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr);

    dev->state = PHY_DOWN;

    mutex_init(&dev->lock);
    INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);      ----------+
                                                                            |
    /* Request the appropriate module unconditionally; don't                |
       bother trying to do so only if it isn't already loaded,              |
       because that gets complicated. A hotplug event would have            |
       done an unconditional modprobe anyway.                               |
       We don't do normal hotplug because it won't work for MDIO            |
       -- because it relies on the device staying around for long           |
       enough for the driver to get loaded. With MDIO, the NIC              |
       driver will get bored and give up as soon as it finds that           |
       there's no driver _already_ loaded. */                               |
    request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));   |
                                                                            |
    return dev;                                                             |
}                                                                           |
                                                                            |
void phy_state_machine(struct work_struct *work)              <-------------+
{
    struct delayed_work *dwork = to_delayed_work(work);
    struct phy_device *phydev =
            container_of(dwork, struct phy_device, state_queue);
    int needs_aneg = 0;
    int err = 0;


    mutex_lock(&phydev->lock);

    if (phydev->adjust_state)
        phydev->adjust_state(phydev->attached_dev);

    switch(phydev->state) {
        case PHY_DOWN:
        case PHY_STARTING:
        case PHY_READY:
        case PHY_PENDING:
            break;
        case PHY_UP:
            needs_aneg = 1;

            phydev->link_timeout = PHY_AN_TIMEOUT;

            break;
        case PHY_AN:
            err = phy_read_status(phydev);

            if (err < 0)
                break;

            /* If the link is down, give up on
             * negotiation for now */
            if (!phydev->link) {
                phydev->state = PHY_NOLINK;
                netif_carrier_off(phydev->attached_dev);
                phydev->adjust_link(phydev->attached_dev);
                break;
            }

            /* Check if negotiation is done.  Break
             * if there's an error */
            err = phy_aneg_done(phydev);
            if (err < 0)
                break;

            /* If AN is done, we're running */
            if (err > 0) {
                phydev->state = PHY_RUNNING;
                netif_carrier_on(phydev->attached_dev);
                phydev->adjust_link(phydev->attached_dev);

            } else if (0 == phydev->link_timeout--) {
                int idx;

                needs_aneg = 1;
                /* If we have the magic_aneg bit,
                 * we try again */
                if (phydev->drv->flags & PHY_HAS_MAGICANEG)
                    break;

                /* The timer expired, and we still
                 * don't have a setting, so we try
                 * forcing it until we find one that
                 * works, starting from the fastest speed,
                 * and working our way down */
                idx = phy_find_valid(0, phydev->supported);

                phydev->speed = settings[idx].speed;
                phydev->duplex = settings[idx].duplex;

                phydev->autoneg = AUTONEG_DISABLE;

                pr_info("Trying %d/%s\n", phydev->speed,
                        DUPLEX_FULL ==
                        phydev->duplex ?
                        "FULL" : "HALF");
            }
            break;
        case PHY_NOLINK:
            err = phy_read_status(phydev);

            if (err)
                break;

            if (phydev->link) {
                phydev->state = PHY_RUNNING;
                netif_carrier_on(phydev->attached_dev);
                phydev->adjust_link(phydev->attached_dev);
            }
            break;
        case PHY_FORCING:
            err = genphy_update_link(phydev);

            if (err)
                break;

            if (phydev->link) {
                phydev->state = PHY_RUNNING;
                netif_carrier_on(phydev->attached_dev);
            } else {
                if (0 == phydev->link_timeout--) {
                    phy_force_reduction(phydev);
                    needs_aneg = 1;
                }
            }

            phydev->adjust_link(phydev->attached_dev);
            break;
        case PHY_RUNNING:
            /* Only register a CHANGE if we are
             * polling */
            if (PHY_POLL == phydev->irq)
                phydev->state = PHY_CHANGELINK;
            break;
        case PHY_CHANGELINK:
            err = phy_read_status(phydev);

            if (err)
                break;

            if (phydev->link) {
                phydev->state = PHY_RUNNING;
                netif_carrier_on(phydev->attached_dev);
            } else {
                phydev->state = PHY_NOLINK;
                netif_carrier_off(phydev->attached_dev);
            }

            phydev->adjust_link(phydev->attached_dev);

            if (PHY_POLL != phydev->irq)
                err = phy_config_interrupt(phydev,
                        PHY_INTERRUPT_ENABLED);
            break;
        case PHY_HALTED:
            if (phydev->link) {
                phydev->link = 0;
                netif_carrier_off(phydev->attached_dev);
                phydev->adjust_link(phydev->attached_dev);
            }
            break;
        case PHY_RESUMING:

            err = phy_clear_interrupt(phydev);

            if (err)
                break;

            err = phy_config_interrupt(phydev,
                    PHY_INTERRUPT_ENABLED);

            if (err)
                break;

            if (AUTONEG_ENABLE == phydev->autoneg) {
                err = phy_aneg_done(phydev);
                if (err < 0)
                    break;

                /* err > 0 if AN is done.
                 * Otherwise, it's 0, and we're
                 * still waiting for AN */
                if (err > 0) {
                    err = phy_read_status(phydev);
                    if (err)
                        break;

                    if (phydev->link) {
                        phydev->state = PHY_RUNNING;
                        netif_carrier_on(phydev->attached_dev);
                    } else
                        phydev->state = PHY_NOLINK;
                    phydev->adjust_link(phydev->attached_dev);
                } else {
                    phydev->state = PHY_AN;
                    phydev->link_timeout = PHY_AN_TIMEOUT;
                }
            } else {
                err = phy_read_status(phydev);                   ---------+
                if (err)                                                  |
                    break;                                                |
                                                                          |
                if (phydev->link) {                                       |
                    phydev->state = PHY_RUNNING;                          |
                    netif_carrier_on(phydev->attached_dev);               |
                } else                                                    |
                    phydev->state = PHY_NOLINK;                           |
                phydev->adjust_link(phydev->attached_dev);                |
            }                                                             |
            break;                                                        |
    }                                                                     |
                                                                          |
    mutex_unlock(&phydev->lock);                                          |
                                                                          |
    if (needs_aneg)                                                       |
        err = phy_start_aneg(phydev);                                     |
                                                                          |
    if (err < 0)                                                          |
        phy_error(phydev);                                                |
                                                                          |
    schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ);     |
}                                                                         |
                                                                          |
static inline int phy_read_status(struct phy_device *phydev) {   <--------+
    return phydev->drv->read_status(phydev);
}

 

目录
打赏
0
0
0
0
12
分享
相关文章
Cause: dm.jdbc.driver.DMException: Digital overflow
在测试环境中遇到报错:`org.apache.ibatis.type.TypeException`,原因是为参数#1设置非空值时JdbcType为空,最终导致`dm.jdbc.driver.DMException: Digital overflow`。经分析发现,测试环境中的`invoiceId`字段类型为`BIGINT`,而传入的字符串值超出了`BIGINT`的最大长度,导致数字溢出。本地环境中该字段为`varchar(64)`,因此未出现此问题。调整测试环境表结构后,问题得以解决。
OK335xS dhcpcd porting
/********************************************************************** * OK335xS dhcpcd porting * 说明: * 之前在看Android源码的时候看到dhcpcd,但是busybox一直用的dhcpc, * 一般来说加了d都是后台运行的程序,也就意味着可能是可以做到动态获取 * DHCP IP了,当然目前没有测试,仅仅是先编译看一下效果,看是否会出问题。
1111 0
OK335xS Qt network hacking
/********************************************************************** * OK335xS Qt network hacking * 说明: * 应该半年前尝试过来解读这个程序,但是那时候对有些东西不是很理解, * 最后不了了之了,这次因为需要,所以重新对network的mainwindow.cpp进行 * 一下解读。
717 0
OK335xS LAN8710 phy driver hacking
/******************************************************************** * OK335xS LAN8710 phy driver hacking * 说明: * 本文主要是对OK335xS中的phy的驱动进行代码跟踪,并解决当前遇到 * LAN8710上电后插入网线,会导致LAN8710无法自动握手,Link灯不亮,内核 * 也检测不到LAN8710有状态发生了改变,最终问题定位于LAN8710的驱动初 * 始化部分,本文解决办法选择注释掉对应的内容就行了。
1133 0
OK335xS mac address hacking
/*********************************************************************** * OK335xS mac address hacking * 声明: * 在一般的嵌入式产品中,一般mac地址都是存在于CPU芯片中,不过有时候 * 我们也许会表示怀疑,因为我们可能更希望知道那些东西到底存在哪里,以一 * 种什么样的形式存在。
899 0
I.MX6 Goodix GT9xx touchscreen driver porting
/************************************************************************ * I.MX6 Goodix GT9xx touchscreen driver porting * 声明: * 本文主要是记录GT9xx电容Touch移植过程中遇到的一些问题。
2440 0
OK335xS knob driver hacking
/************************************************************************* * OK335xS knob driver hacking * 说明: * 本文主要是为了分析knob设备的创建,驱动层如何注册,发送信息。
716 0