From 1fbb6a0bd3e6e04d06da0e677533f54ef628dbde Mon Sep 17 00:00:00 2001 From: wollman Date: Sat, 1 Oct 1994 20:16:47 +0000 Subject: Add Matt Thomas's DC21040 PCI Ethernet driver. (This is turning out to be quite a popular chip, so expect to see a number of products based on it.) --- sys/pci/README.de | 65 +++ sys/pci/README.de-le | 34 ++ sys/pci/dc21040.h | 214 +++++++++ sys/pci/if_de.c | 1180 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1493 insertions(+) create mode 100644 sys/pci/README.de create mode 100644 sys/pci/README.de-le create mode 100644 sys/pci/dc21040.h create mode 100644 sys/pci/if_de.c (limited to 'sys/pci') diff --git a/sys/pci/README.de b/sys/pci/README.de new file mode 100644 index 0000000..1486460 --- /dev/null +++ b/sys/pci/README.de @@ -0,0 +1,65 @@ +$Id: README.de,v 1.1 1994/08/16 20:40:56 thomas Exp $ + +---------------- + +The enclosed driver should be considered as beta-test software. It +has run on exactly one machine. Therefore testing has been limited. +This driver is in no way supported by Digital Equipment. See the +disclaimers in the sources for more. + +This driver the DEC DE435 PCI NIC. It should also work with other PCI +boards that use the DC21040-AA chip (also known as TULIP). This +driver requires the DC21040-AA to be pass 2.3 or later. If you are +using a eariler pass chip, you may encounter undetected transmit +corruptions. This driver also requires that DC21040-AA use a serial +Ethernet address ROM as described in the DC21040 specification. + +The DEC DE425 ESIA NIC based on the DC21040-AA is not support at +this time. A future update will include support for it. + +The driver includes full support for both BPF and IP Multicast. +If the autosensing of the driver fails, you can use ifconfig(8) to +switch the driver to the correct port. + + ifconfig de0 altphys Thinwire/AUI port + ifconfig de0 -altphys 10baseT/UTP port + +This driver requires the NCR 53C810 PCI SCSI package to be installed. +This can be obtained from FTP.Uni-Koeln.DE:~ftp/packages/FreeBSD. + +[All paths are relative to the top of sys source area, usually +/usr/src/sys.] + +The following files need to be moved into their respective +directories: + + if_de.c --> i386/pci + dc21040.h --> i386/pci + +You will need to apply the following patches: + + pat.files.i386 --> i386/conf/files.i386 + pat.pci --> i386/pci/pci.c, i386/pci/pci_config.c + +After that is done you will need to edit your config file (in +i386/conf) and lines similar to: + + controller pci0 at isa? irq 9 + device de0 + +The above assumes the board is configured to use IRQ 9 by your +BIOS. Change the IRQ if need to match your configuration. + +Now you are ready to rebuild your kernel, reboot, and see if the +driver can configure your board. When the system boots, you will +hopefully something close to: + + on pci0:7 irq a isa=9 [1] as de0 + memory size=0x1000 virtual=0xf25e9000 physical=0xc0001000 + de0: enabling Thinwire/AUI port + de0: DC21040 pass 2.3 (TULIP) ethernet address 08:00:2b:e2:1e:09 + bpf: de0 attached + +in the startup log. If so, the board configured properly and +should be ready to use. + diff --git a/sys/pci/README.de-le b/sys/pci/README.de-le new file mode 100644 index 0000000..c9d9279 --- /dev/null +++ b/sys/pci/README.de-le @@ -0,0 +1,34 @@ +$Id: README,v 1.5 1994/08/16 20:40:56 thomas Exp $ + +---------------- + +The enclosed drivers should be considered beta-test software. These +drivers are in no way supported by Digital Equipment. See the +disclaimers in the sources for more information. Please be aware that +Digital does not employee me to write drivers for FreeBSD. + +This kit contains two drivers: + + de DEC DE435 PCI NIC or compatible + le DEC EtherWORKS II/EtherWORKS III NICs + +See README.de and README.le for information and installation +instruction specific to each driver. + +Could you please send me the startup messages in the boot +long along with the type of your PC once the driver configures? + +If you have any problems, comments, suggestions, rant or raves, don't +hesitate to send me mail @ thomas@lkg.dec.com. + +Lastly, if you change or modify the code, I want context diffs of your +changes. I want this to the canonical DEC EtherWORKS driver kit for +FreeBSD, NetBSD, BSD/386, and any other BSD based O/S. Please make +sure your diffs are approriate conditionalized. + +Thanks, +Matt Thomas +-- +Mail: thomas@lkg.dec.com +URL: http://ftp.digital.com/~thomas/ + diff --git a/sys/pci/dc21040.h b/sys/pci/dc21040.h new file mode 100644 index 0000000..4afa274 --- /dev/null +++ b/sys/pci/dc21040.h @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1994 Matt Thomas (thomas@lkg.dec.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dc21040.h,v 1.2 1994/08/15 20:42:25 thomas Exp thomas $ + * + * $Log: dc21040.h,v $ + * Revision 1.2 1994/08/15 20:42:25 thomas + * misc additions + * + * Revision 1.1 1994/08/12 21:02:46 thomas + * Initial revision + * + * Revision 1.8 1994/08/05 20:20:54 thomas + * Enable change log + * + * Revision 1.7 1994/08/05 20:20:14 thomas + * *** empty log message *** + * + */ + +#if !defined(_DC21040_H) +#define _DC21040_H + +typedef signed int tulip_sint32_t; +typedef unsigned int tulip_uint32_t; + +#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN +#define TULIP_BITFIELD2(a, b) b, a +#define TULIP_BITFIELD3(a, b, c) c, b, a +#define TULIP_BITFIELD4(a, b, c, d) d, c, b, a +#else +#define TULIP_BITFIELD2(a, b) a, b +#define TULIP_BITFIELD3(a, b, c) a, b, c +#define TULIP_BITFIELD4(a, b, c, d) a, b, c, d +#endif + +typedef struct { + tulip_uint32_t d_status; + tulip_uint32_t TULIP_BITFIELD3(d_length1 : 11, + d_length2 : 11, + d_flag : 10); + tulip_uint32_t d_addr1; + tulip_uint32_t d_addr2; +} tulip_desc_t; + +#define TULIP_DSTS_OWNER 0x80000000 /* Owner (1 = DC21040) */ +#define TULIP_DSTS_ERRSUM 0x00008000 /* Error Summary */ +/* + * Transmit Status + */ +#define TULIP_DSTS_TxBABBLE 0x00004000 /* Transmitter Babbled */ +#define TULIP_DSTS_TxCARRLOSS 0x00000800 /* Carrier Loss */ +#define TULIP_DSTS_TxNOCARR 0x00000400 /* No Carrier */ +#define TULIP_DSTS_TxLATECOLL 0x00000200 /* Late Collision */ +#define TULIP_DSTS_TxEXCCOLL 0x00000100 /* Excessive Collisions */ +#define TULIP_DSTS_TxNOHRTBT 0x00000080 /* No Heartbeat */ +#define TULIP_DSTS_TxCOLLMASK 0x00000078 /* Collision Count (mask) */ +#define TULIP_DSTS_V_TxCOLLCNT 0x00000003 /* Collision Count (bit) */ +#define TULIP_DSTS_TxLINKFAIL 0x00000004 /* Link Failure */ +#define TULIP_DSTS_TxUNDERFLOW 0x00000002 /* Underflow Error */ +#define TULIP_DSTS_TxDEFERRED 0x00000001 /* Initially Deferred */ +/* + * Receive Status + */ +#define TULIP_DSTS_RxBADLENGTH 0x00004000 /* Length Error */ +#define TULIP_DSTS_RxDATATYPE 0x00003000 /* Data Type */ +#define TULIP_DSTS_RxRUNT 0x00000800 /* Runt Frame */ +#define TULIP_DSTS_RxMULTICAST 0x00000400 /* Multicast Frame */ +#define TULIP_DSTS_RxFIRSTDESC 0x00000200 /* First Descriptor */ +#define TULIP_DSTS_RxLASTDESC 0x00000100 /* Last Descriptor */ +#define TULIP_DSTS_RxTOOLONG 0x00000080 /* Frame Too Long */ +#define TULIP_DSTS_RxCOLLSEEN 0x00000040 /* Collision Seen */ +#define TULIP_DSTS_RxFRAMETYPE 0x00000020 /* Frame Type */ +#define TULIP_DSTS_RxWATCHDOG 0x00000010 /* Receive Watchdog */ +#define TULIP_DSTS_RxDRBBLBIT 0x00000004 /* Dribble Bit */ +#define TULIP_DSTS_RxBADCRC 0x00000002 /* CRC Error */ +#define TULIP_DSTS_RxOVERFLOW 0x00000001 /* Overflow */ + + +#define TULIP_DFLAG_ENDRING 0x0008 /* End of Transmit Ring */ +#define TULIP_DFLAG_CHAIN 0x0004 /* Chain using d_addr2 */ + +#define TULIP_DFLAG_TxWANTINTR 0x0200 /* Signal Interrupt on Completion */ +#define TULIP_DFLAG_TxLASTSEG 0x0100 /* Last Segment */ +#define TULIP_DFLAG_TxFIRSTSEG 0x0080 /* First Segment */ +#define TULIP_DFLAG_TxINVRSFILT 0x0040 /* Inverse Filtering */ +#define TULIP_DFLAG_TxSETUPPKT 0x0020 /* Setup Packet */ +#define TULIP_DFLAG_TxHASCRC 0x0010 /* Don't Append the CRC */ +#define TULIP_DFLAG_TxNOPADDING 0x0002 /* Don't AutoPad */ +#define TULIP_DFLAG_TxHASHFILT 0x0001 /* Hash/Perfect Filtering */ + +/* + * The DC21040 Registers (IO Space Addresses) + */ +#define TULIP_REG_BUSMODE 0x00 /* CSR0 -- Bus Mode */ +#define TULIP_REG_TXPOLL 0x08 /* CSR1 -- Transmit Poll Demand */ +#define TULIP_REG_RXPOLL 0x10 /* CSR2 -- Receive Poll Demand */ +#define TULIP_REG_RXLIST 0x18 /* CSR3 -- Receive List Base Addr */ +#define TULIP_REG_TXLIST 0x20 /* CSR4 -- Transmit List Base Addr */ +#define TULIP_REG_STATUS 0x28 /* CSR5 -- Status */ +#define TULIP_REG_CMD 0x30 /* CSR6 -- Command */ +#define TULIP_REG_INTR 0x38 /* CSR7 -- Interrupt Control */ +#define TULIP_REG_MISSES 0x40 /* CSR8 -- Missed Frame Counter */ +#define TULIP_REG_ADDRROM 0x48 /* CSR9 -- ENET ROM Register */ +#define TULIP_REG_RSRVD 0x50 /* CSR10 -- Reserved */ +#define TULIP_REG_FULL_DUPLEX 0x58 /* CSR11 -- Full Duplex */ +#define TULIP_REG_SIA_STATUS 0x60 /* CSR12 -- SIA Status */ +#define TULIP_REG_SIA_CONN 0x68 /* CSR13 -- SIA Connectivity */ +#define TULIP_REG_SIA_TXRX 0x70 /* CSR14 -- SIA Tx Rx */ +#define TULIP_REG_SIA_GEN 0x78 /* CSR15 -- SIA General */ + +/* + * CSR5 -- Status Register + * CSR7 -- Interrupt Control + */ +#define TULIP_STS_ERRORMASK 0x03800000L /* ( R) Error Bits (Valid when SYSERROR is set) */ +#define TULIP_STS_ERR_PARITY 0x00000000L /* 000 - Parity Error (Perform Reset) */ +#define TULIP_STS_ERR_MASTER 0x00800000L /* 001 - Master Abort */ +#define TULIP_STS_ERR_TARGET 0x01000000L /* 010 - Target Abort */ +#define TULIP_STS_TXSTATEMASK 0x00700000L /* ( R) Transmission Process State */ +#define TULIP_STS_TXS_RESET 0x00000000L /* 000 - Rset or transmit jabber expired */ +#define TULIP_STS_TXS_FETCH 0x00100000L /* 001 - Fetching transmit descriptor */ +#define TULIP_STS_TXS_WAITEND 0x00200000L /* 010 - Wait for end of transmission */ +#define TULIP_STS_TXS_READING 0x00300000L /* 011 - Read buffer and enqueue data */ +#define TULIP_STS_TXS_RSRVD 0x00400000L /* 100 - Reserved */ +#define TULIP_STS_TXS_SETUP 0x00500000L /* 101 - Setup Packet */ +#define TULIP_STS_TXS_SUSPEND 0x00600000L /* 110 - Transmit FIFO underflow or an + unavailable transmit descriptor */ +#define TULIP_STS_TXS_CLOSE 0x00700000L /* 111 - Close transmit descriptor */ +#define TULIP_STS_RXSTATEMASK 0x000E0000L /* ( R) Receive Process State*/ +#define TULIP_STS_RXS_STOPPED 0x00000000L /* 000 - Stopped */ +#define TULIP_STS_RXS_FETCH 0x00020000L /* 001 - Running -- Fetch receive descriptor */ +#define TULIP_STS_RXS_ENDCHECK 0x00040000L /* 010 - Running -- Check for end of receive + packet before prefetch of next descriptor */ +#define TULIP_STS_RXS_WAIT 0x00060000L /* 011 - Running -- Wait for receive packet */ +#define TULIP_STS_RXS_SUSPEND 0x00080000L /* 100 - Suspended -- As a result of + unavailable receive buffers */ +#define TULIP_STS_RXS_CLOSE 0x000A0000L /* 101 - Running -- Close receive descriptor */ +#define TULIP_STS_RXS_FLUSH 0x000C0000L /* 110 - Running -- Flush the current frame + from the receive FIFO as a result of + an unavailable receive buffer */ +#define TULIP_STS_RXS_DEQUEUE 0x000E0000L /* 111 - Running -- Dequeue the receive frame + from the receive FIFO into the receive + buffer. */ +#define TULIP_STS_NORMALINTR 0x00010000L /* (RW) Normal Interrupt */ +#define TULIP_STS_ABNRMLINTR 0x00008000L /* (RW) Abnormal Interrupt */ +#define TULIP_STS_SYSERROR 0x00002000L /* (RW) System Error */ +#define TULIP_STS_LINKFAIL 0x00001000L /* (RW) Link Failure */ +#define TULIP_STS_FULDPLXSHRT 0x00000800L /* (RW) Full Duplex Short Fram Rcvd */ +#define TULIP_STS_AUI 0x00000400L /* (RW) AUI/TP Switch */ +#define TULIP_STS_RXTIMEOUT 0x00000200L /* (RW) Receive Watchbog Timeout */ +#define TULIP_STS_RXSTOPPED 0x00000100L /* (RW) Receive Process Stopped */ +#define TULIP_STS_RXNOBUF 0x00000080L /* (RW) Receive Buffer Unavailable */ +#define TULIP_STS_RXINTR 0x00000040L /* (RW) Receive Interrupt */ +#define TULIP_STS_TXUNDERFLOW 0x00000020L /* (RW) Transmit Underflow */ +#define TULIP_STS_TXBABBLE 0x00000008L /* (RW) Transmit Jabber Timeout */ +#define TULIP_STS_TXNOBUF 0x00000004L /* (RW) Transmit Buffer Unavailable */ +#define TULIP_STS_TXSTOPPED 0x00000002L /* (RW) Transmit Process Stopped */ +#define TULIP_STS_TXINTR 0x00000001L /* (RW) Transmit Interrupt */ + +/* + * CSR6 -- Command (Operation Mode) Register + */ +#define TULIP_CMD_CAPTREFFCT 0x00020000L /* (RW) Capture Effect (!802.3) */ +#define TULIP_CMD_BACKPRESSURE 0x00010000L /* (RW) Back Pressure (!802.3) */ +#define TULIP_CMD_THRESHOLDCTL 0x0000C000L /* (RW) Threshold Control */ +#define TULIP_CMD_THRSHLD72 0x00000000L /* 00 - 72 Bytes */ +#define TULIP_CMD_THRSHLD96 0x00004000L /* 01 - 96 Bytes */ +#define TULIP_CMD_THRSHLD128 0x00008000L /* 10 - 128 bytes */ +#define TULIP_CMD_THRSHLD160 0x0000C000L /* 11 - 160 Bytes */ +#define TULIP_CMD_TXRUN 0x00002000L /* (RW) Start/Stop Transmitter */ +#define TULIP_CMD_FORCECOLL 0x00001000L /* (RW) Force Collisions */ +#define TULIP_CMD_OPERMODE 0x00000C00L /* (RW) Operating Mode */ +#define TULIP_CMD_FULLDULPEX 0x00000200L /* (RW) Full Duplex Mode */ +#define TULIP_CMD_FLAKYOSCDIS 0x00000100L /* (RW) Flakey Oscillator Disable */ +#define TULIP_CMD_ALLMULTI 0x00000080L /* (RW) Pass All Multicasts */ +#define TULIP_CMD_PROMISCUOUS 0x00000040L /* (RW) Promiscuous Mode */ +#define TULIP_CMD_BACKOFFCTR 0x00000020L /* (RW) Start/Stop Backoff Counter (!802.3) */ +#define TULIP_CMD_INVFILTER 0x00000010L /* (R ) Inverse Filtering */ +#define TULIP_CMD_PASSBADPKT 0x00000008L /* (RW) Pass Bad Frames */ +#define TULIP_CMD_HASHONLYFLTR 0x00000004L /* (R ) Hash Only Filtering */ +#define TULIP_CMD_RXRUN 0x00000002L /* (RW) Start/Stop Receive Filtering */ +#define TULIP_CMD_HASHPRFCTFLTR 0x00000001L /* (R ) Hash/Perfect Receive Filtering */ + + +#define TULIP_SIASTS_LINKFAIL 0x00000004L +#define TULIP_SIACONN_RESET 0x00000000L + +#define TULIP_SIACONN_AUI 0x0000000DL +#define TULIP_SIACONN_10BASET 0x00000005L + +#define TULIP_BUSMODE_SWRESET 0x00000001L + +#endif /* !defined(_DC21040_H) */ diff --git a/sys/pci/if_de.c b/sys/pci/if_de.c new file mode 100644 index 0000000..2b9774e --- /dev/null +++ b/sys/pci/if_de.c @@ -0,0 +1,1180 @@ +/*- + * Copyright (c) 1994 Matt Thomas (thomas@lkg.dec.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: if_de.c,v 1.5 1994/10/01 16:10:24 thomas Exp $ + * + * $Log: if_de.c,v $ + * Revision 1.5 1994/10/01 16:10:24 thomas + * Modifications for FreeBSD 2.0 + * + * Revision 1.4 1994/09/09 21:10:05 thomas + * mbuf debugging code + * transmit fifo owkraroudns + * + * Revision 1.3 1994/08/16 20:40:56 thomas + * New README files (one per driver) + * Minor updates to drivers (DEPCA support and add pass to attach + * output) + * + * Revision 1.2 1994/08/15 20:41:22 thomas + * Support AUI and TP. Autosense either. + * Revamp receive logic to use private kmem_alloc'ed 64K region. + * Some cleanup + * + * Revision 1.1 1994/08/12 21:01:18 thomas + * Initial revision + * + */ + +/* + * DEC DC21040 PCI Ethernet Controller + * + * Written by Matt Thomas + * BPF support code stolen directly from if_ec.c + * + * This driver supports the DEC DE435 or any other PCI + * board which support DC21040. + */ + +#include +#if NDE > 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#if NBPFILTER > 0 +#include +#include +#endif + + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#ifdef NS +#include +#include +#endif + +#include +#include +#include + + +#include +#if NPCI > 0 +#include +#include +#endif +#include +#include + +/* + * This module supports the DEC DC21040 PCI Ethernet Controller. + */ + +typedef struct { + unsigned long addr; + unsigned long length; +} tulip_addrvec_t; + +typedef struct { + tulip_desc_t *ri_first; + tulip_desc_t *ri_last; + tulip_desc_t *ri_nextin; + tulip_desc_t *ri_nextout; + int ri_max; + int ri_free; +} tulip_ringinfo_t; + +typedef struct { + volatile tulip_uint32_t *csr_busmode; /* CSR0 */ + volatile tulip_uint32_t *csr_txpoll; /* CSR1 */ + volatile tulip_uint32_t *csr_rxpoll; /* CSR2 */ + volatile tulip_uint32_t *csr_rxlist; /* CSR3 */ + volatile tulip_uint32_t *csr_txlist; /* CSR4 */ + volatile tulip_uint32_t *csr_status; /* CSR5 */ + volatile tulip_uint32_t *csr_command; /* CSR6 */ + volatile tulip_uint32_t *csr_intr; /* CSR7 */ + volatile tulip_uint32_t *csr_missed_frame; /* CSR8 */ + volatile tulip_sint32_t *csr_enetrom; /* CSR9 */ + volatile tulip_uint32_t *csr_reserved; /* CSR10 */ + volatile tulip_uint32_t *csr_full_duplex; /* CSR11 */ + volatile tulip_uint32_t *csr_sia_status; /* CSR12 */ + volatile tulip_uint32_t *csr_sia_connectivity; /* CSR13 */ + volatile tulip_uint32_t *csr_sia_tx_rx; /* CSR14 */ + volatile tulip_uint32_t *csr_sia_general; /* CSR15 */ +} tulip_regfile_t; + +/* + * The DC21040 has a stupid restriction in that the receive + * buffers must be longword aligned. But since Ethernet + * headers are not a multiple of longwords in size this forces + * the data to non-longword aligned. Since IP requires the + * data to be longword aligned, we can to copy it after it has + * been DMA'ed in our memory. + * + * Since we have to copy it anyways, we might as well as allocate + * dedicated receive space for the input. This allows to use a + * small receive buffer size and more ring entries to be able to + * better keep with a foold of tiny Ethernet packets. + * + * The receive space MUST ALWAYS be a multiple of the page size. + * And the number of receive descriptors multiplied by the size + * of the receive buffers must equal the recevive space. This + * is that we can manipulate the page tables so that even if a + * packet wraps around the end of the receive space, we can + * treat it as virtually contiguous. + */ +#define TULIP_RXBUFSIZE 512 +#define TULIP_RXDESCS 128 +#define TULIP_RXSPACE (TULIP_RXBUFSIZE * TULIP_RXDESCS) +#define TULIP_TXDESCS 128 + +typedef struct { + struct arpcom tulip_ac; + tulip_regfile_t tulip_csrs; + vm_offset_t tulip_rxspace; + unsigned tulip_high_intrspins; + unsigned tulip_flags; +#define TULIP_WANTSETUP 0x01 +#define TULIP_WANTHASH 0x02 +#define TULIP_DOINGSETUP 0x04 +#define TULIP_ALTPHYS 0x08 /* use AUI */ + unsigned char tulip_rombuf[32]; + tulip_uint32_t tulip_setupbuf[192/sizeof(tulip_uint32_t)]; + tulip_uint32_t tulip_setupdata[192/sizeof(tulip_uint32_t)]; + tulip_uint32_t tulip_intrmask; + tulip_uint32_t tulip_cmdmode; + tulip_uint32_t tulip_revinfo; +#if NBPFILTER > 0 + caddr_t tulip_bpf; /* BPF context */ +#endif + struct ifqueue tulip_txq; + tulip_ringinfo_t tulip_rxinfo; + tulip_ringinfo_t tulip_txinfo; +} tulip_softc_t; + +#ifndef IFF_ALTPHYS +#define IFF_ALTPHYS IFF_LINK0 /* In case it isn't defined */ +#endif +tulip_softc_t *tulips[NDE]; +unsigned tulip_intrs[NDE]; + +#define tulip_if tulip_ac.ac_if +#define tulip_unit tulip_ac.ac_if.if_unit +#define tulip_name tulip_ac.ac_if.if_name +#define tulip_hwaddr tulip_ac.ac_enaddr + +#define TULIP_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian */ +#define TULIP_CHECK_RXCRC 0 +#define TULIP_MAX_TXSEG 32 + +#define TULIP_ADDREQUAL(a1, a2) \ + (((u_short *)a1)[0] == ((u_short *)a2)[0] \ + || ((u_short *)a1)[1] == ((u_short *)a2)[1] \ + || ((u_short *)a1)[2] == ((u_short *)a2)[2]) +#define TULIP_ADDRBRDCST(a1) \ + (((u_short *)a1)[0] == 0xFFFFU \ + || ((u_short *)a1)[1] == 0xFFFFU \ + || ((u_short *)a1)[2] == 0xFFFFU) + +static void tulip_start(struct ifnet *ifp); +static void tulip_addr_filter(tulip_softc_t *sc); + +#if __FreeBSD__ > 1 +#define TULIP_IFRESET_ARGS int unit +#define TULIP_RESET(sc) tulip_reset((sc)->tulip_unit) +#else +#define TULIP_IFRESET_ARGS int unit, int uban +#define TULIP_RESET(sc) tulip_reset((sc)->tulip_unit, 0) +#endif + +static void +tulip_reset( + TULIP_IFRESET_ARGS) +{ + tulip_softc_t *sc = tulips[unit]; + tulip_ringinfo_t *ri; + tulip_desc_t *di; + vm_offset_t vmoff; + + *sc->tulip_csrs.csr_busmode = TULIP_BUSMODE_SWRESET; + DELAY(10); /* Wait 10 microsends (actually 50 PCI cycles but at + 33MHz that comes to two microseconds but wait a + bit longer anyways) */ + + /* + * Use the + */ + *sc->tulip_csrs.csr_sia_connectivity = TULIP_SIACONN_RESET; + if (sc->tulip_if.if_flags & IFF_ALTPHYS) { + if ((sc->tulip_flags & TULIP_ALTPHYS) == 0) + printf("%s%d: enabling Thinwire/AUI port\n", + sc->tulip_if.if_name, sc->tulip_if.if_unit); + *sc->tulip_csrs.csr_sia_connectivity = TULIP_SIACONN_AUI; + sc->tulip_flags |= TULIP_ALTPHYS; + } else { + if (sc->tulip_flags & TULIP_ALTPHYS) + printf("%s%d: enabling 10baseT/UTP port\n", + sc->tulip_if.if_name, sc->tulip_if.if_unit); + *sc->tulip_csrs.csr_sia_connectivity = TULIP_SIACONN_10BASET; + sc->tulip_flags &= ~TULIP_ALTPHYS; + } + *sc->tulip_csrs.csr_txlist = vtophys(&sc->tulip_txinfo.ri_first[0]); + *sc->tulip_csrs.csr_rxlist = vtophys(&sc->tulip_rxinfo.ri_first[0]); + *sc->tulip_csrs.csr_intr = 0; + *sc->tulip_csrs.csr_busmode = 0x4800; + + sc->tulip_txq.ifq_maxlen = TULIP_TXDESCS; + /* + * Free all the mbufs that were on the transmit ring. + */ + for (;;) { + struct mbuf *m; + IF_DEQUEUE(&sc->tulip_txq, m); + if (m == NULL) + break; + m_freem(m); + } + + ri = &sc->tulip_txinfo; + ri->ri_nextin = ri->ri_nextout = ri->ri_first; + ri->ri_free = ri->ri_max; + for (di = ri->ri_first; di < ri->ri_last; di++) + di->d_status = 0; + + /* + * We need to collect all the mbufs were on the + * receive ring before we reinit it either to put + * them back on or to know if we have to allocate + * more. + */ + ri = &sc->tulip_rxinfo; + ri->ri_nextin = ri->ri_nextout = ri->ri_first; + ri->ri_free = ri->ri_max; + for (vmoff = vtophys(sc->tulip_rxspace), di = ri->ri_first; + di < ri->ri_last; di++, vmoff += TULIP_RXBUFSIZE) { + di->d_status |= TULIP_DSTS_OWNER; + di->d_length1 = TULIP_RXBUFSIZE; di->d_addr1 = vmoff; + di->d_length2 = 0; di->d_addr2 = 0; + } + + sc->tulip_intrmask = TULIP_STS_NORMALINTR|TULIP_STS_RXINTR|TULIP_STS_TXINTR + |TULIP_STS_ABNRMLINTR|TULIP_STS_SYSERROR|TULIP_STS_TXSTOPPED + |TULIP_STS_TXBABBLE|TULIP_STS_LINKFAIL|TULIP_STS_RXSTOPPED; + sc->tulip_flags &= ~(TULIP_DOINGSETUP|TULIP_WANTSETUP); + tulip_addr_filter(sc); +} + +static void +tulip_init( + int unit) +{ + tulip_softc_t *sc = tulips[unit]; + unsigned new_cmdmode; + + if (sc->tulip_if.if_flags & IFF_UP) { + sc->tulip_if.if_flags |= IFF_RUNNING; + if (sc->tulip_if.if_flags & IFF_PROMISC) { + sc->tulip_cmdmode |= TULIP_CMD_PROMISCUOUS; + } else { + sc->tulip_cmdmode &= ~TULIP_CMD_PROMISCUOUS; + if (sc->tulip_if.if_flags & IFF_ALLMULTI) { + sc->tulip_cmdmode |= TULIP_CMD_ALLMULTI; + } else { + sc->tulip_cmdmode &= ~TULIP_CMD_ALLMULTI; + } + } + sc->tulip_cmdmode |= TULIP_CMD_TXRUN; + if ((sc->tulip_flags & TULIP_WANTSETUP) == 0) { + sc->tulip_cmdmode |= TULIP_CMD_RXRUN; + sc->tulip_intrmask |= TULIP_STS_RXSTOPPED; + } else { + sc->tulip_intrmask &= ~TULIP_STS_RXSTOPPED; + tulip_start(&sc->tulip_if); + } + tulip_cmdnode |= TULIP_CMD_THRSHLD160; + *sc->tulip_csrs.csr_intr = sc->tulip_intrmask; + *sc->tulip_csrs.csr_command = sc->tulip_cmdmode; + } else { + TULIP_RESET(sc); + sc->tulip_if.if_flags &= ~IFF_RUNNING; + } +} + +static struct { + unsigned notwhole; + unsigned rxerror; + unsigned nombufs[2]; + unsigned rcvs; +#if TULIP_CHECK_RXCRC + unsigned badcrc; +#endif + unsigned badsop; +} tulip_rx; + +#if TULIP_CHECK_RXCRC +static unsigned +tulip_crc32( + u_char *addr, + int len) +{ + unsigned int crc = 0xFFFFFFFF; + static unsigned int crctbl[256]; + int idx; + static int done; + /* + * initialize the multicast address CRC table + */ + for (idx = 0; !done && idx < 256; idx++) { + unsigned int tmp = idx; + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + tmp = (tmp >> 1) ^ (tmp & 1 ? TULIP_CRC32_POLY : 0); /* XOR */ + crctbl[idx] = tmp; + } + done = 1; + + while (len-- > 0) + crc = (crc >> 8) ^ crctbl[*addr++] ^ crctbl[crc & 0xFF]; + + return crc; +} +#endif + +static void +tulip_rx_intr( + tulip_softc_t *sc) +{ + tulip_ringinfo_t *ri = &sc->tulip_rxinfo; + + for (;; tulip_rx.rcvs++) { + tulip_desc_t *eop; + int total_len, ndescs; + caddr_t bufaddr = (caddr_t) sc->tulip_rxspace; + + for (ndescs = 1, eop = ri->ri_nextin;; ndescs++) { + if (((volatile tulip_desc_t *) eop)->d_status & TULIP_DSTS_OWNER) + return; + + if ((eop->d_status & TULIP_DSTS_RxFIRSTDESC) && eop != ri->ri_nextin) { + tulip_rx.badsop++; + } + if (eop->d_status & TULIP_DSTS_RxLASTDESC) + break; + if (++eop == ri->ri_last) + eop = ri->ri_first; + } + + bufaddr += TULIP_RXBUFSIZE * (ri->ri_nextin - ri->ri_first); + total_len = ((eop->d_status >> 16) & 0x7FF) - 4; + + if ((eop->d_status & TULIP_DSTS_ERRSUM) == 0) { + struct ether_header eh; + struct mbuf *m; + +#if TULIP_CHECK_RXCRC + unsigned crc = tulip_crc32(bufaddr, total_len); + if (~crc != *((unsigned *) &bufaddr[total_len])) { + printf("de0: %d: bad rx crc: %08x [rx] != %08x\n", + tulip_rx.rcvs, + *((unsigned *) &bufaddr[total_len]), ~crc); + goto next; + } +#endif + eh = *(struct ether_header *) bufaddr; + eh.ether_type = ntohs(eh.ether_type); +#if NBPFILTER > 0 + if (sc->tulip_bpf != NULL) { + bpf_tap(sc->tulip_bpf, bufaddr, total_len); + if (eh.ether_type != ETHERTYPE_IP && eh.ether_type != ETHERTYPE_ARP) + goto next; + if ((eh.ether_dhost[0] & 1) == 0 && + !TULIP_ADDREQUAL(eh.ether_dhost, sc->tulip_ac.ac_enaddr)) + goto next; + } else if (!TULIP_ADDREQUAL(eh.ether_dhost, sc->tulip_ac.ac_enaddr) + && !TULIP_ADDRBRDCST(eh.ether_dhost)) { + goto next; + } +#endif + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m != NULL) { + total_len -= sizeof(eh); + if (total_len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + tulip_rx.nombufs[1]++; + sc->tulip_if.if_ierrors++; + goto next; + } + } + bcopy(bufaddr + sizeof(eh), mtod(m, caddr_t), total_len); + m->m_len = m->m_pkthdr.len = total_len; + ether_input(&sc->tulip_if, &eh, m); + } else { + tulip_rx.nombufs[0]++; + sc->tulip_if.if_ierrors++; + } + } else { + tulip_rx.rxerror++; + sc->tulip_if.if_ierrors++; + } +next: + sc->tulip_if.if_ipackets++; + while (ndescs-- > 0) { + ri->ri_nextin->d_status |= TULIP_DSTS_OWNER; + if (++ri->ri_nextin == ri->ri_last) + ri->ri_nextin = ri->ri_first; + } + } +} + +static int +tulip_tx_intr( + tulip_softc_t *sc) +{ + tulip_ringinfo_t *ri = &sc->tulip_txinfo; + struct mbuf *m; + int xmits = 0; + + while (ri->ri_free < ri->ri_max) { + if (((volatile tulip_desc_t *) ri->ri_nextin)->d_status & TULIP_DSTS_OWNER) + break; + + if (ri->ri_nextin->d_flag & TULIP_DFLAG_TxLASTSEG) { + if (ri->ri_nextin->d_flag & TULIP_DFLAG_TxSETUPPKT) { + /* + * We've just finished processing a setup packet. + * Mark that we can finished it. If there's not + * another pending, startup the TULIP receiver. + */ + sc->tulip_flags &= ~TULIP_DOINGSETUP; + if ((sc->tulip_flags & TULIP_WANTSETUP) == 0) { + sc->tulip_cmdmode |= TULIP_CMD_RXRUN; + sc->tulip_intrmask |= TULIP_STS_RXSTOPPED; + *sc->tulip_csrs.csr_command = sc->tulip_cmdmode; + *sc->tulip_csrs.csr_intr = sc->tulip_intrmask; + } + } else { + IF_DEQUEUE(&sc->tulip_txq, m); + m_freem(m); + sc->tulip_if.if_collisions += + (ri->ri_nextin->d_status & TULIP_DSTS_TxCOLLMASK) + >> TULIP_DSTS_V_TxCOLLCNT; + if (ri->ri_nextin->d_status & TULIP_DSTS_ERRSUM) + sc->tulip_if.if_oerrors++; + xmits++; + } + } + + if (++ri->ri_nextin == ri->ri_last) + ri->ri_nextin = ri->ri_first; + ri->ri_free++; + sc->tulip_if.if_flags &= ~IFF_OACTIVE; + } + sc->tulip_if.if_opackets += xmits; + return xmits; +} + +static int +tulip_txsegment( + tulip_softc_t *sc, + struct mbuf *m, + tulip_addrvec_t *avp, + size_t maxseg) +{ + int segcnt; + + for (segcnt = 0; m; m = m->m_next) { + int len = m->m_len; + caddr_t addr = mtod(m, caddr_t); + unsigned clsize = CLBYTES - (((u_long) addr) & (CLBYTES-1)); + + while (len > 0) { + unsigned slen = min(len, clsize); + if (segcnt < maxseg) { + avp->addr = vtophys(addr); + avp->length = slen; + } + len -= slen; + addr += slen; + clsize = CLBYTES; + avp++; + segcnt++; + } + } + if (segcnt >= maxseg) { + printf("%s%d: tulip_txsegment: extremely fragmented packet dropped (%d segments)\n", + sc->tulip_name, sc->tulip_unit, segcnt); + return -1; + } + avp->addr = 0; + avp->length = 0; + return segcnt; +} + +static void +tulip_start( + struct ifnet *ifp) +{ + tulip_softc_t *sc = (tulip_softc_t *) ifp; + struct ifqueue *ifq = &ifp->if_snd; + tulip_ringinfo_t *ri = &sc->tulip_txinfo; + tulip_desc_t *sop, *eop; + struct mbuf *m; + tulip_addrvec_t addrvec[TULIP_MAX_TXSEG+1], *avp; + int segcnt; + tulip_uint32_t d_status; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + for (;;) { + if (sc->tulip_flags & TULIP_WANTSETUP) { + if ((sc->tulip_flags & TULIP_DOINGSETUP) || ri->ri_free == 1) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + bcopy(sc->tulip_setupdata, sc->tulip_setupbuf, + sizeof(sc->tulip_setupbuf)); + sc->tulip_flags &= ~TULIP_WANTSETUP; + sc->tulip_flags |= TULIP_DOINGSETUP; + ri->ri_free--; + ri->ri_nextout->d_flag &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN; + ri->ri_nextout->d_flag |= TULIP_DFLAG_TxFIRSTSEG|TULIP_DFLAG_TxLASTSEG + |TULIP_DFLAG_TxSETUPPKT|TULIP_DFLAG_TxWANTINTR; + if (sc->tulip_flags & TULIP_WANTHASH) + ri->ri_nextout->d_flag |= TULIP_DFLAG_TxHASHFILT; + ri->ri_nextout->d_length1 = sizeof(sc->tulip_setupbuf); + ri->ri_nextout->d_addr1 = vtophys(sc->tulip_setupbuf); + ri->ri_nextout->d_length2 = 0; + ri->ri_nextout->d_addr2 = 0; + ri->ri_nextout->d_status = TULIP_DSTS_OWNER; + *sc->tulip_csrs.csr_txpoll = 1; + /* + * Advance the ring for the next transmit packet. + */ + if (++ri->ri_nextout == ri->ri_last) + ri->ri_nextout = ri->ri_first; + } + + IF_DEQUEUE(ifq, m); + if (m == NULL) + break; + + /* + * First find out how many and which different pages + * the mbuf data occupies. Then check to see if we + * have enough descriptor space in our transmit ring + * to actually send it. + */ + segcnt = tulip_txsegment(sc, m, addrvec, + min(ri->ri_max - 1, TULIP_MAX_TXSEG)); + if (segcnt < 0) { +#if 0 + struct mbuf *m0; + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 != NULL) { + if (m->m_pkthdr.len > MHLEN) { + MCLGET(m0, M_DONTWAIT); + if ((m0->m_flags & M_EXT) == 0) { + m_freem(m); + continue; + } + } + m_copydata(m, 0, mtod(m0, caddr_t), m->m_pkthdr.len); + m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len; + m_freem(m); + IF_PREPEND(ifq, m0); + continue; + } else { +#endif + m_freem(m); + continue; +#if 0 + } +#endif + } + if (ri->ri_free - 2 <= (segcnt + 1) / 2) + break; + + ri->ri_free -= (segcnt + 1) / 2; + /* + * Now we fill in our transmit descriptors. This is + * a bit reminiscent of going on the Ark two by two + * since each descriptor for the TULIP can describe + * two buffers. So we advance through the address + * vector two entries at a time to to fill each + * descriptor. Clear the first and last segment bits + * in each descriptor (actually just clear everything + * but the end-of-ring or chain bits) to make sure + * we don't get messed up by previously sent packets. + */ + sop = ri->ri_nextout; + d_status = 0; + avp = addrvec; + do { + eop = ri->ri_nextout; + eop->d_flag &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN; + eop->d_status = d_status; + eop->d_addr1 = avp->addr; eop->d_length1 = avp->length; avp++; + eop->d_addr2 = avp->addr; eop->d_length2 = avp->length; avp++; + d_status = TULIP_DSTS_OWNER; + if (++ri->ri_nextout == ri->ri_last) + ri->ri_nextout = ri->ri_first; + } while ((segcnt -= 2) > 0); + + /* + * The descriptors have been filled in. Mark the first + * and last segments, indicate we want a transmit complete + * interrupt, give the descriptors to the TULIP, and tell + * it to transmit! + */ + IF_ENQUEUE(&sc->tulip_txq, m); + eop->d_flag |= TULIP_DFLAG_TxLASTSEG|TULIP_DFLAG_TxWANTINTR; + sop->d_flag |= TULIP_DFLAG_TxFIRSTSEG; + sop->d_status = TULIP_DSTS_OWNER; + + *sc->tulip_csrs.csr_txpoll = 1; + } + if (m != NULL) { + ifp->if_flags |= IFF_OACTIVE; + IF_PREPEND(ifq, m); + } +} + +static int +tulip_intr( + int unit) +{ + tulip_softc_t *sc = tulips[unit]; + tulip_uint32_t csr; + unsigned spins = 0; + + tulip_intrs[unit]++; + + while ((csr = *sc->tulip_csrs.csr_status) & (TULIP_STS_NORMALINTR|TULIP_STS_ABNRMLINTR)) { + *sc->tulip_csrs.csr_status = csr & sc->tulip_intrmask; + spins++; + + if (csr & TULIP_STS_SYSERROR) { + if ((csr & TULIP_STS_ERRORMASK) == TULIP_STS_ERR_PARITY) { + TULIP_RESET(sc); + tulip_init(sc->tulip_unit); + return unit; + } + } + if (csr & TULIP_STS_RXINTR) + tulip_rx_intr(sc); + if (sc->tulip_txinfo.ri_free < sc->tulip_txinfo.ri_max) { + tulip_tx_intr(sc); + tulip_start(&sc->tulip_if); + } + if (csr & TULIP_STS_ABNRMLINTR) { + printf("%s%d: abnormal interrupt: 0x%05x [0x%05x]\n", + sc->tulip_name, sc->tulip_unit, csr, csr & sc->tulip_intrmask); + *sc->tulip_csrs.csr_command = sc->tulip_cmdmode; + } + } + if (spins > sc->tulip_high_intrspins) + sc->tulip_high_intrspins = spins; + return unit; +} + +/* + * This is the standard method of reading the DEC Address ROMS. + */ +static int +tulip_read_macaddr( + tulip_softc_t *sc) +{ + int cksum, rom_cksum, idx; + tulip_sint32_t csr; + unsigned char tmpbuf[8]; + static u_char testpat[] = { 0xFF, 0, 0x55, 0xAA, 0xFF, 0, 0x55, 0xAA }; + + *sc->tulip_csrs.csr_enetrom = 1; + for (idx = 0; idx < 32; idx++) { + int cnt = 0; + while ((csr = *sc->tulip_csrs.csr_enetrom) < 0 && cnt < 10000) + cnt++; + sc->tulip_rombuf[idx] = csr & 0xFF; + } + + if (bcmp(&sc->tulip_rombuf[0], &sc->tulip_rombuf[16], 8) != 0) + return -4; + if (bcmp(&sc->tulip_rombuf[24], testpat, 8) != 0) + return -3; + + tmpbuf[0] = sc->tulip_rombuf[15]; tmpbuf[1] = sc->tulip_rombuf[14]; + tmpbuf[2] = sc->tulip_rombuf[13]; tmpbuf[3] = sc->tulip_rombuf[12]; + tmpbuf[4] = sc->tulip_rombuf[11]; tmpbuf[5] = sc->tulip_rombuf[10]; + tmpbuf[6] = sc->tulip_rombuf[9]; tmpbuf[7] = sc->tulip_rombuf[8]; + if (bcmp(&sc->tulip_rombuf[0], tmpbuf, 8) != 0) + return -2; + + bcopy(sc->tulip_rombuf, sc->tulip_hwaddr, 6); + + cksum = *(u_short *) &sc->tulip_hwaddr[0]; + cksum *= 2; + if (cksum > 65535) cksum -= 65535; + cksum += *(u_short *) &sc->tulip_hwaddr[2]; + if (cksum > 65535) cksum -= 65535; + cksum *= 2; + if (cksum > 65535) cksum -= 65535; + cksum += *(u_short *) &sc->tulip_hwaddr[4]; + if (cksum >= 65535) cksum -= 65535; + + rom_cksum = *(u_short *) &sc->tulip_rombuf[6]; + + if (cksum != rom_cksum) + return -1; + return 0; +} + +static unsigned +tulip_mchash( + unsigned char *mca) +{ + u_int idx, bit, data, crc = 0xFFFFFFFFUL; + +#ifdef __alpha + for (data = *(__unaligned u_long *) mca, bit = 0; bit < 48; bit++, data >>= +1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? TULIP_CRC32_POLY : 0); +#else + for (idx = 0; idx < 6; idx++) + for (data = *mca++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? TULIP_CRC32_POLY : 0); +#endif + return crc & 0x1FF; +} + +static void +tulip_addr_filter( + tulip_softc_t *sc) +{ + tulip_uint32_t *sp = sc->tulip_setupdata; + struct ether_multistep step; + struct ether_multi *enm; + int i; + + sc->tulip_flags &= ~TULIP_WANTHASH; + sc->tulip_flags |= TULIP_WANTSETUP; + sc->tulip_cmdmode &= ~TULIP_CMD_RXRUN; + sc->tulip_intrmask &= ~TULIP_STS_RXSTOPPED; + if (sc->tulip_ac.ac_multicnt > 14) { + unsigned hash; + /* + * If we have more than 14 multicasts, we have + * go into hash perfect mode (512 bit multicast + * hash and one perfect hardware). + */ + + bzero(sc->tulip_setupdata, sizeof(sc->tulip_setupdata)); + hash = tulip_mchash(etherbroadcastaddr); + sp[hash >> 4] |= 1 << (hash & 0xF); + ETHER_FIRST_MULTI(step, &sc->tulip_ac, enm); + while (enm != NULL) { + hash = tulip_mchash(enm->enm_addrlo); + sp[hash >> 4] |= 1 << (hash & 0xF); + ETHER_NEXT_MULTI(step, enm); + } + sc->tulip_cmdmode |= TULIP_WANTHASH; + sp[40] = ((u_short *) sc->tulip_ac.ac_enaddr)[0]; + sp[41] = ((u_short *) sc->tulip_ac.ac_enaddr)[1]; + sp[42] = ((u_short *) sc->tulip_ac.ac_enaddr)[2]; + } else { + /* + * Else can get perfect filtering for 16 addresses. + */ + i = 0; + ETHER_FIRST_MULTI(step, &sc->tulip_ac, enm); + for (; enm != NULL; i++) { + *sp++ = ((u_short *) enm->enm_addrlo)[0]; + *sp++ = ((u_short *) enm->enm_addrlo)[1]; + *sp++ = ((u_short *) enm->enm_addrlo)[2]; + ETHER_NEXT_MULTI(step, enm); + } + /* + * If an IP address is enabled, turn on broadcast + */ + if (sc->tulip_ac.ac_ipaddr.s_addr != 0) { + i++; + *sp++ = 0xFFFF; + *sp++ = 0xFFFF; + *sp++ = 0xFFFF; + } + /* + * Pad the rest with our hardware address + */ + for (; i < 16; i++) { + *sp++ = ((u_short *) sc->tulip_ac.ac_enaddr)[0]; + *sp++ = ((u_short *) sc->tulip_ac.ac_enaddr)[1]; + *sp++ = ((u_short *) sc->tulip_ac.ac_enaddr)[2]; + } + } +} + +static int +tulip_ioctl( + struct ifnet *ifp, + int cmd, + caddr_t data) +{ + tulip_softc_t *sc = tulips[ifp->if_unit]; + int s, error = 0; + + s = splimp(); + + switch (cmd) { + case SIOCSIFADDR: { + struct ifaddr *ifa = (struct ifaddr *)data; + + ifp->if_flags |= IFF_UP; + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: { + ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + (*ifp->if_init)(ifp->if_unit); + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; + } +#endif /* INET */ + +#ifdef NS + /* This magic copied from if_is.c; I don't use XNS, + * so I have no way of telling if this actually + * works or not. + */ + case AF_NS: { + struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + if (ns_nullhost(*ina)) { + ina->x_host = *(union ns_host *)(sc->tulip_ac.ac_enaddr); + } else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->tulip_ac.ac_enaddr, + sizeof sc->tulip_ac.ac_enaddr); + } + + (*ifp->if_init)(ifp->if_unit); + break; + } +#endif /* NS */ + + default: { + (*ifp->if_init)(ifp->if_unit); + break; + } + } + break; + } + + case SIOCSIFFLAGS: { + /* + * Changing the connection forces a reset. + */ + if (sc->tulip_flags & TULIP_ALTPHYS) { + if ((ifp->if_flags & IFF_ALTPHYS) == 0) + TULIP_RESET(sc); + } else { + if (ifp->if_flags & IFF_ALTPHYS) + TULIP_RESET(sc); + } + (*ifp->if_init)(ifp->if_unit); + break; + } + + case SIOCADDMULTI: + case SIOCDELMULTI: { + /* + * Update multicast listeners + */ + if (cmd == SIOCADDMULTI) + error = ether_addmulti((struct ifreq *)data, &sc->tulip_ac); + else + error = ether_delmulti((struct ifreq *)data, &sc->tulip_ac); + + if (error == ENETRESET) { + tulip_addr_filter(sc); /* reset multicast filtering */ + (*ifp->if_init)(ifp->if_unit); + error = 0; + } + break; + } + + default: { + error = EINVAL; + break; + } + } + + splx(s); + return error; +} + +static void +tulip_attach( + tulip_softc_t *sc) +{ + struct ifnet *ifp = &sc->tulip_if; + struct ifaddr *ifa = ifp->if_addrlist; + int cnt; + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; + ifp->if_flags |= IFF_MULTICAST; + + *sc->tulip_csrs.csr_sia_connectivity = 0; + *sc->tulip_csrs.csr_sia_connectivity = TULIP_SIACONN_10BASET; + for (cnt = 0; cnt < 240000; cnt++) { + if ((*sc->tulip_csrs.csr_sia_status & TULIP_SIASTS_LINKFAIL) == 0) + break; + DELAY(10); + } + if (*sc->tulip_csrs.csr_sia_status & TULIP_SIASTS_LINKFAIL) { + ifp->if_flags |= IFF_ALTPHYS; + } else { + sc->tulip_flags |= TULIP_ALTPHYS; + } + TULIP_RESET(sc); + + ifp->if_init = tulip_init; + ifp->if_ioctl = tulip_ioctl; + ifp->if_output = ether_output; + ifp->if_reset = tulip_reset; + ifp->if_start = tulip_start; + ifp->if_mtu = ETHERMTU; + ifp->if_type = IFT_ETHER; + ifp->if_addrlen = 6; + ifp->if_hdrlen = 14; + + printf("%s%d: DC21040 pass %d.%d (TULIP) ethernet address %s\n", + sc->tulip_name, sc->tulip_unit, + (sc->tulip_revinfo & 0xF0) >> 4, + sc->tulip_revinfo & 0x0F, + ether_sprintf(sc->tulip_hwaddr)); + +#if NBPFILTER > 0 + bpfattach(&sc->tulip_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + + if_attach(ifp); + + while (ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK) + ifa = ifa->ifa_next; + + if (ifa != NULL && ifa->ifa_addr != NULL) { + struct sockaddr_dl *sdl; + /* + * Provide our ether address to the higher layers + */ + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = 6; + sdl->sdl_slen = 0; + bcopy(sc->tulip_ac.ac_enaddr, LLADDR(sdl), 6); + } +} + +static void +tulip_initcsrs( + tulip_softc_t *sc, + volatile tulip_uint32_t *va_csrs, + size_t csr_size) +{ + sc->tulip_csrs.csr_busmode = va_csrs + 0 * csr_size; + sc->tulip_csrs.csr_txpoll = va_csrs + 1 * csr_size; + sc->tulip_csrs.csr_rxpoll = va_csrs + 2 * csr_size; + sc->tulip_csrs.csr_rxlist = va_csrs + 3 * csr_size; + sc->tulip_csrs.csr_txlist = va_csrs + 4 * csr_size; + sc->tulip_csrs.csr_status = va_csrs + 5 * csr_size; + sc->tulip_csrs.csr_command = va_csrs + 6 * csr_size; + sc->tulip_csrs.csr_intr = va_csrs + 7 * csr_size; + sc->tulip_csrs.csr_missed_frame = va_csrs + 8 * csr_size; + sc->tulip_csrs.csr_enetrom = va_csrs + 9 * csr_size; + sc->tulip_csrs.csr_reserved = va_csrs + 10 * csr_size; + sc->tulip_csrs.csr_full_duplex = va_csrs + 11 * csr_size; + sc->tulip_csrs.csr_sia_status = va_csrs + 12 * csr_size; + sc->tulip_csrs.csr_sia_connectivity = va_csrs + 13 * csr_size; + sc->tulip_csrs.csr_sia_tx_rx = va_csrs + 14 * csr_size; + sc->tulip_csrs.csr_sia_general = va_csrs + 15 * csr_size; +} + +static void +tulip_initring( + tulip_softc_t *sc, + tulip_ringinfo_t *ri, + tulip_desc_t *descs, + int ndescs) +{ + ri->ri_max = ndescs; + ri->ri_first = descs; + ri->ri_last = ri->ri_first + ri->ri_max; + bzero((caddr_t) ri->ri_first, sizeof(ri->ri_first[0]) * ri->ri_max); + ri->ri_last[-1].d_flag = TULIP_DFLAG_ENDRING; +} + +#if NPCI > 0 +/* + * This is the PCI configuration support. Since the DC21040 is available + * on both EISA and PCI boards, one must be careful in how defines the + * DC21040 in the config file. + */ +static int tulip_pci_probe(pcici_t config_id); +static int tulip_pci_attach(pcici_t config_id); + +struct pci_driver dedevice = { + tulip_pci_probe, + tulip_pci_attach, + 0x00021011ul, +#if __FreeBSD__ == 1 + "de", +#endif + "digital dc21040 ethernet", + tulip_intr +}; + +#define PCI_CFID 0x00 /* Configuration ID */ +#define PCI_CFCS 0x04 /* Configurtion Command/Status */ +#define PCI_CFRV 0x08 /* Configuration Revision */ +#define PCI_CFLT 0x0c /* Configuration Latency Timer */ +#define PCI_CBIO 0x10 /* Configuration Base IO Address */ +#define PCI_CBMA 0x14 /* Configuration Base Memory Address */ +#define PCI_CFIT 0x3c /* Configuration Interrupt */ +#define PCI_CFDA 0x40 /* Configuration Driver Area */ + +#define TULIP_PCI_CSRSIZE (8 / sizeof(tulip_uint32_t)) +static int +tulip_pci_probe( + pcici_t config_id) +{ + int idx; + for (idx = 0; idx < NDE; idx++) + if (tulips[idx] == NULL) + return idx; + return -1; +} + +static int +tulip_pci_attach( + pcici_t config_id) +{ + tulip_softc_t *sc; + int retval, idx, revinfo, unit; + signed int csr; + vm_offset_t va_csrs, pa_csrs; + int result; + tulip_desc_t *rxdescs, *txdescs; + + unit = tulip_pci_probe(config_id); + + sc = (tulip_softc_t *) malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); + if (sc == NULL) + return -1; + + rxdescs = (tulip_desc_t *) + malloc(sizeof(tulip_desc_t) * TULIP_RXDESCS, M_DEVBUF, M_NOWAIT); + if (rxdescs == NULL) { + free((caddr_t) sc, M_DEVBUF); + return -1; + } + + txdescs = (tulip_desc_t *) + malloc(sizeof(tulip_desc_t) * TULIP_TXDESCS, M_DEVBUF, M_NOWAIT); + if (txdescs == NULL) { + free((caddr_t) rxdescs, M_DEVBUF); + free((caddr_t) sc, M_DEVBUF); + return -1; + } + + bzero(sc, sizeof(sc)); /* Zero out the softc*/ + sc->tulip_rxspace = kmem_alloc(kernel_map, TULIP_RXSPACE + NBPG); + /* + * We've allocated an extra page of receive space so we can double map + * the first page of the receive space into the page after the last page + * of the receive space. This means that even if a receive wraps around + * the end of the receive space, it will still virtually contiguous and + * that greatly simplifies the recevie logic. + */ + pmap_enter(pmap_kernel(), sc->tulip_rxspace + TULIP_RXSPACE, + vtophys(sc->tulip_rxspace), VM_PROT_READ, TRUE); + + sc->tulip_unit = unit; + sc->tulip_name = "de"; + retval = pci_map_mem(config_id, PCI_CBMA, &va_csrs, &pa_csrs); + if (retval) { + printf("de%d: pci_map_mem failed.\n", unit); + kmem_free(kernel_map, sc->tulip_rxspace, TULIP_RXSPACE + NBPG); + free((caddr_t) txdescs, M_DEVBUF); + free((caddr_t) rxdescs, M_DEVBUF); + free((caddr_t) sc, M_DEVBUF); + return -1; + } + tulips[unit] = sc; + tulip_initcsrs(sc, (volatile tulip_uint32_t *) va_csrs, TULIP_PCI_CSRSIZE); + tulip_initring(sc, &sc->tulip_rxinfo, rxdescs, TULIP_RXDESCS); + tulip_initring(sc, &sc->tulip_txinfo, txdescs, TULIP_TXDESCS); + sc->tulip_revinfo = pci_conf_read(config_id, PCI_CFRV); + if ((retval = tulip_read_macaddr(sc)) < 0) { + printf("de%d: can't read ENET ROM (why=%d) (", sc->tulip_unit, retval); + for (idx = 0; idx < 32; idx++) + printf("%02x", sc->tulip_rombuf[idx]); + printf("\n"); + printf("%s%d: DC21040 %d.%d ethernet address %s\n", + sc->tulip_name, sc->tulip_unit, + (sc->tulip_revinfo & 0xF0) >> 4, sc->tulip_revinfo & 0x0F, + "unknown"); + } else { + TULIP_RESET(sc); + tulip_attach(sc); + } + return 1; +} +#endif /* NPCI > 0 */ +#endif /* NDE > 0 */ -- cgit v1.1