当前位置: 首页 > 工具软件 > Xvisor > 使用案例 >

renesas ravb网卡驱动实现分析(linux uboot xvisor)

尹俊贤
2023-12-01

 

net_device结构体

相对于linux做了相当大简化,其结构及含义如下:

struct net_device {

char name[MAX_NETDEV_NAME_LEN]; //用于存放网络设备的设备名称

struct vmm_device *dev; //???

const struct net_device_ops *netdev_ops; //上层ops接口

const struct ethtool_ops *ethtool_ops; //可选ops接口

unsigned int state; //网络设备接口的状态

unsigned int link_state; //链接状态

void *priv; /* Driver specific private data */

void *nsw_priv; /* VMM virtual packet switching layer specific private data.*/

void *net_priv; /* VMM specific private data -Usecase is currently undefined */

unsigned char dev_addr[MAX_NDEV_HW_ADDRESS]; //硬件接口地址

unsigned int hw_addr_len; //硬件地址长度

unsigned int mtu; //网络设备接口的最大传输单元

unsigned int flags; //网络设备接口的标识符,其状态类型被定义在<linux/if.h>之中

unsigned long last_rx; //上次收包的jiffies时间

u32 irq; //设备中断号

physical_addr_t base_addr; //设备IO基地址

unsigned char dma; /* DMA channel */

struct net_device_stats stats; //设备状态(收发包数、错误数等统计信息)

struct phy_device *phydev; //PHY结构体实例

unsigned long trans_start; //传输开始的jiffies时间

int watchdog_timeo; //看门狗超时时间

struct vmm_device *vmm_dev; //???

struct netdev_queue *_tx; //发送队列

unsigned int num_tx_queues; //发送队列数

unsigned int num_rx_queues; //接收队列数

};

 

PHY设备信息及操作结构体-struct phy_device

struct phy_device {

struct phy_driver *drv; //phy设备驱动

struct mii_bus *bus; //mii挂载总线

struct device dev; //设备

u32 phy_id; //标识设备发现时的uid

struct phy_c45_device_ids c45_ids; //802.3-c45 Device Id

bool is_c45; //是否是c45设备

bool is_internal; //是否是MAC内嵌设备

enum phy_state state; //phy状态(start、ready、pending、running等)

u32 dev_flags; //设备标识

phy_interface_t interface; //phy接口模式(renesas设置为"rgmii-txid")

int addr; /* Bus address of the PHY (0-31) */

/*

 * forced speed & duplex (no autoneg)

 * partner speed & duplex & pause (autoneg)

 */

int speed;

int duplex;

int pause;

int asym_pause;

 

int link; /* The most recently read link state */

u32 interrupts; /* 目前只支持是否使能,后续扩展使能的中断号*/

 

/* Union of PHY and Attached devices' supported modes */

/* See mii.h for more info */

u32 supported;

u32 advertising;

 

int autoneg; //自协商

int link_timeout; //链接超时

int irq; //中断号,-1表示无中断号

 

/* private data pointer */

/* For use by PHYs to maintain extra state */

void *priv;

 

/* Interrupt and Polling infrastructure */

struct work_struct phy_queue;

struct delayed_work state_queue;

atomic_t irq_disable;

 

struct mutex lock; //互斥锁

struct net_device *attached_dev; //attach的net_device设备

void (*adjust_link)(struct net_device *dev); //连接改变回调

void (*adjust_state)(struct net_device *dev); //状态改变回调

};

 

PHY驱动结构体 - struct phy_driver

(config_aneg和read_status必须实现,其他的可选)

