diff options
Diffstat (limited to 'drivers/net/daynaport.c')
-rw-r--r-- | drivers/net/daynaport.c | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/drivers/net/daynaport.c b/drivers/net/daynaport.c new file mode 100644 index 000000000..0b550fd57 --- /dev/null +++ b/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 int fwrd4_offsets[16]={ + 0, 4, 8, 12, + 16, 20, 24, 28, + 32, 36, 40, 44, + 48, 52, 56, 60 + }; + static int 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: + */ |