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

相关推荐
byte轻骑兵3 小时前
嵌入式 ARM Linux 系统构成全解:从硬件到应用层层剖析
linux·arm开发·arm·嵌入式开发
Linux运维技术栈7 小时前
基于CVE安全公告号,全面修复麒麟ARM系统OpenSSH漏洞
安全·系统安全·arm
TeYiToKu1 天前
笔记整理—linux驱动开发部分(13)块设备
linux·c语言·驱动开发·笔记·嵌入式硬件·arm
Heaven6452 天前
2.7 以太网的 MAC 层
网络·计算机网络·mac·以太网
郁大锤3 天前
linux alsa-lib snd_pcm_open函数源码分析(四)
linux·音频·pcm·源码分析·驱动·alsa
fareast_mzh4 天前
no matching manifest for linux/amd64 in the manifest list entries
docker·arm·raspberry·busybox
yanlaifan4 天前
ARM(安谋) China处理器
arm
Linux运维技术栈6 天前
企业生产环境-麒麟V10(ARM架构)操作系统部署Zookeeper单节点&集群版
linux·运维·zookeeper·架构·arm
Linux运维技术栈6 天前
企业生产环境-麒麟V10(ARM架构)操作系统部署kafka高可用集群
linux·运维·架构·kafka·arm
嵌入式小能手8 天前
CAN编程示例之socket CAN
linux·arm