struct phy_driver {

u32 phy_id; //匹配的PHY id(phy_device.phy_id字段)

char *name; //兼容的名字

unsigned int phy_id_mask; //mask

u32 features; //特性(speed, duplex, etc)

u32 flags; //其他支持特性,如中断

 

/*

 * Called to initialize the PHY,

 * including after a reset

 */

int (*config_init)(struct phy_device *phydev);

 

/*

 * Called during discovery.  Used to set

 * up device-specific structures, if any

 */

int (*probe)(struct phy_device *phydev);

 

/* PHY Power Management */

int (*suspend)(struct phy_device *phydev);

int (*resume)(struct phy_device *phydev);

 

/*

 * Configures the advertisement and resets

 * autonegotiation if phydev->autoneg is on,

 * forces the speed to the current settings in phydev

 * if phydev->autoneg is off

 */

int (*config_aneg)(struct phy_device *phydev);

 

/* Determines the negotiated speed and duplex */

int (*read_status)(struct phy_device *phydev);

 

/* Clears any pending interrupts */

int (*ack_interrupt)(struct phy_device *phydev);

 

/* Enables or disables interrupts */

int (*config_intr)(struct phy_device *phydev);

 

/*

 * Checks if the PHY generated an interrupt.

 * For multi-PHY devices with shared PHY interrupt pin

 */

int (*did_interrupt)(struct phy_device *phydev);

 

/* Clears up any memory if needed */

void (*remove)(struct phy_device *phydev);

 

/* Returns true if this is a suitable driver for the given

 * phydev.  If NULL, matching is based on phy_id and

 * phy_id_mask.

 */

int (*match_phy_device)(struct phy_device *phydev);

 

/* A function provided by a phy specific driver to override the

 * the PHY driver framework support for reading a MMD register

 * from the PHY. If not supported, return -1. This function is

 * optional for PHY specific drivers, if not provided then the

 * default MMD read function is used by the PHY framework.

 */

int (*read_mmd_indirect)(struct phy_device *dev, int ptrad,

 int devnum, int regnum);

 

/* A function provided by a phy specific driver to override the

 * the PHY driver framework support for writing a MMD register

 * from the PHY. This function is optional for PHY specific drivers,

 * if not provided then the default MMD read function is used by

 * the PHY framework.

 */

void (*write_mmd_indirect)(struct phy_device *dev, int ptrad,

   int devnum, int regnum, u32 val);

 

struct device_driver driver;

};

 

PHY总线结构体-struct mii_bus

/*

 * The Bus class for PHYs.  Devices which provide access to

 * PHYs should register using this structure

 */

struct mii_bus {

const char *name;

char id[MII_BUS_ID_SIZE];

void *priv;

int (*read)(struct mii_bus *bus, int phy_id, int regnum);

int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);

int (*reset)(struct mii_bus *bus);

 

/*

 * A lock to ensure that only one thing can read/write

 * the MDIO bus at a time

 */

struct mutex mdio_lock;

 

struct device *parent;

 

enum {

MDIOBUS_ALLOCATED = 1,

MDIOBUS_REGISTERED,

MDIOBUS_UNREGISTERED,

MDIOBUS_RELEASED,

} state;

struct device dev;

 

/* list of all PHYs on bus ,最多可以挂32个phy*/

struct phy_device *phy_map[PHY_MAX_ADDR];

 

/* PHY addresses to be ignored when probing */

u32 phy_mask;

 

/*

 * Pointer to an array of interrupts, each PHY's

 * interrupt at the index matching its address

 */

int *irq;

};

 

net_device私有数据结构体-struct ravb_private

