512 lines
15 KiB
C
512 lines
15 KiB
C
/*****************************************************************************
|
|
*
|
|
* Name: sky2le.c
|
|
* Project: Gigabit Ethernet Adapters, Common Modules
|
|
* Version: $Revision: 1.13 $
|
|
* Date: $Date: 2006/04/05 14:06:06 $
|
|
* Purpose: Functions for handling List Element Tables
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/******************************************************************************
|
|
*
|
|
* LICENSE:
|
|
* (C)Copyright 2002-2006 Marvell.
|
|
*
|
|
* 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.
|
|
* /LICENSE
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef CONFIG_SK98
|
|
|
|
#if (defined(DEBUG) || ((!defined(LINT)) && (!defined(SK_SLIM))))
|
|
static const char SysKonnectFileId[] = "@(#)"
|
|
"$Id: sky2le.c,v 1.8 2004/06/03 15:09:29 malthoff Exp $ (C) Marvell.";
|
|
#endif /* DEBUG || (!LINT && !SK_SLIM) */
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Description:
|
|
*
|
|
* This module contains the code necessary for handling List Elements.
|
|
*
|
|
* Supported Gigabit Ethernet Chipsets:
|
|
* Yukon-2 (PCI, PCI-X, PCI-Express)
|
|
*
|
|
* Include File Hierarchy:
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
#include "h/skdrv1st.h"
|
|
#include "h/skdrv2nd.h"
|
|
|
|
/* defines *******************************************************************/
|
|
/* typedefs ******************************************************************/
|
|
/* global variables **********************************************************/
|
|
/* local variables ***********************************************************/
|
|
/* function prototypes *******************************************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* SkGeY2InitSingleLETable() - initializes a list element table
|
|
*
|
|
* Description:
|
|
* This function will initialize the selected list element table.
|
|
* Should be called once during DriverInit. No InitLevel required.
|
|
*
|
|
* Arguments:
|
|
* pAC - pointer to the adapter context struct.
|
|
* pLETab - pointer to list element table structure
|
|
* NumLE - number of list elements in this table
|
|
* pVMem - virtual address of memory allocated for this LE table
|
|
* PMemLowAddr - physical address of memory to be used for the LE table
|
|
* PMemHighAddr
|
|
*
|
|
* Returns:
|
|
* nothing
|
|
*/
|
|
void SkGeY2InitSingleLETable(
|
|
SK_AC *pAC, /* pointer to adapter context */
|
|
SK_LE_TABLE *pLETab, /* pointer to list element table to be initialized */
|
|
unsigned int NumLE, /* number of list elements to be filled in tab */
|
|
void *pVMem, /* virtual address of memory used for list elements */
|
|
SK_U32 PMemLowAddr, /* physical addr of mem used for LE */
|
|
SK_U32 PMemHighAddr)
|
|
{
|
|
unsigned int i;
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("==> SkGeY2InitSingleLETable()\n"));
|
|
|
|
#ifdef DEBUG
|
|
if (NumLE != 2) { /* not table for polling unit */
|
|
if ((NumLE % MIN_LEN_OF_LE_TAB) != 0 || NumLE > MAX_LEN_OF_LE_TAB) {
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_ERR,
|
|
("ERROR: Illegal number of list elements %d\n", NumLE));
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/* special case: unused list element table */
|
|
if (NumLE == 0) {
|
|
PMemLowAddr = 0;
|
|
PMemHighAddr = 0;
|
|
pVMem = 0;
|
|
}
|
|
|
|
/*
|
|
* in order to get the best possible performance the macros to access
|
|
* list elements use & instead of %
|
|
* this requires the length of LE tables to be a power of 2
|
|
*/
|
|
|
|
/*
|
|
* this code guarantees that we use the next power of 2 below the
|
|
* value specified for NumLe - this way some LEs in the table may
|
|
* not be used but the macros work correctly
|
|
* this code does not check for bad values below 128 because in such a
|
|
* case we cannot do anything here
|
|
*/
|
|
|
|
if ((NumLE != 2) && (NumLE != 0)) {
|
|
/* no check for polling unit and unused sync Tx */
|
|
i = MIN_LEN_OF_LE_TAB;
|
|
while (NumLE > i) {
|
|
i *= 2;
|
|
if (i > MAX_LEN_OF_LE_TAB) {
|
|
break;
|
|
}
|
|
}
|
|
if (NumLE != i) {
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_ERR,
|
|
("ERROR: Illegal number of list elements %d adjusted to %d\n",
|
|
NumLE, (i / 2)));
|
|
NumLE = i / 2;
|
|
}
|
|
}
|
|
|
|
/* set addresses */
|
|
pLETab->pPhyLETABLow = PMemLowAddr;
|
|
pLETab->pPhyLETABHigh = PMemHighAddr;
|
|
pLETab->pLETab = pVMem;
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("contains %d LEs", NumLE));
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
(" and starts at virt %08lx and phys %08lx:%08lx\n",
|
|
pVMem, PMemHighAddr, PMemLowAddr));
|
|
|
|
/* initialize indexes */
|
|
pLETab->Done = 0;
|
|
pLETab->Put = 0;
|
|
pLETab->HwPut = 0;
|
|
/* initialize size */
|
|
pLETab->Num = NumLE;
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("<== SkGeY2InitSingleLETable()\n"));
|
|
} /* SkGeY2InitSingleLETable */
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* SkGeY2InitPrefetchUnit() - Initialize a Prefetch Unit
|
|
*
|
|
* Description:
|
|
* Calling this function requires an already configured list element
|
|
* table. The prefetch unit to be configured is specified in the parameter
|
|
* 'Queue'. The function is able to initialze the prefetch units of
|
|
* the following queues: Q_R1, Q_R2, Q_XS1, Q_XS2, Q_XA1, Q_XA2.
|
|
* The funcution should be called before SkGeInitPort().
|
|
*
|
|
* Arguments:
|
|
* pAC - pointer to the adapter context struct.
|
|
* IoC - I/O context.
|
|
* Queue - I/O offset of queue e.g. Q_XA1.
|
|
* pLETab - pointer to list element table to be initialized
|
|
*
|
|
* Returns: N/A
|
|
*/
|
|
void SkGeY2InitPrefetchUnit(
|
|
SK_AC *pAC, /* pointer to adapter context */
|
|
SK_IOC IoC, /* I/O context */
|
|
unsigned int Queue, /* Queue offset for finding the right registers */
|
|
SK_LE_TABLE *pLETab) /* pointer to list element table to be initialized */
|
|
{
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("==> SkGeY2InitPrefetchUnit()\n"));
|
|
|
|
#ifdef DEBUG
|
|
if (Queue != Q_R1 && Queue != Q_R2 && Queue != Q_XS1 &&
|
|
Queue != Q_XS2 && Queue != Q_XA1 && Queue != Q_XA2) {
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_ERR,
|
|
("ERROR: Illegal queue identifier %x\n", Queue));
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/* disable the prefetch unit */
|
|
SK_OUT32(IoC, Y2_PREF_Q_ADDR(Queue, PREF_UNIT_CTRL_REG), PREF_UNIT_RST_SET);
|
|
SK_OUT32(IoC, Y2_PREF_Q_ADDR(Queue, PREF_UNIT_CTRL_REG), PREF_UNIT_RST_CLR);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Base address: %08lx:%08lx\n", pLETab->pPhyLETABHigh,
|
|
pLETab->pPhyLETABLow));
|
|
|
|
/* Set the list base address high part*/
|
|
SK_OUT32(IoC, Y2_PREF_Q_ADDR(Queue, PREF_UNIT_ADDR_HI_REG),
|
|
pLETab->pPhyLETABHigh);
|
|
|
|
/* Set the list base address low part */
|
|
SK_OUT32(IoC, Y2_PREF_Q_ADDR(Queue, PREF_UNIT_ADDR_LOW_REG),
|
|
pLETab->pPhyLETABLow);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Last index: %d\n", pLETab->Num-1));
|
|
|
|
/* Set the list last index */
|
|
SK_OUT16(IoC, Y2_PREF_Q_ADDR(Queue, PREF_UNIT_LAST_IDX_REG),
|
|
(SK_U16)(pLETab->Num - 1));
|
|
|
|
/* turn on prefetch unit */
|
|
SK_OUT32(IoC, Y2_PREF_Q_ADDR(Queue, PREF_UNIT_CTRL_REG), PREF_UNIT_OP_ON);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("<== SkGeY2InitPrefetchUnit()\n"));
|
|
} /* SkGeY2InitPrefetchUnit */
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* SkGeY2InitStatBmu() - Initialize the Status BMU
|
|
*
|
|
* Description:
|
|
* Calling this function requires an already configured list element
|
|
* table. Ensure the status BMU is only initialized once during
|
|
* DriverInit - InitLevel2 required.
|
|
*
|
|
* Arguments:
|
|
* pAC - pointer to the adapter context struct.
|
|
* IoC - I/O context.
|
|
* pLETab - pointer to status LE table to be initialized
|
|
*
|
|
* Returns: N/A
|
|
*/
|
|
void SkGeY2InitStatBmu(
|
|
SK_AC *pAC, /* pointer to adapter context */
|
|
SK_IOC IoC, /* I/O context */
|
|
SK_LE_TABLE *pLETab) /* pointer to status LE table */
|
|
{
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("==> SkGeY2InitStatBmu()\n"));
|
|
|
|
/* disable the prefetch unit */
|
|
SK_OUT32(IoC, STAT_CTRL, SC_STAT_RST_SET);
|
|
SK_OUT32(IoC, STAT_CTRL, SC_STAT_RST_CLR);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Base address Low: %08lX\n", pLETab->pPhyLETABLow));
|
|
|
|
/* Set the list base address */
|
|
SK_OUT32(IoC, STAT_LIST_ADDR_LO, pLETab->pPhyLETABLow);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Base address High: %08lX\n", pLETab->pPhyLETABHigh));
|
|
|
|
SK_OUT32(IoC, STAT_LIST_ADDR_HI, pLETab->pPhyLETABHigh);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Last index: %d\n", pLETab->Num - 1));
|
|
|
|
/* Set the list last index */
|
|
SK_OUT16(IoC, STAT_LAST_IDX, (SK_U16)(pLETab->Num - 1));
|
|
|
|
if (HW_FEATURE(pAC, HWF_WA_DEV_43_418)) {
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Set Tx index threshold\n"));
|
|
/* WA for dev. #4.3 */
|
|
SK_OUT16(IoC, STAT_TX_IDX_TH, ST_TXTH_IDX_MASK);
|
|
|
|
/* set Status-FIFO watermark */
|
|
SK_OUT8(IoC, STAT_FIFO_WM, 0x21); /* WA for dev. #4.18 */
|
|
|
|
/* set Status-FIFO ISR watermark */
|
|
SK_OUT8(IoC, STAT_FIFO_ISR_WM, 0x07); /* WA for dev. #4.18 */
|
|
|
|
/* WA for dev. #4.3 and #4.18 */
|
|
/* set Status-FIFO Tx timer init value */
|
|
SK_OUT32(IoC, STAT_TX_TIMER_INI, HW_MS_TO_TICKS(pAC, 10));
|
|
}
|
|
else {
|
|
/*
|
|
* Further settings may be added if required...
|
|
* 1) Status-FIFO watermark (STAT_FIFO_WM, STAT_FIFO_ISR_WM)
|
|
* 2) Status-FIFO timer values (STAT_TX_TIMER_INI,
|
|
* STAT_LEV_TIMER_INI and STAT_ISR_TIMER_INI)
|
|
* but tests shows that the default values give the best results,
|
|
* therefore the defaults are used.
|
|
*/
|
|
|
|
/*
|
|
* Theses settings should avoid the temporary hang of the status BMU.
|
|
* May be not all required... still under investigation...
|
|
*/
|
|
SK_OUT16(IoC, STAT_TX_IDX_TH, 0x000a);
|
|
|
|
/* set Status-FIFO watermark */
|
|
SK_OUT8(IoC, STAT_FIFO_WM, 0x10);
|
|
|
|
/* set Status-FIFO ISR watermark */
|
|
SK_OUT8(IoC, STAT_FIFO_ISR_WM,
|
|
HW_FEATURE(pAC, HWF_WA_DEV_4109) ? 0x10 : 0x04);
|
|
|
|
/* set ISR Timer Init Value to 400 (3.2 us on Yukon-EC) */
|
|
SK_OUT32(IoC, STAT_ISR_TIMER_INI, 0x0190);
|
|
}
|
|
|
|
/* start Status-FIFO timer */
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Start Status FiFo timer\n"));
|
|
|
|
/* enable the prefetch unit */
|
|
/* operational bit not functional for Yukon-EC, but fixed in Yukon-2 */
|
|
SK_OUT32(IoC, STAT_CTRL, SC_STAT_OP_ON);
|
|
|
|
/* start Status-FIFO timer */
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Start Status FiFo timer\n"));
|
|
|
|
SK_OUT8(IoC, STAT_TX_TIMER_CTRL, TIM_START);
|
|
SK_OUT8(IoC, STAT_LEV_TIMER_CTRL, TIM_START);
|
|
SK_OUT8(IoC, STAT_ISR_TIMER_CTRL, TIM_START);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("<== SkGeY2InitStatBmu()\n"));
|
|
} /* SkGeY2InitStatBmu */
|
|
|
|
#ifdef USE_POLLING_UNIT
|
|
/*****************************************************************************
|
|
*
|
|
* SkGeY2InitPollUnit() - Initialize the Polling Unit
|
|
*
|
|
* Description:
|
|
* This function will write the data of one polling LE table into the
|
|
* adapter.
|
|
*
|
|
* Arguments:
|
|
* pAC - pointer to the adapter context struct.
|
|
* IoC - I/O context.
|
|
* pLETab - pointer to polling LE table to be initialized
|
|
*
|
|
* Returns: N/A
|
|
*/
|
|
void SkGeY2InitPollUnit(
|
|
SK_AC *pAC, /* pointer to adapter context */
|
|
SK_IOC IoC, /* I/O context */
|
|
SK_LE_TABLE *pLETab) /* pointer to polling LE table */
|
|
{
|
|
SK_HWLE *pLE;
|
|
int i;
|
|
#ifdef VCPU
|
|
VCPU_VARS();
|
|
#endif /* VCPU */
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("==> SkGeY2InitPollUnit()\n"));
|
|
|
|
#ifdef VCPU
|
|
for (i = 0; i < SK_MAX_MACS; i++) {
|
|
GET_PO_LE(pLE, pLETab, i);
|
|
VCPU_START_AND_COPY_LE();
|
|
/* initialize polling LE but leave indexes invalid */
|
|
POLE_SET_OPC(pLE, OP_PUTIDX | HW_OWNER);
|
|
POLE_SET_LINK(pLE, i);
|
|
POLE_SET_RXIDX(pLE, 0);
|
|
POLE_SET_TXAIDX(pLE, 0);
|
|
POLE_SET_TXSIDX(pLE, 0);
|
|
VCPU_WRITE_LE();
|
|
SK_DBG_DUMP_PO_LE(pLE);
|
|
}
|
|
#endif /* VCPU */
|
|
|
|
/* disable the polling unit */
|
|
SK_OUT32(IoC, POLL_CTRL, PC_POLL_RST_SET);
|
|
SK_OUT32(IoC, POLL_CTRL, PC_POLL_RST_CLR);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Base address Low: %08lX\n", pLETab->pPhyLETABLow));
|
|
|
|
/* Set the list base address */
|
|
SK_OUT32(IoC, POLL_LIST_ADDR_LO, pLETab->pPhyLETABLow);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("Base address High: %08lX\n", pLETab->pPhyLETABHigh));
|
|
|
|
SK_OUT32(IoC, POLL_LIST_ADDR_HI, pLETab->pPhyLETABHigh);
|
|
|
|
/* we don't need to write the last index - it is hardwired to 1 */
|
|
|
|
/* enable the prefetch unit */
|
|
SK_OUT32(IoC, POLL_CTRL, PC_POLL_OP_ON);
|
|
|
|
/*
|
|
* now we have to start the descriptor poll timer because it triggers
|
|
* the polling unit
|
|
*/
|
|
|
|
/*
|
|
* still playing with the value (timer runs at 125 MHz)
|
|
* descriptor poll timer is enabled by GeInit
|
|
*/
|
|
SK_OUT32(IoC, B28_DPT_INI,
|
|
(SK_DPOLL_DEF_Y2 * (SK_U32)pAC->GIni.GIHstClkFact / 100));
|
|
|
|
SK_OUT8(IoC, B28_DPT_CTRL, TIM_START);
|
|
|
|
SK_DBG_MSG(pAC, SK_DBGMOD_HWM, SK_DBGCAT_INIT,
|
|
("<== SkGeY2InitPollUnit()\n"));
|
|
} /* SkGeY2InitPollUnit */
|
|
#endif /* USE_POLLING_UNIT */
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* SkGeY2SetPutIndex
|
|
*
|
|
* Description:
|
|
* This function is writing the Done index of a transmit
|
|
* list element table.
|
|
*
|
|
* Notes:
|
|
* Dev. Issue 4.2
|
|
*
|
|
* Returns: N/A
|
|
*/
|
|
void SkGeY2SetPutIndex(
|
|
SK_AC *pAC, /* pointer to adapter context */
|
|
SK_IOC IoC, /* pointer to the IO context */
|
|
SK_U32 StartAddrPrefetchUnit, /* start address of the prefetch unit */
|
|
SK_LE_TABLE *pLETab) /* list element table to work with */
|
|
{
|
|
unsigned int Put;
|
|
SK_U16 EndOfListIndex;
|
|
SK_U16 HwGetIndex;
|
|
SK_U16 HwPutIndex;
|
|
|
|
/* set put index we would like to write */
|
|
Put = GET_PUT_IDX(pLETab);
|
|
|
|
/*
|
|
* in this case we wrap around
|
|
* new put is lower than last put given to HW
|
|
*/
|
|
if (Put < pLETab->HwPut) {
|
|
|
|
/* set put index = last index of list */
|
|
EndOfListIndex = (NUM_LE_IN_TABLE(pLETab)-1);
|
|
|
|
/* read get index of hw prefetch unit */
|
|
SK_IN16(IoC, (StartAddrPrefetchUnit + PREF_UNIT_GET_IDX_REG),
|
|
&HwGetIndex);
|
|
|
|
/* read put index of hw prefetch unit */
|
|
SK_IN16(IoC, (StartAddrPrefetchUnit + PREF_UNIT_PUT_IDX_REG),
|
|
&HwPutIndex);
|
|
|
|
/* prefetch unit reached end of list */
|
|
/* prefetch unit reached first list element */
|
|
if (HwGetIndex == 0) {
|
|
/* restore watermark */
|
|
SK_OUT8(IoC, StartAddrPrefetchUnit + PREF_UNIT_FIFO_WM_REG, 0xe0U);
|
|
/* write put index */
|
|
SK_OUT16(IoC, StartAddrPrefetchUnit + PREF_UNIT_PUT_IDX_REG,
|
|
(SK_U16)Put);
|
|
|
|
/* remember put index we wrote to hw */
|
|
pLETab->HwPut = Put;
|
|
}
|
|
else if (HwGetIndex == EndOfListIndex) {
|
|
/* set watermark to one list element */
|
|
SK_OUT8(IoC, StartAddrPrefetchUnit + PREF_UNIT_FIFO_WM_REG, 8);
|
|
/* set put index to first list element */
|
|
SK_OUT16(IoC, StartAddrPrefetchUnit + PREF_UNIT_PUT_IDX_REG, 0);
|
|
}
|
|
/* prefetch unit did not reach end of list yet */
|
|
/* and we did not write put index to end of list yet */
|
|
else if ((HwPutIndex != EndOfListIndex) &&
|
|
(HwGetIndex != EndOfListIndex)) {
|
|
/* write put index */
|
|
SK_OUT16(IoC, StartAddrPrefetchUnit + PREF_UNIT_PUT_IDX_REG,
|
|
EndOfListIndex);
|
|
}
|
|
else {
|
|
/* do nothing */
|
|
}
|
|
}
|
|
else {
|
|
#ifdef XXX /* leads in to problems in the Windows Driver */
|
|
if (Put != pLETab->HwPut) {
|
|
/* write put index */
|
|
SK_OUT16(IoC, StartAddrPrefetchUnit + PREF_UNIT_PUT_IDX_REG,
|
|
(SK_U16)Put);
|
|
/* update put index */
|
|
UPDATE_HWPUT_IDX(pLETab);
|
|
}
|
|
#else
|
|
/* write put index */
|
|
SK_OUT16(IoC, StartAddrPrefetchUnit + PREF_UNIT_PUT_IDX_REG,
|
|
(SK_U16)Put);
|
|
/* update put index */
|
|
UPDATE_HWPUT_IDX(pLETab);
|
|
#endif
|
|
}
|
|
} /* SkGeY2SetPutIndex */
|
|
|
|
#endif
|