aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/tlan.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/tlan.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/net/tlan.c')
-rw-r--r--drivers/net/tlan.c3304
1 files changed, 3304 insertions, 0 deletions
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
new file mode 100644
index 00000000000..a7ffa64502d
--- /dev/null
+++ b/drivers/net/tlan.c
@@ -0,0 +1,3304 @@
+/*******************************************************************************
+ *
+ * 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/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_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;
+
+static int bbuf;
+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);
+
+} /* 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)
+{
+ static int pad_allocated;
+
+ printk(KERN_INFO "%s", tlan_banner);
+
+ TLanPadBuffer = (u8 *) pci_alloc_consistent(NULL, TLAN_MIN_FRAME_SIZE, &TLanPadBufferDMA);
+
+ if (TLanPadBuffer == NULL) {
+ printk(KERN_ERR "TLAN: Could not allocate memory for pad buffer.\n");
+ return -ENOMEM;
+ }
+
+ memset(TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE);
+ pad_allocated = 1;
+
+ TLAN_DBG(TLAN_DEBUG_PROBE, "Starting PCI Probe....\n");
+
+ /* Use new style PCI probing. Now the kernel will
+ do most of this for us */
+ pci_register_driver(&tlan_driver);
+
+ 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) {
+ pci_unregister_driver(&tlan_driver);
+ pci_free_consistent(NULL, TLAN_MIN_FRAME_SIZE, TLanPadBuffer, TLanPadBufferDMA);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+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;
+ u8 pci_rev;
+ u16 device_id;
+ int reg, rc = -ENODEV;
+
+ 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;
+ }
+ }
+
+ 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_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ priv = netdev_priv(dev);
+
+ priv->pciDev = pdev;
+
+ /* 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, 0xFFFFFFFF);
+ if (rc) {
+ printk(KERN_ERR "TLAN: No suitable PCI mapping available.\n");
+ goto err_out_free_dev;
+ }
+
+ pci_read_config_byte ( pdev, PCI_REVISION_ID, &pci_rev);
+
+ 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 = pci_rev;
+ 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 {
+ priv->adapter = &board_info[14];
+ priv->adapterRev = 10; /* TLAN 1.0 */
+ }
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ }
+
+ /* Kernel parameters */
+ if (dev->mem_start) {
+ priv->aui = dev->mem_start & 0x01;
+ priv->duplex = ((dev->mem_start & 0x06) == 0x06) ? 0 : (dev->mem_start & 0x06) >> 1;
+ priv->speed = ((dev->mem_start & 0x18) == 0x18) ? 0 : (dev->mem_start & 0x18) >> 3;
+
+ if (priv->speed == 0x1) {
+ priv->speed = TLAN_SPEED_10;
+ } else if (priv->speed == 0x2) {
+ priv->speed = TLAN_SPEED_100;
+ }
+ debug = priv->debug = dev->mem_end;
+ } else {
+ priv->aui = aui[boards_found];
+ priv->speed = speed[boards_found];
+ priv->duplex = duplex[boards_found];
+ priv->debug = debug;
+ }
+
+ /* This will be used when we get an adapter error from
+ * within our irq handler */
+ INIT_WORK(&priv->tlan_tqueue, (void *)(void*)TLan_tx_timeout, dev);
+
+ spin_lock_init(&priv->lock);
+
+ rc = TLan_Init(dev);
+ if (rc) {
+ printk(KERN_ERR "TLAN: Could not set up device.\n");
+ goto err_out_free_dev;
+ }
+
+ rc = register_netdev(dev);
+ if (rc) {
+ printk(KERN_ERR "TLAN: Could not register device.\n");
+ goto err_out_uninit;
+ }
+
+
+ TLanDevicesInstalled++;
+ boards_found++;
+
+ /* pdev is NULL if this is an EISA device */
+ if (pdev)
+ tlan_have_pci++;
+ else {
+ priv->nextDevice = TLan_Eisa_Devices;
+ TLan_Eisa_Devices = dev;
+ tlan_have_eisa++;
+ }
+
+ printk(KERN_INFO "TLAN: %s irq=%2d, io=%04x, %s, Rev. %d\n",
+ dev->name,
+ (int) dev->irq,
+ (int) dev->base_addr,
+ priv->adapter->deviceLabel,
+ priv->adapterRev);
+ return 0;
+
+err_out_uninit:
+ pci_free_consistent(priv->pciDev, priv->dmaSize, priv->dmaStorage,
+ priv->dmaStorageDMA );
+err_out_free_dev:
+ free_netdev(dev);
+err_out_regions:
+#ifdef CONFIG_PCI
+ if (pdev)
+ pci_release_regions(pdev);
+#endif
+err_out:
+ if (pdev)
+ pci_disable_device(pdev);
+ return rc;
+}
+
+
+static void TLan_Eisa_Cleanup(void)
+{
+ struct net_device *dev;
+ TLanPrivateInfo *priv;
+
+ while( tlan_have_eisa ) {
+ dev = TLan_Eisa_Devices;
+ priv = netdev_priv(dev);
+ if (priv->dmaStorage) {
+ pci_free_consistent(priv->pciDev, priv->dmaSize, priv->dmaStorage, priv->dmaStorageDMA );
+ }
+ release_region( dev->base_addr, 0x10);
+ unregister_netdev( dev );
+ TLan_Eisa_Devices = priv->nextDevice;
+ free_netdev( dev );
+ tlan_have_eisa--;
+ }
+}
+
+
+static void __exit tlan_exit(void)
+{
+ pci_unregister_driver(&tlan_driver);
+
+ if (tlan_have_eisa)
+ TLan_Eisa_Cleanup();
+
+ pci_free_consistent(NULL, TLAN_MIN_FRAME_SIZE, TLanPadBuffer, TLanPadBufferDMA);
+
+}
+
+
+/* Module loading/unloading */
+module_init(tlan_probe);
+module_exit(tlan_exit);
+
+
+
+ /**************************************************************
+ * TLan_EisaProbe
+ *
+ * Returns: 0 on success, 1 otherwise
+ *
+ * Parms: None
+ *
+ *
+ * This functions probes for EISA devices and calls
+ * TLan_probe1 when one is found.
+ *
+ *************************************************************/
+
+static void __init TLan_EisaProbe (void)
+{
+ long ioaddr;
+ int rc = -ENODEV;
+ int irq;
+ u16 device_id;
+
+ if (!EISA_bus) {
+ TLAN_DBG(TLAN_DEBUG_PROBE, "No EISA bus present\n");
+ return;
+ }
+
+ /* Loop through all slots of the EISA bus */
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+
+ TLAN_DBG(TLAN_DEBUG_PROBE,"EISA_ID 0x%4x: 0x%4x\n", (int) ioaddr + 0xC80, inw(ioaddr + EISA_ID));
+ TLAN_DBG(TLAN_DEBUG_PROBE,"EISA_ID 0x%4x: 0x%4x\n", (int) ioaddr + 0xC82, inw(ioaddr + EISA_ID2));
+
+
+ TLAN_DBG(TLAN_DEBUG_PROBE, "Probing for EISA adapter at IO: 0x%4x : ",
+ (int) ioaddr);
+ if (request_region(ioaddr, 0x10, TLanSignature) == NULL)
+ goto out;
+
+ if (inw(ioaddr + EISA_ID) != 0x110E) {
+ release_region(ioaddr, 0x10);
+ goto out;
+ }
+
+ device_id = inw(ioaddr + EISA_ID2);
+ if (device_id != 0x20F1 && device_id != 0x40F1) {
+ release_region (ioaddr, 0x10);
+ goto out;
+ }
+
+ if (inb(ioaddr + EISA_CR) != 0x1) { /* Check if adapter is enabled */
+ release_region (ioaddr, 0x10);
+ goto out2;
+ }
+
+ if (debug == 0x10)
+ printk("Found one\n");
+
+
+ /* Get irq from board */
+ switch (inb(ioaddr + 0xCC0)) {
+ case(0x10):
+ irq=5;
+ break;
+ case(0x20):
+ irq=9;
+ break;
+ case(0x40):
+ irq=10;
+ break;
+ case(0x80):
+ irq=11;
+ break;
+ default:
+ goto out;
+ }
+
+
+ /* Setup the newly found eisa adapter */
+ rc = TLan_probe1( NULL, ioaddr, irq,
+ 12, NULL);
+ continue;
+
+ out:
+ if (debug == 0x10)
+ printk("None found\n");
+ continue;
+
+ out2: if (debug == 0x10)
+ printk("Card found but it is not enabled, skipping\n");
+ continue;
+
+ }
+
+} /* TLan_EisaProbe */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void TLan_Poll(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ TLan_HandleInterrupt(dev->irq, dev, NULL);
+ enable_irq(dev->irq);
+}
+#endif
+
+
+
+
+ /***************************************************************
+ * TLan_Init
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev The structure of the device to be
+ * init'ed.
+ *
+ * This function completes the initialization of the
+ * device structure and driver. It reserves the IO
+ * addresses, allocates memory for the lists and bounce
+ * buffers, retrieves the MAC address from the eeprom
+ * and assignes the device's methods.
+ *
+ **************************************************************/
+
+static int TLan_Init( struct net_device *dev )
+{
+ int dma_size;
+ int err;
+ int i;
+ TLanPrivateInfo *priv;
+
+ priv = netdev_priv(dev);
+
+ if ( bbuf ) {
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+ } else {
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ * ( sizeof(TLanList) );
+ }
+ priv->dmaStorage = pci_alloc_consistent(priv->pciDev, dma_size, &priv->dmaStorageDMA);
+ priv->dmaSize = dma_size;
+
+ if ( priv->dmaStorage == NULL ) {
+ printk(KERN_ERR "TLAN: Could not allocate lists and buffers for %s.\n",
+ dev->name );
+ return -ENOMEM;
+ }
+ memset( priv->dmaStorage, 0, dma_size );
+ priv->rxList = (TLanList *)
+ ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
+ priv->rxListDMA = ( ( ( (u32) priv->dmaStorageDMA ) + 7 ) & 0xFFFFFFF8 );
+ priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
+ priv->txListDMA = priv->rxListDMA + sizeof(TLanList) * TLAN_NUM_RX_LISTS;
+ if ( bbuf ) {
+ priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+ priv->rxBufferDMA =priv->txListDMA + sizeof(TLanList) * TLAN_NUM_TX_LISTS;
+ priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+ priv->txBufferDMA = priv->rxBufferDMA + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+ }
+
+ err = 0;
+ for ( i = 0; i < 6 ; i++ )
+ err |= TLan_EeReadByte( dev,
+ (u8) priv->adapter->addrOfs + i,
+ (u8 *) &dev->dev_addr[i] );
+ if ( err ) {
+ printk(KERN_ERR "TLAN: %s: Error reading MAC from eeprom: %d\n",
+ dev->name,
+ err );
+ }
+ dev->addr_len = 6;
+
+ netif_carrier_off(dev);
+
+ /* Device methods */
+ dev->open = &TLan_Open;
+ dev->hard_start_xmit = &TLan_StartTx;
+ dev->stop = &TLan_Close;
+ dev->get_stats = &TLan_GetStats;
+ dev->set_multicast_list = &TLan_SetMulticastList;
+ dev->do_ioctl = &TLan_ioctl;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = &TLan_Poll;
+#endif
+ dev->tx_timeout = &TLan_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ return 0;
+
+} /* TLan_Init */
+
+
+
+
+ /***************************************************************
+ * TLan_Open
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev Structure of device to be opened.
+ *
+ * This routine puts the driver and TLAN adapter in a
+ * state where it is ready to send and receive packets.
+ * It allocates the IRQ, resets and brings the adapter
+ * out of reset, and allows interrupts. It also delays
+ * the startup for autonegotiation or sends a Rx GO
+ * command to the adapter, as appropriate.
+ *
+ **************************************************************/
+
+static int TLan_Open( struct net_device *dev )
+{
+ TLanPrivateInfo *priv = netdev_priv(dev);
+ int err;
+
+ priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+
+ if ( err ) {
+ printk(KERN_ERR "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
+ return err;
+ }
+
+ init_timer(&priv->timer);
+ netif_start_queue(dev);
+
+ /* NOTE: It might not be necessary to read the stats before a
+ reset if you don't care what the values are.
+ */
+ TLan_ResetLists( dev );
+ TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+ TLan_ResetAdapter( dev );
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "%s: Opened. TLAN Chip Rev: %x\n", dev->name, priv->tlanRev );
+
+ return 0;
+
+} /* TLan_Open */
+
+
+
+ /**************************************************************
+ * TLan_ioctl
+ *
+ * Returns:
+ * 0 on success, error code otherwise
+ * Params:
+ * dev structure of device to receive ioctl.
+ *
+ * rq ifreq structure to hold userspace data.
+ *
+ * cmd ioctl command.
+ *
+ *
+ *************************************************************/
+
+static int TLan_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ TLanPrivateInfo *priv = netdev_priv(dev);
+ struct mii_ioctl_data *data = if_mii(rq);
+ u32 phy = priv->phy[priv->phyNum];
+
+ if (!priv->phyOnline)
+ return -EAGAIN;
+
+ switch(cmd) {
+ case SIOCGMIIPHY: /* Get address of MII PHY in use. */
+ data->phy_id = phy;
+
+
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ TLan_MiiReadReg(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, &data->val_out);
+ return 0;
+
+
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ TLan_MiiWriteReg(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+} /* tlan_ioctl */
+
+
+ /***************************************************************
+ * TLan_tx_timeout
+ *
+ * Returns: nothing
+ *
+ * Params:
+ * dev structure of device which timed out
+ * during transmit.
+ *
+ **************************************************************/
+
+static void TLan_tx_timeout(struct net_device *dev)
+{
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "%s: Transmit timed out.\n", dev->name);
+
+ /* Ok so we timed out, lets see what we can do about it...*/
+ TLan_FreeLists( dev );
+ TLan_ResetLists( dev );
+ TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+ TLan_ResetAdapter( dev );
+ dev->trans_start = jiffies;
+ netif_wake_queue( dev );
+
+}
+
+
+
+ /***************************************************************
+ * TLan_StartTx
+ *
+ * Returns:
+ * 0 on success, non-zero on failure.
+ * Parms:
+ * skb A pointer to the sk_buff containing the
+ * frame to be sent.
+ * dev The device to send the data on.
+ *
+ * This function adds a frame to the Tx list to be sent
+ * ASAP. First it verifies that the adapter is ready and
+ * there is room in the queue. Then it sets up the next
+ * available list, copies the frame to the corresponding
+ * buffer. If the adapter Tx channel is idle, it gives
+ * the adapter a Tx Go command on the list, otherwise it
+ * sets the forward address of the previous list to point
+ * to this one. Then it frees the sk_buff.
+ *
+ **************************************************************/
+
+static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev )
+{
+ TLanPrivateInfo *priv = netdev_priv(dev);
+ TLanList *tail_list;
+ dma_addr_t tail_list_phys;
+ u8 *tail_buffer;
+ int pad;
+ unsigned long flags;
+
+ if ( ! priv->phyOnline ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TRANSMIT: %s PHY is not ready\n", dev->name );
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+ tail_list_phys = priv->txListDMA + sizeof(TLanList) * priv->txTail;
+
+ if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
+ netif_stop_queue(dev);
+ priv->txBusyCount++;
+ return 1;
+ }
+
+ tail_list->forward = 0;
+
+ if ( bbuf ) {
+ tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+ memcpy( tail_buffer, skb->data, skb->len );
+ } else {
+ tail_list->buffer[0].address = pci_map_single(priv->pciDev, skb->data, skb->len, PCI_DMA_TODEVICE);
+ TLan_StoreSKB(tail_list, skb);
+ }
+
+ pad = TLAN_MIN_FRAME_SIZE - skb->len;
+
+ if ( pad > 0 ) {
+ tail_list->frameSize = (u16) skb->len