blob: bf711b2dde936b5399765ffb0aac3b0fb4a617bb [file] [log] [blame]
/*****************************************************************************
*
* 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 0x%08x and phys %08x:%08x\n",
(unsigned int)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: %08x:%08x\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: %08X\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: %08X\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