summaryrefslogtreecommitdiffstats
path: root/drivers/net/sk_mca.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 00:20:36 +0200
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 00:20:36 +0200
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/sk_mca.c
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
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/sk_mca.c')
-rw-r--r--drivers/net/sk_mca.c1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c
new file mode 100644
index 000000000000..4c56b8d8221b
--- /dev/null
+++ b/drivers/net/sk_mca.c
@@ -0,0 +1,1217 @@
+/*
+net-3-driver for the SKNET MCA-based cards
+
+This is an extension to the Linux operating system, and is covered by the
+same GNU General Public License that covers that work.
+
+Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de,
+ alfred.arnold@lancom.de)
+
+This driver is based both on the 3C523 driver and the SK_G16 driver.
+
+paper sources:
+ 'PC Hardware: Aufbau, Funktionsweise, Programmierung' by
+ Hans-Peter Messmer for the basic Microchannel stuff
+
+ 'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer
+ for help on Ethernet driver programming
+
+ 'Ethernet/IEEE 802.3 Family 1992 World Network Data Book/Handbook' by AMD
+ for documentation on the AM7990 LANCE
+
+ 'SKNET Personal Technisches Manual', Version 1.2 by Schneider&Koch
+ for documentation on the Junior board
+
+ 'SK-NET MC2+ Technical Manual", Version 1.1 by Schneider&Koch for
+ documentation on the MC2 bord
+
+ A big thank you to the S&K support for providing me so quickly with
+ documentation!
+
+ Also see http://www.syskonnect.com/
+
+ Missing things:
+
+ -> set debug level via ioctl instead of compile-time switches
+ -> I didn't follow the development of the 2.1.x kernels, so my
+ assumptions about which things changed with which kernel version
+ are probably nonsense
+
+History:
+ May 16th, 1999
+ startup
+ May 22st, 1999
+ added private structure, methods
+ begun building data structures in RAM
+ May 23nd, 1999
+ can receive frames, send frames
+ May 24th, 1999
+ modularized initialization of LANCE
+ loadable as module
+ still Tx problem :-(
+ May 26th, 1999
+ MC2 works
+ support for multiple devices
+ display media type for MC2+
+ May 28th, 1999
+ fixed problem in GetLANCE leaving interrupts turned off
+ increase TX queue to 4 packets to improve send performance
+ May 29th, 1999
+ a few corrections in statistics, caught rcvr overruns
+ reinitialization of LANCE/board in critical situations
+ MCA info implemented
+ implemented LANCE multicast filter
+ Jun 6th, 1999
+ additions for Linux 2.2
+ Dec 25th, 1999
+ unfortunately there seem to be newer MC2+ boards that react
+ on IRQ 3/5/9/10 instead of 3/5/10/11, so we have to autoprobe
+ in questionable cases...
+ Dec 28th, 1999
+ integrated patches from David Weinehall & Bill Wendling for 2.3
+ kernels (isa_...functions). Things are defined in a way that
+ it still works with 2.0.x 8-)
+ Dec 30th, 1999
+ added handling of the remaining interrupt conditions. That
+ should cure the spurious hangs.
+ Jan 30th, 2000
+ newer kernels automatically probe more than one board, so the
+ 'startslot' as a variable is also needed here
+ June 1st, 2000
+ added changes for recent 2.3 kernels
+
+ *************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/mca-legacy.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/processor.h>
+#include <asm/io.h>
+
+#define _SK_MCA_DRIVER_
+#include "sk_mca.h"
+
+/* ------------------------------------------------------------------------
+ * global static data - not more since we can handle multiple boards and
+ * have to pack all state info into the device struct!
+ * ------------------------------------------------------------------------ */
+
+static char *MediaNames[Media_Count] =
+ { "10Base2", "10BaseT", "10Base5", "Unknown" };
+
+static unsigned char 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
+};
+
+/* ------------------------------------------------------------------------
+ * private subfunctions
+ * ------------------------------------------------------------------------ */
+
+/* dump parts of shared memory - only needed during debugging */
+
+#ifdef DEBUG
+static void dumpmem(struct net_device *dev, u32 start, u32 len)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ int z;
+
+ for (z = 0; z < len; z++) {
+ if ((z & 15) == 0)
+ printk("%04x:", z);
+ printk(" %02x", readb(priv->base + start + z));
+ if ((z & 15) == 15)
+ printk("\n");
+ }
+}
+
+/* print exact time - ditto */
+
+static void PrTime(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ printk("%9d:%06d: ", tv.tv_sec, tv.tv_usec);
+}
+#endif
+
+/* deduce resources out of POS registers */
+
+static void __init getaddrs(int slot, int junior, int *base, int *irq,
+ skmca_medium * medium)
+{
+ u_char pos0, pos1, pos2;
+
+ if (junior) {
+ pos0 = mca_read_stored_pos(slot, 2);
+ *base = ((pos0 & 0x0e) << 13) + 0xc0000;
+ *irq = ((pos0 & 0x10) >> 4) + 10;
+ *medium = Media_Unknown;
+ } else {
+ /* reset POS 104 Bits 0+1 so the shared memory region goes to the
+ configured area between 640K and 1M. Afterwards, enable the MC2.
+ I really don't know what rode SK to do this... */
+
+ mca_write_pos(slot, 4,
+ mca_read_stored_pos(slot, 4) & 0xfc);
+ mca_write_pos(slot, 2,
+ mca_read_stored_pos(slot, 2) | 0x01);
+
+ pos1 = mca_read_stored_pos(slot, 3);
+ pos2 = mca_read_stored_pos(slot, 4);
+ *base = ((pos1 & 0x07) << 14) + 0xc0000;
+ switch (pos2 & 0x0c) {
+ case 0:
+ *irq = 3;
+ break;
+ case 4:
+ *irq = 5;
+ break;
+ case 8:
+ *irq = -10;
+ break;
+ case 12:
+ *irq = -11;
+ break;
+ }
+ *medium = (pos2 >> 6) & 3;
+ }
+}
+
+/* check for both cards:
+ When the MC2 is turned off, it was configured for more than 15MB RAM,
+ is disabled and won't get detected using the standard probe. We
+ therefore have to scan the slots manually :-( */
+
+static int __init dofind(int *junior, int firstslot)
+{
+ int slot;
+ unsigned int id;
+
+ for (slot = firstslot; slot < MCA_MAX_SLOT_NR; slot++) {
+ id = mca_read_stored_pos(slot, 0)
+ + (((unsigned int) mca_read_stored_pos(slot, 1)) << 8);
+
+ *junior = 0;
+ if (id == SKNET_MCA_ID)
+ return slot;
+ *junior = 1;
+ if (id == SKNET_JUNIOR_MCA_ID)
+ return slot;
+ }
+ return MCA_NOTFOUND;
+}
+
+/* reset the whole board */
+
+static void ResetBoard(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+
+ writeb(CTRL_RESET_ON, priv->ctrladdr);
+ udelay(10);
+ writeb(CTRL_RESET_OFF, priv->ctrladdr);
+}
+
+/* wait for LANCE interface to become not busy */
+
+static int WaitLANCE(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ int t = 0;
+
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) ==
+ STAT_IO_BUSY) {
+ udelay(1);
+ if (++t > 1000) {
+ printk("%s: LANCE access timeout", dev->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* set LANCE register - must be atomic */
+
+static void SetLANCE(struct net_device *dev, u16 addr, u16 value)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ /* disable interrupts */
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* wait until no transfer is pending */
+
+ WaitLANCE(dev);
+
+ /* transfer register address to RAP */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr);
+ writew(addr, priv->ioregaddr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ WaitLANCE(dev);
+
+ /* transfer data to register */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA, priv->ctrladdr);
+ writew(value, priv->ioregaddr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ WaitLANCE(dev);
+
+ /* reenable interrupts */
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/* get LANCE register */
+
+static u16 GetLANCE(struct net_device *dev, u16 addr)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int res;
+
+ /* disable interrupts */
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* wait until no transfer is pending */
+
+ WaitLANCE(dev);
+
+ /* transfer register address to RAP */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr);
+ writew(addr, priv->ioregaddr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ WaitLANCE(dev);
+
+ /* transfer data from register */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA, priv->ctrladdr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ WaitLANCE(dev);
+ res = readw(priv->ioregaddr);
+
+ /* reenable interrupts */
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return res;
+}
+
+/* build up descriptors in shared RAM */
+
+static void InitDscrs(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ u32 bufaddr;
+
+ /* Set up Tx descriptors. The board has only 16K RAM so bits 16..23
+ are always 0. */
+
+ bufaddr = RAM_DATABASE;
+ {
+ LANCE_TxDescr descr;
+ int z;
+
+ for (z = 0; z < TXCOUNT; z++) {
+ descr.LowAddr = bufaddr;
+ descr.Flags = 0;
+ descr.Len = 0xf000;
+ descr.Status = 0;
+ memcpy_toio(priv->base + RAM_TXBASE +
+ (z * sizeof(LANCE_TxDescr)), &descr,
+ sizeof(LANCE_TxDescr));
+ memset_io(priv->base + bufaddr, 0, RAM_BUFSIZE);
+ bufaddr += RAM_BUFSIZE;
+ }
+ }
+
+ /* do the same for the Rx descriptors */
+
+ {
+ LANCE_RxDescr descr;
+ int z;
+
+ for (z = 0; z < RXCOUNT; z++) {
+ descr.LowAddr = bufaddr;
+ descr.Flags = RXDSCR_FLAGS_OWN;
+ descr.MaxLen = -RAM_BUFSIZE;
+ descr.Len = 0;
+ memcpy_toio(priv->base + RAM_RXBASE +
+ (z * sizeof(LANCE_RxDescr)), &descr,
+ sizeof(LANCE_RxDescr));
+ memset_io(priv->base + bufaddr, 0, RAM_BUFSIZE);
+ bufaddr += RAM_BUFSIZE;
+ }
+ }
+}
+
+/* calculate the hash bit position for a given multicast address
+ taken more or less directly from the AMD datasheet... */
+
+static void UpdateCRC(unsigned char *CRC, int bit)
+{
+ int j;
+
+ /* shift CRC one bit */
+
+ memmove(CRC + 1, CRC, 32 * sizeof(unsigned char));
+ CRC[0] = 0;
+
+ /* if bit XOR controlbit = 1, set CRC = CRC XOR polynomial */
+
+ if (bit ^ CRC[32])
+ for (j = 0; j < 32; j++)
+ CRC[j] ^= poly[j];
+}
+
+static unsigned int GetHash(char *address)
+{
+ unsigned char CRC[33];
+ int i, byte, hashcode;
+
+ /* a multicast address has bit 0 in the first byte set */
+
+ if ((address[0] & 1) == 0)
+ return -1;
+
+ /* initialize CRC */
+
+ memset(CRC, 1, sizeof(CRC));
+
+ /* loop through address bits */
+
+ for (byte = 0; byte < 6; byte++)
+ for (i = 0; i < 8; i++)
+ UpdateCRC(CRC, (address[byte] >> i) & 1);
+
+ /* hashcode is the 6 least significant bits of the CRC */
+
+ hashcode = 0;
+ for (i = 0; i < 6; i++)
+ hashcode = (hashcode << 1) + CRC[i];
+ return hashcode;
+}
+
+/* feed ready-built initialization block into LANCE */
+
+static void InitLANCE(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+
+ /* build up descriptors. */
+
+ InitDscrs(dev);
+
+ /* next RX descriptor to be read is the first one. Since the LANCE
+ will start from the beginning after initialization, we have to
+ reset out pointers too. */
+
+ priv->nextrx = 0;
+
+ /* no TX descriptors active */
+
+ priv->nexttxput = priv->nexttxdone = priv->txbusy = 0;
+
+ /* set up the LANCE bus control register - constant for SKnet boards */
+
+ SetLANCE(dev, LANCE_CSR3,
+ CSR3_BSWAP_OFF | CSR3_ALE_LOW | CSR3_BCON_HOLD);
+
+ /* write address of initialization block into LANCE */
+
+ SetLANCE(dev, LANCE_CSR1, RAM_INITBASE & 0xffff);
+ SetLANCE(dev, LANCE_CSR2, (RAM_INITBASE >> 16) & 0xff);
+
+ /* we don't get ready until the LANCE has read the init block */
+
+ netif_stop_queue(dev);
+
+ /* let LANCE read the initialization block. LANCE is ready
+ when we receive the corresponding interrupt. */
+
+ SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_INIT);
+}
+
+/* stop the LANCE so we can reinitialize it */
+
+static void StopLANCE(struct net_device *dev)
+{
+ /* can't take frames any more */
+
+ netif_stop_queue(dev);
+
+ /* disable interrupts, stop it */
+
+ SetLANCE(dev, LANCE_CSR0, CSR0_STOP);
+}
+
+/* initialize card and LANCE for proper operation */
+
+static void InitBoard(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ LANCE_InitBlock block;
+
+ /* Lay out the shared RAM - first we create the init block for the LANCE.
+ We do not overwrite it later because we need it again when we switch
+ promiscous mode on/off. */
+
+ block.Mode = 0;
+ if (dev->flags & IFF_PROMISC)
+ block.Mode |= LANCE_INIT_PROM;
+ memcpy(block.PAdr, dev->dev_addr, 6);
+ memset(block.LAdrF, 0, sizeof(block.LAdrF));
+ block.RdrP = (RAM_RXBASE & 0xffffff) | (LRXCOUNT << 29);
+ block.TdrP = (RAM_TXBASE & 0xffffff) | (LTXCOUNT << 29);
+
+ memcpy_toio(priv->base + RAM_INITBASE, &block, sizeof(block));
+
+ /* initialize LANCE. Implicitly sets up other structures in RAM. */
+
+ InitLANCE(dev);
+}
+
+/* deinitialize card and LANCE */
+
+static void DeinitBoard(struct net_device *dev)
+{
+ /* stop LANCE */
+
+ StopLANCE(dev);
+
+ /* reset board */
+
+ ResetBoard(dev);
+}
+
+/* probe for device's irq */
+
+static int __init ProbeIRQ(struct net_device *dev)
+{
+ unsigned long imaskval, njiffies, irq;
+ u16 csr0val;
+
+ /* enable all interrupts */
+
+ imaskval = probe_irq_on();
+
+ /* initialize the board. Wait for interrupt 'Initialization done'. */
+
+ ResetBoard(dev);
+ InitBoard(dev);
+
+ njiffies = jiffies + HZ;
+ do {
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+ }
+ while (((csr0val & CSR0_IDON) == 0) && (jiffies != njiffies));
+
+ /* turn of interrupts again */
+
+ irq = probe_irq_off(imaskval);
+
+ /* if we found something, ack the interrupt */
+
+ if (irq)
+ SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_IDON);
+
+ /* back to idle state */
+
+ DeinitBoard(dev);
+
+ return irq;
+}
+
+/* ------------------------------------------------------------------------
+ * interrupt handler(s)
+ * ------------------------------------------------------------------------ */
+
+/* LANCE has read initialization block -> start it */
+
+static u16 irqstart_handler(struct net_device *dev, u16 oldcsr0)
+{
+ /* now we're ready to transmit */
+
+ netif_wake_queue(dev);
+
+ /* reset IDON bit, start LANCE */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_IDON | CSR0_STRT);
+ return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* did we lose blocks due to a FIFO overrun ? */
+
+static u16 irqmiss_handler(struct net_device *dev, u16 oldcsr0)
+{
+ skmca_priv *priv = netdev_priv(dev);
+
+ /* update statistics */
+
+ priv->stat.rx_fifo_errors++;
+
+ /* reset MISS bit */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_MISS);
+ return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* receive interrupt */
+
+static u16 irqrx_handler(struct net_device *dev, u16 oldcsr0)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ LANCE_RxDescr descr;
+ unsigned int descraddr;
+
+ /* run through queue until we reach a descriptor we do not own */
+
+ descraddr = RAM_RXBASE + (priv->nextrx * sizeof(LANCE_RxDescr));
+ while (1) {
+ /* read descriptor */
+ memcpy_fromio(&descr, priv->base + descraddr,
+ sizeof(LANCE_RxDescr));
+
+ /* if we reach a descriptor we do not own, we're done */
+ if ((descr.Flags & RXDSCR_FLAGS_OWN) != 0)
+ break;
+
+#ifdef DEBUG
+ PrTime();
+ printk("Receive packet on descr %d len %d\n", priv->nextrx,
+ descr.Len);
+#endif
+
+ /* erroneous packet ? */
+ if ((descr.Flags & RXDSCR_FLAGS_ERR) != 0) {
+ priv->stat.rx_errors++;
+ if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0)
+ priv->stat.rx_crc_errors++;
+ else if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0)
+ priv->stat.rx_frame_errors++;
+ else if ((descr.Flags & RXDSCR_FLAGS_OFLO) != 0)
+ priv->stat.rx_fifo_errors++;
+ }
+
+ /* good packet ? */
+ else {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(descr.Len + 2);
+ if (skb == NULL)
+ priv->stat.rx_dropped++;
+ else {
+ memcpy_fromio(skb_put(skb, descr.Len),
+ priv->base +
+ descr.LowAddr, descr.Len);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ priv->stat.rx_packets++;
+ priv->stat.rx_bytes += descr.Len;
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ }
+ }
+
+ /* give descriptor back to LANCE */
+ descr.Len = 0;
+ descr.Flags |= RXDSCR_FLAGS_OWN;
+
+ /* update descriptor in shared RAM */
+ memcpy_toio(priv->base + descraddr, &descr,
+ sizeof(LANCE_RxDescr));
+
+ /* go to next descriptor */
+ priv->nextrx++;
+ descraddr += sizeof(LANCE_RxDescr);
+ if (priv->nextrx >= RXCOUNT) {
+ priv->nextrx = 0;
+ descraddr = RAM_RXBASE;
+ }
+ }
+
+ /* reset RINT bit */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_RINT);
+ return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* transmit interrupt */
+
+static u16 irqtx_handler(struct net_device *dev, u16 oldcsr0)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ LANCE_TxDescr descr;
+ unsigned int descraddr;
+
+ /* check descriptors at most until no busy one is left */
+
+ descraddr =
+ RAM_TXBASE + (priv->nexttxdone * sizeof(LANCE_TxDescr));
+ while (priv->txbusy > 0) {
+ /* read descriptor */
+ memcpy_fromio(&descr, priv->base + descraddr,
+ sizeof(LANCE_TxDescr));
+
+ /* if the LANCE still owns this one, we've worked out all sent packets */
+ if ((descr.Flags & TXDSCR_FLAGS_OWN) != 0)
+ break;
+
+#ifdef DEBUG
+ PrTime();
+ printk("Send packet done on descr %d\n", priv->nexttxdone);
+#endif
+
+ /* update statistics */
+ if ((descr.Flags & TXDSCR_FLAGS_ERR) == 0) {
+ priv->stat.tx_packets++;
+ priv->stat.tx_bytes++;
+ } else {
+ priv->stat.tx_errors++;
+ if ((descr.Status & TXDSCR_STATUS_UFLO) != 0) {
+ priv->stat.tx_fifo_errors++;
+ InitLANCE(dev);
+ }
+ else
+ if ((descr.Status & TXDSCR_STATUS_LCOL) !=
+ 0) priv->stat.tx_window_errors++;
+ else if ((descr.Status & TXDSCR_STATUS_LCAR) != 0)
+ priv->stat.tx_carrier_errors++;
+ else if ((descr.Status & TXDSCR_STATUS_RTRY) != 0)
+ priv->stat.tx_aborted_errors++;
+ }
+
+ /* go to next descriptor */
+ priv->nexttxdone++;
+ descraddr += sizeof(LANCE_TxDescr);
+ if (priv->nexttxdone >= TXCOUNT) {
+ priv->nexttxdone = 0;
+ descraddr = RAM_TXBASE;
+ }
+ priv->txbusy--;
+ }
+
+ /* reset TX interrupt bit */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_TINT);
+ oldcsr0 = GetLANCE(dev, LANCE_CSR0);
+
+ /* at least one descriptor is freed. Therefore we can accept
+ a new one */
+ /* inform upper layers we're in business again */
+
+ netif_wake_queue(dev);
+
+ return oldcsr0;
+}
+
+/* general interrupt entry */
+
+static irqreturn_t irq_handler(int irq, void *device, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) device;
+ u16 csr0val;
+
+ /* read CSR0 to get interrupt cause */
+
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+
+ /* in case we're not meant... */
+
+ if ((csr0val & CSR0_INTR) == 0)
+ return IRQ_NONE;
+
+#if 0
+ set_bit(LINK_STATE_RXSEM, &dev->state);
+#endif
+
+ /* loop through the interrupt bits until everything is clear */
+
+ do {
+ if ((csr0val & CSR0_IDON) != 0)
+ csr0val = irqstart_handler(dev, csr0val);
+ if ((csr0val & CSR0_RINT) != 0)
+ csr0val = irqrx_handler(dev, csr0val);
+ if ((csr0val & CSR0_MISS) != 0)
+ csr0val = irqmiss_handler(dev, csr0val);
+ if ((csr0val & CSR0_TINT) != 0)
+ csr0val = irqtx_handler(dev, csr0val);
+ if ((csr0val & CSR0_MERR) != 0) {
+ SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_MERR);
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+ }
+ if ((csr0val & CSR0_BABL) != 0) {
+ SetLANCE(dev, LANCE_CSR0, csr0val | CSR0_BABL);
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+ }
+ }
+ while ((csr0val & CSR0_INTR) != 0);
+
+#if 0
+ clear_bit(LINK_STATE_RXSEM, &dev->state);
+#endif
+ return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------------------
+ * driver methods
+ * ------------------------------------------------------------------------ */
+
+/* MCA info */
+
+static int skmca_getinfo(char *buf, int slot, void *d)
+{
+ int len = 0, i;
+ struct net_device *dev = (struct net_device *) d;
+ skmca_priv *priv;
+
+ /* can't say anything about an uninitialized device... */
+
+ if (dev == NULL)
+ return len;
+ priv = netdev_priv(dev);
+
+ /* print info */
+
+ len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);
+ len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start,
+ dev->mem_end - 1);
+ len +=
+ sprintf(buf + len, "Transceiver: %s\n",
+ MediaNames[priv->medium]);
+ len += sprintf(buf + len, "Device: %s\n", dev->name);
+ len += sprintf(buf + len, "MAC address:");
+ for (i = 0; i < 6; i++)
+ len += sprintf(buf + len, " %02x", dev->dev_addr[i]);
+ buf[len++] = '\n';
+ buf[len] = 0;
+
+ return len;
+}
+
+/* open driver. Means also initialization and start of LANCE */
+
+static int skmca_open(struct net_device *dev)
+{
+ int result;
+ skmca_priv *priv = netdev_priv(dev);
+
+ /* register resources - only necessary for IRQ */
+ result =
+ request_irq(priv->realirq, irq_handler,
+ SA_SHIRQ | SA_SAMPLE_RANDOM, "sk_mca", dev);
+ if (result != 0) {
+ printk("%s: failed to register irq %d\n", dev->name,
+ dev->irq);
+ return result;
+ }
+ dev->irq = priv->realirq;
+
+ /* set up the card and LANCE */
+
+ InitBoard(dev);
+
+ /* set up flags */
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/* close driver. Shut down board and free allocated resources */
+
+static int skmca_close(struct net_device *dev)
+{
+ /* turn off board */
+ DeinitBoard(dev);
+
+ /* release resources */
+ if (dev->irq != 0)
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+
+ return 0;
+}
+
+/* transmit a block. */
+
+static int skmca_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ LANCE_TxDescr descr;
+ unsigned int address;
+ int tmplen, retval = 0;
+ unsigned long flags;
+
+ /* if we get called with a NULL descriptor, the Ethernet layer thinks
+ our card is stuck an we should reset it. We'll do this completely: */
+
+ if (skb == NULL) {
+ DeinitBoard(dev);
+ InitBoard(dev);
+ return 0; /* don't try to free the block here ;-) */
+ }
+
+ /* is there space in the Tx queue ? If no, the upper layer gave us a
+ packet in spite of us not being ready and is really in trouble.
+ We'll do the dropping for him: */
+ if (priv->txbusy >= TXCOUNT) {
+ priv->stat.tx_dropped++;
+ retval = -EIO;
+ goto tx_done;
+ }
+
+ /* get TX descriptor */
+ address = RAM_TXBASE + (priv->nexttxput * sizeof(LANCE_TxDescr));
+ memcpy_fromio(&descr, priv->base + address, sizeof(LANCE_TxDescr));
+
+ /* enter packet length as 2s complement - assure minimum length */
+ tmplen = skb->len;
+ if (tmplen < 60)
+ tmplen = 60;
+ descr.Len = 65536 - tmplen;
+
+ /* copy filler into RAM - in case we're filling up...
+ we're filling a bit more than necessary, but that doesn't harm
+ since the buffer is far larger... */
+ if (tmplen > skb->len) {
+ char *fill = "NetBSD is a nice OS too! ";
+ unsigned int destoffs = 0, l = strlen(fill);
+
+ while (destoffs < tmplen) {
+ memcpy_toio(priv->base + descr.LowAddr +
+ destoffs, fill, l);
+ destoffs += l;
+ }
+ }
+
+ /* do the real data copying */
+ memcpy_toio(priv->base + descr.LowAddr, skb->data, skb->len);
+
+ /* hand descriptor over to LANCE - this is the first and last chunk */
+ descr.Flags =
+ TXDSCR_FLAGS_OWN | TXDSCR_FLAGS_STP | TXDSCR_FLAGS_ENP;
+
+#ifdef DEBUG
+ PrTime();
+ printk("Send packet on descr %d len %d\n", priv->nexttxput,
+ skb->len);
+#endif
+
+ /* one more descriptor busy */
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->nexttxput++;
+ if (priv->nexttxput >= TXCOUNT)
+ priv->nexttxput = 0;
+ priv->txbusy++;
+
+ /* are we saturated ? */
+
+ if (priv->txbusy >= TXCOUNT)
+ netif_stop_queue(dev);
+
+ /* write descriptor back to RAM */
+ memcpy_toio(priv->base + address, &descr, sizeof(LANCE_TxDescr));
+
+ /* if no descriptors were active, give the LANCE a hint to read it
+ immediately */
+
+ if (priv->txbusy == 0)
+ SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ tx_done:
+
+ dev_kfree_skb(skb);
+
+ return retval;
+}
+
+/* return pointer to Ethernet statistics */
+
+static struct net_device_stats *skmca_stats(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+
+ return &(priv->stat);
+}
+
+/* switch receiver mode. We use the LANCE's multicast filter to prefilter
+ multicast addresses. */
+
+static void skmca_set_multicast_list(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ LANCE_InitBlock block;
+
+ /* first stop the LANCE... */
+ StopLANCE(dev);
+
+ /* ...then modify the initialization block... */
+ memcpy_fromio(&block, priv->base + RAM_INITBASE, sizeof(block));
+ if (dev->flags & IFF_PROMISC)
+ block.Mode |= LANCE_INIT_PROM;
+ else
+ block.Mode &= ~LANCE_INIT_PROM;
+
+ if (dev->flags & IFF_ALLMULTI) { /* get all multicasts */
+ memset(block.LAdrF, 0xff, sizeof(block.LAdrF));
+ } else { /* get selected/no multicasts */
+
+ struct dev_mc_list *mptr;
+ int code;
+
+ memset(block.LAdrF, 0, sizeof(block.LAdrF));
+ for (mptr = dev->mc_list; mptr != NULL; mptr = mptr->next) {
+ code = GetHash(mptr->dmi_addr);
+ block.LAdrF[(code >> 3) & 7] |= 1 << (code & 7);
+ }
+ }
+
+ memcpy_toio(priv->base + RAM_INITBASE, &block, sizeof(block));
+
+ /* ...then reinit LANCE with the correct flags */
+ InitLANCE(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * hardware check
+ * ------------------------------------------------------------------------ */
+
+static int startslot; /* counts through slots when probing multiple devices */
+
+static void cleanup_card(struct net_device *dev)
+{
+ skmca_priv *priv = netdev_priv(dev);
+ DeinitBoard(dev);
+ if (dev->irq != 0)
+ free_irq(dev->irq, dev);
+ iounmap(priv->base);
+ mca_mark_as_unused(priv->slot);
+ mca_set_adapter_procfn(priv->slot, NULL, NULL);
+}
+
+struct net_device * __init skmca_probe(int unit)
+{
+ struct net_device *dev;
+ int force_detect = 0;
+ int junior, slot, i;
+ int base = 0, irq = 0;
+ skmca_priv *priv;
+ skmca_medium medium;
+ int err;
+
+ /* can't work without an MCA bus ;-) */
+
+ if (MCA_bus == 0)
+ return ERR_PTR(-ENODEV);
+
+ dev = alloc_etherdev(sizeof(skmca_priv));
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ }
+
+ SET_MODULE_OWNER(dev);
+
+ /* start address of 1 --> forced detection */
+
+ if (dev->mem_start == 1)
+ force_detect = 1;
+
+ /* search through slots */
+
+ base = dev->mem_start;
+ irq = dev->base_addr;
+ for (slot = startslot; (slot = dofind(&junior, slot)) != -1; slot++) {
+ /* deduce card addresses */
+
+ getaddrs(slot, junior, &base, &irq, &medium);
+
+ /* slot already in use ? */
+
+ if (mca_is_adapter_used(slot))
+ continue;
+
+ /* were we looking for something different ? */
+
+ if (dev->irq && dev->irq != irq)
+ continue;
+ if (dev->mem_start && dev->mem_start != base)
+ continue;
+
+ /* found something that matches */
+
+ break;
+ }
+
+ /* nothing found ? */
+
+ if (slot == -1) {
+ free_netdev(dev);
+ return (base || irq) ? ERR_PTR(-ENXIO) : ERR_PTR(-ENODEV);
+ }
+
+ /* make procfs entries */
+
+ if (junior)
+ mca_set_adapter_name(slot,
+ "SKNET junior MC2 Ethernet Adapter");
+ else
+ mca_set_adapter_name(slot, "SKNET MC2+ Ethernet Adapter");
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) skmca_getinfo, dev);
+
+ mca_mark_as_used(slot);
+
+ /* announce success */
+ printk("%s: SKNet %s adapter found in slot %d\n", dev->name,
+ junior ? "Junior MC2" : "MC2+", slot + 1);
+
+ priv = netdev_priv(dev);
+ priv->base = ioremap(base, 0x4000);
+ if (!priv->base) {
+ mca_set_adapter_procfn(slot, NULL, NULL);
+ mca_mark_as_unused(slot);
+ free_netdev(dev);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ priv->slot = slot;
+ priv->macbase = priv->base + 0x3fc0;
+ priv->ioregaddr = priv->base + 0x3ff0;
+ priv->ctrladdr = priv->base + 0x3ff2;
+ priv->cmdaddr = priv->base + 0x3ff3;
+ priv->medium = medium;
+ memset(&priv->stat, 0, sizeof(struct net_device_stats));
+ spin_lock_init(&priv->lock);
+
+ /* set base + irq for this device (irq not allocated so far) */
+ dev->irq = 0;
+ dev->mem_start = base;
+ dev->mem_end = base + 0x4000;
+
+ /* autoprobe ? */
+ if (irq < 0) {
+ int nirq;
+
+ printk
+ ("%s: ambigous POS bit combination, must probe for IRQ...\n",
+ dev->name);
+ nirq = ProbeIRQ(dev);
+ if (nirq <= 0)
+ printk("%s: IRQ probe failed, assuming IRQ %d",
+ dev->name, priv->realirq = -irq);
+ else
+ priv->realirq = nirq;
+ } else
+ priv->realirq = irq;
+
+ /* set methods */
+ dev->open = skmca_open;
+ dev->stop = skmca_close;
+ dev->hard_start_xmit = skmca_tx;
+ dev->do_ioctl = NULL;
+ dev->get_stats = skmca_stats;
+ dev->set_multicast_list = skmca_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+
+ /* copy out MAC address */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = readb(priv->macbase + (i << 1));
+
+ /* print config */
+ printk("%s: IRQ %d, memory %#lx-%#lx, "
+ "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n",
+ dev->name, priv->realirq, dev->mem_start, dev->mem_end - 1,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+ printk("%s: %s medium\n", dev->name, MediaNames[priv->medium]);
+
+ /* reset board */
+
+ ResetBoard(dev);
+
+ startslot = slot + 1;
+
+ err = register_netdev(dev);
+ if (err) {
+ cleanup_card(dev);
+ free_netdev(dev);
+ dev = ERR_PTR(err);
+ }
+ return dev;
+}
+
+/* ------------------------------------------------------------------------
+ * modularization support
+ * ------------------------------------------------------------------------ */
+
+#ifdef MODULE
+MODULE_LICENSE("GPL");
+
+#define DEVMAX 5
+
+static struct net_device *moddevs[DEVMAX];
+
+int init_module(void)
+{
+ int z;
+
+ startslot = 0;
+ for (z = 0; z < DEVMAX; z++) {
+ struct net_device *dev = skmca_probe(-1);
+ if (IS_ERR(dev))
+ break;
+ moddevs[z] = dev;
+ }
+ if (!z)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int z;
+
+ for (z = 0; z < DEVMAX; z++) {
+ struct net_device *dev = moddevs[z];
+ if (dev) {
+ unregister_netdev(dev);
+ cleanup_card(dev);
+ free_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */