| /***************************************************************************** |
| * |
| * 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 |