struct ravb_private {

struct net_device *ndev; //关联的net_device设备

struct vmm_device *dev; //关联的VMM设备

void __iomem *addr; //网络设备io地址空间

struct mdiobb_ctrl mdiobb; //MDIO的ops

u32 num_rx_ring[NUM_RX_QUEUE]; //rx_ring的描述符数包数(packets)<256>

u32 num_tx_ring[NUM_TX_QUEUE]; //tx_ring的描述符数包数(packets)<1024>

u32 desc_bat_size; //描述符基地址表的size

dma_addr_t desc_bat_dma; //描述符基地址表物理地址

struct ravb_desc *desc_bat; //描述符基地址表虚拟地址

dma_addr_t rx_desc_dma[NUM_RX_QUEUE]; //rx_ring的物理地址

dma_addr_t tx_desc_dma[NUM_TX_QUEUE]; //tx_ring的物理地址

struct ravb_ex_rx_desc *rx_ring[NUM_RX_QUEUE]; //rx_ring的虚拟地址

struct ravb_tx_desc *tx_ring[NUM_TX_QUEUE]; //tx_ring的虚拟地址

void *tx_align[NUM_TX_QUEUE]; //GEN2使用

struct sk_buff **rx_skb[NUM_RX_QUEUE]; //接收skb_buffer虚拟地址起始

struct sk_buff **tx_skb[NUM_TX_QUEUE]; //发送skb_buffer虚拟地址起始

u32 rx_over_errors;

u32 rx_fifo_errors;

struct net_device_stats stats[NUM_RX_QUEUE];

u32 tstamp_tx_ctrl;

u32 tstamp_rx_ctrl;

struct list_head ts_skb_list;

u32 ts_skb_tag;

struct ravb_ptp ptp;

spinlock_t lock; /* Register access lock */

 

/*cur和dirty都是单调增加的,具体在ring内的index需要%(ring的长度)*/

u32 cur_rx[NUM_RX_QUEUE]; /* Consumer ring indices */

u32 dirty_rx[NUM_RX_QUEUE]; /* Producer ring indices */

u32 cur_tx[NUM_TX_QUEUE]; /* Consumer ring indices */

u32 dirty_tx[NUM_TX_QUEUE]; /* Producer ring indices */

 

struct napi_struct napi[NUM_RX_QUEUE]; //napi回调函数

struct work_struct work; //超时work处理

 

/* MII transceiver section. */

struct mii_bus *mii_bus; /* MDIO bus control */

int link; //连接(数?)

phy_interface_t phy_interface; //物理接口模式("rgmii-txid")

int msg_enable; /* Debug message level */

int speed;

int duplex;

int emac_irq; //E-MAC中断号

enum ravb_chip_id chip_id; //GEN3

int rx_irqs[NUM_RX_QUEUE]; //接收中断号

int tx_irqs[NUM_TX_QUEUE]; //发送中断号

 

//2bit位域?后面变量会不会受影响?

unsigned no_avb_link:1;

unsigned avb_link_active_low:1;

int num_tx_desc; /* TX descriptors per packet(GEN2=2;GEN3=1)*/

u32 has_phy_reset_gpio;

u32 phy_reset_gpio;

u32 dev_id;

};

 

Probe函数(ravb_probe)

 

1.分配struct net_device空间及其driver私有数据空间(struct ravb_private)

struct net_device * //返回网络设备结构体

alloc_etherdev_mqs( int sizeof_priv, //driver私有数据空间大小

unsigned int txqs, //发送队列数

unsigned int rxqs) //接收队列数

  1. 获取并设置中断号:irq_of_parse_and_map
  2. 映射寄存器空间:vmm_devtree_request_regmap
  3. 设置超时处理work:INIT_WORK(&priv->work, ravb_tx_timeout_work);
  4. 设置phy接口模式:"rgmii-txid"(dts中获取)
  5. 设置ether link:"renesas,no-ether-link"(dts获取)
  6. 获取并设置emac中断号
  7. 设置网络设备ops:ravb_open、ravb_close、ravb_start_xmit;后面详细介绍
  8. 设置config模式:ravb_set_config_mode
  9. 设置GTI:ravb_set_gti
  10. Request GTI loading
  11. 根据物理接口模式设置APSR:ravb_set_delay_mode
  12. 分配DBAT空间(DMA使用,22个表项):dma_alloc_coherent
  13. 初始化DBAT表项中断使能位为DT_EOS并写入DBAT寄存器
  14. 设置MAC地址:ravb_read_mac_address
  15. MDIO bus初始化:ravb_mdio_init
  16. Napi线程回调设置:netif_napi_add(, , ravb_poll, )
  17. 注册此网络设备

 

Ops上层接口(ravb_netdev_ops)

定义如下:

struct net_device_ops {

int (*ndo_init) (struct net_device *ndev);

int (*ndo_open) (struct net_device *ndev);

int (*ndo_stop) (struct net_device *ndev);

int (*ndo_start_xmit) (struct sk_buff *buf, struct net_device *ndev);

int (*ndo_validate_addr)(struct net_device *dev);

void (*ndo_tx_timeout) (struct net_device *dev);

int (*ndo_do_ioctl)(struct net_device *dev,

    struct ifreq *ifr, int cmd);

int (*ndo_change_mtu)(struct net_device *dev, int new_mtu);

};

但只实现了其中的open、stop和xmit函数。见下面分析:

 

ravb_open函数

  1. 设置error和gptp中断回调函数-ravb_multi_interrupt
  2. 设置E-MAC中断回调函数-ravb_emac_interrupt
  3. 设置发送和接收DMA中断回调函数-ravb_be_interrupt(进行中断mask处理后会调用napi_schedule,激活napi线程进行中断下半段处理)
  4. Dmac设备初始化(写各种相关寄存器):ravb_dmac_init
  5. Emac初始化(写各种相关寄存器):ravb_emac_init
  6. Phy初始化、start:ravb_phy_start

 

