imx8 fec mac驱动流程

下面从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 = &eth_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;

}

  • 24
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值