📄 sky2.c
字号:
/* ** Check for Special Interrupts */ if ((pAC->InterruptSource & ~Y2_IS_STAT_BMU) || pAC->CheckQueue || pNet->TimerExpired) { pAC->CheckQueue = SK_FALSE; spin_lock_irqsave(&pAC->SetPutIndexLock, Flags); SkGeSirqIsr(pAC, pAC->IoBase, pAC->InterruptSource); SkEventDispatcher(pAC, pAC->IoBase); spin_unlock_irqrestore(&pAC->SetPutIndexLock, Flags); } /* Speed enhancement for a2 chipsets */ if (HW_FEATURE(pAC, HWF_WA_DEV_42)) { spin_lock_irqsave(&pAC->SetPutIndexLock, Flags); SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_XA1,0), &pAC->TxPort[0][0].TxALET); SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_R1,0), &pAC->RxPort[0].RxLET); spin_unlock_irqrestore(&pAC->SetPutIndexLock, Flags); } /* ** Reenable interrupts and signal end of ISR */ SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2); /* ** Stop and restart TX timer in case a Status LE was handled */ if ((HW_FEATURE(pAC, HWF_WA_DEV_43_418)) && (handledStatLE)) { SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_STOP); SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_START); } if (!(IS_Q_EMPTY(&(pAC->TxPort[0][TX_PRIO_LOW].TxAQ_waiting)))) { GiveTxBufferToHw(pAC, pAC->IoBase, 0); } if (!(IS_Q_EMPTY(&(pAC->TxPort[1][TX_PRIO_LOW].TxAQ_waiting)))) { GiveTxBufferToHw(pAC, pAC->IoBase, 1); }#endif SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC, ("<== SkY2Isr\n")); return SkIsrRetHandled;} /* SkY2Isr *//***************************************************************************** * * SkY2Xmit - Linux frame transmit function for Yukon2 * * Description: * The system calls this function to send frames onto the wire. * It puts the frame in the tx descriptor ring. If the ring is * full then, the 'tbusy' flag is set. * * Returns: * 0, if everything is ok * !=0, on error * * WARNING: * returning 1 in 'tbusy' case caused system crashes (double * allocated skb's) !!! */int SkY2Xmit(struct sk_buff *skb, /* socket buffer to be sent */struct SK_NET_DEVICE *dev) /* via which device? */{ DEV_NET *pNet = (DEV_NET*) dev->priv; SK_AC *pAC = pNet->pAC; SK_U8 FragIdx = 0; SK_PACKET *pSkPacket; SK_FRAG *PrevFrag; SK_FRAG *CurrFrag; SK_PKT_QUEUE *pWorkQueue; /* corresponding TX queue */ SK_PKT_QUEUE *pWaitQueue; SK_PKT_QUEUE *pFreeQueue; SK_LE_TABLE *pLETab; /* corresponding LETable */ skb_frag_t *sk_frag; SK_U64 PhysAddr; unsigned long Flags; unsigned int Port; int CurrFragCtr; SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, ("==> SkY2Xmit\n")); /* ** Get port and return if no free packet is available */ if (skb_shinfo(skb)->nr_frags > MAX_SKB_FRAGS) { Port = skb_shinfo(skb)->nr_frags - (2*MAX_SKB_FRAGS); skb_shinfo(skb)->nr_frags = 0; } else { Port = (pAC->RlmtNets == 2) ? pNet->PortNr : pAC->ActivePort; }#ifdef USE_ASF_DASH_FW if ((pAC->dev[Port]->flags & IFF_RUNNING) == 0) { DEV_KFREE_SKB_ANY(skb); return 0; }#endif if (IS_Q_EMPTY(&(pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free))) { SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR, ("Not free packets available for send\n")); return 1; /* zero bytes sent! */ } /* ** Put any new packet to be sent in the waiting queue and ** handle also any possible fragment of that packet. */ pWorkQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working); pWaitQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting); pFreeQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free); pLETab = &(pAC->TxPort[Port][TX_PRIO_LOW].TxALET); /* ** Normal send operations require only one fragment, because ** only one sk_buff data area is passed. ** In contradiction to this, scatter-gather (zerocopy) send ** operations might pass one or more additional fragments ** where each fragment needs a separate fragment info packet. */ if (((skb_shinfo(skb)->nr_frags + 1) * MAX_FRAG_OVERHEAD) > NUM_FREE_LE_IN_TABLE(pLETab)) { SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR, ("Not enough LE available for send\n")); return 1; /* zero bytes sent! */ } if ((skb_shinfo(skb)->nr_frags + 1) > MAX_NUM_FRAGS) { SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR, ("Not even one fragment available for send\n")); return 1; /* zero bytes sent! */ } /* ** Get first packet from free packet queue */ POP_FIRST_PKT_FROM_QUEUE(pFreeQueue, pSkPacket); if(pSkPacket == NULL) { SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR, ("Could not obtain free packet used for xmit\n")); return 1; /* zero bytes sent! */ } pSkPacket->pFrag = &(pSkPacket->FragArray[FragIdx]); /* ** map the sk_buff to be available for the adapter */ PhysAddr = (SK_U64) pci_map_page(pAC->PciDev, virt_to_page(skb->data), ((unsigned long) skb->data & ~PAGE_MASK), skb_headlen(skb), PCI_DMA_TODEVICE); pSkPacket->pMBuf = skb; pSkPacket->pFrag->pPhys = PhysAddr; pSkPacket->pFrag->FragLen = skb_headlen(skb); pSkPacket->pFrag->pNext = NULL; /* initial has no next default */ pSkPacket->NumFrags = skb_shinfo(skb)->nr_frags + 1; PrevFrag = pSkPacket->pFrag; /* ** Each scatter-gather fragment need to be mapped... */ for ( CurrFragCtr = 0; CurrFragCtr < skb_shinfo(skb)->nr_frags; CurrFragCtr++) { FragIdx++; sk_frag = &skb_shinfo(skb)->frags[CurrFragCtr]; CurrFrag = &(pSkPacket->FragArray[FragIdx]); /* ** map the sk_buff to be available for the adapter */ PhysAddr = (SK_U64) pci_map_page(pAC->PciDev, sk_frag->page, sk_frag->page_offset, sk_frag->size, PCI_DMA_TODEVICE); CurrFrag->pPhys = PhysAddr; CurrFrag->FragLen = sk_frag->size; CurrFrag->pNext = NULL; /* ** Add the new fragment to the list of fragments */ PrevFrag->pNext = CurrFrag; PrevFrag = CurrFrag; } /* ** Add packet to waiting packets queue */ PUSH_PKT_AS_LAST_IN_QUEUE(pWaitQueue, pSkPacket); GiveTxBufferToHw(pAC, pAC->IoBase, Port); dev->trans_start = jiffies; SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS, ("<== SkY2Xmit(return 0)\n")); return (0);} /* SkY2Xmit */#ifdef CONFIG_SK98LIN_NAPI/***************************************************************************** * * SkY2Poll - NAPI Rx polling callback for Yukon2 chipsets * * Description: * Called by the Linux system in case NAPI polling is activated * * Returns * The number of work data still to be handled * * Notes * The slowpath lock needs to be set because HW accesses may * interfere with slowpath events (e.g. TWSI) */int SkY2Poll(struct net_device *dev, /* device that needs to be polled */int *budget) /* how many budget do we have? */{ DEV_NET *pNet = (DEV_NET*) dev->priv; SK_AC *pAC = pNet->pAC; int WorkToDo = min(*budget, dev->quota); int WorkDone = 0; SK_BOOL handledStatLE = SK_FALSE; handledStatLE = HandleStatusLEs(pAC, &WorkDone, WorkToDo); *budget -= WorkDone; dev->quota -= WorkDone; /* ** Check for Special Interrupts */ if ((pAC->InterruptSource & ~Y2_IS_STAT_BMU) || pAC->CheckQueue || pNet->TimerExpired) { pAC->CheckQueue = SK_FALSE; spin_lock(&pAC->SlowPathLock); SkGeSirqIsr(pAC, pAC->IoBase, pAC->InterruptSource); SkEventDispatcher(pAC, pAC->IoBase); spin_unlock(&pAC->SlowPathLock); } /* Speed enhancement for a2 chipsets */ if (HW_FEATURE(pAC, HWF_WA_DEV_42)) { spin_lock(&pAC->SlowPathLock); SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_XA1,0), &pAC->TxPort[0][0].TxALET); SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_R1,0), &pAC->RxPort[0].RxLET); spin_unlock(&pAC->SlowPathLock); } if(WorkDone < WorkToDo) { netif_rx_complete(dev); /* ** Stop and restart TX timer in case a Status LE was handled */ if ((HW_FEATURE(pAC, HWF_WA_DEV_43_418)) && (handledStatLE)) { SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_STOP); SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_START); } /* ** Reenable interrupts */ SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2); if (!(IS_Q_EMPTY(&(pAC->TxPort[0][TX_PRIO_LOW].TxAQ_waiting)))) { GiveTxBufferToHw(pAC, pAC->IoBase, 0); } if (!(IS_Q_EMPTY(&(pAC->TxPort[1][TX_PRIO_LOW].TxAQ_waiting)))) { GiveTxBufferToHw(pAC, pAC->IoBase, 1); } } return (WorkDone >= WorkToDo);} /* SkY2Poll */#endif/****************************************************************************** * * SkY2PortStop - stop a port on Yukon2 * * Description: * This function stops a port of the Yukon2 chip. This stop * stop needs to be performed in a specific order: * * a) Stop the Prefetch unit * b) Stop the Port (MAC, PHY etc.) * * Returns: N/A */void SkY2PortStop(SK_AC *pAC, /* adapter control context */SK_IOC IoC, /* I/O control context (address of adapter registers) */int Port, /* port to stop (MAC_1 + n) */int Dir, /* StopDirection (SK_STOP_RX, SK_STOP_TX, SK_STOP_ALL) */int RstMode) /* Reset Mode (SK_SOFT_RST, SK_HARD_RST) */{ SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG, ("==> SkY2PortStop (Port %c)\n", 'A' + Port)); /* ** Stop the HW */ SkGeStopPort(pAC, IoC, Port, Dir, RstMode); /* ** Move any TX packet from work queues into the free queue again ** and initialize the TX LETable variables */ SkY2FreeTxBuffers(pAC, pAC->IoBase, Port); pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Bmu.RxTx.TcpWp = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Bmu.RxTx.MssValue = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.BufHighAddr = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Done = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Put = 0;#ifndef USE_ASF_DASH_FW pAC->GIni.GP[Port].PState = SK_PRT_STOP;#endif /* ** Move any RX packet from work queue into the waiting queue ** and initialize the RX LETable variables */ SkY2FreeRxBuffers(pAC, pAC->IoBase, Port); pAC->RxPort[Port].RxLET.BufHighAddr = 0; SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG, ("<== SkY2PortStop()\n"));}/****************************************************************************** * * SkY2PortStart - start a port on Yukon2 * * Description: * This function starts a port of the Yukon2 chip. This start * action needs to be performed in a specific order: * * a) Initialize the LET indices (PUT/GET to 0) * b) Initialize the LET in HW (enables also prefetch unit) * c) Move all RX buffers from waiting queue to working queue * which involves also setting up of RX list elements * d) Initialize the FIFO settings of Yukon2 (Watermark etc.) * e) Initialize the Port (MAC, PHY etc.) * f) Initialize the MC addresses * * Returns: N/A */void SkY2PortStart(SK_AC *pAC, /* adapter control context */SK_IOC IoC, /* I/O control context (address of adapter registers) */int Port) /* port to start */{ // SK_GEPORT *pPrt = &pAC->GIni.GP[Port]; SK_HWLE *pLE; SK_U32 DWord; SK_U32 PrefetchReg; /* register for Put index */ SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG, ("==> SkY2PortStart (Port %c)\n", 'A' + Port)); /* ** Initialize the LET indices */ pAC->RxPort[Port].RxLET.Done = 0; pAC->RxPort[Port].RxLET.Put = 0; pAC->RxPort[Port].RxLET.HwPut = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Done = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Put = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxALET.HwPut = 0; if (HW_SYNC_TX_SUPPORTED(pAC)) { pAC->TxPort[Port][TX_PRIO_LOW].TxSLET.Done = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxSLET.Put = 0; pAC->TxPort[Port][TX_PRIO_LOW].TxSLET.HwPut = 0; } if (HW_FEATURE(pAC, HWF_WA_DEV_420)) { /* ** It might be that we have to limit the RX buffers ** effectively passed to HW. Initialize the start ** value in that case... */ NbrRxBuffersInHW = 0; } /* ** TODO on dual net adapters we need to check if ** StatusLETable need to be set... ** ** pAC->StatusLETable.Done = 0; ** pAC->StatusLETable.Put = 0; ** pAC->StatusLETable.HwPut = 0; ** SkGeY2InitPrefetchUnit(pAC, pAC->IoBase, Q_ST, &pAC->StatusLETable); */ /* ** Initialize the LET in HW (enables also prefetch unit) */ SkGeY2InitPrefetchUnit(pAC, IoC,(Port == 0) ? Q_R1 : Q_R2, &pAC->RxPort[Port].RxLET); SkGeY2InitPrefetchUnit( pAC, IoC,(Port == 0) ? Q_XA1 : Q_XA2, &pAC->TxPort[Port][TX_PRIO_LOW].TxALET); if (HW_SYNC_TX_SUPPORTED(pAC)) { SkGeY2InitPrefetchUnit( pAC, IoC, (Port == 0) ? Q_XS1 : Q_XS2, &pAC->TxPort[Port][TX_PRIO_HIGH].TxSLET); } /* ** Using new values for the watermarks and the timer for ** low latency optimization */ if (pAC->LowLatency) { SK_OUT8(IoC, STAT_FIFO_WM, 1); SK_OUT8(IoC, STAT_FIFO_ISR_WM, 1); SK_OUT32(IoC, STAT_LEV_TIMER_INI, 50); SK_OUT32(IoC, STAT_ISR_TIMER_INI, 10); } /* ** Initialize the Port (MAC, PHY etc.)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -