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

相关推荐
TeYiToKu6 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
编程圈子1 天前
I.MX6U 裸机开发2. 芯片简介、汇编基础及GPIO操作准备工作
嵌入式硬件·arm
爱奔跑的虎子2 天前
FPGA实现以太网(一)、以太网基础知识
fpga开发·以太网·fpga·vivado
CyberSecurity_zhang3 天前
Armv8的安全启动
安全·arm·tf-a·tf-m·安全启动
苏寅3 天前
arm64-v8a 和 armeabi-v7a 有啥区别?
arm
CXDNW6 天前
【网络篇】计算机网络——链路层详述(笔记)
网络·笔记·智能路由器·以太网·局域网·链路层
ShiinaKaze7 天前
用图说明 CPU、MCU、MPU、SoC 的区别
mcu·cpu·mpu·soc·计算机架构
通俗_易懂8 天前
44-RK3588s调试 camera-engine-rkaiq(rkaiq_3A_server)
人工智能·计算机视觉·rk3588·驱动·camera·imx415
肖爱Kun8 天前
LINUX IIC总线驱动-设备框架
驱动
冷凝雨11 天前
【复旦微FM33 MCU 外设开发指南】外设篇1——硬件除法器
arm·1024程序员节·复旦微·fm33·fm33lc0