patch-2.1.112 linux/drivers/net/daynaport.c

Next file: linux/drivers/net/eepro.c
Previous file: linux/drivers/net/bmac.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.111/linux/drivers/net/daynaport.c linux/drivers/net/daynaport.c
@@ -0,0 +1,603 @@
+/* mac_ns8390.c: A Macintosh 8390 based ethernet driver for linux. */
+/*
+	Derived from code:
+	
+	Written 1993-94 by Donald Becker.
+
+	Copyright 1993 United States Government as represented by the
+	Director, National Security Agency.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	    TODO:
+
+	    The block output routines may be wrong for non Dayna
+	    cards
+
+	    Reading MAC addresses
+*/
+
+static const char *version =
+	"mac_ns8390.c:v0.01 7/5/97 Alan Cox (Alan.Cox@linux.org)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/nubus.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+int ns8390_probe1(struct device *dev, int word16, char *name, int id, int prom);
+
+static int ns8390_open(struct device *dev);
+static void ns8390_no_reset(struct device *dev);
+static int ns8390_close_card(struct device *dev);
+
+static void interlan_reset(struct device *dev);
+
+static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page);
+static void dayna_block_input(struct device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void dayna_block_output(struct device *dev, int count,
+							const unsigned char *buf, const start_page);
+
+static void sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+						int ring_page);
+static void sane_block_input(struct device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void sane_block_output(struct device *dev, int count,
+							const unsigned char *buf, const start_page);
+
+
+#define WD_START_PG	0x00	/* First page of TX buffer */
+#define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */
+#define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */
+
+
+#define DAYNA_MAC_BASE		0xf0007
+#define DAYNA_8390_BASE		0x80000 /* 3 */
+#define DAYNA_8390_MEM		0x00000
+#define DAYNA_MEMSIZE		0x04000	/* First word of each long ! */
+
+#define APPLE_8390_BASE		0xE0000
+#define APPLE_8390_MEM		0xD0000
+#define APPLE_MEMSIZE		8192    /* FIXME: need to dynamically check */
+
+#define KINETICS_8390_BASE	0x80003
+#define KINETICS_8390_MEM	0x00000
+#define KINETICS_MEMSIZE	8192    /* FIXME: need to dynamically check */
+
+static int test_8390(volatile char *ptr, int scale)
+{
+	int regd;
+	int v;
+	
+	if(nubus_hwreg_present(&ptr[0x00])==0)
+		return -EIO;
+	if(nubus_hwreg_present(&ptr[0x0D<<scale])==0)
+		return -EIO;
+	if(nubus_hwreg_present(&ptr[0x0D<<scale])==0)
+		return -EIO;
+	ptr[0x00]=E8390_NODMA+E8390_PAGE1+E8390_STOP;
+	regd=ptr[0x0D<<scale];
+	ptr[0x0D<<scale]=0xFF;
+	ptr[0x00]=E8390_NODMA+E8390_PAGE0;
+	v=ptr[0x0D<<scale];
+	if(ptr[0x0D<<scale]!=0)
+	{
+		ptr[0x0D<<scale]=regd;
+		return -ENODEV;
+	}
+/*	printk("NS8390 found at %p scaled %d\n", ptr,scale);*/
+	return 0;
+}
+/*
+ *    Identify the species of NS8390 card/driver we need
+ */
+
+#define NS8390_DAYNA		1
+#define NS8390_INTERLAN		2
+#define NS8390_KINETICS		3
+#define NS8390_APPLE		4
+#define NS8390_FARALLON		5
+#define NS8390_ASANTE		6
+
+int ns8390_ident(struct nubus_type *nb)
+{
+	/* It appears anything with a software type of 0 is an apple
+	   compatible - even if the hardware matches others */
+	   
+	if(nb->DrSW==0x0001 || nb->DrSW==0x0109 || nb->DrSW==0x0000 || nb->DrSW==0x0100)
+		return NS8390_APPLE;
+	
+	/* Dayna ex Kinetics board */
+	if(nb->DrHW==0x0103)
+		return NS8390_DAYNA;
+
+	/* Asante board */
+	if(nb->DrHW==0x0104)
+		return NS8390_ASANTE;
+	if(nb->DrHW==0x0100)
+		return NS8390_INTERLAN;
+	if(nb->DrHW==0x0106)
+		return NS8390_KINETICS;
+	if(nb->DrSW==0x010C)
+		return NS8390_FARALLON;
+	return -1;
+}
+
+/*
+ *    Probe for 8390 cards.  
+ *    The ns8390_probe1() routine initializes the card and fills the
+ *    station address field. On entry base_addr is set, irq is set
+ *    (These come from the nubus probe code). dev->mem_start points
+ *    at the memory ring, dev->mem_end gives the end of it.
+ */
+
+int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type *match)
+{
+	struct device *dev;
+	volatile unsigned short *i;
+	volatile unsigned char *p;
+	int plen;
+	int id;
+
+	if(match->category!=NUBUS_CAT_NETWORK || match->type!=1)
+		return -ENODEV;		
+	/* Ok so it is an ethernet network device */
+	if((id=ns8390_ident(match))==-1)
+	{
+		printk("Ethernet but type unknown %d\n",match->DrHW);
+		return -ENODEV;
+	}
+	dev = init_etherdev(0, 0);
+	if(dev==NULL)
+		return -ENOMEM;
+
+	/*
+	 *	Dayna specific init
+	 */
+	if(id==NS8390_DAYNA)
+	{
+		dev->base_addr=(int)(nubus_slot_addr(slot)+DAYNA_8390_BASE);
+		dev->mem_start=(int)(nubus_slot_addr(slot)+DAYNA_8390_MEM);
+		dev->mem_end=dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */
+	
+		printk("daynaport: testing board: ");
+
+		printk("memory - ");	
+	
+		i=(void *)dev->mem_start;
+		memset((void *)i,0xAA, DAYNA_MEMSIZE);
+		while(i<(volatile unsigned short *)dev->mem_end)
+		{
+			if(*i!=0xAAAA)
+				goto membad;
+			*i=0x5555;
+			if(*i!=0x5555)
+				goto membad;
+			i+=2;	/* Skip a word */
+		}
+
+		printk("controller - ");
+	
+		p=(void *)dev->base_addr;
+		plen=0;
+	
+		while(plen<0x3FF00)
+		{
+			if(test_8390(p,0)==0)
+				break;
+			if(test_8390(p,1)==0)
+				break;
+			if(test_8390(p,2)==0)
+				break;
+			if(test_8390(p,3)==0)
+				break;
+			plen++;
+			p++;
+		}
+		if(plen==0x3FF00)
+			goto membad;
+		printk("OK\n");
+		dev->irq=slot;
+		if(ns8390_probe1(dev, 0, "dayna", id, -1)==0)
+		return 0;
+	}
+	/* Apple, Farallon, Asante */
+	if(id==NS8390_APPLE|| id==NS8390_FARALLON || id==NS8390_ASANTE)
+	{
+		dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE);
+		dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM);
+		dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */
+		dev->irq=slot;
+		printk("apple/clone: testing board: ");
+
+		printk("memory - ");		
+
+		i=(void *)dev->mem_start;
+		memset((void *)i,0xAA, DAYNA_MEMSIZE);
+		while(i<(volatile unsigned short *)dev->mem_end)
+		{
+			if(*i!=0xAAAA)
+				goto membad;
+			*i=0x5555;
+			if(*i!=0x5555)
+				goto membad;
+			i+=2;	/* Skip a word */
+		}
+		printk("OK\n");
+
+		if(id==NS8390_FARALLON)
+		{
+			if(ns8390_probe1(dev, 1, "farallon", id, -1)==0)
+				return 0;
+		}
+		else
+		{
+			if(ns8390_probe1(dev, 1, "apple/clone", id, -1)==0)
+			    return 0;
+		}
+	}
+	/* Interlan */
+	if(id==NS8390_INTERLAN)
+	{
+		/* As apple and asante */
+		dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE);
+		dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM);
+		dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */
+		dev->irq=slot;
+		if(ns8390_probe1(dev, 1, "interlan", id, -1)==0)
+			return 0;
+	}
+	/* Kinetics */
+	if(id==NS8390_KINETICS)
+	{
+		dev->base_addr=(int)(nubus_slot_addr(slot)+KINETICS_8390_BASE);
+		dev->mem_start=(int)(nubus_slot_addr(slot)+KINETICS_8390_MEM);
+		dev->mem_end=dev->mem_start+KINETICS_MEMSIZE; /* 8K it seems */
+		dev->irq=slot;
+		if(ns8390_probe1(dev, 0, "kinetics", id, -1)==0)
+			return 0;
+	}
+	kfree(dev);
+	return -ENODEV;
+membad:
+	printk("failed.\n");
+	kfree(dev);
+	return -ENODEV;
+}
+
+int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, int promoff)
+{
+	static unsigned version_printed = 0;
+
+	static unsigned char fwrd4_offsets[16]={
+		0,      4,      8,      12,
+		16,     20,     24,     28,
+		32,     36,     40,     44,
+		48,     52,     56,     60
+	};
+	static unsigned char back4_offsets[16]={
+		60,     56,     52,     48,
+		44,     40,     36,     32,
+		28,     24,     20,     16,
+		12,     8,      4,      0
+	};
+
+	unsigned char *prom=((unsigned char *)nubus_slot_addr(dev->irq))+promoff;
+
+	if (ei_debug  &&  version_printed++ == 0)
+		printk(version);
+	
+	/* Snarf the interrupt now.  There's no point in waiting since we cannot
+	   share a slot! and the board will usually be enabled. */
+	if (nubus_request_irq(dev->irq, dev, ei_interrupt)) 
+	{
+		printk (" unable to get nubus IRQ %d.\n", dev->irq);
+		return EAGAIN;
+	}
+	
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) 
+	{	
+		printk (" unable to get memory for dev->priv.\n");
+		nubus_free_irq(dev->irq);
+		return -ENOMEM;
+	}
+
+	/* OK, we are certain this is going to work.  Setup the device. */
+
+	ei_status.name = model_name;
+	ei_status.word16 = word16;
+	ei_status.tx_start_page = WD_START_PG;
+	ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+	dev->rmem_end = dev->mem_end;
+	
+	if(promoff==-1)		/* Use nubus resources ? */
+	{
+		if(nubus_ethernet_addr(dev->irq /* slot */, dev->dev_addr))
+		{
+		  printk("mac_ns8390: MAC address not in resources!\n");
+		  return -ENODEV;
+		}
+	}
+	else			/* Pull it off the card */
+	{
+		int i=0;
+		int x=1;
+		/* These should go in the end I hope */
+		if(type==NS8390_DAYNA)
+			x=2;
+		if(type==NS8390_INTERLAN)
+			x=4;
+		while(i<6)
+		{
+			dev->dev_addr[i]=*prom;
+			prom+=x;
+			if(i)
+				printk(":");
+			printk("%02X",dev->dev_addr[i++]);
+		}
+	}
+
+	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+		   model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+
+	switch(type)
+	{
+		case NS8390_DAYNA:      /* Dayna card */
+			/* 16 bit, 4 word offsets */
+			ei_status.reset_8390 = &ns8390_no_reset;
+			ei_status.block_input = &dayna_block_input;
+			ei_status.block_output = &dayna_block_output;
+			ei_status.get_8390_hdr = &dayna_get_8390_hdr;
+			ei_status.reg_offset = fwrd4_offsets;
+			break;
+		case NS8390_APPLE:	/* Apple/Asante/Farallon */
+		case NS8390_FARALLON:
+		case NS8390_ASANTE:
+			/*      16 bit card, register map is reversed */
+			ei_status.reset_8390 = &ns8390_no_reset;
+			ei_status.block_input = &sane_block_input;
+			ei_status.block_output = &sane_block_output;
+			ei_status.get_8390_hdr = &sane_get_8390_hdr;
+			ei_status.reg_offset = back4_offsets;
+			break;
+		case NS8390_INTERLAN:   /* Interlan */
+			/*      16 bit card, map is forward */
+			ei_status.reset_8390 = &interlan_reset;
+			ei_status.block_input = &sane_block_input;
+			ei_status.block_output = &sane_block_output;
+			ei_status.get_8390_hdr = &sane_get_8390_hdr;
+			ei_status.reg_offset = back4_offsets;
+			break;
+		case NS8390_KINETICS:   /* Kinetics */
+			/*      8bit card, map is forward */
+			ei_status.reset_8390 = &ns8390_no_reset;
+			ei_status.block_input = &sane_block_input;
+			ei_status.block_output = &sane_block_output;
+			ei_status.get_8390_hdr = &sane_get_8390_hdr;
+			ei_status.reg_offset = back4_offsets;
+			break;
+		default:
+			panic("Detected a card I can't drive - whoops\n");
+	}
+	dev->open = &ns8390_open;
+	dev->stop = &ns8390_close_card;
+
+	NS8390_init(dev, 0);
+
+	return 0;
+}
+
+static int ns8390_open(struct device *dev)
+{
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void ns8390_no_reset(struct device *dev)
+{
+	if (ei_debug > 1) 
+		printk("Need to reset the NS8390 t=%lu...", jiffies);
+	ei_status.txing = 0;
+	if (ei_debug > 1) printk("reset not supported\n");
+	return;
+}
+
+static int ns8390_close_card(struct device *dev)
+{
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+	ei_close(dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+struct nubus_device_specifier nubus_8390={
+	ns8390_probe,
+	NULL
+};
+
+
+/*
+ *    Interlan Specific Code Starts Here
+ */
+
+static void interlan_reset(struct device *dev)
+{
+	unsigned char *target=nubus_slot_addr(dev->irq);
+	if (ei_debug > 1) 
+	printk("Need to reset the NS8390 t=%lu...", jiffies);
+	ei_status.txing = 0;
+	/* This write resets the card */
+	target[0xC0000]=0;
+	if (ei_debug > 1) printk("reset complete\n");
+	return;
+}
+
+/*
+ *    Daynaport code (some is used by other drivers)
+ */
+
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+   on the Daynaport card where there is no choice of how to do it.
+   The only complications are that the ring buffer wraps.
+*/
+
+static void dayna_cpu_memcpy(struct device *dev, void *to, int from, int count)
+{
+	volatile unsigned short *ptr;
+	unsigned short *target=to;
+	from<<=1;	/* word, skip overhead */
+	ptr=(unsigned short *)(dev->mem_start+from);
+	while(count>=2)
+	{
+		*target++=*ptr++;	/* Copy and */
+		ptr++;			/* Cruft and */
+		count-=2;
+	}
+	/*
+	 *	Trailing byte ?
+	 */
+	if(count)
+	{
+		/* Big endian */
+		unsigned short v=*ptr;
+		*((char *)target)=v>>8;
+	}
+}
+
+static void cpu_dayna_memcpy(struct device *dev, int to, const void *from, int count)
+{
+	volatile unsigned short *ptr;
+	const unsigned short *src=from;
+	to<<=1;	/* word, skip overhead */
+	ptr=(unsigned short *)(dev->mem_start+to);
+	while(count>=2)
+	{
+		*ptr++=*src++;		/* Copy and */
+		ptr++;			/* Cruft and */
+		count-=2;
+	}
+	/*
+	 *	Trailing byte ?
+	 */
+	if(count)
+	{
+		/* Big endian */
+		unsigned short v=*src;
+		*((char *)ptr)=v>>8;
+	}
+}
+
+static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+	dayna_cpu_memcpy(dev, (void *)hdr, hdr_start, 4);
+	/* Register endianism - fix here rather than 8390.c */
+	hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void dayna_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base+dev->mem_start;
+
+	/*
+	 *	Note the offset maths is done in card memory space which
+	 *	is word per long onto our space.
+	 */
+	 
+	if (xfer_start + count > dev->rmem_end) 
+	{
+		/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		dayna_cpu_memcpy(dev, skb->data, xfer_base, semi_count);
+		count -= semi_count;
+		dayna_cpu_memcpy(dev, skb->data + semi_count, 
+			dev->rmem_start - dev->mem_start, count);
+	}
+	else
+	{
+		dayna_cpu_memcpy(dev, skb->data, xfer_base, count);
+	}
+}
+
+static void dayna_block_output(struct device *dev, int count, const unsigned char *buf,
+				int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+	
+	cpu_dayna_memcpy(dev, shmem, buf, count);
+}
+
+/*
+ *	Cards with full width memory
+ */
+
+
+static void sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = (ring_page - WD_START_PG)<<8;
+	memcpy((void *)hdr, (char *)dev->mem_start+hdr_start, 4);
+	/* Register endianism - fix here rather than 8390.c */
+	hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8);
+}
+
+static void sane_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_base = ring_offset - (WD_START_PG<<8);
+	unsigned long xfer_start = xfer_base+dev->mem_start;
+
+	if (xfer_start + count > dev->rmem_end) 
+	{
+		/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		memcpy(skb->data, (char *)dev->mem_start+xfer_base, semi_count);
+		count -= semi_count;
+		memcpy(skb->data + semi_count, 
+			(char *)dev->rmem_start, count);
+	}
+	else
+	{
+		memcpy(skb->data, (char *)dev->mem_start+xfer_base, count);
+	}
+}
+
+static void sane_block_output(struct device *dev, int count, const unsigned char *buf,
+				int start_page)
+{
+	long shmem = (start_page - WD_START_PG)<<8;
+	
+	memcpy((char *)dev->mem_start+shmem, buf, count);
+}
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c daynaport.c"
+ *  version-control: t
+ *  tab-width: 4
+ *  kept-new-versions: 5
+ * End:
+ */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov