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

文章目录

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

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

MAC驱动里面对MDIO的支持

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

c 复制代码
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相关的操作接口。

c 复制代码
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设备名和操作接口

c 复制代码
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的读操作,整个代码还是相当的简单。

c 复制代码
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寄存器的访问,代码实现如下。

c 复制代码
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总线设备。

c 复制代码
struct h3_kszplib_dev
{
    const char *phy_name;
    uint32_t    phy_addr;
    const char *mdio_name;
    struct rt_phy_device rt_phydev;
} ;
c 复制代码
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总线进行绑定,例如在实例初始化时的静态绑定。

c 复制代码
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;
} ;
c 复制代码
#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

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

c 复制代码
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驱动接口,设备的抽象、接口的实现以及彼此之间的关系讲解完成。

相关推荐
星马梦缘5 天前
驱动层开发——蜂鸣器驱动
stm32·单片机·嵌入式硬件·hal·驱动
Stone.Wu7 天前
快速理解ARM Cortex-M流水线:指令执行过程通俗解释
arm
我在人间贩卖青春7 天前
汇编之分支跳转指令
汇编·arm·分支跳转
我在人间贩卖青春7 天前
汇编之加载存储指令
汇编·arm·寄存器加载存储
我在人间贩卖青春7 天前
汇编之状态寄存器访问指令
汇编·arm·状态寄存器
我在人间贩卖青春7 天前
汇编之软中断指令和协处理指令
汇编·arm·软中断·协处理
我在人间贩卖青春7 天前
汇编之数据处理指令
汇编·arm·数据处理指令
fly的fly10 天前
浅析 QT远程部署及debug方案
qt·物联网·arm
切糕师学AI12 天前
ARM标准汇编(armasm)中的标号(Label)
汇编·arm
CHENG-JustDoIt13 天前
嵌入式开发 | ARM Cortex-M 系列中M3、M4、M23 和 M33四款处理器的深度对比分析
arm开发·单片机·嵌入式硬件·arm