orangepi pc2基于uboot的网卡驱动分析

文件位置:drivers/net/sun8i_emac.c

这里顺便把linux的位置jie贴出来,因为全志H5(orangepi-pc2)的网口使用的是stm的ip核,所以驱动位置比较难找,位于drivers\net\ethernet\stmicro\stmmac\dwmac-sun8i.c ,可以作为参考。

本文主要分为两大部分,一是简要分析下uboot实现,二是结合调试经验分析下uboot实现的缺陷

1.网卡驱动uboot实现简析

1.1dma描述符

先看下dma使用的描述符(收发用同一结构体),如下:

struct emac_dma_desc {

u32 status;

u32 st;

u32 buf_addr;

u32 next;

} __aligned(ARCH_DMA_MINALIGN);

其中ARCH_DMA_MINALIGN定义为64,“__aligned”为gcc的对齐属性符号,这里告诉gcc此描述符是64字节对齐的,即便它只占用了16字节。这就意味着如果定义一个struct emac_dma_desc的数组,数组的每个元素都是64字节,而不是16字节。即,sizeof(struct emac_dma_desc)等于64。

 

简要介绍下描述符结构体的每个成员:

status字段:重要说一下bit31,对于收发描述符都一样,为1表示此描述符可被dma使用。Dma发送或接受完成此帧数据后会自动将bit31清0。其余字段,收、发描述符不同,具体可参考H5芯片手册。

st字段:对于发送描述符,主要包括帧长度(bit0~10)、帧开始结束标志以及发送中断控制(bit31);对于接收描述符,则主要包括接收buffer长度限制。

buf_addr字段:用于指示收发buffer物理地址。

next字段:指示下一个描述符物理地址。

1.2 网卡设备结构体

struct emac_eth_dev {

struct emac_dma_desc rx_chain[CONFIG_TX_DESCR_NUM];  //dma接收描述符数组

struct emac_dma_desc tx_chain[CONFIG_RX_DESCR_NUM];  //dma发送描述符数组

char rxbuffer[RX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN); //接收buffer

char txbuffer[TX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN);//发送buffer

 

u32 interface;  //网卡phy接口协议

u32 phyaddr;  //网卡phy地址

u32 link;     //连接状态

u32 speed;    //速度(10、100、1000)

u32 duplex;   //全双工

u32 phy_configured;

u32 tx_currdescnum;  //当前发送使用的描述符

u32 rx_currdescnum;  //当前接收使用的描述符

u32 addr;

u32 tx_slot;

bool use_internal_phy; //内部phy

 

enum emac_variant variant; //emac属性

void *mac_reg;   //mac寄存器基址

phys_addr_t sysctl_reg;  //系统控制寄存器基址

struct phy_device *phydev;

struct mii_dev *bus;  //mdio总线接口

#ifdef CONFIG_DM_GPIO

struct gpio_desc reset_gpio;

#endif

};

可以看到结构体第1、2个成员——dma描述符数组接收和发送的宏定义用反了!!不过幸亏这两个值定义为相同值,不然就走远了。这是此驱动的第一个bug!

1.3 sun8i_emac_eth_ofdata_to_platdata函数

主要是解析dts节点,获取寄存器、phy mode等信息。

 

1.4 sun8i_emac_eth_probe函数

  1. 调用sun8i_emac_board_setup函数,通过设置系统控制寄存器,使能emac时钟。
  2. 调用sun8i_emac_set_syscon函数,设置phy模式。
  3. 调用sun8i_mdio_init函数,设置mdio总线读写函数,并注册。
  4. 调用sun8i_phy_init函数,初始化phy

1.5 Start ops函数——sun8i_emac_eth_start

  1. soft reset mac
  2. 设置mac地址
  3. 设置发送、接收控制寄存器
  4. 设置dma 的burst len为8
  5. 调用rx_descs_init函数,初始化接收描述符
  6. 调用tx_descs_init函数,初始化发送描述符
  7. 调用genphy_parse_link函数,获取speed、duplex等信息
  8. 调用sun8i_adjust_link函数,根据获取的speed、duplex信息,设置mac的寄存器
  9. 使能tx、rx dma
  10. 使能mac rx和tx

