• 在RT-Thread下为MPU手搓以太网MAC驱动-4



    这是个人驱动开发过程中做的一些记录,仅代表个人意见和理解,不喜勿喷

    • MAC驱动需要支持不同的PHY芯片

    MAC驱动里面对MDIO的支持

    在第一篇文章中提到对MAC设备做出了抽象,其中MAC抽象里面有提供通过MDIO总线去访问PHY寄存器的读写操作接口(有省去其他操作接口)

    struct h3_macplib_ops
    {
        int32_t  (*macdev_writephy)(mac_dev *const dev, uint16_t addr, uint16_t reg, uint16_t data);
        int32_t  (*macdev_readphy) (mac_dev *const dev, uint16_t addr, uint16_t reg, uint16_t *val);
    };
    

    那我们同时也需要实现一个MDIO设备驱动,因为在RT-Thread下也有定义MDIO相关的操作接口。

    struct rt_mdio_bus_ops
    {
        rt_bool_t (*init)(void *bus, rt_uint32_t src_clock_hz);
        rt_size_t (*read)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size);
        rt_size_t (*write)(void *bus, rt_uint32_t addr, rt_uint32_t reg, void *data, rt_uint32_t size);
        rt_bool_t (*uninit)(void *bus);
    };
    
    struct rt_mdio_bus
    {
        void *hw_obj;
        char *name;
        struct rt_mdio_bus_ops *ops;
    };
    

    我们可以看到在RT-Thread下对MDIO设备和驱动接口也做了抽象的定义,比如MDIO驱动的操作接口包括初始化、读、写和解除初始化操作。对于MDIO设备,其包含对应的硬件内容,MDIO设备名和操作接口

    static struct rt_mdio_bus_ops h3_mdiobus_ops =
    {
        .init   = h3_mdioplib_init,
        .read   = h3_mdioplib_read,
        .write  = h3_mdioplib_write,
        .uninit = RT_NULL,
    };
    

    在mac驱动下,MDIO设备驱动的读取接口实现如下,在这个驱动接口实现中,我们通过获取MDIO总线下包含的硬件信息,做一个类型的强制转换,获取到了指向macplib_dev实例的指针,然后就可以通过这个macplib_dev访问mac设备抽象接口提供的PHY寄存器访问操作,实现了MDIO的读操作,整个代码还是相当的简单。

    static rt_size_t h3_mdioplib_read(void *bus, rt_uint32_t addr,
                                      rt_uint32_t reg, void *data, rt_uint32_t size)
    {
        rt_uint16_t val;
        rt_uint32_t *data_ptr = (rt_uint32_t *)data;
        struct h3_macplib_dev *macplib_dev;
        struct rt_mdio_bus    *mdioplib_bus = (struct rt_mdio_bus *)bus;
    
        RT_ASSERT(data != NULL);
        RT_ASSERT(bus  != NULL);
    
        if (4 != size) {
            return 0;
        }
    
        macplib_dev = (struct h3_macplib_dev *)mdioplib_bus->hw_obj;
        macplib_dev->mac_ops->macdev_readphy(&macplib_dev->mac_dev,
                                             (rt_uint16_t)addr, (rt_uint16_t)reg,
                                             &val);
    
        /* Get data from MII register. */
        *data_ptr = (rt_uint32_t)val;
    
        return 4;
    }
    

    在mac驱动下另外一个需要注意的地方是,mac驱动需要提供一个类似mdio驱动查找接口,用于PHY设备在初始化的时候,查找需要的MDIO设备驱动接口,用来实现对PHY寄存器的访问,代码实现如下。

    rt_mdio_t *h3_mdioplib_search(const char *name)
    {
        rt_uint32_t table_sz = sizeof(h3_macplib_devtable)/sizeof(uint32_t);
        struct h3_macplib_dev *macplib_dev;
    
        for (uint32_t i = 1; i < table_sz; i++)
        {
            macplib_dev = h3_macplib_devtable[i];
            if (rt_strcmp(name, macplib_dev->rt_mdiobus.name) == 0)
            {
                return &macplib_dev->rt_mdiobus;
            }
        }
    
        return NULL;
    }
    

    在PHY驱动中,对PHY设备的抽象定义时,增加了一个mdio_name的定义,用于定义该PHY设备对应的MDIO总线设备名,然后PHY设备可以通过该mdio_name名字,去查找到对应的MDIO总线设备。

    struct h3_kszplib_dev
    {
        const char *phy_name;
        uint32_t    phy_addr;
        const char *mdio_name;
        struct rt_phy_device rt_phydev;
    } ;
    
    static rt_phy_status h3_ksz9plib_init(struct rt_phy_device *phy, void *object,
                                          rt_uint32_t phy_addr, rt_uint32_t src_clock_hz)
    {
        rt_bool_t ret;
        rt_phy_status result  = PHY_STATUS_FAIL;
        rt_uint32_t counter   = PHY_TIMEOUT_COUNT;
        rt_uint32_t regval    = 0;
        rt_uint32_t deviceID  = 0;
        struct rt_mdio_bus    *mdio_bus;
        struct h3_kszplib_dev *kszplib_dev;
    
        RT_ASSERT(phy != RT_NULL);
    
        kszplib_dev = rt_container_of(phy, struct h3_kszplib_dev, rt_phydev);
        mdio_bus    = h3_mdioplib_search(kszplib_dev->mdio_name);
        RESULT_MATCH_CHECK(mdio_bus, RT_NULL, outs)
    
        kszplib_dev->phy_addr = phy_addr;
        phy->bus              = mdio_bus;
        phy->addr             = phy_addr;
    
        ret = mdio_bus->ops->init(mdio_bus, src_clock_hz);
        NOT_MATCH_CHECK(ret, RT_TRUE, outs)
    
        /* Initialization after PHY stars to work. */
        do
        {
            h3_kszplib_read(phy, GMII_PHYID1, &deviceID);
            counter--;
        } while ((deviceID != GMII_PHYID1_KSZ9131) && (counter != 0));
    
        RESULT_MATCH_CHECK(counter, 0, outs)
    
        result = h3_kszplib_read(phy, GMII_MCR, &regval);
        RESULT_MATCH_CHECK(result, PHY_STATUS_FAIL, outs)
    
        regval |= GMII_MCR_ANENABLE | GMII_MCR_ANRESTART;
        result  = h3_kszplib_write(phy, GMII_MCR, regval);
        RESULT_MATCH_CHECK(result, PHY_STATUS_FAIL, outs)
    
        counter = PHY_TIMEOUT_COUNT;
        /* Check auto negotiation complete. */
        do
        {
            result = h3_kszplib_read(phy, GMII_MSR, &regval);
            RESULT_MATCH_CHECK(result, PHY_STATUS_FAIL, outs)
    
            if ((regval & GMII_MSR_ANEGCOMPLETE) != 0)
            {
                break;
            }
        } while (--counter > 1);
    
    outs:
        return result;
    }
    

    MAC驱动与MDIO总线

    在mac设备的抽象中,由于都包含了rt_mdio_bus,因此在mac设备实例的初始化的时候,都将mac设备与其提供的mdio总线进行绑定,例如在实例初始化时的静态绑定。

    struct h3_macplib_dev
    {
        const char   *name;
        IRQn_Type     irqnum;
        H3_MAC_REGS   regs;
        rt_uint8_t    mac_addr[6];
        rt_uint8_t    dev_id;
        rt_uint8_t    reserved;
        mac_async_dev mac_dev;
        phy_async_dev phy_dev;
        const struct rt_mdio_bus_ops *mdio_ops;
        const struct h3_macplib_ops  *mac_ops;
        struct rt_mdio_bus rt_mdiobus;
        struct eth_device  rt_ethdev;
    } ;
    
    #if defined(BSP_USING_GMAC0) || defined(BSP_USING_EMAC0)
    struct h3_macplib_dev h3_macdev0 = {
        .name       = "e0",
        .irqnum     = MAC0_IRQn,
        .regs       = MAC0_REGS,
        .dev_id     = MAC0_ID,
        .rt_mdiobus =
        {
            .name       = MDIO0_DEVICE_NAME,
            .ops        = &h3_mdiobus_ops,
        },
        .phy_dev    =
        {
            .name       = PHY0_DEVICE_NAME,
            .phyID1     = H3_MACPLIB_PHY0ID1,
            .phyID2     = H3_MACPLIB_PHY0ID2,
            .phyaddr    = PHY0_DEVICE_ADDRESS,
        },
        .mac_ops    = &h3_macdev_ops,
    };
    #endif
    

    初始化时的绑定(仅展示部分相关代码)。

    int h3_macplib_init(void)
    {
        rt_err_t    state;
        rt_uint32_t table_sz = sizeof(h3_macplib_devtable)/sizeof(uint32_t);
        struct h3_macplib_dev *macplib_dev;
    
        for (uint32_t i = 1; i < table_sz; i++)
        {
            macplib_dev = h3_macplib_devtable[i];
    
            macplib_dev->mac_dev.devid     = macplib_dev->dev_id;
            macplib_dev->rt_mdiobus.hw_obj = (void *)macplib_dev;
        }
    }
    

    到此为止,mac驱动接口、PHY驱动接口和MDIO驱动接口,设备的抽象、接口的实现以及彼此之间的关系讲解完成。

  • 相关阅读:
    ElasticSearch第一讲:ElasticSearch从入门到精通
    【小程序】页面跳转
    vue项目,程序控制台不报错,但是也没有达到预期
    java毕业生设计校园讲座管理计算机源码+系统+mysql+调试部署+lw
    发明专利和实用新型专利的区别
    python BeautifulReport 处理非常规标签输出
    【论文精读4】MVSNet系列论文详解-CVP-MVSNet
    在MacOS中将HMCL添加到Launchpad启动台
    proxmox8.1更换源
    SpringBoot面试题3:Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
  • 原文地址:https://blog.csdn.net/oldman147/article/details/139355907