ravb_start_xmit函数

/* Packet transmit function for Ethernet AVB */

static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)

{

……

if (priv->cur_tx[q] - priv->dirty_tx[q] > (priv->num_tx_ring[q] - 1) *

    num_tx_desc) { //出现咬尾,生产者(dirty)供应不足

……

return NETDEV_TX_BUSY;

}

//设置当前entry

entry = priv->cur_tx[q] % (priv->num_tx_ring[q] * num_tx_desc);

//放入entry对应的sk_buffer

priv->tx_skb[q][entry / num_tx_desc] = skb;

if(0){}else{ //GEN3对应 num_tx_desc=1

desc = &priv->tx_ring[q][entry]; //操作对应描述符

len = skb_len(skb);

dma_addr = dma_map_single(ndev->dev, skb_data(skb), skb_len(skb),

  DMA_TO_DEVICE); //映射skb_data地址到dma空间

if (dma_mapping_error(ndev->dev, dma_addr))

goto drop;

}

desc->ds_tagl = cpu_to_le16(len); //设置描述符长度

desc->dptr = cpu_to_le32(dma_addr); //设置描述符对应dma物理地址

 

/* Descriptor type must be set after all the above writes */

dma_wmb(); //等上述结构体填充完毕,设置DT_FSINGLE标志

desc->die_dt = DT_FSINGLE;

//使能TCCR(传输控制寄存器)的TSRQ0位(Best Effort),DMA开始传输

ravb_modify(ndev, TCCR, TCCR_TSRQ0 << q, TCCR_TSRQ0 << q);

 

priv->cur_tx[q] += num_tx_desc; //消费者index加1

//如果出现咬尾,则执行ravb_tx_free函数释放tx_ring(生产者index增加)

if (priv->cur_tx[q] - priv->dirty_tx[q] >

    (priv->num_tx_ring[q] - 1) * num_tx_desc && !ravb_tx_free(ndev, q))

netif_tx_stop_queue(netdev_get_tx_queue(ndev, q));

}

 

 

ravb_ring_format函数

/* Format skb and descriptor buffer for Ethernet AVB */

static void ravb_ring_format(struct net_device *ndev, int q)

{

……

//消费者ring的index清零(tx和rx)

priv->cur_rx[q] = 0;

priv->cur_tx[q] = 0;

//生产者ring的index清零(tx和rx)

priv->dirty_rx[q] = 0;

priv->dirty_tx[q] = 0;

 

memset(priv->rx_ring[q], 0, rx_ring_size); //接收描述符ring清零

/* Build RX ring buffer */

for (i = 0; i < priv->num_rx_ring[q]; i++) {

/* RX descriptor */

rx_desc = &priv->rx_ring[q][i];

rx_desc->ds_cc = cpu_to_le16(PKT_BUF_SZ); //设置接收描述符长度为1538

/*存储虚拟地址(rx_skb),返回物理地址(dma_addr)*/

dma_addr = dma_map_single(ndev->dev, skb_data(priv->rx_skb[q][i]),

  PKT_BUF_SZ, //skb_buffer长度设置为1538

  DMA_FROM_DEVICE); //分配dma用的skb_buffer

/* We just set the data size to 0 for a failed mapping which

 * should prevent DMA from happening...

 */

if (dma_mapping_error(ndev->dev, dma_addr))

rx_desc->ds_cc = cpu_to_le16(0);

rx_desc->dptr = cpu_to_le32(dma_addr); //设置描述物理地址

rx_desc->die_dt = DT_FEMPTY; //描述符初始化为DT_FEMPTY

}

/*此时i= priv->num_rx_ring[q],是否已经越界了?初始化时多分配了1个*/

rx_desc = &priv->rx_ring[q][i];

rx_desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma[q]); //rx_ring的物理地址

rx_desc->die_dt = DT_LINKFIX; /* type */

 

memset(priv->tx_ring[q], 0, tx_ring_size); //发送描述符ring清零

/* Build TX ring buffer ,如果GEN2则一次初始化2个*/

