| /* el.c: A shared-memory NS8390 ethernet driver for linux. */ |
| /* |
| Written 1992,1993 by Donald Becker. This is alpha test code. |
| 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. |
| |
| This driver should work with the 3c503 and 3c503/16. It must be used |
| in shared memory mode. |
| |
| The Author may be reached as becker@super.org or |
| C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 |
| */ |
| |
| static char *version = "el.c:v0.99-10 5/28/93 Donald Becker (becker@super.org)\n"; |
| |
| #include <linux/config.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/errno.h> |
| #include <asm/io.h> |
| #include <asm/system.h> |
| |
| #include "dev.h" |
| |
| #include "8390.h" |
| #include "elreg.h" |
| |
| extern void NS8390_init(struct device *dev, int startp); |
| extern int ei_debug; |
| extern struct sigaction ei_sigaction; |
| |
| int el2autoprobe(int ioaddr, struct device *dev); |
| int el2probe(int ioaddr, struct device *dev); |
| |
| static void el2_reset_8390(struct device *dev); |
| static void el2_init_card(struct device *dev); |
| static void el2_block_output(struct device *dev, int count, |
| const unsigned char *buf, const start_page); |
| static int el2_block_input(struct device *dev, int count, char *buf, |
| int ring_offset); |
| |
| |
| int |
| el2autoprobe(int ioaddr, struct device *dev) |
| { |
| int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0}; |
| int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0}; |
| |
| /* Non-autoprobe case first: */ |
| if (ioaddr > 0) |
| return el2probe(ioaddr, dev); |
| |
| /* We check for a memory-mapped 3c503 board by looking at the |
| port location bitmap at the end of the jumpered boot PROM space. |
| This works even if a PROM isn't there. */ |
| for (addr = addrs; *addr; addr++) { |
| int i; |
| unsigned int base_bits = *(unsigned char *)*addr; |
| /* Find first set bit. */ |
| for(i = 7; i >= 0; i--, base_bits >>= 1) |
| if (base_bits & 0x1) |
| break; |
| if (base_bits == 1 && el2probe(ports[i], dev)) |
| return dev->base_addr; |
| } |
| #ifndef ignore_nonshared_memory |
| /* It's not memory mapped, bummer. Try all of the locations |
| that aren't obviously empty. */ |
| { int i; |
| for (i = 0; i < 8; i++) { |
| inb_p(ports[i] + 0x40F); /* Reset any lurking NE2000 */ |
| if (inb(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */ |
| && el2probe(ports[i], dev)) |
| return dev->base_addr; |
| } |
| } |
| #endif /* probe_nonshared_memory */ |
| return 0; |
| } |
| |
| /* Probe for the Etherlink II card at I/O port base IOADDR, |
| returning non-zero on sucess. If found, set the station |
| address and memory parameters in DEVICE. */ |
| int |
| el2probe(int ioaddr, struct device *dev) |
| { |
| int i, iobase_reg, membase_reg, saved_406; |
| unsigned char *station_addr = dev->dev_addr; |
| |
| /* We verify that it's a 3C503 board by checking the first three octets |
| of its ethernet address. */ |
| printk("3c503 probe at %#3x:", ioaddr); |
| iobase_reg = inb(ioaddr+0x403); |
| membase_reg = inb(ioaddr+0x404); |
| /* Verify ASIC register that should be 0 or have a single bit set. */ |
| if ( (iobase_reg & (iobase_reg - 1)) |
| || (membase_reg & (membase_reg - 1))) { |
| printk(" not found.\n"); |
| return 0; |
| } |
| saved_406 = inb_p(ioaddr + 0x406); |
| outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */ |
| outb_p(ECNTRL_THIN, ioaddr + 0x406); |
| /* Map the station addr PROM into the lower I/O ports. */ |
| outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406); |
| for (i = 0; i < ETHER_ADDR_LEN; i++) { |
| printk(" %2.2X", (station_addr[i] = inb(ioaddr + i))); |
| } |
| if ( station_addr[0] != 0x02 |
| || station_addr[1] != 0x60 |
| || station_addr[2] != 0x8c) { |
| printk(" 3C503 not found.\n"); |
| /* Restore the register we frobbed. */ |
| outb_p(saved_406, ioaddr + 0x406); |
| return 0; |
| } |
| /* Map the 8390 back into the window. */ |
| outb(ECNTRL_THIN, ioaddr + 0x406); |
| dev->base_addr = ioaddr; |
| /* Probe for, turn on and clear the board's shared memory. */ |
| if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg); |
| outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */ |
| if ((membase_reg & 0xf0) == 0) { |
| dev->mem_start = 0; |
| } else { |
| dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) + |
| ((membase_reg & 0xA0) ? 0x4000 : 0); |
| |
| #define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256 |
| #ifdef EL2MEMTEST |
| /* This has never found an error, but someone might care. */ |
| { /* Check the card's memory. */ |
| int *mem_base = (int *)dev->mem_start; |
| int memtest_value = 0xbbadf00d; |
| mem_base[0] = 0xba5eba5e; |
| for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) { |
| mem_base[i] = memtest_value; |
| if (mem_base[0] != 0xba5eba5e |
| || mem_base[i] != memtest_value) { |
| printk(" memory failure or memory address conflict.\n"); |
| dev->mem_start = 0; |
| break; |
| } |
| memtest_value += 0x55555555; |
| mem_base[i] = 0; |
| } |
| } |
| #endif /* EL2MEMTEST */ |
| /* Divide the on-board memory into a single maximum-sized transmit |
| (double-sized for ping-pong transmit) buffer at the base, and |
| use the rest as a receive ring. */ |
| dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE; |
| dev->rmem_start = TX_PAGES*256 + dev->mem_start; |
| } |
| if (ei_debug > 2) |
| printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n", |
| dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end); |
| |
| /* Finish setting the board's parameters. */ |
| ei_status.name = "3C503"; |
| ei_status.tx_start_page = EL2SM_START_PG; |
| ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES; |
| ei_status.stop_page = EL2SM_STOP_PG; |
| ei_status.reset_8390 = &el2_reset_8390; |
| ei_status.block_input = &el2_block_input; |
| ei_status.block_output = &el2_block_output; |
| /* This should be probed for (or set via an ioctl()) at run-time someday. */ |
| #if defined(EI8390_THICK) || defined(EL2_AUI) |
| ei_status.interface_num = 1; |
| #else |
| ei_status.interface_num = 0; |
| #endif |
| |
| if (dev->irq < 2) { |
| int irqlist[] = {5, 9, 3, 4, 0}; |
| int *irqp = irqlist; |
| do { |
| if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) { |
| /* Twinkle the interrupt, and check if it's seen. */ |
| autoirq_setup(0); |
| outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR); |
| outb_p(0x00, E33G_IDCFR); |
| if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */ |
| && request_irq (dev->irq, &ei_interrupt) == 0) { |
| printk(" got IRQ %d", dev->irq); |
| break; |
| } else |
| printk(" IRQ%d busy..", dev->irq); |
| } |
| } while (*++irqp); |
| if (*irqp == 0) { |
| printk(" unable to find an free IRQ line.\n"); |
| return 0; |
| } |
| } else { |
| if (dev->irq == 2) |
| dev->irq = 9; |
| else if (dev->irq > 5 && dev->irq != 9) { |
| printk("\n3c503: configured interrupt number %d out of range.\n", |
| dev->irq); |
| return 0; |
| } |
| if (request_irq(dev->irq, &ei_interrupt)) { |
| printk (" unable to get IRQ%d.\n", dev->irq); |
| return 0; |
| } |
| } |
| |
| dev->start = 0; |
| el2_init_card(dev); |
| |
| if (dev->mem_start) |
| printk("\n%s: %s using IRQ %d with shared memory at %#6x-%#6x,\n", |
| dev->name, ei_status.name, dev->irq, |
| dev->mem_start, dev->mem_end-1); |
| else |
| printk("\n%s: %s using IRQ %d with programmed I/O.\n", |
| dev->name, ei_status.name, dev->irq); |
| if (ei_debug > 1) |
| printk(version); |
| |
| return ioaddr; |
| } |
| |
| /* This is called whenever we have a unrecoverable failure: |
| transmit timeout |
| Bad ring buffer packet header |
| */ |
| static void |
| el2_reset_8390(struct device *dev) |
| { |
| if (ei_debug > 1) { |
| printk("%s: Resetting the 3c503 board...", dev->name); |
| printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR), |
| E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR)); |
| } |
| outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL); |
| ei_status.txing = 0; |
| outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); |
| el2_init_card(dev); |
| if (ei_debug > 1) printk("done\n"); |
| } |
| |
| /* Initialize the 3c503 GA registers after a reset. */ |
| static void |
| el2_init_card(struct device *dev) |
| { |
| /* Unmap the station PROM and select the DIX or BNC connector. */ |
| outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); |
| |
| /* Set ASIC copy of rx's first and last+1 buffer pages */ |
| /* These must be the same as in the 8390. */ |
| outb(ei_status.rx_start_page, E33G_STARTPG); |
| outb(ei_status.stop_page, E33G_STOPPG); |
| |
| /* Point the vector pointer registers somewhere ?harmless?. */ |
| outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */ |
| outb(0xff, E33G_VP1); |
| outb(0x00, E33G_VP0); |
| /* Turn off all interrupts until we're opened. */ |
| outb_p(0x00, dev->base_addr + EN0_IMR); |
| /* Enable IRQs iff started. */ |
| outb(EGACFR_NORM, E33G_GACFR); |
| |
| /* Set the interrupt line. */ |
| outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR); |
| outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */ |
| outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */ |
| outb_p(0x00, E33G_DMAAL); |
| return; /* We always succeed */ |
| } |
| |
| /* Either use the shared memory (if enabled on the board) or put the packet |
| out through the ASIC FIFO. The latter is probably much slower. */ |
| static void |
| el2_block_output(struct device *dev, int count, |
| const unsigned char *buf, const start_page) |
| { |
| int i; /* Buffer index */ |
| int boguscount = 0; /* timeout counter */ |
| |
| /* This should really be set with during an open(). */ |
| outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */ |
| |
| if (dev->mem_start) { /* Shared memory transfer */ |
| void *dest_addr = (void *)(dev->mem_start + |
| ((start_page - ei_status.tx_start_page) << 8)); |
| memcpy(dest_addr, buf, count); |
| if (ei_debug > 2 && memcmp(dest_addr, buf, count)) |
| printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n", |
| dev->name, dest_addr); |
| else if (ei_debug > 4) |
| printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n", |
| dev->name, dest_addr); |
| return; |
| } |
| /* No shared memory, put the packet out the slow way. */ |
| /* Set up then start the internal memory transfer to Tx Start Page */ |
| outb(0x00, E33G_DMAAL); |
| outb_p(start_page, E33G_DMAAH); |
| outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT |
| | ECNTRL_START, E33G_CNTRL); |
| |
| /* This is the byte copy loop: it should probably be tuned for |
| for speed once everything is working. I think it is possible |
| to output 8 bytes between each check of the status bit. */ |
| for(i = 0; i < count; i++) { |
| if (i % 8 == 0) |
| while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) |
| if (++boguscount > (i<<3) + 32) { |
| printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n", |
| dev->name, i, count, boguscount); |
| return; |
| } |
| outb(buf[i], E33G_FIFOH); |
| } |
| outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); |
| return; |
| } |
| |
| /* Returns the new ring pointer. */ |
| static int |
| el2_block_input(struct device *dev, int count, char *buf, int ring_offset) |
| { |
| int boguscount = 0; |
| int end_of_ring = dev->rmem_end; |
| unsigned int i; |
| |
| /* Maybe enable shared memory just be to be safe... nahh.*/ |
| if (dev->mem_start) { /* Use the shared memory. */ |
| ring_offset -= (EL2SM_START_PG<<8); |
| if (dev->mem_start + ring_offset + count > end_of_ring) { |
| /* We must wrap the input move. */ |
| int semi_count = end_of_ring - (dev->mem_start + ring_offset); |
| if (ei_debug > 4) |
| printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n", |
| dev->name, dev->mem_start, ring_offset, |
| (char *)dev->mem_start + ring_offset); |
| memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count); |
| count -= semi_count; |
| memcpy(buf + semi_count, (char *)dev->rmem_start, count); |
| return dev->rmem_start + count; |
| } |
| if (ei_debug > 4) |
| printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n", |
| dev->name, dev->mem_start, ring_offset, |
| (char *)dev->mem_start + ring_offset); |
| memcpy(buf, (char *)dev->mem_start + ring_offset, count); |
| return ring_offset + count; |
| } |
| /* No shared memory, use programmed I/O. */ |
| outb(ring_offset & 0xff, E33G_DMAAL); |
| outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH); |
| outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT |
| | ECNTRL_START, E33G_CNTRL); |
| |
| /* This is the byte copy loop: it should probably be tuned for |
| for speed once everything is working. */ |
| for(i = 0; i < count; i++) { |
| if (i % 8 == 0) |
| while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) |
| if (++boguscount > (i<<3) + 32) { |
| printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n", |
| dev->name, i, count, boguscount); |
| boguscount = 0; |
| break; |
| } |
| buf[i] = inb_p(E33G_FIFOH); |
| } |
| outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); |
| return 0; |
| } |
| |
| /* |
| * Local variables: |
| * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c" |
| * version-control: t |
| * kept-new-versions: 5 |
| * End: |
| */ |