This patch adds support for the SMSC 9210 Ethernet chip, specialized for Alchemy platforms (including the DB1300). The ethernet driver code was provided by SMSC; the platform shim by RMI. Signed-off-by: Kevin Hickey <khickey@xxxxxxxxxxx> --- drivers/net/Kconfig | 6 + drivers/net/Makefile | 3 + drivers/net/smsc9210/Makefile | 9 + drivers/net/smsc9210/ioctl_118.h | 298 ++ drivers/net/smsc9210/platform_alchemy.c | 88 + drivers/net/smsc9210/platform_alchemy.h | 117 + drivers/net/smsc9210/smsc9210.h | 23 + drivers/net/smsc9210/smsc9210_main.c | 7189 +++++++++++++++++++++++++++++++ 8 files changed, 7733 insertions(+), 0 deletions(-) create mode 100644 drivers/net/smsc9210/Makefile create mode 100644 drivers/net/smsc9210/ioctl_118.h create mode 100644 drivers/net/smsc9210/platform_alchemy.c create mode 100644 drivers/net/smsc9210/platform_alchemy.h create mode 100644 drivers/net/smsc9210/smsc9210.h create mode 100644 drivers/net/smsc9210/smsc9210_main.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 8b13c5d..791735e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -907,6 +907,12 @@ config SMC91X inserted in and removed from the running kernel whenever you want). The module will be called smc91x. If you want to compile it as a module, say M here and read <file:Documentation/kbuild/modules.txt>. + +config SMSC9210 + tristate "SMSC 9210 support" + depends on SOC_AU1X00 + help + TODO config NET_NETX tristate "NetX Ethernet support" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3c627d0..7c4052f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -222,6 +222,9 @@ obj-$(CONFIG_IBMVETH) += ibmveth.o obj-$(CONFIG_S2IO) += s2io.o obj-$(CONFIG_MYRI10GE) += myri10ge/ obj-$(CONFIG_SMC91X) += smc91x.o + +obj-$(CONFIG_SMSC9210) += smsc9210/ + obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_SMSC911X) += smsc911x.o obj-$(CONFIG_BFIN_MAC) += bfin_mac.o diff --git a/drivers/net/smsc9210/Makefile b/drivers/net/smsc9210/Makefile new file mode 100644 index 0000000..76a1217 --- /dev/null +++ b/drivers/net/smsc9210/Makefile @@ -0,0 +1,9 @@ +# +# Copyright 2008 RMI Corporation. All rights reserved. +# Author: Kevin Hickey <khickey@xxxxxxxxxxx> +# + +obj-$(CONFIG_SMSC9210) += smsc9210.o + +smsc9210-objs := smsc9210_main.o platform_alchemy.o + diff --git a/drivers/net/smsc9210/ioctl_118.h b/drivers/net/smsc9210/ioctl_118.h new file mode 100644 index 0000000..07187d7 --- /dev/null +++ b/drivers/net/smsc9210/ioctl_118.h @@ -0,0 +1,298 @@ +/*************************************************************************** + + * + + * Copyright (C) 2004-2005 SMSC + + * + + * 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. + + * + + * This program is distributed in the hope that it will be useful, + + * but WITHOUT ANY WARRANTY; without even the implied warranty of + + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + * GNU General Public License for more details. + + * + + * You should have received a copy of the GNU General Public License + + * along with this program; if not, write to the Free Software + + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + * + + *************************************************************************** + + * File: ioctl_118.h + + */ + + + +#ifndef IOCTL_118_H + +#define IOCTL_118_H + + + +#define DRIVER_VERSION (0x00000155UL) + + + +#define SMSC9118_DRIVER_SIGNATURE (0x82745BACUL+DRIVER_VERSION) + +#define SMSC9118_APP_SIGNATURE (0x987BEF28UL+DRIVER_VERSION) + + + +#define SMSC9118_IOCTL (SIOCDEVPRIVATE + 0xB) + + + +#define COMMAND_BASE (0x974FB832UL) + + + +#define COMMAND_GET_SIGNATURE (COMMAND_BASE+0) + + + +#define COMMAND_LAN_GET_REG (COMMAND_BASE+1) + +#define COMMAND_LAN_SET_REG (COMMAND_BASE+2) + + + +#define COMMAND_MAC_GET_REG (COMMAND_BASE+3) + +#define COMMAND_MAC_SET_REG (COMMAND_BASE+4) + + + +#define COMMAND_PHY_GET_REG (COMMAND_BASE+5) + +#define COMMAND_PHY_SET_REG (COMMAND_BASE+6) + + + +#define COMMAND_DUMP_LAN_REGS (COMMAND_BASE+7) + +#define LAN_REG_ID_REV (0) + +#define LAN_REG_INT_CFG (1) + +#define LAN_REG_INT_STS (2) + +#define LAN_REG_INT_EN (3) + +#define LAN_REG_BYTE_TEST (4) + +#define LAN_REG_FIFO_INT (5) + +#define LAN_REG_RX_CFG (6) + +#define LAN_REG_TX_CFG (7) + +#define LAN_REG_HW_CFG (8) + +#define LAN_REG_RX_DP_CTRL (9) + +#define LAN_REG_RX_FIFO_INF (10) + +#define LAN_REG_TX_FIFO_INF (11) + +#define LAN_REG_PMT_CTRL (12) + +#define LAN_REG_GPIO_CFG (13) + +#define LAN_REG_GPT_CFG (14) + +#define LAN_REG_GPT_CNT (15) + +#define LAN_REG_FPGA_REV (16) + +#define LAN_REG_WORD_SWAP (17) + +#define LAN_REG_FREE_RUN (18) + +#define LAN_REG_RX_DROP (19) + +#define LAN_REG_MAC_CSR_CMD (21) + +#define LAN_REG_MAC_CSR_DATA (22) + +#define LAN_REG_AFC_CFG (23) + +#define LAN_REG_E2P_CMD (24) + +#define LAN_REG_E2P_DATA (25) + + + +#define COMMAND_DUMP_MAC_REGS (COMMAND_BASE+8) + +#define MAC_REG_MAC_CR (0) + +#define MAC_REG_ADDRH (1) + +#define MAC_REG_ADDRL (2) + +#define MAC_REG_HASHH (3) + +#define MAC_REG_HASHL (4) + +#define MAC_REG_MII_ACC (5) + +#define MAC_REG_MII_DATA (6) + +#define MAC_REG_FLOW (7) + +#define MAC_REG_VLAN1 (8) + +#define MAC_REG_VLAN2 (9) + +#define MAC_REG_WUFF (10) + +#define MAC_REG_WUCSR (11) + + + +#define COMMAND_DUMP_PHY_REGS (COMMAND_BASE+9) + +#define PHY_REG_0 (0) + +#define PHY_REG_1 (1) + +#define PHY_REG_2 (2) + +#define PHY_REG_3 (3) + +#define PHY_REG_4 (4) + +#define PHY_REG_5 (5) + +#define PHY_REG_6 (6) + +#define PHY_REG_16 (7) + +#define PHY_REG_17 (8) + +#define PHY_REG_18 (9) + +#define PHY_REG_20 (10) + +#define PHY_REG_21 (11) + +#define PHY_REG_22 (12) + +#define PHY_REG_23 (13) + +#define PHY_REG_27 (14) + +#define PHY_REG_28 (15) + +#define PHY_REG_29 (16) + +#define PHY_REG_30 (17) + +#define PHY_REG_31 (18) + + + +#define COMMAND_DUMP_EEPROM (COMMAND_BASE+10) + + + +#define COMMAND_GET_MAC_ADDRESS (COMMAND_BASE+11) + +#define COMMAND_SET_MAC_ADDRESS (COMMAND_BASE+12) + +#define COMMAND_LOAD_MAC_ADDRESS (COMMAND_BASE+13) + +#define COMMAND_SAVE_MAC_ADDRESS (COMMAND_BASE+14) + +#define COMMAND_SET_DEBUG_MODE (COMMAND_BASE+15) + + + +#define COMMAND_SET_POWER_MODE (COMMAND_BASE+16) + +#define COMMAND_GET_POWER_MODE (COMMAND_BASE+17) + + + +#define COMMAND_SET_LINK_MODE (COMMAND_BASE+18) + +#define COMMAND_GET_LINK_MODE (COMMAND_BASE+19) + +#define COMMAND_GET_CONFIGURATION (COMMAND_BASE+20) + +#define COMMAND_DUMP_TEMP (COMMAND_BASE+21) + +#define COMMAND_READ_BYTE (COMMAND_BASE+22) + +#define COMMAND_READ_WORD (COMMAND_BASE+23) + +#define COMMAND_READ_DWORD (COMMAND_BASE+24) + +#define COMMAND_WRITE_BYTE (COMMAND_BASE+25) + +#define COMMAND_WRITE_WORD (COMMAND_BASE+26) + +#define COMMAND_WRITE_DWORD (COMMAND_BASE+27) + +#define COMMAND_CHECK_LINK (COMMAND_BASE+28) + + + +//the following codes are intended for cmd9118 only + +// they are not intended to have any use in the driver + +#define COMMAND_RUN_SERVER (COMMAND_BASE+29) + +#define COMMAND_RUN_TUNER (COMMAND_BASE+30) + + + +#define COMMAND_GET_FLOW_PARAMS (COMMAND_BASE+31) + +#define COMMAND_SET_FLOW_PARAMS (COMMAND_BASE+32) + +#define COMMAND_SET_AMDIX_STS (COMMAND_BASE+33) + +#define COMMAND_GET_AMDIX_STS (COMMAND_BASE+34) + +typedef struct _SMSC9118_IOCTL_DATA { + + unsigned long dwSignature; + + unsigned long dwCommand; + + unsigned long Data[0x60]; + + char Strng1[30]; + + char Strng2[10]; + +} SMSC9118_IOCTL_DATA, *PSMSC9118_IOCTL_DATA; + + + +#endif + + + diff --git a/drivers/net/smsc9210/platform_alchemy.c b/drivers/net/smsc9210/platform_alchemy.c new file mode 100644 index 0000000..1da19a6 --- /dev/null +++ b/drivers/net/smsc9210/platform_alchemy.c @@ -0,0 +1,88 @@ +/* + * Copyright 2003-2008 RMI Corporation. All rights reserved. + * Author: Kevin Hickey <khickey@xxxxxxxxxxx> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/interrupt.h> +#include "platform_alchemy.h" + +u32 Platform_Initialize(PPLATFORM_DATA pd, u32 lan_base, u32 bus_width) +{ + return BASE_ADDRESS; +} + + +/* + * Ignores the passed in irq in favor of the one we know to be correct... + */ +bool Platform_RequestIRQ(PPLATFORM_DATA pd, + u32 irq, + irqreturn_t (*pIsr)(int irq,void *dev_id), + void* dev_id ) +{ + int retval = request_irq(PLATFORM_IRQ, pIsr, 0, "SMSC 9210 Ethernet", dev_id); + pd->irq_dev_id = dev_id; + + return retval == 0 ? true : false; +} + +void Platform_FreeIRQ(PPLATFORM_DATA pd) +{ + free_irq(PLATFORM_IRQ, pd->irq_dev_id); +} + +void Platform_WriteFifo(u32 lan_base, u32 *buf, u32 count) +{ + int i; + for(i = 0; i < count; ++i) + { + au_iowrite32(*buf, (u32*)(lan_base+0x20)); + ++buf; + } +} + +void Platform_ReadFifo(u32 lan_base, u32 *buf, u32 count) +{ + int i; + for(i = 0; i < count; ++i) + { + *buf = au_ioread32((u32*)lan_base); + ++buf; + } + +} + +void Platform_GetFlowControlParameters(PPLATFORM_DATA pd, PFLOW_CONTROL_PARAMETERS fcp, bool useDma) +{ + /* + * Borrowed from xscale_linux.c in the 16-bit PIO 8210 section + */ + memset(fcp,0,sizeof(FLOW_CONTROL_PARAMETERS)); + fcp->BurstPeriod=100; + fcp->IntDeas=0; + fcp->MaxThroughput=0x74378UL; + fcp->MaxPacketCount=0x13AUL; + fcp->PacketCost=0x02UL; + fcp->BurstPeriod=0x76UL; + fcp->IntDeas=0x18UL; +} diff --git a/drivers/net/smsc9210/platform_alchemy.h b/drivers/net/smsc9210/platform_alchemy.h new file mode 100644 index 0000000..17f42e3 --- /dev/null +++ b/drivers/net/smsc9210/platform_alchemy.h @@ -0,0 +1,117 @@ +/* + * Copyright 2003-2008 RMI Corporation. All rights reserved. + * Author: Kevin Hickey <khickey@xxxxxxxxxxx> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PLATFORM_ALCHEMY_H +#define PLATFORM_ALCHEMY_H + +#include <asm/mach-au1x00/au1000.h> +#include <linux/types.h> + +#if defined(CONFIG_MIPS_DB1200) +#include <asm/mach-db1x00/db1200.h> +#define PLATFORM_IRQ DB1200_DC_INT +#elif defined(CONFIG_MIPS_HMP10) +#include <asm/mips-boards/hmp10.h> +#define PLATFORM_IRQ HMP10_ETH_IRQ +#elif defined(CONFIG_MIPS_DB1300) +#include <asm/mips-boards/db1300.h> +#define PLATFORM_IRQ DB1300_ETHERNET_IRQ +#endif + +#include "smsc9210.h" +/* + * Make the IRQ a push-pull; on DB1300 there is no pull up so open-drain will + * always be low. + */ +#define PLATFORM_IRQ_POL 0 +#define PLATFORM_IRQ_TYPE 1 +#define PLATFORM_CACHE_LINE_BYTES 0 +#define PLATFORM_RX_DMA TRANSFER_PIO +#define PLATFORM_TX_DMA TRANSFER_PIO +#define PLATFORM_DMA_THRESHOLD 0 +#define PLATFORM_NAME "Alchemy" + +#ifdef CONFIG_MIPS_DB1200 +#define BASE_ADDRESS 0xBA000000 +#else +#define BASE_ADDRESS 0xB9000000 +#endif + +typedef struct { + void *irq_dev_id; +} PLATFORM_DATA, *PPLATFORM_DATA; + +u32 Platform_Initialize(PPLATFORM_DATA pd, u32 lan_base, u32 bus_width); + +static void Platform_CleanUp(PPLATFORM_DATA pd) { } +static u32 Platform_CurrentIRQ(PPLATFORM_DATA pd) +{ + return PLATFORM_IRQ; +} + +static inline void Platform_SetRegDW(u32 lan_base, u32 offset, u32 val) +{ + au_writel(val, (lan_base + offset)); +} + +static inline u32 Platform_GetRegDW(u32 lan_base, u32 offset) +{ + return au_readl(lan_base + offset); +} + +bool Platform_RequestIRQ(PPLATFORM_DATA pd, + u32 irq, + irqreturn_t (*pIsr)(int irq, void *dev_id), + void* dev_id ); + +void Platform_FreeIRQ(PPLATFORM_DATA pd); +void Platform_GetFlowControlParameters(PPLATFORM_DATA pd, PFLOW_CONTROL_PARAMETERS fcp, bool useDma); +void Platform_WriteFifo(u32 lan_base, u32 *buf, u32 count); +void Platform_ReadFifo(u32 lan_base, u32 *buf, u32 count); + +/* * We're not supporting DMA at this time, so degenerate all functions to return + * errors. + */ +static inline bool Platform_IsValidDmaChannel(u32 chan) { return false; } +static inline bool Platform_DmaInitialize(PPLATFORM_DATA pd, u32 chan) { return false; } +static inline bool Platform_DmaDisable(PPLATFORM_DATA pd, u32 chan) { return false; } +static inline void Platform_CacheInvalidate(PPLATFORM_DATA pd, const void *const sa, + const u32 len) { } +static inline void Platform_CachePurge(PPLATFORM_DATA pd, const void *const sa, + const u32 len) { } +static inline u32 Platform_RequestDmaChannel(PPLATFORM_DATA pd) { return TRANSFER_PIO; } +static inline void Platform_ReleaseDmaChannel(PPLATFORM_DATA pd, u32 chan) { } +static inline bool Platform_DmaStartXfer(PPLATFORM_DATA pd, const DMA_XFER * const pDmaXfer) +{ + return false; +} +static inline u32 Platform_DmaGetDwCnt(PPLATFORM_DATA platformData, const u32 dwDmaCh) +{ + return 0; +} +static inline void Platform_DmaComplete(PPLATFORM_DATA platformData, const u32 dwDmaCh) { } + + +#endif /* PLATFORM_ALCHEMY_H */ diff --git a/drivers/net/smsc9210/smsc9210.h b/drivers/net/smsc9210/smsc9210.h new file mode 100644 index 0000000..903b156 --- /dev/null +++ b/drivers/net/smsc9210/smsc9210.h @@ -0,0 +1,23 @@ +#ifndef SMSC9210_H +#define SMSC9210_H + +#define TRANSFER_PIO (256UL) + +typedef struct _DMA_XFER +{ + u32 dwLanReg; + u32 *pdwBuf; + u32 dwDmaCh; + u32 dwDwCnt; + bool fMemWr; +} DMA_XFER; + +typedef struct _FLOW_CONTROL_PARAMETERS +{ + u32 MaxThroughput; + u32 MaxPacketCount; + u32 PacketCost; + u32 BurstPeriod; + u32 IntDeas; +} FLOW_CONTROL_PARAMETERS, *PFLOW_CONTROL_PARAMETERS; +#endif /* SMSC9210_H */ diff --git a/drivers/net/smsc9210/smsc9210_main.c b/drivers/net/smsc9210/smsc9210_main.c new file mode 100644 index 0000000..15fbf7f --- /dev/null +++ b/drivers/net/smsc9210/smsc9210_main.c @@ -0,0 +1,7189 @@ +/*************************************************************************** + * + * Copyright (C) 2004-2005 SMSC + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + *************************************************************************** + * File: smsc9118.c + * see readme.txt for programmers guide + */ + + +//#define UseScatterGather +//#define UseTxCsum +//#define UseRxCsum + +#define USE_DEBUG + +#ifndef __KERNEL__ +# define __KERNEL__ +#endif + +#ifdef USING_LINT +#include "lint.h" +#else //not USING_LINT +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/delay.h> +#include <linux/mii.h> +#include <linux/timer.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <linux/version.h> + +#include <asm/mach-au1x00/prom.h> +#include "smsc9210.h" + +#endif //not USING_LINT + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#else +#define LINUX_2_6_OR_NEWER +#endif + +#ifdef USE_DEBUG +static u32 debug_mode=0x7UL; +#else +static u32 debug_mode=0x0UL; +#endif + +#ifdef USE_DEBUG +//select debug modes +#define USE_WARNING +#define USE_TRACE +#define USE_ASSERT +#endif //USE_DEBUG + +#define USE_LED1_WORK_AROUND // 10/100 LED link-state inversion +#define USE_PHY_WORK_AROUND // output polarity inversion + +typedef long TIME_SPAN; +#define MAX_TIME_SPAN ((TIME_SPAN)(0x7FFFFFFFUL)) +typedef unsigned short WORD; +typedef unsigned char BYTE; +//typedef unsigned char bool; +#define true ((bool)1) +#define false ((bool)0) + +//unsigned char testbuff[1600]={0}; +//u32 * p= NULL; + + +#define HIBYTE(word) ((BYTE)(((WORD)(word))>>8)) +#define LOBYTE(word) ((BYTE)(((WORD)(word))&0x00FFU)) +#define HIWORD(dWord) ((WORD)(((u32)(dWord))>>16)) +#define LOWORD(dWord) ((WORD)(((u32)(dWord))&0x0000FFFFUL)) + +#define TRANSFER_PIO (256UL) +#define TRANSFER_REQUEST_DMA (255UL) +//these are values that can be assigned to +//PLATFORM_RX_DMA +//PLATFORM_TX_DMA +// in addition to any specific dma channel + +/******************************************************* + * Macro: SMSC_TRACE + * Description: + * This macro is used like printf. + * It can be used anywhere you want to display information + * For any release version it should not be left in + * performance sensitive Tx and Rx code paths. + * To use this macro define USE_TRACE and set bit 0 of debug_mode + *******************************************************/ +#ifdef USING_LINT +extern void SMSC_TRACE(const char * a, ...); +#else //not USING_LINT +#ifdef USE_TRACE +#ifndef USE_WARNING +#define USE_WARNING +#endif +# define SMSC_TRACE(msg,args...) \ + if(debug_mode&0x01UL) { \ + printk("SMSC: " msg "\n", ## args); \ + } +#else +# define SMSC_TRACE(msg,args...) +#endif +#endif //not USING_LINT + +/******************************************************* + * Macro: SMSC_WARNING + * Description: + * This macro is used like printf. + * It can be used anywhere you want to display warning information + * For any release version it should not be left in + * performance sensitive Tx and Rx code paths. + * To use this macro define USE_TRACE or + * USE_WARNING and set bit 1 of debug_mode + *******************************************************/ + + +#ifdef USING_LINT +extern void SMSC_WARNING(const char * a, ...); +#else //not USING_LINT +#ifdef USE_WARNING +#ifndef USE_ASSERT +#define USE_ASSERT +#endif +# define SMSC_WARNING(msg, args...) \ + if(debug_mode&0x02UL) { \ + printk("SMSC_WARNING: " msg "\n",## args); \ + } +#else +# define SMSC_WARNING(msg, args...) +#endif +#endif //not USING_LINT + + +/******************************************************* + * Macro: SMSC_ASSERT + * Description: + * This macro is used to test assumptions made when coding. + * It can be used anywhere, but is intended only for situations + * where a failure is fatal. + * If code execution where allowed to continue it is assumed that + * only further unrecoverable errors would occur and so this macro + * includes an infinite loop to prevent further corruption. + * Assertions are only intended for use during developement to + * insure consistency of logic through out the driver. + * A driver should not be released if assertion failures are + * still occuring. + * To use this macro define USE_TRACE or USE_WARNING or + * USE_ASSERT + *******************************************************/ +#ifdef USING_LINT +extern void SMSC_ASSERT(bool condition); +#else //not USING_LINT +#ifdef USE_ASSERT +# define SMSC_ASSERT(condition) \ + if(!(condition)) { \ + printk("SMSC_ASSERTION_FAILURE: File=" __FILE__ ", Line=%d\n",__LINE__); \ + while(1); \ + } +#else +# define SMSC_ASSERT(condition) +#endif +#endif //not USING_LINT + +//Below are the register offsets and bit definitions +// of the Lan9118 memory space +#define RX_DATA_FIFO (0x00UL) +#define TX_DATA_FIFO (0x20UL) +#define TX_CMD_A_INT_ON_COMP_ (0x80000000UL) +#define TX_CMD_A_INT_BUF_END_ALGN_ (0x03000000UL) +#define TX_CMD_A_INT_4_BYTE_ALGN_ (0x00000000UL) +#define TX_CMD_A_INT_16_BYTE_ALGN_ (0x01000000UL) +#define TX_CMD_A_INT_32_BYTE_ALGN_ (0x02000000UL) +#define TX_CMD_A_INT_DATA_OFFSET_ (0x001F0000UL) +#define TX_CMD_A_INT_FIRST_SEG_ (0x00002000UL) +#define TX_CMD_A_INT_LAST_SEG_ (0x00001000UL) +#define TX_CMD_A_BUF_SIZE_ (0x000007FFUL) +#define TX_CMD_B_PKT_TAG_ (0xFFFF0000UL) +#define TX_CMD_B_CSUM_ENABLE (0x00004000UL) +#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000UL) +#define TX_CMD_B_DISABLE_PADDING_ (0x00001000UL) +#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FFUL) + +#define RX_STATUS_FIFO (0x40UL) +#define RX_STS_ES_ (0x00008000UL) +#define RX_STS_MCAST_ (0x00000400UL) +#define RX_STATUS_FIFO_PEEK (0x44UL) +#define TX_STATUS_FIFO (0x48UL) +#define TX_STATUS_FIFO_PEEK (0x4CUL) +#define ID_REV (0x50UL) +#define ID_REV_CHIP_ID_ (0xFFFF0000UL) // RO +#define ID_REV_REV_ID_ (0x0000FFFFUL) // RO + +#define INT_CFG (0x54UL) +#define INT_CFG_INT_DEAS_ (0xFF000000UL) // R/W +#define INT_CFG_IRQ_INT_ (0x00001000UL) // RO +#define INT_CFG_IRQ_EN_ (0x00000100UL) // R/W +#define INT_CFG_IRQ_POL_ (0x00000010UL) // R/W Not Affected by SW Reset +#define INT_CFG_IRQ_TYPE_ (0x00000001UL) // R/W Not Affected by SW Reset + +#define INT_STS (0x58UL) +#define INT_STS_SW_INT_ (0x80000000UL) // R/WC +#define INT_STS_TXSTOP_INT_ (0x02000000UL) // R/WC +#define INT_STS_RXSTOP_INT_ (0x01000000UL) // R/WC +#define INT_STS_RXDFH_INT_ (0x00800000UL) // R/WC +#define INT_STS_RXDF_INT_ (0x00400000UL) // R/WC +#define INT_STS_TX_IOC_ (0x00200000UL) // R/WC +#define INT_STS_RXD_INT_ (0x00100000UL) // R/WC +#define INT_STS_GPT_INT_ (0x00080000UL) // R/WC +#define INT_STS_PHY_INT_ (0x00040000UL) // RO +#define INT_STS_PME_INT_ (0x00020000UL) // R/WC +#define INT_STS_TXSO_ (0x00010000UL) // R/WC +#define INT_STS_RWT_ (0x00008000UL) // R/WC +#define INT_STS_RXE_ (0x00004000UL) // R/WC +#define INT_STS_TXE_ (0x00002000UL) // R/WC +#define INT_STS_ERX_ (0x00001000UL) // R/WC +#define INT_STS_TDFU_ (0x00000800UL) // R/WC +#define INT_STS_TDFO_ (0x00000400UL) // R/WC +#define INT_STS_TDFA_ (0x00000200UL) // R/WC +#define INT_STS_TSFF_ (0x00000100UL) // R/WC +#define INT_STS_TSFL_ (0x00000080UL) // R/WC +#define INT_STS_RDFO_ (0x00000040UL) // R/WC +#define INT_STS_RDFL_ (0x00000020UL) // R/WC +#define INT_STS_RSFF_ (0x00000010UL) // R/WC +#define INT_STS_RSFL_ (0x00000008UL) // R/WC +#define INT_STS_GPIO2_INT_ (0x00000004UL) // R/WC +#define INT_STS_GPIO1_INT_ (0x00000002UL) // R/WC +#define INT_STS_GPIO0_INT_ (0x00000001UL) // R/WC + +#define INT_EN (0x5CUL) +#define INT_EN_SW_INT_EN_ (0x80000000UL) // R/W +#define INT_EN_TXSTOP_INT_EN_ (0x02000000UL) // R/W +#define INT_EN_RXSTOP_INT_EN_ (0x01000000UL) // R/W +#define INT_EN_RXDFH_INT_EN_ (0x00800000UL) // R/W +#define INT_EN_RXDF_INT_EN_ (0x00400000UL) // R/W +#define INT_EN_TIOC_INT_EN_ (0x00200000UL) // R/W +#define INT_EN_RXD_INT_EN_ (0x00100000UL) // R/W +#define INT_EN_GPT_INT_EN_ (0x00080000UL) // R/W +#define INT_EN_PHY_INT_EN_ (0x00040000UL) // R/W +#define INT_EN_PME_INT_EN_ (0x00020000UL) // R/W +#define INT_EN_TXSO_EN_ (0x00010000UL) // R/W +#define INT_EN_RWT_EN_ (0x00008000UL) // R/W +#define INT_EN_RXE_EN_ (0x00004000UL) // R/W +#define INT_EN_TXE_EN_ (0x00002000UL) // R/W +#define INT_EN_ERX_EN_ (0x00001000UL) // R/W +#define INT_EN_TDFU_EN_ (0x00000800UL) // R/W +#define INT_EN_TDFO_EN_ (0x00000400UL) // R/W +#define INT_EN_TDFA_EN_ (0x00000200UL) // R/W +#define INT_EN_TSFF_EN_ (0x00000100UL) // R/W +#define INT_EN_TSFL_EN_ (0x00000080UL) // R/W +#define INT_EN_RDFO_EN_ (0x00000040UL) // R/W +#define INT_EN_RDFL_EN_ (0x00000020UL) // R/W +#define INT_EN_RSFF_EN_ (0x00000010UL) // R/W +#define INT_EN_RSFL_EN_ (0x00000008UL) // R/W +#define INT_EN_GPIO2_INT_ (0x00000004UL) // R/W +#define INT_EN_GPIO1_INT_ (0x00000002UL) // R/W +#define INT_EN_GPIO0_INT_ (0x00000001UL) // R/W + +#define BYTE_TEST (0x64UL) +#define FIFO_INT (0x68UL) +#define FIFO_INT_TX_AVAIL_LEVEL_ (0xFF000000UL) // R/W +#define FIFO_INT_TX_STS_LEVEL_ (0x00FF0000UL) // R/W +#define FIFO_INT_RX_AVAIL_LEVEL_ (0x0000FF00UL) // R/W +#define FIFO_INT_RX_STS_LEVEL_ (0x000000FFUL) // R/W + +#define RX_CFG (0x6CUL) +#define RX_CFG_RX_END_ALGN_ (0xC0000000UL) // R/W +#define RX_CFG_RX_END_ALGN4_ (0x00000000UL) // R/W +#define RX_CFG_RX_END_ALGN16_ (0x40000000UL) // R/W +#define RX_CFG_RX_END_ALGN32_ (0x80000000UL) // R/W +#define RX_CFG_RX_DMA_CNT_ (0x0FFF0000UL) // R/W +#define RX_CFG_RX_DUMP_ (0x00008000UL) // R/W +#define RX_CFG_RXDOFF_ (0x00001F00UL) // R/W +#define RX_CFG_RXDOFF_2_ (0x00000200UL) //Rx data offset is 2 +#define RX_CFG_RXDOFF_18_ (0x00001200UL) //Rx data offset is 0x12 +#define RX_CFG_RXBAD_ (0x00000001UL) // R/W + +#define TX_CFG (0x70UL) +#define TX_CFG_TX_DMA_LVL_ (0xE0000000UL) // R/W +#define TX_CFG_TX_DMA_CNT_ (0x0FFF0000UL) // R/W Self Clearing +#define TX_CFG_TXS_DUMP_ (0x00008000UL) // Self Clearing +#define TX_CFG_TXD_DUMP_ (0x00004000UL) // Self Clearing +#define TX_CFG_TXSAO_ (0x00000004UL) // R/W +#define TX_CFG_TX_ON_ (0x00000002UL) // R/W +#define TX_CFG_STOP_TX_ (0x00000001UL) // Self Clearing + +#define HW_CFG (0x74UL) +#define HW_CFG_AMDIX_EN_STRAP_STS_ (0x01000000UL) +#define HW_CFG_TTM_ (0x00200000UL) // R/W +#define HW_CFG_SF_ (0x00100000UL) // R/W +#define HW_CFG_TX_FIF_SZ_ (0x000F0000UL) // R/W +#define HW_CFG_TR_ (0x00003000UL) // R/W +#define HW_CFG_PHY_CLK_SEL_ (0x00000060UL) // R/W +#define HW_CFG_PHY_CLK_SEL_INT_PHY_ (0x00000000UL) // R/W +#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ (0x00000020UL) // R/W +#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ (0x00000040UL) // R/W +#define HW_CFG_SMI_SEL_ (0x00000010UL) // R/W +#define HW_CFG_EXT_PHY_DET_ (0x00000008UL) // RO +#define HW_CFG_EXT_PHY_EN_ (0x00000004UL) // R/W +#define HW_CFG_32_16_BIT_MODE_ (0x00000004UL) // RO +#define HW_CFG_SRST_TO_ (0x00000002UL) // RO +#define HW_CFG_SRST_ (0x00000001UL) // Self Clearing + +#define RX_DP_CTRL (0x78UL) +#define RX_DP_CTRL_RX_FFWD_ (0x00000FFFUL) // R/W +#define RX_DP_CTRL_FFWD_BUSY_ (0x80000000UL) // RO + +#define RX_FIFO_INF (0x7CUL) +#define RX_FIFO_INF_RXSUSED_ (0x00FF0000UL) // RO +#define RX_FIFO_INF_RXDUSED_ (0x0000FFFFUL) // RO + +#define TX_FIFO_INF (0x80UL) +#define TX_FIFO_INF_TSUSED_ (0x00FF0000UL) // RO +#define TX_FIFO_INF_TSFREE_ (0x00FF0000UL) // RO +#define TX_FIFO_INF_TDFREE_ (0x0000FFFFUL) // RO + +#define PMT_CTRL (0x84UL) +#define PMT_CTRL_PM_MODE_ (0x00018000UL) // Self Clearing +#define PMT_CTRL_PHY_RST_ (0x00000400UL) // Self Clearing +#define PMT_CTRL_WOL_EN_ (0x00000200UL) // R/W +#define PMT_CTRL_ED_EN_ (0x00000100UL) // R/W +#define PMT_CTRL_PME_TYPE_ (0x00000040UL) // R/W Not Affected by SW Reset +#define PMT_CTRL_WUPS_ (0x00000030UL) // R/WC +#define PMT_CTRL_WUPS_NOWAKE_ (0x00000000UL) // R/WC +#define PMT_CTRL_WUPS_ED_ (0x00000010UL) // R/WC +#define PMT_CTRL_WUPS_WOL_ (0x00000020UL) // R/WC +#define PMT_CTRL_WUPS_MULTI_ (0x00000030UL) // R/WC +#define PMT_CTRL_PME_IND_ (0x00000008UL) // R/W +#define PMT_CTRL_PME_POL_ (0x00000004UL) // R/W +#define PMT_CTRL_PME_EN_ (0x00000002UL) // R/W Not Affected by SW Reset +#define PMT_CTRL_READY_ (0x00000001UL) // RO + +#define GPIO_CFG (0x88UL) +#define GPIO_CFG_LED3_EN_ (0x40000000UL) // R/W +#define GPIO_CFG_LED2_EN_ (0x20000000UL) // R/W +#define GPIO_CFG_LED1_EN_ (0x10000000UL) // R/W +#define GPIO_CFG_GPIO2_INT_POL_ (0x04000000UL) // R/W +#define GPIO_CFG_GPIO1_INT_POL_ (0x02000000UL) // R/W +#define GPIO_CFG_GPIO0_INT_POL_ (0x01000000UL) // R/W +#define GPIO_CFG_EEPR_EN_ (0x00E00000UL) // R/W +#define GPIO_CFG_GPIOBUF2_ (0x00040000UL) // R/W +#define GPIO_CFG_GPIOBUF1_ (0x00020000UL) // R/W +#define GPIO_CFG_GPIOBUF0_ (0x00010000UL) // R/W +#define GPIO_CFG_GPIODIR2_ (0x00000400UL) // R/W +#define GPIO_CFG_GPIODIR1_ (0x00000200UL) // R/W +#define GPIO_CFG_GPIODIR0_ (0x00000100UL) // R/W +#define GPIO_CFG_GPIOD4_ (0x00000020UL) // R/W +#define GPIO_CFG_GPIOD3_ (0x00000010UL) // R/W +#define GPIO_CFG_GPIOD2_ (0x00000004UL) // R/W +#define GPIO_CFG_GPIOD1_ (0x00000002UL) // R/W +#define GPIO_CFG_GPIOD0_ (0x00000001UL) // R/W + +#define GPT_CFG (0x8CUL) +#define GPT_CFG_TIMER_EN_ (0x20000000UL) // R/W +#define GPT_CFG_GPT_LOAD_ (0x0000FFFFUL) // R/W + +#define GPT_CNT (0x90UL) +#define GPT_CNT_GPT_CNT_ (0x0000FFFFUL) // RO + +#define FPGA_REV (0x94UL) +#define FPGA_REV_FPGA_REV_ (0x0000FFFFUL) // RO + +#define WORD_SWAP (0x98UL) +#define FREE_RUN (0x9CUL) +#define RX_DROP (0xA0UL) +#define MAC_CSR_CMD (0xA4UL) +#define MAC_CSR_CMD_CSR_BUSY_ (0x80000000UL) // Self Clearing +#define MAC_CSR_CMD_R_NOT_W_ (0x40000000UL) // R/W +#define MAC_CSR_CMD_CSR_ADDR_ (0x000000FFUL) // R/W + +#define MAC_CSR_DATA (0xA8UL) +#define AFC_CFG (0xACUL) +#define AFC_CFG_AFC_HI_ (0x00FF0000UL) // R/W +#define AFC_CFG_AFC_LO_ (0x0000FF00UL) // R/W +#define AFC_CFG_BACK_DUR_ (0x000000F0UL) // R/W +#define AFC_CFG_FCMULT_ (0x00000008UL) // R/W +#define AFC_CFG_FCBRD_ (0x00000004UL) // R/W +#define AFC_CFG_FCADD_ (0x00000002UL) // R/W +#define AFC_CFG_FCANY_ (0x00000001UL) // R/W + +#define E2P_CMD (0xB0UL) +#define E2P_CMD_EPC_BUSY_ (0x80000000UL) // Self Clearing +#define E2P_CMD_EPC_CMD_ (0x70000000UL) // R/W +#define E2P_CMD_EPC_CMD_READ_ (0x00000000UL) // R/W +#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000UL) // R/W +#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000UL) // R/W +#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000UL) // R/W +#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000UL) // R/W +#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000UL) // R/W +#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000UL) // R/W +#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000UL) // R/W +#define E2P_CMD_EPC_TIMEOUT_ (0x00000200UL) // R +#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100UL) // RO +#define E2P_CMD_EPC_ADDR_ (0x000000FFUL) // R/W + +#define E2P_DATA (0xB4UL) +#define E2P_DATA_EEPROM_DATA_ (0x000000FFUL) // R/W +//end of lan register offsets and bit definitions + +#define LAN_REGISTER_EXTENT (0x00002000UL) + +//The following describes the synchronization policies used in this driver. +//Register Name Policy +//RX_DATA_FIFO Only used by the Rx Thread, Rx_ProcessPackets +//TX_DATA_FIFO Only used by the Tx Thread, Tx_SendSkb +//RX_STATUS_FIFO Only used by the Rx Thread, Rx_ProcessPackets +//RX_STATUS_FIFO_PEEK Not used. +//TX_STATUS_FIFO Used in Tx_CompleteTx in Tx_UpdateTxCounters. +// protected by TxCounterLock +//TX_STATUS_FIFO_PEEK Not used. +//ID_REV Read only +//INT_CFG Set in Lan_Initialize, +// protected by IntEnableLock +//INT_STS Sharable, +//INT_EN Initialized at startup, +// Used in Rx_ProcessPackets +// otherwise protected by IntEnableLock +//BYTE_TEST Read Only +//FIFO_INT Initialized at startup, +// During run time only accessed by +// Tx_HandleInterrupt, and Tx_SendSkb and done in a safe manner +//RX_CFG Used during initialization +// During runtime only used by Rx Thread +//TX_CFG Only used during initialization +//HW_CFG Only used during initialization +//RX_DP_CTRL Only used in Rx Thread, in Rx_FastForward +//RX_FIFO_INF Read Only, Only used in Rx Thread, in Rx_PopRxStatus +//TX_FIFO_INF Read Only, Only used in Tx Thread, in Tx_GetTxStatusCount, Tx_SendSkb, Tx_CompleteTx +//PMT_CTRL Not Used +//GPIO_CFG used during initialization, in Lan_Initialize +// used for debugging +// used during EEPROM access. +// safe enough to not require a lock +//GPT_CFG protected by GpTimerLock +//GPT_CNT Not Used +//WORD_SWAP Not Used +//FREE_RUN Read only +//RX_DROP Used in Rx Interrupt Handler, +// and get_stats. +// safe enough to not require a lock. +//MAC_CSR_CMD Protected by MacPhyLock +//MAC_CSR_DATA Protected by MacPhyLock +// Because the two previous MAC_CSR_ registers are protected +// All MAC, and PHY registers are protected as well. +//AFC_CFG Used during initialization, in Lan_Initialize +// During run time, used in timer call back, in Phy_UpdateLinkMode +//E2P_CMD Used during initialization, in Lan_Initialize +// Used in EEPROM functions +//E2P_DATA Used in EEPROM functions + +#include "platform_alchemy.h" + +#define Lan_GetRegDW(dwOffset) \ + Platform_GetRegDW(privateData->dwLanBase,dwOffset) + +#define Lan_SetRegDW(dwOffset,dwVal) \ + Platform_SetRegDW(privateData->dwLanBase,dwOffset,dwVal) + +#define Lan_ClrBitsDW(dwOffset,dwBits) \ + Platform_SetRegDW(privateData->dwLanBase, \ + dwOffset,Platform_GetRegDW(privateData->dwLanBase, \ + dwOffset)&(~dwBits)) + +#define Lan_SetBitsDW(dwOffset,dwBits) \ + Platform_SetRegDW(privateData->dwLanBase, \ + dwOffset,Platform_GetRegDW(privateData->dwLanBase, \ + dwOffset)|dwBits); + +#define LINK_OFF (0x00UL) +#define LINK_SPEED_10HD (0x01UL) +#define LINK_SPEED_10FD (0x02UL) +#define LINK_SPEED_100HD (0x04UL) +#define LINK_SPEED_100FD (0x08UL) +#define LINK_SYMMETRIC_PAUSE (0x10UL) +#define LINK_ASYMMETRIC_PAUSE (0x20UL) +#define LINK_AUTO_NEGOTIATE (0x40UL) + +typedef unsigned long VL_KEY; +typedef struct _VERIFIABLE_LOCK { + spinlock_t Lock; + VL_KEY KeyCode; +} VERIFIABLE_LOCK, * PVERIFIABLE_LOCK; + +void Vl_InitLock(PVERIFIABLE_LOCK pVl); +bool Vl_CheckLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode); +VL_KEY Vl_WaitForLock(PVERIFIABLE_LOCK pVl,unsigned long *pdwIntFlags); +void Vl_ReleaseLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode,unsigned long *pdwIntFlags); + +typedef struct _PRIVATE_DATA { + u32 dwLanBase; + u32 dwIdRev; + u32 dwFpgaRev; + struct net_device *dev; + u32 dwGeneration;//used to decide which workarounds apply + bool UseScatterGather; + bool UseTxCsum; + bool UseRxCsum; + + + spinlock_t IntEnableLock; + bool LanInitialized; + VERIFIABLE_LOCK MacPhyLock; + bool ExtPhy; // + + u32 dwTxDmaCh; + bool TxDmaChReserved; + DMA_XFER TxDmaXfer; + u32 dwTxDmaThreshold; + u32 dwTxQueueDisableMask; + struct sk_buff *TxSkb; + spinlock_t TxSkbLock; + spinlock_t TxQueueLock; + spinlock_t TxCounterLock; + bool TxInitialized; + + + + // BYTE bCoalesceBuf[1600]; + + + + // struct sk_buff *TxSkbPending; + + + u32 dwRxDmaCh; + struct sk_buff *RxSkb; + bool RxDmaChReserved; + u32 dwRxDmaThreshold; + bool RxCongested; + u32 dwRxOffCount; + bool RxOverrun; + u32 RxOverrunCount; + u32 RxStatusDWReadCount; + u32 RxDataDWReadCount; + u32 RxPacketReadCount; + u32 RxFastForwardCount; + u32 RxPioReadCount; + u32 RxDmaReadCount; + u32 RxCongestedCount; + u32 RxDumpCount; + u32 LastReasonForReleasingCPU; + u32 LastRxStatus1; + u32 LastRxStatus2; + u32 LastRxStatus3; + u32 LastIntStatus1; + u32 LastIntStatus2; + u32 LastIntStatus3; + u32 RxUnloadProgress; + u32 RxUnloadPacketProgress; + u32 RxMaxDataFifoSize; + // bool RxVLanPkt; + + //NAPI + int RxWorkLimit; + int RxPacketsReceived; + // bool RxDone; + + + u32 RxFlowCurrentThroughput; + u32 RxFlowCurrentPacketCount; + u32 RxFlowCurrentWorkLoad; + bool MeasuringRxThroughput; + u32 RxFlowMeasuredMaxThroughput; + u32 RxFlowMeasuredMaxPacketCount; + + //RX_FLOW_ACTIVATION specifies the percentage that RxFlowCurrentWorkLoad must exceed + // RxFlowMaxWorkLoad in order to activate flow control +#define RX_FLOW_ACTIVATION (4UL) + + //RX_FLOW_DEACTIVATION specifies the percentage that RxFlowCurrentWorkLoad must reduce + // from RxFlowMaxWorkLoad in order to deactivate flow control +#define RX_FLOW_DEACTIVATION (25UL) + u32 RxFlowMaxWorkLoad; + + FLOW_CONTROL_PARAMETERS RxFlowParameters; + + u32 RxFlowBurstWorkLoad; + u32 RxFlowBurstMaxWorkLoad; + bool RxFlowControlActive; + bool RxFlowBurstActive; + u32 RxInterrupts; + +#define GPT_SCHEDULE_DEPTH (3) + void *GptFunction[GPT_SCHEDULE_DEPTH]; + u32 GptCallTime[GPT_SCHEDULE_DEPTH]; + u32 Gpt_scheduled_slot_index; + spinlock_t GpTimerLock; + + bool Running; + struct net_device_stats stats; + + u32 dwPhyAddress; + u32 dwPhyId; +#ifdef USE_LED1_WORK_AROUND + u32 NotUsingExtPhy; +#endif + BYTE bPhyModel; + BYTE bPhyRev; + u32 dwLinkSpeed; + u32 dwLinkSettings; + u32 dwRemoteFaultCount; + struct timer_list LinkPollingTimer; + bool StopLinkPolling; + WORD wLastADV; + WORD wLastADVatRestart; +#ifdef USE_PHY_WORK_AROUND +#define MIN_PACKET_SIZE (64) + u32 dwTxStartMargen; + BYTE LoopBackTxPacket[MIN_PACKET_SIZE]; + u32 dwTxEndMargen; + u32 dwRxStartMargen; + BYTE LoopBackRxPacket[MIN_PACKET_SIZE]; + u32 dwRxEndMargen; + u32 dwResetCount; +#endif + + bool SoftwareInterruptSignal; + + PLATFORM_DATA PlatformData; + +#define SMSC_IF_NAME_SIZE (10) + char ifName[SMSC_IF_NAME_SIZE]; + + /* for Rx Multicast work around */ + volatile u32 HashLo; + volatile u32 HashHi; + volatile bool MulticastUpdatePending; + volatile u32 set_bits_mask; + volatile u32 clear_bits_mask; + +} PRIVATE_DATA, *PPRIVATE_DATA; + + +/* + **************************************************************************** + **************************************************************************** + * MAC Control and Status Register (Indirect Address) + * Offset (through the MAC_CSR CMD and DATA port) + **************************************************************************** + **************************************************************************** + * + */ +#define MAC_CR (0x01UL) // R/W + +/* MAC_CR - MAC Control Register */ +#define MAC_CR_RXALL_ (0x80000000UL) +#define MAC_CR_HBDIS_ (0x10000000UL) +#define MAC_CR_RCVOWN_ (0x00800000UL) +#define MAC_CR_LOOPBK_ (0x00200000UL) +#define MAC_CR_FDPX_ (0x00100000UL) +#define MAC_CR_MCPAS_ (0x00080000UL) +#define MAC_CR_PRMS_ (0x00040000UL) +#define MAC_CR_INVFILT_ (0x00020000UL) +#define MAC_CR_PASSBAD_ (0x00010000UL) +#define MAC_CR_HFILT_ (0x00008000UL) +#define MAC_CR_HPFILT_ (0x00002000UL) +#define MAC_CR_LCOLL_ (0x00001000UL) +#define MAC_CR_BCAST_ (0x00000800UL) +#define MAC_CR_DISRTY_ (0x00000400UL) +#define MAC_CR_PADSTR_ (0x00000100UL) +#define MAC_CR_BOLMT_MASK_ (0x000000C0UL) +#define MAC_CR_DFCHK_ (0x00000020UL) +#define MAC_CR_TXEN_ (0x00000008UL) +#define MAC_CR_RXEN_ (0x00000004UL) + +#define ADDRH (0x02UL) // R/W mask 0x0000FFFFUL +#define ADDRL (0x03UL) // R/W mask 0xFFFFFFFFUL +#define HASHH (0x04UL) // R/W +#define HASHL (0x05UL) // R/W + +#define MII_ACC (0x06UL) // R/W +#define MII_ACC_PHY_ADDR_ (0x0000F800UL) +#define MII_ACC_MIIRINDA_ (0x000007C0UL) +#define MII_ACC_MII_WRITE_ (0x00000002UL) +#define MII_ACC_MII_BUSY_ (0x00000001UL) + +#define MII_DATA (0x07UL) // R/W mask 0x0000FFFFUL + +#define FLOW (0x08UL) // R/W +#define FLOW_FCPT_ (0xFFFF0000UL) +#define FLOW_FCPASS_ (0x00000004UL) +#define FLOW_FCEN_ (0x00000002UL) +#define FLOW_FCBSY_ (0x00000001UL) + +#define VLAN1 (0x09UL) // R/W mask 0x0000FFFFUL +#define VLAN2 (0x0AUL) // R/W mask 0x0000FFFFUL + +#define WUFF (0x0BUL) // WO + +#define WUCSR (0x0CUL) // R/W +#define WUCSR_GUE_ (0x00000200UL) +#define WUCSR_WUFR_ (0x00000040UL) +#define WUCSR_MPR_ (0x00000020UL) +#define WUCSR_WAKE_EN_ (0x00000004UL) +#define WUCSR_MPEN_ (0x00000002UL) + +#define COE_CR 0xDUL +#define TX_COE_EN 0x00010000UL +#define RX_COE_MODE 0x00000002UL +#define RX_COE_EN 0x00000001UL + + +bool Mac_Initialize(PPRIVATE_DATA privateData); +static bool MacNotBusy(PPRIVATE_DATA privateData,VL_KEY keyCode); +u32 Mac_GetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,VL_KEY keyCode); +void Mac_SetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,u32 dwVal,VL_KEY keyCode); + +/* + **************************************************************************** + * Chip Specific MII Defines + **************************************************************************** + * + * Phy register offsets and bit definitions + * + */ +#define LAN9118_PHY_ID (0x00C0001C) + +#define PHY_BCR ((u32)0U) +#define PHY_BCR_RESET_ ((WORD)0x8000U) +#define PHY_BCR_LOOPBACK_ ((WORD)0x4000U) +#define PHY_BCR_SPEED_SELECT_ ((WORD)0x2000U) +#define PHY_BCR_AUTO_NEG_ENABLE_ ((WORD)0x1000U) +#define PHY_BCR_RESTART_AUTO_NEG_ ((WORD)0x0200U) +#define PHY_BCR_DUPLEX_MODE_ ((WORD)0x0100U) + +#define PHY_BSR ((u32)1U) +#define PHY_BSR_LINK_STATUS_ ((WORD)0x0004U) +#define PHY_BSR_REMOTE_FAULT_ ((WORD)0x0010U) +#define PHY_BSR_AUTO_NEG_COMP_ ((WORD)0x0020U) + +#define PHY_ID_1 ((u32)2U) +#define PHY_ID_2 ((u32)3U) + +#define PHY_ANEG_ADV ((u32)4U) +#define PHY_ANEG_ADV_PAUSE_ ((WORD)0x0C00) +#define PHY_ANEG_ADV_ASYMP_ ((WORD)0x0800) +#define PHY_ANEG_ADV_SYMP_ ((WORD)0x0400) +#define PHY_ANEG_ADV_10H_ ((WORD)0x20) +#define PHY_ANEG_ADV_10F_ ((WORD)0x40) +#define PHY_ANEG_ADV_100H_ ((WORD)0x80) +#define PHY_ANEG_ADV_100F_ ((WORD)0x100) +#define PHY_ANEG_ADV_SPEED_ ((WORD)0x1E0) + +#define PHY_ANEG_LPA ((u32)5U) +#define PHY_ANEG_LPA_ASYMP_ ((WORD)0x0800) +#define PHY_ANEG_LPA_SYMP_ ((WORD)0x0400) +#define PHY_ANEG_LPA_100FDX_ ((WORD)0x0100) +#define PHY_ANEG_LPA_100HDX_ ((WORD)0x0080) +#define PHY_ANEG_LPA_10FDX_ ((WORD)0x0040) +#define PHY_ANEG_LPA_10HDX_ ((WORD)0x0020) + +#define PHY_MODE_CTRL_STS ((u32)17) // Mode Control/Status Register +#define MODE_CTRL_STS_FASTRIP_ ((WORD)0x4000U) +#define MODE_CTRL_STS_EDPWRDOWN_ ((WORD)0x2000U) +#define MODE_CTRL_STS_LOWSQEN_ ((WORD)0x0800U) +#define MODE_CTRL_STS_MDPREBP_ ((WORD)0x0400U) +#define MODE_CTRL_STS_FARLOOPBACK_ ((WORD)0x0200U) +#define MODE_CTRL_STS_FASTEST_ ((WORD)0x0100U) +#define MODE_CTRL_STS_REFCLKEN_ ((WORD)0x0010U) +#define MODE_CTRL_STS_PHYADBP_ ((WORD)0x0008U) +#define MODE_CTRL_STS_FORCE_G_LINK_ ((WORD)0x0004U) +#define MODE_CTRL_STS_ENERGYON_ ((WORD)0x0002U) + +#define SPECIAL_CTRL_STS ((u32)27) +#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((WORD)0x8000U) +#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((WORD)0x4000U) +#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((WORD)0x2000U) + +#define PHY_INT_SRC ((u32)29) +#define PHY_INT_SRC_ENERGY_ON_ ((WORD)0x0080U) +#define PHY_INT_SRC_ANEG_COMP_ ((WORD)0x0040U) +#define PHY_INT_SRC_REMOTE_FAULT_ ((WORD)0x0020U) +#define PHY_INT_SRC_LINK_DOWN_ ((WORD)0x0010U) + +#define PHY_INT_MASK ((u32)30) +#define PHY_INT_MASK_ENERGY_ON_ ((WORD)0x0080U) +#define PHY_INT_MASK_ANEG_COMP_ ((WORD)0x0040U) +#define PHY_INT_MASK_REMOTE_FAULT_ ((WORD)0x0020U) +#define PHY_INT_MASK_LINK_DOWN_ ((WORD)0x0010U) + +#define PHY_SPECIAL ((u32)31) +#define PHY_SPECIAL_SPD_ ((WORD)0x001CU) +#define PHY_SPECIAL_SPD_10HALF_ ((WORD)0x0004U) +#define PHY_SPECIAL_SPD_10FULL_ ((WORD)0x0014U) +#define PHY_SPECIAL_SPD_100HALF_ ((WORD)0x0008U) +#define PHY_SPECIAL_SPD_100FULL_ ((WORD)0x0018U) + +#define AMDIX_DISABLE_STRAIGHT ((WORD)0x0U) +#define AMDIX_DISABLE_CROSSOVER ((WORD)0x01U) +#define AMDIX_ENABLE ((WORD)0x02U) + +bool Phy_Initialize( + PPRIVATE_DATA privateData, + u32 dwPhyAddress, + u32 dwLinkMode); +void Phy_SetLink(PPRIVATE_DATA privateData, + u32 dwLinkRequest); +WORD Phy_GetRegW( + PPRIVATE_DATA privateData, + u32 dwRegIndex, + VL_KEY keyCode); +void Phy_SetRegW( + PPRIVATE_DATA privateData, + u32 dwRegIndex, + WORD wVal, + VL_KEY keyCode); +void Phy_UpdateLinkMode( + PPRIVATE_DATA privateData); +void Phy_GetLinkMode( + PPRIVATE_DATA privateData, + VL_KEY keyCode); +void Phy_CheckLink(unsigned long ptr); +void Phy_SetAutoMdixSts(PPRIVATE_DATA privateData, + WORD wAutoMdixSts); +void Phy_GetAutoMdixSts(PPRIVATE_DATA privateData); + + + + + +TIME_SPAN Gpt_FreeRunCompare(u32 time1,u32 time2); +void Gpt_ScheduleInterrupt(PPRIVATE_DATA privateData,TIME_SPAN timeSpan); +void Gpt_CancelInterrupt(PPRIVATE_DATA privateData); +void Gpt_CancelCallBack( + PPRIVATE_DATA privateData, + void (*callBackFunction)(PPRIVATE_DATA privateData)); +void Gpt_ScheduleCallBack( + PPRIVATE_DATA privateData, + void (*callBackFunction)(PPRIVATE_DATA privateData), + u32 callBackTime);//100uS units relative to now +bool Gpt_HandleInterrupt( + PPRIVATE_DATA privateData,u32 dwIntSts); +void GptCB_RxCompleteMulticast(PPRIVATE_DATA privateData); +void GptCB_RestartBurst(PPRIVATE_DATA privateData); +void GptCB_MeasureRxThroughput(PPRIVATE_DATA privateData); + +void Tx_Initialize( + PPRIVATE_DATA privateData, + u32 dwTxDmaCh, + u32 dwTxDmaThreshold); +void Tx_SendSkb( + PPRIVATE_DATA privateData, + struct sk_buff *skb); +bool Tx_HandleInterrupt( + PPRIVATE_DATA privateData,u32 dwIntSts); + +void Tx_StopQueue( + PPRIVATE_DATA privateData,u32 dwSource); +void Tx_WakeQueue( + PPRIVATE_DATA privateData,u32 dwSource); + +static u32 Tx_GetTxStatusCount( + PPRIVATE_DATA privateData); +static u32 Tx_CompleteTx( + PPRIVATE_DATA privateData); +void Tx_UpdateTxCounters( + PPRIVATE_DATA privateData); + +void Tx_CompleteDma( + PPRIVATE_DATA privateData); + +void CalculateTxChecksumOffset( + struct sk_buff *skb, + int *csum_start_offset); + +void rkdump(unsigned char *p, unsigned short len); + +void Rx_Initialize( + PPRIVATE_DATA privateData, + u32 dwRxDmaCh, + u32 dwDmaThreshold); + +void Rx_CompleteMulticastUpdate (PPRIVATE_DATA privateData); +static void Rx_HandleOverrun(PPRIVATE_DATA privateData); +static void Rx_HandOffSkb( + PPRIVATE_DATA privateData, + struct sk_buff *skb); +static u32 Rx_PopRxStatus( + PPRIVATE_DATA privateData); +void Rx_CountErrors(PPRIVATE_DATA privateData,u32 dwRxStatus); +void Rx_FastForward(PPRIVATE_DATA privateData,u32 dwDwordCount); +void Rx_ProcessPackets(PPRIVATE_DATA privateData); +void Rx_BeginMulticastUpdate (PPRIVATE_DATA privateData); + +unsigned long Rx_TaskletParameter=0; + +void Rx_ProcessPacketsTasklet(unsigned long data); +DECLARE_TASKLET(Rx_Tasklet,Rx_ProcessPacketsTasklet,0); + +#ifdef LINUX_2_6_OR_NEWER +int Smsc9118_rx_poll(struct net_device *dev,int * budget); +#endif + +bool RxStop_HandleInterrupt( + PPRIVATE_DATA privateData, + u32 dwIntSts); +bool Rx_HandleInterrupt( + PPRIVATE_DATA privateData, + u32 dwIntSts); +static u32 Rx_Hash(BYTE addr[6]); + +void Rx_SetMulticastList( + struct net_device *dev); +void Rx_ReceiverOff( + PPRIVATE_DATA privateData); +void Rx_ReceiverOn( + PPRIVATE_DATA privateData, VL_KEY callerKeyCode); + + +void Eeprom_EnableAccess(PPRIVATE_DATA privateData); +void Eeprom_DisableAccess(PPRIVATE_DATA privateData); + +bool Eeprom_IsMacAddressLoaded(PPRIVATE_DATA privateData); +bool Eeprom_IsBusy(PPRIVATE_DATA privateData); +bool Eeprom_Timeout(PPRIVATE_DATA privateData); + +bool Eeprom_ReadLocation( + PPRIVATE_DATA privateData,BYTE address, BYTE * data); +bool Eeprom_EnableEraseAndWrite( + PPRIVATE_DATA privateData); +bool Eeprom_DisableEraseAndWrite( + PPRIVATE_DATA privateData); +bool Eeprom_WriteLocation( + PPRIVATE_DATA privateData,BYTE address,BYTE data); +bool Eeprom_EraseAll( + PPRIVATE_DATA privateData); +bool Eeprom_Reload( + PPRIVATE_DATA privateData); + +bool Eeprom_SaveMacAddress( + PPRIVATE_DATA privateData, + u32 dwHi16,u32 dwLo32); + + +#define OLD_REGISTERS(privData) (((privData->dwIdRev)==0x01180000UL)&& \ + ((privData->dwFpgaRev)>=0x01)&& \ + ((privData->dwFpgaRev)<=0x25)) + +extern volatile u32 g_GpioSetting; +#define GP_0 (0x01UL) +#define GP_1 (0x02UL) +#define GP_2 (0x04UL) +#define GP_3 (0x08UL) +#define GP_4 (0x10UL) +#define GP_OFF (0x00UL) +#define GP_ISR GP_OFF +#define GP_RX GP_OFF +#define GP_TX GP_OFF +#define GP_BEGIN_MULTICAST_UPDATE GP_OFF +#define GP_COMPLETE_MULTICAST_UPDATE GP_OFF + +#define SET_GPIO(gpioBit) \ + if(debug_mode&0x04UL) { \ + g_GpioSetting|=gpioBit; \ + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); \ + } + +#define CLEAR_GPIO(gpioBit) \ + if(debug_mode&0x04UL) { \ + g_GpioSetting&=(~gpioBit); \ + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); \ + } + +#define PULSE_GPIO(gpioBit,count) \ + if(debug_mode&0x04UL) { \ + u32 pulseNum=0; \ + /*make first pulse longer */ \ + SET_GPIO(gpioBit); \ + while(pulseNum<count) { \ + SET_GPIO(gpioBit); \ + CLEAR_GPIO(gpioBit); \ + pulseNum++; \ + } \ + } +#ifdef USE_LED1_WORK_AROUND +volatile u32 g_GpioSettingOriginal; +#endif + +bool Lan_Initialize( + PPRIVATE_DATA privateData,u32 dwIntCfg, + u32 dwTxFifSz,u32 dwAfcCfg); +void Lan_EnableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask); +void Lan_DisableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask); +void Lan_EnableIRQ(PPRIVATE_DATA privateData); +void Lan_DisableIRQ(PPRIVATE_DATA privateData); +void Lan_SetIntDeas(PPRIVATE_DATA privateData,u32 dwIntDeas); +void Lan_SetTDFL(PPRIVATE_DATA privateData,BYTE level); +void Lan_SetTSFL(PPRIVATE_DATA privateData,BYTE level); +void Lan_SetRDFL(PPRIVATE_DATA privateData,BYTE level); +void Lan_SetRSFL(PPRIVATE_DATA privateData,BYTE level); + +void Lan_SignalSoftwareInterrupt(PPRIVATE_DATA privateData); +bool Lan_HandleSoftwareInterrupt(PPRIVATE_DATA privateData,u32 dwIntSts); + +void Lan_ShowRegs(PPRIVATE_DATA privateData); + +#include "ioctl_118.h" + +static u32 lan_base=0x0UL; +module_param(lan_base, int, S_IRUGO); +MODULE_PARM_DESC(lan_base,"Base Address of LAN9118, (default: choosen by platform code)"); + +static u32 bus_width=0UL; +module_param(bus_width, int, S_IRUGO); +MODULE_PARM_DESC(bus_width,"Force bus width of 16 or 32 bits, default: autodetect"); + +static u32 link_mode=0x7FUL; +module_param(link_mode, int, S_IRUGO); +MODULE_PARM_DESC(link_mode,"Set Link speed and Duplex, 1=10HD,2=10FD,4=100HD,8=100FD,default=0xF"); + +static u32 AutoMdix=0x3U; +module_param(AutoMdix, int, S_IRUGO); +MODULE_PARM_DESC(AutoMdix,"Set Auto-MDIX state, 0=StraightCable,1=CrossOver,2=Enable AMDIX,3=controlled by Strap"); + +static u32 irq=PLATFORM_IRQ; +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq,"Force use of specific IRQ, (default: choosen by platform code)"); + +static u32 int_deas=0xFFFFFFFFUL; +module_param(int_deas, int, S_IRUGO); +MODULE_PARM_DESC(int_deas,"Interrupt Deassertion Interval in 10uS units"); + +static u32 irq_pol=PLATFORM_IRQ_POL; +module_param(irq_pol, int, S_IRUGO); +MODULE_PARM_DESC(irq_pol,"IRQ Polarity bit, see definition of INT_CFG register"); + +static u32 irq_type=PLATFORM_IRQ_TYPE; +module_param(irq_type, int, S_IRUGO); +MODULE_PARM_DESC(irq_type,"IRQ Buffer Type bit, see definition of INT_CFG register"); + +static u32 rx_dma=PLATFORM_RX_DMA; +module_param(rx_dma, int, S_IRUGO); +MODULE_PARM_DESC(rx_dma,"Receiver DMA Channel, 255=find available channel, 256=use PIO"); + +static u32 tx_dma=PLATFORM_TX_DMA; +module_param(tx_dma, int, S_IRUGO); +MODULE_PARM_DESC(tx_dma,"Transmitter DMA Channel, 255=find available channel, 256=use PIO"); + +static u32 dma_threshold=PLATFORM_DMA_THRESHOLD; +module_param(dma_threshold, int, S_IRUGO); +MODULE_PARM_DESC(dma_threshold,"Specifies the minimum packet size for DMA to be used."); + +static u32 mac_addr_hi16=0xFFFFFFFF; +module_param(mac_addr_hi16, int, S_IRUGO); +MODULE_PARM_DESC(mac_addr_hi16,"Specifies the high 16 bits of the mac address"); + +static u32 mac_addr_lo32=0xFFFFFFFF; +module_param(mac_addr_lo32, int, S_IRUGO); +MODULE_PARM_DESC(mac_addr_lo32,"Specifies the low 32 bits of the mac address"); + +module_param(debug_mode, int, S_IRUGO); +MODULE_PARM_DESC(debug_mode,"bit 0 enables trace points, bit 1 enables warning points, bit 2 enables gpios"); + +static u32 tx_fif_sz=0x00050000UL; +module_param(tx_fif_sz, int, S_IRUGO); +MODULE_PARM_DESC(tx_fif_sz,"Specifies TX_FIF_SZ of the HW_CFG register"); + +static u32 afc_cfg=0xFFFFFFFFUL; +module_param(afc_cfg, int, S_IRUGO); +MODULE_PARM_DESC(afc_cfg,"Specifies the setting for the AFC_CFG register"); + +//static u32 tasklets=1UL; +//module_param(tasklets, int, S_IRUGO); +//MODULE_PARM_DESC(tasklets,"non-zero== use tasklets for receiving packets, zero==receive packets in ISR"); + +#define PROCESSING_MODE_IDLE (0UL) +#define PROCESSING_MODE_TASKLET (1UL) +#define PROCESSING_MODE_NAPI (2UL) + + +static u32 rx_mode=PROCESSING_MODE_TASKLET; +module_param(rx_mode, int, S_IRUGO); +MODULE_PARM_DESC(rx_mode,"0==use ISR, 1==use Rx Tasklet, 2==use NAPI"); + +#ifdef LINUX_2_6_OR_NEWER +static u32 napi_weight=4UL; +module_param(napi_weight, int, S_IRUGO); +MODULE_PARM_DESC(napi_weight,"The weight value to use if NAPI is used"); +#endif + + + +static u32 phy_addr=0xFFFFFFFFUL; +module_param(phy_addr, int, S_IRUGO); +MODULE_PARM_DESC(phy_addr,"phy_addr, 0xFFFFFFFF=use interal phy, 0-31=use external phy with specified address, else autodetect external phy addr"); + +static u32 max_throughput=0xFFFFFFFFUL; +module_param(max_throughput, int, S_IRUGO); +MODULE_PARM_DESC(max_throughput,"See readme.txt"); + +static u32 max_packet_count=0xFFFFFFFFUL; +module_param(max_packet_count, int, S_IRUGO); +MODULE_PARM_DESC(max_packet_count,"See Readme.txt"); + +static u32 packet_cost=0xFFFFFFFFUL; +module_param(packet_cost, int, S_IRUGO); +MODULE_PARM_DESC(packet_cost,"See Readme.txt"); + +static u32 burst_period=0xFFFFFFFFUL; +module_param(burst_period, int, S_IRUGO); +MODULE_PARM_DESC(burst_period,"See Readme.txt"); + +static u32 max_work_load=0xFFFFFFFFUL; +module_param(max_work_load, int, S_IRUGO); +MODULE_PARM_DESC(max_work_load,"See Readme.txt"); + +static int Scatter_gather=false; +module_param(Scatter_gather, bool, S_IRUGO); +MODULE_PARM_DESC(Scatter_gather,"Enable Scatter Gather"); + +static int tx_Csum=false; +module_param(tx_Csum, bool, S_IRUGO); +MODULE_PARM_DESC(tx_Csum,"Enable Tx Hardware Checksum Offload"); + +static int rx_Csum=false; +module_param(rx_Csum, bool, S_IRUGO); +MODULE_PARM_DESC(rx_Csum,"Enable Rx Hardware Checksum Offload"); + +/* The three parameters below are used for the new unkonw chip before we formally add them in the driver + So that in the future we can just immediately support chips with new IDs by passing the new id and timing at load time. + But we still need to add the supported new Chip id and the flow control parameters formally later. + */ +static u32 id_reg=0x0UL; +module_param(id_reg, int, S_IRUGO); +MODULE_PARM_DESC(id_reg,"Chip Id"); + +static u32 bus_timing=0UL; +module_param(bus_timing, int, S_IRUGO); +MODULE_PARM_DESC(bus_timing,"bus timing"); + + +static int Csum_Support=false; +module_param(Csum_Support, int, S_IRUGO); +MODULE_PARM_DESC(Csum_Support,"The chip has the ability of Checksum offload"); + + +MODULE_LICENSE("GPL"); + +int Smsc9118_init_module(void); +void Smsc9118_cleanup_module(void); +void Smsc9118_init(struct net_device *dev); +int Smsc9118_open(struct net_device *dev); +int Smsc9118_stop(struct net_device *dev); +int Smsc9118_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +struct net_device_stats * Smsc9118_get_stats(struct net_device *dev); +void Smsc9118_set_multicast_list(struct net_device *dev); +int Smsc9118_private_ioctl(PPRIVATE_DATA privateData,void *useraddr); +int Smsc9118_ethtool_ioctl(PPRIVATE_DATA privateData, void * userAddr); +int Smsc9118_do_ioctl(struct net_device *dev, struct ifreq *ifr,int cmd); +irqreturn_t Smsc9118_ISR(int irq,void *dev_id); + +#ifdef USING_LINT +//struct net_device SMSC9118; +#else //not USING_LINT +/* KH: Don't initialize here. */ +/* struct net_device SMSC9118 = {init: Smsc9118_init,}; */ +struct net_device *SMSC9118; +#endif //not USING_LINT + +int Smsc9118_init_module(void) +{ + int result=0; + int device_present=0; + + SMSC_TRACE("--> init_module()"); + SMSC_TRACE("Compiled: %s, %s",__DATE__,__TIME__); + SMSC_TRACE("Platform: %s",PLATFORM_NAME); + SMSC_TRACE("Driver Parameters"); + + if(lan_base==0UL) { + SMSC_TRACE(" lan_base = 0x%08X, driver will decide",lan_base); + } else { + SMSC_TRACE(" lan_base = 0x%08X",lan_base); + } + if((bus_width==16UL)||(bus_width==32UL)) { + SMSC_TRACE(" bus_width = %d",bus_width); + } else { + SMSC_TRACE(" bus_width = %d, driver will autodetect",bus_width); + } + if(link_mode>0x7FUL) { + SMSC_WARNING(" link_mode = %d, Unknown",link_mode); + link_mode=0x7FUL; + SMSC_WARNING(" resetting link_mode to %d, 100FD,100HD,10FD,10HD,ASYMP,SYMP,ANEG",link_mode); + } else if(link_mode==0UL) { + SMSC_TRACE(" link_mode = %d, LINK_OFF",link_mode); + } else { + SMSC_TRACE(" link_mode = 0x%X, %s,%s,%s,%s,%s,%s,%s", + link_mode, + (link_mode&LINK_SPEED_10HD)?"10HD":"", + (link_mode&LINK_SPEED_10FD)?"10FD":"", + (link_mode&LINK_SPEED_100HD)?"100HD":"", + (link_mode&LINK_SPEED_100FD)?"100FD":"", + (link_mode&LINK_ASYMMETRIC_PAUSE)?"ASYMP":"", + (link_mode&LINK_SYMMETRIC_PAUSE)?"SYMP":"", + (link_mode&LINK_AUTO_NEGOTIATE)?"ANEG":""); + } + SMSC_TRACE( " irq = %d",irq); + if(int_deas!=0xFFFFFFFFUL) { + if(int_deas>0xFFUL) { + SMSC_WARNING(" int_deas = %d, too high",int_deas); + int_deas=0xFFFFFFFFUL; + SMSC_WARNING(" resetting int_deas to %d",int_deas); + } + } + if(int_deas==0xFFFFFFFFUL) { + SMSC_TRACE( " int_deas = 0x%08X, use platform default",int_deas); + } else { + SMSC_TRACE( " int_deas = %d, %duS",int_deas,(unsigned int)(10UL*int_deas)); + } + if(irq_pol) { + SMSC_TRACE(" irq_pol = %d, IRQ output is active high",irq_pol); + } else { + SMSC_TRACE(" irq_pol = %d, IRQ output is active low",irq_pol); + } + if(irq_type) { + SMSC_TRACE(" irq_type = %d, IRQ output is Push-Pull driver",irq_type); + } else { + SMSC_TRACE(" irq_type = %d, IRQ output is Open-Drain buffer",irq_type); + } + if(rx_dma<TRANSFER_REQUEST_DMA) { + if(Platform_IsValidDmaChannel(rx_dma)) { + SMSC_TRACE( + " rx_dma = %d, DMA Channel %d",rx_dma,rx_dma); + } else { + SMSC_WARNING(" rx_dma = %d, Invalid Dma Channel",rx_dma); + rx_dma=TRANSFER_PIO; + SMSC_WARNING(" resetting rx_dma to %d, RX will use PIO",rx_dma); + } + } else if(rx_dma==TRANSFER_REQUEST_DMA) { + SMSC_TRACE(" rx_dma = %d, RX will try to find available channel",rx_dma); + } else { + SMSC_TRACE(" rx_dma = %d, RX will use PIO",rx_dma); + } + if(tx_dma<TRANSFER_REQUEST_DMA) { + if(Platform_IsValidDmaChannel(tx_dma)) { + if(tx_dma!=rx_dma) { + SMSC_TRACE( + " tx_dma = %d, DMA Channel %d",tx_dma,tx_dma); + } else { + SMSC_WARNING(" tx_dma == rx_dma"); + tx_dma=TRANSFER_PIO; + SMSC_WARNING(" resetting tx_dma to %d, TX will use PIO",tx_dma); + } + } else { + SMSC_WARNING(" tx_dma = %d, Invalid Dma Channel",tx_dma); + tx_dma=TRANSFER_PIO; + SMSC_WARNING(" resetting tx_dma to %d, TX will use PIO",tx_dma); + } + } else if(tx_dma==TRANSFER_REQUEST_DMA) { + SMSC_TRACE(" tx_dma = %d, TX will try to find available channel",tx_dma); + } else { + SMSC_TRACE(" tx_dma = %d, TX will use PIO",tx_dma); + } + SMSC_TRACE( " dma_threshold = %d",dma_threshold); + + if(mac_addr_hi16==0xFFFFFFFFUL) { + SMSC_TRACE(" mac_addr_hi16 = 0x%08X, will attempt to read from LAN9118",mac_addr_hi16); + SMSC_TRACE(" mac_addr_lo32 = 0x%08X, will attempt to read from LAN9118",mac_addr_lo32); + } else { + if(mac_addr_hi16&0xFFFF0000UL) { + //The high word is reserved + SMSC_WARNING(" mac_addr_hi16 = 0x%08X, reserved bits are high.",mac_addr_hi16); + mac_addr_hi16&=0x0000FFFFUL; + SMSC_WARNING(" reseting to mac_addr_hi16 = 0x%08X",mac_addr_hi16); + } + if(mac_addr_lo32&0x00000001UL) { + //bit 0 is the I/G bit + SMSC_WARNING(" mac_addr_lo32 = 0x%08X, I/G bit is set.",mac_addr_lo32); + mac_addr_lo32&=0xFFFFFFFEUL; + SMSC_WARNING(" reseting to mac_addr_lo32 = 0x%08X",mac_addr_lo32); + } + SMSC_TRACE(" mac_addr_hi16 = 0x%08X",mac_addr_hi16); + SMSC_TRACE(" mac_addr_lo32 = 0x%08X",mac_addr_lo32); + } + SMSC_TRACE( " debug_mode = 0x%08X",debug_mode); + if(tx_fif_sz&(~HW_CFG_TX_FIF_SZ_)) { + SMSC_WARNING("tx_fif_sz = 0x%08X is invalid",tx_fif_sz); + tx_fif_sz&=HW_CFG_TX_FIF_SZ_; + SMSC_WARNING(" resetting tx_fif_sz to 0x%08X",tx_fif_sz); + } + if(tx_fif_sz>0x000E0000UL) { + SMSC_WARNING("tx_fif_sz = 0x%08X is too high",tx_fif_sz); + tx_fif_sz=0x000E0000UL; + SMSC_WARNING(" resetting tx_fif_sz to 0x%08X",tx_fif_sz); + } + if(tx_fif_sz<0x00020000UL) { + SMSC_WARNING("tx_fif_sz = 0x%08X is too low",tx_fif_sz); + tx_fif_sz=0x00020000UL; + SMSC_WARNING(" resetting tx_fif_sz to 0x%08X",tx_fif_sz); + } + SMSC_TRACE( " tx_fif_sz = 0x%08X",tx_fif_sz); + if(afc_cfg==0xFFFFFFFFUL) { + SMSC_TRACE(" afc_cfg = 0x%08X, driver will decide",afc_cfg); + } else { + if(afc_cfg&0xFF000000UL) { + SMSC_WARNING("afc_cfg = 0x%08X is invalid",afc_cfg); + afc_cfg&=0xFFFFFFFFUL; + SMSC_WARNING(" resetting to afc_cfg = 0x%08X, driver will decide",afc_cfg); + } else { + SMSC_TRACE( + " afc_cfg = 0x%08X",afc_cfg); + } + } + if(rx_mode==PROCESSING_MODE_TASKLET) { + SMSC_TRACE(" rx_mode = 0x%08X, Tasklets enabled",rx_mode); + } else if(rx_mode==PROCESSING_MODE_NAPI) { +#ifndef LINUX_2_6_OR_NEWER + SMSC_WARNING(" rx_mode = 0x%08X requires Linux 2.6 or newer", rx_mode); + rx_mode=PROCESSING_MODE_TASKLET; + SMSC_WARNING(" resetting to rx_mode = 0x%08X, Tasklets enabled", rx_mode); +#else + SMSC_TRACE(" rx_mode = 0x%08X, NAPI enabled",rx_mode); +#endif + } else { + SMSC_TRACE(" rx_mode = 0, use ISR"); + } + + if(phy_addr==0xFFFFFFFFUL) { + SMSC_TRACE(" phy_addr = 0xFFFFFFFF, Use internal phy"); + } else if(phy_addr<=31UL) { + SMSC_TRACE(" phy_addr = 0x%08X, use this address for external phy",phy_addr); + } else { + SMSC_TRACE(" phy_addr = 0x%08X, auto detect external phy",phy_addr); + } + if(max_throughput) { + SMSC_TRACE(" max_throughput = 0x%08X, Use platform default",max_throughput); + } else { + SMSC_TRACE(" max_throughput = 0x%08X",max_throughput); + } + if(max_packet_count) { + SMSC_TRACE(" max_packet_count = 0x%08X, Use platform default",max_packet_count); + } else { + SMSC_TRACE(" max_packet_count = 0x%08X",max_packet_count); + } + if(packet_cost) { + SMSC_TRACE(" packet_cost = 0x%08X, Use platform default",packet_cost); + } else { + SMSC_TRACE(" packet_cost = 0x%08X",packet_cost); + } + if(burst_period) { + SMSC_TRACE(" burst_period = 0x%08X, Use platform default",burst_period); + } else { + SMSC_TRACE(" burst_period = 0x%08X",burst_period); + } + if(max_work_load) { + SMSC_TRACE(" max_work_load = 0x%08X, Use platform default",max_work_load); + } else { + SMSC_TRACE(" max_work_load = 0x%08X",max_work_load); + } + + SMSC9118 = alloc_netdev_mq(0, "eth%d", Smsc9118_init, 1); + SMSC_TRACE(" alloc_netdev complete. SMSC9118 = 0x%08X\n", (u32)SMSC9118); + result=register_netdev(SMSC9118); + if(result) { + SMSC_WARNING("error %i registering device",result); + } else { + device_present=1; + SMSC_TRACE(" Interface Name = \"%s\"",SMSC9118->name); + } + result=result;//make lint happy + SMSC_TRACE("<-- init_module()"); + return device_present ? 0 : -ENODEV; +} + +void Smsc9118_cleanup_module(void) +{ + SMSC_TRACE("--> cleanup_module()"); + if(SMSC9118->ml_priv!=NULL) { + PPRIVATE_DATA privateData=(PPRIVATE_DATA)SMSC9118->ml_priv; + PPLATFORM_DATA platformData=(PPLATFORM_DATA)&(privateData->PlatformData); + Platform_CleanUp(platformData); + kfree(SMSC9118->ml_priv); + SMSC9118->ml_priv=NULL; + } + unregister_netdev(SMSC9118); + SMSC_TRACE("<-- cleanup_module()"); +} + +void Smsc9118_init(struct net_device *dev) +{ + u32 dwLanBase=0UL; + u32 dwIdRev=0UL; + u32 dwFpgaRev=0UL; + // WORD SpecialCtrlSts=0U; + PPRIVATE_DATA privateData=NULL; + PPLATFORM_DATA platformData=NULL; + bool platformInitialized=false; + int result=-ENODEV; + + int i; + bool acquired_mem_region=false; + bool acquired_isr=false; + + SMSC_TRACE("-->Smsc9118_init(dev=0x%08X)",(u32)dev); + + if(dev==NULL) { + SMSC_WARNING("Smsc9118_init(dev==NULL)"); + result=-EFAULT; + goto DONE; + } + + if(dev->ml_priv!=NULL) { + SMSC_WARNING("dev->ml_priv!=NULL, going to overwrite pointer"); + } + dev->ml_priv=kmalloc(sizeof(PRIVATE_DATA),GFP_KERNEL); + if(dev->ml_priv==NULL) { + SMSC_WARNING("Unable to allocate PRIVATE_DATA"); + result=-ENOMEM; + goto DONE; + } + memset(dev->ml_priv,0,sizeof(PRIVATE_DATA)); + privateData=(PPRIVATE_DATA)(dev->ml_priv); + platformData=&(privateData->PlatformData); + + dwLanBase=Platform_Initialize( + platformData, + lan_base,bus_width); + + if(dwLanBase==0UL) { + SMSC_WARNING("dwLanBase==0x00000000"); + result=-ENODEV; + goto DONE; + } + platformInitialized=true; + SMSC_TRACE("dwLanBase=0x%08X",dwLanBase); + + if(check_mem_region(dwLanBase,LAN_REGISTER_EXTENT)!=0) { + SMSC_WARNING(" Memory Region specified (0x%08X to 0x%08X) is not available.", + dwLanBase,(u32)(dwLanBase+LAN_REGISTER_EXTENT-1UL)); + result=-ENOMEM; + goto DONE; + } + + privateData->dwLanBase=dwLanBase; + dwIdRev=Lan_GetRegDW(ID_REV); + if(HIWORD(dwIdRev)==LOWORD(dwIdRev)) { + //this may mean the chip is set for 32 bit + // while the bus is reading as 16 bit +UNKNOWN_CHIP: + SMSC_WARNING(" LAN9118 Family NOT Identified, dwIdRev==0x%08X",dwIdRev); + result=-ENODEV; + goto DONE; + } + switch(dwIdRev&0xFFFF0000UL) { + + case 0x93120000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 1UL: + SMSC_TRACE(" Hydra identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + default: + SMSC_TRACE(" Hydra identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + };break; + + + case 0x01180000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9118 Beacon identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=0; + break; + case 1UL: + SMSC_TRACE(" LAN9118 Concord A0 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=1; + break; + case 2UL: + SMSC_TRACE(" LAN9118 Concord A1 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + default: + SMSC_TRACE(" LAN9118 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + };break; + + case 0x01170000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9117 Beacon identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=0; + break; + case 1UL: + SMSC_TRACE(" LAN9117 Concord A0 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=1; + break; + case 2UL: + SMSC_TRACE(" LAN9117 Concord A1 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + default: + SMSC_TRACE(" LAN9117 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + };break; + + case 0x01160000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + goto UNKNOWN_CHIP; + case 1UL: + SMSC_TRACE(" LAN9116 Concord A0 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=1; + break; + case 2UL: + SMSC_TRACE(" LAN9116 Concord A1 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + default: + SMSC_TRACE(" LAN9116 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + };break; + + case 0x01150000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + goto UNKNOWN_CHIP; + case 1UL: + SMSC_TRACE(" LAN9115 Concord A0 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=1; + break; + case 2UL: + SMSC_TRACE(" LAN9115 Concord A1 identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + default: + SMSC_TRACE(" LAN9115 Concord A1 identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=2; + break; + };break; + + case 0x118A0000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9218 Boylston identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + default: + SMSC_TRACE(" LAN9218 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + };break; + + case 0x117A0000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9217 Boylston identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + default: + SMSC_TRACE(" LAN9217 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + };break; + + case 0x116A0000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9216 Boylston identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + default: + SMSC_TRACE(" LAN9216 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + };break; + + case 0x115A0000UL: + if (Scatter_gather | tx_Csum | rx_Csum) + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9215 Boylston identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + default: + SMSC_TRACE(" LAN9215 Boylston identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=3; + break; + };break; + + + case 0x92100000UL: + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + if (Scatter_gather) + SMSC_TRACE(" Tx Scatter-Gather"); + if (tx_Csum) + SMSC_TRACE(" Tx HW Checksum"); + if (rx_Csum) + SMSC_TRACE(" Rx HW Checksum"); + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9210 Boylston Lite identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + + default: + SMSC_TRACE(" LAN9210 Boylston Lite identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + };break; + + case 0x92110000UL: + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + if (Scatter_gather) + SMSC_TRACE(" Tx Scatter-Gather"); + if (tx_Csum) + SMSC_TRACE(" Tx HW Checksum"); + if (rx_Csum) + SMSC_TRACE(" Rx HW Checksum"); + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9211 Boylston Lite identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + + default: + SMSC_TRACE(" LAN9211 Boylston Lite identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + };break; + + case 0x215A0000UL: + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + if (Scatter_gather) + SMSC_TRACE(" Tx Scatter-Gather"); + if (tx_Csum) + SMSC_TRACE(" Tx HW Checksum"); + if (rx_Csum) + SMSC_TRACE(" Rx HW Checksum"); + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9215A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + + default: + SMSC_TRACE(" LAN9215A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + };break; + + case 0x216A0000UL: + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + if (Scatter_gather) + SMSC_TRACE(" Tx Scatter-Gather"); + if (tx_Csum) + SMSC_TRACE(" Tx HW Checksum"); + if (rx_Csum) + SMSC_TRACE(" Rx HW Checksum"); + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9216A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + + default: + SMSC_TRACE(" LAN9216A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + };break; + + case 0x217A0000UL: + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + if (Scatter_gather) + SMSC_TRACE(" Tx Scatter-Gather"); + if (tx_Csum) + SMSC_TRACE(" Tx HW Checksum"); + if (rx_Csum) + SMSC_TRACE(" Rx HW Checksum"); + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9217A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + + default: + SMSC_TRACE(" LAN9217A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + };break; + + + case 0x218A0000UL: + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + if (Scatter_gather) + SMSC_TRACE(" Tx Scatter-Gather"); + if (tx_Csum) + SMSC_TRACE(" Tx HW Checksum"); + if (rx_Csum) + SMSC_TRACE(" Rx HW Checksum"); + + switch(dwIdRev&0x0000FFFFUL) { + case 0UL: + SMSC_TRACE(" LAN9218A Boylston Auto identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + + default: + SMSC_TRACE(" LAN9218A Boylston Auto identified (NEW), dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=4; + break; + };break; + + default: + //This is used for the new unkonw chip before we formally add them in the driver + if (id_reg==dwIdRev) { + if (Csum_Support) { + privateData->UseScatterGather=Scatter_gather; + privateData->UseTxCsum=tx_Csum; + privateData->UseRxCsum=rx_Csum; + } else { + SMSC_TRACE("This chip doesn't support checksum offload!!! Will use nonchecksum offload by default"); + privateData->UseScatterGather=false; + privateData->UseTxCsum=false; + privateData->UseRxCsum=false; + } + + SMSC_TRACE(" New Chip identified, dwIdRev==0x%08X",dwIdRev); + privateData->dwGeneration=5; + + } else { + + SMSC_WARNING("unknow chip, dwIdRev==0x%08X",dwIdRev); + + }; break; + + + } + + //printk("dwGeneration = %d\n", privateData->dwGeneration); + + dwFpgaRev=Lan_GetRegDW(FPGA_REV); + SMSC_TRACE(" FPGA_REV == 0x%08X",dwFpgaRev); + + + ether_setup(dev); + dev->open= Smsc9118_open; + dev->stop= Smsc9118_stop; + dev->hard_start_xmit= Smsc9118_hard_start_xmit; + dev->get_stats= Smsc9118_get_stats; + dev->do_ioctl= Smsc9118_do_ioctl; + dev->set_multicast_list=Smsc9118_set_multicast_list; + dev->flags|=IFF_MULTICAST; +#ifdef LINUX_2_6_OR_NEWER + /* + if (rx_mode==PROCESSING_MODE_NAPI) { + dev->poll=Smsc9118_rx_poll; + dev->weight=napi_weight; + } + */ +#endif + if(privateData->UseScatterGather) { + + if(privateData->UseTxCsum) + dev->features = (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST); + else + dev->features = (NETIF_F_SG | NETIF_F_FRAGLIST); // Kernel will turn off SG in this case. + } + + else { + + if(privateData->UseTxCsum) + dev->features = (NETIF_F_HW_CSUM); + else + dev->features = 0; + } + + if(dev==NULL) { + SMSC_WARNING("Smsc9118_open(dev==NULL)"); + result=-EFAULT; + goto DONE; + } + privateData=(PPRIVATE_DATA)(dev->ml_priv); + if(privateData==NULL) { + SMSC_WARNING("Smsc9118_open(privateData==NULL)"); + result=-EFAULT; + goto DONE; + } + platformData=&(privateData->PlatformData); + + for (i = 0; i < GPT_SCHEDULE_DEPTH; i++) { + privateData->GptFunction [i] = NULL; + } + privateData->Gpt_scheduled_slot_index = GPT_SCHEDULE_DEPTH; + + //get memory region + if(check_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT)!=0) + { + SMSC_WARNING("Device memory is already in use."); + result=-ENOMEM; + goto DONE; + } + request_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT,"SMSC_LAN9118"); + acquired_mem_region=true; + + //initialize the LAN9118 + { + u32 dwIntCfg=0; + if(irq_pol) { + dwIntCfg|=INT_CFG_IRQ_POL_; + } + if(irq_type) { + dwIntCfg|=INT_CFG_IRQ_TYPE_; + } + if(!Lan_Initialize(privateData,dwIntCfg,tx_fif_sz,afc_cfg)) + { + SMSC_WARNING("Failed Lan_Initialize"); + result=-ENODEV; + goto DONE; + } + } + + if(!Platform_RequestIRQ(platformData,irq,Smsc9118_ISR,privateData)) { + result=-ENODEV; + goto DONE; + } + acquired_isr=true; + + //must now test the IRQ connection to the ISR + SMSC_TRACE("Testing ISR using IRQ %d",Platform_CurrentIRQ(platformData)); + { + u32 dwTimeOut=1000000; + Lan_SignalSoftwareInterrupt(privateData); + SMSC_TRACE("privateData=%08X", (u32)privateData); + do { + udelay(10); + dwTimeOut--; + } while((dwTimeOut)&&(!(privateData->SoftwareInterruptSignal))); + if(!(privateData->SoftwareInterruptSignal)) { + SMSC_WARNING("ISR failed signaling test"); + result=-ENODEV; + goto DONE; + } + } + SMSC_TRACE("ISR passed test using IRQ %d",Platform_CurrentIRQ(platformData)); + + if(!Mac_Initialize(privateData)) { + SMSC_WARNING("Failed Mac_Initialize"); + result=-ENODEV; + goto DONE; + } + {//get mac address + u32 dwHigh16=0; + u32 dwLow32=0; + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if(mac_addr_hi16==0xFFFFFFFF) { + dwHigh16=Mac_GetRegDW(privateData,ADDRH,keyCode); + dwLow32=Mac_GetRegDW(privateData,ADDRL,keyCode); + if((dwHigh16==0x0000FFFFUL)&&(dwLow32==0xFFFFFFFF)) + { + dwHigh16=0x00000070UL; + dwLow32=0x110F8000UL; + Mac_SetRegDW(privateData,ADDRH,dwHigh16,keyCode); + Mac_SetRegDW(privateData,ADDRL,dwLow32,keyCode); + SMSC_TRACE("Mac Address is set by default to 0x%04X%08X", + dwHigh16,dwLow32); + } else { + SMSC_TRACE("Mac Address is read from LAN9118 as 0x%04X%08X", + dwHigh16,dwLow32); + } + } else { + //SMSC_ASSERT((mac_addr_hi16&0xFFFF8000UL)==0); + dwHigh16=mac_addr_hi16; + dwLow32=mac_addr_lo32; + Mac_SetRegDW(privateData,ADDRH,dwHigh16,keyCode); + Mac_SetRegDW(privateData,ADDRL,dwLow32,keyCode); + SMSC_TRACE("Mac Address is set by parameter to 0x%04X%08X", + dwHigh16,dwLow32); + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + dev->dev_addr[0]=LOBYTE(LOWORD(dwLow32)); + dev->dev_addr[1]=HIBYTE(LOWORD(dwLow32)); + dev->dev_addr[2]=LOBYTE(HIWORD(dwLow32)); + dev->dev_addr[3]=HIBYTE(HIWORD(dwLow32)); + dev->dev_addr[4]=LOBYTE(LOWORD(dwHigh16)); + dev->dev_addr[5]=HIBYTE(LOWORD(dwHigh16)); + } + + privateData->dwIdRev=dwIdRev; + privateData->dwFpgaRev=dwFpgaRev&(0x000000FFUL); + privateData->dev=dev; + + sprintf(privateData->ifName,"%s","eth1"); + SMSC_TRACE("privateData->ifName = %s\n", privateData->ifName); + result=0; + +DONE: + if(result!=0) { + if(dev!=NULL) { + if(dev->ml_priv!=NULL) { + if(platformInitialized) { + Platform_CleanUp(platformData); + } + kfree(dev->ml_priv); + dev->ml_priv=NULL; + } + } + } + SMSC_TRACE("<--Smsc9118_init(), result=%d",result); +} + +int Smsc9118_open(struct net_device *dev) +{ + int result=-ENODEV; + PPRIVATE_DATA privateData=NULL; + PPLATFORM_DATA platformData=NULL; + bool acquired_mem_region=false; + bool acquired_isr=false; + SMSC_TRACE("-->Smsc9118_open(dev=0x%08X)",(u32)dev); + if(dev==NULL) { + SMSC_WARNING("Smsc9118_open(dev==NULL)"); + result=-EFAULT; + goto DONE; + } + privateData=(PPRIVATE_DATA)(dev->ml_priv); + if(privateData==NULL) { + SMSC_WARNING("Smsc9118_open(privateData==NULL)"); + result=-EFAULT; + goto DONE; + } + platformData=&(privateData->PlatformData); + + privateData->MulticastUpdatePending = false; +#ifdef USE_PHY_WORK_AROUND + netif_carrier_off(dev); + if(!Phy_Initialize( + privateData, + phy_addr, + link_mode)) + { + SMSC_WARNING("Failed to initialize Phy"); + result=-ENODEV; + goto DONE; + } +#endif + + { + u32 dwRxDmaCh=rx_dma; + u32 dwTxDmaCh=tx_dma; + privateData->RxDmaChReserved=false; + + + if(rx_dma==TRANSFER_REQUEST_DMA) { + dwRxDmaCh=Platform_RequestDmaChannel(&(privateData->PlatformData)); + SMSC_ASSERT(dwRxDmaCh!=TRANSFER_REQUEST_DMA); + if(dwRxDmaCh<TRANSFER_REQUEST_DMA) { + privateData->RxDmaChReserved=true; + } + } + privateData->TxDmaChReserved=false; + if(tx_dma==TRANSFER_REQUEST_DMA) { + dwTxDmaCh=Platform_RequestDmaChannel(&(privateData->PlatformData)); + SMSC_ASSERT(dwTxDmaCh!=TRANSFER_REQUEST_DMA); + if(dwTxDmaCh<TRANSFER_REQUEST_DMA) { + privateData->TxDmaChReserved=true; + } + } + Tx_Initialize(privateData,dwTxDmaCh,dma_threshold); + Rx_Initialize(privateData,dwRxDmaCh,dma_threshold); + + } + +#ifndef LINUX_2_6_OR_NEWER + MOD_INC_USE_COUNT; +#endif + privateData->Running=true; + netif_start_queue(dev); + Tx_StopQueue(privateData,0x01UL); + + + spin_lock_init(&(privateData->GpTimerLock)); + Lan_EnableInterrupt(privateData,INT_EN_GPT_INT_EN_); + +#ifndef USE_PHY_WORK_AROUND + netif_carrier_off(dev); + if(!Phy_Initialize( + privateData, + phy_addr, + link_mode)) + { + SMSC_WARNING("Failed to initialize Phy"); + result=-ENODEV; + goto DONE; + } +#endif + + result=0; + +DONE: + if(result!=0) { +#ifndef LINUX_2_6_OR_NEWER + MOD_DEC_USE_COUNT; +#endif + if(privateData!=NULL) { + if(privateData->TxDmaChReserved) { + Platform_ReleaseDmaChannel(platformData, + privateData->dwTxDmaCh); + privateData->TxDmaChReserved=false; + } + if(privateData->RxDmaChReserved) { + Platform_ReleaseDmaChannel(platformData, + privateData->dwRxDmaCh); + privateData->RxDmaChReserved=false; + } + if(acquired_isr) { + Platform_FreeIRQ(platformData); + } + if(acquired_mem_region) { + release_mem_region( + privateData->dwLanBase, + LAN_REGISTER_EXTENT); + } + } + } + SMSC_TRACE("<--Smsc9118_open, result=%d",result); + return result; +} + +int Smsc9118_stop(struct net_device *dev) +{ + int result=0; + PPRIVATE_DATA privateData=NULL; + SMSC_TRACE("-->Smsc9118_stop(dev=0x%08X)",(u32)dev); + if(dev==NULL) { + SMSC_WARNING("Smsc9118_stop(dev==NULL)"); + result=-EFAULT; + goto DONE; + } + privateData=(PPRIVATE_DATA)(dev->ml_priv); + if(privateData==NULL) { + SMSC_WARNING("Smsc9118_stop(privateData==NULL)"); + result=-EFAULT; + goto DONE; + } + + privateData->StopLinkPolling=true; + del_timer_sync(&(privateData->LinkPollingTimer)); + + Lan_DisableInterrupt(privateData,INT_EN_GPT_INT_EN_); + + Tx_UpdateTxCounters(privateData); + privateData->Running=false; + Lan_DisableIRQ(privateData); + + Tx_CompleteDma(privateData); + + Tx_StopQueue(privateData,0x01UL); + +#ifndef LINUX_2_6_OR_NEWER + MOD_DEC_USE_COUNT; +#endif + + if(privateData->TxDmaChReserved) { + Platform_ReleaseDmaChannel( + &(privateData->PlatformData), + privateData->dwTxDmaCh); + privateData->TxDmaChReserved=false; + } + if(privateData->RxDmaChReserved) { + Platform_ReleaseDmaChannel( + &(privateData->PlatformData), + privateData->dwRxDmaCh); + privateData->RxDmaChReserved=false; + } + + Platform_FreeIRQ(&(privateData->PlatformData)); + release_mem_region(privateData->dwLanBase,LAN_REGISTER_EXTENT); + + { + const u32 dwLanBase=privateData->dwLanBase; + const u32 dwIdRev=privateData->dwIdRev; + const u32 dwFpgaRev=privateData->dwFpgaRev; + struct net_device * const tempDev=privateData->dev; + char ifName[SMSC_IF_NAME_SIZE]; + PLATFORM_DATA platformDataBackup; + memcpy(ifName,privateData->ifName,SMSC_IF_NAME_SIZE); + memcpy(&platformDataBackup,&(privateData->PlatformData),sizeof(PLATFORM_DATA)); + + memset(privateData,0,sizeof(PRIVATE_DATA)); + + privateData->dwLanBase=dwLanBase; + privateData->dwIdRev=dwIdRev; + privateData->dwFpgaRev=dwFpgaRev; + privateData->dev=tempDev; + memcpy(privateData->ifName,ifName,SMSC_IF_NAME_SIZE); + memcpy(&(privateData->PlatformData),&platformDataBackup,sizeof(PLATFORM_DATA)); + } + +DONE: + SMSC_TRACE("<--Smsc9118_stop, result=%d",result); + return result; +} + +int Smsc9118_hard_start_xmit( + struct sk_buff *skb, struct net_device * const dev) +{ + int result=0; + PPRIVATE_DATA privateData=NULL; + //SMSC_TRACE("-->Smsc9118_hard_start_xmit(skb=0x%08X,dev=0x%08X)",(u32)skb,(u32)dev); + if(skb==NULL) { + SMSC_WARNING("Smsc9118_hard_start_xmit(skb==NULL)"); + result=-EFAULT; + goto DONE; + } + if(dev==NULL) { + SMSC_WARNING("Smsc9118_hard_start_xmit(dev==NULL)"); + result=-EFAULT; + goto DONE; + } + if(dev->ml_priv==NULL) { + SMSC_WARNING("Smsc9118_hard_start_xmit(dev->ml_priv==NULL)"); + result=-EFAULT; + goto DONE; + } + privateData=(PPRIVATE_DATA)(dev->ml_priv); + // SET_GPIO(GP_TX); + + Tx_SendSkb(privateData,skb); + + // CLEAR_GPIO(GP_TX); +DONE: + //SMSC_TRACE("<--Smsc9118_hard_start_xmit, result=%d",result); + return result; +} + +struct net_device_stats * Smsc9118_get_stats(struct net_device * const dev) +{ + PPRIVATE_DATA privateData=NULL; + if(dev==NULL) { + SMSC_WARNING("Smsc9118_get_stats(dev==NULL)"); + return NULL; + } + if(dev->ml_priv==NULL) { + // SMSC_WARNING("Smsc9118_get_stats(dev->ml_priv==NULL)"); + return NULL; + } + + privateData=(PPRIVATE_DATA)(dev->ml_priv); + if(privateData->Running) { + privateData->stats.rx_dropped+=Lan_GetRegDW(RX_DROP); + Tx_UpdateTxCounters(privateData); + } + return &(privateData->stats); +} + +void Smsc9118_set_multicast_list(struct net_device *dev) +{ + SMSC_ASSERT(dev!=NULL); + Rx_SetMulticastList(dev); +} + + +int Smsc9118_do_ioctl( + struct net_device *dev, + struct ifreq *ifr, + int cmd) +{ + int result=0; + PPRIVATE_DATA privateData=NULL; + void *userAddr=NULL; + + // bool success=false; + + SMSC_TRACE("-->Smsc9118_do_ioctl"); + SMSC_TRACE("cmd=%d,SIOCGMIIPHY=%d,SIOCDEVPRIVATE=%d", + cmd,SIOCGMIIPHY,SIOCDEVPRIVATE); + + + if(dev==NULL) { + SMSC_WARNING("dev==NULL"); + result=-EFAULT; + goto DONE; + } + if(dev->ml_priv==NULL) { + SMSC_WARNING("dev->ml_priv==NULL"); + result=-EFAULT; + goto DONE; + } + privateData=((PPRIVATE_DATA)dev->ml_priv); + if(ifr==NULL) { + SMSC_WARNING("ifr==NULL"); + result=-EFAULT; + goto DONE; + } + userAddr=ifr->ifr_data; + + + if(privateData->LanInitialized) { + // standard MII IOC's + struct mii_ioctl_data * const data= + (struct mii_ioctl_data *) & ifr->ifr_data; + switch(cmd) { + case SIOCGMIIPHY: + + case SIOCDEVPRIVATE: + data->phy_id=1; + SMSC_TRACE("SIOCGMIIPHY: phy_id set to 0x%04X",data->phy_id); + break; + case SIOCGMIIREG: + case SIOCDEVPRIVATE+1: + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + data->val_out=Phy_GetRegW( + privateData,data->reg_num,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + SMSC_TRACE("SIOCGMIIREG: phy_id=0x%04X, reg_num=0x%04X, val_out set to 0x%04X", + data->phy_id,data->reg_num,data->val_out); + break; + case SIOCSMIIREG: + case SIOCDEVPRIVATE+2: + SMSC_TRACE("SIOCSMIIREG: phy_id=0x%04X, reg_num=0x%04X, val_in=0x%04X", + data->phy_id,data->reg_num,data->val_in); + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + Phy_SetRegW( + privateData,data->reg_num,((WORD)(data->val_in)),keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + break; + + case SIOCETHTOOL: + result=Smsc9118_ethtool_ioctl(privateData,userAddr); + break; + case SMSC9118_IOCTL: + result=Smsc9118_private_ioctl(privateData,userAddr); + break; + + default: + result=-EOPNOTSUPP; + break;//make lint happy + } + } + +DONE: + + return result; +} + + + +int Smsc9118_private_ioctl(PPRIVATE_DATA privateData,void *useraddr) +{ + + + bool success=false; + int result=-EFAULT; + SMSC9118_IOCTL_DATA ioctlData; + + if(useraddr==NULL) { + SMSC_WARNING("useraddr==NULL"); + result=-EFAULT; + goto DONE; + } + + if(copy_from_user(&ioctlData,useraddr,sizeof(ioctlData))) { + SMSC_WARNING("copy_from_user failed"); + result=-EFAULT; + goto DONE; + } + + if(ioctlData.dwSignature!=SMSC9118_APP_SIGNATURE) { + SMSC_WARNING("invalid application signature"); + result=-EFAULT; + goto DONE; + } + + switch(ioctlData.dwCommand) { + case COMMAND_GET_SIGNATURE: + success=true; + break; + case COMMAND_GET_FLOW_PARAMS: + ioctlData.Data[0]=privateData->RxFlowMeasuredMaxThroughput; + ioctlData.Data[1]=privateData->RxFlowMeasuredMaxPacketCount; + ioctlData.Data[2]=privateData->RxFlowParameters.MaxThroughput; + ioctlData.Data[3]=privateData->RxFlowParameters.MaxPacketCount; + ioctlData.Data[4]=privateData->RxFlowParameters.PacketCost; + ioctlData.Data[5]=privateData->RxFlowParameters.BurstPeriod; + ioctlData.Data[6]=privateData->RxFlowMaxWorkLoad; + ioctlData.Data[7]=Lan_GetRegDW(INT_CFG)>>24; + privateData->RxFlowMeasuredMaxThroughput=0; + privateData->RxFlowMeasuredMaxPacketCount=0; + success=true; + break; + case COMMAND_SET_FLOW_PARAMS: + if(!(privateData->RxFlowControlActive)) { + privateData->RxFlowParameters.MaxThroughput=ioctlData.Data[2]; + privateData->RxFlowParameters.MaxPacketCount=ioctlData.Data[3]; + privateData->RxFlowParameters.PacketCost=ioctlData.Data[4]; + privateData->RxFlowParameters.BurstPeriod=ioctlData.Data[5]; + if(ioctlData.Data[6]==0xFFFFFFFFUL) { + privateData->RxFlowMaxWorkLoad= + privateData->RxFlowParameters.MaxThroughput+ + (privateData->RxFlowParameters.MaxPacketCount* + privateData->RxFlowParameters.PacketCost); + } else { + privateData->RxFlowMaxWorkLoad=ioctlData.Data[6]; + } + Lan_SetIntDeas(privateData,ioctlData.Data[7]); + privateData->RxFlowBurstMaxWorkLoad= + (privateData->RxFlowMaxWorkLoad* + privateData->RxFlowParameters.BurstPeriod)/1000; + success=true; + };break; + case COMMAND_GET_CONFIGURATION: + ioctlData.Data[0]=DRIVER_VERSION; + ioctlData.Data[1]=lan_base; + ioctlData.Data[2]=bus_width; + ioctlData.Data[3]=link_mode; + ioctlData.Data[4]=irq; + ioctlData.Data[5]=int_deas; + ioctlData.Data[6]=irq_pol; + ioctlData.Data[7]=irq_type; + ioctlData.Data[8]=rx_dma; + ioctlData.Data[9]=tx_dma; + ioctlData.Data[10]=dma_threshold; + ioctlData.Data[11]=mac_addr_hi16; + ioctlData.Data[12]=mac_addr_lo32; + ioctlData.Data[13]=debug_mode; + ioctlData.Data[14]=tx_fif_sz; + ioctlData.Data[15]=afc_cfg; + ioctlData.Data[16]=rx_mode; + ioctlData.Data[17]=max_throughput; + ioctlData.Data[18]=max_packet_count; + ioctlData.Data[19]=packet_cost; + ioctlData.Data[20]=burst_period; + ioctlData.Data[21]=max_work_load; + ioctlData.Data[22]=privateData->dwIdRev; + ioctlData.Data[23]=privateData->dwFpgaRev; + ioctlData.Data[24]=1; + ioctlData.Data[25]=privateData->dwPhyId; + ioctlData.Data[26]=privateData->bPhyModel; + ioctlData.Data[27]=privateData->bPhyRev; + ioctlData.Data[28]=privateData->dwLinkSpeed; + ioctlData.Data[29]=privateData->RxFlowMeasuredMaxThroughput; + ioctlData.Data[30]=privateData->RxFlowMeasuredMaxPacketCount; + ioctlData.Data[31]=privateData->RxFlowParameters.MaxThroughput; + ioctlData.Data[32]=privateData->RxFlowParameters.MaxPacketCount; + ioctlData.Data[33]=privateData->RxFlowParameters.PacketCost; + ioctlData.Data[34]=privateData->RxFlowParameters.BurstPeriod; + ioctlData.Data[35]=privateData->RxFlowMaxWorkLoad; + sprintf(ioctlData.Strng1,"%s, %s",__DATE__,__TIME__); + sprintf(ioctlData.Strng2,"%s",privateData->ifName); + privateData->RxFlowMeasuredMaxThroughput=0; + privateData->RxFlowMeasuredMaxPacketCount=0; + success=true; + break; + case COMMAND_LAN_GET_REG: + if((ioctlData.Data[0]<LAN_REGISTER_EXTENT)&& + ((ioctlData.Data[0]&0x3UL)==0)) + { + ioctlData.Data[1]= + (*((volatile u32 *)(privateData->dwLanBase+ + ioctlData.Data[0]))); + success=true; + } else { + SMSC_WARNING("Reading LAN9118 Mem Map Failed"); + goto MEM_MAP_ACCESS_FAILED; + } + break; + case COMMAND_LAN_SET_REG: + if((ioctlData.Data[0]<LAN_REGISTER_EXTENT)&& + ((ioctlData.Data[0]&0x3UL)==0)) + { + (*((volatile u32 *)(privateData->dwLanBase+ + ioctlData.Data[0])))=ioctlData.Data[1]; + success=true; + } else { + SMSC_WARNING("Reading LAN9118 Mem Map Failed"); +MEM_MAP_ACCESS_FAILED: + SMSC_WARNING(" Invalid offset == 0x%08X",(u32)(ioctlData.Data[0])); + if(ioctlData.Data[0]>=LAN_REGISTER_EXTENT) { + SMSC_WARNING(" Out of range"); + } + if(ioctlData.Data[0]&0x3UL) { + SMSC_WARNING(" Not u32 aligned"); + } + } + break; + case COMMAND_MAC_GET_REG: + if((ioctlData.Data[0]<=0xC)&&(privateData->LanInitialized)) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[1]= + Mac_GetRegDW(privateData,ioctlData.Data[0],keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Reading Mac Register Failed"); + goto MAC_ACCESS_FAILURE; + } + break; + case COMMAND_MAC_SET_REG: + if((ioctlData.Data[0]<=0xC)&&(privateData->LanInitialized)) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + Mac_SetRegDW( + privateData, + ioctlData.Data[0], + ioctlData.Data[1], + keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Writing Mac Register Failed"); +MAC_ACCESS_FAILURE: + if(!(privateData->LanInitialized)) { + + SMSC_WARNING(" LAN Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + } + if(!(ioctlData.Data[0]<=0xC)) { + SMSC_WARNING(" Invalid index == 0x%08X",(u32)(ioctlData.Data[0])); + } + } + break; + case COMMAND_PHY_GET_REG: + if((ioctlData.Data[0]<32)&&(privateData->LanInitialized)) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[1]=((u32) + Phy_GetRegW(privateData,ioctlData.Data[0],keyCode)); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Reading Phy Register Failed"); + goto PHY_ACCESS_FAILURE; + } + break; + case COMMAND_PHY_SET_REG: + if((ioctlData.Data[0]<32)&&(privateData->LanInitialized)) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + Phy_SetRegW( + privateData, + ioctlData.Data[0], + ((WORD)(ioctlData.Data[1])), + keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Writing Phy Register Failed"); +PHY_ACCESS_FAILURE: + if(!(privateData->LanInitialized)) { + SMSC_WARNING(" Lan Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + } + if(!(ioctlData.Data[0]<32)) { + SMSC_WARNING(" Invalid index == 0x%d",(u32)(ioctlData.Data[0])); + } + } + break; + // case COMMAND_DUMP_TEMP: + // { + // u32 c=0; + // for(c=0;c<0x40;c++) + // ioctlData.Data[c]=privateData->temp[c]; + // } + // success=true; + // break; + case COMMAND_DUMP_LAN_REGS: + ioctlData.Data[LAN_REG_ID_REV]=Lan_GetRegDW(ID_REV); + ioctlData.Data[LAN_REG_INT_CFG]=Lan_GetRegDW(INT_CFG); + ioctlData.Data[LAN_REG_INT_STS]=Lan_GetRegDW(INT_STS); + ioctlData.Data[LAN_REG_INT_EN]=Lan_GetRegDW(INT_EN); + ioctlData.Data[LAN_REG_BYTE_TEST]=Lan_GetRegDW(BYTE_TEST); + ioctlData.Data[LAN_REG_FIFO_INT]=Lan_GetRegDW(FIFO_INT); + ioctlData.Data[LAN_REG_RX_CFG]=Lan_GetRegDW(RX_CFG); + ioctlData.Data[LAN_REG_TX_CFG]=Lan_GetRegDW(TX_CFG); + ioctlData.Data[LAN_REG_HW_CFG]=Lan_GetRegDW(HW_CFG); + ioctlData.Data[LAN_REG_RX_DP_CTRL]=Lan_GetRegDW(RX_DP_CTRL); + ioctlData.Data[LAN_REG_RX_FIFO_INF]=Lan_GetRegDW(RX_FIFO_INF); + ioctlData.Data[LAN_REG_TX_FIFO_INF]=Lan_GetRegDW(TX_FIFO_INF); + ioctlData.Data[LAN_REG_PMT_CTRL]=Lan_GetRegDW(PMT_CTRL); + ioctlData.Data[LAN_REG_GPIO_CFG]=Lan_GetRegDW(GPIO_CFG); + ioctlData.Data[LAN_REG_GPT_CFG]=Lan_GetRegDW(GPT_CFG); + ioctlData.Data[LAN_REG_GPT_CNT]=Lan_GetRegDW(GPT_CNT); + ioctlData.Data[LAN_REG_FPGA_REV]=Lan_GetRegDW(FPGA_REV); + ioctlData.Data[LAN_REG_WORD_SWAP]=Lan_GetRegDW(WORD_SWAP); + ioctlData.Data[LAN_REG_FREE_RUN]=Lan_GetRegDW(FREE_RUN); + ioctlData.Data[LAN_REG_RX_DROP]=Lan_GetRegDW(RX_DROP); + if(privateData->LanInitialized) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[LAN_REG_MAC_CSR_CMD]=Lan_GetRegDW(MAC_CSR_CMD); + ioctlData.Data[LAN_REG_MAC_CSR_DATA]=Lan_GetRegDW(MAC_CSR_DATA); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } else { + ioctlData.Data[LAN_REG_MAC_CSR_CMD]=Lan_GetRegDW(MAC_CSR_CMD); + ioctlData.Data[LAN_REG_MAC_CSR_DATA]=Lan_GetRegDW(MAC_CSR_DATA); + } + ioctlData.Data[LAN_REG_AFC_CFG]=Lan_GetRegDW(AFC_CFG); + ioctlData.Data[LAN_REG_E2P_CMD]=Lan_GetRegDW(E2P_CMD); + ioctlData.Data[LAN_REG_E2P_DATA]=Lan_GetRegDW(E2P_DATA); + success=true; + break; + case COMMAND_DUMP_MAC_REGS: + if(privateData->LanInitialized) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[MAC_REG_MAC_CR]=Mac_GetRegDW(privateData,MAC_CR,keyCode); + ioctlData.Data[MAC_REG_ADDRH]=Mac_GetRegDW(privateData,ADDRH,keyCode); + ioctlData.Data[MAC_REG_ADDRL]=Mac_GetRegDW(privateData,ADDRL,keyCode); + ioctlData.Data[MAC_REG_HASHH]=Mac_GetRegDW(privateData,HASHH,keyCode); + ioctlData.Data[MAC_REG_HASHL]=Mac_GetRegDW(privateData,HASHL,keyCode); + ioctlData.Data[MAC_REG_MII_ACC]=Mac_GetRegDW(privateData,MII_ACC,keyCode); + ioctlData.Data[MAC_REG_MII_DATA]=Mac_GetRegDW(privateData,MII_DATA,keyCode); + ioctlData.Data[MAC_REG_FLOW]=Mac_GetRegDW(privateData,FLOW,keyCode); + ioctlData.Data[MAC_REG_VLAN1]=Mac_GetRegDW(privateData,VLAN1,keyCode); + ioctlData.Data[MAC_REG_VLAN2]=Mac_GetRegDW(privateData,VLAN2,keyCode); + ioctlData.Data[MAC_REG_WUFF]=Mac_GetRegDW(privateData,WUFF,keyCode); + ioctlData.Data[MAC_REG_WUCSR]=Mac_GetRegDW(privateData,WUCSR,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Mac Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + } + break; + case COMMAND_DUMP_PHY_REGS: + if(privateData->LanInitialized) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[PHY_REG_0]=Phy_GetRegW(privateData,0,keyCode); + ioctlData.Data[PHY_REG_1]=Phy_GetRegW(privateData,1,keyCode); + ioctlData.Data[PHY_REG_2]=Phy_GetRegW(privateData,2,keyCode); + ioctlData.Data[PHY_REG_3]=Phy_GetRegW(privateData,3,keyCode); + ioctlData.Data[PHY_REG_4]=Phy_GetRegW(privateData,4,keyCode); + ioctlData.Data[PHY_REG_5]=Phy_GetRegW(privateData,5,keyCode); + ioctlData.Data[PHY_REG_6]=Phy_GetRegW(privateData,6,keyCode); + ioctlData.Data[PHY_REG_16]=Phy_GetRegW(privateData,16,keyCode); + ioctlData.Data[PHY_REG_17]=Phy_GetRegW(privateData,17,keyCode); + ioctlData.Data[PHY_REG_18]=Phy_GetRegW(privateData,18,keyCode); + ioctlData.Data[PHY_REG_20]=Phy_GetRegW(privateData,20,keyCode); + ioctlData.Data[PHY_REG_21]=Phy_GetRegW(privateData,21,keyCode); + ioctlData.Data[PHY_REG_22]=Phy_GetRegW(privateData,22,keyCode); + ioctlData.Data[PHY_REG_23]=Phy_GetRegW(privateData,23,keyCode); + ioctlData.Data[PHY_REG_27]=Phy_GetRegW(privateData,27,keyCode); + ioctlData.Data[PHY_REG_28]=Phy_GetRegW(privateData,28,keyCode); + ioctlData.Data[PHY_REG_29]=Phy_GetRegW(privateData,29,keyCode); + ioctlData.Data[PHY_REG_30]=Phy_GetRegW(privateData,30,keyCode); + ioctlData.Data[PHY_REG_31]=Phy_GetRegW(privateData,31,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Phy Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + } + break; + case COMMAND_DUMP_EEPROM: + { + BYTE data=0; + BYTE index=0; + Eeprom_EnableAccess(privateData); + success=true; + for(index=0;index<8;index++) { + if(Eeprom_ReadLocation(privateData,index,&data)) { + ioctlData.Data[index]=(u32)data; + } else { + success=false; + break; + } + } + Eeprom_DisableAccess(privateData); + };break; + case COMMAND_GET_MAC_ADDRESS: + if(privateData->LanInitialized) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[0]=Mac_GetRegDW(privateData,ADDRH,keyCode); + ioctlData.Data[1]=Mac_GetRegDW(privateData,ADDRL,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Lan Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + } + break; + + case COMMAND_SET_MAC_ADDRESS: + if(privateData->LanInitialized) + { + u32 dwLow32=ioctlData.Data[1]; + u32 dwHigh16=ioctlData.Data[0]; + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + Mac_SetRegDW(privateData,ADDRH,dwHigh16,keyCode); + Mac_SetRegDW(privateData,ADDRL,dwLow32,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Lan Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + };break; + case COMMAND_LOAD_MAC_ADDRESS: + if(privateData->LanInitialized) { + Eeprom_EnableAccess(privateData); + if(Eeprom_Reload(privateData)) { + if(Eeprom_IsMacAddressLoaded(privateData)) { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + ioctlData.Data[0]=Mac_GetRegDW(privateData,ADDRH,keyCode); + ioctlData.Data[1]=Mac_GetRegDW(privateData,ADDRL,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + success=true; + } else { + SMSC_WARNING("Failed to Load Mac Address(1)"); + } + } else { + SMSC_WARNING("Failed to Load Mac Address(2)"); + } + Eeprom_DisableAccess(privateData); + } else { + SMSC_WARNING("Lan Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + };break; + case COMMAND_SAVE_MAC_ADDRESS: + if(privateData->LanInitialized) { + if(Eeprom_SaveMacAddress(privateData, + ioctlData.Data[0],ioctlData.Data[1])) { + success=true; + } + } else { + SMSC_WARNING("Lan Not Initialized,"); + SMSC_WARNING(" Use ifconfig to bring interface UP"); + };break; + case COMMAND_SET_DEBUG_MODE: + debug_mode=ioctlData.Data[0]; + if(debug_mode&0x04UL) { + if(OLD_REGISTERS(privateData)) + { + g_GpioSetting=0x00270700UL; + } else { + g_GpioSetting=0x00670700UL; + } + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); + } else { + Lan_SetRegDW(GPIO_CFG,0x70070000); + } + success=true; + break; + case COMMAND_SET_LINK_MODE: + link_mode=(ioctlData.Data[0]&0x7FUL); + if(privateData->LanInitialized) { + Phy_SetLink(privateData,link_mode); + } + success=true; + break; + case COMMAND_GET_LINK_MODE: + ioctlData.Data[0]=link_mode; + success=true; + break; + case COMMAND_CHECK_LINK: + Phy_UpdateLinkMode(privateData); + success=true; + break; + case COMMAND_READ_BYTE: + ioctlData.Data[1]=(*((volatile BYTE *)(ioctlData.Data[0]))); + success=true; + break; + case COMMAND_READ_WORD: + ioctlData.Data[1]=(*((volatile WORD *)(ioctlData.Data[0]))); + success=true; + break; + case COMMAND_READ_DWORD: + ioctlData.Data[1]=(*((volatile u32 *)(ioctlData.Data[0]))); + success=true; + break; + case COMMAND_WRITE_BYTE: + (*((volatile BYTE *)(ioctlData.Data[0])))= + ((BYTE)(ioctlData.Data[1])); + success=true; + break; + case COMMAND_WRITE_WORD: + (*((volatile WORD *)(ioctlData.Data[0])))= + ((WORD)(ioctlData.Data[1])); + success=true; + break; + case COMMAND_WRITE_DWORD: + (*((volatile u32 *)(ioctlData.Data[0])))= + ((u32)(ioctlData.Data[1])); + success=true; + break; + case COMMAND_SET_AMDIX_STS: + AutoMdix=(ioctlData.Data[0]); + if(privateData->LanInitialized) { + Phy_SetAutoMdixSts(privateData,AutoMdix); + } + success=true; + break; + case COMMAND_GET_AMDIX_STS: + ioctlData.Data[0]=AutoMdix; + if(privateData->LanInitialized) { + Phy_GetAutoMdixSts(privateData); + } + success=true; + break; + + default:return -EOPNOTSUPP; + } + +DONE: + if(success) { + ioctlData.dwSignature=SMSC9118_DRIVER_SIGNATURE; + if(copy_to_user(useraddr, &ioctlData, sizeof(ioctlData))) { + SMSC_WARNING("copy_to_user failed"); + result=-EFAULT; + } + result=0; + } + // SMSC_TRACE("<--Smsc9118_do_ioctl"); + return result; + +} + + + +int Smsc9118_ethtool_ioctl(PPRIVATE_DATA privateData, void * userAddr) +{ + int result=-EFAULT; + u32 ethcmd=0; + if(copy_from_user(ðcmd,userAddr,sizeof(ethcmd))) + { + result=-EFAULT; + goto DONE; + } + switch(ethcmd) { + case ETHTOOL_GSET:// Get settings. + // SMSC_TRACE("ETHTOOL_GSET"); + { + struct ethtool_cmd settings={ETHTOOL_GSET}; + settings.supported= + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_MII; + settings.advertising=ADVERTISED_MII; + if(privateData->dwLinkSettings & LINK_SPEED_10HD) + settings.advertising|=ADVERTISED_10baseT_Half; + if(privateData->dwLinkSettings & LINK_SPEED_10FD) + settings.advertising|=ADVERTISED_10baseT_Full; + if(privateData->dwLinkSettings & LINK_SPEED_100HD) + settings.advertising|=ADVERTISED_100baseT_Half; + if(privateData->dwLinkSettings & LINK_SPEED_100FD) + settings.advertising|=ADVERTISED_100baseT_Full; + if(privateData->dwLinkSettings & LINK_AUTO_NEGOTIATE) { + settings.advertising|=ADVERTISED_Autoneg; + settings.autoneg=AUTONEG_ENABLE; + } else settings.autoneg=AUTONEG_DISABLE; + if(privateData->dwLinkSpeed & (LINK_SPEED_100HD|LINK_SPEED_100FD)) + settings.speed=SPEED_100; + else settings.speed=SPEED_10; + if(privateData->dwLinkSpeed & (LINK_SPEED_10FD|LINK_SPEED_100FD)) + settings.duplex=DUPLEX_FULL; + else settings.duplex=DUPLEX_HALF; + settings.port=PORT_MII; + settings.phy_address=(u8)privateData->dwPhyAddress; + settings.transceiver=XCVR_INTERNAL; + settings.maxtxpkt=0; + settings.maxrxpkt=0; + if(copy_to_user(userAddr,&settings,sizeof(settings))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_SSET:// Set settings. + // SMSC_TRACE("ETHTOOL_SSET"); + { + struct ethtool_cmd settings; + u16 speed=0; + u8 duplex=0; + u8 autoneg=0; + if(copy_from_user(&settings,userAddr,sizeof(settings))) { + result=-EFAULT; + goto DONE; + } + if(privateData->dwLinkSettings&LINK_AUTO_NEGOTIATE) { + autoneg=AUTONEG_ENABLE; + } else { + autoneg=AUTONEG_DISABLE; + } + if(privateData->dwLinkSpeed&(LINK_SPEED_100HD|LINK_SPEED_100FD)) + { + speed=SPEED_100; + } else { + speed=SPEED_10; + } + if(privateData->dwLinkSpeed&(LINK_SPEED_10FD|LINK_SPEED_100FD)) + { + duplex=DUPLEX_FULL; + } else { + duplex=DUPLEX_HALF; + } + if((settings.speed!=100)&&(settings.speed!=10)) { + result=-EOPNOTSUPP; + goto DONE; + } + if((settings.duplex!=DUPLEX_FULL)&&(settings.duplex!=DUPLEX_HALF)) { + result=-EOPNOTSUPP; + goto DONE; + } + if((settings.autoneg!=AUTONEG_ENABLE)&&(settings.autoneg!=AUTONEG_DISABLE)) { + result=-EOPNOTSUPP; + goto DONE; + } + if((settings.autoneg!=autoneg)|| + (settings.speed!=speed)|| + (settings.duplex!=duplex)) + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if(settings.autoneg==AUTONEG_ENABLE) { + + + + WORD wAdvertisement=Phy_GetRegW(privateData,PHY_ANEG_ADV,keyCode); + wAdvertisement&=(~PHY_ANEG_ADV_SPEED_); + if(settings.speed==SPEED_100) { + if(settings.duplex==DUPLEX_FULL) { + wAdvertisement|=PHY_ANEG_ADV_100F_; + } else { + wAdvertisement|=PHY_ANEG_ADV_100H_; + } + } else { + if(settings.duplex==DUPLEX_FULL) { + wAdvertisement|=PHY_ANEG_ADV_10F_; + } else { + wAdvertisement|=PHY_ANEG_ADV_10H_; + } + } + + + + Phy_SetRegW(privateData,PHY_ANEG_ADV,wAdvertisement,keyCode); + Phy_SetRegW(privateData,PHY_BCR, + (PHY_BCR_AUTO_NEG_ENABLE_|PHY_BCR_RESTART_AUTO_NEG_),keyCode); + + + } else { + WORD wBcr=Phy_GetRegW(privateData,PHY_BCR,keyCode); + if(settings.speed==SPEED_100) { + wBcr|=PHY_BCR_SPEED_SELECT_; + } else { + wBcr&=(~PHY_BCR_SPEED_SELECT_); + } + if(settings.duplex==DUPLEX_FULL) { + wBcr|=PHY_BCR_DUPLEX_MODE_; + } else { + wBcr&=(~PHY_BCR_DUPLEX_MODE_); + } + Phy_SetRegW(privateData,PHY_BCR,wBcr,keyCode); + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + + } + result=0; + } + break; + case ETHTOOL_GDRVINFO:// Get driver info. + // SMSC_TRACE("ETHTOOL_GDRVINFO"); + { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + strcpy(info.driver,"Smsc9131_eth"); + memset(&info.version,0,sizeof(info.version)); + memset(&info.fw_version,0,sizeof(info.fw_version)); + sprintf(info.fw_version,"%u",(privateData->dwIdRev)&0xFFFF); + memset(&info.bus_info,0,sizeof(info.bus_info)); + memset(&info.reserved1,0,sizeof(info.reserved1)); + memset(&info.reserved2,0,sizeof(info.reserved2)); +#ifdef LINUX_2_6_OR_NEWER + info.n_stats=0; + info.testinfo_len=0; +#endif + info.eedump_len=0; + info.regdump_len=0; + if(copy_to_user(userAddr,&info,sizeof(info))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_GREGS:// Get NIC registers. + // SMSC_TRACE("ETHTOOL_GREGS"); + result=-EOPNOTSUPP; + break; + /* + case ETHTOOL_GWOL:// Get wake-on-lan options. + SMSC_TRACE("ETHTOOL_GWOL"); + { + struct ethtool_wolinfo wol_info={ETHTOOL_GWOL}; + //wol_info.supported=true; + wol_info.supported=(WAKE_PHY | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC); + wol_info.wolopts= privateData->WolWakeupOpts; + memset(&wol_info.sopass,0,sizeof(wol_info.sopass)); + if(copy_to_user(userAddr,&wol_info,sizeof(wol_info))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_SWOL:// Set wake-on-lan options. + SMSC_TRACE("ETHTOOL_SWOL"); + { + unsigned long dwIntFlags=0; + struct ethtool_wolinfo wol_info; + if(copy_from_user(&wol_info,userAddr,sizeof(wol_info))) + { + result=-EFAULT; + } else { + SMSC_TRACE(DBG_IOCTL,"WOL OPTS = 0x%x", wol_info.wolopts); + spin_lock_irqsave(&(privateData->PhyLock),dwIntFlags); + privateData->WolWakeupOpts = wol_info.wolopts; + spin_unlock_irqrestore(&(privateData->PhyLock),dwIntFlags); + result=0; + } + } + break; + */ + case ETHTOOL_GMSGLVL:// Get driver message level + // SMSC_TRACE("ETHTOOL_GMSGLVL"); + { + struct ethtool_value msgLevel={ETHTOOL_GMSGLVL}; + msgLevel.data=debug_mode; + if(copy_to_user(userAddr, &msgLevel,sizeof(msgLevel))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_SMSGLVL:// Set driver msg level. + // SMSC_TRACE("ETHTOOL_SMSGLVL"); + { + struct ethtool_value msgLevel; + if(copy_from_user(&msgLevel,userAddr,sizeof(msgLevel))) + { + result=-EFAULT; + } else { + debug_mode=msgLevel.data; + result=0; + } + } + break; + case ETHTOOL_NWAY_RST:// Restart autonegotiation. + // SMSC_TRACE("ETHTOOL_NWAY_RST"); + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + Phy_SetRegW(privateData,PHY_BCR, + (PHY_BCR_AUTO_NEG_ENABLE_|PHY_BCR_RESTART_AUTO_NEG_),keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + result=0; + } + break; + case ETHTOOL_GLINK:// Get link status (ethtool_value) + // SMSC_TRACE("ETHTOOL_GLINK"); + { + struct ethtool_value linkStatus={ETHTOOL_GLINK}; + if(privateData->dwLinkSpeed!=LINK_OFF) { + linkStatus.data=1; + } else { + linkStatus.data=0; + } + if(copy_to_user(userAddr,&linkStatus,sizeof(linkStatus))) + { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_GEEPROM:// Get EEPROM data + // SMSC_TRACE("ETHTOOL_GEEPROM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_SEEPROM:// Set EEPROM data. + // SMSC_TRACE("ETHTOOL_SEEPROM"); + result=-EOPNOTSUPP; + break; + +#ifdef LINUX_2_6_OR_NEWER + + case ETHTOOL_GCOALESCE:// Get coalesce config + // SMSC_TRACE("ETHTOOL_GCOALESCE"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_SCOALESCE:// Set coalesce config. + // SMSC_TRACE("ETHTOOL_SCOALESCE"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GRINGPARAM:// Get ring parameters + // SMSC_TRACE("ETHTOOL_GRINGPARAM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_SRINGPARAM:// Set ring parameters. + // SMSC_TRACE("ETHTOOL_SRINGPARAM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GPAUSEPARAM:// Get pause parameters + // SMSC_TRACE("ETHTOOL_GPAUSEPARAM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_SPAUSEPARAM:// Set pause parameters. + // SMSC_TRACE("ETHTOOL_SPAUSEPARAM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GRXCSUM:// Get RX hw csum enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_GRXCSUM"); + { + struct ethtool_value rxCsum={ETHTOOL_GRXCSUM}; + rxCsum.data=0; + if(copy_to_user(userAddr,&rxCsum,sizeof(rxCsum))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_SRXCSUM:// Set RX hw csum enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_SRXCSUM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GTXCSUM:// Get TX hw csum enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_GTXCSUM"); + { + struct ethtool_value txCsum={ETHTOOL_GTXCSUM}; + txCsum.data=0; + if(copy_to_user(userAddr,&txCsum,sizeof(txCsum))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_STXCSUM:// Set TX hw csum enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_STXCSUM"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GSG:// Get scatter-gather enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_GSG"); + { + struct ethtool_value sg={ETHTOOL_GSG}; + sg.data=0; + if(copy_to_user(userAddr,&sg,sizeof(sg))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_SSG:// Set scatter-gather enable (ethtool_value). + // SMSC_TRACE("ETHTOOL_SSG"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_TEST:// execute NIC self-test. + // SMSC_TRACE("ETHTOOL_TEST"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GSTRINGS:// get specified string set + // SMSC_TRACE("ETHTOOL_GSTRINGS"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_PHYS_ID:// identify the NIC + // SMSC_TRACE("ETHTOOL_PHYS_ID"); + result=-EOPNOTSUPP; + break; + case ETHTOOL_GSTATS:// get NIC-specific statistics + // SMSC_TRACE("ETHTOOL_GSTATS"); + result=-EOPNOTSUPP; + break; + + case ETHTOOL_GTSO:// Get TSO enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_GTSO"); + { + struct ethtool_value tso={ETHTOOL_GTSO}; + tso.data=0; + if(copy_to_user(userAddr,&tso,sizeof(tso))) { + result=-EFAULT; + } else { + result=0; + } + } + break; + case ETHTOOL_STSO:// Set TSO enable (ethtool_value) + // SMSC_TRACE("ETHTOOL_STS0"); + result=-EOPNOTSUPP; + break; +#endif + default: + // SMSC_WARNING("unknown ethcmd=0x%08X",ethcmd); + result=-EOPNOTSUPP; + break; + } +DONE: + return result; +} + + +//returns time1-time2; +TIME_SPAN Gpt_FreeRunCompare(u32 time1,u32 time2) +{ + return ((TIME_SPAN)(time1-time2)); +} +void Gpt_ScheduleInterrupt(PPRIVATE_DATA privateData,TIME_SPAN timeSpan) +{ + u32 timerValue=0; + if(timeSpan<0) timeSpan=0; + timerValue=(u32)timeSpan; + if((timerValue%2500)>=1250) { + timerValue=(timerValue/2500)+1; + } else { + timerValue=(timerValue/2500); + } + if(timerValue>0x0000FFFFUL) { + timerValue=0x0000FFFF; + } + Lan_SetRegDW(GPT_CFG,(timerValue|GPT_CFG_TIMER_EN_)); + Lan_SetRegDW(INT_STS,INT_STS_GPT_INT_); +} + +void Gpt_CancelInterrupt(PPRIVATE_DATA privateData) +{ + Lan_SetRegDW(GPT_CFG,0UL); + Lan_SetRegDW(INT_STS,INT_STS_GPT_INT_); +} + +void Gpt_ScheduleCallBack( + PPRIVATE_DATA privateData, + void (*callBackFunction)(PPRIVATE_DATA privateData), + u32 callBackTime) +{ + u32 slot_index=GPT_SCHEDULE_DEPTH; + bool result=false; + if((callBackFunction!=NULL)&&(callBackTime!=0)) { + unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + spin_lock_irqsave(&privateData->GpTimerLock,dwIntFlags); + { + u32 index=0; + u32 currentTime=Lan_GetRegDW(FREE_RUN); + TIME_SPAN nextCallTime=MAX_TIME_SPAN; + TIME_SPAN timeSpan=MAX_TIME_SPAN; + bool rescheduleRequired=false; + for(index=0;index<GPT_SCHEDULE_DEPTH;index++) { + if(privateData->GptFunction[index]==NULL) { + if(!result) { + result=true; + //lint -save + //lint -e611 //suspicious cast + privateData->GptFunction[index]=(void *)callBackFunction; + //lint -restore_ + privateData->GptCallTime[index]=currentTime+(2500*callBackTime); + timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime); + if(nextCallTime>timeSpan) { + nextCallTime=timeSpan; + rescheduleRequired=true; + slot_index = index; + } + } + } else { + timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime); + if(nextCallTime>=timeSpan) { + nextCallTime=timeSpan; + rescheduleRequired=false; + } + } + } + if(rescheduleRequired) { + privateData->Gpt_scheduled_slot_index = slot_index; + Gpt_ScheduleInterrupt(privateData,nextCallTime); + } + } + spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags); + } + if(!result) { + SMSC_WARNING("Gpt_ScheduleCallBack: Failed"); + } +} + +void Gpt_CancelCallBack( + PPRIVATE_DATA privateData, + void (*callBackFunction)(PPRIVATE_DATA privateData)) +{ + bool result=false; + if(callBackFunction!=NULL) { + unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + spin_lock_irqsave(&(privateData->GpTimerLock),dwIntFlags); + { + u32 index=0; + u32 currentTime=Lan_GetRegDW(FREE_RUN); + TIME_SPAN nextCallTime=MAX_TIME_SPAN; + TIME_SPAN timeSpan=MAX_TIME_SPAN; + bool rescheduleRequired=false; + for(index=0;index<GPT_SCHEDULE_DEPTH;index++) { + if(privateData->GptFunction[index]==callBackFunction) { + result=true; + //lint -save + //lint -e611 //suspicious cast + privateData->GptFunction[index]=(void *)NULL; + // cancelled time will not need a + // re-scheduled + + // re-scheduled is done at other + // non-null slots + } + else if(privateData->GptFunction[index]!=NULL) { + timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime); + // if this scheduled time is earlier + // than current scheduled time + // AND not a duplicated one + if(nextCallTime>=timeSpan && privateData->Gpt_scheduled_slot_index != index) { + nextCallTime=timeSpan; + rescheduleRequired=true; + privateData->Gpt_scheduled_slot_index = index; + } + } + } + if(rescheduleRequired) { + Gpt_ScheduleInterrupt(privateData,nextCallTime); + } + else if (privateData->Gpt_scheduled_slot_index==GPT_SCHEDULE_DEPTH) { + Gpt_CancelInterrupt(privateData); + } + } + spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags); + } + if(!result) { + SMSC_WARNING("Gpt_CancelCallBack: Failed"); + } +} + +bool Gpt_HandleInterrupt( + PPRIVATE_DATA privateData,u32 dwIntSts) +{ + SMSC_ASSERT(privateData!=NULL); + if(dwIntSts&INT_STS_GPT_INT_) + { + unsigned long dwIntFlags=0; + Lan_SetRegDW(INT_STS,INT_STS_GPT_INT_); + spin_lock_irqsave(&(privateData->GpTimerLock),dwIntFlags); + { + u32 index=0; + u32 currentTime=Lan_GetRegDW(FREE_RUN); + TIME_SPAN timeSpan=MAX_TIME_SPAN; + TIME_SPAN nextCallTime=MAX_TIME_SPAN; + bool rescheduleRequired=false; + for(index=0;index<GPT_SCHEDULE_DEPTH;index++) { + if(privateData->GptFunction[index]!=NULL) { + timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime); + if(timeSpan<1250) { + void (*callBackFunction)(PPRIVATE_DATA privateData); + callBackFunction=privateData->GptFunction[index]; + privateData->GptFunction[index]=NULL; + spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags); + privateData->Gpt_scheduled_slot_index = GPT_SCHEDULE_DEPTH; + callBackFunction(privateData); + spin_lock_irqsave(&(privateData->GpTimerLock),dwIntFlags); + } + } + } + for(index=0;index<GPT_SCHEDULE_DEPTH;index++) { + if(privateData->GptFunction[index]!=NULL) { + rescheduleRequired=true; + timeSpan=Gpt_FreeRunCompare(privateData->GptCallTime[index],currentTime); + if(nextCallTime>timeSpan) { + nextCallTime=timeSpan; + privateData->Gpt_scheduled_slot_index = index; + } + } + } + if(rescheduleRequired) { + Gpt_ScheduleInterrupt(privateData,nextCallTime); + } + } + spin_unlock_irqrestore(&(privateData->GpTimerLock),dwIntFlags); + return true; + } + return false; +} + +void GptCB_RxCompleteMulticast(PPRIVATE_DATA privateData) +{ + Rx_CompleteMulticastUpdate (privateData); +} + +void GptCB_RestartBurst(PPRIVATE_DATA privateData) +{ + if(privateData->RxFlowControlActive) { + privateData->RxFlowBurstActive=true; + if(privateData->RxFlowBurstWorkLoad>privateData->RxFlowBurstMaxWorkLoad) { + privateData->RxFlowBurstWorkLoad-=privateData->RxFlowBurstMaxWorkLoad; + } else { + privateData->RxFlowBurstWorkLoad=0; + } + Gpt_ScheduleCallBack(privateData,GptCB_RestartBurst, + privateData->RxFlowParameters.BurstPeriod); + } + Lan_EnableInterrupt(privateData,privateData->RxInterrupts); +} + +void GptCB_MeasureRxThroughput(PPRIVATE_DATA privateData) +{ + if(privateData->RxFlowMeasuredMaxThroughput<privateData->RxFlowCurrentThroughput) { + privateData->RxFlowMeasuredMaxThroughput=privateData->RxFlowCurrentThroughput; + } + if(privateData->RxFlowMeasuredMaxPacketCount<privateData->RxFlowCurrentPacketCount) { + privateData->RxFlowMeasuredMaxPacketCount=privateData->RxFlowCurrentPacketCount; + } + if(privateData->RxFlowCurrentThroughput!=0) { + if(privateData->RxFlowMaxWorkLoad!=0) { + if(!(privateData->RxFlowControlActive)) { + u32 activationLevel= + (privateData->RxFlowMaxWorkLoad*(100+RX_FLOW_ACTIVATION))/100; + if(privateData->RxFlowCurrentWorkLoad>=activationLevel) { + privateData->RxFlowControlActive=true; + privateData->RxFlowBurstActive=true; + privateData->RxFlowBurstWorkLoad=0; + Gpt_ScheduleCallBack(privateData,GptCB_RestartBurst, + privateData->RxFlowParameters.BurstPeriod); + //SET_GPIO(GP_TX); + } + } else { + u32 deactivationLevel= + (privateData->RxFlowMaxWorkLoad*(100-RX_FLOW_DEACTIVATION))/100; + if(privateData->RxFlowCurrentWorkLoad<=deactivationLevel) { + privateData->RxFlowControlActive=false; + //CLEAR_GPIO(GP_TX); + } + } + } + privateData->RxFlowCurrentThroughput=0; + privateData->RxFlowCurrentPacketCount=0; + privateData->RxFlowCurrentWorkLoad=0; + Gpt_ScheduleCallBack(privateData,GptCB_MeasureRxThroughput,1000); + } else { + if(privateData->RxFlowMaxWorkLoad!=0) { + if(privateData->RxFlowControlActive) { + privateData->RxFlowControlActive=false; + //CLEAR_GPIO(GP_TX); + } + } + privateData->MeasuringRxThroughput=false; + } +} + +irqreturn_t Smsc9118_ISR(int Irq, void *dev_id) +{ + u32 dwIntCfg=0; + u32 dwIntSts=0; + u32 dwIntEn=0; + u32 dwIntBits=0; + PPRIVATE_DATA privateData=(PPRIVATE_DATA)dev_id; + bool serviced=false; + + Irq=Irq;//make lint happy + + if(privateData==NULL) { + SMSC_WARNING("Smsc9118_ISR(privateData==NULL)"); + goto DONE; + } + if(privateData->dwLanBase==0) { + SMSC_WARNING("Smsc9118_ISR(dwLanBase==0)"); + goto DONE; + } + SET_GPIO(GP_ISR); + dwIntCfg=Lan_GetRegDW(INT_CFG); + if((dwIntCfg&0x00001100)!=0x00001100) { + SMSC_TRACE("In ISR, not my interrupt, dwIntCfg=0x%08X", + dwIntCfg); + goto ALMOST_DONE; + } + + { + /* + * KH: Neither is true for 9210 + u32 reservedBits; + if(OLD_REGISTERS(privateData)) { + reservedBits=0x00FFEEEEUL; + } else { + reservedBits=0x00FFCEEEUL; + } + */ + /* + reservedBits = 0x00FF8EEEUL; + if(dwIntCfg&reservedBits) { + SMSC_WARNING("In ISR, reserved bits are high.\n"); + SMSC_TRACE("(reserved=0x%08X int=0x%08X)\n", reservedBits, dwIntCfg); + //this could mean surprise removal + goto ALMOST_DONE; + } + */ + } + + dwIntSts=Lan_GetRegDW(INT_STS); + dwIntEn=Lan_GetRegDW(INT_EN); + dwIntBits=dwIntSts&dwIntEn; + //SMSC_TRACE("dwIntBits= 0x%x8l \n", dwIntBits); + privateData->LastIntStatus3=privateData->LastIntStatus2; + privateData->LastIntStatus2=privateData->LastIntStatus1; + privateData->LastIntStatus1=dwIntBits; + if(Lan_HandleSoftwareInterrupt(privateData,dwIntBits)) { + serviced=true; + } + if(Gpt_HandleInterrupt(privateData,dwIntBits)) { + serviced=true; + } + if(Tx_HandleInterrupt(privateData,dwIntBits)) { + serviced=true; + } + + if(RxStop_HandleInterrupt(privateData,dwIntBits)) { + serviced=true; + } + + + if(Rx_HandleInterrupt(privateData,dwIntBits)) { + serviced=true; + } + + if(!serviced) { + SMSC_WARNING("unserviced interrupt dwIntCfg=0x%08X,dwIntSts=0x%08X,dwIntEn=0x%08X,dwIntBits=0x%08X", + dwIntCfg,dwIntSts,dwIntEn,dwIntBits); + } + +ALMOST_DONE: + CLEAR_GPIO(GP_ISR); +DONE: + return IRQ_RETVAL(serviced); +} + +#ifdef USE_PHY_WORK_AROUND +bool Phy_Reset(PPRIVATE_DATA privateData,VL_KEY keyCode) +{ + bool result=false; + WORD wTemp=0; + u32 dwLoopCount=100000; + SMSC_TRACE("Performing PHY BCR Reset"); + Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_RESET_,keyCode); + do { + udelay(10); + wTemp=Phy_GetRegW(privateData,PHY_BCR,keyCode); + dwLoopCount--; + } while((dwLoopCount>0)&&(wTemp&PHY_BCR_RESET_)); + if(wTemp&PHY_BCR_RESET_) { + SMSC_WARNING("Phy Reset failed to complete."); + goto DONE; + } + //extra delay required because the phy may not be completed with its reset + // when PHY_BCR_RESET_ is cleared. + // They say 256 uS is enough delay but I'm using 500 here to be safe + udelay(500); + result=true; +DONE: + return result; +} + +u32 Phy_LBT_GetTxStatus(PPRIVATE_DATA privateData) +{ + u32 result=Lan_GetRegDW(TX_FIFO_INF); + if(OLD_REGISTERS(privateData)) { + result&=TX_FIFO_INF_TSFREE_; + if(result!=0x00800000UL) { + result=Lan_GetRegDW(TX_STATUS_FIFO); + } else { + result=0; + } + } else { + result&=TX_FIFO_INF_TSUSED_; + if(result!=0x00000000UL) { + result=Lan_GetRegDW(TX_STATUS_FIFO); + } else { + result=0; + } + } + return result; +} + +u32 Phy_LBT_GetRxStatus(PPRIVATE_DATA privateData) +{ + u32 result=Lan_GetRegDW(RX_FIFO_INF); + if(result&0x00FF0000UL) { + //Rx status is available, read it + result=Lan_GetRegDW(RX_STATUS_FIFO); + } else { + result=0; + } + return result; +} + +bool Phy_TransmitTestPacket(PPRIVATE_DATA privateData) +{ + bool result=false; + u32 dwLoopCount=0; + u32 dwTxCmdA=0; + u32 dwTxCmdB=0; + u32 dwStatus=0; + + //write Tx Packet to 118 + dwTxCmdA= + ((((u32)(privateData->LoopBackTxPacket))&0x03UL)<<16) | //u32 alignment adjustment + TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ | + ((u32)(MIN_PACKET_SIZE)); + dwTxCmdB= + (((u32)(MIN_PACKET_SIZE))<<16) | + ((u32)(MIN_PACKET_SIZE)); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(privateData->LoopBackTxPacket))&0xFFFFFFFCUL), + (((u32)(MIN_PACKET_SIZE))+3+ + (((u32)(privateData->LoopBackTxPacket))&0x03UL))>>2); + + //wait till transmit is done + dwLoopCount=60; + while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetTxStatus(privateData))==0)) { + udelay(5); + dwLoopCount--; + } + if(dwStatus==0) { + SMSC_WARNING("Failed to Transmit during Packet Test"); + goto DONE; + } + if(dwStatus&0x00008000UL) { + SMSC_WARNING("Transmit encountered errors during Packet Test"); + goto DONE; + } +DONE: + return result; +} + +bool Phy_CheckLoopBackPacket(PPRIVATE_DATA privateData) + +{ + bool result=false; + u32 tryCount=0; + u32 dwLoopCount=0; + for(tryCount=0;tryCount<10;tryCount++) + { + u32 dwTxCmdA=0; + u32 dwTxCmdB=0; + u32 dwStatus=0; + u32 dwPacketLength=0; + + //zero-out Rx Packet memory + memset(privateData->LoopBackRxPacket,0,MIN_PACKET_SIZE); + + //write Tx Packet to 118 + dwTxCmdA= + ((((u32)(privateData->LoopBackTxPacket))&0x03UL)<<16) | //u32 alignment adjustment + TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ | + ((u32)(MIN_PACKET_SIZE)); + dwTxCmdB= + (((u32)(MIN_PACKET_SIZE))<<16) | + ((u32)(MIN_PACKET_SIZE)); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(privateData->LoopBackTxPacket))&0xFFFFFFFCUL), + (((u32)(MIN_PACKET_SIZE))+3+ + (((u32)(privateData->LoopBackTxPacket))&0x03UL))>>2); + + //wait till transmit is done + dwLoopCount=60; + while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetTxStatus(privateData))==0)) { + udelay(5); + dwLoopCount--; + } + if(dwStatus==0) { + SMSC_WARNING("Failed to Transmit during Loop Back Test"); + continue; + } + if(dwStatus&0x00008000UL) { + SMSC_WARNING("Transmit encountered errors during Loop Back Test"); + continue; + } + + //wait till receive is done + dwLoopCount=60; + while((dwLoopCount>0)&&((dwStatus=Phy_LBT_GetRxStatus(privateData))==0)) + { + udelay(5); + dwLoopCount--; + } + if(dwStatus==0) { + SMSC_WARNING("Failed to Receive during Loop Back Test"); + continue; + } + if(dwStatus&RX_STS_ES_) + { + SMSC_WARNING("Receive encountered errors during Loop Back Test"); + continue; + } + + dwPacketLength=((dwStatus&0x3FFF0000UL)>>16); + + Platform_ReadFifo( + privateData->dwLanBase, + ((u32 *)(privateData->LoopBackRxPacket)), + (dwPacketLength+3+(((u32)(privateData->LoopBackRxPacket))&0x03UL))>>2); + + if(dwPacketLength!=(MIN_PACKET_SIZE+4)) { + SMSC_WARNING("Unexpected packet size during loop back test, size=%d, will retry",dwPacketLength); + } else { + u32 byteIndex=0; + bool foundMissMatch=false; + for(byteIndex=0;byteIndex<MIN_PACKET_SIZE;byteIndex++) { + if(privateData->LoopBackTxPacket[byteIndex]!=privateData->LoopBackRxPacket[byteIndex]) + { + foundMissMatch=true; + break; + } + } + if(!foundMissMatch) { + SMSC_TRACE("Successfully Verified Loop Back Packet"); + result=true; + goto DONE; + } else { + SMSC_WARNING("Data miss match during loop back test, will retry."); + } + } + } +DONE: + return result; +} + +bool Phy_LoopBackTest(PPRIVATE_DATA privateData) +{ + bool result=false; + u32 byteIndex=0; + u32 tryCount=0; + // u32 failed=0; + //Initialize Tx Packet + for(byteIndex=0;byteIndex<6;byteIndex++) { + //use broadcast destination address + privateData->LoopBackTxPacket[byteIndex]=(BYTE)0xFF; + } + for(byteIndex=6;byteIndex<12;byteIndex++) { + //use incrementing source address + privateData->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex; + } + //Set length type field + privateData->LoopBackTxPacket[12]=0x00; + privateData->LoopBackTxPacket[13]=0x00; + for(byteIndex=14;byteIndex<MIN_PACKET_SIZE;byteIndex++) + { + privateData->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex; + } + //TRY_AGAIN: + { + u32 dwRegVal=Lan_GetRegDW(HW_CFG); + dwRegVal&=(HW_CFG_TX_FIF_SZ_|0x00000FFFUL); + dwRegVal|=HW_CFG_SF_; + Lan_SetRegDW(HW_CFG,dwRegVal); + } + Lan_SetRegDW(TX_CFG,TX_CFG_TX_ON_); + + Lan_SetRegDW(RX_CFG,(((u32)(privateData->LoopBackRxPacket))&0x03)<<8); + + { + + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + //Set Phy to 10/FD, no ANEG, + Phy_SetRegW(privateData,PHY_BCR,0x0100,keyCode); + + //enable MAC Tx/Rx, FD + Mac_SetRegDW(privateData,MAC_CR,MAC_CR_FDPX_|MAC_CR_TXEN_|MAC_CR_RXEN_,keyCode); + + // Phy_TransmitTestPacket(privateData); + + //set Phy to loopback mode + Phy_SetRegW(privateData,PHY_BCR,0x4100,keyCode); + + for(tryCount=0;tryCount<10;tryCount++) { + if(Phy_CheckLoopBackPacket(privateData)) + { + result=true; + goto DONE; + } + privateData->dwResetCount++; + //disable MAC rx + Mac_SetRegDW(privateData,MAC_CR,0UL,keyCode); + Phy_Reset(privateData,keyCode); + + //Set Phy to 10/FD, no ANEG, and Loopbackmode + Phy_SetRegW(privateData,PHY_BCR,0x4100,keyCode); + + //enable MAC Tx/Rx, FD + Mac_SetRegDW(privateData,MAC_CR,MAC_CR_FDPX_|MAC_CR_TXEN_|MAC_CR_RXEN_,keyCode); + } + // if(failed<2) { + // if(tryCount>=10) { + // u32 timeOut=10000; + // Lan_ShowRegs(privateData); + // SMSC_TRACE("Performing full reset"); + // privateData->Lan9118->HW_CFG=HW_CFG_SRST_; + // while((timeOut>0)&&(privateData->Lan9118->HW_CFG&HW_CFG_SRST_)) { + // udelay(1); + // timeOut--; + // } + // failed++; + // goto TRY_AGAIN; + // } + // } +DONE: + //disable MAC + Mac_SetRegDW(privateData,MAC_CR,0UL,keyCode); + //Cancel Phy loopback mode + Phy_SetRegW(privateData,PHY_BCR,0U,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + + Lan_SetRegDW(TX_CFG,0UL); + Lan_SetRegDW(RX_CFG,0UL); + + return result; +} + +#endif //USE_PHY_WORK_AROUND +void Phy_SetLink(PPRIVATE_DATA privateData, + u32 dwLinkRequest) +{ + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if(dwLinkRequest&LINK_AUTO_NEGOTIATE) { + WORD wTemp; + wTemp=Phy_GetRegW(privateData, + PHY_ANEG_ADV,keyCode); + wTemp&=~PHY_ANEG_ADV_PAUSE_; + if(dwLinkRequest&LINK_ASYMMETRIC_PAUSE) { + wTemp|=PHY_ANEG_ADV_ASYMP_; + } + if(dwLinkRequest&LINK_SYMMETRIC_PAUSE) { + wTemp|=PHY_ANEG_ADV_SYMP_; + } + wTemp&=~PHY_ANEG_ADV_SPEED_; + if(dwLinkRequest&LINK_SPEED_10HD) { + wTemp|=PHY_ANEG_ADV_10H_; + } + if(dwLinkRequest&LINK_SPEED_10FD) { + wTemp|=PHY_ANEG_ADV_10F_; + } + if(dwLinkRequest&LINK_SPEED_100HD) { + wTemp|=PHY_ANEG_ADV_100H_; + } + if(dwLinkRequest&LINK_SPEED_100FD) { + wTemp|=PHY_ANEG_ADV_100F_; + } + Phy_SetRegW(privateData,PHY_ANEG_ADV,wTemp,keyCode); + + // begin to establish link + privateData->dwRemoteFaultCount=0; + Phy_SetRegW(privateData, + PHY_BCR, + PHY_BCR_AUTO_NEG_ENABLE_| + PHY_BCR_RESTART_AUTO_NEG_, + keyCode); + } else { + WORD wTemp=0; + if(dwLinkRequest&(LINK_SPEED_100FD)) { + dwLinkRequest=LINK_SPEED_100FD; + } else if(dwLinkRequest&(LINK_SPEED_100HD)) { + dwLinkRequest=LINK_SPEED_100HD; + } else if(dwLinkRequest&(LINK_SPEED_10FD)) { + dwLinkRequest=LINK_SPEED_10FD; + } else if(dwLinkRequest&(LINK_SPEED_10HD)) { + dwLinkRequest=LINK_SPEED_10HD; + } + if(dwLinkRequest&(LINK_SPEED_10FD|LINK_SPEED_100FD)) { + wTemp|=PHY_BCR_DUPLEX_MODE_; + } + if(dwLinkRequest&(LINK_SPEED_100HD|LINK_SPEED_100FD)) { + wTemp|=PHY_BCR_SPEED_SELECT_; + } + Phy_SetRegW(privateData,PHY_BCR,wTemp,keyCode); + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); +} + +void Phy_SetAutoMdixSts(PPRIVATE_DATA privateData, + WORD wAutoMdixSts) +{ + WORD SpecialCtrlSts=0U; + + if (((privateData->dwGeneration)>2) && (!(privateData->ExtPhy))) + { + if (wAutoMdixSts > 2) + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + SpecialCtrlSts=Phy_GetRegW(privateData, SPECIAL_CTRL_STS,keyCode); + SpecialCtrlSts = (SpecialCtrlSts&0x1FFF); + Phy_SetRegW(privateData, SPECIAL_CTRL_STS,SpecialCtrlSts,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + + if (Lan_GetRegDW(HW_CFG) & HW_CFG_AMDIX_EN_STRAP_STS_) { + SMSC_TRACE("Auto-MDIX Enable by default!!!"); + } + else { + SMSC_TRACE("Auto-MDIX Disable by default!!!"); + } + } + else + { + + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + SpecialCtrlSts=Phy_GetRegW(privateData, SPECIAL_CTRL_STS,keyCode); + SpecialCtrlSts = (((wAutoMdixSts+4) << 13) | (SpecialCtrlSts&0x1FFF)); + Phy_SetRegW(privateData, SPECIAL_CTRL_STS,SpecialCtrlSts,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + + if (wAutoMdixSts & AMDIX_ENABLE) { + SMSC_TRACE("Override Strap, Enable Auto-MDIX "); + } else if (wAutoMdixSts & AMDIX_DISABLE_CROSSOVER) { + SMSC_TRACE("Override Strap, Disable Auto-MDIX, CrossOver Cable"); + } else { + SMSC_TRACE("Override Strap, Disable Auto-MDIX, Straight Cable"); + } + + } + } + + else { + SMSC_TRACE("This chip or PHY doesn't support HP AMDIX!!!"); + } + +} + +void Phy_GetAutoMdixSts(PPRIVATE_DATA privateData) +{ + + + WORD SpecialCtrlSts=0U; + unsigned long dwIntFlags=0; + + if (((privateData->dwGeneration)>2) && (!(privateData->ExtPhy))) + { + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + SpecialCtrlSts=Phy_GetRegW(privateData, SPECIAL_CTRL_STS,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + + if (SpecialCtrlSts & SPECIAL_CTRL_STS_OVRRD_AMDIX_) { + + if (SpecialCtrlSts & SPECIAL_CTRL_STS_AMDIX_ENABLE_) { + SMSC_TRACE("AutoMdix Status: Override Strap, Enable Auto Mdix"); + } + else if (SpecialCtrlSts & SPECIAL_CTRL_STS_AMDIX_STATE_) { + + SMSC_TRACE("AutoMdix Status: Override Strap, Disable Auto Mdix, CrossOver Cable"); + + } else { + + SMSC_TRACE("AutoMdix Status: Override Strap, Disable Auto Mdix, Straight Cable"); + } + + } + else { + if (Lan_GetRegDW(HW_CFG) & HW_CFG_AMDIX_EN_STRAP_STS_) { + SMSC_TRACE("AutoMdix Status: Enable by default!!!"); + } + else { + SMSC_TRACE("AutoMdix Status: Disable by default!!!"); + } + } + + } + else { + SMSC_TRACE("This chip or PHY doesn't support HP AMDIX!!!"); + } + +} + + +bool Phy_Initialize( + PPRIVATE_DATA privateData, + u32 dwPhyAddr, + u32 dwLinkRequest) +{ + bool result=false; + u32 dwTemp=0; + WORD wTemp=0; + u32 dwLoopCount=0; + // WORD SpecialCtrlSts=0U; + + SMSC_TRACE("-->Phy_Initialize"); + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dwLanBase!=0); + SMSC_ASSERT(dwLinkRequest<=0x7FUL); + privateData->ExtPhy=false; + + if(dwPhyAddr!=0xFFFFFFFFUL) { + switch(privateData->dwIdRev&0xFFFF0000) { + case 0x117A0000UL: + case 0x115A0000UL: + goto EXTERNAL_PHY_SUPPORTED; + case 0x01170000UL: + case 0x01150000UL: + if(privateData->dwIdRev&0x0000FFFF) { + u32 dwHwCfg=0; +EXTERNAL_PHY_SUPPORTED: + dwHwCfg=Lan_GetRegDW(HW_CFG); + if(dwHwCfg&HW_CFG_EXT_PHY_DET_) { + //External phy is requested, supported, and detected + //Attempt to switch + //NOTE: Assuming Rx and Tx are stopped + // because Phy_Initialize is called before + // Rx_Initialize and Tx_Initialize + WORD wPhyId1=0; + WORD wPhyId2=0; + + //Disable phy clocks to the mac + dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_); + dwHwCfg|= HW_CFG_PHY_CLK_SEL_CLK_DIS_; + Lan_SetRegDW(HW_CFG,dwHwCfg); + udelay(10);//wait for clocks to acutally stop + + dwHwCfg|=HW_CFG_EXT_PHY_EN_; + Lan_SetRegDW(HW_CFG,dwHwCfg); + + dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_); + dwHwCfg|= HW_CFG_PHY_CLK_SEL_EXT_PHY_; + Lan_SetRegDW(HW_CFG,dwHwCfg); + udelay(10);//wait for clocks to actually start + + dwHwCfg|=HW_CFG_SMI_SEL_; + Lan_SetRegDW(HW_CFG,dwHwCfg); + + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if(dwPhyAddr<=31) { + //only check the phy address specified + privateData->dwPhyAddress=dwPhyAddr; + wPhyId1=Phy_GetRegW(privateData,PHY_ID_1,keyCode); + wPhyId2=Phy_GetRegW(privateData,PHY_ID_2,keyCode); + } else { + //auto detect phy + u32 address=0; + for(address=0;address<=31;address++) { + privateData->dwPhyAddress=address; + wPhyId1=Phy_GetRegW(privateData,PHY_ID_1,keyCode); + wPhyId2=Phy_GetRegW(privateData,PHY_ID_2,keyCode); + if((wPhyId1!=0xFFFFU)||(wPhyId2!=0xFFFFU)) { + SMSC_TRACE("Detected Phy at address = 0x%02X = %d", + address,address); + break; + } + } + if(address>=32) { + SMSC_WARNING("Failed to auto detect external phy"); + } + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + if((wPhyId1==0xFFFFU)&&(wPhyId2==0xFFFFU)) { + SMSC_WARNING("External Phy is not accessable"); + SMSC_WARNING(" using internal phy instead"); + //revert back to interal phy settings. + + //Disable phy clocks to the mac + dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_); + dwHwCfg|= HW_CFG_PHY_CLK_SEL_CLK_DIS_; + Lan_SetRegDW(HW_CFG,dwHwCfg); + udelay(10);//wait for clocks to actually stop + + dwHwCfg&=(~HW_CFG_EXT_PHY_EN_); + Lan_SetRegDW(HW_CFG,dwHwCfg); + + dwHwCfg&= (~HW_CFG_PHY_CLK_SEL_); + dwHwCfg|= HW_CFG_PHY_CLK_SEL_INT_PHY_; + Lan_SetRegDW(HW_CFG,dwHwCfg); + udelay(10);//wait for clocks to actually start + + dwHwCfg&=(~HW_CFG_SMI_SEL_); + Lan_SetRegDW(HW_CFG,dwHwCfg); + goto USE_INTERNAL_PHY; + } else { + SMSC_TRACE("Successfully switched to external phy"); + privateData->ExtPhy=true; +#ifdef USE_LED1_WORK_AROUND + privateData->NotUsingExtPhy=0; +#endif + } + } else { + SMSC_WARNING("No External Phy Detected"); + SMSC_WARNING(" using internal phy instead"); + goto USE_INTERNAL_PHY; + } + } else { + SMSC_WARNING("External Phy is not supported"); + SMSC_WARNING(" using internal phy instead"); + goto USE_INTERNAL_PHY; + };break; + default: + SMSC_WARNING("External Phy is not supported"); + SMSC_WARNING(" using internal phy instead"); + goto USE_INTERNAL_PHY; + } + } else { +USE_INTERNAL_PHY: + + privateData->dwPhyAddress=1; + privateData->ExtPhy=false; +#ifdef USE_LED1_WORK_AROUND + if(privateData->dwGeneration<=2) { + privateData->NotUsingExtPhy=1; + } else { + //Generation 3 or higher has the LED problem fixed + // to disable the workaround pretend the phy is external + privateData->NotUsingExtPhy=0; + } +#endif + + Phy_SetAutoMdixSts(privateData,AutoMdix); + } + + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + dwTemp=Phy_GetRegW(privateData,PHY_ID_2,keyCode); + privateData->bPhyRev=((BYTE)(dwTemp&(0x0FUL))); + privateData->bPhyModel=((BYTE)((dwTemp>>4)&(0x3FUL))); + privateData->dwPhyId=((dwTemp&(0xFC00UL))<<8); + dwTemp=Phy_GetRegW(privateData,PHY_ID_1,keyCode); + privateData->dwPhyId|=((dwTemp&(0x0000FFFFUL))<<2); + + SMSC_TRACE("dwPhyId==0x%08X,bPhyModel==0x%02X,bPhyRev==0x%02X", + privateData->dwPhyId, + privateData->bPhyModel, + privateData->bPhyRev); + + privateData->dwLinkSpeed=LINK_OFF; + privateData->dwLinkSettings=LINK_OFF; + //reset the PHY + Phy_SetRegW(privateData,PHY_BCR,PHY_BCR_RESET_,keyCode); + dwLoopCount=100000; + do { + + udelay(10); + wTemp=Phy_GetRegW(privateData,PHY_BCR,keyCode); + dwLoopCount--; + } while((dwLoopCount>0) && (wTemp&PHY_BCR_RESET_)); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + + if(wTemp&PHY_BCR_RESET_) { + SMSC_WARNING("PHY reset failed to complete."); + goto DONE; + } + +#ifdef USE_PHY_WORK_AROUND + if(privateData->dwGeneration<=2) { + // printk("phy_LoopBackTest\n"); + if(!Phy_LoopBackTest(privateData)) { + SMSC_WARNING("Failed Loop back test"); + goto DONE; + } else { + SMSC_TRACE("Passed Loop Back Test"); + } + } +#endif + + Phy_SetLink(privateData,dwLinkRequest); + + init_timer(&(privateData->LinkPollingTimer)); + privateData->LinkPollingTimer.function=Phy_CheckLink; + privateData->LinkPollingTimer.data=(unsigned long)privateData; + privateData->LinkPollingTimer.expires=jiffies+HZ; + add_timer(&(privateData->LinkPollingTimer)); + + result=true; +DONE: + SMSC_TRACE("<--Phy_Initialize, result=%s",result?"true":"false"); + return result; +} + +WORD Phy_GetRegW( + PPRIVATE_DATA privateData, + u32 dwRegIndex, + VL_KEY keyCode) +{ + u32 dwAddr=0; + int i=0; + WORD result=0xFFFFU; + + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode)); + + // confirm MII not busy + if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) != 0UL) + { + SMSC_WARNING("MII is busy in Phy_GetRegW???"); + result=0; + goto DONE; + } + + // set the address, index & direction (read from PHY) + dwAddr = ((privateData->dwPhyAddress&0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6); + Mac_SetRegDW(privateData, MII_ACC, dwAddr,keyCode); + + // wait for read to complete w/ timeout + for(i=0;i<100;i++) { + // see if MII is finished yet + if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) == 0UL) + { + // get the read data from the MAC & return i + result=((WORD)Mac_GetRegDW(privateData, MII_DATA,keyCode)); + goto DONE; + } + } + SMSC_WARNING("timeout waiting for MII write to finish"); + +DONE: + return result; +} + +void Phy_SetRegW( + PPRIVATE_DATA privateData, + u32 dwRegIndex,WORD wVal, + VL_KEY keyCode) +{ + u32 dwAddr=0; + int i=0; + + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + + SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode)); + + if(dwRegIndex==0) { + if((wVal&0x1200)==0x1200) { + privateData->wLastADVatRestart=privateData->wLastADV; + } + } + if(dwRegIndex==4) { + privateData->wLastADV=wVal; + } + + // confirm MII not busy + if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) != 0UL) + { + SMSC_WARNING("MII is busy in Phy_SetRegW???"); + goto DONE; + } + + // put the data to write in the MAC + Mac_SetRegDW(privateData, MII_DATA, (u32)wVal,keyCode); + + // set the address, index & direction (write to PHY) + dwAddr = ((privateData->dwPhyAddress&0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6) | MII_ACC_MII_WRITE_; + Mac_SetRegDW(privateData, MII_ACC, dwAddr,keyCode); + + // wait for write to complete w/ timeout + for(i=0;i<100;i++) { + // see if MII is finished yet + if ((Mac_GetRegDW(privateData, MII_ACC,keyCode) & MII_ACC_MII_BUSY_) == 0UL) + { + goto DONE; + } + } + SMSC_WARNING("timeout waiting for MII write to finish"); +DONE: + return; +} + +void Phy_UpdateLinkMode(PPRIVATE_DATA privateData) +{ + u32 dwOldLinkSpeed=privateData->dwLinkSpeed; + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + + Phy_GetLinkMode(privateData,keyCode); + + if(dwOldLinkSpeed!=(privateData->dwLinkSpeed)) { + if(privateData->dwLinkSpeed!=LINK_OFF) { + u32 dwRegVal=0; + switch(privateData->dwLinkSpeed) { + case LINK_SPEED_10HD: + SMSC_TRACE("Link is now UP at 10Mbps HD"); + break; + case LINK_SPEED_10FD: + SMSC_TRACE("Link is now UP at 10Mbps FD"); + break; + case LINK_SPEED_100HD: + SMSC_TRACE("Link is now UP at 100Mbps HD"); + break; + case LINK_SPEED_100FD: + SMSC_TRACE("Link is now UP at 100Mbps FD"); + break; + default: + SMSC_WARNING("Link is now UP at Unknown Link Speed, dwLinkSpeed=0x%08X", + privateData->dwLinkSpeed); + break; + } + + dwRegVal=Mac_GetRegDW(privateData,MAC_CR,keyCode); + dwRegVal&=~(MAC_CR_FDPX_|MAC_CR_RCVOWN_); + switch(privateData->dwLinkSpeed) { + case LINK_SPEED_10HD: + case LINK_SPEED_100HD: + dwRegVal|=MAC_CR_RCVOWN_; + break; + case LINK_SPEED_10FD: + case LINK_SPEED_100FD: + dwRegVal|=MAC_CR_FDPX_; + break; + default:break;//make lint happy + } + + Mac_SetRegDW(privateData, + MAC_CR,dwRegVal,keyCode); + + if(privateData->dwLinkSettings&LINK_AUTO_NEGOTIATE) { + WORD linkPartner=0; + WORD localLink=0; + localLink=Phy_GetRegW(privateData,4,keyCode); + linkPartner=Phy_GetRegW(privateData,5,keyCode); + switch(privateData->dwLinkSpeed) { + case LINK_SPEED_10FD: + case LINK_SPEED_100FD: + if(((localLink&linkPartner)&((WORD)0x0400U)) != ((WORD)0U)) { + //Enable PAUSE receive and transmit + Mac_SetRegDW(privateData,FLOW,0xFFFF0002UL,keyCode); + Lan_SetBitsDW(AFC_CFG,(afc_cfg&0x0000000FUL)); + } else if(((localLink&((WORD)0x0C00U))==((WORD)0x0C00U)) && + ((linkPartner&((WORD)0x0C00U))==((WORD)0x0800U))) + { + //Enable PAUSE receive, disable PAUSE transmit + Mac_SetRegDW(privateData,FLOW,0xFFFF0002UL,keyCode); + Lan_ClrBitsDW(AFC_CFG,0x0000000FUL); + } else { + //Disable PAUSE receive and transmit + Mac_SetRegDW(privateData,FLOW,0UL,keyCode); + Lan_ClrBitsDW(AFC_CFG,0x0000000FUL); + };break; + case LINK_SPEED_10HD: + case LINK_SPEED_100HD: + Mac_SetRegDW(privateData,FLOW,0UL,keyCode); + Lan_SetBitsDW(AFC_CFG,0x0000000FUL); + break; + default:break;//make lint happy + } + SMSC_TRACE("LAN9118: %s,%s,%s,%s,%s,%s", + (localLink&PHY_ANEG_ADV_ASYMP_)?"ASYMP":" ", + (localLink&PHY_ANEG_ADV_SYMP_)?"SYMP ":" ", + (localLink&PHY_ANEG_ADV_100F_)?"100FD":" ", + (localLink&PHY_ANEG_ADV_100H_)?"100HD":" ", + (localLink&PHY_ANEG_ADV_10F_)?"10FD ":" ", + (localLink&PHY_ANEG_ADV_10H_)?"10HD ":" "); + + SMSC_TRACE("Partner: %s,%s,%s,%s,%s,%s", + (linkPartner&PHY_ANEG_LPA_ASYMP_)?"ASYMP":" ", + (linkPartner&PHY_ANEG_LPA_SYMP_)?"SYMP ":" ", + (linkPartner&PHY_ANEG_LPA_100FDX_)?"100FD":" ", + (linkPartner&PHY_ANEG_LPA_100HDX_)?"100HD":" ", + (linkPartner&PHY_ANEG_LPA_10FDX_)?"10FD ":" ", + (linkPartner&PHY_ANEG_LPA_10HDX_)?"10HD ":" "); + } else { + switch(privateData->dwLinkSpeed) { + case LINK_SPEED_10HD: + case LINK_SPEED_100HD: + Mac_SetRegDW(privateData,FLOW,0x0UL,keyCode); + Lan_SetBitsDW(AFC_CFG,0x0000000FUL); + break; + default: + Mac_SetRegDW(privateData,FLOW,0x0UL,keyCode); + Lan_ClrBitsDW(AFC_CFG,0x0000000FUL); + break; + } + } + netif_carrier_on(privateData->dev); + Tx_WakeQueue(privateData,0x01); +#ifdef USE_LED1_WORK_AROUND + if ((g_GpioSettingOriginal & GPIO_CFG_LED1_EN_) && + privateData->NotUsingExtPhy) + { + // Restore orginal GPIO configuration + g_GpioSetting = g_GpioSettingOriginal; + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); + } +#endif // USE_LED1_WORK_AROUND + } else { + SMSC_TRACE("Link is now DOWN"); + Tx_StopQueue(privateData,0x01); + netif_carrier_off(privateData->dev); + Mac_SetRegDW(privateData,FLOW,0UL,keyCode); + Lan_ClrBitsDW(AFC_CFG,0x0000000FUL); +#ifdef USE_LED1_WORK_AROUND + // Check global setting that LED1 usage is 10/100 indicator + // g_GpioSetting = Lan_GetRegDW(GPIO_CFG); + if ((g_GpioSetting & GPIO_CFG_LED1_EN_) && + privateData->NotUsingExtPhy) + { + //Force 10/100 LED off, after saving orginal GPIO configuration + g_GpioSettingOriginal = g_GpioSetting; + + g_GpioSetting &= ~GPIO_CFG_LED1_EN_; + g_GpioSetting |= + (GPIO_CFG_GPIOBUF0_|GPIO_CFG_GPIODIR0_|GPIO_CFG_GPIOD0_); + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); + } +#endif // USE_LED1_WORK_AROUND + } + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); +} + +void Phy_CheckLink(unsigned long ptr) +{ + PPRIVATE_DATA privateData=(PPRIVATE_DATA)ptr; + if(privateData==NULL) { + SMSC_WARNING("Phy_CheckLink(ptr==0)"); + return; + } + + //must call this twice + Phy_UpdateLinkMode(privateData); + Phy_UpdateLinkMode(privateData); + + if(!(privateData->StopLinkPolling)) { + privateData->LinkPollingTimer.expires=jiffies+HZ; + add_timer(&(privateData->LinkPollingTimer)); + } +} + +void Phy_GetLinkMode( + PPRIVATE_DATA privateData, + VL_KEY keyCode) +{ + u32 result=LINK_OFF; + WORD wRegVal=0; + WORD wRegBSR=Phy_GetRegW( + privateData, + PHY_BSR,keyCode); + privateData->dwLinkSettings=LINK_OFF; + if(wRegBSR&PHY_BSR_LINK_STATUS_) { + wRegVal=Phy_GetRegW( + privateData, + PHY_BCR,keyCode); + if(wRegVal&PHY_BCR_AUTO_NEG_ENABLE_) { + u32 linkSettings=LINK_AUTO_NEGOTIATE; + WORD wRegADV=privateData->wLastADVatRestart; + // Phy_GetRegW( + // privateData, + // PHY_ANEG_ADV,keyCode); + WORD wRegLPA=Phy_GetRegW( + privateData, + PHY_ANEG_LPA,keyCode); + if(wRegADV&PHY_ANEG_ADV_ASYMP_) { + linkSettings|=LINK_ASYMMETRIC_PAUSE; + } + if(wRegADV&PHY_ANEG_ADV_SYMP_) { + linkSettings|=LINK_SYMMETRIC_PAUSE; + } + if(wRegADV&PHY_ANEG_LPA_100FDX_) { + linkSettings|=LINK_SPEED_100FD; + } + if(wRegADV&PHY_ANEG_LPA_100HDX_) { + linkSettings|=LINK_SPEED_100HD; + } + if(wRegADV&PHY_ANEG_LPA_10FDX_) { + linkSettings|=LINK_SPEED_10FD; + } + if(wRegADV&PHY_ANEG_LPA_10HDX_) { + linkSettings|=LINK_SPEED_10HD; + } + privateData->dwLinkSettings=linkSettings; + wRegLPA&=wRegADV; + if(wRegLPA&PHY_ANEG_LPA_100FDX_) { + result=LINK_SPEED_100FD; + } else if(wRegLPA&PHY_ANEG_LPA_100HDX_) { + result=LINK_SPEED_100HD; + } else if(wRegLPA&PHY_ANEG_LPA_10FDX_) { + result=LINK_SPEED_10FD; + } else if(wRegLPA&PHY_ANEG_LPA_10HDX_) { + result=LINK_SPEED_10HD; + } + } else { + if(wRegVal&PHY_BCR_SPEED_SELECT_) { + if(wRegVal&PHY_BCR_DUPLEX_MODE_) { + privateData->dwLinkSettings=result=LINK_SPEED_100FD; + } else { + privateData->dwLinkSettings=result=LINK_SPEED_100HD; + } + } else { + if(wRegVal&PHY_BCR_DUPLEX_MODE_) { + privateData->dwLinkSettings=result=LINK_SPEED_10FD; + } else { + privateData->dwLinkSettings=result=LINK_SPEED_10HD; + } + } + } + } + privateData->dwLinkSpeed=result; +} + +extern int smsc_prom_get_ethernet_mac_addr(char *addr); + +bool Mac_Initialize(PPRIVATE_DATA privateData) +{ + unsigned char ea[6]; + int result; + u32 dwHigh16, dwLow32; + unsigned long dwIntFlags = 0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + + SMSC_ASSERT(privateData!=NULL); +#ifdef CONFIG_MIPS_HMP10 + result = smsc_prom_get_ethernet_mac_addr(ea); +#else + result = prom_get_ethernet_addr(ea); +#endif + + if (result == 0) { + SMSC_TRACE("Got ethernet addr %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X from prom\n", + ea[0], ea[1], ea[2], ea[3], ea[4], ea[5] ); + dwHigh16 = (ea[5] << 8) | ea[4]; + Mac_SetRegDW(privateData, ADDRH, dwHigh16, keyCode); + dwLow32 = (ea[3] << 24) | (ea[2] << 16) | (ea[1] << 8) | ea[0]; + Mac_SetRegDW(privateData, ADDRL, dwLow32, keyCode); + } else { + SMSC_TRACE("Failed to get ethernet addr from prom\n"); + return result; + } + + return true; +} + +static bool MacNotBusy(PPRIVATE_DATA privateData, VL_KEY keyCode) +{ + int i=0; + SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode)); + // wait for MAC not busy, w/ timeout + for(i=0;i<40;i++) + { + if((Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)==(0UL)) { + return true; + } + } + SMSC_WARNING("timeout waiting for MAC not BUSY. MAC_CSR_CMD = 0x%08X", + Lan_GetRegDW(MAC_CSR_CMD)); + return false; +} + +u32 Mac_GetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,VL_KEY keyCode) +{ + u32 result=0xFFFFFFFFUL; + u32 dwTemp=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode)); + SMSC_ASSERT(privateData->dwLanBase!=0); + + // wait until not busy + if (Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_) + { + SMSC_WARNING("Mac_GetRegDW() failed, MAC already busy at entry"); + goto DONE; + } + + // send the MAC Cmd w/ offset + Lan_SetRegDW(MAC_CSR_CMD, + ((dwRegOffset & 0x000000FFUL) | + MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_)); + dwTemp=Lan_GetRegDW(BYTE_TEST);//to flush previous write + dwTemp=dwTemp; + + // wait for the read to happen, w/ timeout + if (!MacNotBusy(privateData,keyCode)) + { + SMSC_WARNING("Mac_GetRegDW() failed, waiting for MAC not busy after read"); + goto DONE; + } else { + // finally, return the read data + result=Lan_GetRegDW(MAC_CSR_DATA); + } +DONE: + return result; +} + +void Mac_SetRegDW(PPRIVATE_DATA privateData,u32 dwRegOffset,u32 dwVal,VL_KEY keyCode) +{ + u32 dwTemp=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),keyCode)); + SMSC_ASSERT(privateData->dwLanBase!=0); + + if (Lan_GetRegDW(MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_) + { + SMSC_WARNING("Mac_SetRegDW() failed, MAC already busy at entry"); + goto DONE; + } + + // send the data to write + Lan_SetRegDW(MAC_CSR_DATA,dwVal); + + // do the actual write + Lan_SetRegDW(MAC_CSR_CMD,((dwRegOffset & 0x000000FFUL) | MAC_CSR_CMD_CSR_BUSY_)); + dwTemp=Lan_GetRegDW(BYTE_TEST);//force flush of previous write + dwTemp=dwTemp; + + // wait for the write to complete, w/ timeout + if (!MacNotBusy(privateData,keyCode)) + { + SMSC_WARNING("Mac_SetRegDW() failed, waiting for MAC not busy after write"); + } +DONE: + return; +} + +#define TX_FIFO_LOW_THRESHOLD (1600) +//#define Tx_Max_Fragments (86) //every fragment needs 6 bytes overhead. 1514+2(alignment)+6*86=2032 < 2036 + +void Tx_Initialize( + PPRIVATE_DATA privateData, + u32 dwTxDmaCh, + u32 dwDmaThreshold) +{ + u32 dwRegVal=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dwLanBase!=0); + + dwRegVal=Lan_GetRegDW(HW_CFG); + dwRegVal&=(HW_CFG_TX_FIF_SZ_|0x00000FFFUL); + dwRegVal|=HW_CFG_SF_; + Lan_SetRegDW(HW_CFG,dwRegVal); + + + if(privateData->UseTxCsum) + + //Set TX COE + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + u32 dwCoeCr=Mac_GetRegDW(privateData,COE_CR,keyCode); + dwCoeCr|=(TX_COE_EN); + Mac_SetRegDW(privateData,COE_CR,dwCoeCr,keyCode); + //printk("COE_CR = 0x%08x\n", Mac_GetRegDW(privateData,COE_CR,keyCode)); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + + } + + + + Lan_SetTDFL(privateData,0xFF); + Lan_EnableInterrupt(privateData,INT_EN_TDFA_EN_); + + privateData->dwTxDmaThreshold=dwDmaThreshold; + privateData->dwTxDmaCh=dwTxDmaCh; + if(dwTxDmaCh>=TRANSFER_PIO) { + SMSC_TRACE("Tx will use PIO"); + } else { + SMSC_TRACE("Tx will use DMA channel %d",dwTxDmaCh); + SMSC_ASSERT(Platform_IsValidDmaChannel(dwTxDmaCh)); + if(!Platform_DmaInitialize( + &(privateData->PlatformData), + dwTxDmaCh)) + { + SMSC_WARNING("Failed Platform_DmaInitialize, dwTxDmaCh=%u",dwTxDmaCh); + } + privateData->TxDmaXfer.dwLanReg=privateData->dwLanBase+TX_DATA_FIFO; + privateData->TxDmaXfer.pdwBuf=NULL;//this will be reset per dma request + privateData->TxDmaXfer.dwDmaCh=privateData->dwTxDmaCh; + privateData->TxDmaXfer.dwDwCnt=0;//this will be reset per dma request + privateData->TxDmaXfer.fMemWr=false; + } + + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + u32 dwMacCr=Mac_GetRegDW(privateData,MAC_CR,keyCode); + dwMacCr|=(MAC_CR_TXEN_|MAC_CR_HBDIS_); + Mac_SetRegDW(privateData,MAC_CR,dwMacCr,keyCode); + Lan_SetRegDW(TX_CFG,TX_CFG_TX_ON_); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + + privateData->TxSkb=NULL; + spin_lock_init(&(privateData->TxSkbLock)); + privateData->dwTxQueueDisableMask=0; + spin_lock_init(&(privateData->TxQueueLock)); + spin_lock_init(&(privateData->TxCounterLock)); + privateData->TxInitialized=true; + +} + +bool Tx_HandleInterrupt( + PPRIVATE_DATA privateData,u32 dwIntSts) +{ + SMSC_ASSERT(privateData!=NULL); + if(dwIntSts&INT_STS_TDFA_) + { + Lan_SetTDFL(privateData,0xFF); + Lan_SetRegDW(INT_STS,INT_STS_TDFA_); + Tx_WakeQueue(privateData,0x02UL); + return true; + } + return false; +} + +void Tx_StopQueue( + PPRIVATE_DATA privateData,u32 dwSource) +{ + unsigned long intFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dev!=NULL); + SMSC_ASSERT(privateData->TxInitialized); + spin_lock_irqsave(&(privateData->TxQueueLock),intFlags); + if(privateData->dwTxQueueDisableMask==0) { + netif_stop_queue(privateData->dev); + } + privateData->dwTxQueueDisableMask|=dwSource; + spin_unlock_irqrestore(&(privateData->TxQueueLock),intFlags); +} + +void Tx_WakeQueue( + PPRIVATE_DATA privateData,u32 dwSource) +{ + unsigned long intFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dev!=NULL); + SMSC_ASSERT(privateData->TxInitialized); + spin_lock_irqsave(&(privateData->TxQueueLock),intFlags); + privateData->dwTxQueueDisableMask&=(~dwSource); + if(privateData->dwTxQueueDisableMask==0) { + netif_wake_queue(privateData->dev); + } + spin_unlock_irqrestore(&(privateData->TxQueueLock),intFlags); +} + +static u32 Tx_GetTxStatusCount( + PPRIVATE_DATA privateData) +{ + u32 result=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dwLanBase!=0); + result=Lan_GetRegDW(TX_FIFO_INF); + if(OLD_REGISTERS(privateData)) { + result&=TX_FIFO_INF_TSFREE_; + result>>=16; + if(result>0x80) { + SMSC_WARNING("TX_FIFO_INF_TSFREE_>0x80"); + result=0x80; + } + result=0x80-result; + } else { + result&=TX_FIFO_INF_TSUSED_; + result>>=16; + } + return result; +} + + + +void Tx_SendSkb( + PPRIVATE_DATA privateData, + struct sk_buff *skb) +{ + u32 dwFreeSpace=0; + unsigned int i=0, TxFrag=0, skbFragCnt = skb_shinfo(skb)->nr_frags + 1; + + //if (skbFragCnt>1) + // printk("skbFrsgCnt = (%d)\n", skbFragCnt); + + // if (privateData->UseTxCsum) { + int Chsum_start_offset=0; + u32 dwTxCsumPreamble=0; + // } + + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dwLanBase!=0); + + if(privateData->UseTxCsum){ + + if(skb->ip_summed == CHECKSUM_PARTIAL) + { + //printk("ip summed!\n"); + TxFrag = skbFragCnt + 1; + } + else + { + TxFrag = skbFragCnt; + } + } + else { + TxFrag = skbFragCnt; + } + TxFrag = skbFragCnt; + + + if (privateData->UseTxCsum) { + + if (skb->ip_summed == CHECKSUM_PARTIAL) + { + CalculateTxChecksumOffset( + skb, + &Chsum_start_offset); + + + dwTxCsumPreamble=(((WORD) (Chsum_start_offset + skb->csum)) << 16) | ((WORD) Chsum_start_offset); + + + } + } + + //printk("Tx skb->len = 0x%08x\n", (u32) skb->len); + + + + if(privateData->dwTxDmaCh>=TRANSFER_PIO) + { + //Use PIO only + + //printk("Tx using pio only\n"); + + u32 dwTxCmdA=0; + u32 dwTxCmdB=0; + + + dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF); + dwFreeSpace&=TX_FIFO_INF_TDFREE_; + if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) { + SMSC_WARNING("Tx Data Fifo Low, space available = %d",dwFreeSpace); + } + + + if(privateData->UseTxCsum) { + if(skb->ip_summed == CHECKSUM_PARTIAL) + { + + + dwTxCmdA=TX_CMD_A_INT_FIRST_SEG_ |((u32)sizeof(u32)) ; + + dwTxCmdB= + (((u32)(skb->len+4))<<16) | TX_CMD_B_CSUM_ENABLE | + (((u32)(skb->len+4)&0x7FFUL)); + + + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCsumPreamble); + + + } + } + + if (skbFragCnt == 1){ + + if(skb->ip_summed == CHECKSUM_PARTIAL){ + + dwTxCmdA =((((u32)(skb->data))&0x03UL)<<16) | + TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len)); + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + + + + } + else{ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) | TX_CMD_A_INT_FIRST_SEG_ | + TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len)); + + dwTxCmdB= + (((u32)(skb->len))<<16) | + (((u32)(skb->len)&0x7FFUL)); + + } + + //printk("dwTxCmdA = 0x%08x\n", (u32) dwTxCmdA); + //printk("dwTxCmdB = 0x%08x\n", (u32) dwTxCmdB); + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + + + // rkdump(skb->data, skb->len); + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL), + (((u32)(skb->len))+3+ + (((u32)(skb->data))&0x03UL))>>2); + + //printk("Tx skb->len2 = 0x%08x\n", (u32) skb->len); + dwFreeSpace-=(skb->len+32); + dev_kfree_skb(skb); + + } + + else { + + if(skb->ip_summed == CHECKSUM_PARTIAL){ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment + ((u32)((skb->len)-(skb->data_len))); + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + + } + else{ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) |TX_CMD_A_INT_FIRST_SEG_ | + ((u32)((skb->len)-(skb->data_len))); + dwTxCmdB= + (((u32)(skb->len))<<16) | + (((u32)(skb->len)&0x7FFUL)); + + } + //printk("first frag. \n"); + //rkdump(skb->data, ((skb->len)-(skb->data_len))); + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + + + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL), + (((u32)((skb->len)-(skb->data_len)))+3+ + (((u32)(skb->data))&0x03UL))>>2); + // dwFreeSpace-=((skb->len-skb->data_len)+32); + + + for(i=1;i<skbFragCnt;i++) + { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1]; + void *frag_addr = page_address(frag->page) + frag->page_offset; + + + dwTxCmdA= + ((((u32)(frag_addr))&0x03UL)<<16) | //u32 alignment adjustment + ((u32)(frag->size)); + + + if (i==(skbFragCnt-1)){ + dwTxCmdA |= TX_CMD_A_INT_LAST_SEG_ ; + } + + if(skb->ip_summed == CHECKSUM_PARTIAL) { + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + } + else { + dwTxCmdB= + (((u32)(skb->len))<<16) | + (((u32)(skb->len)&0x7FFUL)); + } + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + //printk("i= %d\n", i); + //rkdump(frag_addr, frag->size); + + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(frag_addr))&0xFFFFFFFCUL), + (((u32)(frag->size))+3+ + (((u32)(frag_addr))&0x03UL))>>2); + // dwFreeSpace-=(frag->size+8); + + } + + dwFreeSpace-=skb->len+12*TxFrag+16; + dev_kfree_skb(skb); + } + + } + + + else + { + //Use DMA and PIO + + //printk("Tx using dma!!\n"); + + u32 dwDmaCh=privateData->dwTxDmaCh; + PPLATFORM_DATA platformData=&(privateData->PlatformData); + SMSC_ASSERT(TX_FIFO_LOW_THRESHOLD>(skb->len+32)); + + if(((skb->len)>=(privateData->dwTxDmaThreshold)) && (skbFragCnt == 1)) + { + + //printk("Tx using dma!!\n"); + // if(privateData->UseTxCsum) { + u32 dwTxCmdA1=0; + u32 dwTxCmdB1=0; + // } + u32 dwTxCmdA=0; + u32 dwTxCmdB=0; + + + if(privateData->UseTxCsum) { + if(skb->ip_summed == CHECKSUM_PARTIAL) + + { + + + dwTxCmdA1= + TX_CMD_A_INT_FIRST_SEG_ | + ((u32)sizeof(u32));//buffer length + + dwTxCmdB1= + (((u32)(skb->len+4))<<16) |TX_CMD_B_CSUM_ENABLE | + (((u32)(skb->len+4)&0x7FFUL)); + + + } + } + + if (skbFragCnt == 1){ + + dwTxCmdA = +#if (PLATFORM_CACHE_LINE_BYTES == 16) + (0x01UL<<24)|//16 byte end alignment +#endif +#if (PLATFORM_CACHE_LINE_BYTES == 32) + (0x02UL<<24)|//32 byte end alignment +#endif + ((((u32)(skb->data))&(PLATFORM_CACHE_LINE_BYTES-1))<<16) |//16 Byte start alignment + TX_CMD_A_INT_LAST_SEG_ | + ((u32)(skb->len));//buffer length + + if(skb->ip_summed != CHECKSUM_PARTIAL) + dwTxCmdA |= TX_CMD_A_INT_FIRST_SEG_; + + if (skb->ip_summed == CHECKSUM_PARTIAL){ + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + } + else{ + dwTxCmdB= + (((u32)(skb->len))<<16) | + ((u32)(skb->len)); + } + + + + privateData->TxDmaXfer.pdwBuf= + (u32 *)(((u32)(skb->data))& + (~(PLATFORM_CACHE_LINE_BYTES-1))); + privateData->TxDmaXfer.dwDwCnt= + ((((u32)(skb->len))+ + (PLATFORM_CACHE_LINE_BYTES-1)+ + (((u32)(skb->data))& + (PLATFORM_CACHE_LINE_BYTES-1)))& + (~(PLATFORM_CACHE_LINE_BYTES-1)))>>2; + Platform_CachePurge( + platformData, + privateData->TxDmaXfer.pdwBuf, + (privateData->TxDmaXfer.dwDwCnt)<<2); + + spin_lock(&(privateData->TxSkbLock)); + { + if(privateData->TxSkb) + Platform_DmaComplete(platformData,dwDmaCh); + + dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF); + dwFreeSpace&=TX_FIFO_INF_TDFREE_; + if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) { + SMSC_WARNING("Tx DATA FIFO LOW, space available = %d",dwFreeSpace); + } + + if (privateData->UseTxCsum) { + if(skb->ip_summed == CHECKSUM_PARTIAL) + + { + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA1); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB1); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCsumPreamble); + } + } + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + if(!Platform_DmaStartXfer(platformData,&(privateData->TxDmaXfer))) + { + SMSC_WARNING("Failed Platform_DmaStartXfer"); + } + + dwFreeSpace-=(skb->len+32); + if(privateData->TxSkb) + dev_kfree_skb(privateData->TxSkb); + + privateData->TxSkb=skb; + } + spin_unlock(&(privateData->TxSkbLock)); + + } + + + + } + + else + { + + + //Use PIO + + //printk("skb->len (%d)\n", skb->len); + //printk("Tx using pio\n"); + + u32 dwTxCmdA=0; + u32 dwTxCmdB=0; + + + spin_lock(&(privateData->TxSkbLock)); + if(privateData->TxSkb) { + Platform_DmaComplete(platformData,dwDmaCh); + dev_kfree_skb(privateData->TxSkb); + privateData->TxSkb=NULL; + } + spin_unlock(&(privateData->TxSkbLock)); + + + dwFreeSpace=Lan_GetRegDW(TX_FIFO_INF); + dwFreeSpace&=TX_FIFO_INF_TDFREE_; + if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) { + SMSC_WARNING("Tx Data Fifo Low, space available = %d",dwFreeSpace); + } + + if(privateData->UseTxCsum) { + + if(skb->ip_summed == CHECKSUM_PARTIAL) + { + + dwTxCmdA=TX_CMD_A_INT_FIRST_SEG_ |((u32)sizeof(u32)) ; + + + dwTxCmdB= + (((u32)(skb->len+4))<<16) |TX_CMD_B_CSUM_ENABLE | + (((u32)(skb->len+4)&0x7FFUL)); + + + //printk("dwTxCmdA = 0x%08x\n", (u32) dwTxCmdA); + //printk("dwTxCmdB = 0x%08x\n", (u32) dwTxCmdB); + //printk("dwTxCsumPreamble = 0x%08x\n", (u32) dwTxCsumPreamble); + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCsumPreamble); + + } + + } + + if (skbFragCnt == 1){ + + if(skb->ip_summed == CHECKSUM_PARTIAL){ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment + TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len)); + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + + } + else{ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) | TX_CMD_A_INT_FIRST_SEG_ | + TX_CMD_A_INT_LAST_SEG_|((u32)(skb->len)); + dwTxCmdB= + (((u32)(skb->len))<<16) | + ((u32)(skb->len)); + + } + + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL), + (((u32)(skb->len))+3+ + (((u32)(skb->data))&0x03UL))>>2); + dwFreeSpace-=(skb->len+32); + dev_kfree_skb(skb); + } + + else { + + if(skb->ip_summed == CHECKSUM_PARTIAL){ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment + ((u32)((skb->len)-(skb->data_len))); + + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + } + else{ + dwTxCmdA = + ((((u32)(skb->data))&0x03UL)<<16) |TX_CMD_A_INT_FIRST_SEG_ | //u32 alignment adjustment + ((u32)((skb->len)-(skb->data_len))); + + dwTxCmdB= + (((u32)(skb->len))<<16) | + (((u32)(skb->len)&0x7FFUL)); + + } + + //printk("first frag. \n"); + //rkdump(skb->data, ((skb->len)-(skb->data_len))); + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(skb->data))&0xFFFFFFFCUL), + (((u32)((skb->len)-(skb->data_len)))+3+ + (((u32)(skb->data))&0x03UL))>>2); + // dwFreeSpace-=((skb->len-skb->data_len)+32); + + + for(i=1;i<skbFragCnt;i++) + { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1]; + void *frag_addr = page_address(frag->page) + frag->page_offset; + + + dwTxCmdA= + ((((u32)(frag_addr))&0x03UL)<<16) | //u32 alignment adjustment + ((u32)(frag->size)); + + + if (i==(skbFragCnt-1)){ + dwTxCmdA |= TX_CMD_A_INT_LAST_SEG_ ; + } + + if (skb->ip_summed == CHECKSUM_PARTIAL){ + dwTxCmdB= + (((u32)(skb->len+4))<<16) | + (((u32)(skb->len+4)&0x7FFUL)); + } + else{ + dwTxCmdB= + (((u32)(skb->len))<<16) | + ((u32)(skb->len)); + } + + //printk("i= %d\n", i); + //rkdump(frag_addr, frag->size); + + + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdA); + Lan_SetRegDW(TX_DATA_FIFO,dwTxCmdB); + + Platform_WriteFifo( + privateData->dwLanBase, + (u32 *)(((u32)(frag_addr))&0xFFFFFFFCUL), + (((u32)(frag->size))+3+ + (((u32)(frag_addr))&0x03UL))>>2); + // dwFreeSpace-=(frag->size+8); + + } + + dwFreeSpace-=skb->len+12*TxFrag+16; + dev_kfree_skb(skb); + + } + + } + + } + + //printk("finish.\n"); + + + if(Tx_GetTxStatusCount(privateData)>=30) + { + Tx_UpdateTxCounters(privateData); + } + + if(dwFreeSpace<TX_FIFO_LOW_THRESHOLD) { + Tx_StopQueue(privateData,0x02UL); + Lan_SetTDFL(privateData,0x32); + } + + +} + + + + + + + + + +static u32 Tx_CompleteTx( + PPRIVATE_DATA privateData) +{ + u32 result=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dwLanBase!=0); + SMSC_ASSERT(privateData->TxInitialized==true); + result=Lan_GetRegDW(TX_FIFO_INF); + if(OLD_REGISTERS(privateData)) { + result&=TX_FIFO_INF_TSFREE_; + if(result!=0x00800000UL) { + result=Lan_GetRegDW(TX_STATUS_FIFO); + } else { + result=0; + } + } else { + result&=TX_FIFO_INF_TSUSED_; + if(result!=0x00000000UL) { + result=Lan_GetRegDW(TX_STATUS_FIFO); + } else { + result=0; + } + } + return result; +} + +void Tx_UpdateTxCounters( + PPRIVATE_DATA privateData) +{ + + u32 dwTxStatus=0; + SMSC_ASSERT(privateData!=NULL); + spin_lock(&(privateData->TxCounterLock)); + while((dwTxStatus=Tx_CompleteTx(privateData))!=0) + { + if(dwTxStatus&0x80000000UL) { + SMSC_WARNING("Packet tag reserved bit is high"); + privateData->stats.tx_errors++; + } else if(dwTxStatus&0x00007080UL) { + SMSC_WARNING("Tx Status reserved bits are high"); + privateData->stats.tx_errors++; + } else { + if(dwTxStatus&0x00008000UL) { + privateData->stats.tx_errors++; + } else { + privateData->stats.tx_packets++; + privateData->stats.tx_bytes+=(dwTxStatus>>16); + } + if(dwTxStatus&0x00000100UL) { + privateData->stats.collisions+=16; + privateData->stats.tx_aborted_errors+=1; + } else { + privateData->stats.collisions+= + ((dwTxStatus>>3)&0xFUL); + } + if(dwTxStatus&0x00000800UL) { + privateData->stats.tx_carrier_errors+=1; + } + if(dwTxStatus&0x00000200UL) { + privateData->stats.collisions++; + privateData->stats.tx_aborted_errors++; + } + } + } + spin_unlock(&(privateData->TxCounterLock)); +} + +void Tx_CompleteDma( + PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + + spin_lock(&(privateData->TxSkbLock)); + if(privateData->TxSkb) { + Platform_DmaComplete( + &(privateData->PlatformData), + privateData->dwTxDmaCh); + dev_kfree_skb(privateData->TxSkb); + privateData->TxSkb=NULL; + } + spin_unlock(&(privateData->TxSkbLock)); +} + + +void CalculateTxChecksumOffset( + struct sk_buff *skb, + int *csum_start_offset + ) +{ + unsigned int skbFragCnt; + + skbFragCnt = skb_shinfo(skb)->nr_frags + 1; + *csum_start_offset = skb->csum_offset; +} + + + + +void rkdump(unsigned char *p, unsigned short len) +{ + int i; + + for(i=0; i<len; i++) + { + if (i%16 == 0) + { + printk("\n0x%08x: ", (u32) (p+i)); + } + + printk("%02x ", *(p+i)); + } + + printk("\n"); +} + + + +void Rx_Initialize( + PPRIVATE_DATA privateData, + u32 dwRxDmaCh, + u32 dwDmaThreshold) +{ + SMSC_ASSERT(privateData!=NULL); + + privateData->dwRxDmaCh=dwRxDmaCh; + if(dwRxDmaCh>=TRANSFER_PIO) { + SMSC_TRACE("Rx will use PIO"); + Platform_GetFlowControlParameters( + &(privateData->PlatformData), + &(privateData->RxFlowParameters), + false); + } else { + SMSC_TRACE("Rx will use DMA Channel %d",dwRxDmaCh); + SMSC_ASSERT(Platform_IsValidDmaChannel(dwRxDmaCh)); + if(!Platform_DmaInitialize( + &(privateData->PlatformData), + dwRxDmaCh)) + { + SMSC_WARNING("Failed Platform_DmaInitialize, dwRxDmaCh=%u",dwRxDmaCh); + } + Platform_GetFlowControlParameters( + &(privateData->PlatformData), + &(privateData->RxFlowParameters), + true); + } + if(max_throughput!=0xFFFFFFFFUL) { + privateData->RxFlowParameters.MaxThroughput=max_throughput; + } + if(max_packet_count!=0xFFFFFFFFUL) { + privateData->RxFlowParameters.MaxPacketCount=max_packet_count; + } + if(packet_cost!=0xFFFFFFFFUL) { + privateData->RxFlowParameters.PacketCost=packet_cost; + } + if(burst_period!=0xFFFFFFFFUL) { + privateData->RxFlowParameters.BurstPeriod=burst_period; + } + if(privateData->RxFlowParameters.BurstPeriod==0) { + SMSC_WARNING("burst_period of 0 is not allowed"); + SMSC_WARNING(" resetting burst_period to 100"); + privateData->RxFlowParameters.BurstPeriod=100; + } + if(max_work_load!=0xFFFFFFFFUL) { + privateData->RxFlowMaxWorkLoad=max_work_load; + } else { + privateData->RxFlowMaxWorkLoad= + privateData->RxFlowParameters.MaxThroughput+ + (privateData->RxFlowParameters.MaxPacketCount* + privateData->RxFlowParameters.PacketCost); + } + privateData->RxFlowBurstMaxWorkLoad= + (privateData->RxFlowMaxWorkLoad* + privateData->RxFlowParameters.BurstPeriod)/1000; + if(int_deas!=0xFFFFFFFFUL) { + Lan_SetIntDeas(privateData,int_deas); + } else { + Lan_SetIntDeas(privateData,privateData->RxFlowParameters.IntDeas); + } + + + //Set RX COE + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + Mac_SetRegDW(privateData,VLAN1,ETH_P_8021Q,keyCode); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + // privateData->RxVLanPkt=true; + + } + + + if (privateData->UseRxCsum) { + + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + u32 dwCoeCr=Mac_GetRegDW(privateData,COE_CR,keyCode); + dwCoeCr|=(RX_COE_EN | RX_COE_MODE); + Mac_SetRegDW(privateData,COE_CR,dwCoeCr,keyCode); + //printk("COE_CR2 = 0x%08x\n", Mac_GetRegDW(privateData,COE_CR,keyCode)); + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + + } + + + } + + + + //initially the receiver is off + // a following link up detection will turn the receiver on + privateData->dwRxOffCount=1; + Lan_SetRegDW(RX_CFG,RX_CFG_RXDOFF_2_); + Rx_ReceiverOn(privateData, 0); + + privateData->dwRxDmaThreshold=dwDmaThreshold; + Lan_SetRDFL(privateData,0x01); + Lan_SetRSFL(privateData,0x00); + privateData->RxInterrupts=INT_EN_RSFL_EN_; + privateData->RxInterrupts|=INT_EN_RXE_EN_; + if(privateData->dwGeneration==0) { + privateData->RxInterrupts|=INT_EN_RDFL_EN_; + } else { + privateData->RxInterrupts|=INT_EN_RDFO_EN_; + } + privateData->RxInterrupts|=INT_EN_RXDFH_INT_EN_; + Lan_EnableInterrupt(privateData,privateData->RxInterrupts); + +} + +static void Rx_HandleOverrun(PPRIVATE_DATA privateData) +{ + if(privateData->dwGeneration==0) { + if(privateData->RxOverrun==false) { + Rx_ReceiverOff(privateData); + privateData->RxUnloadProgress= + (((((privateData->LastRxStatus1)&0x3FFF0000UL)>>16)+2+3)&0xFFFFFFFCUL); + if(privateData->dwRxDmaCh<TRANSFER_REQUEST_DMA) { + privateData->RxUnloadProgress+= + (((((privateData->LastRxStatus2)&0x3FFF0000UL)>>16)+2+3)&0xFFFFFFFCUL); + } + privateData->RxUnloadPacketProgress=0; + privateData->RxOverrun=true; + privateData->RxOverrunCount++; + } + } else { + privateData->RxOverrunCount++; + } +} + +static void Rx_HandOffSkb( + PPRIVATE_DATA privateData, + struct sk_buff *skb) +{ + int result=0; + + skb->dev=privateData->dev; + skb->protocol= eth_type_trans(skb,privateData->dev); + + // #ifdef UseRxCsum + // WORD wHwCsum = 0; + // #endif + + //printk(" Handoff skb->len = 0x%08x\n",(u32) skb->len); + if (privateData->UseRxCsum) { + + WORD wHwCsum = *(WORD *)(skb->tail +4); + skb->csum = wHwCsum; + //printk(" HW csum = 0x%04x\n", skb->csum); + + } + + + if (privateData->UseRxCsum) + + skb->ip_summed = CHECKSUM_PARTIAL; + + else + skb->ip_summed = CHECKSUM_NONE; +#ifdef LINUX_2_6_OR_NEWER + if(rx_mode==PROCESSING_MODE_NAPI) { + result=netif_receive_skb(skb); + privateData->RxWorkLimit--; + privateData->RxPacketsReceived++; + if(privateData->RxWorkLimit<=0) { + privateData->RxCongested=true; + } + + } else { + result=netif_rx(skb);//hand off the Received packet to higher layer + } +#else + result=netif_rx(skb); +#endif + + // result=netif_rx(skb); + + switch(result) + { + case NET_RX_SUCCESS: + break; + case NET_RX_CN_LOW: + case NET_RX_CN_MOD: + case NET_RX_CN_HIGH: + case NET_RX_DROP: + privateData->RxCongested=true; + privateData->RxCongestedCount++; + break; + default: + privateData->RxCongested=true; + privateData->RxCongestedCount++; + SMSC_WARNING("Unknown return value from netif_rx, result=%d",result); + break; + } +} + +void Rx_CompleteMulticastUpdate (PPRIVATE_DATA privateData) +{ + u32 local_MACCR; + VL_KEY keyCode=0; + unsigned long dwIntFlags=0; + + keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if (privateData->MulticastUpdatePending) { + SET_GPIO(GP_COMPLETE_MULTICAST_UPDATE); + Mac_SetRegDW(privateData,HASHH,privateData->HashHi,keyCode); + Mac_SetRegDW(privateData,HASHL,privateData->HashLo,keyCode); + local_MACCR = Mac_GetRegDW(privateData,MAC_CR,keyCode); + local_MACCR |= privateData->set_bits_mask; + local_MACCR &= ~(privateData->clear_bits_mask); + Mac_SetRegDW(privateData,MAC_CR,local_MACCR,keyCode); + Rx_ReceiverOn(privateData, keyCode); + privateData->MulticastUpdatePending = false; + CLEAR_GPIO(GP_COMPLETE_MULTICAST_UPDATE); + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); +} + +void Rx_BeginMulticastUpdate (PPRIVATE_DATA privateData) +{ + u32 startTime, currentTime; + u32 timeout; + unsigned long flags; + + SET_GPIO(GP_BEGIN_MULTICAST_UPDATE); + + //NOTE: we can't rely on privateData->dwLinkSpeed because + // it updates only once per second and may be out dated. + + local_irq_save(flags); + Rx_ReceiverOff(privateData); + if(privateData->dwGeneration>0) { + //since this is concord or later there is no + // overrun processing that might turn off the receiver. + // there for we can rely on RxStop Int. + + //if the speed is 100Mb then lets poll rx stop to get the + // quickest response. + timeout = 200UL; + while ((timeout)&&(!(Lan_GetRegDW(INT_STS)&(INT_STS_RXSTOP_INT_)))) { + // wait 1 uSec + startTime=Lan_GetRegDW(FREE_RUN); + while (1) { + currentTime=Lan_GetRegDW(FREE_RUN); + if (currentTime-startTime >= 25UL) + break; + } + timeout--; + } + if(timeout==0) { + //this is probably a 10Mb link, therefore prepare + // interrupt for update later. + Lan_EnableInterrupt(privateData,INT_EN_RXSTOP_INT_EN_); + + // if this is a 10Mbps half duplex connection + // then Rx stop is only 99.6% reliable + // Therefor we must schedule Gpt callback as + // back up + + // using 18*(100uS) because we already waited 200uS + Gpt_ScheduleCallBack(privateData,GptCB_RxCompleteMulticast, 18UL); + } else { + //Rx is stopped + Lan_SetRegDW(INT_STS,INT_STS_RXSTOP_INT_);//clear interrupt signal + Rx_CompleteMulticastUpdate(privateData); + } + } else { + // for generation 0 we can't rely on Rx stop because + // the receiver may have already been stopped due to + // overflow processing + + // for the same reason we can't just wait 200uS and + // check stopped status there for we must rely on GP timer + // and we must assume a worse case of 10Mb speed + + Gpt_ScheduleCallBack(privateData,GptCB_RxCompleteMulticast, 20UL); + } + local_irq_restore (flags); + CLEAR_GPIO(GP_BEGIN_MULTICAST_UPDATE); +} + +static u32 Rx_PopRxStatus( + PPRIVATE_DATA privateData) +{ + u32 result=Lan_GetRegDW(RX_FIFO_INF); + if((privateData->RxCongested==false)|| + ((privateData->RxCongested==true)&&((result&0x00FF0000UL)==0UL))) + { + if(result&0x00FF0000UL) { + u32 dwIntSts=Lan_GetRegDW(INT_STS); + if(privateData->dwGeneration==0) { + if(dwIntSts&INT_STS_RDFL_) { + Lan_SetRegDW(INT_STS,INT_STS_RDFL_); + Rx_HandleOverrun(privateData); + } + } else { + if(dwIntSts&INT_STS_RDFO_) { + Lan_SetRegDW(INT_STS,INT_STS_RDFO_); + Rx_HandleOverrun(privateData); + } + } + if((privateData->RxFlowControlActive==false)|| + ((privateData->RxFlowControlActive==true)&& + (privateData->RxFlowBurstActive==true))) + { + //Rx status is available, read it + result=Lan_GetRegDW(RX_STATUS_FIFO); + privateData->RxStatusDWReadCount++; + privateData->LastRxStatus3= + privateData->LastRxStatus2; + privateData->LastRxStatus2= + privateData->LastRxStatus1; + privateData->LastRxStatus1=result; + + if(privateData->RxOverrun) { + u32 dwPacketLength=((result&0x3FFF0000UL)>>16); + u32 dwByteCount=((dwPacketLength+2+3)&0xFFFFFFFCUL); + if((privateData->RxUnloadProgress+dwByteCount)>= + ((privateData->RxMaxDataFifoSize)-16)) + { + //This is the packet that crosses the corruption point + // so just ignore it and complete the overrun processing. + result=0; + goto FINISH_OVERRUN_PROCESSING; + } + privateData->RxUnloadProgress+=dwByteCount; + privateData->RxUnloadPacketProgress++; + } + + privateData->RxFlowCurrentThroughput+= + ((((result&0x3FFF0000UL)>>16)-4UL)); + privateData->RxFlowCurrentPacketCount++; + privateData->RxFlowCurrentWorkLoad+= + ((((result&0x3FFF0000UL)>>16)-4UL)+privateData->RxFlowParameters.PacketCost); + if(privateData->RxFlowControlActive) { + privateData->RxFlowBurstWorkLoad+= + ((((result&0x3FFF0000UL)>>16)-4UL)+privateData->RxFlowParameters.PacketCost); + if(privateData->RxFlowBurstWorkLoad>= + privateData->RxFlowBurstMaxWorkLoad) + { + privateData->RxFlowBurstActive=false; + Lan_DisableInterrupt(privateData,privateData->RxInterrupts); + } + } + } else { + result=0; + } + } + else + { + if(privateData->RxOverrun) { + u32 timeOut; + u32 temp; +FINISH_OVERRUN_PROCESSING: + temp=0; + { + timeOut=2000; + while((timeOut>0)&&(!(Lan_GetRegDW(INT_STS)&(INT_STS_RXSTOP_INT_)))) { + udelay(1); + timeOut--; + } + if(timeOut==0) { + // privateData->RxStopTimeOutCount++; + // PULSE_GPIO(GP_TX,1); + SMSC_WARNING("Timed out waiting for Rx to Stop\n"); + } + Lan_SetRegDW(INT_STS,INT_STS_RXSTOP_INT_); + } + + if(privateData->dwRxDmaCh<TRANSFER_REQUEST_DMA) { + //make sure DMA has stopped before doing RX Dump + if(privateData->RxSkb) { + Platform_DmaComplete( + &(privateData->PlatformData), + privateData->dwRxDmaCh); + + Rx_HandOffSkb(privateData,privateData->RxSkb); + privateData->RxSkb=NULL; + } + } + + temp=Lan_GetRegDW(RX_CFG); + Lan_SetRegDW(RX_CFG,(temp&0x3FFFFFFFUL)); + timeOut=10000000; + Lan_SetBitsDW(RX_CFG,RX_CFG_RX_DUMP_); + while((timeOut>0)&&(Lan_GetRegDW(RX_CFG)&(RX_CFG_RX_DUMP_))) { + udelay(1); + timeOut--; + } + if(timeOut==0) { + SMSC_WARNING("Timed out waiting for Rx Dump to complete\n"); + } + Lan_SetRegDW(RX_CFG,temp); + + privateData->RxDumpCount++; + Lan_SetRegDW(INT_STS,INT_STS_RDFL_); + Rx_ReceiverOn(privateData, 0); + privateData->RxOverrun=false; + } + result=0; + privateData->LastReasonForReleasingCPU=1;//Status FIFO Empty + } + } else { + //disable and reenable the INT_EN + // This will allow the deassertion interval to begin + u32 temp=Lan_GetRegDW(INT_EN); + Lan_SetRegDW(INT_EN,0); + Lan_SetRegDW(INT_EN,temp); + result=0; + privateData->LastReasonForReleasingCPU=2;//High Congestion + } + return result; +} + +void Rx_CountErrors(PPRIVATE_DATA privateData,u32 dwRxStatus) +{ + bool crcError=false; + if(dwRxStatus&0x00008000UL) { + privateData->stats.rx_errors++; + if(dwRxStatus&0x00000002UL) { + privateData->stats.rx_crc_errors++; + crcError=true; + } + } + if(!crcError) { + if((dwRxStatus&0x00001020UL)==0x00001020UL) { + //Frame type indicates length, and length error is set + privateData->stats.rx_length_errors++; + } + if(dwRxStatus&RX_STS_MCAST_) { + privateData->stats.multicast++; + } + } +} + +void Rx_FastForward(PPRIVATE_DATA privateData,u32 dwDwordCount) +{ + privateData->RxFastForwardCount++; + if((dwDwordCount>=4) + && ( + (((privateData->dwIdRev&0x0000FFFFUL)==0x00000000UL) + && (privateData->dwFpgaRev>=0x36)) + || + ((privateData->dwIdRev&0x0000FFFFUL)!=0UL) + ) + ) + { + u32 dwTimeOut=500; + Lan_SetRegDW(RX_DP_CTRL,(dwDwordCount|RX_DP_CTRL_FFWD_BUSY_)); + while((dwTimeOut)&&(Lan_GetRegDW(RX_DP_CTRL)& + RX_DP_CTRL_FFWD_BUSY_)) + { + udelay(1); + dwTimeOut--; + } + if(dwTimeOut==0) { + + SMSC_WARNING("timed out waiting for RX FFWD to finish, RX_DP_CTRL=0x%08X", + Lan_GetRegDW(RX_DP_CTRL)); + } + } else { + while(dwDwordCount) { + u32 dwTemp=Lan_GetRegDW(RX_DATA_FIFO); + dwTemp=dwTemp; + dwDwordCount--; + } + } +} + +//Rx_ReceiverOff, and Rx_ReceiverOn use a reference counter +// because they are used in both the Rx code and the link management count +void Rx_ReceiverOff(PPRIVATE_DATA privateData) +{ + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if(privateData->dwRxOffCount==0) { + u32 dwMacCr=Mac_GetRegDW(privateData,MAC_CR,keyCode); + if(!(dwMacCr&MAC_CR_RXEN_)) { + SMSC_WARNING("Rx_ReceiverOff: Receiver is already Off"); + } + dwMacCr&=(~MAC_CR_RXEN_); + Mac_SetRegDW(privateData,MAC_CR,dwMacCr,keyCode); + //CLEAR_GPIO(GP_RX); + } + privateData->dwRxOffCount++; + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); +} + +//Rx_ReceiverOff, and Rx_ReceiverOn use a reference counter +// because they are used in both the Rx code and the link management count +void Rx_ReceiverOn(PPRIVATE_DATA privateData, VL_KEY callerKeyCode) +{ + unsigned long dwIntFlags=0; + VL_KEY keyCode=0; + + if (callerKeyCode == 0) { + keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + } + else { + SMSC_ASSERT(Vl_CheckLock(&(privateData->MacPhyLock),callerKeyCode)); + keyCode = callerKeyCode; + } + if(privateData->dwRxOffCount>0) { + privateData->dwRxOffCount--; + if(privateData->dwRxOffCount==0) { + u32 dwMacCr=Mac_GetRegDW(privateData,MAC_CR,keyCode); + if(dwMacCr&MAC_CR_RXEN_) { + SMSC_WARNING("Rx_ReceiverOn: Receiver is already on"); + } + dwMacCr|=MAC_CR_RXEN_; + Mac_SetRegDW(privateData,MAC_CR,dwMacCr,keyCode); + //SET_GPIO(GP_RX); + } + } else { + SMSC_ASSERT(false); + } + if (callerKeyCode == 0) { + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } +} + +void Rx_ProcessPackets(PPRIVATE_DATA privateData) +{ + u32 dwRxStatus=0; + PPLATFORM_DATA platformData=NULL; + + /* +#ifdef UseRxCsum +WORD wHwCsum = 0; +#endif +*/ + + // SET_GPIO(GP_RX); + + privateData->RxCongested=false; + platformData=&(privateData->PlatformData); + if(privateData->dwRxDmaCh>=TRANSFER_PIO) { + //Use PIO only + + //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running , + // however we are here re-writing the same value it already had, so it's ok" + Lan_SetRegDW(RX_CFG,RX_CFG_RXDOFF_2_); + while((dwRxStatus=Rx_PopRxStatus(privateData))!=0) + { + u32 dwPacketLength=((dwRxStatus&0x3FFF0000UL)>>16); + + //printk("dwPacketLength = 0x%08x\n", (u32) dwPacketLength); + //printk("dwRxStatus = 0x%08x\n", (u32) dwRxStatus); + + Rx_CountErrors(privateData,dwRxStatus); + + + if((dwRxStatus&RX_STS_ES_)==0) + //||(((dwRxStatus&0x00001080)==0x00001080)&&(privateData->RxVLanPkt==true))) + { + struct sk_buff *skb=NULL; + skb=dev_alloc_skb(dwPacketLength+2); + if(skb!=NULL) { + skb->data=skb->head; + skb->tail=skb->head; + skb_reserve(skb,2); // align IP on 16B boundary + + if (privateData->UseRxCsum) + { + skb_put(skb,dwPacketLength-2UL-4UL); + } + else + { + skb_put(skb,dwPacketLength-4UL); + } + + + //update counters + privateData->stats.rx_packets++; + + + if (privateData->UseRxCsum) + { + privateData->stats.rx_bytes+=(dwPacketLength-4-2); + + } + else + { + privateData->stats.rx_bytes+=(dwPacketLength-4); + } + + + privateData->RxPacketReadCount++; + privateData->RxPioReadCount++; + privateData->RxDataDWReadCount+= + (dwPacketLength+2+3)>>2; + + + + Platform_ReadFifo( + privateData->dwLanBase, + ((u32 *)(skb->head)), + (dwPacketLength+2+3)>>2); + + + //printk("Rx skb->len = 0x%08x\n", (u32) skb->len); + + Rx_HandOffSkb(privateData,skb); + continue; + } else { + SMSC_WARNING("Unable to allocate sk_buff for RX Packet, in PIO path"); + privateData->stats.rx_dropped++; + } + } + //if we get here then the packet is to be read + // out of the fifo and discarded + //printk("fast forward\n"); + dwPacketLength+=(2+3); + dwPacketLength>>=2; + Rx_FastForward(privateData,dwPacketLength); + } + } + else { + + //Use DMA and PIO + u32 dwDmaCh=privateData->dwRxDmaCh; + //struct sk_buff *dmaSkb=NULL;//use privateData->RxDmaSkb + DMA_XFER dmaXfer; + dmaXfer.dwLanReg=privateData->dwLanBase+RX_DATA_FIFO; + dmaXfer.pdwBuf=NULL;// this will be reset per dma request + dmaXfer.dwDmaCh=dwDmaCh; + dmaXfer.dwDwCnt=0;// this will be reset per dma request + dmaXfer.fMemWr=true; + while((dwRxStatus=Rx_PopRxStatus(privateData))!=0) + { + u32 dwPacketLength; +RUN_AGAIN: + Rx_CountErrors(privateData,dwRxStatus); + dwPacketLength=((dwRxStatus&0x3FFF0000UL)>>16); + + + if((dwRxStatus&RX_STS_ES_)==0) + { + struct sk_buff *skb=dev_alloc_skb(dwPacketLength+2*PLATFORM_CACHE_LINE_BYTES); + if(skb!=NULL) + { + skb->data=skb->head; + skb->tail=skb->head; + + //align IP on cache line boundary + privateData->stats.rx_packets++; + privateData->stats.rx_bytes+=(dwPacketLength-4UL); + if(dwPacketLength>=privateData->dwRxDmaThreshold) + { + //use DMA + //printk("Rx using DMA\n"); + u32 dwDwordCount; + skb_reserve(skb,PLATFORM_CACHE_LINE_BYTES-14); + + if (privateData->UseRxCsum) + { + skb_put(skb,dwPacketLength-2UL-4UL); + } + else + { + skb_put(skb,dwPacketLength-4UL); + } + + + + // skb_put(skb,dwPacketLength-4UL); + dwDwordCount=((dwPacketLength+ + (PLATFORM_CACHE_LINE_BYTES-14)+ + PLATFORM_CACHE_LINE_BYTES-1)& + (~(PLATFORM_CACHE_LINE_BYTES-1)))>>2; + Platform_CacheInvalidate( + platformData, + skb->head,dwDwordCount<<2); + dmaXfer.pdwBuf=(u32 *)(skb->head); + dmaXfer.dwDwCnt=dwDwordCount; + privateData->RxDataDWReadCount+=dwDwordCount; + privateData->RxPacketReadCount++; + privateData->RxDmaReadCount++; + if(privateData->RxSkb) + Platform_DmaComplete(platformData,dwDmaCh); + + //set end alignment and offset + switch(PLATFORM_CACHE_LINE_BYTES) + { + //case 4: Lan_SetRegDW(RX_CFG,0x00000200UL);break; + //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running , + // however we are here re-writing the same value it already had, so it's ok" + case 16:Lan_SetRegDW(RX_CFG, RX_CFG_RX_END_ALGN16_ |RX_CFG_RXDOFF_2_ );break; + case 32:Lan_SetRegDW(RX_CFG, RX_CFG_RX_END_ALGN32_ |RX_CFG_RXDOFF_18_);break; + default:SMSC_ASSERT(false); + } + if(!Platform_DmaStartXfer(platformData,&dmaXfer)) { + SMSC_WARNING("Failed Platform_DmaStartXfer"); + } + + + + + if(privateData->RxSkb) { + + Rx_HandOffSkb(privateData,privateData->RxSkb); + } + privateData->RxSkb=skb; + } + else + { + //use PIO + + // printk("Rx using PIO\n"); + if(privateData->RxSkb) { + Platform_DmaComplete(platformData,dwDmaCh); + Rx_HandOffSkb(privateData,privateData->RxSkb); + } + + privateData->RxSkb=skb; + + skb_reserve(skb,2); + + if (privateData->UseRxCsum) + { + skb_put(skb,dwPacketLength-2UL-4UL); + } + else + { + skb_put(skb,dwPacketLength-4UL); + } + + + + // skb_put(skb,dwPacketLength-4UL); + //set end alignment and offset + //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running , + // however we are here re-writing the same value it already had, so it's ok" + Lan_SetRegDW(RX_CFG, RX_CFG_RXDOFF_2_);//4 byte end alignment + privateData->RxPacketReadCount++; + privateData->RxPioReadCount++; + privateData->RxDataDWReadCount+= + ((dwPacketLength+2+3)>>2); + Platform_ReadFifo( + privateData->dwLanBase, + ((u32 *)(skb->head)), + (dwPacketLength+2+3)>>2); + + + + } + continue; + } + else + { + SMSC_WARNING("Unable to allocate sk_buff for RX Packet, in DMA path"); + privateData->stats.rx_dropped++; + } + } + //if we get here then the packet is to be read + // out of the fifo and discarded + if(privateData->RxSkb) Platform_DmaComplete(platformData,dwDmaCh); + //delay returning the dmaSkb to OS till later + dwPacketLength+=(2+3); + dwPacketLength>>=2; + //WARNING: the two LSBs of RXDOFF cannot be changed with the receiver running , + // however we are here re-writing the same value it already had, so it's ok" + Lan_SetRegDW(RX_CFG,RX_CFG_RXDOFF_2_);//4 byte end alignment + Rx_FastForward(privateData,dwPacketLength); + } + if(privateData->RxSkb) { + //while waiting for dma to complete, + // check if another packet arrives + u32 dwTimeOut=1000000; + while((Platform_DmaGetDwCnt(platformData,dwDmaCh))&& + (dwTimeOut)) + { + if((dwRxStatus=Rx_PopRxStatus(privateData))!=0) { + goto RUN_AGAIN; + } + udelay(1); + dwTimeOut--; + } + if(dwTimeOut==0) { + SMSC_WARNING("Timed out while waiting for final Dma to complete"); + } + if((dwRxStatus=Rx_PopRxStatus(privateData))!=0) { + goto RUN_AGAIN; + } + + + Rx_HandOffSkb(privateData,privateData->RxSkb); + privateData->RxSkb=NULL; + + //check one last time for another packet. + if((dwRxStatus=Rx_PopRxStatus(privateData))!=0) { + goto RUN_AGAIN; + } + } + } + Lan_SetRegDW(INT_STS,INT_STS_RSFL_); + // CLEAR_GPIO(GP_RX); +} + +void Rx_ProcessPacketsTasklet(unsigned long data) +{ + PPRIVATE_DATA privateData=(PPRIVATE_DATA)Rx_TaskletParameter; + data=data;//make lint happy + if(privateData==NULL) { + SMSC_WARNING("Rx_ProcessPacketsTasklet(privateData==NULL)"); + return; + } + Rx_ProcessPackets(privateData); + Lan_EnableIRQ(privateData); +} + +#ifdef LINUX_2_6_OR_NEWER +/* +int Smsc9118_rx_poll(struct net_device *dev,int * budget) +{ + int result=0; + int limit=0; + + PPRIVATE_DATA privateData=NULL; + SMSC_ASSERT(dev!=NULL); + SMSC_ASSERT(budget!=NULL); + privateData=((PPRIVATE_DATA)(dev->ml_priv)); + SMSC_ASSERT(privateData!=NULL); + + privateData->RxWorkLimit=dev->quota; + if((privateData->RxWorkLimit)>(*budget)) { + privateData->RxWorkLimit=(*budget); + } + + limit=privateData->RxWorkLimit; + + privateData->RxPacketsReceived=0; + // privateData->RxDone=false; + Rx_ProcessPackets(privateData); + + dev->quota-=privateData->RxPacketsReceived; + (*budget)-=privateData->RxPacketsReceived; + + if(privateData->RxPacketsReceived < limit) { + netif_rx_complete(dev); + Lan_EnableIRQ(privateData); + } else { + result=1; + } + + return result; +} +*/ +#endif + + + + + +bool Rx_HandleInterrupt( + PPRIVATE_DATA privateData, + u32 dwIntSts) +{ + bool result=false; + SMSC_ASSERT(privateData!=NULL); + + privateData->LastReasonForReleasingCPU=0; + + if(dwIntSts&INT_STS_RXE_) { + SMSC_TRACE("Rx_HandleInterrupt: RXE signalled"); + privateData->stats.rx_errors++; + Lan_SetRegDW(INT_STS,INT_STS_RXE_); + result=true; + } + + if(dwIntSts&INT_STS_RXDFH_INT_) { + privateData->stats.rx_dropped+=Lan_GetRegDW(RX_DROP); + Lan_SetRegDW(INT_STS,INT_STS_RXDFH_INT_); + result=true; + } + + if(privateData->dwGeneration==0) { + if(dwIntSts&(INT_STS_RDFL_)) { + Lan_SetRegDW(INT_STS,INT_STS_RDFL_); + Rx_HandleOverrun(privateData); + result=true; + } + } else { + if(dwIntSts&(INT_STS_RDFO_)) { + Lan_SetRegDW(INT_STS,INT_STS_RDFO_); + Rx_HandleOverrun(privateData); + result=true; + } + } + + if((!(dwIntSts&INT_STS_RSFL_))&&(privateData->RxOverrun==false)) { + return result; + } + result=true; + + if(privateData->MeasuringRxThroughput==false) { + privateData->MeasuringRxThroughput=true; + Gpt_ScheduleCallBack(privateData,GptCB_MeasureRxThroughput,1000); + privateData->RxFlowCurrentThroughput=0; + privateData->RxFlowCurrentPacketCount=0; + privateData->RxFlowCurrentWorkLoad=0; + } + + +#ifdef LINUX_2_6_OR_NEWER + if(rx_mode==PROCESSING_MODE_TASKLET) { + Lan_DisableIRQ(privateData); + Rx_TaskletParameter=(unsigned long)privateData; + tasklet_schedule(&Rx_Tasklet); + }else if (rx_mode==PROCESSING_MODE_NAPI) { + /* + Lan_DisableIRQ(privateData); + netif_rx_schedule(privateData->dev); + */ + }else { + Rx_ProcessPackets(privateData); + } +#else + if(rx_mode==PROCESSING_MODE_TASKLET) { + Lan_DisableIRQ(privateData); + Rx_TaskletParameter=(unsigned long)privateData; + tasklet_schedule(&Rx_Tasklet); + }else { + Rx_ProcessPackets(privateData); + } +#endif + + return result; +} + +bool RxStop_HandleInterrupt( + PPRIVATE_DATA privateData, + u32 dwIntSts) +{ + bool result=false; + SMSC_ASSERT(privateData!=NULL); + + if(dwIntSts&INT_STS_RXSTOP_INT_) { + result=true; + Gpt_CancelCallBack (privateData, GptCB_RxCompleteMulticast); + Rx_CompleteMulticastUpdate (privateData); + Lan_SetRegDW(INT_STS,INT_STS_RXSTOP_INT_); + Lan_DisableInterrupt(privateData,INT_EN_RXSTOP_INT_EN_); + } + return result; +} + +//returns hash bit number for given MAC address +//example: +// 01 00 5E 00 00 01 -> returns bit number 31 +static u32 Rx_Hash(BYTE addr[6]) +{ + int i; + u32 crc=0xFFFFFFFFUL; + u32 poly=0xEDB88320UL; + u32 result=0; + for(i=0;i<6;i++) + { + int bit; + u32 data=((u32)addr[i]); + for(bit=0;bit<8;bit++) + { + u32 p = (crc^((u32)data))&1UL; + crc >>= 1; + if(p!=0) crc ^= poly; + data >>=1; + } + } + result=((crc&0x01UL)<<5)| + ((crc&0x02UL)<<3)| + ((crc&0x04UL)<<1)| + ((crc&0x08UL)>>1)| + ((crc&0x10UL)>>3)| + ((crc&0x20UL)>>5); + return result; +} + +void Rx_SetMulticastList( + struct net_device *dev) +{ + PPRIVATE_DATA privateData=NULL; + VL_KEY keyCode=0; + unsigned long dwIntFlags=0; + SMSC_ASSERT(dev!=NULL); + + privateData=((PPRIVATE_DATA)(dev->ml_priv)); + SMSC_ASSERT(privateData!=NULL); + keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + + if(dev->flags & IFF_PROMISC) { + // SMSC_TRACE("Promiscuous Mode Enabled"); + privateData->set_bits_mask = MAC_CR_PRMS_; + privateData->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_); + + privateData->HashHi = 0UL; + privateData->HashLo = 0UL; + goto PREPARE; + } + + if(dev->flags & IFF_ALLMULTI) { + // SMSC_TRACE("Receive all Multicast Enabled"); + privateData->set_bits_mask = MAC_CR_MCPAS_; + privateData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_); + + privateData->HashHi = 0UL; + privateData->HashLo = 0UL; + goto PREPARE; + } + + + if(dev->mc_count>0) { + u32 dwHashH=0; + u32 dwHashL=0; + u32 dwCount=0; + struct dev_mc_list *mc_list=dev->mc_list; + + privateData->set_bits_mask = MAC_CR_HPFILT_; + privateData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_); + + while(mc_list!=NULL) { + dwCount++; + if((mc_list->dmi_addrlen)==6) { + u32 dwMask=0x01UL; + u32 dwBitNum=Rx_Hash(mc_list->dmi_addr); + // SMSC_TRACE("Multicast: enable dwBitNum=%d,addr=%02X %02X %02X %02X %02X %02X", + // dwBitNum, + // ((BYTE *)(mc_list->dmi_addr))[0], + // ((BYTE *)(mc_list->dmi_addr))[1], + // ((BYTE *)(mc_list->dmi_addr))[2], + // ((BYTE *)(mc_list->dmi_addr))[3], + // ((BYTE *)(mc_list->dmi_addr))[4], + // ((BYTE *)(mc_list->dmi_addr))[5]); + dwMask<<=(dwBitNum&0x1FUL); + if(dwBitNum&0x20UL) { + dwHashH|=dwMask; + } else { + dwHashL|=dwMask; + } + } else { + SMSC_WARNING("dmi_addrlen!=6"); + } + mc_list=mc_list->next; + } + if(dwCount!=((u32)(dev->mc_count))) { + SMSC_WARNING("dwCount!=dev->mc_count"); + } + // SMSC_TRACE("Multicast: HASHH=0x%08X,HASHL=0x%08X",dwHashH,dwHashL); + privateData->HashHi = dwHashH; + privateData->HashLo = dwHashL; + } + else + { + privateData->set_bits_mask = 0L; + privateData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); + + // SMSC_TRACE("Receive own packets only."); + privateData->HashHi = 0UL; + privateData->HashLo = 0UL; + } + +PREPARE: + if(privateData->dwGeneration<=1) { + if (privateData->MulticastUpdatePending == false) { + privateData->MulticastUpdatePending = true; + // prepare to signal software interrupt + Lan_SignalSoftwareInterrupt(privateData); + } + else { + // Rx_CompleteMulticastUpdate has not yet been called + // therefore these latest settings will be used instead + } + } else { + u32 local_MACCR; + Mac_SetRegDW(privateData,HASHH,privateData->HashHi,keyCode); + Mac_SetRegDW(privateData,HASHL,privateData->HashLo,keyCode); + local_MACCR = Mac_GetRegDW(privateData,MAC_CR,keyCode); + local_MACCR |= privateData->set_bits_mask; + local_MACCR &= ~(privateData->clear_bits_mask); + Mac_SetRegDW(privateData,MAC_CR,local_MACCR,keyCode); + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + return; +} + +void Eeprom_EnableAccess(PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + if(debug_mode&0x04UL) { + Lan_SetRegDW(GPIO_CFG,(g_GpioSetting&0xFF0FFFFFUL)); + } else { + Lan_ClrBitsDW(GPIO_CFG,0x00F00000UL); + } + udelay(100); +} + +void Eeprom_DisableAccess(PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + if(debug_mode&0x04UL) { + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); + } +} + +bool Eeprom_IsMacAddressLoaded(PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + return (Lan_GetRegDW(E2P_CMD)& + E2P_CMD_MAC_ADDR_LOADED_)?true:false; +} + +bool Eeprom_IsBusy(PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + return (Lan_GetRegDW(E2P_CMD)& + E2P_CMD_EPC_BUSY_)?true:false; +} + +bool Eeprom_Timeout(PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + return (Lan_GetRegDW(E2P_CMD)& + E2P_CMD_EPC_TIMEOUT_)?true:false; +} + +bool Eeprom_ReadLocation( + PPRIVATE_DATA privateData, + BYTE address, BYTE * data) +{ + u32 timeout=100000; + u32 temp=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(data!=NULL); + if((temp=Lan_GetRegDW(E2P_CMD))&E2P_CMD_EPC_BUSY_) { + SMSC_WARNING("Eeprom_ReadLocation: Busy at start, E2P_CMD=0x%08X",temp); + return false; + } + Lan_SetRegDW(E2P_CMD, + (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_READ_|((u32)address))); + while((timeout>0)&& + (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) + { + udelay(10); + timeout--; + } + if(timeout==0) { + return false; + } + (*data)=(BYTE)(Lan_GetRegDW(E2P_DATA)); + return true; +} + +bool Eeprom_EnableEraseAndWrite( + PPRIVATE_DATA privateData) +{ + u32 timeout=100000; + SMSC_ASSERT(privateData!=NULL); + if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) { + SMSC_WARNING("Eeprom_EnableEraseAndWrite: Busy at start"); + return false; + } + Lan_SetRegDW(E2P_CMD, + (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_EWEN_)); + + while((timeout>0)&& + (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) + { + udelay(10); + timeout--; + } + if(timeout==0) { + return false; + } + return true; +} + +bool Eeprom_DisableEraseAndWrite( + PPRIVATE_DATA privateData) +{ + u32 timeout=100000; + SMSC_ASSERT(privateData!=NULL); + if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) { + SMSC_WARNING("Eeprom_DisableEraseAndWrite: Busy at start"); + return false; + } + Lan_SetRegDW(E2P_CMD, + (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_EWDS_)); + + while((timeout>0)&& + (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) + { + udelay(10); + timeout--; + } + if(timeout==0) { + return false; + } + return true; +} + +bool Eeprom_WriteLocation( + PPRIVATE_DATA privateData,BYTE address,BYTE data) +{ + u32 timeout=100000; + SMSC_ASSERT(privateData!=NULL); + if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) { + SMSC_WARNING("Eeprom_WriteLocation: Busy at start"); + return false; + } + Lan_SetRegDW(E2P_DATA,((u32)data)); + Lan_SetRegDW(E2P_CMD, + (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_WRITE_|((u32)address))); + + while((timeout>0)&& + (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) + { + udelay(10); + timeout--; + } + if(timeout==0) { + return false; + } + return true; +} + +bool Eeprom_EraseAll( + PPRIVATE_DATA privateData) +{ + u32 timeout=100000; + SMSC_ASSERT(privateData!=NULL); + if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) { + SMSC_WARNING("Eeprom_EraseAll: Busy at start"); + return false; + } + Lan_SetRegDW(E2P_CMD, + (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_ERAL_)); + + while((timeout>0)&& + (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) + { + udelay(10); + timeout--; + } + if(timeout==0) { + return false; + } + return true; +} + +bool Eeprom_Reload( + PPRIVATE_DATA privateData) +{ + u32 timeout=100000; + SMSC_ASSERT(privateData!=NULL); + if(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_) { + SMSC_WARNING("Eeprom_Reload: Busy at start"); + return false; + } + Lan_SetRegDW(E2P_CMD, + (E2P_CMD_EPC_BUSY_|E2P_CMD_EPC_CMD_RELOAD_)); + + while((timeout>0)&& + (Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) + { + udelay(10); + timeout--; + } + if(timeout==0) { + return false; + } + return true; +} + +bool Eeprom_SaveMacAddress( + PPRIVATE_DATA privateData, + u32 dwHi16,u32 dwLo32) +{ + bool result=false; + SMSC_ASSERT(privateData!=NULL); + Eeprom_EnableAccess(privateData); + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_EraseAll(privateData)) goto DONE; + if(privateData->dwGeneration==0) { + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,0,0xA5)) goto DONE; + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,1,LOBYTE(LOWORD(dwLo32)))) goto DONE; + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,2,HIBYTE(LOWORD(dwLo32)))) goto DONE; + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,3,LOBYTE(HIWORD(dwLo32)))) goto DONE; + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,4,HIBYTE(HIWORD(dwLo32)))) goto DONE; + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,5,LOBYTE(LOWORD(dwHi16)))) goto DONE; + if(!Eeprom_EnableEraseAndWrite(privateData)) goto DONE; + if(!Eeprom_WriteLocation(privateData,6,HIBYTE(LOWORD(dwHi16)))) goto DONE; + } else { + if(!Eeprom_WriteLocation(privateData,0,0xA5)) goto DONE; + if(!Eeprom_WriteLocation(privateData,1,LOBYTE(LOWORD(dwLo32)))) goto DONE; + if(!Eeprom_WriteLocation(privateData,2,HIBYTE(LOWORD(dwLo32)))) goto DONE; + if(!Eeprom_WriteLocation(privateData,3,LOBYTE(HIWORD(dwLo32)))) goto DONE; + if(!Eeprom_WriteLocation(privateData,4,HIBYTE(HIWORD(dwLo32)))) goto DONE; + if(!Eeprom_WriteLocation(privateData,5,LOBYTE(LOWORD(dwHi16)))) goto DONE; + if(!Eeprom_WriteLocation(privateData,6,HIBYTE(LOWORD(dwHi16)))) goto DONE; + } + if(!Eeprom_DisableEraseAndWrite(privateData)) goto DONE; + + if(!Eeprom_Reload(privateData)) goto DONE; + if(!Eeprom_IsMacAddressLoaded(privateData)) goto DONE; + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + if(dwHi16!=Mac_GetRegDW(privateData,ADDRH,keyCode)) goto DONE; + if(dwLo32!=Mac_GetRegDW(privateData,ADDRL,keyCode)) goto DONE; + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + result=true; +DONE: + Eeprom_DisableAccess(privateData); + return result; +} + +volatile u32 g_GpioSetting=0x00000000UL; +#ifdef USE_LED1_WORK_AROUND +volatile u32 g_GpioSettingOriginal=0x00000000UL; +#endif + +bool Lan_Initialize( + PPRIVATE_DATA privateData, + u32 dwIntCfg, + u32 dwTxFifSz, + u32 dwAfcCfg) +{ + bool result=false; + u32 dwTimeOut=0; + u32 dwTemp=0; + u32 dwResetCount=3; + + SMSC_TRACE("-->Lan_Initialize(dwIntCfg=0x%08X)",dwIntCfg); + SMSC_ASSERT(privateData!=NULL); + + //Reset the LAN9118 + if(privateData->dwGeneration>0) { + dwResetCount=1; + } + while(dwResetCount>0) { + Lan_SetRegDW(HW_CFG,HW_CFG_SRST_); + dwTimeOut=1000000; + do { + udelay(10); + dwTemp=Lan_GetRegDW(HW_CFG); + dwTimeOut--; + } while((dwTimeOut>0)&&(dwTemp&HW_CFG_SRST_)); + if(dwTemp&HW_CFG_SRST_) { + SMSC_WARNING(" Failed to complete reset."); + goto DONE; + } + dwResetCount--; + } + + SMSC_ASSERT(dwTxFifSz>=0x00020000UL); + SMSC_ASSERT(dwTxFifSz<=0x000E0000UL); + SMSC_ASSERT((dwTxFifSz&(~HW_CFG_TX_FIF_SZ_))==0); + Lan_SetRegDW(HW_CFG,dwTxFifSz); + privateData->RxMaxDataFifoSize=0; + switch(dwTxFifSz>>16) { + case 2:privateData->RxMaxDataFifoSize=13440;break; + case 3:privateData->RxMaxDataFifoSize=12480;break; + case 4:privateData->RxMaxDataFifoSize=11520;break; + case 5:privateData->RxMaxDataFifoSize=10560;break; + case 6:privateData->RxMaxDataFifoSize=9600;break; + case 7:privateData->RxMaxDataFifoSize=8640;break; + case 8:privateData->RxMaxDataFifoSize=7680;break; + case 9:privateData->RxMaxDataFifoSize=6720;break; + case 10:privateData->RxMaxDataFifoSize=5760;break; + case 11:privateData->RxMaxDataFifoSize=4800;break; + case 12:privateData->RxMaxDataFifoSize=3840;break; + case 13:privateData->RxMaxDataFifoSize=2880;break; + case 14:privateData->RxMaxDataFifoSize=1920;break; + default:SMSC_ASSERT(false);break; + } + + if(dwAfcCfg==0xFFFFFFFF) { + switch(dwTxFifSz) { + + //AFC_HI is about ((Rx Data Fifo Size)*2/3)/64 + //AFC_LO is AFC_HI/2 + //BACK_DUR is about 5uS*(AFC_LO) rounded down + case 0x00020000UL://13440 Rx Data Fifo Size + dwAfcCfg=0x008C46AF;break; + case 0x00030000UL://12480 Rx Data Fifo Size + dwAfcCfg=0x0082419F;break; + case 0x00040000UL://11520 Rx Data Fifo Size + + dwAfcCfg=0x00783C9F;break; + case 0x00050000UL://10560 Rx Data Fifo Size + // dwAfcCfg=0x006E378F;break; + dwAfcCfg=0x006E374F;break; + case 0x00060000UL:// 9600 Rx Data Fifo Size + dwAfcCfg=0x0064328F;break; + case 0x00070000UL:// 8640 Rx Data Fifo Size + dwAfcCfg=0x005A2D7F;break; + case 0x00080000UL:// 7680 Rx Data Fifo Size + dwAfcCfg=0x0050287F;break; + case 0x00090000UL:// 6720 Rx Data Fifo Size + dwAfcCfg=0x0046236F;break; + case 0x000A0000UL:// 5760 Rx Data Fifo Size + dwAfcCfg=0x003C1E6F;break; + case 0x000B0000UL:// 4800 Rx Data Fifo Size + dwAfcCfg=0x0032195F;break; + + //AFC_HI is ~1520 bytes less than RX Data Fifo Size + //AFC_LO is AFC_HI/2 + //BACK_DUR is about 5uS*(AFC_LO) rounded down + case 0x000C0000UL:// 3840 Rx Data Fifo Size + dwAfcCfg=0x0024124F;break; + case 0x000D0000UL:// 2880 Rx Data Fifo Size + dwAfcCfg=0x0015073F;break; + case 0x000E0000UL:// 1920 Rx Data Fifo Size + dwAfcCfg=0x0006032F;break; + default:SMSC_ASSERT(false);break; + } + } + Lan_SetRegDW(AFC_CFG,(dwAfcCfg&0xFFFFFFF0UL)); + + //make sure EEPROM has finished loading before setting GPIO_CFG + dwTimeOut=1000; + while((dwTimeOut>0)&&(Lan_GetRegDW(E2P_CMD)&E2P_CMD_EPC_BUSY_)) { + udelay(5); + dwTimeOut--; + } + if(dwTimeOut==0) { + SMSC_WARNING("Lan_Initialize: Timed out waiting for EEPROM busy bit to clear\n"); + } + + if(debug_mode&0x04UL) { + if(OLD_REGISTERS(privateData)) + { + g_GpioSetting=0x00270700UL; + } else { + g_GpioSetting=0x00670700UL; + } + } else { + g_GpioSetting = 0x70070000UL; + } + Lan_SetRegDW(GPIO_CFG,g_GpioSetting); + + //initialize interrupts + Lan_SetRegDW(INT_EN,0); + Lan_SetRegDW(INT_STS,0xFFFFFFFFUL); + dwIntCfg|=INT_CFG_IRQ_EN_; + Lan_SetRegDW(INT_CFG,dwIntCfg); + + Vl_InitLock(&(privateData->MacPhyLock)); + spin_lock_init(&(privateData->IntEnableLock)); + privateData->LanInitialized=true; + + result=true; + +DONE: + SMSC_TRACE("<--Lan_Initialize"); + return result; +} + +void Lan_EnableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask) +{ + unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + Lan_SetBitsDW(INT_EN,dwIntEnMask); + spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_DisableInterrupt(PPRIVATE_DATA privateData,u32 dwIntEnMask) +{ + unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + Lan_ClrBitsDW(INT_EN,dwIntEnMask); + spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +//Spin locks for the following functions have been commented out +// because at this time they are not necessary. +//These function are +// Lan_SetTDFL +// Lan_SetTSFL +// Lan_SetRDFL +// Lan_SetRSFL +//Both the Rx and Tx side of the driver use the FIFO_INT, +// but the Rx side only touches is during initialization, +// so it is sufficient that Tx side simple preserve the Rx setting + +void Lan_SetTDFL(PPRIVATE_DATA privateData,BYTE level) { + // unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + u32 temp=Lan_GetRegDW(FIFO_INT); + temp&=0x00FFFFFFUL; + temp|=((u32)level)<<24; + Lan_SetRegDW(FIFO_INT,temp); + } + // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_SetTSFL(PPRIVATE_DATA privateData,BYTE level) { + // unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + u32 temp=Lan_GetRegDW(FIFO_INT); + temp&=0xFF00FFFFUL; + temp|=((u32)level)<<16; + Lan_SetRegDW(FIFO_INT,temp); + } + // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_SetRDFL(PPRIVATE_DATA privateData,BYTE level) { + // unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + u32 temp=Lan_GetRegDW(FIFO_INT); + temp&=0xFFFF00FFUL; + temp|=((u32)level)<<8; + Lan_SetRegDW(FIFO_INT,temp); + } + // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_SetRSFL(PPRIVATE_DATA privateData,BYTE level) { + // unsigned long dwIntFlags=0; + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->LanInitialized==true); + // spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + u32 temp=Lan_GetRegDW(FIFO_INT); + temp&=0xFFFFFF00UL; + temp|=((u32)level); + Lan_SetRegDW(FIFO_INT,temp); + } + // spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_EnableIRQ(PPRIVATE_DATA privateData) +{ + unsigned long dwIntFlags=0; + spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + Lan_SetBitsDW(INT_CFG,INT_CFG_IRQ_EN_); + } + spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_DisableIRQ(PPRIVATE_DATA privateData) +{ + unsigned long dwIntFlags=0; + spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + Lan_ClrBitsDW(INT_CFG,INT_CFG_IRQ_EN_); + } + spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_SetIntDeas(PPRIVATE_DATA privateData, u32 dwIntDeas) +{ + unsigned long dwIntFlags=0; + spin_lock_irqsave(&(privateData->IntEnableLock),dwIntFlags); + { + Lan_ClrBitsDW(INT_CFG,INT_CFG_INT_DEAS_); + Lan_SetBitsDW(INT_CFG,(dwIntDeas<<24)); + } + spin_unlock_irqrestore(&(privateData->IntEnableLock),dwIntFlags); +} + +void Lan_SignalSoftwareInterrupt(PPRIVATE_DATA privateData) +{ + SMSC_ASSERT(privateData!=NULL); + SMSC_ASSERT(privateData->dwLanBase!=0); + privateData->SoftwareInterruptSignal=false; + Lan_EnableInterrupt(privateData,INT_EN_SW_INT_EN_); +} + +bool Lan_HandleSoftwareInterrupt( + PPRIVATE_DATA privateData, + u32 dwIntSts) +{ + if(dwIntSts&INT_STS_SW_INT_) { + SMSC_TRACE("Got SW Interrupt (privateData=%08X)", (u32)privateData); + SMSC_ASSERT(privateData!=NULL); + Lan_DisableInterrupt(privateData,INT_EN_SW_INT_EN_); + Lan_SetRegDW(INT_STS,INT_STS_SW_INT_); + privateData->SoftwareInterruptSignal=true; + if (privateData->MulticastUpdatePending) { + Rx_BeginMulticastUpdate (privateData); + } + return true; + } + return false; +} + +typedef struct _SHOW_REG +{ + char szName[20]; + u32 dwOffset; +} SHOW_REG; +/* +FUNCTION: Lan_ShowRegs +This function is used to display the registers. +Except the phy. +*/ +void Lan_ShowRegs(PPRIVATE_DATA privateData) +{ + // Make these const struct's static to keep them off the stack. + // Otherwise, gcc will try to use _memcpy() to initialize them, + // which will *NOT* work in our RunTime environment. + static const SHOW_REG sysCsr[] = { + { "ID_REV", 0x50UL }, + { "INT_CFG", 0x54UL }, + { "INT_STS", 0x58UL }, + { "INT_EN", 0x5CUL }, + { "DMA_CFG", 0x60UL }, + { "BYTE_TEST", 0x64UL }, + { "FIFO_INT", 0x68UL }, + { "RX_CFG", 0x6CUL }, + { "TX_CFG", 0x70UL }, + { "HW_CFG", 0x74UL }, + { "RX_DP_CTRL", 0x78UL }, + { "RX_FIFO_INF", 0x7CUL }, + { "TX_FIFO_INF", 0x80UL }, + { "PMT_CTRL", 0x84UL }, + { "GPIO_CFG", 0x88UL }, + { "GPT_CFG", 0x8CUL }, + { "GPT_CNT", 0x90UL }, + { "FPGA_REV", 0x94UL }, + { "WORD_SWAP", 0x98UL }, + { "FREE_RUN", 0x9CUL }, + { "RX_DROP", 0xA0UL }, + { "MAC_CSR_CMD", 0xA4UL }, + { "MAC_CSR_DATA", 0xA8UL }, + { "AFC_CFG", 0xACUL }, + { "E2P_CMD", 0xB0UL }, + { "E2P_DATA", 0xB4UL }, + { "TEST_REG_A", 0xC0UL }}; + + static const SHOW_REG macCsr[] = { + { "MAC_CR", MAC_CR }, + { "ADDRH", ADDRH }, + { "ADDRL", ADDRL }, + { "HASHH", HASHH }, + { "HASHL", HASHL }, + { "MII_ACC", MII_ACC }, + { "MII_DATA", MII_DATA }, + { "FLOW", FLOW }, + { "VLAN1", VLAN1 }, + { "VLAN2", VLAN2 }, + { "WUFF", WUFF }, + { "WUCSR", WUCSR }}; + + int i, iNumSysRegs, iNumMacRegs; + u32 dwOldMacCmdReg, dwOldMacDataReg; + + iNumSysRegs = (int)(sizeof(sysCsr) / sizeof(SHOW_REG)); + iNumMacRegs = (int)(sizeof(macCsr) / sizeof(SHOW_REG)); + + // preserve MAC cmd/data reg's + dwOldMacCmdReg = Lan_GetRegDW(MAC_CSR_CMD); + dwOldMacDataReg = Lan_GetRegDW(MAC_CSR_DATA); + + SMSC_TRACE(""); + SMSC_TRACE(" LAN91C118 CSR's"); + SMSC_TRACE(" SYS CSR's MAC CSR's"); + + { + unsigned long dwIntFlags=0; + VL_KEY keyCode=Vl_WaitForLock(&(privateData->MacPhyLock),&dwIntFlags); + for (i=0; i<iNumMacRegs; i++) + { + SMSC_TRACE( + "%16s (0x%02X) = 0x%08X, %8s (0x%02X) + 0x%08X", + sysCsr[i].szName, + sysCsr[i].dwOffset, + *((volatile u32 *)(privateData->dwLanBase+sysCsr[i].dwOffset)), + macCsr[i].szName, + macCsr[i].dwOffset, + Mac_GetRegDW(privateData,macCsr[i].dwOffset,keyCode)); + + // restore original mac cmd/data reg's after each usage + Lan_SetRegDW(MAC_CSR_CMD,dwOldMacCmdReg); + Lan_SetRegDW(MAC_CSR_DATA,dwOldMacDataReg); + } + Vl_ReleaseLock(&(privateData->MacPhyLock),keyCode,&dwIntFlags); + } + for (i=iNumMacRegs; i<iNumSysRegs; i++) + { + SMSC_TRACE("%16s (0x%02X) = 0x%08X", + sysCsr[i].szName, + sysCsr[i].dwOffset, + *((volatile u32 *)(privateData->dwLanBase+sysCsr[i].dwOffset))); + } +} + +void Vl_InitLock(PVERIFIABLE_LOCK pVl) +{ + SMSC_ASSERT(pVl!=NULL); + spin_lock_init(&(pVl->Lock)); + pVl->KeyCode=0; +} + +bool Vl_CheckLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode) +{ + bool result=false; + SMSC_ASSERT(pVl!=NULL); + if(keyCode==pVl->KeyCode) + result=true; + return result; +} + +VL_KEY Vl_WaitForLock(PVERIFIABLE_LOCK pVl,unsigned long *pdwIntFlags) +{ + VL_KEY result=0; + SMSC_ASSERT(pVl!=NULL); + spin_lock_irqsave( + &(pVl->Lock), + (*pdwIntFlags)); + pVl->KeyCode++; + if(pVl->KeyCode>0x80000000UL) { + pVl->KeyCode=1; + } + result=pVl->KeyCode; + return result; +} + +void Vl_ReleaseLock(PVERIFIABLE_LOCK pVl,VL_KEY keyCode,unsigned long *pdwIntFlags) +{ + SMSC_ASSERT(pVl!=NULL); + SMSC_ASSERT(pVl->KeyCode==keyCode); + spin_unlock_irqrestore(&(pVl->Lock),(*pdwIntFlags)); +} + +#ifndef USING_LINT +module_init(Smsc9118_init_module); +module_exit(Smsc9118_cleanup_module); +#endif -- 1.5.4.3