/* ---------------------------------------------------------------------------- Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN. nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media Access Controller for Ethernet (MACE). It is essentially the Am2150 PCMCIA Ethernet card contained in the Am2150 Demo Kit. Written by Roger C. Pao <[email protected]> Copyright 1995 Roger C. Pao Linux 2.5 cleanups Copyright Red Hat 2003 This software may be used and distributed according to the terms of the GNU General Public License. Ported to Linux 1.3.* network driver environment by Matti Aarnio <[email protected]> References Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993 Am79C940 (MACE) Data Sheet, 1994 Am79C90 (C-LANCE) Data Sheet, 1994 Linux PCMCIA Programmer's Guide v1.17 /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8 Eric Mears, New Media Corporation Tom Pollard, New Media Corporation Dean Siasoyco, New Media Corporation Ken Lesniak, Silicon Graphics, Inc. <[email protected]> Donald Becker <[email protected]> David Hinds <[email protected]> The Linux client driver is based on the 3c589_cs.c client driver by David Hinds. The Linux network driver outline is based on the 3c589_cs.c driver, the 8390.c driver, and the example skeleton.c kernel code, which are by Donald Becker. The Am2150 network driver hardware interface code is based on the OS/9000 driver for the New Media Ethernet LAN by Eric Mears. Special thanks for testing and help in debugging this driver goes to Ken Lesniak. ------------------------------------------------------------------------------- Driver Notes and Issues ------------------------------------------------------------------------------- 1. Developed on a Dell 320SLi PCMCIA Card Services 2.6.2 Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386 2. rc.pcmcia may require loading pcmcia_core with io_speed=300: 'insmod pcmcia_core.o io_speed=300'. This will avoid problems with fast systems which causes rx_framecnt to return random values. 3. If hot extraction does not work for you, use 'ifconfig eth0 down' before extraction. 4. There is a bad slow-down problem in this driver. 5. Future: Multicast processing. In the meantime, do _not_ compile your kernel with multicast ip enabled. ------------------------------------------------------------------------------- History ------------------------------------------------------------------------------- Log: nmclan_cs.c,v * 2.5.75-ac1 2003/07/11 Alan Cox <[email protected]> * Fixed hang on card eject as we probe it * Cleaned up to use new style locking. * * Revision 0.16 1995/07/01 06:42:17 rpao * Bug fix: nmclan_reset() called CardServices incorrectly. * * Revision 0.15 1995/05/24 08:09:47 rpao * Re-implement MULTI_TX dev->tbusy handling. * * Revision 0.14 1995/05/23 03:19:30 rpao * Added, in nmclan_config(), "tuple.Attributes = 0;". * Modified MACE ID check to ignore chip revision level. * Avoid tx_free_frames race condition between _start_xmit and _interrupt. * * Revision 0.13 1995/05/18 05:56:34 rpao * Statistics changes. * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list. * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup. * * Revision 0.12 1995/05/14 00:12:23 rpao * Statistics overhaul. * 95/05/13 rpao V0.10a Bug fix: MACE statistics counters used wrong I/O ports. Bug fix: mace_interrupt() needed to allow statistics to be processed without RX or TX interrupts pending. 95/05/11 rpao V0.10 Multiple transmit request processing. Modified statistics to use MACE counters where possible. 95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO. *Released 95/05/10 rpao V0.08 Bug fix: Make all non-exported functions private by using static keyword. Bug fix: Test IntrCnt _before_ reading MACE_IR. 95/05/10 rpao V0.07 Statistics. 95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states. ---------------------------------------------------------------------------- */ #define pr_fmt(fmt) … #define DRV_NAME … /* ---------------------------------------------------------------------------- Conditional Compilation Options ---------------------------------------------------------------------------- */ #define MULTI_TX … #define RESET_ON_TIMEOUT … #define TX_INTERRUPTABLE … #define RESET_XILINX … /* ---------------------------------------------------------------------------- Include Files ---------------------------------------------------------------------------- */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/in.h> #include <linux/delay.h> #include <linux/ethtool.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/ioport.h> #include <linux/bitops.h> #include <pcmcia/cisreg.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> #include <linux/uaccess.h> #include <asm/io.h> /* ---------------------------------------------------------------------------- Defines ---------------------------------------------------------------------------- */ #define MACE_LADRF_LEN … /* 8 bytes in Logical Address Filter */ /* Loop Control Defines */ #define MACE_MAX_IR_ITERATIONS … #define MACE_MAX_RX_ITERATIONS … /* TBD: Dean brought this up, and I assumed the hardware would handle it: If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be non-zero when the isr exits. We may not get another interrupt to process the remaining packets for some time. */ /* The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA) which manages the interface between the MACE and the PCMCIA bus. It also includes buffer management for the 32K x 8 SRAM to control up to four transmit and 12 receive frames at a time. */ #define AM2150_MAX_TX_FRAMES … #define AM2150_MAX_RX_FRAMES … /* Am2150 Ethernet Card I/O Mapping */ #define AM2150_RCV … #define AM2150_XMT … #define AM2150_XMT_SKIP … #define AM2150_RCV_NEXT … #define AM2150_RCV_FRAME_COUNT … #define AM2150_MACE_BANK … #define AM2150_MACE_BASE … /* MACE Registers */ #define MACE_RCVFIFO … #define MACE_XMTFIFO … #define MACE_XMTFC … #define MACE_XMTFS … #define MACE_XMTRC … #define MACE_RCVFC … #define MACE_RCVFS … #define MACE_FIFOFC … #define MACE_IR … #define MACE_IMR … #define MACE_PR … #define MACE_BIUCC … #define MACE_FIFOCC … #define MACE_MACCC … #define MACE_PLSCC … #define MACE_PHYCC … #define MACE_CHIPIDL … #define MACE_CHIPIDH … #define MACE_IAC … /* Reserved */ #define MACE_LADRF … #define MACE_PADR … /* Reserved */ /* Reserved */ #define MACE_MPC … /* Reserved */ #define MACE_RNTPC … #define MACE_RCVCC … /* Reserved */ #define MACE_UTR … #define MACE_RTR1 … #define MACE_RTR2 … /* MACE Bit Masks */ #define MACE_XMTRC_EXDEF … #define MACE_XMTRC_XMTRC … #define MACE_XMTFS_XMTSV … #define MACE_XMTFS_UFLO … #define MACE_XMTFS_LCOL … #define MACE_XMTFS_MORE … #define MACE_XMTFS_ONE … #define MACE_XMTFS_DEFER … #define MACE_XMTFS_LCAR … #define MACE_XMTFS_RTRY … #define MACE_RCVFS_RCVSTS … #define MACE_RCVFS_OFLO … #define MACE_RCVFS_CLSN … #define MACE_RCVFS_FRAM … #define MACE_RCVFS_FCS … #define MACE_FIFOFC_RCVFC … #define MACE_FIFOFC_XMTFC … #define MACE_IR_JAB … #define MACE_IR_BABL … #define MACE_IR_CERR … #define MACE_IR_RCVCCO … #define MACE_IR_RNTPCO … #define MACE_IR_MPCO … #define MACE_IR_RCVINT … #define MACE_IR_XMTINT … #define MACE_MACCC_PROM … #define MACE_MACCC_DXMT2PD … #define MACE_MACCC_EMBA … #define MACE_MACCC_RESERVED … #define MACE_MACCC_DRCVPA … #define MACE_MACCC_DRCVBC … #define MACE_MACCC_ENXMT … #define MACE_MACCC_ENRCV … #define MACE_PHYCC_LNKFL … #define MACE_PHYCC_DLNKTST … #define MACE_PHYCC_REVPOL … #define MACE_PHYCC_DAPC … #define MACE_PHYCC_LRT … #define MACE_PHYCC_ASEL … #define MACE_PHYCC_RWAKE … #define MACE_PHYCC_AWAKE … #define MACE_IAC_ADDRCHG … #define MACE_IAC_PHYADDR … #define MACE_IAC_LOGADDR … #define MACE_UTR_RTRE … #define MACE_UTR_RTRD … #define MACE_UTR_RPA … #define MACE_UTR_FCOLL … #define MACE_UTR_RCVFCSE … #define MACE_UTR_LOOP_INCL_MENDEC … #define MACE_UTR_LOOP_NO_MENDEC … #define MACE_UTR_LOOP_EXTERNAL … #define MACE_UTR_LOOP_NONE … #define MACE_UTR_RESERVED … /* Switch MACE register bank (only 0 and 1 are valid) */ #define MACEBANK(win_num) … #define MACE_IMR_DEFAULT … #undef MACE_IMR_DEFAULT #define MACE_IMR_DEFAULT … #define TX_TIMEOUT … /* ---------------------------------------------------------------------------- Type Definitions ---------------------------------------------------------------------------- */ mace_statistics; mace_private; /* ---------------------------------------------------------------------------- Private Global Variables ---------------------------------------------------------------------------- */ static const char *if_names[]= …; /* ---------------------------------------------------------------------------- Parameters These are the parameters that can be set during loading with 'insmod'. ---------------------------------------------------------------------------- */ MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; #define INT_MODULE_PARM(n, v) … /* 0=auto, 1=10baseT, 2 = 10base2, default=auto */ INT_MODULE_PARM(if_port, 0); /* ---------------------------------------------------------------------------- Function Prototypes ---------------------------------------------------------------------------- */ static int nmclan_config(struct pcmcia_device *link); static void nmclan_release(struct pcmcia_device *link); static void nmclan_reset(struct net_device *dev); static int mace_config(struct net_device *dev, struct ifmap *map); static int mace_open(struct net_device *dev); static int mace_close(struct net_device *dev); static netdev_tx_t mace_start_xmit(struct sk_buff *skb, struct net_device *dev); static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue); static irqreturn_t mace_interrupt(int irq, void *dev_id); static struct net_device_stats *mace_get_stats(struct net_device *dev); static int mace_rx(struct net_device *dev, unsigned char RxCnt); static void restore_multicast_list(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static const struct ethtool_ops netdev_ethtool_ops; static void nmclan_detach(struct pcmcia_device *p_dev); static const struct net_device_ops mace_netdev_ops = …; static int nmclan_probe(struct pcmcia_device *link) { … } /* nmclan_attach */ static void nmclan_detach(struct pcmcia_device *link) { … } /* nmclan_detach */ /* ---------------------------------------------------------------------------- mace_read Reads a MACE register. This is bank independent; however, the caller must ensure that this call is not interruptable. We are assuming that during normal operation, the MACE is always in bank 0. ---------------------------------------------------------------------------- */ static int mace_read(mace_private *lp, unsigned int ioaddr, int reg) { … } /* mace_read */ /* ---------------------------------------------------------------------------- mace_write Writes to a MACE register. This is bank independent; however, the caller must ensure that this call is not interruptable. We are assuming that during normal operation, the MACE is always in bank 0. ---------------------------------------------------------------------------- */ static void mace_write(mace_private *lp, unsigned int ioaddr, int reg, int data) { … } /* mace_write */ /* ---------------------------------------------------------------------------- mace_init Resets the MACE chip. ---------------------------------------------------------------------------- */ static int mace_init(mace_private *lp, unsigned int ioaddr, const char *enet_addr) { … } /* mace_init */ static int nmclan_config(struct pcmcia_device *link) { … } /* nmclan_config */ static void nmclan_release(struct pcmcia_device *link) { … } static int nmclan_suspend(struct pcmcia_device *link) { … } static int nmclan_resume(struct pcmcia_device *link) { … } /* ---------------------------------------------------------------------------- nmclan_reset Reset and restore all of the Xilinx and MACE registers. ---------------------------------------------------------------------------- */ static void nmclan_reset(struct net_device *dev) { … } /* nmclan_reset */ /* ---------------------------------------------------------------------------- mace_config [Someone tell me what this is supposed to do? Is if_port a defined standard? If so, there should be defines to indicate 1=10Base-T, 2=10Base-2, etc. including limited automatic detection.] ---------------------------------------------------------------------------- */ static int mace_config(struct net_device *dev, struct ifmap *map) { … } /* mace_config */ /* ---------------------------------------------------------------------------- mace_open Open device driver. ---------------------------------------------------------------------------- */ static int mace_open(struct net_device *dev) { … } /* mace_open */ /* ---------------------------------------------------------------------------- mace_close Closes device driver. ---------------------------------------------------------------------------- */ static int mace_close(struct net_device *dev) { … } /* mace_close */ static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { … } static const struct ethtool_ops netdev_ethtool_ops = …; /* ---------------------------------------------------------------------------- mace_start_xmit This routine begins the packet transmit function. When completed, it will generate a transmit interrupt. According to /usr/src/linux/net/inet/dev.c, if _start_xmit returns 0, the "packet is now solely the responsibility of the driver." If _start_xmit returns non-zero, the "transmission failed, put skb back into a list." ---------------------------------------------------------------------------- */ static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue) { … } static netdev_tx_t mace_start_xmit(struct sk_buff *skb, struct net_device *dev) { … } /* mace_start_xmit */ /* ---------------------------------------------------------------------------- mace_interrupt The interrupt handler. ---------------------------------------------------------------------------- */ static irqreturn_t mace_interrupt(int irq, void *dev_id) { … } /* mace_interrupt */ /* ---------------------------------------------------------------------------- mace_rx Receives packets. ---------------------------------------------------------------------------- */ static int mace_rx(struct net_device *dev, unsigned char RxCnt) { … } /* mace_rx */ /* ---------------------------------------------------------------------------- pr_linux_stats ---------------------------------------------------------------------------- */ static void pr_linux_stats(struct net_device_stats *pstats) { … } /* pr_linux_stats */ /* ---------------------------------------------------------------------------- pr_mace_stats ---------------------------------------------------------------------------- */ static void pr_mace_stats(mace_statistics *pstats) { … } /* pr_mace_stats */ /* ---------------------------------------------------------------------------- update_stats Update statistics. We change to register window 1, so this should be run single-threaded if the device is active. This is expected to be a rare operation, and it's simpler for the rest of the driver to assume that window 0 is always valid rather than use a special window-state variable. oflo & uflo should _never_ occur since it would mean the Xilinx was not able to transfer data between the MACE FIFO and the card's SRAM fast enough. If this happens, something is seriously wrong with the hardware. ---------------------------------------------------------------------------- */ static void update_stats(unsigned int ioaddr, struct net_device *dev) { … } /* update_stats */ /* ---------------------------------------------------------------------------- mace_get_stats Gathers ethernet statistics from the MACE chip. ---------------------------------------------------------------------------- */ static struct net_device_stats *mace_get_stats(struct net_device *dev) { … } /* net_device_stats */ /* ---------------------------------------------------------------------------- updateCRC Modified from Am79C90 data sheet. ---------------------------------------------------------------------------- */ #ifdef BROKEN_MULTICAST static void updateCRC(int *CRC, int bit) { static const int poly[]={ 1,1,1,0, 1,1,0,1, 1,0,1,1, 1,0,0,0, 1,0,0,0, 0,0,1,1, 0,0,1,0, 0,0,0,0 }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the CRC generator polynomial. */ int j; /* shift CRC and control bit (CRC[32]) */ for (j = 32; j > 0; j--) CRC[j] = CRC[j-1]; CRC[0] = 0; /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */ if (bit ^ CRC[32]) for (j = 0; j < 32; j++) CRC[j] ^= poly[j]; } /* updateCRC */ /* ---------------------------------------------------------------------------- BuildLAF Build logical address filter. Modified from Am79C90 data sheet. Input ladrf: logical address filter (contents initialized to 0) adr: ethernet address ---------------------------------------------------------------------------- */ static void BuildLAF(int *ladrf, int *adr) { int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */ int i, byte; /* temporary array indices */ int hashcode; /* the output object */ CRC[32]=0; for (byte = 0; byte < 6; byte++) for (i = 0; i < 8; i++) updateCRC(CRC, (adr[byte] >> i) & 1); hashcode = 0; for (i = 0; i < 6; i++) hashcode = (hashcode << 1) + CRC[i]; byte = hashcode >> 3; ladrf[byte] |= (1 << (hashcode & 7)); #ifdef PCMCIA_DEBUG if (0) printk(KERN_DEBUG " adr =%pM\n", adr); printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode); for (i = 0; i < 8; i++) pr_cont(" %02X", ladrf[i]); pr_cont("\n"); #endif } /* BuildLAF */ /* ---------------------------------------------------------------------------- restore_multicast_list Restores the multicast filter for MACE chip to the last set_multicast_list() call. Input multicast_num_addrs multicast_ladrf[] ---------------------------------------------------------------------------- */ static void restore_multicast_list(struct net_device *dev) { mace_private *lp = netdev_priv(dev); int num_addrs = lp->multicast_num_addrs; int *ladrf = lp->multicast_ladrf; unsigned int ioaddr = dev->base_addr; int i; pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name, num_addrs); if (num_addrs > 0) { pr_debug("Attempt to restore multicast list detected.\n"); mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); /* Poll ADDRCHG bit */ while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) ; /* Set LADRF register */ for (i = 0; i < MACE_LADRF_LEN; i++) mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]); mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } else if (num_addrs < 0) { /* Promiscuous mode: receive all packets */ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV ); } else { /* Normal mode */ mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); } } /* restore_multicast_list */ /* ---------------------------------------------------------------------------- set_multicast_list Set or clear the multicast filter for this adaptor. Input num_addrs == -1 Promiscuous mode, receive all packets num_addrs == 0 Normal mode, clear multicast list num_addrs > 0 Multicast mode, receive normal and MC packets, and do best-effort filtering. Output multicast_num_addrs multicast_ladrf[] ---------------------------------------------------------------------------- */ static void set_multicast_list(struct net_device *dev) { mace_private *lp = netdev_priv(dev); int adr[ETH_ALEN] = {0}; /* Ethernet address */ struct netdev_hw_addr *ha; #ifdef PCMCIA_DEBUG { static int old; if (netdev_mc_count(dev) != old) { old = netdev_mc_count(dev); pr_debug("%s: setting Rx mode to %d addresses.\n", dev->name, old); } } #endif /* Set multicast_num_addrs. */ lp->multicast_num_addrs = netdev_mc_count(dev); /* Set multicast_ladrf. */ if (num_addrs > 0) { /* Calculate multicast logical address filter */ memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN); netdev_for_each_mc_addr(ha, dev) { memcpy(adr, ha->addr, ETH_ALEN); BuildLAF(lp->multicast_ladrf, adr); } } restore_multicast_list(dev); } /* set_multicast_list */ #endif /* BROKEN_MULTICAST */ static void restore_multicast_list(struct net_device *dev) { … } /* restore_multicast_list */ static void set_multicast_list(struct net_device *dev) { … } /* set_multicast_list */ static const struct pcmcia_device_id nmclan_ids[] = …; MODULE_DEVICE_TABLE(pcmcia, nmclan_ids); static struct pcmcia_driver nmclan_cs_driver = …; module_pcmcia_driver(…);