for (i = 0, tx_desc = priv->tx_ring[q]; i < priv->num_tx_ring[q];

     i++, tx_desc++) {

tx_desc->die_dt = DT_EEMPTY;

if (num_tx_desc >= 2) {

tx_desc++;

tx_desc->die_dt = DT_EEMPTY;

}

}

tx_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma[q]);

tx_desc->die_dt = DT_LINKFIX; /* type */

 

/* RX descriptor base address for best effort */

//设置DBAT表中rx_ring的物理地址(偏移4,因为rensas支持多达4个queue)

desc = &priv->desc_bat[RX_QUEUE_OFFSET + q];

desc->die_dt = DT_LINKFIX; /* type */

desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma[q]);

 

/* TX descriptor base address for best effort */

//设置DBAT表中tx_ring的物理地址(偏移0)

desc = &priv->desc_bat[q];

desc->die_dt = DT_LINKFIX; /* type */

desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma[q]);

}

 

Dmac初始化函数-ravb_dmac_init

/* Device init function for Ethernet AVB */

static int ravb_dmac_init(struct net_device *ndev)

{

……

/* 将CCC(模式寄存器)设置为config模式*/

error = ravb_config(ndev);

……

error = ravb_ring_init(ndev, RAVB_BE); //初始化并分配skb和r(t)x_ring空间

……

/* Descriptor format(做映射) */

ravb_ring_format(ndev, RAVB_BE);

 

/* Set AVB RX(接收配置寄存器RCR) */

ravb_write(ndev, RCR_EFFS | RCR_ENCF | RCR_ETS0 | RCR_ESF | 0x18000000, RCR);

 

/* Set AVB TX(传输配置寄存器TGC) */

ravb_write(ndev, TGC_TQP_AVBMODE1 | 0x00222200, TGC);

 

/* Timestamp enable */

ravb_write(ndev, TCCR_TFEN, TCCR);

 

/* Interrupt init: */

if (priv->chip_id == RCAR_GEN3) {

/* Clear DIL.DPLx */

ravb_write(ndev, 0, DIL);

/* Set queue specific interrupt (使能收、发中断<queue specific>)*/

ravb_write(ndev, CIE_CRIE | CIE_CTIE | CIE_CL0M, CIE);

}

/* Frame receive */

ravb_write(ndev, RIC0_FRE0 | RIC0_FRE1, RIC0);

/* Disable FIFO full warning */

ravb_write(ndev, 0, RIC1);

/* Receive FIFO full error, descriptor empty */

ravb_write(ndev, RIC2_QFE0 | RIC2_QFE1 | RIC2_RFFE, RIC2);

/* Frame transmitted, timestamp FIFO updated */

ravb_write(ndev, TIC_FTE0 | TIC_FTE1, TIC);

 

// Setting the control will start the AVB-DMAC process. 设置运行模式,前面配置将生效

ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_OPERATION);

return 0;

}

 

E-MAC初始化函数-ravb_emac_init

/* E-MAC init function */

static void ravb_emac_init(struct net_device *ndev)

{

……

/* Receive frame limit (设置mtu) */

ravb_write(ndev, ndev->mtu + ETH_HLEN + 4 + ETH_FCS_LEN, RFLR);

 

/* PAUSE prohibition(E-MAC模式寄存器配置) */

ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |

   ECMR_TE | ECMR_RE | ECMR_PRM, ECMR);

ravb_set_rate(ndev); //speed配置

 

/* Set MAC address */

ravb_write(ndev,

   (ndev->dev_addr[0] << 24) | (ndev->dev_addr[1] << 16) |

   (ndev->dev_addr[2] << 8)  | (ndev->dev_addr[3]), MAHR);

ravb_write(ndev,

   (ndev->dev_addr[4] << 8)  | (ndev->dev_addr[5]), MALR);

 

/* E-MAC status register clear */

ravb_write(ndev, ECSR_ICD | ECSR_MPD, ECSR);

 

/* E-MAC interrupt enable register */

ravb_write(ndev, ECSIPR_ICDIP | ECSIPR_MPDIP | ECSIPR_LCHNGIP, ECSIPR);

}

 

PHY初始化-ravb_phy_init

/* PHY init function */

static int ravb_phy_init(struct net_device *ndev)

