/*******************************************************************************
*
* 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.
*
** 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@lxorguk.ukuu.org.uk>:
* 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
*
* Sakari Ailus <sakari.ailus@iki.fi>:
*
* v1.15a Dec 15 2008 - Remove bbuf support, it doesn't work anyway.
*
*******************************************************************************/
#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 const char TLanSignature[] = "TLAN";
static const char tlan_banner[] = "ThunderLAN driver v1.15a\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 *);
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 void TLan_tx_timeout_work(struct work_struct *work);
static int tlan_init_one( struct pci_dev *pdev, const struct pci_device_id *ent);
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 inline void
TLan_StoreSKB( struct tlan_list_tag *tag, struct sk_buff *skb)
{
unsigned long addr = (unsigned long)skb;
tag->buffer[9].address = addr;
tag->buffer[8].address = upper_32_bits(addr);
}
static inline struct sk_buff *
TLan_GetSKB( const struct tlan_list_tag *tag)
{
unsigned long addr;
addr = tag->buffer[9].address;
addr |= (tag->buffer[8].address << 16) << 16;
return (struct sk_buff *) addr;
}
static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
NULL,
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);
} /* TLan_SetTimer */
/*****************************************************************************
******************************************************************************
ThunderLAN Driver Primary Functions
These functions are more or less common to all Linux network drivers.
******************************************************************************
*****************************************************************************/
/***************************************************************
* tlan_remove_one
*
* Returns:
* Nothing
* Parms:
* None
*
* Goes through the TLanDevices list and frees the device
* structs and memory associated with each device (lists
* and buffers). It also ureserves the IO port regions
* associated with this device.
*
**************************************************************/
static void __devexit tlan_remove_one( struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata( pdev );
TLanPrivateInfo *priv = netdev_priv(dev);
unregister_netdev( dev );
if ( priv->dmaStorage ) {
pci_free_consistent(priv->pciDev,
priv->dmaSize, priv->dmaStorage,
priv->dmaStorageDMA );
}
#ifdef CONFIG_PCI
pci_release_regions(pdev);
#endif
free_netdev( dev );
pci_set_drvdata( pdev, NULL );
}
static struct pci_driver tlan_driver = {
.name = "tlan",
.id_table = tlan_pci_tbl,
.probe = tlan_init_one,
.remove = __devexit_p(tlan_remove_one),
};
static int __init tlan_probe(void)
{
int rc = -ENODEV;
printk(KERN_INFO "%s", tlan_banner);
TLAN_DBG(TLAN_DEBUG_PROBE, "Starting PCI Probe....\n");
/* Use new style PCI probing. Now the kernel will
do most of this for us */
rc = pci_register_driver(&tlan_driver);
if (rc != 0) {
printk(KERN_ERR "TLAN: Could not register pci driver.\n");
goto err_out_pci_free;
}
TLAN_DBG(TLAN_DEBUG_PROBE, "Starting EISA Probe....\n");
TLan_EisaProbe();
printk(KERN_INFO "TLAN: %d device%s installed, PCI: %d EISA: %d\n",
TLanDevicesInstalled, TLanDevicesInstalled == 1 ? "" : "s",
tlan_have_pci, tlan_have_eisa);
if (TLanDevicesInstalled == 0) {
rc = -ENODEV;
goto err_out_pci_unreg;
}
return 0;
err_out_pci_unreg:
pci_unregister_driver(&tlan_driver);
err_out_pci_free:
return rc;
}
static int __devinit tlan_init_one( struct pci_dev *pdev,
const struct pci_device_id *ent)
{
return TLan_probe1( pdev, -1, -1, 0, ent);
}
/*
***************************************************************
* tlan_probe1
*
* Returns:
* 0 on success, error code on error
* Parms:
* none
*
* The name is lower case to fit in with all the rest of
* the netcard_probe names. This function looks for
* another TLan based adapter, setting it up with the
* allocated device struct if one is found.
* tlan_probe has been ported to the new net API and
* now allocates its own device structure. This function
* is also used by modules.
*
**************************************************************/
static int __devinit TLan_probe1(struct pci_dev *pdev,
long ioaddr, int irq, int rev,
const struct pci_device_id *ent )
{
struct net_device *dev;
TLanPrivateInfo *priv;
u16 device_id;
int reg, rc = -ENODEV;
#ifdef CONFIG_PCI
if (pdev) {
rc = pci_enable_device(pdev);
if (rc)
return rc;
rc = pci_request_regions(pdev, TLanSignature);
if (rc) {
printk(KERN_ERR "TLAN: Could not reserve IO regions\n");
goto err_out;
}
}
#endif /* CONFIG_PCI */
dev = alloc_etherdev(sizeof(TLanPrivateInfo));
if (dev == NULL) {
printk(KERN_ERR "TLAN: Could not allocate memory for device.\n");
rc = -ENOMEM;
goto err_out_regions;
}
SET_NETDEV_DEV(dev, &pdev->dev);
priv = netdev_priv(dev);
priv->pciDev = pdev;
priv->dev = dev;
/* Is this a PCI device? */
if (pdev) {
u32 pci_io_base = 0;
priv->adapter = &board_info[ent->driver_data];
rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (rc) {
printk(KERN_ERR "TLAN: No suitable PCI mapping available.\n");
goto err_out_free_dev;
}
for ( reg= 0; reg <= 5; reg ++ ) {
if (pci_resource_flags(pdev, reg) & IORESOURCE_IO) {
pci_io_base = pci_resource_start(pdev, reg);
TLAN_DBG( TLAN_DEBUG_GNRL, "IO mapping is available at %x.\n",
pci_io_base);
break;
}
}
if (!pci_io_base) {
printk(KERN_ERR "TLAN: No IO mappings available\n");
rc = -EIO;
goto err_out_free_dev;
}
dev->base_addr = pci_io_base;
dev->irq = pdev->irq;
priv->adapterRev = pdev->revision;
pci_set_master(pdev);
pci_set_drvdata(pdev, dev);
} else { /* EISA card */
/* This is a hack. We need to know which board structure
* is suited for this adapter */
device_id = inw(ioaddr + EISA_ID2);
priv->is_eisa = 1;
if (device_id == 0x20F1) {
priv->adapter = &board_info[13]; /* NetFlex-3/E */
priv->adapterRev = 23; /* TLAN 2.3 */
} else {
|