Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / drivers / net / ne2k_isa.c
diff --git a/qemu/roms/ipxe/src/drivers/net/ne2k_isa.c b/qemu/roms/ipxe/src/drivers/net/ne2k_isa.c
new file mode 100644 (file)
index 0000000..a923fd3
--- /dev/null
@@ -0,0 +1,375 @@
+/**************************************************************************
+ ETHERBOOT -  BOOTP/TFTP Bootstrap Program
+
+ Author: Martin Renters
+ Date: May/94
+
+ This code is based heavily on David Greenman's if_ed.c driver
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+ Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
+ Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
+ Card Detect support adapted from the eCos driver (Christian Plessl <cplessl@ee.ethz.ch>)
+ Extracted from ns8390.c and adapted by Pantelis Koukousoulas <pktoss@gmail.com>
+ **************************************************************************/
+
+FILE_LICENCE ( BSD2 );
+
+#include "ns8390.h"
+#include "etherboot.h"
+#include "nic.h"
+#include <ipxe/ethernet.h>
+#include <ipxe/isa.h>
+#include <errno.h>
+
+#define ASIC_PIO NE_DATA
+
+static unsigned char eth_vendor, eth_flags;
+static unsigned short eth_nic_base, eth_asic_base;
+static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
+static Address eth_bmem, eth_rmem;
+static unsigned char eth_drain_receiver;
+
+static struct nic_operations ne_operations;
+static void ne_reset(struct nic *nic, struct isa_device *isa);
+
+static isa_probe_addr_t ne_probe_addrs[] = { 0x300, 0x280, 0x320, 0x340, 0x380, 0x220, };
+
+/**************************************************************************
+ ETH_PIO_READ - Read a frame via Programmed I/O
+ **************************************************************************/
+static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) {
+       outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+       outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+       outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
+       outb(src, eth_nic_base + D8390_P0_RSAR0);
+       outb(src >> 8, eth_nic_base + D8390_P0_RSAR1);
+       outb(D8390_COMMAND_RD0 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+       if (eth_flags & FLAG_16BIT)
+               cnt = (cnt + 1) >> 1;
+
+       while (cnt--) {
+               if (eth_flags & FLAG_16BIT) {
+                       *((unsigned short *) dst) = inw(eth_asic_base + ASIC_PIO);
+                       dst += 2;
+               } else
+                       *(dst++) = inb(eth_asic_base + ASIC_PIO);
+       }
+}
+
+/**************************************************************************
+ ETH_PIO_WRITE - Write a frame via Programmed I/O
+ **************************************************************************/
+static void eth_pio_write(const unsigned char *src, unsigned int dst,
+               unsigned int cnt) {
+       outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+       outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
+       outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+       outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
+       outb(dst, eth_nic_base + D8390_P0_RSAR0);
+       outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
+       outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+       if (eth_flags & FLAG_16BIT)
+               cnt = (cnt + 1) >> 1;
+
+       while (cnt--) {
+
+               if (eth_flags & FLAG_16BIT) {
+                       outw(*((unsigned short *) src), eth_asic_base + ASIC_PIO);
+                       src += 2;
+               } else
+                       outb(*(src++), eth_asic_base + ASIC_PIO);
+       }
+}
+
+/**************************************************************************
+ enable_multicast - Enable Multicast
+ **************************************************************************/
+static void enable_multicast(unsigned short eth_nic_base) {
+       unsigned char mcfilter[8];
+       int i;
+
+       memset(mcfilter, 0xFF, 8);
+       outb(4, eth_nic_base + D8390_P0_RCR);
+       outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
+       for (i = 0; i < 8; i++) {
+               outb(mcfilter[i], eth_nic_base + 8 + i);
+               if (inb(eth_nic_base + 8 + i) != mcfilter[i])
+                       DBG("Error SMC 83C690 Multicast filter read/write mishap %d\n",
+                                       i);
+       }
+       outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
+       outb(4 | 0x08, eth_nic_base + D8390_P0_RCR);
+}
+
+/**************************************************************************
+ NE_PROBE1 - Look for an adapter on the ISA bus
+ **************************************************************************/
+static int ne_probe1(isa_probe_addr_t ioaddr) {
+       //From the eCos driver
+       unsigned int regd;
+       unsigned int state;
+
+       state = inb(ioaddr);
+       outb(ioaddr, D8390_COMMAND_RD2 | D8390_COMMAND_PS1 | D8390_COMMAND_STP);
+       regd = inb(ioaddr + D8390_P0_TCR);
+
+       if (inb(ioaddr + D8390_P0_TCR)) {
+               outb(ioaddr, state);
+               outb(ioaddr + 0x0d, regd);
+               return 0;
+       }
+
+       return 1;
+}
+
+/**************************************************************************
+ NE_PROBE - Initialize an adapter ???
+ **************************************************************************/
+static int ne_probe(struct nic *nic, struct isa_device *isa) {
+       int i;
+       unsigned char c;
+       unsigned char romdata[16];
+       unsigned char testbuf[32];
+
+       eth_vendor = VENDOR_NONE;
+       eth_drain_receiver = 0;
+
+       nic->irqno = 0;
+       nic->ioaddr = isa->ioaddr;
+       eth_nic_base = isa->ioaddr;
+
+       /******************************************************************
+        Search for NE1000/2000 if no WD/SMC or 3com cards
+        ******************************************************************/
+       if (eth_vendor == VENDOR_NONE) {
+
+               static unsigned char test[] = "NE*000 memory";
+
+               eth_bmem = 0; /* No shared memory */
+
+               eth_flags = FLAG_PIO;
+               eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
+               eth_memsize = MEM_16384;
+               eth_tx_start = 32;
+               eth_rx_start = 32 + D8390_TXBUF_SIZE;
+               c = inb(eth_asic_base + NE_RESET);
+               outb(c, eth_asic_base + NE_RESET);
+               (void) inb(0x84);
+               outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base
+                               + D8390_P0_COMMAND);
+               outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
+               outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+               outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
+               outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
+               eth_pio_write((unsigned char *) test, 8192, sizeof(test));
+               eth_pio_read(8192, testbuf, sizeof(test));
+               if (!memcmp(test, testbuf, sizeof(test)))
+                       goto out;
+               eth_flags |= FLAG_16BIT;
+               eth_memsize = MEM_32768;
+               eth_tx_start = 64;
+               eth_rx_start = 64 + D8390_TXBUF_SIZE;
+               outb(D8390_DCR_WTS | D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base
+                               + D8390_P0_DCR);
+               outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
+               outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
+               eth_pio_write((unsigned char *) test, 16384, sizeof(test));
+               eth_pio_read(16384, testbuf, sizeof(test));
+               if (!memcmp(testbuf, test, sizeof(test)))
+                       goto out;
+
+
+out:
+               if (eth_nic_base == 0)
+                       return (0);
+               if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */
+                       eth_flags |= FLAG_16BIT;
+               eth_vendor = VENDOR_NOVELL;
+               eth_pio_read(0, romdata, sizeof(romdata));
+               for (i = 0; i < ETH_ALEN; i++) {
+                       nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
+               }
+               nic->ioaddr = eth_nic_base;
+               DBG("\nNE%c000 base %4.4x, MAC Addr %s\n",
+                               (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, eth_ntoa(
+                                               nic->node_addr));
+       }
+
+       if (eth_vendor == VENDOR_NONE)
+               return (0);
+
+       if (eth_vendor != VENDOR_3COM)
+               eth_rmem = eth_bmem;
+
+       ne_reset(nic, isa);
+       nic->nic_op = &ne_operations;
+       return 1;
+}
+
+
+/**************************************************************************
+ NE_DISABLE - Turn off adapter
+ **************************************************************************/
+static void ne_disable(struct nic *nic, struct isa_device *isa) {
+       ne_reset(nic, isa);
+}
+
+
+/**************************************************************************
+ NE_RESET - Reset adapter
+ **************************************************************************/
+static void ne_reset(struct nic *nic, struct isa_device *isa __unused)
+{
+       int i;
+
+       eth_drain_receiver = 0;
+       outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+                       D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+       if (eth_flags & FLAG_16BIT)
+       outb(0x49, eth_nic_base+D8390_P0_DCR);
+       else
+       outb(0x48, eth_nic_base+D8390_P0_DCR);
+       outb(0, eth_nic_base+D8390_P0_RBCR0);
+       outb(0, eth_nic_base+D8390_P0_RBCR1);
+       outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */
+       outb(2, eth_nic_base+D8390_P0_TCR);
+       outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+       outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
+
+       outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
+       outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
+       outb(0xFF, eth_nic_base+D8390_P0_ISR);
+       outb(0, eth_nic_base+D8390_P0_IMR);
+       outb(D8390_COMMAND_PS1 |
+                       D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+
+       for (i=0; i<ETH_ALEN; i++)
+       outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
+       for (i=0; i<ETH_ALEN; i++)
+       outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
+       outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
+       outb(D8390_COMMAND_PS0 |
+                       D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+       outb(0xFF, eth_nic_base+D8390_P0_ISR);
+       outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */
+       outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */
+
+       enable_multicast(eth_nic_base);
+}
+
+
+/**************************************************************************
+ NE_POLL - Wait for a frame
+ **************************************************************************/
+static int ne_poll(struct nic *nic __unused, int retrieve __unused)
+{
+       int ret = 0;
+       unsigned char rstat, curr, next;
+       unsigned short len, frag;
+       unsigned short pktoff;
+       unsigned char *p;
+       struct ringbuffer pkthdr;
+
+       rstat = inb(eth_nic_base+D8390_P0_RSR);
+       if (!(rstat & D8390_RSTAT_PRX)) return(0);
+       next = inb(eth_nic_base+D8390_P0_BOUND)+1;
+       if (next >= eth_memsize) next = eth_rx_start;
+       outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
+       curr = inb(eth_nic_base+D8390_P1_CURR);
+       outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
+       if (curr >= eth_memsize) curr=eth_rx_start;
+       if (curr == next) return(0);
+
+       if ( ! retrieve ) return 1;
+
+       pktoff = next << 8;
+       if (eth_flags & FLAG_PIO)
+       eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4);
+       else
+       memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
+       pktoff += sizeof(pkthdr);
+       /* incoming length includes FCS so must sub 4 */
+       len = pkthdr.len - 4;
+       if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
+                       || len> ETH_FRAME_LEN) {
+               DBG("Bogus packet, ignoring\n");
+               return (0);
+       }
+       else {
+               p = nic->packet;
+               nic->packetlen = len; /* available to caller */
+               frag = (eth_memsize << 8) - pktoff;
+               if (len> frag) { /* We have a wrap-around */
+                       /* read first part */
+                       if (eth_flags & FLAG_PIO)
+                       eth_pio_read(pktoff, p, frag);
+                       else
+                       memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
+                       pktoff = eth_rx_start << 8;
+                       p += frag;
+                       len -= frag;
+               }
+               /* read second part */
+               if (eth_flags & FLAG_PIO)
+               eth_pio_read(pktoff, p, len);
+               else
+               memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
+               ret = 1;
+       }
+       next = pkthdr.next; /* frame number of next packet */
+       if (next == eth_rx_start)
+       next = eth_memsize;
+       outb(next-1, eth_nic_base+D8390_P0_BOUND);
+       return(ret);
+}
+
+
+/**************************************************************************
+ NE_TRANSMIT - Transmit a frame
+ **************************************************************************/
+static void ne_transmit(struct nic *nic, const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *p) { /* Packet */
+
+       /* Programmed I/O */
+       unsigned short type;
+       type = (t >> 8) | (t << 8);
+       eth_pio_write((unsigned char *) d, eth_tx_start << 8, ETH_ALEN);
+       eth_pio_write(nic->node_addr, (eth_tx_start << 8) + ETH_ALEN, ETH_ALEN);
+       /* bcc generates worse code without (const+const) below */
+       eth_pio_write((unsigned char *) &type, (eth_tx_start << 8) + (ETH_ALEN
+                       + ETH_ALEN), 2);
+       eth_pio_write((unsigned char *) p, (eth_tx_start << 8) + ETH_HLEN, s);
+       s += ETH_HLEN;
+       if (s < ETH_ZLEN)
+               s = ETH_ZLEN;
+
+       outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA,
+                       eth_nic_base + D8390_P0_COMMAND);
+       outb(eth_tx_start, eth_nic_base + D8390_P0_TPSR);
+       outb(s, eth_nic_base + D8390_P0_TBCR0);
+       outb(s >> 8, eth_nic_base + D8390_P0_TBCR1);
+
+       outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2
+                       | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+}
+
+static struct nic_operations ne_operations = { .connect = dummy_connect,
+               .poll = ne_poll, .transmit = ne_transmit, .irq = dummy_irq,
+};
+
+ISA_DRIVER ( ne_driver, ne_probe_addrs, ne_probe1,
+               GENERIC_ISAPNP_VENDOR, 0x0600 );
+
+DRIVER ( "ne", nic_driver, isapnp_driver, ne_driver,
+               ne_probe, ne_disable );
+
+ISA_ROM("ne","NE1000/2000 and clones");