2775 lines
83 KiB
C
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
|