/* * fec.c * * Created on: 2019-8-30 * Author: sunxi */ //sunxi 20220411: 暂时屏蔽 //#ifndef __LIGHT_DIFF_ACT_PRO__ //#define __LIGHT_DIFF_ACT_PRO__ //#endif #ifndef __SAVE_TO_BUFF_FOR_READ__ #define __SAVE_TO_BUFF_FOR_READ__ #endif #ifdef __LIGHT_DIFF_ACT_PRO__ // sunxi 20190904 光差保护 #include #include #include #include #include #include #include #include "rt.h" #include "bsp.h" #include "fec.h" #ifdef __SAVE_TO_BUFF_FOR_READ__ // 由采样中断读取读取临时缓冲区的数据,因为使用了 memcpy 拷贝了两次数据,效率会低一些 #define FEC_LP_TXBUF_CNT 256 // 发送缓冲区数量最大值 static unsigned char fec2_lp_txbuf[FEC_LP_TXBUF_CNT][FEC_LP_BUF_MAX]; #if 0 // sunxi 20191015 unsigned char fec2_lp_rx_head = 0; unsigned char fec2_lp_rx_tail = 0; struct fec_lp_rx_buf fec2_lp_rxbuf[FEC_LP_BUF_CNT_MAX]; #else // sunxi 20191015 struct fec_lp_rx_buf fec2_lp_rxbuf; unsigned int fec2_lp_rx_cnt = 0; #endif #endif // __SAVE_TO_BUFF_FOR_READ__ #define POLL_FRAME 0x50 //巡检报文 #define SAMP_FRAME 0xA0 //采样报文 #define POLL_FRAME_WORD (12+4*9) //轮巡帧6个字,12字节 #define SAMP_FRAME_WORD 32 //采样帧16个字,32字节 /* Interrupt events/masks. */ #define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */ #define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */ #define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */ #define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */ #define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */ #define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */ #define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */ #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ #define FEC_ENET_TS_AVAIL ((uint)0x00010000) #define FEC_ENET_TS_TIMER ((uint)0x00008000) static struct net_device *g_fec2_netdev = NULL; struct sk_buff g_fec2_skb[FEC_LP_TXBUF_CNT]; #ifdef __FEC2_LDAP_TEST__ #define TEST_BUF_MAX 64 static unsigned char g_test_buf[TEST_BUF_MAX] = {0x00}; static unsigned long g_fec2_us1, g_fec2_us2, g_fec2_us3; #endif // __FEC2_LDAP_TEST__ static int fec2_lp_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct fec_enet_private *fep; volatile fec_t *fecp; volatile cbd_t *bdp, *bdp_tmp; unsigned short status; fep = netdev_priv(dev); fecp = (volatile fec_t *)dev->base_addr; if (!fep->link) { /* Link is down or autonegotiation is in progress. */ return 1; } /* Fill in a Tx ring entry */ bdp = fep->cur_tx; status = bdp->cbd_sc; #ifndef final_version if (status & BD_ENET_TX_READY) { /* Ooops. All transmit buffers are full. Bail out. * This should not happen, since dev->tbusy should be set. */ // sunxi 20190814 printk(KERN_ERR "%s: tx queue full!.\n", dev->name); return 1; } #endif /* Clear all of the status flags. */ status &= ~BD_ENET_TX_STATS; /* Set buffer length and buffer pointer. */ bdp->cbd_bufaddr = __pa(skb->data); bdp->cbd_datlen = skb->len; /* * On some FEC implementations data must be aligned on * 4-byte boundaries. Use bounce buffers to copy data * and get it aligned. Ugh. */ /*if (bdp->cbd_bufaddr & 0x3) { unsigned int index1; index1 = bdp - fep->tx_bd_base; memcpy(fep->tx_bounce[index1], (void *)skb->data, bdp->cbd_datlen); bdp->cbd_bufaddr = __pa(fep->tx_bounce[index1]); } */ dev->stats.tx_bytes += skb->len; /* Push the data cache so the CPM does not get stale memory * data. */ flush_dcache_range((unsigned long)skb->data, (unsigned long)skb->data + skb->len); /* Send it on its way. Tell FEC it's ready, interrupt when done, * it's the last BD of the frame, and to put the CRC on the end. */ status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC); bdp->cbd_sc = status; dev->trans_start = jiffies; /* Trigger transmission start */ fecp->fec_x_des_active = 0; /* If this was the last BD in the ring, start at the beginning again. */ if (status & BD_ENET_TX_WRAP) bdp = fep->tx_bd_base; else bdp++; if (bdp == fep->dirty_tx) { fep->tx_full = 1; // sunxi 20200812 由于缓冲区满了后会调用该函数,导致后面调用 netif_wake_queue函数时导致内核出错,因此进行屏蔽,满了的缓冲区丢弃掉最前面一帧数据 netif_stop_queue(dev); // sunxi 20200812 added bdp_tmp = bdp; bdp_tmp ++; fep->dirty_tx = (cbd_t *)bdp_tmp; } fep->cur_tx = (cbd_t *)bdp; #ifdef __FEC2_LDAP_TEST__ g_fec2_us2 = ustimer_get_origin(); #endif // __FEC2_LDAP_TEST__ return 0; } int fec2_lp_start_xmit(unsigned char *data, int len) { struct sk_buff *skb; static unsigned char skb_index = 0; unsigned long flags; if((g_fec2_netdev == NULL) || (data == NULL) || (len <= 0) || (len > FEC_LP_BUF_MAX)) return 0; rt_irq_save(flags); skb = &g_fec2_skb[skb_index]; skb->data = fec2_lp_txbuf[skb_index]; //拷贝用户数据到skb->data memcpy(skb->data, data, len); //指定长度 skb->len = len; skb_index ++; if(fec2_lp_enet_start_xmit(skb, g_fec2_netdev)) len = 0; rt_irq_restore(flags); return len; } static void fec2_enet_tx(struct net_device *dev) { struct fec_enet_private *fep; volatile cbd_t *bdp; unsigned short status; unsigned long flags; fep = netdev_priv(dev); // sunxi 20191015 spin_lock_irqsave(&fep->hw_lock, flags); rt_irq_save(flags); bdp = fep->dirty_tx; #ifdef __FEC2_LDAP_TEST__ g_fec2_us3 = ustimer_get_origin(); #endif // __FEC2_LDAP_TEST__ while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { if (bdp == fep->cur_tx && fep->tx_full == 0) break; /* Check for errors. */ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | BD_ENET_TX_RL | BD_ENET_TX_UN | BD_ENET_TX_CSL)) { dev->stats.tx_errors++; if (status & BD_ENET_TX_HB) /* No heartbeat */ dev->stats.tx_heartbeat_errors++; if (status & BD_ENET_TX_LC) /* Late collision */ dev->stats.tx_window_errors++; if (status & BD_ENET_TX_RL) /* Retrans limit */ dev->stats.tx_aborted_errors++; if (status & BD_ENET_TX_UN) /* Underrun */ dev->stats.tx_fifo_errors++; if (status & BD_ENET_TX_CSL) /* Carrier lost */ dev->stats.tx_carrier_errors++; } else { dev->stats.tx_packets++; } #ifndef final_version if (status & BD_ENET_TX_READY) printk(KERN_ERR "HEY! " "Enet xmit interrupt and TX_READY.\n"); #endif /* Deferred means some collisions occurred during transmit, * but we eventually sent the packet OK. */ if (status & BD_ENET_TX_DEF) dev->stats.collisions++; /* Update pointer to next buffer descriptor to be transmitted. */ if (status & BD_ENET_TX_WRAP) bdp = fep->tx_bd_base; else bdp++; /* Since we have freed up a buffer, the ring is no longer * full. * sunxi 20200812 前面发送接口判断如果缓冲区满了,进行数据丢弃,因此不会出现以下情况,不会导致调用以下函数导致内核出错 */ if (fep->tx_full) { fep->tx_full = 0; // sunxi 20200812 if (netif_queue_stopped(dev)) // sunxi 20200812 netif_wake_queue(dev); } } fep->dirty_tx = (cbd_t *)bdp; #ifdef __FEC2_LDAP_TEST__ rt_printf("us1 = %lu, us2 = %lu, us3 = %lu\n", g_fec2_us1, g_fec2_us2, g_fec2_us3); g_fec2_us1 = 0; g_fec2_us2 = 0; g_fec2_us3 = 0; #endif // __FEC2_LDAP_TEST__ // sunxi 20191015 spin_unlock_irqrestore(&fep->hw_lock, flags); rt_irq_restore(flags); } extern void fec2_lp_frame_handle(unsigned char *data, bool bGoodPacket,int errtype); static void fec2_enet_rx(struct net_device *dev) { struct fec_enet_private *fep; volatile fec_t *fecp; volatile cbd_t *bdp; unsigned short status; int errtype=0; #if 0 struct sk_buff *skb; #endif ushort pkt_len; __u8 *data; unsigned long flags; int frame_count; ushort i, wRecvCnt; ushort wCRC=0; ushort ucrc; u8 *rdptr = 0; bool bGoodPacket = false; fep = netdev_priv(dev); fecp = (volatile fec_t *)dev->base_addr; // sunxi 20190920 spin_lock_irqsave(&fep->hw_lock, flags); rt_irq_save(flags); /* First, grab all of the stats for the incoming packet. * These get messed up if we get called due to a busy condition. */ bdp = fep->cur_rx; frame_count = 0; while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { #if 1 //xbtong 目前流量控制好像不起作用,但在MCF54452上经测试是可以起作用的。 //流控制,防止广播风暴 frame_count++; if(frame_count > 2) { // opd寄存器存放发送给对方的PAUSE时间,以slot time(512 bit times ,5.12us) 为单位 // printk("rx(%d):%d\n", fep->index, frame_count); // fecp->fec_r_fifo_section_empty = 0; // fecp->fec_opd = 0xfff0; // sunxi 20190731 fecp->fec_opd = 0x400; fecp->fec_opd = 0x80; fecp->fec_x_cntrl |= 0x0008; } #endif #ifndef final_version /* Since we have allocated space to hold a complete frame, * the last indicator should be set. */ if ((status & BD_ENET_RX_LAST) == 0){ printk(KERN_INFO "FEC ENET: rcv is not +last\n"); //非法数据长度处理 if(bdp->cbd_datlen > FEC_ENET_RX_FRSIZE ) goto rx_processing_done; } #endif if (!fep->opened) goto rx_processing_done; /* Check for errors. */ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) { dev->stats.rx_errors++; if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { /* Frame too long or too short. */ dev->stats.rx_length_errors++; } if (status & BD_ENET_RX_NO) /* Frame alignment */ dev->stats.rx_frame_errors++; if (status & BD_ENET_RX_CR) /* CRC Error */ dev->stats.rx_crc_errors++; if (status & BD_ENET_RX_OV) /* FIFO overrun */ dev->stats.rx_fifo_errors++; } /* Report late collisions as a frame error. * On this error, the BD is closed, but we don't know what we * have in the buffer. So, just drop this frame on the floor. */ if (status & BD_ENET_RX_CL) { dev->stats.rx_errors++; dev->stats.rx_frame_errors++; goto rx_processing_done; } /* Process the incoming frame. */ dev->stats.rx_packets++; pkt_len = bdp->cbd_datlen; dev->stats.rx_bytes += pkt_len; data = (__u8 *)__va(bdp->cbd_bufaddr); data+=12; // 以下为光差保护数据接收处理 /* 1. 光纤保护的数据帧,根据ut的协议帧,不是以太网数据帧格式(6字节目标mac地址,接着6字节源mac地址,再两字节帧类型,然后就是帧数据) * 2. 光纤保护的数据帧:两字节帧类型,0x5000是轮询帧,中间8字节的帧数据,最后加上两字节wCRC,一共10字节, * 3. 0xA000是采样帧,中间26字节的帧数据,最后加上两字节wCRC,一共是28字节。 * 4. wCRC 是包括两个字节的帧类型和帧数据的异或 */ rdptr = (u8 *)data; switch( data[0] & 0xF0 ) { case POLL_FRAME: //轮巡帧 wRecvCnt = POLL_FRAME_WORD; break; case SAMP_FRAME: //采样帧 wRecvCnt = SAMP_FRAME_WORD; break; default: wRecvCnt = 0; break; } if( wRecvCnt ) { rdptr = (u8 *)data; for( i = 0; i < wRecvCnt-2; i++ ) { wCRC += *rdptr++; } ucrc=(ushort )rdptr[0]|(rdptr[1]<<8); if( wCRC == ucrc ) //效验成功 { bGoodPacket = true; } else { errtype=1; } } else { memset(data, 0x00, (SAMP_FRAME_WORD )); wRecvCnt = 0; errtype=2; } #ifdef __SAVE_TO_BUFF_FOR_READ__ // 保存在临时缓冲区,由采样中断读取,因为使用了 memcpy 拷贝了两次数据,效率会低一些 // 把正确的数据保存到缓冲区 #if 0 // sunxi 20191015 fec2_lp_rx_head=0; //为保证采样数据的实效性,只处理一帧数据,不做缓存 fec2_lp_rxbuf[fec2_lp_rx_head].bvalid = bGoodPacket; fec2_lp_rxbuf[fec2_lp_rx_head].len = wRecvCnt; if(wRecvCnt > 0) memcpy(fec2_lp_rxbuf[fec2_lp_rx_head].buf, data, wRecvCnt); fec2_lp_rx_head ++; if(fec2_lp_rx_head == fec2_lp_rx_tail) fec2_lp_rx_tail ++; #else // sunxi 20191015 fec2_lp_rx_cnt = 0; fec2_lp_rxbuf.bvalid = bGoodPacket; fec2_lp_rxbuf.len = wRecvCnt; if(wRecvCnt > 0) memcpy(fec2_lp_rxbuf.buf, data, wRecvCnt); fec2_lp_rx_cnt = 1; #endif // sunxi 20191015 // 数据直接网口2接收中断里面进行处理,调用app那边的接口,效率比较高 fec2_lp_frame_handle(data, bGoodPacket,errtype); //接收报文处理 #ifdef __FEC2_LDAP_TEST__ for(i = 0; i < 256; i ++) data[i] = i; g_fec2_us1 = ustimer_get_origin(); fec2_lp_start_xmit(g_test_buf, TEST_BUF_MAX); #endif // __FEC2_LDAP_TEST__ #endif // __SAVE_TO_BUFF_FOR_READ__ rx_processing_done: /* Clear the status flags for this buffer. */ status &= ~BD_ENET_RX_STATS; /* Mark the buffer empty. */ status |= BD_ENET_RX_EMPTY; bdp->cbd_sc = status; /* Update BD pointer to next entry. */ if (status & BD_ENET_RX_WRAP) bdp = fep->rx_bd_base; else bdp++; /* Doing this here will keep the FEC running while we process * incoming frames. On a heavily loaded network, we should be * able to keep up at the expense of system resources. */ fecp->fec_r_des_active = 0; } /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */ fep->cur_rx = (cbd_t *)bdp; // sunxi 20190920 spin_unlock_irqrestore(&fep->hw_lock, flags); rt_irq_restore(flags); } /* The interrupt handler. * This is called from the MPC core interrupt. */ static void fec2_enet_interrupt(void) { struct net_device *dev = g_fec2_netdev; volatile fec_t *fecp; uint int_events; if(dev == NULL) // sunxi 20200811 added return; fecp = (volatile fec_t *)dev->base_addr; /* Get the interrupt events that caused us to be here. */ do { int_events = fecp->fec_ievent; fecp->fec_ievent = int_events; // & (FEC_ENET_GRA | FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_TXB | FEC_ENET_RXB); /* Handle receive event in its own function. */ if (int_events & FEC_ENET_RXF) { fec2_enet_rx(dev); } /* Transmit OK, or non-fatal error. Update the buffer descriptors. FEC handles all errors, we just discover them as part of the transmit process. */ if (int_events & FEC_ENET_TXF) { fec2_enet_tx(dev); } } while (int_events); } // sunxi 20191217 增加以下4个接口,当模块在手动卸载后,不会出现网口看门狗动作出现的网口2重启的内核出错信息,也不会在手动关闭和打开网口2时出现内核出错信息。 static int fec2_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) { return 0; } static void fec2_timeout(struct net_device *dev) { } static int fec2_enet_open(struct net_device *dev) { return 0; } static int fec2_enet_close(struct net_device *dev) { return 0; } extern struct net_device *fec_get_net_device(int idx); static const int fec_idx = 1; // sunxi 20200722 增加该变量,使用光纤通信可以不止是网口2,也可以是网口1,但是应用那边也需要对应改动。最好在调用fec2_reinit接口时可以把索引号传进来。 int fec2_reinit(void) { #ifdef __FEC2_LDAP_TEST__ int i; #endif // __FEC2_LDAP_TEST__ g_fec2_netdev = fec_get_net_device(fec_idx); if(g_fec2_netdev == NULL) { return 0; } #ifdef __FEC2_LDAP_TEST__ for(i = 0; i < TEST_BUF_MAX; i ++ ) g_test_buf[i] = i; #endif // __FEC2_LDAP_TEST__ if(g_fec2_netdev->stop != fec2_enet_close) { g_fec2_netdev->stop(g_fec2_netdev); g_fec2_netdev->stop = fec2_enet_close; // 重新定位新的网口2中断处理函数 if(fec_idx == 1) { //free_irq(113, (void *)g_fec2_netdev); //free_irq(117, (void *)g_fec2_netdev); } else if(!fec_idx) { //free_irq(100, (void *)g_fec2_netdev); //free_irq(104, (void *)g_fec2_netdev); } } // 防止网口2的网络数据在其它地方来发送 if(g_fec2_netdev->hard_start_xmit != fec2_enet_start_xmit) { g_fec2_netdev->hard_start_xmit = fec2_enet_start_xmit; g_fec2_netdev->tx_timeout = fec2_timeout; } if(fec_idx == 1) { rt_request_irq(113, 6, fec2_enet_interrupt, "fec2(TXF)"); rt_request_irq(117, 6, fec2_enet_interrupt, "fec2(RXF)"); } else if(!fec_idx) { rt_request_irq(100, 6, fec2_enet_interrupt, "fec1(TXF)"); rt_request_irq(104, 6, fec2_enet_interrupt, "fec1(RXF)"); } if(g_fec2_netdev->open != fec2_enet_open) { g_fec2_netdev->open(g_fec2_netdev); g_fec2_netdev->open = fec2_enet_open; } return 0; } int fec2_exit(void) { if(g_fec2_netdev == NULL) { return 0; } msleep(10); // 让有足够时间把数据都发送出去了再释放实时中断,不然会触发数据发送的看门狗动作。 if(fec_idx == 1) { rt_free_irq(117); rt_free_irq(113); } else if(!fec_idx) { rt_free_irq(104); rt_free_irq(100); } return 0; } #endif // __LIGHT_DIFF_ACT_PRO__