/*******************************************************************************
*
* Linux ThunderLAN Driver
*
* tlan.c
* by James Banks
*
* (C) 1997-1998 Caldera, Inc.
* (C) 1998 James Banks
* (C) 1999-2001 Torben Mathiasen
* (C) 2002 Samuel Chessman
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
** This file is best viewed/edited with columns>=132.
*
** Useful (if not required) reading:
*
* Texas Instruments, ThunderLAN Programmer's Guide,
* TI Literature Number SPWU013A
* available in PDF format from www.ti.com
* Level One, LXT901 and LXT970 Data Sheets
* available in PDF format from www.level1.com
* National Semiconductor, DP83840A Data Sheet
* available in PDF format from www.national.com
* Microchip Technology, 24C01A/02A/04A Data Sheet
* available in PDF format from www.microchip.com
*
* Change History
*
* Tigran Aivazian <tigran@sco.com>: TLan_PciProbe() now uses
* new PCI BIOS interface.
* Alan Cox <alan@redhat.com>: Fixed the out of memory
* handling.
*
* Torben Mathiasen <torben.mathiasen@compaq.com> New Maintainer!
*
* v1.1 Dec 20, 1999 - Removed linux version checking
* Patch from Tigran Aivazian.
* - v1.1 includes Alan's SMP updates.
* - We still have problems on SMP though,
* but I'm looking into that.
*
* v1.2 Jan 02, 2000 - Hopefully fixed the SMP deadlock.
* - Removed dependency of HZ being 100.
* - We now allow higher priority timers to
* overwrite timers like TLAN_TIMER_ACTIVITY
* Patch from John Cagle <john.cagle@compaq.com>.
* - Fixed a few compiler warnings.
*
* v1.3 Feb 04, 2000 - Fixed the remaining HZ issues.
* - Removed call to pci_present().
* - Removed SA_INTERRUPT flag from irq handler.
* - Added __init and __initdata to reduce resisdent
* code size.
* - Driver now uses module_init/module_exit.
* - Rewrote init_module and tlan_probe to
* share a lot more code. We now use tlan_probe
* with builtin and module driver.
* - Driver ported to new net API.
* - tlan.txt has been reworked to reflect current
* driver (almost)
* - Other minor stuff
*
* v1.4 Feb 10, 2000 - Updated with more changes required after Dave's
* network cleanup in 2.3.43pre7 (Tigran & myself)
* - Minor stuff.
*
* v1.5 March 22, 2000 - Fixed another timer bug that would hang the driver
* if no cable/link were present.
* - Cosmetic changes.
* - TODO: Port completely to new PCI/DMA API
* Auto-Neg fallback.
*
* v1.6 April 04, 2000 - Fixed driver support for kernel-parameters. Haven't
* tested it though, as the kernel support is currently
* broken (2.3.99p4p3).
* - Updated tlan.txt accordingly.
* - Adjusted minimum/maximum frame length.
* - There is now a TLAN website up at
* http://tlan.kernel.dk
*
* v1.7 April 07, 2000 - Started to implement custom ioctls. Driver now
* reports PHY information when used with Donald
* Beckers userspace MII diagnostics utility.
*
* v1.8 April 23, 2000 - Fixed support for forced speed/duplex settings.
* - Added link information to Auto-Neg and forced
* modes. When NIC operates with auto-neg the driver
* will report Link speed & duplex modes as well as
* link partner abilities. When forced link is used,
* the driver will report status of the established
* link.
* Please read tlan.txt for additional information.
* - Removed call to check_region(), and used
* return value of request_region() instead.
*
* v1.8a May 28, 2000 - Minor updates.
*
* v1.9 July 25, 2000 - Fixed a few remaining Full-Duplex issues.
* - Updated with timer fixes from Andrew Morton.
* - Fixed module race in TLan_Open.
* - Added routine to monitor PHY status.
* - Added activity led support for Proliant devices.
*
* v1.10 Aug 30, 2000 - Added support for EISA based tlan controllers
* like the Compaq NetFlex3/E.
* - Rewrote tlan_probe to better handle multiple
* bus probes. Probing and device setup is now
* done through TLan_Probe and TLan_init_one. Actual
* hardware probe is done with kernel API and
* TLan_EisaProbe.
* - Adjusted debug information for probing.
* - Fixed bug that would cause general debug information
* to be printed after driver removal.
* - Added transmit timeout handling.
* - Fixed OOM return values in tlan_probe.
* - Fixed possible mem leak in tlan_exit
* (now tlan_remove_one).
* - Fixed timer bug in TLan_phyMonitor.
* - This driver version is alpha quality, please
* send me any bug issues you may encounter.
*
* v1.11 Aug 31, 2000 - Do not try to register irq 0 if no irq line was
* set for EISA cards.
* - Added support for NetFlex3/E with nibble-rate
* 10Base-T PHY. This is untestet as I haven't got
* one of these cards.
* - Fixed timer being added twice.
* - Disabled PhyMonitoring by default as this is
* work in progress. Define MONITOR to enable it.
* - Now we don't display link info with PHYs that
* doesn't support it (level1).
* - Incresed tx_timeout beacuse of auto-neg.
* - Adjusted timers for forced speeds.
*
* v1.12 Oct 12, 2000 - Minor fixes (memleak, init, etc.)
*
* v1.13 Nov 28, 2000 - Stop flooding console with auto-neg issues
* when link can't be established.
* - Added the bbuf option as a kernel parameter.
* - Fixed ioaddr probe bug.
* - Fixed stupid deadlock with MII interrupts.
* - Added support for speed/duplex selection with
* multiple nics.
* - Added partly fix for TX Channel lockup with
* TLAN v1.0 silicon. This needs to be investigated
* further.
*
* v1.14 Dec 16, 2000 - Added support for servicing multiple frames per.
* interrupt. Thanks goes to
* Adam Keys <adam@ti.com>
* Denis Beaudoin <dbeaudoin@ti.com>
* for providing the patch.
* - Fixed auto-neg output when using multiple
* adapters.
* - Converted to use new taskq interface.
*
* v1.14a Jan 6, 2001 - Minor adjustments (spinlocks, etc.)
*
* Samuel Chessman <chessman@tux.org> New Maintainer!
*
* v1.15 Apr 4, 2002 - Correct operation when aui=1 to be
* 10T half duplex no loopback
* Thanks to Gunnar Eikman
*******************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/eisa.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/mii.h>
#include "tlan.h"
typedef u32 (TLanIntVectorFunc)( struct net_device *, u16 );
/* For removing EISA devices */
static struct net_device *TLan_Eisa_Devices;
static int TLanDevicesInstalled;
/* Set speed, duplex and aui settings */
static int aui[MAX_TLAN_BOARDS];
static int duplex[MAX_TLAN_BOARDS];
static int speed[MAX_TLAN_BOARDS];
static int boards_found;
module_param_array(aui, int, NULL, 0);
module_param_array(duplex, int, NULL, 0);
module_param_array(speed, int, NULL, 0);
MODULE_PARM_DESC(aui, "ThunderLAN use AUI port(s) (0-1)");
MODULE_PARM_DESC(duplex, "ThunderLAN duplex setting(s) (0-default, 1-half, 2-full)");
MODULE_PARM_DESC(speed, "ThunderLAN port speen setting(s) (0,10,100)");
MODULE_AUTHOR("Maintainer: Samuel Chessman <chessman@tux.org>");
MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters");
MODULE_LICENSE("GPL");
/* Define this to enable Link beat monitoring */
#undef MONITOR
/* Turn on debugging. See Documentation/networking/tlan.txt for details */
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "ThunderLAN debug mask");
static int bbuf;
module_param(bbuf, int, 0);
MODULE_PARM_DESC(bbuf, "ThunderLAN use big buffer (0-1)");
static u8 *TLanPadBuffer;
static dma_addr_t TLanPadBufferDMA;
static char TLanSignature[] = "TLAN";
static const char tlan_banner[] = "ThunderLAN driver v1.15\n";
static int tlan_have_pci;
static int tlan_have_eisa;
static const char *media[] = {
"10BaseT-HD ", "10BaseT-FD ","100baseTx-HD ",
"100baseTx-FD", "100baseT4", NULL
};
static struct board {
const char *deviceLabel;
u32 flags;
u16 addrOfs;
} board_info[] = {
{ "Compaq Netelligent 10 T PCI UTP", TLAN_ADAPTER_ACTIVITY_LED, 0x83 },
{ "Compaq Netelligent 10/100 TX PCI UTP", TLAN_ADAPTER_ACTIVITY_LED, 0x83 },
{ "Compaq Integrated NetFlex-3/P", TLAN_ADAPTER_NONE, 0x83 },
{ "Compaq NetFlex-3/P", TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, 0x83 },
{ "Compaq NetFlex-3/P", TLAN_ADAPTER_NONE, 0x83 },
{ "Compaq Netelligent Integrated 10/100 TX UTP", TLAN_ADAPTER_ACTIVITY_LED, 0x83 },
{ "Compaq Netelligent Dual 10/100 TX PCI UTP", TLAN_ADAPTER_NONE, 0x83 },
{ "Compaq Netelligent 10/100 TX Embedded UTP", TLAN_ADAPTER_NONE, 0x83 },
{ "Olicom OC-2183/2185", TLAN_ADAPTER_USE_INTERN_10, 0x83 },
{ "Olicom OC-2325", TLAN_ADAPTER_UNMANAGED_PHY, 0xF8 },
{ "Olicom OC-2326", TLAN_ADAPTER_USE_INTERN_10, 0xF8 },
{ "Compaq Netelligent 10/100 TX UTP", TLAN_ADAPTER_ACTIVITY_LED, 0x83 },
{ "Compaq Netelligent 10 T/2 PCI UTP/Coax", TLAN_ADAPTER_NONE, 0x83 },
{ "Compaq NetFlex-3/E", TLAN_ADAPTER_ACTIVITY_LED | /* EISA card */
TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, 0x83 },
{ "Compaq NetFlex-3/E", TLAN_ADAPTER_ACTIVITY_LED, 0x83 }, /* EISA card */
};
static struct pci_device_id tlan_pci_tbl[] = {
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL10,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETFLEX3I,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_THUNDER,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETFLEX3B,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100PI,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100D,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_NETEL100I,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 },
{ PCI_VENDOR_ID_OLICOM, PCI_DEVICE_ID_OLICOM_OC2183,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 },
{ PCI_VENDOR_ID_OLICOM, PCI_DEVICE_ID_OLICOM_OC2325,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 },
{ PCI_VENDOR_ID_OLICOM, PCI_DEVICE_ID_OLICOM_OC2326,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 },
{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_T2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 },
{ 0,}
};
MODULE_DEVICE_TABLE(pci, tlan_pci_tbl);
static void TLan_EisaProbe( void );
static void TLan_Eisa_Cleanup( void );
static int TLan_Init( struct net_device * );
static int TLan_Open( struct net_device *dev );
static int TLan_StartTx( struct sk_buff *, struct net_device *);
static irqreturn_t TLan_HandleInterrupt( int, void *, struct pt_regs *);
static int TLan_Close( struct net_device *);
static struct net_device_stats *TLan_GetStats( struct net_device *);
static void TLan_SetMulticastList( struct net_device *);
static int TLan_ioctl( struct net_device *dev, struct ifreq *rq, int cmd);
static int TLan_probe1( struct pci_dev *pdev, long ioaddr, int irq, int rev, const struct pci_device_id *ent);
static void TLan_tx_timeout( struct net_device *dev);
static int tlan_init_one( struct pci_dev *pdev, const struct pci_device_id *ent);
static u32 TLan_HandleInvalid( struct net_device *, u16 );
static u32 TLan_HandleTxEOF( struct net_device *, u16 );
static u32 TLan_HandleStatOverflow( struct net_device *, u16 );
static u32 TLan_HandleRxEOF( struct net_device *, u16 );
static u32 TLan_HandleDummy( struct net_device *, u16 );
static u32 TLan_HandleTxEOC( struct net_device *, u16 );
static u32 TLan_HandleStatusCheck( struct net_device *, u16 );
static u32 TLan_HandleRxEOC( struct net_device *, u16 );
static void TLan_Timer( unsigned long );
static void TLan_ResetLists( struct net_device * );
static void TLan_FreeLists( struct net_device * );
static void TLan_PrintDio( u16 );
static void TLan_PrintList( TLanList *, char *, int );
static void TLan_ReadAndClearStats( struct net_device *, int );
static void TLan_ResetAdapter( struct net_device * );
static void TLan_FinishReset( struct net_device * );
static void TLan_SetMac( struct net_device *, int areg, char *mac );
static void TLan_PhyPrint( struct net_device * );
static void TLan_PhyDetect( struct net_device * );
static void TLan_PhyPowerDown( struct net_device * );
static void TLan_PhyPowerUp( struct net_device * );
static void TLan_PhyReset( struct net_device * );
static void TLan_PhyStartLink( struct net_device * );
static void TLan_PhyFinishAutoNeg( struct net_device * );
#ifdef MONITOR
static void TLan_PhyMonitor( struct net_device * );
#endif
/*
static int TLan_PhyNop( struct net_device * );
static int TLan_PhyInternalCheck( struct net_device * );
static int TLan_PhyInternalService( struct net_device * );
static int TLan_PhyDp83840aCheck( struct net_device * );
*/
static int TLan_MiiReadReg( struct net_device *, u16, u16, u16 * );
static void TLan_MiiSendData( u16, u32, unsigned );
static void TLan_MiiSync( u16 );
static void TLan_MiiWriteReg( struct net_device *, u16, u16, u16 );
static void TLan_EeSendStart( u16 );
static int TLan_EeSendByte( u16, u8, int );
static void TLan_EeReceiveByte( u16, u8 *, int );
static int TLan_EeReadByte( struct net_device *, u8, u8 * );
static void
TLan_StoreSKB( struct tlan_list_tag *tag, struct sk_buff *skb)
{
unsigned long addr = (unsigned long)skb;
tag->buffer[9].address = (u32)addr;
addr >>= 31; /* >>= 32 is undefined for 32bit arch, stupid C */
addr >>= 1;
tag->buffer[8].address = (u32)addr;
}
static struct sk_buff *
TLan_GetSKB( struct tlan_list_tag *tag)
{
unsigned long addr = tag->buffer[8].address;
addr <<= 31;
addr <<= 1;
addr |= tag->buffer[9].address;
return (struct sk_buff *) addr;
}
static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
TLan_HandleInvalid,
TLan_HandleTxEOF,
TLan_HandleStatOverflow,
TLan_HandleRxEOF,
TLan_HandleDummy,
TLan_HandleTxEOC,
TLan_HandleStatusCheck,
TLan_HandleRxEOC
};
static inline void
TLan_SetTimer( struct net_device *dev, u32 ticks, u32 type )
{
TLanPrivateInfo *priv = netdev_priv(dev);
unsigned long flags = 0;
if (!in_irq())
spin_lock_irqsave(&priv->lock, flags);
if ( priv->timer.function != NULL &&
priv->timerType != TLAN_TIMER_ACTIVITY ) {
if (!in_irq())
spin_unlock_irqrestore(&priv->lock, flags);
return;
}
priv->timer.function = &TLan_Timer;
if (!in_irq())
spin_unlock_irqrestore(&priv->lock, flags);
priv->timer.data = (unsigned long) dev;
priv->timerSetAt = jiffies;
priv->timerType = type;
mod_timer(&priv->timer, jiffies + ticks);
|