文章目录
这是个人驱动开发过程中做的一些记录,仅代表个人意见和理解,不喜勿喷
- 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, ®val);
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, ®val);
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驱动接口,设备的抽象、接口的实现以及彼此之间的关系讲解完成。