移植时注意:发送、接收描述符中“buf_addr”和“next”都需要是物理地址(uboot中没有使能MMU,所以不需要区分)。

 

1.6 Send ops函数——sun8i_emac_eth_send

主要是根据需要发送包的物理地址和长度来fill发送描述符,并start 发送dma。

 

1.7 Recv ops函数——sun8i_emac_eth_recv

根据接收描述符status字段的bit31判断是否dma完成(完成bit31会被清零)。若完成,则取出描述符中的buffer地址返回给上层调用者。

 

总结:总的来说,uboot的orangepi网卡驱动比较简单。移植时注意虚实地址转换、数据cache处理、寄存器也较简单不需要做特殊处理。

2. Uboot实现缺陷分析

不过就是这么简单的一个uboot网卡驱动,确隐藏着多个缺陷。下面列举一二。

  • 就是上面1.2节提到的,收发dma描述符数量宏定义。使用反了,要不是恰好两个值一样,这样的低级错误,完全是没有code review的!
  • 发送函数中操作寄存器使能dma,没有进行判断

/* Start the DMA */

v = readl(priv->mac_reg + EMAC_TX_CTL1);

v |= BIT(31);/* mandatory */

v |= BIT(30);/* mandatory */

writel(v, priv->mac_reg + EMAC_TX_CTL1);

读取发送控制寄存器(EMAC_TX_CTL1)之后直接置相应位(bit31),然后写入控制寄存器。正确的做法是判断bit31是否为1,只有在dma停止执行时,才可以写此寄存器,否则在dma工作时操作寄存器是可能导致dma异常的!而且bit30在函数sun8i_emac_eth_init中已经使能,没必要每次都设置。可以改为下面的实现:

  /* Start the DMA */

  v = readl(priv->mac_reg + EMAC_TX_CTL1);

  if(~(v&BIT(31)))

  {

    v |= BIT(31);/* mandatory */

    writel(v, priv->mac_reg + EMAC_TX_CTL1);

  }

 

  • 未考虑promisc模式场景

这一点除了在寄存器上需要使能外,还体现在接收函数sun8i_emac_eth_recv。其只处理了当前一个描述符。在使能promisc模式后,线路上可能同时来多个包(包括杂包)。另外,在一个包处理的同时也可能继续来包。由于有杂包的混入,只处理一个包返回给上层,并不一定就是上层需要的那个正确包。更妥当的方式是在接收函数中对所有描述符轮询一遍,保证当前所有可用的描述符都得到处理返回给上层。当然,这个逻辑也可以在上层来做。

  • 未考虑中断场景

当然,uboot中均未使用中断,都是轮询模式。这里提出这点,主要是想披露下全志datasheet的bug。通看“Allwinner H5 Datasheet”居然没有找到网口中断清标志寄存器,这让我在准备自己实现中断处理函数时非常疑惑。由于H5网卡中断是高电平触发,这要没有清中断标志位的寄存器,中断一旦触发就会持续触发,不可能不提供这个寄存器的!Uboot虽然没有使用到中断,linux可是使用了的啊。经仔细研读linux相关代码,发现中断状态寄存器—— Interrupt Status Register(偏移为0x08)。其实也是中断清除寄存器(写1清除)。而datasheet显示这个寄存器为只读,也并未说明写1可以清除对应中断!!

  • 函数未考虑入参有效性检查

收发函数均未对入参进行有效性检查。一般的函数也就算了,网卡收发函数入参涉及收发包物理地址指针、长度等重要信息。如果不对入参进行判断,如指针为空、发包长度为0均会导致硬件dma异常或系统崩盘。

 

总结:真是国产的芯片品质、国产的datasheet品质、国产的驱动品质啊!哎,不说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值