1
0
uboot-1.1.4-kirkwood/drivers/sk98lin/sky2.c
2024-01-08 00:07:02 +01:00

2775 lines
83 KiB
C

/******************************************************************************
*
* Name: sky2.c
* Project: Yukon2 specific functions and implementations
* Version: $Revision: 1.1.1.1 $
* Date: $Date: 2008/12/15 11:39:21 $
* Purpose: The main driver source module
*
*****************************************************************************/
/******************************************************************************
*
* (C)Copyright 1998-2002 SysKonnect GmbH.
* (C)Copyright 2002-2005 Marvell.
*
* Driver for Marvell Yukon/2 chipset and SysKonnect Gigabit Ethernet
* Server Adapters.
*
* Author: Ralph Roesler (rroesler@syskonnect.de)
* Mirko Lindner (mlindner@syskonnect.de)
*
* Address all question to: linux@syskonnect.de
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The information in this file is provided "AS IS" without warranty.
*
*****************************************************************************/
#include <config.h>
#ifdef CONFIG_SK98
#include "h/skdrv1st.h"
#include "h/skdrv2nd.h"
#if 0 /* uboot */
#include <linux/tcp.h>
#endif
/******************************************************************************
*
* Local Function Prototypes
*
*****************************************************************************/
static void InitPacketQueues(SK_AC *pAC,int Port);
static void GiveTxBufferToHw(SK_AC *pAC,SK_IOC IoC,int Port);
static void GiveRxBufferToHw(SK_AC *pAC,SK_IOC IoC,int Port,SK_PACKET *pPacket);
static SK_BOOL HandleReceives(SK_AC *pAC,int Port,SK_U16 Len,SK_U32 FrameStatus,SK_U16 Tcp1,SK_U16 Tcp2,SK_U32 Tist,SK_U16 Vlan);
static void CheckForSendComplete(SK_AC *pAC,SK_IOC IoC,int Port,SK_PKT_QUEUE *pPQ,SK_LE_TABLE *pLETab,unsigned int Done);
static void UnmapAndFreeTxPktBuffer(SK_AC *pAC,SK_PACKET *pSkPacket,int TxPort);
static SK_BOOL AllocateAndInitLETables(SK_AC *pAC);
static SK_BOOL AllocatePacketBuffersYukon2(SK_AC *pAC);
static void FreeLETables(SK_AC *pAC);
static void FreePacketBuffers(SK_AC *pAC);
static SK_BOOL AllocAndMapRxBuffer(SK_AC *pAC,SK_PACKET *pSkPacket,int Port);
#ifdef CONFIG_SK98LIN_NAPI
static SK_BOOL HandleStatusLEs(SK_AC *pAC,int *WorkDone,int WorkToDo);
#else
static SK_BOOL HandleStatusLEs(SK_AC *pAC);
#endif
extern void SkGeCheckTimer (DEV_NET *pNet);
extern void SkLocalEventQueue( SK_AC *pAC,
SK_U32 Class,
SK_U32 Event,
SK_U32 Param1,
SK_U32 Param2,
SK_BOOL Flag);
extern void SkLocalEventQueue64( SK_AC *pAC,
SK_U32 Class,
SK_U32 Event,
SK_U64 Param,
SK_BOOL Flag);
/******************************************************************************
*
* Local Variables
*
*****************************************************************************/
#define MAX_NBR_RX_BUFFERS_IN_HW 0x15
static SK_U8 NbrRxBuffersInHW;
#define FLUSH_OPC(le)
/******************************************************************************
*
* Global Functions
*
*****************************************************************************/
int SkY2Xmit( struct sk_buff *skb, struct SK_NET_DEVICE *dev);
void FillReceiveTableYukon2(SK_AC *pAC,SK_IOC IoC,int Port);
/*****************************************************************************
*
* SkY2RestartStatusUnit - restarts teh status unit
*
* Description:
* Reenables the status unit after any De-Init (e.g. when altering
* the sie of the MTU via 'ifconfig a.b.c.d mtu xxx')
*
* Returns: N/A
*/
void SkY2RestartStatusUnit(
SK_AC *pAC) /* pointer to adapter control context */
{
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("==> SkY2RestartStatusUnit\n"));
/*
** It might be that the TX timer is not started. Therefore
** it is initialized here -> to be more investigated!
*/
SK_OUT32(pAC->IoBase, STAT_TX_TIMER_INI, HW_MS_TO_TICKS(pAC,10));
pAC->StatusLETable.Done = 0;
pAC->StatusLETable.Put = 0;
pAC->StatusLETable.HwPut = 0;
SkGeY2InitStatBmu(pAC, pAC->IoBase, &pAC->StatusLETable);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== SkY2RestartStatusUnit\n"));
}
/*****************************************************************************
*
* SkY2RlmtSend - sends out a single RLMT notification
*
* Description:
* This function sends out an RLMT frame
*
* Returns:
* > 0 - on succes: the number of bytes in the message
* = 0 - on resource shortage: this frame sent or dropped, now
* the ring is full ( -> set tbusy)
* < 0 - on failure: other problems ( -> return failure to upper layers)
*/
int SkY2RlmtSend (
SK_AC *pAC, /* pointer to adapter control context */
int PortNr, /* index of port the packet(s) shall be send to */
struct sk_buff *pMessage) /* pointer to send-message */
{
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("=== SkY2RlmtSend\n"));
return -1; // temporarily do not send out RLMT frames
#if 0
return(SkY2Xmit(pMessage, pAC->dev[PortNr])); SkY2Xmit needs device
#endif
}
/*****************************************************************************
*
* SkY2AllocateResources - Allocates all required resources for Yukon2
*
* Description:
* This function allocates all memory needed for the Yukon2.
* It maps also RX buffers to the LETables and initializes the
* status list element table.
*
* Returns:
* SK_TRUE, if all resources could be allocated and setup succeeded
* SK_FALSE, if an error
*/
SK_BOOL SkY2AllocateResources (
SK_AC *pAC) /* pointer to adapter control context */
{
int CurrMac;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("==> SkY2AllocateResources\n"));
/*
** Initialize the packet queue variables first
*/
for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
InitPacketQueues(pAC, CurrMac);
}
/*
** Get sufficient memory for the LETables
*/
if (!AllocateAndInitLETables(pAC)) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
("No memory for LETable.\n"));
return(SK_FALSE);
}
/*
** Allocate and intialize memory for both RX and TX
** packet and fragment buffers. On an error, free
** previously allocated LETable memory and quit.
*/
if (!AllocatePacketBuffersYukon2(pAC)) {
FreeLETables(pAC);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
("No memory for Packetbuffers.\n"));
return(SK_FALSE);
}
/*
** Rx and Tx LE tables will be initialized in SkGeOpen()
**
** It might be that the TX timer is not started. Therefore
** it is initialized here -> to be more investigated!
*/
SK_OUT32(pAC->IoBase, STAT_TX_TIMER_INI, HW_MS_TO_TICKS(pAC,10));
SkGeY2InitStatBmu(pAC, pAC->IoBase, &pAC->StatusLETable);
pAC->MaxUnusedRxLeWorking = MAX_UNUSED_RX_LE_WORKING;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("<== SkY2AllocateResources\n"));
return (SK_TRUE);
}
/*****************************************************************************
*
* SkY2FreeResources - Frees previously allocated resources of Yukon2
*
* Description:
* This function frees all previously allocated memory of the Yukon2.
*
* Returns: N/A
*/
void SkY2FreeResources (
SK_AC *pAC) /* pointer to adapter control context */
{
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("==> SkY2FreeResources\n"));
FreeLETables(pAC);
FreePacketBuffers(pAC);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== SkY2FreeResources\n"));
}
/*****************************************************************************
*
* SkY2AllocateRxBuffers - Allocates the receive buffers for a port
*
* Description:
* This function allocated all the RX buffers of the Yukon2.
*
* Returns: N/A
*/
void SkY2AllocateRxBuffers (
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context */
int Port) /* port index of RX */
{
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("==> SkY2AllocateRxBuffers (Port %c)\n", Port));
FillReceiveTableYukon2(pAC, IoC, Port);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("<== SkY2AllocateRxBuffers\n"));
}
/*****************************************************************************
*
* SkY2FreeRxBuffers - Free's all allocates RX buffers of
*
* Description:
* This function frees all RX buffers of the Yukon2 for a single port
*
* Returns: N/A
*/
void SkY2FreeRxBuffers (
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context */
int Port) /* port index of RX */
{
SK_PACKET *pSkPacket;
unsigned long Flags; /* for POP/PUSH macros */
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("==> SkY2FreeRxBuffers (Port %c)\n", Port));
if (pAC->RxPort[Port].ReceivePacketTable != NULL) {
POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
while (pSkPacket != NULL) {
if ((pSkPacket->pFrag) != NULL) {
pci_unmap_page(pAC->PciDev,
(dma_addr_t) pSkPacket->pFrag->pPhys,
pSkPacket->pFrag->FragLen - 2,
PCI_DMA_FROMDEVICE);
DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
pSkPacket->pMBuf = NULL;
pSkPacket->pFrag->pPhys = (SK_U64) 0;
pSkPacket->pFrag->pVirt = NULL;
}
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
}
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== SkY2FreeRxBuffers\n"));
}
/*****************************************************************************
*
* SkY2FreeTxBuffers - Free's any currently maintained Tx buffer
*
* Description:
* This function frees the TX buffers of the Yukon2 for a single port
* which might be in use by a transmit action
*
* Returns: N/A
*/
void SkY2FreeTxBuffers (
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context */
int Port) /* port index of TX */
{
SK_PACKET *pSkPacket;
SK_FRAG *pSkFrag;
unsigned long Flags;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("==> SkY2FreeTxBuffers (Port %c)\n", Port));
if (pAC->TxPort[Port][0].TransmitPacketTable != NULL) {
POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxAQ_working, pSkPacket);
while (pSkPacket != NULL) {
if ((pSkFrag = pSkPacket->pFrag) != NULL) {
UnmapAndFreeTxPktBuffer(pAC, pSkPacket, Port);
}
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->TxPort[Port][0].TxQ_free, pSkPacket);
POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxAQ_working, pSkPacket);
}
#if USE_SYNC_TX_QUEUE
POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxSQ_working, pSkPacket);
while (pSkPacket != NULL) {
if ((pSkFrag = pSkPacket->pFrag) != NULL) {
UnmapAndFreeTxPktBuffer(pAC, pSkPacket, Port);
}
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->TxPort[Port][0].TxQ_free, pSkPacket);
POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxSQ_working, pSkPacket);
}
#endif
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== SkY2FreeTxBuffers\n"));
}
/*****************************************************************************
*
* SkY2Isr - handle a receive IRQ for all yukon2 cards
*
* Description:
* This function is called when a receive IRQ is set. (only for yukon2)
* HandleReceives does the deferred processing of all outstanding
* interrupt operations.
*
* Returns: N/A
*/
SkIsrRetVar SkY2Isr (
int irq, /* the irq we have received (might be shared!) */
void *dev_id, /* current device id */
struct pt_regs *ptregs) /* not used by our driver */
{
struct SK_NET_DEVICE *dev = (struct SK_NET_DEVICE *)dev_id;
DEV_NET *pNet = (DEV_NET*) dev->priv;
SK_AC *pAC = pNet->pAC;
SK_U32 IntSrc;
unsigned long Flags;
#ifndef CONFIG_SK98LIN_NAPI
SK_BOOL handledStatLE = SK_FALSE;
#else
SK_BOOL SetIntMask = SK_FALSE;
#endif
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("==> SkY2Isr\n"));
SK_IN32(pAC->IoBase, B0_Y2_SP_ISRC2, &IntSrc);
if ((IntSrc == 0) && (!pNet->NetConsoleMode)){
SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("No Interrupt\n ==> SkY2Isr\n"));
return;
}
#ifdef Y2_RECOVERY
if (pNet->InRecover) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Already in recover\n ==> SkY2Isr\n"));
SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2);
return;
}
#endif
#ifdef CONFIG_SK98LIN_NAPI
if (netif_rx_schedule_prep(pAC->dev[0])) {
pAC->GIni.GIValIrqMask &= ~(Y2_IS_STAT_BMU);
SK_OUT32(pAC->IoBase, B0_IMSK, pAC->GIni.GIValIrqMask);
SetIntMask = SK_TRUE;
__netif_rx_schedule(pAC->dev[0]);
}
if (netif_rx_schedule_prep(pAC->dev[1])) {
if (!SetIntMask) {
pAC->GIni.GIValIrqMask &= ~(Y2_IS_STAT_BMU);
SK_OUT32(pAC->IoBase, B0_IMSK, pAC->GIni.GIValIrqMask);
}
__netif_rx_schedule(pAC->dev[1]);
}
#else
handledStatLE = HandleStatusLEs(pAC);
#endif
/*
** Check for Special Interrupts
*/
if ((IntSrc & ~Y2_IS_STAT_BMU) || pAC->CheckQueue || pNet->TimerExpired) {
pAC->CheckQueue = SK_FALSE;
spin_lock_irqsave(&pAC->SlowPathLock, Flags);
SkGeSirqIsr(pAC, pAC->IoBase, IntSrc);
SkEventDispatcher(pAC, pAC->IoBase);
spin_unlock_irqrestore(&pAC->SlowPathLock, 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
*/
#ifndef CONFIG_SK98LIN_NAPI
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);
}
#endif
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);
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("<== SkY2Isr\n"));
return;
} /* 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;
#if 0 /* uboot */
SK_FRAG *PrevFrag;
SK_FRAG *CurrFrag;
#endif
SK_PKT_QUEUE *pWorkQueue; /* corresponding TX queue */
SK_PKT_QUEUE *pWaitQueue;
SK_PKT_QUEUE *pFreeQueue;
SK_LE_TABLE *pLETab; /* corresponding LETable */
#if 0 /* uboot */
skb_frag_t *sk_frag;
#endif
SK_U64 PhysAddr;
unsigned long Flags;
unsigned int Port;
#if 0 /* uboot */
int CurrFragCtr;
#endif
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("==> SkY2Xmit\n"));
/*
** Get port and return if no free packet is available
*/
Port = (pAC->RlmtNets == 2) ? pNet->PortNr : pAC->ActivePort;
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);
#if 0 /* uboot */
/*
** 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! */
}
#endif
/*
** 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
*/
#if 0 /* uboot */
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);
#else
PhysAddr = (SK_U64)(SK_U32)skb->data;
#endif
pSkPacket->pMBuf = skb;
pSkPacket->pFrag->pPhys = PhysAddr;
pSkPacket->pFrag->FragLen = skb->len;/*skb_headlen(skb);*/
pSkPacket->pFrag->pNext = NULL; /* initial has no next default */
pSkPacket->NumFrags = 2; /*skb_shinfo(skb)->nr_frags + 1;*/
#if 0
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;
}
#endif
/*
** Add packet to waiting packets queue
*/
PUSH_PKT_AS_LAST_IN_QUEUE(pWaitQueue, pSkPacket);
GiveTxBufferToHw(pAC, pAC->IoBase, Port);
#if 0 /* uboot */
dev->trans_start = jiffies;
#endif
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? */
{
SK_AC *pAC = ((DEV_NET*)(dev->priv))->pAC;
int WorkToDo = min(*budget, dev->quota);
int WorkDone = 0;
SK_BOOL handledStatLE = SK_FALSE;
unsigned long Flags;
spin_lock_irqsave(&pAC->SlowPathLock, Flags);
handledStatLE = HandleStatusLEs(pAC, &WorkDone, WorkToDo);
*budget -= WorkDone;
dev->quota -= WorkDone;
if(WorkDone < WorkToDo) {
netif_rx_complete(dev);
pAC->GIni.GIValIrqMask |= (Y2_IS_STAT_BMU);
SK_OUT32(pAC->IoBase, B0_IMSK, pAC->GIni.GIValIrqMask);
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);
}
}
spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
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;
// pAC->GIni.GP[Port].PState = SK_PRT_STOP;
/*
** 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.)
*/
if (SkGeInitPort(pAC, IoC, Port)) {
if (Port == 0) {
printk("%s: SkGeInitPort A failed.\n",pAC->dev[0]->name);
} else {
printk("%s: SkGeInitPort B failed.\n",pAC->dev[1]->name);
}
}
if (IS_GMAC(pAC)) {
/* disable Rx GMAC FIFO Flush Mode */
SK_OUT8(IoC, MR_ADDR(Port, RX_GMF_CTRL_T), (SK_U8) GMF_RX_F_FL_OFF);
}
/*
** Initialize the MC addresses
*/
SkAddrMcUpdate(pAC,IoC, Port);
SkMacRxTxEnable(pAC, IoC,Port);
if (pAC->RxPort[Port].UseRxCsum) {
SkGeRxCsum(pAC, IoC, Port, SK_TRUE);
GET_RX_LE(pLE, &pAC->RxPort[Port].RxLET);
RXLE_SET_STACS1(pLE, pAC->CsOfs1);
RXLE_SET_STACS2(pLE, pAC->CsOfs2);
RXLE_SET_CTRL(pLE, 0);
RXLE_SET_OPC(pLE, OP_TCPSTART | HW_OWNER);
FLUSH_OPC(pLE);
if (Port == 0) {
PrefetchReg=Y2_PREF_Q_ADDR(Q_R1,PREF_UNIT_PUT_IDX_REG);
} else {
PrefetchReg=Y2_PREF_Q_ADDR(Q_R2,PREF_UNIT_PUT_IDX_REG);
}
DWord = GET_PUT_IDX(&pAC->RxPort[Port].RxLET);
SK_OUT32(IoC, PrefetchReg, DWord);
UPDATE_HWPUT_IDX(&pAC->RxPort[Port].RxLET);
}
pAC->GIni.GP[Port].PState = SK_PRT_RUN;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== SkY2PortStart()\n"));
}
/******************************************************************************
*
* Local Functions
*
*****************************************************************************/
/*****************************************************************************
*
* InitPacketQueues - initialize SW settings of packet queues
*
* Description:
* This function will initialize the packet queues for a port.
*
* Returns: N/A
*/
static void InitPacketQueues(
SK_AC *pAC, /* pointer to adapter control context */
int Port) /* index of port to be initialized */
{
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("==> InitPacketQueues(Port %c)\n", 'A' + Port));
pAC->RxPort[Port].RxQ_working.pHead = NULL;
pAC->RxPort[Port].RxQ_working.pTail = NULL;
spin_lock_init(&pAC->RxPort[Port].RxQ_working.QueueLock);
pAC->RxPort[Port].RxQ_waiting.pHead = NULL;
pAC->RxPort[Port].RxQ_waiting.pTail = NULL;
spin_lock_init(&pAC->RxPort[Port].RxQ_waiting.QueueLock);
pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free.pHead = NULL;
pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free.pTail = NULL;
spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free.QueueLock);
pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working.pHead = NULL;
pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working.pTail = NULL;
spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working.QueueLock);
pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting.pHead = NULL;
pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting.pTail = NULL;
spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting.QueueLock);
#if USE_SYNC_TX_QUEUE
pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_working.pHead = NULL;
pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_working.pTail = NULL;
spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_working.QueueLock);
pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_waiting.pHead = NULL;
pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_waiting.pTail = NULL;
spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_waiting.QueueLock);
#endif
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("<== InitPacketQueues(Port %c)\n", 'A' + Port));
} /* InitPacketQueues */
/*****************************************************************************
*
* GiveTxBufferToHw - commits a previously allocated DMA area to HW
*
* Description:
* This functions gives transmit buffers to HW. If no list elements
* are available the buffers will be queued.
*
* Notes:
* This function can run only once in a system at one time.
*
* Returns: N/A
*/
static void GiveTxBufferToHw(
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context (address of registers) */
int Port) /* port index for which the buffer is used */
{
SK_HWLE *pLE;
SK_PACKET *pSkPacket;
SK_FRAG *pFrag;
SK_PKT_QUEUE *pWorkQueue; /* corresponding TX queue */
SK_PKT_QUEUE *pWaitQueue;
SK_LE_TABLE *pLETab; /* corresponding LETable */
SK_BOOL SetOpcodePacketFlag;
SK_U32 HighAddress;
SK_U32 LowAddress;
#if 0 //uboot
SK_U16 TcpSumStart;
SK_U16 TcpSumWrite;
#endif
SK_U8 OpCode;
SK_U8 Ctrl;
unsigned long Flags;
unsigned long LockFlag;
#if 0 //uboot
int Protocol;
#endif
#ifdef NETIF_F_TSO
SK_U16 Mss;
int TcpOptLen;
int IpTcpLen;
#endif
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("==> GiveTxBufferToHw\n"));
if (IS_Q_EMPTY(&(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting))) {
return;
}
spin_lock_irqsave(&pAC->TxQueueLock, LockFlag);
/*
** Initialize queue settings
*/
pWorkQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working);
pWaitQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting);
pLETab = &(pAC->TxPort[Port][TX_PRIO_LOW].TxALET);
POP_FIRST_PKT_FROM_QUEUE(pWaitQueue, pSkPacket);
while (pSkPacket != NULL) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("\tWe have a packet to send %p\n", pSkPacket));
/*
** the first frag of a packet gets opcode OP_PACKET
*/
SetOpcodePacketFlag = SK_TRUE;
pFrag = pSkPacket->pFrag;
/*
** fill list elements with data from fragments
*/
while (pFrag != NULL) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("\tGet LE\n"));
#ifdef NETIF_F_TSO
Mss = skb_shinfo(pSkPacket->pMBuf)->tso_size;
if (Mss) {
TcpOptLen = ((pSkPacket->pMBuf->h.th->doff - 5) * 4);
IpTcpLen = ((pSkPacket->pMBuf->nh.iph->ihl * 4) +
sizeof(struct tcphdr));
Mss += (TcpOptLen + IpTcpLen + C_LEN_ETHERMAC_HEADER);
}
if (pLETab->Bmu.RxTx.MssValue != Mss) {
pLETab->Bmu.RxTx.MssValue = Mss;
/* Take a new LE for TSO from the table */
GET_TX_LE(pLE, pLETab);
#if 0
if(pSkPacket->VlanId) {
TXLE_SET_OPC(pLE, OP_LRGLENVLAN | HW_OWNER);
TXLE_SET_VLAN(pLE, pSkPacket->VlanId);
pSkPacket->VlanId = 0;
Ctrl |= INS_VLAN;
} else {
#endif
TXLE_SET_OPC(pLE, OP_LRGLEN | HW_OWNER);
#if 0
}
#endif
/* set maximum segment size for new packet */
TXLE_SET_LSLEN(pLE, pLETab->Bmu.RxTx.MssValue);
FLUSH_OPC(pLE) ;
}
#endif
GET_TX_LE(pLE, pLETab);
Ctrl = 0;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("\tGot empty LE %p idx %d\n", pLE, GET_PUT_IDX(pLETab)));
SK_DBG_DUMP_TX_LE(pLE);
LowAddress = (SK_U32) (pFrag->pPhys & 0xffffffff);
HighAddress = (SK_U32) (pFrag->pPhys >> 32);
if (HighAddress != pLETab->BufHighAddr) {
/* set opcode high part of the address in one LE */
OpCode = OP_ADDR64 | HW_OWNER;
/* Set now the 32 high bits of the address */
TXLE_SET_ADDR( pLE, HighAddress);
/* Set the opcode into the LE */
TXLE_SET_OPC(pLE, OpCode);
/* Flush the LE to memory */
FLUSH_OPC(pLE);
/* remember the HighAddress we gave to the Hardware */
pLETab->BufHighAddr = HighAddress;
/* get a new LE because we filled one with high address */
GET_TX_LE(pLE, pLETab);
}
#if 0 /* uboot */
/*
** TCP checksum offload
*/
if ((pSkPacket->pMBuf->ip_summed == CHECKSUM_HW) &&
(SetOpcodePacketFlag == SK_TRUE)) {
Protocol = ((SK_U8)pSkPacket->pMBuf->data[C_OFFSET_IPPROTO] & 0xff);
/* if (Protocol & C_PROTO_ID_IP) { Ctrl = 0; } */
if (Protocol & C_PROTO_ID_TCP) {
Ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
/* TCP Checksum Calculation Start Position */
TcpSumStart = C_LEN_ETHERMAC_HEADER + IP_HDR_LEN;
/* TCP Checksum Write Position */
TcpSumWrite = TcpSumStart + TCP_CSUM_OFFS;
} else {
Ctrl = UDPTCP | CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
/* TCP Checksum Calculation Start Position */
TcpSumStart = ETHER_MAC_HDR_LEN + IP_HDR_LEN;
/* UDP Checksum Write Position */
TcpSumWrite = TcpSumStart + UDP_CSUM_OFFS;
}
if ((Ctrl) && (pLETab->Bmu.RxTx.TcpWp != TcpSumWrite)) {
/* Update the last value of the write position */
pLETab->Bmu.RxTx.TcpWp = TcpSumWrite;
/* Set the Lock field for this LE: */
/* Checksum calculation for one packet only */
TXLE_SET_LCKCS(pLE, 1);
/* Set the start position for checksum. */
TXLE_SET_STACS(pLE, TcpSumStart);
/* Set the position where the checksum will be writen */
TXLE_SET_WRICS(pLE, TcpSumWrite);
/* Set the initial value for checksum */
/* PseudoHeader CS passed from Linux -> 0! */
TXLE_SET_INICS(pLE, 0);
/* Set the opcode for tcp checksum */
TXLE_SET_OPC(pLE, OP_TCPLISW | HW_OWNER);
/* Flush the LE to memory */
FLUSH_OPC(pLE);
/* get a new LE because we filled one with data for checksum */
GET_TX_LE(pLE, pLETab);
}
} /* end TCP offload handling */
#endif
TXLE_SET_ADDR(pLE, LowAddress);
TXLE_SET_LEN(pLE, pFrag->FragLen);
if (SetOpcodePacketFlag){
#ifdef NETIF_F_TSO
if (Mss) {
OpCode = OP_LARGESEND | HW_OWNER;
} else {
#endif
OpCode = OP_PACKET| HW_OWNER;
#ifdef NETIF_F_TSO
}
#endif
SetOpcodePacketFlag = SK_FALSE;
} else {
/* Follow packet in a sequence has always OP_BUFFER */
OpCode = OP_BUFFER | HW_OWNER;
}
/* Check if the low address is near the upper limit. */
CHECK_LOW_ADDRESS(pLETab->BufHighAddr, LowAddress, pFrag->FragLen);
pFrag = pFrag->pNext;
if (pFrag == NULL) {
/* mark last fragment */
Ctrl |= EOP;
}
TXLE_SET_CTRL(pLE, Ctrl);
TXLE_SET_OPC(pLE, OpCode);
FLUSH_OPC(pLE);
SK_DBG_DUMP_TX_LE(pLE);
}
/*
** Remember next LE for tx complete
*/
pSkPacket->NextLE = GET_PUT_IDX(pLETab);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("\tNext LE for pkt %p is %d\n", pSkPacket, pSkPacket->NextLE));
/*
** Add packet to working packets queue
*/
PUSH_PKT_AS_LAST_IN_QUEUE(pWorkQueue, pSkPacket);
/*
** give transmit start command
*/
if (HW_FEATURE(pAC, HWF_WA_DEV_42)) {
spin_lock(&pAC->SetPutIndexLock);
SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_XA1,0), &pAC->TxPort[0][0].TxALET);
spin_unlock(&pAC->SetPutIndexLock);
} else {
/* write put index */
if (Port == 0) {
SK_OUT32(pAC->IoBase,
Y2_PREF_Q_ADDR(Q_XA1,PREF_UNIT_PUT_IDX_REG),
GET_PUT_IDX(&pAC->TxPort[0][0].TxALET));
UPDATE_HWPUT_IDX(&pAC->TxPort[0][0].TxALET);
} else {
SK_OUT32(pAC->IoBase,
Y2_PREF_Q_ADDR(Q_XA2, PREF_UNIT_PUT_IDX_REG),
GET_PUT_IDX(&pAC->TxPort[1][0].TxALET));
UPDATE_HWPUT_IDX(&pAC->TxPort[1][0].TxALET);
}
}
if (IS_Q_EMPTY(&(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting))) {
break; /* get out of while */
}
POP_FIRST_PKT_FROM_QUEUE(pWaitQueue, pSkPacket);
} /* while (pSkPacket != NULL) */
spin_unlock_irqrestore(&pAC->TxQueueLock, LockFlag);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("<== GiveTxBufferToHw\n"));
return;
} /* GiveTxBufferToHw */
/***********************************************************************
*
* GiveRxBufferToHw - commits a previously allocated DMA area to HW
*
* Description:
* This functions gives receive buffers to HW. If no list elements
* are available the buffers will be queued.
*
* Notes:
* This function can run only once in a system at one time.
*
* Returns: N/A
*/
static void GiveRxBufferToHw(
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context (address of registers) */
int Port, /* port index for which the buffer is used */
SK_PACKET *pPacket) /* receive buffer(s) */
{
SK_HWLE *pLE;
SK_LE_TABLE *pLETab;
SK_BOOL Done = SK_FALSE; /* at least on LE changed? */
SK_U32 LowAddress;
SK_U32 HighAddress;
SK_U32 PrefetchReg; /* register for Put index */
unsigned NumFree;
unsigned Required;
unsigned long Flags;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("==> GiveRxBufferToHw(Port %c, Packet %p)\n", 'A' + Port, pPacket));
pLETab = &pAC->RxPort[Port].RxLET;
if (Port == 0) {
PrefetchReg = Y2_PREF_Q_ADDR(Q_R1, PREF_UNIT_PUT_IDX_REG);
} else {
PrefetchReg = Y2_PREF_Q_ADDR(Q_R2, PREF_UNIT_PUT_IDX_REG);
}
if (pPacket != NULL) {
/*
** For the time being, we have only one packet passed
** to this function which might be changed in future!
*/
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
}
/*
** now pPacket contains the very first waiting packet
*/
POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
while (pPacket != NULL) {
if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
if (NbrRxBuffersInHW >= MAX_NBR_RX_BUFFERS_IN_HW) {
PUSH_PKT_AS_FIRST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== GiveRxBufferToHw()\n"));
return;
}
NbrRxBuffersInHW++;
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("Try to add packet %p\n", pPacket));
/*
** Check whether we have enough listelements:
**
** we have to take into account that each fragment
** may need an additional list element for the high
** part of the address here I simplified it by
** using MAX_FRAG_OVERHEAD maybe it's worth to split
** this constant for Rx and Tx or to calculate the
** real number of needed LE's
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tNum %d Put %d Done %d Free %d %d\n",
pLETab->Num, pLETab->Put, pLETab->Done,
NUM_FREE_LE_IN_TABLE(pLETab),
(NUM_FREE_LE_IN_TABLE(pLETab))));
Required = pPacket->NumFrags + MAX_FRAG_OVERHEAD;
NumFree = NUM_FREE_LE_IN_TABLE(pLETab);
if (NumFree) {
NumFree--;
}
if (Required > NumFree ) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
("\tOut of LEs have %d need %d\n",
NumFree, Required));
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tWaitQueue starts with packet %p\n", pPacket));
PUSH_PKT_AS_FIRST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
if (Done) {
/*
** write Put index to BMU or Polling Unit and make the LE's
** available for the hardware
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tWrite new Put Idx\n"));
SK_OUT32(IoC, PrefetchReg, GET_PUT_IDX(pLETab));
UPDATE_HWPUT_IDX(pLETab);
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== GiveRxBufferToHw()\n"));
return;
} else {
if (!AllocAndMapRxBuffer(pAC, pPacket, Port)) {
/*
** Failure while allocating sk_buff might
** be due to temporary short of resources
** Maybe next time buffers are available.
** Until this, the packet remains in the
** RX waiting queue...
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
("Failed to allocate Rx buffer\n"));
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("WaitQueue starts with packet %p\n", pPacket));
PUSH_PKT_AS_FIRST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
if (Done) {
/*
** write Put index to BMU or Polling
** Unit and make the LE's
** available for the hardware
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tWrite new Put Idx\n"));
SK_OUT32(IoC, PrefetchReg, GET_PUT_IDX(pLETab));
UPDATE_HWPUT_IDX(pLETab);
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== GiveRxBufferToHw()\n"));
return;
}
}
Done = SK_TRUE;
LowAddress = (SK_U32) (pPacket->pFrag->pPhys & 0xffffffff);
HighAddress = (SK_U32) (pPacket->pFrag->pPhys >> 32);
if (HighAddress != pLETab->BufHighAddr) {
/* get a new LE for high address */
GET_RX_LE(pLE, pLETab);
/* Set now the 32 high bits of the address */
RXLE_SET_ADDR(pLE, HighAddress);
/* Set the control bits of the address */
RXLE_SET_CTRL(pLE, 0);
/* Set the opcode into the LE */
RXLE_SET_OPC(pLE, (OP_ADDR64 | HW_OWNER));
/* Flush the LE to memory */
FLUSH_OPC(pLE);
/* remember the HighAddress we gave to the Hardware */
pLETab->BufHighAddr = HighAddress;
}
/*
** Fill data into listelement
*/
GET_RX_LE(pLE, pLETab);
RXLE_SET_ADDR(pLE, LowAddress);
RXLE_SET_LEN(pLE, pPacket->pFrag->FragLen);
RXLE_SET_CTRL(pLE, 0);
RXLE_SET_OPC(pLE, (OP_PACKET | HW_OWNER));
FLUSH_OPC(pLE);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("=== LE filled\n"));
SK_DBG_DUMP_RX_LE(pLE);
/*
** Remember next LE for rx complete
*/
pPacket->NextLE = GET_PUT_IDX(pLETab);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tPackets Next LE is %d\n", pPacket->NextLE));
/*
** Add packet to working receive buffer queue and get
** any next packet out of the waiting queue
*/
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_working, pPacket);
if (IS_Q_EMPTY(&(pAC->RxPort[Port].RxQ_waiting))) {
break; /* get out of while processing */
}
POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tWaitQueue is empty\n"));
if (Done) {
/*
** write Put index to BMU or Polling Unit and make the LE's
** available for the hardware
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("\tWrite new Put Idx\n"));
/* 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_R1,0), pLETab);
spin_unlock_irqrestore(&pAC->SetPutIndexLock, Flags);
} else {
/* write put index */
if (Port == 0) {
SK_OUT32(IoC,
Y2_PREF_Q_ADDR(Q_R1, PREF_UNIT_PUT_IDX_REG),
GET_PUT_IDX(pLETab));
} else {
SK_OUT32(IoC,
Y2_PREF_Q_ADDR(Q_R2, PREF_UNIT_PUT_IDX_REG),
GET_PUT_IDX(pLETab));
}
/* Update put index */
UPDATE_HWPUT_IDX(pLETab);
}
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== GiveRxBufferToHw()\n"));
} /* GiveRxBufferToHw */
/***********************************************************************
*
* FillReceiveTableYukon2 - map any waiting RX buffers to HW
*
* Description:
* If the list element table contains more empty elements than
* specified this function tries to refill them.
*
* Notes:
* This function can run only once per port in a system at one time.
*
* Returns: N/A
*/
void FillReceiveTableYukon2(
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context */
int Port) /* port index of RX */
{
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("==> FillReceiveTableYukon2 (Port %c)\n", 'A' + Port));
if (NUM_FREE_LE_IN_TABLE(&pAC->RxPort[Port].RxLET) >
pAC->MaxUnusedRxLeWorking) {
/*
** Give alle waiting receive buffers down
** The queue holds all RX packets that
** need a fresh allocation of the sk_buff.
*/
if (pAC->RxPort[Port].RxQ_waiting.pHead != NULL) {
SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("Waiting queue is not empty -> give it to HW"));
GiveRxBufferToHw(pAC, IoC, Port, NULL);
}
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== FillReceiveTableYukon2 ()\n"));
} /* FillReceiveTableYukon2 */
/******************************************************************************
*
*
* HandleReceives - will pass any ready RX packet to kernel
*
* Description:
* This functions handles a received packet. It checks wether it is
* valid, updates the receive list element table and gives the receive
* buffer to Linux
*
* Notes:
* This function can run only once per port at one time in the system.
*
* Returns: N/A
*/
static SK_BOOL HandleReceives(
SK_AC *pAC, /* adapter control context */
int Port, /* port on which a packet has been received */
SK_U16 Len, /* number of bytes which was actually received */
SK_U32 FrameStatus, /* MAC frame status word */
SK_U16 Tcp1, /* first hw checksum */
SK_U16 Tcp2, /* second hw checksum */
SK_U32 Tist, /* timestamp */
SK_U16 Vlan) /* Vlan Id */
{
SK_PACKET *pSkPacket;
SK_LE_TABLE *pLETab;
SK_MBUF *pRlmtMbuf; /* buffer for giving RLMT frame */
struct sk_buff *pMsg; /* ptr to message holding frame */
#ifdef __ia64__
struct sk_buff *pNewMsg; /* used when IP aligning */
#endif
#ifdef CONFIG_SK98LIN_NAPI
SK_BOOL SlowPathLock = SK_FALSE;
#else
SK_BOOL SlowPathLock = SK_TRUE;
#endif
SK_BOOL IsGoodPkt;
SK_BOOL IsBc;
SK_BOOL IsMc;
SK_EVPARA EvPara; /* an event parameter union */
SK_I16 LenToFree; /* must be signed integer */
unsigned long Flags; /* for spin lock */
unsigned int RlmtNotifier;
#if 0 /* uboot */
unsigned short Type;
int IpFrameLength;
#endif
int FrameLength; /* total length of recvd frame */
int NumBytes;
#if 0
int Result;
#endif
int Offset = 0;
#ifdef Y2_SYNC_CHECK
#if 0
SK_U16 MyTcp;
#endif
#endif
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("==> HandleReceives (Port %c)\n", 'A' + Port));
/*
** initialize vars for selected port
*/
pLETab = &pAC->RxPort[Port].RxLET;
/*
** check whether we want to receive this packet
*/
SK_Y2_RXSTAT_CHECK_PKT(Len, FrameStatus, IsGoodPkt);
/*
** Remember length to free (in case of RxBuffer overruns;
** unlikely, but might happen once in a while)
*/
LenToFree = (SK_I16) Len;
/*
** maybe we put these two checks into the SK_RXDESC_CHECK_PKT macro too
*/
if (Len > pAC->RxPort[Port].RxBufSize) {
IsGoodPkt = SK_FALSE;
}
/*
** take first receive buffer out of working queue
*/
POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
if (pSkPacket == NULL) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_ERROR,
("Packet not available. NULL pointer.\n"));
return(SK_TRUE);
}
if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
NbrRxBuffersInHW--;
}
/*
** Verify the received length of the frame! Note that having
** multiple RxBuffers being aware of one single receive packet
** (one packet spread over multiple RxBuffers) is not supported
** by this driver!
*/
if ((Len > pAC->RxPort[Port].RxBufSize) ||
(Len > (SK_U16) pSkPacket->PacketLen)) {
IsGoodPkt = SK_FALSE;
}
/*
** Reset own bit in LE's between old and new Done index
** This is not really necessary but makes debugging easier
*/
CLEAR_LE_OWN_FROM_DONE_TO(pLETab, pSkPacket->NextLE);
/*
** Free the list elements for new Rx buffers
*/
SET_DONE_INDEX(pLETab, pSkPacket->NextLE);
pMsg = pSkPacket->pMBuf;
FrameLength = Len;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("Received frame of length %d on port %d\n",FrameLength, Port));
if (!IsGoodPkt) {
printf("\npacket is not good\n");
/*
** release the DMA mapping
*/
pci_dma_sync_single(pAC->PciDev,
(dma_addr_t) pSkPacket->pFrag->pPhys,
pSkPacket->pFrag->FragLen,
PCI_DMA_FROMDEVICE);
DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== HandleReceives (Port %c)\n", 'A' + Port));
/*
** Sanity check for RxBuffer overruns...
*/
LenToFree = LenToFree - (pSkPacket->pFrag->FragLen);
while (LenToFree > 0) {
POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
NbrRxBuffersInHW--;
}
CLEAR_LE_OWN_FROM_DONE_TO(pLETab, pSkPacket->NextLE);
SET_DONE_INDEX(pLETab, pSkPacket->NextLE);
pci_dma_sync_single(pAC->PciDev,
(dma_addr_t) pSkPacket->pFrag->pPhys,
pSkPacket->pFrag->FragLen,
PCI_DMA_FROMDEVICE);
DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
LenToFree = LenToFree - ((SK_I16)(pSkPacket->pFrag->FragLen));
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
("<==HandleReceives (Port %c) drop faulty len pkt(2)\n",'A'+Port));
}
return(SK_TRUE);
} else {
/*
** Release the DMA mapping
*/
pci_unmap_single(pAC->PciDev,
pSkPacket->pFrag->pPhys,
pAC->RxPort[Port].RxBufSize,
PCI_DMA_FROMDEVICE);
#if 0 /* uboot */
skb_put(pMsg, FrameLength); /* set message len */
pMsg->ip_summed = CHECKSUM_NONE; /* initial default */
#ifdef Y2_SYNC_CHECK
pAC->FramesWithoutSyncCheck++;
if (pAC->FramesWithoutSyncCheck > Y2_RESYNC_WATERMARK) {
if ((Tcp1 != 1) || (Tcp2 != 0)) {
pAC->FramesWithoutSyncCheck = 0;
MyTcp = (SK_U16) SkCsCalculateChecksum(
&pMsg->data[14],
FrameLength - 14);
if (MyTcp != Tcp1) {
/* Queue port reset event */
SkLocalEventQueue(pAC, SKGE_DRV,
SK_DRV_RECOVER,Port,-1,SK_FALSE);
}
}
}
#endif
#endif
#if 0 /* uboot */
pMsg->ip_summed = CHECKSUM_NONE; /* initial default */
if (pAC->RxPort[Port].UseRxCsum) {
Type = ntohs(*((short*)&pMsg->data[12]));
if (Type == 0x800) {
*((char *)&(IpFrameLength)) = pMsg->data[16];
*(((char *)&(IpFrameLength))+1) = pMsg->data[17];
IpFrameLength = ntohs(IpFrameLength);
HeaderLength = FrameLength - IpFrameLength;
if (HeaderLength == 0xe) {
Result =
SkCsGetReceiveInfo(pAC,&pMsg->data[14],Tcp1,Tcp2, Port);
if ((Result == SKCS_STATUS_IP_FRAGMENT) ||
(Result == SKCS_STATUS_IP_CSUM_OK) ||
(Result == SKCS_STATUS_TCP_CSUM_OK) ||
(Result == SKCS_STATUS_UDP_CSUM_OK)) {
pMsg->ip_summed = CHECKSUM_UNNECESSARY;
} else if ((Result == SKCS_STATUS_TCP_CSUM_ERROR) ||
(Result == SKCS_STATUS_UDP_CSUM_ERROR) ||
(Result == SKCS_STATUS_IP_CSUM_ERROR_UDP) ||
(Result == SKCS_STATUS_IP_CSUM_ERROR_TCP) ||
(Result == SKCS_STATUS_IP_CSUM_ERROR)) {
SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
("skge: CRC error. Frame dropped!\n"));
DEV_KFREE_SKB_ANY(pMsg);
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
SK_DBG_MSG(pAC,SK_DBGMOD_DRV,SK_DBGCAT_DRV_RX_PROGRESS,
("<==HandleReceives(Port %c)\n",'A'+Port));
return(SK_TRUE);
} else {
pMsg->ip_summed = CHECKSUM_NONE;
}
} /* end if (HeaderLength == valid) */
} /* end if (Type == 0x800) -> IP frame */
} /* end if (pRxPort->UseRxCsum) */
#endif
SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS,("V"));
RlmtNotifier = SK_RLMT_RX_PROTOCOL;
IsBc = (FrameStatus & GMR_FS_BC) ? SK_TRUE : SK_FALSE;
SK_RLMT_PRE_LOOKAHEAD(pAC,Port,FrameLength,
IsBc,&Offset,&NumBytes);
if (NumBytes != 0) {
IsMc = (FrameStatus & GMR_FS_MC) ? SK_TRUE : SK_FALSE;
SK_RLMT_LOOKAHEAD(pAC,Port,&pMsg->data[Offset],
IsBc,IsMc,&RlmtNotifier);
}
if (RlmtNotifier == SK_RLMT_RX_PROTOCOL) {
SK_DBG_MSG(NULL,SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS,("W"));
if ((Port == pAC->ActivePort)||(pAC->RlmtNets == 2)) {
/* send up only frames from active port */
SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS,("U"));
#ifdef xDEBUG
DumpMsg(pMsg, "Rx");
#endif
SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC,
FrameLength, Port);
#ifdef __ia64__
pNewMsg = alloc_skb(pMsg->len, GFP_ATOMIC);
skb_reserve(pNewMsg, 2); /* to align IP */
SK_MEMCPY(pNewMsg->data,pMsg->data,pMsg->len);
pNewMsg->ip_summed = pMsg->ip_summed;
skb_put(pNewMsg, pMsg->len);
DEV_KFREE_SKB_ANY(pMsg);
pMsg = pNewMsg;
#endif
#if 0 /* uboot */
pMsg->dev = pAC->dev[Port];
pMsg->protocol = eth_type_trans(pMsg,
pAC->dev[Port]);
netif_rx(pMsg);
pAC->dev[Port]->last_rx = jiffies;
#else /* Marvell - uboot */
NetReceive(pMsg->data, FrameLength);
DEV_KFREE_SKB_ANY(pMsg);
#endif
} else { /* drop frame */
SK_DBG_MSG(NULL,SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS,("D"));
DEV_KFREE_SKB_ANY(pMsg);
}
} else { /* This is an RLMT-packet! */
SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS,("R"));
pRlmtMbuf = SkDrvAllocRlmtMbuf(pAC,
pAC->IoBase, FrameLength);
if (pRlmtMbuf != NULL) {
pRlmtMbuf->pNext = NULL;
pRlmtMbuf->Length = FrameLength;
pRlmtMbuf->PortIdx = Port;
EvPara.pParaPtr = pRlmtMbuf;
SK_MEMCPY((char*)(pRlmtMbuf->pData),
(char*)(pMsg->data),FrameLength);
if (SlowPathLock == SK_TRUE) {
spin_lock_irqsave(&pAC->SlowPathLock, Flags);
SkEventQueue(pAC, SKGE_RLMT,
SK_RLMT_PACKET_RECEIVED,
EvPara);
pAC->CheckQueue = SK_TRUE;
spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
} else {
SkEventQueue(pAC, SKGE_RLMT,
SK_RLMT_PACKET_RECEIVED,
EvPara);
pAC->CheckQueue = SK_TRUE;
}
SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS,("Q"));
}
#if 0 /* uboot */
if (pAC->dev[Port]->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
#ifdef __ia64__
pNewMsg = alloc_skb(pMsg->len, GFP_ATOMIC);
skb_reserve(pNewMsg, 2); /* to align IP */
SK_MEMCPY(pNewMsg->data,pMsg->data,pMsg->len);
pNewMsg->ip_summed = pMsg->ip_summed;
pNewMsg->len = pMsg->len;
DEV_KFREE_SKB_ANY(pMsg);
pMsg = pNewMsg;
#endif
pMsg->dev = pAC->dev[Port];
pMsg->protocol = eth_type_trans(pMsg,pAC->dev[Port]);
netif_rx(pMsg);
pAC->dev[Port]->last_rx = jiffies;
} else
#else /* Marvell - uboot*/
if (1) {
NetReceive(pMsg->data, FrameLength);
DEV_KFREE_SKB_ANY(pMsg);
} else
#endif
{
DEV_KFREE_SKB_ANY(pMsg);
}
} /* if packet for rlmt */
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
} /* end if-else (IsGoodPkt) */
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<== HandleReceives (Port %c)\n", 'A' + Port));
return(SK_TRUE);
} /* HandleReceives */
/***********************************************************************
*
* CheckForSendComplete - Frees any freeable Tx bufffer
*
* Description:
* This function checks the queues of a port for completed send
* packets and returns these packets back to the OS.
*
* Notes:
* This function can run simultaneously for both ports if
* the OS function OSReturnPacket() can handle this,
*
* Such a send complete does not mean, that the packet is really
* out on the wire. We just know that the adapter has copied it
* into its internal memory and the buffer in the systems memory
* is no longer needed.
*
* Returns: N/A
*/
static void CheckForSendComplete(
SK_AC *pAC, /* pointer to adapter control context */
SK_IOC IoC, /* I/O control context */
int Port, /* port index */
SK_PKT_QUEUE *pPQ, /* tx working packet queue to check */
SK_LE_TABLE *pLETab, /* corresponding list element table */
unsigned int Done) /* done index reported for this LET */
{
SK_PACKET *pSkPacket;
SK_PKT_QUEUE SendCmplPktQ = { NULL, NULL /* , SPIN_LOCK_UNLOCKED uboot */};
SK_BOOL DoWakeQueue = SK_FALSE;
unsigned long Flags;
unsigned Put;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("==> CheckForSendComplete(Port %c)\n", 'A' + Port));
/*
** Reset own bit in LE's between old and new Done index
** This is not really necessairy but makes debugging easier
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Clear Own Bits in TxTable from %d to %d\n",
pLETab->Done, (Done == 0) ?
NUM_LE_IN_TABLE(pLETab) :
(Done - 1)));
spin_lock_irqsave(&(pPQ->QueueLock), Flags);
CLEAR_LE_OWN_FROM_DONE_TO(pLETab, Done);
Put = GET_PUT_IDX(pLETab);
/*
** Check whether some packets have been completed
*/
PLAIN_POP_FIRST_PKT_FROM_QUEUE(pPQ, pSkPacket);
while (pSkPacket != NULL) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Check Completion of Tx packet %p\n", pSkPacket));
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Put %d NewDone %d NextLe of Packet %d\n", Put, Done,
pSkPacket->NextLE));
if ((Put > Done) &&
((pSkPacket->NextLE > Put) || (pSkPacket->NextLE <= Done))) {
PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Packet finished (a)\n"));
} else if ((Done > Put) &&
(pSkPacket->NextLE > Put) && (pSkPacket->NextLE <= Done)) {
PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Packet finished (b)\n"));
} else if ((Done == TXA_MAX_LE-1) && (Put == 0) && (pSkPacket->NextLE == 0)) {
PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Packet finished (b)\n"));
DoWakeQueue = SK_TRUE;
} else if (Done == Put) {
/* all packets have been sent */
PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Packet finished (c)\n"));
} else {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("Packet not yet finished\n"));
PLAIN_PUSH_PKT_AS_FIRST_IN_QUEUE(pPQ, pSkPacket);
break;
}
PLAIN_POP_FIRST_PKT_FROM_QUEUE(pPQ, pSkPacket);
}
spin_unlock_irqrestore(&(pPQ->QueueLock), Flags);
/*
** Set new done index in list element table
*/
SET_DONE_INDEX(pLETab, Done);
/*
** All TX packets that are send complete should be added to
** the free queue again for new sents to come
*/
pSkPacket = SendCmplPktQ.pHead;
while (pSkPacket != NULL) {
while (pSkPacket->pFrag != NULL) {
pci_unmap_page(pAC->PciDev,
(dma_addr_t) pSkPacket->pFrag->pPhys,
pSkPacket->pFrag->FragLen,
PCI_DMA_FROMDEVICE);
pSkPacket->pFrag = pSkPacket->pFrag->pNext;
}
DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
pSkPacket->pMBuf = NULL;
pSkPacket = pSkPacket->pNext; /* get next packet */
}
/*
** Append the available TX packets back to free queue
*/
if (SendCmplPktQ.pHead != NULL) {
spin_lock_irqsave(&(pAC->TxPort[Port][0].TxQ_free.QueueLock), Flags);
if (pAC->TxPort[Port][0].TxQ_free.pTail != NULL) {
pAC->TxPort[Port][0].TxQ_free.pTail->pNext = SendCmplPktQ.pHead;
pAC->TxPort[Port][0].TxQ_free.pTail = SendCmplPktQ.pTail;
if (pAC->TxPort[Port][0].TxQ_free.pHead->pNext == NULL) {
netif_wake_queue(pAC->dev[Port]);
}
} else {
pAC->TxPort[Port][0].TxQ_free.pHead = SendCmplPktQ.pHead;
pAC->TxPort[Port][0].TxQ_free.pTail = SendCmplPktQ.pTail;
netif_wake_queue(pAC->dev[Port]);
}
if (Done == Put) {
netif_wake_queue(pAC->dev[Port]);
}
if (DoWakeQueue) {
netif_wake_queue(pAC->dev[Port]);
DoWakeQueue = SK_FALSE;
}
spin_unlock_irqrestore(&pAC->TxPort[Port][0].TxQ_free.QueueLock, Flags);
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("<== CheckForSendComplete()\n"));
return;
} /* CheckForSendComplete */
/*****************************************************************************
*
* UnmapAndFreeTxPktBuffer
*
* Description:
* This function free any allocated space of receive buffers
*
* Arguments:
* pAC - A pointer to the adapter context struct.
*
*/
static void UnmapAndFreeTxPktBuffer(
SK_AC *pAC, /* pointer to adapter context */
SK_PACKET *pSkPacket, /* pointer to port struct of ring to fill */
int TxPort) /* TX port index */
{
SK_FRAG *pFrag = pSkPacket->pFrag;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("--> UnmapAndFreeTxPktBuffer\n"));
while (pFrag != NULL) {
pci_unmap_page(pAC->PciDev,
(dma_addr_t) pFrag->pPhys,
pFrag->FragLen,
PCI_DMA_FROMDEVICE);
pFrag = pFrag->pNext;
}
DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
pSkPacket->pMBuf = NULL;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
("<-- UnmapAndFreeTxPktBuffer\n"));
}
/*****************************************************************************
*
* HandleStatusLEs
*
* Description:
* This function checks for any new status LEs that may have been
* received. Those status LEs may either be Rx or Tx ones.
*
* Returns: N/A
*/
static SK_BOOL HandleStatusLEs(
#ifdef CONFIG_SK98LIN_NAPI
SK_AC *pAC, /* pointer to adapter context */
int *WorkDone, /* Done counter needed for NAPI */
int WorkToDo) /* ToDo counter for NAPI */
#else
SK_AC *pAC) /* pointer to adapter context */
#endif
{
int DoneTxA[SK_MAX_MACS];
int DoneTxS[SK_MAX_MACS];
int Port;
SK_BOOL handledStatLE = SK_FALSE;
SK_BOOL NewDone = SK_FALSE;
SK_HWLE *pLE;
SK_U16 HighVal;
SK_U32 LowVal;
SK_U8 OpCode;
int i;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("==> HandleStatusLEs\n"));
do {
if (OWN_OF_FIRST_LE(&pAC->StatusLETable) != HW_OWNER)
break;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Check next Own Bit of ST-LE[%d]: 0x%li \n",
(pAC->StatusLETable.Done + 1) % NUM_LE_IN_TABLE(&pAC->StatusLETable),
OWN_OF_FIRST_LE(&pAC->StatusLETable)));
while (OWN_OF_FIRST_LE(&pAC->StatusLETable) == HW_OWNER) {
GET_ST_LE(pLE, &pAC->StatusLETable);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Working on finished status LE[%d]:\n",
GET_DONE_INDEX(&pAC->StatusLETable)));
SK_DBG_DUMP_ST_LE(pLE);
handledStatLE = SK_TRUE;
OpCode = STLE_GET_OPC(pLE) & ~HW_OWNER;
Port = STLE_GET_LINK(pLE);
#ifdef USE_TIST_FOR_RESET
if (SK_ADAPTER_WAITING_FOR_TIST(pAC)) {
/* do we just have a tist LE ? */
if ((OpCode & OP_RXTIMESTAMP) == OP_RXTIMESTAMP) {
for (i = 0; i < pAC->GIni.GIMacsFound; i++) {
if (SK_PORT_WAITING_FOR_ANY_TIST(pAC, i)) {
/* if a port is waiting for any tist it is done */
SK_CLR_STATE_FOR_PORT(pAC, i);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Got any Tist on port %c (now 0x%X!!!)\n",
'A' + i, pAC->AdapterResetState));
}
if (SK_PORT_WAITING_FOR_SPECIFIC_TIST(pAC, i)) {
Y2_GET_TIST_LOW_VAL(pAC->IoBase, &LowVal);
if ((pAC->MinTistHi != pAC->GIni.GITimeStampCnt) ||
(pAC->MinTistLo < LowVal)) {
/* time is up now */
SK_CLR_STATE_FOR_PORT(pAC, i);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Got expected Tist on Port %c (now 0x%X)!!!\n",
'A' + i, pAC->AdapterResetState));
#ifdef Y2_SYNC_CHECK
pAC->FramesWithoutSyncCheck =
Y2_RESYNC_WATERMARK;
#endif
} else {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Got Tist %l:%l on Port %c but still waiting\n",
pAC->GIni.GITimeStampCnt, pAC->MinTistLo,
'A' + i));
}
}
}
#ifndef Y2_RECOVERY
if (!SK_ADAPTER_WAITING_FOR_TIST(pAC)) {
/* nobody needs tist anymore - turn it off */
Y2_DISABLE_TIST(pAC->IoBase);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Turn off Tist !!!\n"));
}
#endif
} else if (OpCode == OP_TXINDEXLE) {
/*
* change OpCode to notify the folowing code
* to ignore the done index from this LE
* unfortunately tist LEs will be generated only
* for RxStat LEs
* so in order to get a safe Done index for a
* port currently waiting for a tist we have to
* get the done index directly from the BMU
*/
OpCode = OP_MOD_TXINDEX;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Mark unusable TX_INDEX LE!!!\n"));
} else {
if (SK_PORT_WAITING_FOR_TIST(pAC, Port)) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Ignore LE 0x%X on Port %c!!!\n",
OpCode, 'A' + Port));
OpCode = OP_MOD_LE;
#ifdef Y2_LE_CHECK
/* mark entries invalid */
pAC->LastOpc = 0xFF;
pAC->LastPort = 3;
#endif
}
}
} /* if (SK_ADAPTER_WAITING_FOR_TIST(pAC)) */
#endif
#ifdef Y2_LE_CHECK
if (pAC->LastOpc != 0xFF) {
/* last opc is valid
* check if current opcode follows last opcode
*/
if ((((OpCode & OP_RXTIMESTAMP) == OP_RXTIMESTAMP) && (pAC->LastOpc != OP_RXSTAT)) ||
(((OpCode & OP_RXCHKS) == OP_RXCHKS) && (pAC->LastOpc != OP_RXTIMESTAMP)) ||
((OpCode == OP_RXSTAT) && (pAC->LastOpc != OP_RXCHKS))) {
/* opcode sequence broken
* current LE is invalid
*/
if (pAC->LastOpc == OP_RXTIMESTAMP) {
/* force invalid checksum */
pLE->St.StUn.StRxTCPCSum.RxTCPSum1 = 1;
pLE->St.StUn.StRxTCPCSum.RxTCPSum2 = 0;
OpCode = pAC->LastOpc = OP_RXCHKS;
Port = pAC->LastPort;
} else if (pAC->LastOpc == OP_RXCHKS) {
/* force invalid frame */
Port = pAC->LastPort;
pLE->St.Stat.BufLen = 64;
pLE->St.StUn.StRxStatWord = GMR_FS_CRC_ERR;
OpCode = pAC->LastOpc = OP_RXSTAT;
#ifdef Y2_SYNC_CHECK
/* force rx sync check */
pAC->FramesWithoutSyncCheck = Y2_RESYNC_WATERMARK;
#endif
} else if (pAC->LastOpc == OP_RXSTAT) {
/* create dont care tist */
pLE->St.StUn.StRxTimeStamp = 0;
OpCode = pAC->LastOpc = OP_RXTIMESTAMP;
/* dont know the port yet */
} else {
#ifdef DEBUG
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Unknown LastOpc %X for Timestamp on port %c.\n",
pAC->LastOpc, Port));
#endif
}
}
}
#endif
switch (OpCode) {
case OP_RXSTAT:
#ifdef Y2_RECOVERY
pAC->LastOpc = OP_RXSTAT;
#endif
/*
** This is always the last Status LE belonging
** to a received packet -> handle it...
*/
if ((Port != 0) && (Port != 1)) {
/* Unknown port */
panic("sk98lin: Unknown port %d\n",
Port);
}
HandleReceives(
pAC,
Port,
STLE_GET_LEN(pLE),
STLE_GET_FRSTATUS(pLE),
pAC->StatusLETable.Bmu.Stat.TcpSum1,
pAC->StatusLETable.Bmu.Stat.TcpSum2,
pAC->StatusLETable.Bmu.Stat.RxTimeStamp,
pAC->StatusLETable.Bmu.Stat.VlanId);
#ifdef CONFIG_SK98LIN_NAPI
if (*WorkDone >= WorkToDo) {
break;
}
(*WorkDone)++;
#endif
break;
case OP_RXVLAN:
/* this value will be used for next RXSTAT */
pAC->StatusLETable.Bmu.Stat.VlanId = STLE_GET_VLAN(pLE);
break;
case OP_RXTIMEVLAN:
/* this value will be used for next RXSTAT */
pAC->StatusLETable.Bmu.Stat.VlanId = STLE_GET_VLAN(pLE);
/* fall through */
case OP_RXTIMESTAMP:
/* this value will be used for next RXSTAT */
pAC->StatusLETable.Bmu.Stat.RxTimeStamp = STLE_GET_TIST(pLE);
#ifdef Y2_RECOVERY
pAC->LastOpc = OP_RXTIMESTAMP;
pAC->LastPort = Port;
#endif
break;
case OP_RXCHKSVLAN:
/* this value will be used for next RXSTAT */
pAC->StatusLETable.Bmu.Stat.VlanId = STLE_GET_VLAN(pLE);
/* fall through */
case OP_RXCHKS:
/* this value will be used for next RXSTAT */
pAC->StatusLETable.Bmu.Stat.TcpSum1 = STLE_GET_TCP1(pLE);
pAC->StatusLETable.Bmu.Stat.TcpSum2 = STLE_GET_TCP2(pLE);
#ifdef Y2_RECOVERY
pAC->LastPort = Port;
pAC->LastOpc = OP_RXCHKS;
#endif
break;
case OP_RSS_HASH:
/* this value will be used for next RXSTAT */
#if 0
pAC->StatusLETable.Bmu.Stat.RssHashValue = STLE_GET_RSS(pLE);
#endif
break;
case OP_TXINDEXLE:
/*
** :;:; TODO
** it would be possible to check for which queues
** the index has been changed and call
** CheckForSendComplete() only for such queues
*/
STLE_GET_DONE_IDX(pLE,LowVal,HighVal);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("LowVal: 0x%x HighVal: 0x%x\n", LowVal, HighVal));
/*
** It would be possible to check whether we really
** need the values for second port or sync queue,
** but I think checking whether we need them is
** more expensive than the calculation
*/
DoneTxA[0] = STLE_GET_DONE_IDX_TXA1(LowVal,HighVal);
DoneTxS[0] = STLE_GET_DONE_IDX_TXS1(LowVal,HighVal);
DoneTxA[1] = STLE_GET_DONE_IDX_TXA2(LowVal,HighVal);
DoneTxS[1] = STLE_GET_DONE_IDX_TXS2(LowVal,HighVal);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("DoneTxa1 0x%x DoneTxS1: 0x%x DoneTxa2 0x%x DoneTxS2: 0x%x\n",
DoneTxA[0], DoneTxS[0], DoneTxA[1], DoneTxS[1]));
NewDone = SK_TRUE;
break;
#ifdef USE_TIST_FOR_RESET
case OP_MOD_TXINDEX:
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("OP_MOD_TXINDEX\n"));
SK_IN16(pAC->IoBase, Q_ADDR(Q_XA1, Q_DONE), &DoneTxA[0]);
if (pAC->GIni.GIMacsFound > 1) {
SK_IN16(pAC->IoBase, Q_ADDR(Q_XA2, Q_DONE), &DoneTxA[1]);
}
NewDone = SK_TRUE;
break;
case OP_MOD_LE:
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
("Ignore marked LE on port in Reset\n"));
break;
#endif
default:
/*
** Have to handle the illegal Opcode in Status LE
*/
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Unexpected OpCode\n"));
break;
}
#ifdef Y2_RECOVERY
OpCode = STLE_GET_OPC(pLE) & ~HW_OWNER;
STLE_SET_OPC(pLE, OpCode);
#else
/*
** Reset own bit we have to do this in order to detect a overflow
*/
STLE_SET_OPC(pLE, SW_OWNER);
#endif
} /* while (OWN_OF_FIRST_LE(&pAC->StatusLETable) == HW_OWNER) */
/*
** Now handle any new transmit complete
*/
if (NewDone) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Done Index for Tx BMU has been changed\n"));
for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
/*
** Do we have a new Done idx ?
*/
if (DoneTxA[Port] != GET_DONE_INDEX(&pAC->TxPort[Port][0].TxALET)) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Check TxA%d\n", Port + 1));
CheckForSendComplete(pAC, pAC->IoBase, Port,
&(pAC->TxPort[Port][0].TxAQ_working),
&pAC->TxPort[Port][0].TxALET,
DoneTxA[Port]);
} else {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("No changes for TxA%d\n", Port + 1));
}
#if USE_SYNC_TX_QUEUE
if (HW_SYNC_TX_SUPPORTED(pAC)) {
/*
** Do we have a new Done idx ?
*/
if (DoneTxS[Port] !=
GET_DONE_INDEX(&pAC->TxPort[Port][0].TxSLET)) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_INT_SRC,
("Check TxS%d\n", Port));
CheckForSendComplete(pAC, pAC->IoBase, Port,
&(pAC->TxPort[Port][0].TxSQ_working),
&pAC->TxPort[Port][0].TxSLET,
DoneTxS[Port]);
} else {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_INT_SRC,
("No changes for TxS%d\n", Port));
}
}
#endif
}
}
NewDone = SK_FALSE;
/*
** Check whether we have to refill our RX table
*/
if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
if (NbrRxBuffersInHW < MAX_NBR_RX_BUFFERS_IN_HW) {
for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Check for refill of RxBuffers on Port %c\n", 'A' + Port));
FillReceiveTableYukon2(pAC, pAC->IoBase, Port);
}
}
} else {
for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
("Check for refill of RxBuffers on Port %c\n", 'A' + Port));
if (NUM_FREE_LE_IN_TABLE(&pAC->RxPort[Port].RxLET) >= 64) {
FillReceiveTableYukon2(pAC, pAC->IoBase, Port);
}
}
}
#ifdef CONFIG_SK98LIN_NAPI
if (*WorkDone >= WorkToDo) {
break;
}
#endif
} while (OWN_OF_FIRST_LE(&pAC->StatusLETable) == HW_OWNER);
/*
** Clear status BMU
*/
if (handledStatLE)
SK_OUT32(pAC->IoBase, STAT_CTRL, SC_STAT_CLR_IRQ);
return(handledStatLE);
} /* HandleStatusLEs */
/*****************************************************************************
*
* AllocateAndInitLETables - allocate memory for the LETable and init
*
* Description:
* This function will allocate space for the LETable and will also
* initialize them. The size of the tables must have been specified
* before.
*
* Arguments:
* pAC - A pointer to the adapter context struct.
*
* Returns:
* SK_TRUE - all LETables initialized
* SK_FALSE - failed
*/
static SK_BOOL AllocateAndInitLETables(
SK_AC *pAC) /* pointer to adapter context */
{
char *pVirtMemAddr;
dma_addr_t pPhysMemAddr = 0;
SK_U32 CurrMac;
unsigned Size;
unsigned Aligned;
unsigned Alignment;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("==> AllocateAndInitLETables()\n"));
/*
** Determine how much memory we need with respect to alignment
*/
Alignment = MAX_LEN_OF_LE_TAB;
Size = 0;
for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
SK_ALIGN_SIZE(LE_TAB_SIZE(RX_MAX_LE), Alignment, Aligned);
Size += Aligned;
SK_ALIGN_SIZE(LE_TAB_SIZE(TXA_MAX_LE), Alignment, Aligned);
Size += Aligned;
SK_ALIGN_SIZE(LE_TAB_SIZE(TXS_MAX_LE), Alignment, Aligned);
Size += Aligned;
}
SK_ALIGN_SIZE(LE_TAB_SIZE(ST_MAX_LE), Alignment, Aligned);
Size += Aligned;
Size += Alignment;
pAC->SizeOfAlignedLETables = Size;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("Need %08x bytes in total\n", Size));
/*
** Allocate the memory
*/
pVirtMemAddr = pci_alloc_consistent(pAC->PciDev, Size, &pPhysMemAddr);
if (pVirtMemAddr == NULL) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
("AllocateAndInitLETables: kernel malloc failed!\n"));
return (SK_FALSE);
}
/*
** Initialize the memory
*/
SK_MEMSET(pVirtMemAddr, 0, Size);
ALIGN_ADDR(pVirtMemAddr, Alignment); /* Macro defined in skgew.h */
#if 1 /* Marvell - uboot */
ALIGN_ADDR(pPhysMemAddr, Alignment);
#endif
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("Virtual address of LETab is %8p!\n", pVirtMemAddr));
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("Phys address of LETab is %8p!\n", (void *) pPhysMemAddr));
for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("RxLeTable for Port %c", 'A' + CurrMac));
SkGeY2InitSingleLETable(
pAC,
&pAC->RxPort[CurrMac].RxLET,
RX_MAX_LE,
pVirtMemAddr,
(SK_U32) (pPhysMemAddr & 0xffffffff),
(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));
SK_ALIGN_SIZE(LE_TAB_SIZE(RX_MAX_LE), Alignment, Aligned);
pVirtMemAddr += Aligned;
pPhysMemAddr += Aligned;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("TxALeTable for Port %c", 'A' + CurrMac));
SkGeY2InitSingleLETable(
pAC,
&pAC->TxPort[CurrMac][0].TxALET,
TXA_MAX_LE,
pVirtMemAddr,
(SK_U32) (pPhysMemAddr & 0xffffffff),
(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));
SK_ALIGN_SIZE(LE_TAB_SIZE(TXA_MAX_LE), Alignment, Aligned);
pVirtMemAddr += Aligned;
pPhysMemAddr += Aligned;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("TxSLeTable for Port %c", 'A' + CurrMac));
SkGeY2InitSingleLETable(
pAC,
&pAC->TxPort[CurrMac][0].TxSLET,
TXS_MAX_LE,
pVirtMemAddr,
(SK_U32) (pPhysMemAddr & 0xffffffff),
(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));
SK_ALIGN_SIZE(LE_TAB_SIZE(TXS_MAX_LE), Alignment, Aligned);
pVirtMemAddr += Aligned;
pPhysMemAddr += Aligned;
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,("StLeTable"));
SkGeY2InitSingleLETable(
pAC,
&pAC->StatusLETable,
ST_MAX_LE,
pVirtMemAddr,
(SK_U32) (pPhysMemAddr & 0xffffffff),
(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("<== AllocateAndInitLETables(OK)\n"));
return(SK_TRUE);
} /* AllocateAndInitLETables */
/*****************************************************************************
*
* AllocatePacketBuffersYukon2 - allocate packet and fragment buffers
*
* Description:
* This function will allocate space for the packets and fragments
*
* Arguments:
* pAC - A pointer to the adapter context struct.
*
* Returns:
* SK_TRUE - Memory was allocated correctly
* SK_FALSE - An error occured
*/
static SK_BOOL AllocatePacketBuffersYukon2(
SK_AC *pAC) /* pointer to adapter context */
{
SK_PACKET *pRxPacket;
SK_PACKET *pTxPacket;
SK_U32 CurrBuff;
SK_U32 CurrMac;
unsigned long Flags; /* needed for POP/PUSH functions */
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("==> AllocatePacketBuffersYukon2()"));
for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
/*
** Allocate RX packet space, initialize the packets and
** add them to the RX waiting queue. Waiting queue means
** that packet and fragment are initialized, but no sk_buff
** has been assigned to it yet.
*/
pAC->RxPort[CurrMac].ReceivePacketTable =
kmalloc((RX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)), GFP_KERNEL);
if (pAC->RxPort[CurrMac].ReceivePacketTable == NULL) {
SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
("AllocatePacketBuffersYukon2: no mem RxPkts (port %i)",CurrMac));
break;
} else {
SK_MEMSET(pAC->RxPort[CurrMac].ReceivePacketTable, 0,
(RX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)));
pRxPacket = pAC->RxPort[CurrMac].ReceivePacketTable;
for (CurrBuff=0;CurrBuff<RX_MAX_NBR_BUFFERS;CurrBuff++) {
pRxPacket->pFrag = &(pRxPacket->FragArray[0]);
pRxPacket->NumFrags = 1;
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[CurrMac].RxQ_waiting, pRxPacket);
pRxPacket++;
}
}
/*
** Allocate TX packet space, initialize the packets and
** add them to the TX free queue. Free queue means that
** packet is available and initialized, but no fragment
** has been assigned to it. (Must be done at TX side)
*/
pAC->TxPort[CurrMac][0].TransmitPacketTable =
kmalloc((TX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)), GFP_KERNEL);
if (pAC->TxPort[CurrMac][0].TransmitPacketTable == NULL) {
SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
("AllocatePacketBuffersYukon2: no mem TxPkts (port %i)",CurrMac));
kfree(pAC->RxPort[CurrMac].ReceivePacketTable);
return(SK_FALSE);
} else {
SK_MEMSET(pAC->TxPort[CurrMac][0].TransmitPacketTable, 0,
(TX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)));
pTxPacket = pAC->TxPort[CurrMac][0].TransmitPacketTable;
for (CurrBuff=0;CurrBuff<TX_MAX_NBR_BUFFERS;CurrBuff++) {
PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->TxPort[CurrMac][0].TxQ_free, pTxPacket);
pTxPacket++;
}
}
} /* end for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) */
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
("<== AllocatePacketBuffersYukon2 (OK)\n"));
return(SK_TRUE);
} /* AllocatePacketBuffersYukon2 */
/*****************************************************************************
*
* FreeLETables - release allocated memory of LETables
*
* Description:
* This function will free all resources of the LETables
*
* Arguments:
* pAC - A pointer to the adapter context struct.
*
* Returns: N/A
*/
static void FreeLETables(
SK_AC *pAC) /* pointer to adapter control context */
{
dma_addr_t pPhysMemAddr;
char *pVirtMemAddr;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("==> FreeLETables()\n"));
/*
** The RxLETable is the first of all LET.
** Therefore we can use its address for the input
** of the free function.
*/
pVirtMemAddr = (char *) pAC->RxPort[0].RxLET.pLETab;
pPhysMemAddr = (((SK_U64) pAC->RxPort[0].RxLET.pPhyLETABHigh << (SK_U64) 32) |
((SK_U64) pAC->RxPort[0].RxLET.pPhyLETABLow));
/* free continuous memory */
pci_free_consistent(pAC->PciDev, pAC->SizeOfAlignedLETables,
pVirtMemAddr, pPhysMemAddr);
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== FreeLETables()\n"));
} /* FreeLETables */
/*****************************************************************************
*
* FreePacketBuffers - free's all packet buffers of an adapter
*
* Description:
* This function will free all previously allocated memory of the
* packet buffers.
*
* Arguments:
* pAC - A pointer to the adapter context struct.
*
* Returns: N/A
*/
static void FreePacketBuffers(
SK_AC *pAC) /* pointer to adapter control context */
{
int Port;
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("==> FreePacketBuffers()\n"));
for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
kfree(pAC->RxPort[Port].ReceivePacketTable);
kfree(pAC->TxPort[Port][0].TransmitPacketTable);
}
SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
("<== FreePacketBuffers()\n"));
} /* FreePacketBuffers */
/*****************************************************************************
*
* AllocAndMapRxBuffer - fill one buffer into the receive packet/fragment
*
* Description:
* The function allocates a new receive buffer and assigns it to the
* the passsed receive packet/fragment
*
* Returns:
* SK_TRUE - a buffer was allocated and assigned
* SK_FALSE - a buffer could not be added
*/
static SK_BOOL AllocAndMapRxBuffer(
SK_AC *pAC, /* pointer to the adapter control context */
SK_PACKET *pSkPacket, /* pointer to packet that is to fill */
int Port) /* port the packet belongs to */
{
struct sk_buff *pMsgBlock; /* pointer to a new message block */
SK_U64 PhysAddr; /* physical address of a rx buffer */
SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("--> AllocAndMapRxBuffer (Port: %i)\n", Port));
pMsgBlock = alloc_skb(pAC->RxPort[Port].RxBufSize, GFP_ATOMIC);
#if 1 /*Marvell - uboot*/
skb_reserve(pMsgBlock, 2); /* to align IP frames */
#endif
if (pMsgBlock == NULL) {
SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
("%s: Allocation of rx buffer failed !\n",
pAC->dev[Port]->name));
SK_PNMI_CNT_NO_RX_BUF(pAC, pAC->RxPort[Port].PortIndex);
return(SK_FALSE);
}
skb_reserve(pMsgBlock, 8);
#if 0 /* uboot */
PhysAddr = (SK_U64) pci_map_page(pAC->PciDev,
virt_to_page(pMsgBlock->data),
((unsigned long) pMsgBlock->data &
~PAGE_MASK),
pAC->RxPort[Port].RxBufSize,
PCI_DMA_FROMDEVICE);
#else
PhysAddr = (SK_U64)(SK_U32)pMsgBlock->data;
#endif
pSkPacket->pFrag->pVirt = pMsgBlock->data;
pSkPacket->pFrag->pPhys = PhysAddr;
pSkPacket->pFrag->FragLen = pAC->RxPort[Port].RxBufSize; /* for correct unmap */
pSkPacket->pMBuf = pMsgBlock;
pSkPacket->PacketLen = pAC->RxPort[Port].RxBufSize;
SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
("<-- AllocAndMapRxBuffer\n"));
return (SK_TRUE);
} /* AllocAndMapRxBuffer */
/*******************************************************************************
*
* End of file
*
******************************************************************************/
#endif