{

……

priv->link = 0;

priv->speed = 0;

priv->duplex = -1;

 

/* Try connecting to PHY */

pn = of_parse_phandle(np, "phy-handle", 0);

……

phydev = of_phy_connect(ndev, pn, ravb_adjust_link, 0,

priv->phy_interface); //mdio通信原理待充电!?

of_node_put(pn); //何用?

 

if (priv->chip_id == RCAR_GEN3) {

gpio = of_get_named_gpio_flags(np, "phy-gpios", 0, &flags);

if (gpio_is_valid(gpio)) {

err = gpio_request_one(gpio, GPIOF_DIR_IN, "phy-gpios");

if (err == 0 && (phydev->irq == gpio_to_irq(gpio))) {

未处理

}

}

}

}

 

收包函数-ravb_poll

满足NAPI接口?--待充电!?

static int ravb_poll(struct napi_struct *napi, int budget)

{

……

for (;;) {

tis = ravb_read(ndev, TIS); //获取传输中断状态(TIS)

ris0 = ravb_read(ndev, RIS0); //获取接收中断状态(RIS0)

if (!((ris0 & mask) || (tis & mask)))

break;

 

/* Processing RX Descriptor Ring */

if (ris0 & mask) {

/* Clear RX interrupt */

ravb_write(ndev, ~mask, RIS0);

if (ravb_rx(ndev, "a, q)) //调用接收处理函数-ravb_rx,见后分析

goto out;

}

/* Processing TX Descriptor Ring */

if (tis & mask) {

spin_lock(&priv->lock);

 

/* Clear TX interrupt */

ravb_write(ndev, ~mask, TIS);

ravb_tx_free(ndev, q); //生产者增加

……

spin_unlock(&priv->lock);

}

}

 

/* Re-enable RX/TX interrupts */

spin_lock_irqsave(&priv->lock, flags);

ravb_write(ndev, mask, RIE0);

ravb_write(ndev, mask, TIE);

mmiowb();

spin_unlock_irqrestore(&priv->lock, flags);

 

/* Receive error message handling(错误统计) */

priv->rx_over_errors =  priv->stats[RAVB_BE].rx_over_errors;

if (priv->rx_over_errors != ndev->stats.rx_over_errors) {

ndev->stats.rx_over_errors = priv->rx_over_errors;

}

if (priv->rx_fifo_errors != ndev->stats.rx_fifo_errors) {

ndev->stats.rx_fifo_errors = priv->rx_fifo_errors;

}

out:

return budget - quota;

}

 

ravb_rx函数

/* Packet receive function for Ethernet AVB */

static bool ravb_rx(struct net_device *ndev, int *quota, int q)

{

……

desc = &priv->rx_ring[q][entry];

while (desc->die_dt != DT_FEMPTY) { //处理所有有数据的描述符

/* Descriptor type must be checked before all other reads */

dma_rmb();

 

/*异常和状态统计*/

desc_status = desc->msc;

pkt_len = le16_to_cpu(desc->ds_cc) & RX_DS;

if (--boguscnt < 0)

break;

/* We use 0-byte descriptors to mark the DMA mapping errors */

if (!pkt_len)

continue;

if (desc_status & MSC_MC)

stats->multicast++;

if (desc_status & (MSC_CRC | MSC_RFE | MSC_RTSF | MSC_RTLF |

   MSC_CEEF)) {

stats->rx_errors++;

if (desc_status & MSC_CRC)

stats->rx_crc_errors++;

if (desc_status & MSC_RFE)

stats->rx_frame_errors++;

if (desc_status & (MSC_RTLF | MSC_RTSF))

stats->rx_length_errors++;

if (desc_status & MSC_CEEF)

stats->rx_missed_errors++;

} else { //正常分支

u32 get_ts = priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE;

 

//临时skb指针来接管数据

skb = priv->rx_skb[q][entry];

priv->rx_skb[q][entry] = NULL;

dma_unmap_single(ndev->dev, le32_to_cpu(desc->dptr),

 PKT_BUF_SZ,

 DMA_FROM_DEVICE);

get_ts &= (q == RAVB_NC) ?

RAVB_RXTSTAMP_TYPE_V2_L2_EVENT :

~RAVB_RXTSTAMP_TYPE_V2_L2_EVENT;

 

skb_put(skb, pkt_len); //扩展skb buffer

napi_gro_receive(&priv->napi[q], skb); //调用napi通知上层取数

stats->rx_packets++;

stats->rx_bytes += pkt_len;

}

//消费者增加计数

entry = (++priv->cur_rx[q]) % priv->num_rx_ring[q];

desc = &priv->rx_ring[q][entry];

}

 

/* Refill the RX ring buffers. 参照ravb_ring_format函数注释*/

for (; priv->cur_rx[q] - priv->dirty_rx[q] > 0; priv->dirty_rx[q]++) {

entry = priv->dirty_rx[q] % priv->num_rx_ring[q];

desc = &priv->rx_ring[q][entry];

desc->ds_cc = cpu_to_le16(PKT_BUF_SZ);

 

if (!priv->rx_skb[q][entry]) {

skb = __netdev_alloc_skb(ndev,

PKT_BUF_SZ + RAVB_ALIGN - 1, VMM_MBUF_ALLOC_DMA);

if (!skb)

break; /* Better luck next round. */

ravb_set_buffer_align(skb);

dma_addr = dma_map_single(ndev->dev, skb_data(skb),

  le16_to_cpu(desc->ds_cc),

  DMA_FROM_DEVICE);

skb_checksum_none_assert(skb);

/* We just set the data size to 0 for a failed mapping

 * which should prevent DMA  from happening...

 */

if (dma_mapping_error(ndev->dev, dma_addr))

desc->ds_cc = cpu_to_le16(0);

desc->dptr = cpu_to_le32(dma_addr);

priv->rx_skb[q][entry] = skb;

}

/* Descriptor type must be set after all the above writes */

dma_wmb();

desc->die_dt = DT_FEMPTY;

}

*quota -= limit - (++boguscnt);

return boguscnt <= 0;

}

 

网卡中断流程

Rcar-m3网卡中断概述

总共25个中断,可通过DIS, EIS, RISi, TIS, and GIS状态寄存器读取。

中断除ISS and EIS.QFS外,分为4组。

 

Ethernet AVB.ch0

Line0_Rx[0]

Ethernet AVB.ch1

Line0_Rx[1]

Ethernet AVB.ch2

Line0_Rx[2]

Ethernet AVB.ch3

Line0_Rx[3]

Ethernet AVB.ch4

Line0_Rx[4]

Ethernet AVB.ch5

Line0_Rx[5]

Ethernet AVB.ch6

Line0_Rx[6]

Ethernet AVB.ch7

Line0_Rx[7]

Ethernet AVB.ch8

Line0_Rx[8]

Ethernet AVB.ch9

Line0_Rx[9]

Ethernet AVB.ch10

 Line0_Rx[10]

Ethernet AVB.ch11

 Line0_Rx[11]

Ethernet AVB.ch12

 Line0_Rx[12]

Ethernet AVB.ch13

 Line0_Rx[13]

Ethernet AVB.ch14

 Line0_Rx[14]

Ethernet AVB.ch15

 Line0_Rx[15]

Ethernet AVB.ch16

 Line0_Rx[16]

Ethernet AVB.ch17

 Line0_Rx[17]

Ethernet AVB.ch18

 Line0_Tx[0]

Ethernet AVB.ch19

 Line0_Tx[1]

Ethernet AVB.ch20

 Line0_Tx[2]

Ethernet AVB.ch21

 Line0_Tx[3]

Ethernet AVB.ch22

 Line0_DiA | Line1_A | Line2_A

Ethernet AVB.ch23

 Line0_DiB | Line1_B | Line2_B

Ethernet AVB.ch24

 Line 3

 

Group 0 (Data related interrupts)

构成:

RIS0.FRFr:帧接收中断(每存储在URAM中的接收帧触发一次)

RIS1.RWFr:接收警告中断(当未读取的帧数目达到警告级别时触发)

RIS3.RDPFr:接收描述符已处理中断

TIS.FTFt:帧已发送中断(E-MAC用来完成传输)

TIS.TDPFr:描述符已处理中断(发送)

DIS.DPFi:描述符已处理中断(接收和发送)

 

通过读取ISS寄存器, CPU 能获得一个全局的跟数据相关的中断 (ISS.FRM, ISS.RWM, ISS.DPMi, ISS.FTM).

 

Line0_Rx[17:0]

Line0_Tx[3:0]

接收中断总使能:CIE.CRIE

发送中断总使能:CIE.CTIE

 

Group 1 (Error related interrupts)

EIS.TBFF:发送buffer满中断   

EIS.MFFF:Interrupt for E-MAC Status FIFO overwrite

EIS.TFFF:Interrupt for Timestamp FIFO overrun  

EIS.CULF1, EIS.CULF0, EIS.CLLF1, EIS.CLLF0:Interrupts for CBS limits  

EIS.SEF:Interrupt for separation filtering error  

EIS.QEF:Interrupt for queue error (error while processing receive or transmit queue)  

EIS.MTEF, EIS.MREF:Interrupt for transmission error detected by E-MAC  

RIS2.RFFF:接收FIFO满中断

RIS2.QFFr : Receive queue full interrupts (when no sufficient space is in receive queue)

EIS寄存器是所有Error状态的总揽,另外,ISS.EM指示pending error

ESR寄存器:错误状态寄存器

Group 2 (Management related interrupts)

RIS1.RFWF:Rx-FIFO Warning interrupt(Rx-FIFO has reached configured warning level)

TIS.MFWF:E-MAC status FIFO warning interrupt  

TIS.MFUF :E-MAC Status FIFO updated interrupt

TIS.TFWF:Timestamp FIFO warning interrupt  

TIS.TFUF:Timestamp FIFO updated interrupt

GIS.ATCFi:AVTP time captured interrupt  

GIS.PTMFi:AVTP presentation target matched interrupt  

GIS.PTOF:AVTP presentation time overrun interrupt (when external trigger before old value is processed)

GIS.PTCF:AVTP presentation time captured interrupt

ISS寄存器提供总揽(ISS.RFWM, ISS.MFWM, ISS.MFUM,ISS.TFWM, ISS.TFUM, ISS.CGIM)

 

Line 3 (E-MAC interrupt)

通过ISS.MS可以读取E-MAC的错误状态。

 

Rcar-m3网卡中断流程

  1. 在Probe函数中通过读取dts分别获得收、发、错误及EMAC中断号
  2. 在open函数里分别对收(ch0)、发(ch18)、错误(ch22)及EMAC(ch24)中断通过ravb_hook_irq函数设置中断回调钩子
  3. 在ravb_hook_irq函数中通过request_irq最终完成中断号及服务程序的绑定和注册
  4. E-MAC中断回调函数比较简单,主要处理载波失效及LINK改变情况
  5. 错误中断回调函数也比较简单,主要错收发错误的统计
  6. 收、发中断回调则主要屏蔽对应中断,并调用napi线程启动收发处理

MDIO bus初始化-ravb_mdio_init

 

Xvisor和uboot不同点记录

  1. 初始化设置gptp
  2. Uboot在ravb_dmac_init中将收发中断全部关闭
  3. Emac初始化时,uboot写死mtu
  4. Uboot将speed写死为100M
  5. Uboot对rx描述符初始化流程没看懂(--已看懂,后补充图解)
  6. DBAT如何使用rx或tx描述符(uboot和xvisor描述符尺寸不同!)--uboot可以通过DT_LINKFIX来指向下一个;而xvisor貌似是用的另一个扩展协议(描述符头部带size字段--ds_cc,待详细看下)。

 

::解决“轮询方式能正常收发,但中间有部分空帧”问题(实空空空空空空空实):将uboot对接收描述符初始化流程中的“ARCH_DMA_MINALIGN”限制取消,连续存储接收描述符;同步修改接收处理函数对描述符处理。

 

 

Queue Error Flag

This bit indicates that an error has been detected while processing

reception or transmit queue or while processing AVTP FIFO

entries.

Details about the detected error is indicated by ESR.

Section 50.3.2(2) gives an overview of detail error conditions and

the required interaction.

The CPU can only write 0 to this bit.

[Conditions for Changing]

—  This bit is set to 0 when leaving operating mode.

—  This bit is set to 1 when an error condition is detected.

0: No interrupt pending.

1: Interrupt pending.

 

 

 

描述符需要4字节对齐;

 

问题:虚拟机跟物理机ping或者iperf第一次卡顿

 

中断方式,第一次正常接收,第二次之后接收队列满(某处配置有问题)

Descriptors are recommended to be located at addresses H'00 E6A0 0000 to H'00 E6A0 FFFF. //描述符建议放在0xe6a0 0000~0xe6a0 ffff区间内。

 

 类似资料: