大家好,又见面了,我是你们的朋友全栈君。
说是网络,其实是网卡驱动。而且是针对于FREESCALE芯片的FEC端的驱动,我不知道别的芯片厂商的FEC模块是怎么样的, 但就我接触过的几款FREESCALE的芯片来看,比如基于POWERPC的860T和ARM系列的MX27等,他们的FEC有一个明显的特点就是 都是由BD和一个DMA缓冲组成,而这个DMA是专用的,也就是只是给FEC使用,区别于芯片内的DMAC模块。我们先来从fec.c这 个与硬件直接相关的看起: 首先找到module_init(fec_enet_module_init);这里fec_enet_module_init为入口点 fec_enet_module_init() 首先调用fec_arch_init,它调用gpio_fec_active设置GPIO为FEC模式,然后如果有电源管理的话,就调用 mxc_fec_power_on开启电源。接着调用clk_get,clk_enable, clk_put设置FEC的CLOCK,这里退出fec_arch_init函数,接 着循环FEC_MAX_PORTS次,也就是有几个FEC就循环几次,在这里我们只有一个FEC,所以这个循环不用管。接下来因为我们 用的是以太网,所以调用dev = alloc_etherdev(sizeof(struct fec_enet_private));申请一个以太网设备描述,其中 struct fec_enet_private是用来描述FEC专有的数据结构。如下: /* The FEC buffer descriptors track the ring buffers. The rx_bd_base and * tx_bd_base always point to the base of the buffer descriptors. The * cur_rx and cur_tx point to the currently available buffer. * The dirty_tx tracks the current buffer that is being sent by the * controller. The cur_tx and dirty_tx are equal under both completely * empty and completely full conditions. The empty/ready indicator in * the buffer descriptor determines the actual condition. */ struct fec_enet_private { /* Hardware registers of the FEC device */ volatile fec_t *hwp; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ unsigned char *tx_bounce[TX_RING_SIZE]; struct sk_buff* tx_skbuff[TX_RING_SIZE]; struct sk_buff* rx_skbuff[RX_RING_SIZE]; ushort skb_cur; ushort skb_dirty; /* CPM dual port RAM relative addresses. */ void * cbd_mem_base; /* save the virtual base address of rx&tx buffer descripter */ cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ cbd_t *tx_bd_base; cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ struct net_device_stats stats; uint tx_full; spinlock_t lock; uint phy_id; uint phy_id_done; uint phy_status; uint phy_speed; phy_info_t const *phy; struct work_struct phy_task; uint sequence_done; uint mii_phy_task_queued; uint phy_addr; int index; int opened; int link; int old_link; int full_duplex; struct clk *clk; }; 调用完这个函数后我们就有eth0这人设备了,其中一些结构的初始化是在这个函数里的一个回调ether_setup里设 置的,在这里我也把它帖出来吧。 /** * 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->change_mtu = eth_change_mtu; dev->hard_header = eth_header; dev->rebuild_header = eth_rebuild_header; dev->set_mac_address = eth_mac_addr; dev->hard_header_cache = eth_header_cache; dev->header_cache_update= eth_header_cache_update; dev->hard_header_parse = eth_header_parse; dev->type = ARPHRD_ETHER; dev->hard_header_len = ETH_HLEN; dev->mtu = ETH_DATA_LEN; dev->addr_len = ETH_ALEN; dev->tx_queue_len = 1000; /* Ethernet wants good queues */ dev->flags = IFF_BROADCAST|IFF_MULTICAST; memset(dev->broadcast, 0xFF, ETH_ALEN); } alloc_etherdev完了之后,调用fec_enet_init函数,这里才是真正芯片级的代码: fec_enet_init() 上锁spin_lock_init(&(fep->lock)),调用__get_free_page申请一页的内存,这个返回的地址为虚拟地 址,然后将这个地址值赋给fep->cbc_mem_base,然后将fecp和fep->hwp指向FEC硬件寄存器,fecp->fec_ecntrl = 1,复位 FEC模块,等待10us后调用fec_get_mac从FLASH中读取MAC地址,调用cbd_base = (cbd_t *)fec_map_uncache(mem_addr, PAGE_SIZE);将刚才申请的一页进行映射(mem_addr不是虚拟地址么?为什么这里还要映射??)作为BD的初始地址并将FEC 的rx_bd_base指向该地址,同时将tx_bd_base指向RX_RING_SIZE之后的地址,这个RX_RING_SIZE定义如下: /* The number of Tx and Rx buffers. These are allocated from the page * pool. The code may assume these are power of two, so it it best * to keep them that size. * We don’t need to allocate pages for the transmitter. We just use * the skbuffer directly. */ #define FEC_ENET_RX_PAGES 8 #define FEC_ENET_RX_FRSIZE 2048 #define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE) #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) #define FEC_ENET_TX_FRSIZE 2048 #define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) #define TX_RING_SIZE 16 /* Must be power of two */ #define TX_RING_MOD_MASK 15 /* for this to work */ #if (((RX_RING_SIZE TX_RING_SIZE) * 8) > PAGE_SIZE) #error “FEC: descriptor ring size constants too large” #endif 其中PAGE_SIZE是为4096,所以这个RX_RING_SIZE的值也为16,意思好像是说有16个FEC_ENET_RX_FRSIZE? 接下来循环初始化RX_RING_SIZE个frame,循环中,调用dev_alloc_skb(FEC_ENET_RX_FRSIZE);初始化skb,并将赋给fep- >rx_skbuff[i],接下来将pskb->data进行对齐,这个对齐要求是通过 #define FEC_ADDR_ALIGNMENT(x) ((unsigned char *)(((unsigned long )(x) (FEC_ALIGNMENT)) & (~FEC_ALIGNMENT))) 来实现的,也就是说这个对齐要求是由datasheet里的FEC模块规定的。注意的是最后将这个地址进行物理 地址变换后赋给cbd_bufaddr的,即cbd_bufaddr = __pa(pskb->data);然后将BD的cbd_sc状态描述置为BD_ENET_RX_EMPTY空 闲。 接下来将最后一个BD的cbd_sc状态描述置上BD_SC_WRAP标志,这估计是datasheet里规定的吧??接下来 初始化发送BD,这里看不懂,先帖上来先 bdp = fep->tx_bd_base; for (i=0, j=FEC_ENET_TX_FRPPG; i<TX_RING_SIZE; i ) { if (j >= FEC_ENET_TX_FRPPG) { mem_addr = __get_free_page(GFP_KERNEL); j = 1; } else { mem_addr = FEC_ENET_TX_FRSIZE; j ; } fep->tx_bounce[i] = (unsigned char *) mem_addr; /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; bdp ; } /* Set the last buffer to wrap. */ bdp–; bdp->cbd_sc |= BD_SC_WRAP; 接着调用 /* Set receive and transmit descriptor base. */ fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base)); fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base RX_RING_SIZE*sizeof (cbd_t))); 将发送同接收地址值写入寄存器中。下面调用fec_request_intrs申请两个中断,一个为FEC的中 断static irqreturn_t fec_enet_interrupt(int irq, void * dev_id),用作发送接收、MII及出错等中断使用,另一个为 PHY端的中断static irqreturn_t mii_link_interrupt(int irq, void * dev_id),用作PHY芯片检测LINK DOWN或AUTO- NEGO DONE中断,下面 /* Clear and enable interrupts */ fecp->fec_ievent = FEC_ENET_MASK; fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII; fecp->fec_hash_table_high = 0; fecp->fec_hash_table_low = 0; fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; fecp->fec_ecntrl = 2; fecp->fec_r_des_active = 0x01000000; 都是设置硬件寄存器,接着设置 /* The FEC Ethernet specific entries in the device structure. */ dev->open = fec_enet_open; dev->hard_start_xmit = fec_enet_start_xmit; dev->tx_timeout = fec_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->stop = fec_enet_close; dev->get_stats = fec_enet_get_stats; dev->set_multicast_list = set_multicast_list; 这里是重载DEV的一些实现,当我们敲入ifconfig eth0 up的时候就会调用fec_enet_open。这里 要说明的是fec_timeout这个函数,这个函数运行在SOFTIRQ上下文,被当作网络的watchdog超时函数。而 set_multicast_list函数还不清楚是用来做什么的????接下来 for (i=0; i<NMII-1; i ) mii_cmds[i].mii_next = &mii_cmds[i 1]; mii_free = mii_cmds; 这是初始化mii的命令队列,这个其实是一个静态数组,然后通过内部的链表变量将这个数组链 起来。mii_free是一个链表头,指向第一个可用的mii_cmd元素。接着调用fec_set_mii初始化MII的速度: static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep) { u32 rate; struct clk *clk; volatile fec_t *fecp; fecp = fep->hwp; fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04; fecp->fec_x_cntrl = 0x00; /* * Set MII speed to 2.5 MHz */ clk = clk_get(NULL, “fec_clk”); rate = clk_get_rate(clk); clk_put(clk); fep->phy_speed = ((((rate / 2 4999999) / 2500000) / 2) & 0x3F) << 1; fecp->fec_mii_speed = fep->phy_speed; fec_restart(dev, 0); } 设置完clk后,调用fec_restart重新设置FEC的参数: /* This function is called to start or restart the FEC during a link * change. This only happens when switching between half and full * duplex. */ static void fec_restart(struct net_device *dev, int duplex) { struct fec_enet_private *fep; volatile cbd_t *bdp; volatile fec_t *fecp; int i; OUTDEB(“entry!!!!!n”); fep = netdev_priv(dev); fecp = fep->hwp; /* Whack a reset. We should wait for this. */ fecp->fec_ecntrl = 1; udelay(10); /* Enable interrupts we wish to service. */ fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII; /* Clear any outstanding interrupt. * */ fecp->fec_ievent = FEC_ENET_MASK; #if 0/*modified by Zenith @ 2010.1.21 for int bug*/ fec_enable_phy_intr(); #endif /* Set station address. */ fec_set_mac_address(dev); /* Reset all multicast. */ fecp->fec_hash_table_high = 0; fecp->fec_hash_table_low = 0; /* Set maximum receive buffer size. */ fecp->fec_r_buff_size = PKT_MAXBLR_SIZE; fec_localhw_setup(); /* Set receive and transmit descriptor base. */ fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base)); fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base RX_RING_SIZE*sizeof(cbd_t))); fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->cur_rx = fep->rx_bd_base; /* Reset SKB transmit buffers. */ fep->skb_cur = fep->skb_dirty = 0; for (i=0; i<=TX_RING_MOD_MASK; i ) { if (fep->tx_skbuff[i] != NULL) { dev_kfree_skb_any(fep->tx_skbuff[i]); fep->tx_skbuff[i] = NULL; } } /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; for (i=0; i<RX_RING_SIZE; i ) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = BD_ENET_RX_EMPTY; bdp ; } /* Set the last buffer to wrap. */ bdp–; bdp->cbd_sc |= BD_SC_WRAP; /* …and the same for transmmit. */ bdp = fep->tx_bd_base; for (i=0; i<TX_RING_SIZE; i ) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = 0; bdp->cbd_bufaddr = 0; bdp ; } /* Set the last buffer to wrap. */ bdp–; bdp->cbd_sc |= BD_SC_WRAP; /* Enable MII mode. */ if (duplex) { fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;/* MII enable */ fecp->fec_x_cntrl = 0x04; /* FD enable */ } else { /* MII enable|No Rcv on Xmit */ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x06; fecp->fec_x_cntrl = 0x00; } fep->full_duplex = duplex; /* Set MII speed. */ fecp->fec_mii_speed = fep->phy_speed; /* And last, enable the transmit and receive processing. */ fecp->fec_ecntrl = 2; fecp->fec_r_des_active = 0x01000000; netif_start_queue(dev); } 这里的其实是一个内联函数: static inline void netif_start_queue(struct net_device *dev) { clear_bit(__LINK_STATE_XOFF, &dev->state); } 它清除state中的XOFF位。 下面我们继续回到fec_enet_init函数中: /* Queue up command to detect the PHY and initialize the * remainder of the interface. */ fep->phy_id_done = 0; fep->phy_addr = 0; mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); 这里是将命令mk_mii_read(MII_REG_PHYIR1)插入mii的命令队列,并设置回调函数mii_discover_phy。 end fec_enet_init() end fec_enet_module_init() 我们来看一下mii_queue的实现: static int mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *)) { struct fec_enet_private *fep; unsigned long flags; mii_list_t *mip; int retval; /* Add PHY address to register command. */ fep = netdev_priv(dev); regval |= fep->phy_addr << 23; retval = 0; spin_lock_irqsave(&fep->lock,flags); if ((mip = mii_free) != NULL) { mii_free = mip->mii_next; mip->mii_regval = regval; mip->mii_func = func; mip->mii_next = NULL; if (mii_head) { mii_tail->mii_next = mip; mii_tail = mip; } else { mii_head = mii_tail = mip; fep->hwp->fec_mii_data = regval; } } else { retval = 1; } spin_unlock_irqrestore(&fep->lock,flags); return(retval); } 可以看到,它先是读取FEC硬件寄存器phy_addr的低八位,将它放入变量regval中,这里应该是取PHY的地址,然后先看下 mii_free队列中有无元素,也即是判断有无可用的MII命令队列,如果有,则把寄存器的值(即是要读写PHY的哪个寄存器) 及回调函数放入队列中,再判断mii_head有无元素,如果无的话就将mii_head指向当前这个元素,如果有的话,更新 mii_tail指向当前元素(mii_tail始终指向最后一个命令元素),所以当我们向MII命令队列插入第一个元素的时候,它就 直接读写PHY的那个寄存器(fep->hwp->fec_mii_data = regval;)。而我们前面FEC的MII初始化的时候已经置了MII的中断 ,所以这里往fep->hwp->fec_mii_data寄存器写数据完后便会产生一个FEC中断,这个中断由fec_enet_interrupt函数里调 用fec_enet_mii来处理。 我们再来看下fec_enet_mii的实现: /* called from interrupt context */ static void fec_enet_mii(struct net_device *dev) { struct fec_enet_private *fep; volatile fec_t *ep; mii_list_t *mip; uint mii_reg; fep = netdev_priv(dev); ep = fep->hwp; mii_reg = ep->fec_mii_data; spin_lock(&fep->lock); if ((mip = mii_head) == NULL) { printk(“MII and no head!n”); goto unlock; } if (mip->mii_func != NULL) (*(mip->mii_func))(mii_reg, dev); mii_head = mip->mii_next; mip->mii_next = mii_free; mii_free = mip; if ((mip = mii_head) != NULL) ep->fec_mii_data = mip->mii_regval; unlock: spin_unlock(&fep->lock); } 可以看到,函数首先是读取FEC的硬件寄存器fec_mii_data,这里的值应该是PHY芯片通过MII接口返回上次要求读的寄存器 的值,然后如果有mip->mii_func的话就回调这个函数,接着移动mii_head和mii_free队列,最后再写入下一个要读(写) 的PHY的寄存器。 所以当我们在初始化函数中调用mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);将mii_discover_phy 插入命令队列时,它先将要读的寄存器MII_REG_PHYIR1通过宏mk_mii_read转换成MII寄存器的格式,然后写入fec_mii_data 寄存器,表示我想读取PHY芯片的MII_REG_PHYIR1寄存器的值,而读寄存器写完后,FEC的MII模块会产生一个中断表示说这 个PHY寄存器的读已经完成,然后中断处理函数fec_enet_mii中,会读取PHY返回的刚才我想要读取的寄存器的值给 fec_mii_data寄存器,后来再调用刚才插入的那个mii_discover_phy函数,mii_discover_phy函数就会根据这个返回的ID来 判断是哪个厂家的PHY芯片。这里将mii_discover_phy再帖出来: /* Scan all of the MII PHY addresses looking for someone to respond * with a valid ID. This usually happens quickly. */ static void mii_discover_phy(uint mii_reg, struct net_device *dev) { struct fec_enet_private *fep; volatile fec_t *fecp; uint phytype; fep = netdev_priv(dev); fecp = fep->hwp; if (fep->phy_addr < 32) { if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) { /* Got first part of ID, now get remainder. */ fep->phy_id = phytype << 16; mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3); } else { fep->phy_addr ; mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy); } } else { printk(“FEC: No PHY device found.n”); /* Disable external MII interface */ fecp->fec_mii_speed = fep->phy_speed = 0; fec_disable_phy_intr(); } } 至于判断fep->phy_addr < 32与否,这个是说一个FEC可以接多个PHY,每个PHY有一个地址,用5个BIT来表示,这里是从 0~31去自加,然后查询对应的PHY地址上是否有PHY设备。 初始化过程中fec_enet_init调用完了之后,dev这个变量已经设置得差不多了,接下来是调用register_netdev(dev) 来注 册一个网络设备,后面fec_device[i] = dev;将这个DEV放入FEC的设备表中。到此,module_init完成。。。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/169594.html原文链接:https://javaforall.cn