下面从imx8 fec mac控制器初始化流程中涉及到的关键代码进行了细致注释后张贴出来,对于如果正在查看该部分的童靴而言具有很大的参考意义。后面会继续将mac接收网络数据帧的详细过程以及tx/rx 软中断以及new api相关的内容进行描述。
1、接口说明:
1.1、fec_probe探测接口函数
fec_probe处理流程:
(1)、调用fec_enet_get_queue_num,获取设备树fsl,num-tx-queues/fsl,num-rx-queues中tx/rx queue的值(设备树配置的值都为3)
(2)、调用alloc_etherdev_mqs(sizeof(struct fec_enet_private) +FEC_STATS_SIZE, num_tx_qs, num_rx_qs),
分配地址空间,注意前面部分为struct net_device,后面部分为struct fec_enet_private+...*/
即前面部分空间为struct net_device(32字节对齐)后面部分为私有数据struct fec_enet_private地址空间,初始化部分内部成员以及dev->_tx和dev->_rx分配对应的tx和rx队列。
(3)、解析"fsl,magic-packet","fsl,rgmii_txc_dly","fsl,rgmii_rxc_dly"属性值
(4)、解析设备树"phy-handle"节点信息,若phy-handle节点不存在但存在fixed-link属性则解析fixed-link属性值如speed等设置fep->phy_node为对应的设备树节点。
解析设备树"phy-mode"属性值,设置fep->phy_interface为对应解析的值。
(5)、时钟初始化等操作
(6)、调用fec_reset_phy进行phy复位,需要设置如下属性:"phy-reset-duration"(单位ms),"phy-reset-gpios","phy-reset-post-delay","phy-reset-active-high"。
(7)、调用fec_enet_init,分配imx8qxp fec私有数据 rx/tx queue空间,分配buffer descriptor空间(dmam_alloc_coherent),获取MAC地址(设备树,efuse中都无效则生成随机单播MAC地址),
设置ndev->netdev_ops = &fec_netdev_ops(网卡的开启,发送,关闭,ioctl等多种接口),设置ndev->ethtool_ops = &fec_enet_ethtool_ops.
注册New API,netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT/*64*/);
调用fec_restart(ndev)初始化MAC相关寄存器。
(8)、解析中断配置,挂接中断处理函数,注意设备树中每个MAC控制器配置了4个中断irq。devm_request_irq(&pdev->dev, irq, fec_enet_interrupt,0, pdev->name, ndev);
(9)、调用fec_enet_mii_init,注册mdio总线读写接口,解析设备树mdio节点,如果解析到mdio则调用of_mdiobus_register,如果设置了“phy-handle”,但是无对应配置,直接报错,否则调用mdiobus_register进行mdio设备的发现。
(10)、注册网络设备,register_netdev(ndev)。
1.2、接口细节
mac操作函数接口
static const struct net_device_ops fec_netdev_ops = {
.ndo_open = fec_enet_open,
.ndo_stop = fec_enet_close,
.ndo_start_xmit = fec_enet_start_xmit,
.ndo_select_queue = fec_enet_select_queue,
.ndo_set_rx_mode = set_multicast_list,
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = fec_timeout,
.ndo_set_mac_address = fec_set_mac_address,
.ndo_do_ioctl = fec_enet_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = fec_poll_controller,
#endif
.ndo_set_features = fec_set_features,
};
net地址空间分配接口alloc_etherdev_mqs:
struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,unsigned int rxqs)
{
return alloc_netdev_mqs(sizeof_priv, "eth%d"/*eth0*/, NET_NAME_UNKNOWN,ether_setup, txqs, rxqs);
}
/**
* alloc_netdev_mqs - allocate network device
* @sizeof_priv: size of private data to allocate space for
* @name: device name format string
* @name_assign_type: origin of device name
* @setup: callback to initialize device
* @txqs: the number of TX subqueues to allocate
* @rxqs: the number of RX subqueues to allocate
*
* Allocates a struct net_device with private data area for driver use
* and performs basic initialization. Also allocates subqueue structs
* for each queue on the device.
*/
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
unsigned char name_assign_type,
void (*setup)(struct net_device *),
unsigned int txqs, unsigned int rxqs)
{
struct net_device *dev;
size_t alloc_size;
struct net_device *p;
BUG_ON(strlen(name) >= sizeof(dev->name));
if (txqs < 1) {
pr_err("alloc_netdev: Unable to allocate device with zero queues\n");
return NULL;
}
#ifdef CONFIG_SYSFS
if (rxqs < 1) {
pr_err("alloc_netdev: Unable to allocate device with zero RX queues\n");
return NULL;
}
#endif
alloc_size = sizeof(struct net_device); //struct net_device的长度
if (sizeof_priv) {
/* ensure 32-byte alignment of private area */
alloc_size = ALIGN(alloc_size, NETDEV_ALIGN/*32*/); /*struct net_device长度进行32字节对齐*/
alloc_size += sizeof_priv; //alloc_size的长度包含两部分,32字节对齐的struct net_device和sizeof_priv长度
}
/* ensure 32-byte alignment of whole construct */
alloc_size += NETDEV_ALIGN - 1; //保证整体alloc_size在 32字节对齐时且有超出部分。
//分配空间,空间内容初始化为0
p = kvzalloc(alloc_size, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
if (!p)
return NULL;
//dev严格32字节对齐
dev = PTR_ALIGN(p, NETDEV_ALIGN); //注意这里对分配空间返回值P进行32字节对齐后赋值给dev,dev是struct net_device结构指针,对齐后的dev地址可能不是kvzalloc分配的地址
dev->padded = (char *)dev - (char *)p;/*填充数据长度*/
dev->pcpu_refcnt = alloc_percpu(int); //动态分配per-CPU变量,返回其地址
if (!dev->pcpu_refcnt)
goto free_dev;
if (dev_addr_init(dev)) //初始化&dev->dev_addrs链表,向其中添加一个全0的mac地址数据项
goto free_pcpu;
dev_mc_init(dev); //Init multicast address list,struct netdev_hw_addr_list mc;调用__hw_addr_init,初始化mc链表
dev_uc_init(dev); //Init unicast address list,struct netdev_hw_addr_list mu;调用__hw_addr_init,初始化mu链表
dev_net_set(dev, &init_net);
dev->gso_max_size = GSO_MAX_SIZE; //65535
dev->gso_max_segs = GSO_MAX_SEGS; //65535
//初始化一大堆链表
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
INIT_LIST_HEAD(&dev->close_list);
INIT_LIST_HEAD(&dev->link_watch_list);
INIT_LIST_HEAD(&dev->adj_list.upper);
INIT_LIST_HEAD(&dev->adj_list.lower);
INIT_LIST_HEAD(&dev->ptype_all);
INIT_LIST_HEAD(&dev->ptype_specific);
#ifdef CONFIG_NET_SCHED
hash_init(dev->qdisc_hash);
#endif
dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
setup(dev); //以太网则调用 ether_setup,设置以太网参数
if (!dev->tx_queue_len) {
dev->priv_flags |= IFF_NO_QUEUE;
dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; //1000
}
dev->num_tx_queues = txqs;
dev->real_num_tx_queues = txqs;
//分配 struct net_device结构所需的tx queue,注意和imx8qxp fec 私有数据所需的queue进行区分。
if (netif_alloc_netdev_queues(dev))
goto free_all;
#ifdef CONFIG_SYSFS
dev->num_rx_queues = rxqs;
dev->real_num_rx_queues = rxqs;
//分配struct net_device结构所需的rx queue,注意和imx8qxp fec 私有数据所需的queue进行区分。
if (netif_alloc_rx_queues(dev))
goto free_all;
#endif
strcpy(dev->name, name);
dev->name_assign_type = name_assign_type;
dev->group = INIT_NETDEV_GROUP;
if (!dev->ethtool_ops)
dev->ethtool_ops = &default_ethtool_ops; //默认ethtool_ops
nf_hook_ingress_init(dev);
return dev; //返回分配的struct net_device空间地址
free_all:
free_netdev(dev);
return NULL;
free_pcpu:
free_percpu(dev->pcpu_refcnt);
free_dev:
netdev_freemem(dev);
return NULL;
}
const struct header_ops eth_header_ops ____cacheline_aligned = {
.create = eth_header,
.parse = eth_header_parse,
.cache = eth_header_cache,
.cache_update = eth_header_cache_update,
};
/**
* ether_setup - setup Ethernet network device
* @dev: network device
*
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
dev->header_ops = ð_header_ops; //结构header_ops包含的回调函数用于创建,分析,重建第2层报头等。
dev->type = ARPHRD_ETHER; //1
dev->hard_header_len = ETH_HLEN; //14;6+6+2
dev->min_header_len = ETH_HLEN; //14
dev->mtu = ETH_DATA_LEN; //1500
dev->min_mtu = ETH_MIN_MTU;//68
dev->max_mtu = ETH_DATA_LEN; //1500
dev->addr_len = ETH_ALEN; //6
dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; //1000
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
dev->priv_flags |= IFF_TX_SKB_SHARING;
/*设置广播地址为全oxFF*/
eth_broadcast_addr(dev->broadcast); //memset(addr, 0xff, ETH_ALEN);设置为全FF
}
struct netdev_queue {
/*
* read-mostly part
*/
struct net_device *dev;
struct Qdisc __rcu *qdisc;
struct Qdisc *qdisc_sleeping;
#ifdef CONFIG_SYSFS
struct kobject kobj;
#endif
#if defined(CONFIG_XPS) && defined(CONFIG_NUMA)
int numa_node;
#endif
unsigned long tx_maxrate;
/*
* Number of TX timeouts for this queue
* (/sys/class/net/DEV/Q/trans_timeout)
*/
unsigned long trans_timeout;
/*
* write-mostly part
*/
spinlock_t _xmit_lock ____cacheline_aligned_in_smp;
int xmit_lock_owner;
/*
* Time (in jiffies) of last Tx
*/
unsigned long trans_start;
unsigned long state;
#ifdef CONFIG_BQL
struct dql dql;
#endif
} ____cacheline_aligned_in_smp;
/* This structure contains an instance of an RX queue. */
struct netdev_rx_queue {
#ifdef CONFIG_RPS
struct rps_map __rcu *rps_map;
struct rps_dev_flow_table __rcu *rps_flow_table;
#endif
struct kobject kobj;
struct net_device *dev;
} ____cacheline_aligned_in_smp;
static int netif_alloc_netdev_queues(struct net_device *dev)
{
unsigned int count = dev->num_tx_queues;
struct netdev_queue *tx;
size_t sz = count * sizeof(*tx);
if (count < 1 || count > 0xffff)
return -EINVAL;
//分配 tx_queue空间
tx = kvzalloc(sz, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
if (!tx)
return -ENOMEM;
dev->_tx = tx; //一个发送队列(netdev_queue对象)数组,由方法netif_alloc_netdev_queues()初始化
netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL);
spin_lock_init(&dev->tx_global_lock); //初始化自旋锁
return 0;
}
static inline void netdev_for_each_tx_queue(struct net_device *dev,
void (*f)(struct net_device *,struct netdev_queue *,void *),
void *arg)
{
unsigned int i;
//对每个tx queue执行 netdev_init_one_queue 初始化操作
for (i = 0; i < dev->num_tx_queues; i++)
f(dev, &dev->_tx[i], arg); //f为netdev_init_one_queue
}
static int netif_alloc_rx_queues(struct net_device *dev)
{
unsigned int i, count = dev->num_rx_queues;
struct netdev_rx_queue *rx;
size_t sz = count * sizeof(*rx);
BUG_ON(count < 1);
rx = kvzalloc(sz, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
if (!rx)
return -ENOMEM;
dev->_rx = rx; //一个接收队列(netdev_rx_queue对象)数组,由方法netif_rx_queues()初始化。
for (i = 0; i < count; i++)
rx[i].dev = dev;
return 0;
}
/**
*netdev_priv - access network device private data
*@dev: network device
*
* Get network device private data
*/
static inline void *netdev_priv(const struct net_device *dev)
{
return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN); //跳过struct net_device后获取私有数据空间
}
static int fec_enet_alloc_queue(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int i;
int ret = 0;
struct fec_enet_priv_tx_q *txq;
//初始化私有数据struct fec_enet_private 内部的 tx_queue
for (i = 0; i < fep->num_tx_queues/*3*/; i++) {
txq = kzalloc(sizeof(*txq), GFP_KERNEL); //分配struct fec_enet_priv_tx_q的空间
if (!txq) {
ret = -ENOMEM;
goto alloc_failed;
}
fep->tx_queue[i] = txq;
txq->bd.ring_size = TX_RING_SIZE;//512,buffer descriptor 的个数
fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size;
txq->tx_stop_threshold = FEC_MAX_SKB_DESCS;//217
txq->tx_wake_threshold = (txq->bd.ring_size - txq->tx_stop_threshold) / 2; //(512-217)/2=147
//dma一致性空间分配(tso header大小的空间),发送时才会使用到tso,所以接收没有分配该空间
//这里使用dma_alloc_coherent,应该是为了避开cache的缓存
txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev,
txq->bd.ring_size * TSO_HEADER_SIZE/*128*/,
&txq->tso_hdrs_dma,
GFP_KERNEL);
if (!txq->tso_hdrs) {
ret = -ENOMEM;
goto alloc_failed;
}
}
//初始化私有数据struct fec_enet_private 内部的 rx_queue
for (i = 0; i < fep->num_rx_queues;/*3*/ i++) {
fep->rx_queue[i] = kzalloc(sizeof(*fep->rx_queue[i]),GFP_KERNEL);
if (!fep->rx_queue[i]) {
ret = -ENOMEM;
goto alloc_failed;
}
fep->rx_queue[i]->bd.ring_size = RX_RING_SIZE; //512
fep->total_rx_ring_size += fep->rx_queue[i]->bd.ring_size;
}
return ret;
alloc_failed:
fec_enet_free_queue(ndev);
return ret;
}
//fec 网卡tx/rx queue数据初始化以及开启fec MAC
static int fec_enet_init(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev); //私有数据
struct bufdesc *cbd_base;
dma_addr_t bd_dma;
int bd_size;
unsigned int i;
unsigned dsize = fep->bufdesc_ex ? sizeof(struct bufdesc_ex) :sizeof(struct bufdesc); //使用增强描述符还是老旧描述符
unsigned dsize_log2 = __fls(dsize);
WARN_ON(dsize != (1 << dsize_log2));
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
fep->rx_align = 0xf; //0xf对齐
fep->tx_align = 0xf;
#else
fep->rx_align = 0x3;
fep->tx_align = 0x3;
#endif
//分配接收/发送 queue
fec_enet_alloc_queue(ndev);
bd_size = (fep->total_tx_ring_size + fep->total_rx_ring_size) * dsize; //rx/tx buffer descripotr 描述符总空间大小
printk("%s,dsize:%d,dsize_log2:%d,bd_size:%d \n",__FUNCTION__,dsize,dsize_log2,bd_size);
/* Allocate memory for buffer descriptors. */
//分配整个buffer descriptor空间,注意内存空间是连续的,第一个接收/发送buffer descriptor的地址存放在Receive/Transmit buffer descriptor Ring 0/1/2 Start register寄存器中,
//MAC控制器接收/发送数据时依次去索引buffer descriptor进行接收数据的保存和发送数据的获取。
//最后一个buffer descriptor会设置其为last buffer descriptor的标志(bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); ),索引到最后一个buffer descriptor时,
//再从Receive/Transmit buffer descriptor Ring 0/1/2 Start register寄存器中,获取新一轮的第一个接收/发送buffer descriptor的地址进行数据的发送和接收。
cbd_base = dmam_alloc_coherent(&fep->pdev->dev, bd_size, &bd_dma,GFP_KERNEL);
if (!cbd_base) {
return -ENOMEM;
}
memset(cbd_base, 0, bd_size); //清空buffer descriptor 空间
/* Get the Ethernet address */
fec_get_mac(ndev); //获取MAC地址
/* make sure MAC we just acquired is programmed into the hw */
fec_set_mac_address(ndev, NULL); //将MAC地址写入寄存器
/* Set receive and transmit descriptor base. */
for (i = 0; i < fep->num_rx_queues; i++) {
struct fec_enet_priv_rx_q *rxq = fep->rx_queue[i];
unsigned size = dsize * rxq->bd.ring_size; //每个rx queue中 buffer descripotr的大小
//将上面分配的整个buffer descriptor空间进行拆分, 细化赋值给每个queue
rxq->bd.qid = i; //队列id
rxq->bd.base = cbd_base; //描述符地址
rxq->bd.cur = cbd_base; //当前buffer descripotr对应虚拟地址
rxq->bd.dma = bd_dma; //当前buffer descripotr对应dma地址
rxq->bd.dsize = dsize; //buffer descripotr的大小
rxq->bd.dsize_log2 = dsize_log2;
rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; //寄存器地址,+0x10
bd_dma += size; //偏移处理
cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
}
for (i = 0; i < fep->num_tx_queues; i++) {
struct fec_enet_priv_tx_q *txq = fep->tx_queue[i];
unsigned size = dsize * txq->bd.ring_size;
//将上面分配的整个buffer descriptor空间进行拆分, 细化赋值给每个queue
txq->bd.qid = i; //队列id
txq->bd.base = cbd_base; //描述符地址
txq->bd.cur = cbd_base; //当前buffer descripotr对应虚拟地址
txq->bd.dma = bd_dma; //当前buffer descripotr对应dma地址
txq->bd.dsize = dsize; //buffer descripotr的大小
txq->bd.dsize_log2 = dsize_log2;
txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i];
bd_dma += size; //偏移处理
cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
}
/* The FEC Ethernet specific entries in the device structure */
ndev->watchdog_timeo = TX_TIMEOUT; //2S
ndev->netdev_ops = &fec_netdev_ops; //非常重要关键的赋值,mac控制器的操作函数指针
ndev->ethtool_ops = &fec_enet_ethtool_ops; //ethtool操作函数指针
writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);/*关闭接收中断mask*/
//New API
netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi/*poll函数*/, NAPI_POLL_WEIGHT/*64*/);
if (fep->quirks & FEC_QUIRK_HAS_VLAN)
/* enable hw VLAN support */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX;
if (fep->quirks & FEC_QUIRK_HAS_CSUM) {
ndev->gso_max_segs = FEC_MAX_TSO_SEGS;/*100*/ /* Max number of allowed TCP segments for software TSO */
/* enable hw accelerator */
ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO);
fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
}
if (fep->quirks & FEC_QUIRK_HAS_AVB) {
fep->tx_align = 0;
fep->rx_align = 0x3f;
}
ndev->hw_features = ndev->features;
fec_restart(ndev); //重启网卡
if (fep->quirks & FEC_QUIRK_MIB_CLEAR)
fec_enet_clear_ethtool_stats(ndev);
else
fec_enet_update_ethtool_stats(ndev);
return 0;
}
/*
* This function is called to start or restart the FEC during a link
* change, transmit timeout, or to reconfigure the FEC. The network
* packet processing for this device must be stopped before this call.
*/
static void fec_restart(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
u32 val;
u32 temp_mac[2];
u32 rcntl = OPT_FRAME_SIZE | 0x04;
u32 ecntl = FEC_ENET_ETHEREN; /* ETHEREN */
/* Whack a reset. We should wait for this.
* For i.MX6SX SOC, enet use AXI bus, we use disable MAC
* instead of reset MAC itself.
*/
if (fep->quirks & FEC_QUIRK_HAS_AVB) {
writel(0, fep->hwp + FEC_ECNTRL);
} else {
writel(1, fep->hwp + FEC_ECNTRL/*0x24*/); //reset MAC,关闭mac控制器
udelay(10);
}
/*
* enet-mac reset will reset mac address registers too,
* so need to reconfigure it.
*/
//写入MAC地址到对一个寄存器
memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
writel((__force u32)cpu_to_be32(temp_mac[0]), /*0x0e4*/
fep->hwp + FEC_ADDR_LOW);
writel((__force u32)cpu_to_be32(temp_mac[1]), /*0x0e8*/
fep->hwp + FEC_ADDR_HIGH);
/* Clear any outstanding interrupt. */
writel(0xffffffff, fep->hwp + FEC_IEVENT);//清除所有的中断事件(event),w1c
//初始化 rx/tx/ queue内部的每个buffer descriptor
fec_enet_bd_init(ndev);
//将rx/tx queue内部buffer descriptor的起始地址(DRR 物理地址)赋值给对应ring 0/1/2的 Receive/Transmit descriptor 寄存器
fec_enet_enable_ring(ndev);
/* Reset tx SKB buffers. */
//释放tx queue中的所有tx_skbuff[]
fec_enet_reset_skb(ndev);
/* Enable MII mode */
if (fep->full_duplex == DUPLEX_FULL) {
/* FD enable */
writel(0x04, fep->hwp + FEC_X_CNTRL); //Full-Duplex Enable
} else {
/* No Rcv on Xmit */
rcntl |= 0x02;
writel(0x0, fep->hwp + FEC_X_CNTRL);
}
/* Set MII speed */
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
#if !defined(CONFIG_M5272)
if (fep->quirks & FEC_QUIRK_HAS_RACC) {
val = readl(fep->hwp + FEC_RACC);
/* align IP header */
val |= FEC_RACC_SHIFT16;
if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
/* set RX checksum */
val |= FEC_RACC_OPTIONS;
else
val &= ~FEC_RACC_OPTIONS;
writel(val, fep->hwp + FEC_RACC);
writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL);
}
writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_FTRL);
#endif
/*
* The phy interface and speed need to get configured
* differently on enet-mac.
*/
if (fep->quirks & FEC_QUIRK_ENET_MAC) {
/* Enable flow control and length check */
rcntl |= 0x40000000 | 0x00000020;
/* RGMII, RMII or MII */
if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII ||
fep->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
fep->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
fep->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID)
rcntl |= (1 << 6);
else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
rcntl |= (1 << 8);
else
rcntl &= ~(1 << 8);
/* 1G, 100M or 10M */
if (ndev->phydev) {
if (ndev->phydev->speed == SPEED_1000)
ecntl |= (1 << 5);
else if (ndev->phydev->speed == SPEED_100)
rcntl &= ~(1 << 9);
else
rcntl |= (1 << 9);
}
} else {
#ifdef FEC_MIIGSK_ENR
if (fep->quirks & FEC_QUIRK_USE_GASKET) {
u32 cfgr;
/* disable the gasket and wait */
writel(0, fep->hwp + FEC_MIIGSK_ENR);
while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
udelay(1);
/*
* configure the gasket:
* RMII, 50 MHz, no loopback, no echo
* MII, 25 MHz, no loopback, no echo
*/
cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII;
if (ndev->phydev && ndev->phydev->speed == SPEED_10)
cfgr |= BM_MIIGSK_CFGR_FRCONT_10M;
writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR);
/* re-enable the gasket */
writel(2, fep->hwp + FEC_MIIGSK_ENR);
}
#endif
}
#if !defined(CONFIG_M5272)
/* enable pause frame*/
if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) ||
((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) &&
ndev->phydev && ndev->phydev->pause)) {
rcntl |= FEC_ENET_FCE;
/* set FIFO threshold parameter to reduce overrun */
writel(FEC_ENET_RSEM_V, fep->hwp + FEC_R_FIFO_RSEM);
writel(FEC_ENET_RSFL_V, fep->hwp + FEC_R_FIFO_RSFL);
writel(FEC_ENET_RAEM_V, fep->hwp + FEC_R_FIFO_RAEM);
writel(FEC_ENET_RAFL_V, fep->hwp + FEC_R_FIFO_RAFL);
/* OPD */
writel(FEC_ENET_OPD_V, fep->hwp + FEC_OPD);
} else {
rcntl &= ~FEC_ENET_FCE;
}
#endif /* !defined(CONFIG_M5272) */
writel(rcntl, fep->hwp + FEC_R_CNTRL); //#define FEC_R_CNTRL 0x084 /* Receive control reg */
/* Setup multicast filter. */
set_multicast_list(ndev);
#ifndef CONFIG_M5272
writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
#endif
if (fep->quirks & FEC_QUIRK_ENET_MAC) {
/* enable ENET endian swap */
ecntl |= (1 << 8);
/* enable ENET store and forward mode */
writel(1 << 8, fep->hwp + FEC_X_WMRK);
}
if (fep->bufdesc_ex)
ecntl |= (1 << 4);
if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT &&
fep->rgmii_txc_dly)
ecntl |= FEC_ENET_TXC_DLY;
if (fep->quirks & FEC_QUIRK_DELAYED_CLKS_SUPPORT &&
fep->rgmii_rxc_dly)
ecntl |= FEC_ENET_RXC_DLY;
#ifndef CONFIG_M5272
/* Enable the MIB statistic event counters */
writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT);
#endif
/* And last, enable the transmit and receive processing */
writel(ecntl, fep->hwp + FEC_ECNTRL); //#define FEC_ECNTRL 0x024 /* Ethernet control reg */
fec_enet_active_rxring(ndev);
if (fep->bufdesc_ex)
fec_ptp_start_cyclecounter(ndev);
/* Enable interrupts we wish to service */
if (fep->link)
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
else
writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
/* Init the interrupt coalescing */
//初始化中断合并
fec_enet_itr_coal_init(ndev);
}
#define BD_SC_WRAP (0x2000) /* Last buffer descriptor */
/* Init RX & TX buffer descriptors */
static void fec_enet_bd_init(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
struct fec_enet_priv_tx_q *txq;
struct fec_enet_priv_rx_q *rxq;
struct bufdesc *bdp;
unsigned int i;
unsigned int q;
for (q = 0; q < fep->num_rx_queues; q++) {
/* Initialize the receive buffer descriptors. */
rxq = fep->rx_queue[q]; //私有数据 struct fec_enet_private 中申请的接收队列
bdp = rxq->bd.base; //描述符地址
for (i = 0; i < rxq->bd.ring_size/*512*/; i++) { //这里的ring_size的单位成员就是buffder discriptor 描述符(bd)
/* Initialize the BD for every fragment in the page. */
if (bdp->cbd_bufaddr) //因为fec_start可能多次调用,cbd_bufaddr可能存在
bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); //0x8000,E标志置位,表明描述符为空
else
bdp->cbd_sc = cpu_to_fec16(0); //0
bdp = fec_enet_get_nextdesc(bdp, &rxq->bd); //bdp指向下一个描述符
}
/* Set the last buffer to wrap */
bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
//最后一个buffer descriptor 设置其sc值为wrap,很重要,不然导致错误,后面代码中会设置 Receive descriptor ring/1/2描述符的起始地址
bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
rxq->bd.cur = rxq->bd.base;
}
for (q = 0; q < fep->num_tx_queues; q++) {
/* ...and the same for transmit */
txq = fep->tx_queue[q];
bdp = txq->bd.base;
txq->bd.cur = bdp;
for (i = 0; i < txq->bd.ring_size; i++) {
/* Initialize the BD for every fragment in the page. */
bdp->cbd_sc = cpu_to_fec16(0);
if (bdp->cbd_bufaddr &&
!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
dma_unmap_single(&fep->pdev->dev, //释放地址空间,因为fec_start可能多次调用,这里应该是做释放处理
fec32_to_cpu(bdp->cbd_bufaddr),
fec16_to_cpu(bdp->cbd_datlen),
DMA_TO_DEVICE);
if (txq->tx_skbuff[i]) { //释放tx_skbuff[]数组内的地址
dev_kfree_skb_any(txq->tx_skbuff[i]);
txq->tx_skbuff[i] = NULL;
}
bdp->cbd_bufaddr = cpu_to_fec32(0);
bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
}
/* Set the last buffer to wrap */
bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);//后面代码中会设置 Transmit descriptor ring/1/2描述符的起始地址
txq->dirty_tx = bdp; //dirty_tx指向最后一个buffer descriptor
}
}
上面代码完成buffer descriptor缓存区描述符的初始化,但是buffer descriptor中的Buffer address即用于存放数据的内存空间在何处分配?
在fec_enet_open(..)中为rx buffer descriptor分配对应的数据接收缓冲区,为tx buffer descriptor 进行设置。
static int fec_enet_alloc_buffers(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
unsigned int i;
for (i = 0; i < fep->num_rx_queues;/*3*/ i++)
if (fec_enet_alloc_rxq_buffers(ndev, i))
return -ENOMEM;
for (i = 0; i < fep->num_tx_queues;/*3*/ i++)
if (fec_enet_alloc_txq_buffers(ndev, i))
return -ENOMEM;
return 0;
}
注意分配struct sk_buff+数据缓存区+struct skb_shared_info空间,其中sk_buff数据缓存区大小为2048字节(所有接收的skb数据缓冲区都是2048字节)
static int fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
{
struct fec_enet_private *fep = netdev_priv(ndev);
unsigned int i;
struct sk_buff *skb;
struct bufdesc *bdp;
struct fec_enet_priv_rx_q *rxq;
rxq = fep->rx_queue[queue];
bdp = rxq->bd.base;
//对单个rx queue内部512个buffer descriptor分配地址空间以及用于将数据帧传递到上层的struct skb_buff空间
for (i = 0; i < rxq->bd.ring_size;/*512*/ i++) {
//分配struct sk_buff+数据缓存区+struct skb_shared_info空间,其中sk_buff数据缓存区大小为2048字节
skb = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE/*2048*/);
if (!skb)
goto err_alloc;
//直接将sk_buff中数据缓冲区的sk_buff->data的值设置为buffer descriptor中存放数据的cdb_bufaddr,这样避免了数据的拷贝
if (fec_enet_new_rxbdp(ndev, bdp, skb)) {
dev_kfree_skb(skb);
goto err_alloc;
}
rxq->rx_skbuff[i] = skb; //很关键,这样rx queue中的所有rx_skbuff都已分配空间
bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY); //buffer descriptor状态设置为empty
if (fep->bufdesc_ex) {
struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
ebdp->cbd_esc = cpu_to_fec32(BD_ENET_RX_INT); //产生RXB/RXFn中断
}
bdp = fec_enet_get_nextdesc(bdp, &rxq->bd);
}
/* Set the last buffer to wrap. */
bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); //The next buffer descriptor is found at the location defined in ENETn_RDSR.,
return 0;
err_alloc:
fec_enet_free_buffers(ndev);
return -ENOMEM;
}
netdev_alloc_skb:见结构sk_buff和net_device章节中关于struct sk_buffer的分配
static int fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int off;
off = ((unsigned long)skb->data) & fep->rx_align; //做对齐处理
if (off)
skb_reserve(skb, fep->rx_align + 1 - off);
//直接将sk_buff中数据缓冲区的sk_buff->data的值设置为buffer descriptor中存放数据的cdb_bufaddr,这样避免了数据的拷贝
//这里使用dma_map_single(...)进行流式DMA映射
bdp->cbd_bufaddr = cpu_to_fec32(dma_map_single(&fep->pdev->dev, skb->data, FEC_ENET_RX_FRSIZE - fep->rx_align, DMA_FROM_DEVICE));
if (dma_mapping_error(&fep->pdev->dev, fec32_to_cpu(bdp->cbd_bufaddr))) {
if (net_ratelimit())
netdev_err(ndev, "Rx DMA memory map failed\n");
return -ENOMEM;
}
return 0;
}
static int fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue)
{
struct fec_enet_private *fep = netdev_priv(ndev);
unsigned int i;
struct bufdesc *bdp;
struct fec_enet_priv_tx_q *txq;
txq = fep->tx_queue[queue];
bdp = txq->bd.base;
for (i = 0; i < txq->bd.ring_size; i++) {
txq->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE/*2048*/, GFP_KERNEL); //tx_bounce用于存放待发送的网络数据帧
if (!txq->tx_bounce[i])
goto err_alloc;
bdp->cbd_sc = cpu_to_fec16(0);
bdp->cbd_bufaddr = cpu_to_fec32(0);
if (fep->bufdesc_ex) {
struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp;
ebdp->cbd_esc = cpu_to_fec32(BD_ENET_TX_INT); //产生中断
}
bdp = fec_enet_get_nextdesc(bdp, &txq->bd);
}
/* Set the last buffer to wrap. */
bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP); //The next buffer descriptor is found at the location defined in ETDSR.
return 0;
err_alloc:
fec_enet_free_buffers(ndev);
return -ENOMEM;
}
2、mii总线初始化
//进行mdio_bus的注册以及mdio总线下phy/switch设备的注册
static int fec_enet_mii_init(struct platform_device *pdev)
{
static struct mii_bus *fec0_mii_bus;
static int *fec_mii_bus_share;
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *node;
int err = -ENXIO;
u32 mii_speed, holdtime;
/*
* The i.MX28 dual fec interfaces are not equal.
* Here are the differences:
*
* - fec0 supports MII & RMII modes while fec1 only supports RMII
* - fec0 acts as the 1588 time master while fec1 is slave
* - external phys can only be configured by fec0
*
* That is to say fec1 can not work independently. It only works
* when fec0 is working. The reason behind this design is that the
* second interface is added primarily for Switch mode.
*
* Because of the last point above, both phys are attached on fec0
* mdio interface in board design, and need to be configured by
* fec0 mii_bus.
*/
if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) {
/* fec1 uses fec0 mii_bus */
if (mii_cnt && fec0_mii_bus) {
fep->mii_bus = fec0_mii_bus;
*fec_mii_bus_share = FEC0_MII_BUS_SHARE_TRUE;
mii_cnt++;
return 0;
}
return -ENOENT;
}
fep->mii_timeout = 0;
/*
* Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
*
* The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
* for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
* Reference Manual has an error on this, and gets fixed on i.MX6Q
* document.
*/
mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
if (fep->quirks & FEC_QUIRK_ENET_MAC)
mii_speed--;
if (mii_speed > 63) {
dev_err(&pdev->dev,
"fec clock (%lu) too fast to get right mii speed\n",
clk_get_rate(fep->clk_ipg));
err = -EINVAL;
goto err_out;
}
/*
* The i.MX28 and i.MX6 types have another filed in the MSCR (aka
* MII_SPEED) register that defines the MDIO output hold time. Earlier
* versions are RAZ there, so just ignore the difference and write the
* register always.
* The minimal hold time according to IEE802.3 (clause 22) is 10 ns.
* HOLDTIME + 1 is the number of clk cycles the fec is holding the
* output.
* The HOLDTIME bitfield takes values between 0 and 7 (inclusive).
* Given that ceil(clkrate / 5000000) <= 64, the calculation for
* holdtime cannot result in a value greater than 3.
*/
holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1;
fep->phy_speed = mii_speed << 1 | holdtime << 8;
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); //mdc时钟设置
//分配mdio_bus空间,
fep->mii_bus = mdiobus_alloc(); //mdiobus_alloc_size(0); //不分配私有数据空间
if (fep->mii_bus == NULL) {
err = -ENOMEM;
goto err_out;
}
fep->mii_bus->name = "fec_enet_mii_bus";
//只有完成mii_bus->read/write赋值后才能进行mdio读写操作,进行phy初始化
fep->mii_bus->read = fec_enet_mdio_read; //mdio read函数
fep->mii_bus->write = fec_enet_mdio_write;//mdio write函数
snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",pdev->name, fep->dev_id + 1);
fep->mii_bus->priv = fep; ///struct fec_enet_private
fep->mii_bus->parent = &pdev->dev;
node = of_get_child_by_name(pdev->dev.of_node, "mdio"); //解析mdio节点
if (node) {
err = of_mdiobus_register(fep->mii_bus, node); //设备树中解析到"mdio"节点后调用of_mdiobus_register,注册mdio设备
of_node_put(node);
} else if (fep->phy_node && !fep->fixed_link) {//设置了“phy-handle”值但是设备树中无“mdio”配置情况且也无fixed-link相关配置,则直接报错(“phy-handle”设置的节点必须包含在"mdio"节点下)
err = -EPROBE_DEFER;
} else { //没有在设备树中解析到mdio节点,且要么没有设置“phy-handle”值,要么设置了fixed-link相关配置
err = mdiobus_register(fep->mii_bus);
}
if (err)
goto err_out_free_mdiobus;
mii_cnt++;
/* save fec0 mii_bus */
if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) {
fec0_mii_bus = fep->mii_bus;
fec_mii_bus_share = &fep->mii_bus_share;
}
return 0;
err_out_free_mdiobus:
mdiobus_free(fep->mii_bus);
err_out:
return err;
mii_cnt++;
/* save fec0 mii_bus */
if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) {
fec0_mii_bus = fep->mii_bus;
fec_mii_bus_share = &fep->mii_bus_share;
}
return 0;
err_out_free_mdiobus:
mdiobus_free(fep->mii_bus);
err_out:
return err;
}