linux/drivers/net/ethernet/amd/nmclan_cs.c

/* ----------------------------------------------------------------------------
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();