diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2005-07-28 01:31:23 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2005-07-28 01:31:23 +0000 |
commit | dbddc1295cdb0f1631ba635a872ba2c7dd04a39c (patch) | |
tree | 87669697e381f874687c0a81310f290482ce663c /sys/dev | |
parent | b26a00201df96bb652e7c198e476e54856bcb3c8 (diff) |
New driver for the AMD PCnet-PCI family of Ethernet chips. This
driver uses direct DMA to mbufs (like other PCI network drivers,
and unlike the old "le at pci" driver), and also supports communication
with the MII-connected PHYs on the 10/100 boards.
From NetBSD
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/am79900reg.h | 150 | ||||
-rw-r--r-- | sys/dev/ic/lancereg.h | 612 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 8 | ||||
-rw-r--r-- | sys/dev/pci/if_pcn.c | 2128 | ||||
-rw-r--r-- | sys/dev/pci/if_pcnreg.h | 78 |
5 files changed, 2975 insertions, 1 deletions
diff --git a/sys/dev/ic/am79900reg.h b/sys/dev/ic/am79900reg.h new file mode 100644 index 00000000000..87ef1c724be --- /dev/null +++ b/sys/dev/ic/am79900reg.h @@ -0,0 +1,150 @@ +/* $OpenBSD: am79900reg.h,v 1.1 2005/07/28 01:31:22 brad Exp $ */ +/* $NetBSD: am79900reg.h,v 1.7 2005/02/27 00:27:00 perry Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)if_lereg.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Receive message descriptor + */ +struct lermd { + u_int32_t rmd0; + u_int32_t rmd1; + u_int32_t rmd2; + int32_t rmd3; +}; + +/* + * Transmit message descriptor + */ +struct letmd { + u_int32_t tmd0; + u_int32_t tmd1; + u_int32_t tmd2; + int32_t tmd3; +}; + +/* + * Initialization block + */ +struct leinit { + u_int32_t init_mode; /* +0x0000 */ + u_int32_t init_padr[2]; /* +0x0002 */ + u_int16_t init_ladrf[4]; /* +0x0008 */ + u_int32_t init_rdra; /* +0x0010 */ + u_int32_t init_tdra; /* +0x0014 */ + int32_t pad; /* Pad to 16 shorts */ +}; + +/* Receive message descriptor 1 (rmd1_bits) */ +#define LE_R1_OWN (1<<31) /* LANCE owns the packet */ +#define LE_R1_ERR (1<<30) /* error summary */ +#define LE_R1_FRAM (1<<29) /* framing error */ +#define LE_R1_OFLO (1<<28) /* overflow error */ +#define LE_R1_CRC (1<<27) /* CRC error */ +#define LE_R1_BUFF (1<<26) /* buffer error */ +#define LE_R1_STP (1<<25) /* start of packet */ +#define LE_R1_ENP (1<<24) /* end of packet */ +#define LE_R1_ONES (0xf<<12) /* must be ones */ +#define LE_R1_BCNT_MASK (0xfff) /* byte count mask */ + +#define LE_R1_BITS \ + "\20\40OWN\37ERR\36FRAM\35OFLO\34CRC\33BUFF\32STP\31ENP" + +/* Transmit message descriptor 1 (tmd1_bits) */ +#define LE_T1_OWN (1<<31) /* LANCE owns the packet */ +#define LE_T1_ERR (1<<30) /* error summary */ +#define LE_T1_ADD_FCS (1<<29) /* add FCS (PCnet-PCI) */ +#define LE_T1_NO_FCS (1<<29) /* no FCS (ILACC) */ +#define LE_T1_MORE (1<<28) /* multiple collisions */ +#define LE_T1_LTINT (1<<28) /* transmit interrupt (if LTINTEN) */ +#define LE_T1_ONE (1<<27) /* single collision */ +#define LE_T1_DEF (1<<26) /* deferred transmit */ +#define LE_T1_STP (1<<25) /* start of packet */ +#define LE_T1_ENP (1<<24) /* end of packet */ +#define LE_T1_ONES (0xf<<12) /* must be ones */ +#define LE_T1_BCNT_MASK (0xfff) /* byte count mask */ + +#define LE_T1_BITS \ + "\20\40OWN\37ERR\36RES\35MORE\34ONE\33DEF\32STP\31ENP" + +/* Transmit message descriptor 3 (tmd3) */ +#define LE_T2_BUFF (1<<31) /* buffer error */ +#define LE_T2_UFLO (1<<30) /* underflow error */ +#define LE_T2_EXDEF (1<<29) /* excessive defferral */ +#define LE_T2_LCOL (1<<28) /* late collision */ +#define LE_T2_LCAR (1<<27) /* loss of carrier */ +#define LE_T2_RTRY (1<<26) /* retry error */ +#if 0 +#define LE_T3_TDR_MASK 0x03ff /* time domain reflectometry counter */ +#endif + +#define LE_T3_BITS \ + "\12\40BUFF\37UFLO\35LCOL\34LCAR\33RTRY" diff --git a/sys/dev/ic/lancereg.h b/sys/dev/ic/lancereg.h new file mode 100644 index 00000000000..ddf278c0fec --- /dev/null +++ b/sys/dev/ic/lancereg.h @@ -0,0 +1,612 @@ +/* $OpenBSD: lancereg.h,v 1.1 2005/07/28 01:31:22 brad Exp $ */ +/* $NetBSD: lancereg.h,v 1.11 2003/11/02 11:07:45 wiz Exp $ */ + +/*- + * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum and Jason R. Thorpe. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)if_lereg.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * Register description for the following Advanced Micro Devices + * Ethernet chips: + * + * - Am7990 Local Area Network Controller for Ethernet (LANCE) + * (and its descendent Am79c90 C-LANCE). + * + * - Am79c900 Integrated Local Area Communications Controller (ILACC) + * + * - Am79c960 PCnet-ISA Single-Chip Ethernet Controller for ISA + * + * - Am79c961 PCnet-ISA+ Jumperless Single-Chip Ethernet Controller + * for ISA + * + * - Am79c961A PCnet-ISA II Jumperless Full-Duplex Single-Chip + * Ethernet Controller for ISA + * + * - Am79c965A PCnet-32 Single-Chip 32-bit Ethernet Controller + * (for VESA and 486 local busses) + * + * - Am79c970 PCnet-PCI Single-Chip Ethernet Controller for PCI + * Local Bus + * + * - Am79c970A PCnet-PCI II Single-Chip Full-Duplex Ethernet Controller + * for PCI Local Bus + * + * - Am79c971 PCnet-FAST Single-Chip Full-Duplex 10/100Mbps + * Ethernet Controller for PCI Local Bus + * + * - Am79c972 PCnet-FAST+ Enhanced 10/100Mbps PCI Ethernet Controller + * with OnNow Support + * + * - Am79c973/Am79c975 PCnet-FAST III Single-Chip 10/100Mbps PCI + * Ethernet Controller with Integrated PHY + * + * - Am79c978 PCnet-Home Single-Chip 1/10 Mbps PCI Home + * Networking Controller. + * + * Initialization block, transmit descriptor, and receive descriptor + * formats are described in two separate files: + * + * 16-bit software model (LANCE) am7990reg.h + * + * 32-bit software model (ILACC) am79900reg.h + * + * Note that the vast majority of the registers described in this file + * belong to follow-on chips to the original LANCE. Only CSR0-CSR3 are + * valid on the LANCE. + */ + +#define LEBLEN 1536 /* ETHERMTU + header + CRC */ +#define LEMINSIZE 60 /* should be 64 if mode DTCR is set */ + +#define LE_INITADDR(sc) (sc->sc_initaddr) +#define LE_RMDADDR(sc, bix) (sc->sc_rmdaddr + sizeof(struct lermd) * (bix)) +#define LE_TMDADDR(sc, bix) (sc->sc_tmdaddr + sizeof(struct letmd) * (bix)) +#define LE_RBUFADDR(sc, bix) (sc->sc_rbufaddr[bix]) +#define LE_TBUFADDR(sc, bix) (sc->sc_tbufaddr[bix]) + +/* + * The byte count fields in descriptors are in two's complement. + * This macro does the conversion for us on unsigned numbers. + */ +#define LE_BCNT(x) (~(x) + 1) + +/* + * Control and Status Register addresses + */ +#define LE_CSR0 0x0000 /* Control and status register */ +#define LE_CSR1 0x0001 /* low address of init block */ +#define LE_CSR2 0x0002 /* high address of init block */ +#define LE_CSR3 0x0003 /* Bus master and control */ +#define LE_CSR4 0x0004 /* Test and features control */ +#define LE_CSR5 0x0005 /* Extended control and Interrupt 1 */ +#define LE_CSR6 0x0006 /* Rx/Tx Descriptor table length */ +#define LE_CSR7 0x0007 /* Extended control and interrupt 2 */ +#define LE_CSR8 0x0008 /* Logical Address Filter 0 */ +#define LE_CSR9 0x0009 /* Logical Address Filter 1 */ +#define LE_CSR10 0x000a /* Logical Address Filter 2 */ +#define LE_CSR11 0x000b /* Logical Address Filter 3 */ +#define LE_CSR12 0x000c /* Physical Address 0 */ +#define LE_CSR13 0x000d /* Physical Address 1 */ +#define LE_CSR14 0x000e /* Physical Address 2 */ +#define LE_CSR15 0x000f /* Mode */ +#define LE_CSR16 0x0010 /* Initialization Block addr lower */ +#define LE_CSR17 0x0011 /* Initialization Block addr upper */ +#define LE_CSR18 0x0012 /* Current Rx Buffer addr lower */ +#define LE_CSR19 0x0013 /* Current Rx Buffer addr upper */ +#define LE_CSR20 0x0014 /* Current Tx Buffer addr lower */ +#define LE_CSR21 0x0015 /* Current Tx Buffer addr upper */ +#define LE_CSR22 0x0016 /* Next Rx Buffer addr lower */ +#define LE_CSR23 0x0017 /* Next Rx Buffer addr upper */ +#define LE_CSR24 0x0018 /* Base addr of Rx ring lower */ +#define LE_CSR25 0x0019 /* Base addr of Rx ring upper */ +#define LE_CSR26 0x001a /* Next Rx Desc addr lower */ +#define LE_CSR27 0x001b /* Next Rx Desc addr upper */ +#define LE_CSR28 0x001c /* Current Rx Desc addr lower */ +#define LE_CSR29 0x001d /* Current Rx Desc addr upper */ +#define LE_CSR30 0x001e /* Base addr of Tx ring lower */ +#define LE_CSR31 0x001f /* Base addr of Tx ring upper */ +#define LE_CSR32 0x0020 /* Next Tx Desc addr lower */ +#define LE_CSR33 0x0021 /* Next Tx Desc addr upper */ +#define LE_CSR34 0x0022 /* Current Tx Desc addr lower */ +#define LE_CSR35 0x0023 /* Current Tx Desc addr upper */ +#define LE_CSR36 0x0024 /* Next Next Rx Desc addr lower */ +#define LE_CSR37 0x0025 /* Next Next Rx Desc addr upper */ +#define LE_CSR38 0x0026 /* Next Next Tx Desc addr lower */ +#define LE_CSR39 0x0027 /* Next Next Tx Desc adddr upper */ +#define LE_CSR40 0x0028 /* Current Rx Byte Count */ +#define LE_CSR41 0x0029 /* Current Rx Status */ +#define LE_CSR42 0x002a /* Current Tx Byte Count */ +#define LE_CSR43 0x002b /* Current Tx Status */ +#define LE_CSR44 0x002c /* Next Rx Byte Count */ +#define LE_CSR45 0x002d /* Next Rx Status */ +#define LE_CSR46 0x002e /* Tx Poll Time Counter */ +#define LE_CSR47 0x002f /* Tx Polling Interval */ +#define LE_CSR48 0x0030 /* Rx Poll Time Counter */ +#define LE_CSR49 0x0031 /* Rx Polling Interval */ +#define LE_CSR58 0x003a /* Software Style */ +#define LE_CSR60 0x003c /* Previous Tx Desc addr lower */ +#define LE_CSR61 0x003d /* Previous Tx Desc addr upper */ +#define LE_CSR62 0x003e /* Previous Tx Byte Count */ +#define LE_CSR63 0x003f /* Previous Tx Status */ +#define LE_CSR64 0x0040 /* Next Tx Buffer addr lower */ +#define LE_CSR65 0x0041 /* Next Tx Buffer addr upper */ +#define LE_CSR66 0x0042 /* Next Tx Byte Count */ +#define LE_CSR67 0x0043 /* Next Tx Status */ +#define LE_CSR72 0x0048 /* Receive Ring Counter */ +#define LE_CSR74 0x004a /* Transmit Ring Counter */ +#define LE_CSR76 0x004c /* Receive Ring Length */ +#define LE_CSR78 0x004e /* Transmit Ring Length */ +#define LE_CSR80 0x0050 /* DMA Transfer Counter and FIFO + Threshold Control */ +#define LE_CSR82 0x0052 /* Tx Desc addr Pointer lower */ +#define LE_CSR84 0x0054 /* DMA addr register lower */ +#define LE_CSR85 0x0055 /* DMA addr register upper */ +#define LE_CSR86 0x0056 /* Buffer Byte Counter */ +#define LE_CSR88 0x0058 /* Chip ID Register lower */ +#define LE_CSR89 0x0059 /* Chip ID Register upper */ +#define LE_CSR92 0x005c /* Ring Length Conversion */ +#define LE_CSR100 0x0064 /* Bus Timeout */ +#define LE_CSR112 0x0070 /* Missed Frame Count */ +#define LE_CSR114 0x0072 /* Receive Collision Count */ +#define LE_CSR116 0x0074 /* OnNow Power Mode Register */ +#define LE_CSR122 0x007a /* Advanced Feature Control */ +#define LE_CSR124 0x007c /* Test Register 1 */ +#define LE_CSR125 0x007d /* MAC Enhanced Configuration Control */ + +/* + * Bus Configuration Register addresses + */ +#define LE_BCR0 0x0000 /* Master Mode Read Active */ +#define LE_BCR1 0x0001 /* Master Mode Write Active */ +#define LE_BCR2 0x0002 /* Misc. Configuration */ +#define LE_BCR4 0x0004 /* LED0 Status */ +#define LE_BCR5 0x0005 /* LED1 Status */ +#define LE_BCR6 0x0006 /* LED2 Status */ +#define LE_BCR7 0x0007 /* LED3 Status */ +#define LE_BCR9 0x0009 /* Full-duplex Control */ +#define LE_BCR16 0x0010 /* I/O Base Address lower */ +#define LE_BCR17 0x0011 /* I/O Base Address upper */ +#define LE_BCR18 0x0012 /* Burst and Bus Control Register */ +#define LE_BCR19 0x0013 /* EEPROM Control and Status */ +#define LE_BCR20 0x0014 /* Software Style */ +#define LE_BCR22 0x0016 /* PCI Latency Register */ +#define LE_BCR23 0x0017 /* PCI Subsystem Vendor ID */ +#define LE_BCR24 0x0018 /* PCI Subsystem ID */ +#define LE_BCR25 0x0019 /* SRAM Size Register */ +#define LE_BCR26 0x001a /* SRAM Boundary Register */ +#define LE_BCR27 0x001b /* SRAM Interface Control Register */ +#define LE_BCR28 0x001c /* Exp. Bus Port Addr lower */ +#define LE_BCR29 0x001d /* Exp. Bus Port Addr upper */ +#define LE_BCR30 0x001e /* Exp. Bus Data Port */ +#define LE_BCR31 0x001f /* Software Timer Register */ +#define LE_BCR32 0x0020 /* PHY Control and Status Register */ +#define LE_BCR33 0x0021 /* PHY Address Register */ +#define LE_BCR34 0x0022 /* PHY Management Data Register */ +#define LE_BCR35 0x0023 /* PCI Vendor ID Register */ +#define LE_BCR36 0x0024 /* PCI Power Management Cap. Alias */ +#define LE_BCR37 0x0025 /* PCI DATA0 Alias */ +#define LE_BCR38 0x0026 /* PCI DATA1 Alias */ +#define LE_BCR39 0x0027 /* PCI DATA2 Alias */ +#define LE_BCR40 0x0028 /* PCI DATA3 Alias */ +#define LE_BCR41 0x0029 /* PCI DATA4 Alias */ +#define LE_BCR42 0x002a /* PCI DATA5 Alias */ +#define LE_BCR43 0x002b /* PCI DATA6 Alias */ +#define LE_BCR44 0x002c /* PCI DATA7 Alias */ +#define LE_BCR45 0x002d /* OnNow Pattern Matching 1 */ +#define LE_BCR46 0x002e /* OnNow Pattern Matching 2 */ +#define LE_BCR47 0x002f /* OnNow Pattern Matching 3 */ +#define LE_BCR48 0x0030 /* LED4 Status */ +#define LE_BCR49 0x0031 /* PHY Select */ + +/* Control and status register 0 (csr0) */ +#define LE_C0_ERR 0x8000 /* error summary */ +#define LE_C0_BABL 0x4000 /* transmitter timeout error */ +#define LE_C0_CERR 0x2000 /* collision */ +#define LE_C0_MISS 0x1000 /* missed a packet */ +#define LE_C0_MERR 0x0800 /* memory error */ +#define LE_C0_RINT 0x0400 /* receiver interrupt */ +#define LE_C0_TINT 0x0200 /* transmitter interrupt */ +#define LE_C0_IDON 0x0100 /* initialization done */ +#define LE_C0_INTR 0x0080 /* interrupt condition */ +#define LE_C0_INEA 0x0040 /* interrupt enable */ +#define LE_C0_RXON 0x0020 /* receiver on */ +#define LE_C0_TXON 0x0010 /* transmitter on */ +#define LE_C0_TDMD 0x0008 /* transmit demand */ +#define LE_C0_STOP 0x0004 /* disable all external activity */ +#define LE_C0_STRT 0x0002 /* enable external activity */ +#define LE_C0_INIT 0x0001 /* begin initialization */ + +#define LE_C0_BITS \ + "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\ +\12TINT\11IDON\10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT" + +/* Control and status register 3 (csr3) */ +#define LE_C3_BABLM 0x4000 /* babble mask */ +#define LE_C3_MISSM 0x1000 /* missed frame mask */ +#define LE_C3_MERRM 0x0800 /* memory error mask */ +#define LE_C3_RINTM 0x0400 /* receive interrupt mask */ +#define LE_C3_TINTM 0x0200 /* transmit interrupt mask */ +#define LE_C3_IDONM 0x0100 /* initialization done mask */ +#define LE_C3_DXSUFLO 0x0040 /* disable tx stop on underflow */ +#define LE_C3_LAPPEN 0x0020 /* look ahead packet processing enbl */ +#define LE_C3_DXMT2PD 0x0010 /* disable tx two part deferral */ +#define LE_C3_EMBA 0x0008 /* enable modified backoff algorithm */ +#define LE_C3_BSWP 0x0004 /* byte swap */ +#define LE_C3_ACON 0x0002 /* ALE control, eh? */ +#define LE_C3_BCON 0x0001 /* byte control */ + +/* Control and status register 4 (csr4) */ +#define LE_C4_EN124 0x8000 /* enable CSR124 */ +#define LE_C4_DMAPLUS 0x4000 /* always set (PCnet-PCI) */ +#define LE_C4_TIMER 0x2000 /* enable bus activity timer */ +#define LE_C4_TXDPOLL 0x1000 /* disable transmit polling */ +#define LE_C4_APAD_XMT 0x0800 /* auto pad transmit */ +#define LE_C4_ASTRP_RCV 0x0400 /* auto strip receive */ +#define LE_C4_MFCO 0x0200 /* missed frame counter overflow */ +#define LE_C4_MFCOM 0x0100 /* missed frame coutner overflow mask */ +#define LE_C4_UINTCMD 0x0080 /* user interrupt command */ +#define LE_C4_UINT 0x0040 /* user interrupt */ +#define LE_C4_RCVCCO 0x0020 /* receive collision counter overflow */ +#define LE_C4_RCVCCOM 0x0010 /* receive collision counter overflow + mask */ +#define LE_C4_TXSTRT 0x0008 /* transmit start status */ +#define LE_C4_TXSTRTM 0x0004 /* transmit start mask */ + +/* Control and status register 5 (csr5) */ +#define LE_C5_TOKINTD 0x8000 /* transmit ok interrupt disable */ +#define LE_C5_LTINTEN 0x4000 /* last transmit interrupt enable */ +#define LE_C5_SINT 0x0800 /* system interrupt */ +#define LE_C5_SINTE 0x0400 /* system interrupt enable */ +#define LE_C5_EXDINT 0x0080 /* excessive deferral interrupt */ +#define LE_C5_EXDINTE 0x0040 /* excessive deferral interrupt enbl */ +#define LE_C5_MPPLBA 0x0020 /* magic packet physical logical + broadcast accept */ +#define LE_C5_MPINT 0x0010 /* magic packet interrupt */ +#define LE_C5_MPINTE 0x0008 /* magic packet interrupt enable */ +#define LE_C5_MPEN 0x0004 /* magic packet enable */ +#define LE_C5_MPMODE 0x0002 /* magic packet mode */ +#define LE_C5_SPND 0x0001 /* suspend */ + +/* Control and status register 6 (csr6) */ +#define LE_C6_TLEN 0xf000 /* TLEN from init block */ +#define LE_C6_RLEN 0x0f00 /* RLEN from init block */ + +/* Control and status register 7 (csr7) */ +#define LE_C7_FASTSPNDE 0x8000 /* fast suspend enable */ +#define LE_C7_RDMD 0x2000 /* receive demand */ +#define LE_C7_RDXPOLL 0x1000 /* receive disable polling */ +#define LE_C7_STINT 0x0800 /* software timer interrupt */ +#define LE_C7_STINTE 0x0400 /* software timer interrupt enable */ +#define LE_C7_MREINT 0x0200 /* PHY management read error intr */ +#define LE_C7_MREINTE 0x0100 /* PHY management read error intr + enable */ +#define LE_C7_MAPINT 0x0080 /* PHY management auto-poll intr */ +#define LE_C7_MAPINTE 0x0040 /* PHY management auto-poll intr + enable */ +#define LE_C7_MCCINT 0x0020 /* PHY management command complete + interrupt */ +#define LE_C7_MCCINTE 0x0010 /* PHY management command complete + interrupt enable */ +#define LE_C7_MCCIINT 0x0008 /* PHY management command complete + internal interrupt */ +#define LE_C7_MCCIINTE 0x0004 /* PHY management command complete + internal interrupt enable */ +#define LE_C7_MIIPDTINT 0x0002 /* PHY management detect transition + interrupt */ +#define LE_C7_MIIPDTINTE 0x0001 /* PHY management detect transition + interrupt enable */ + +/* Control and status register 15 (csr15) */ +#define LE_C15_PROM 0x8000 /* promiscuous mode */ +#define LE_C15_DRCVBC 0x4000 /* disable Rx of broadcast */ +#define LE_C15_DRCVPA 0x2000 /* disable Rx of physical address */ +#define LE_C15_DLNKTST 0x1000 /* disable link status */ +#define LE_C15_DAPC 0x0800 /* disable auto-polarity correction */ +#define LE_C15_MENDECL 0x0400 /* MENDEC Loopback mode */ +#define LE_C15_LRT 0x0200 /* low receive threshold (TMAU) */ +#define LE_C15_TSEL 0x0200 /* transmit mode select (AUI) */ +#define LE_C15_PORTSEL(x) ((x) << 7) /* port select */ +#define LE_C15_INTL 0x0040 /* internal loopback */ +#define LE_C15_DRTY 0x0020 /* disable retry */ +#define LE_C15_FCOLL 0x0010 /* force collision */ +#define LE_C15_DXMTFCS 0x0008 /* disable Tx FCS (ADD_FCS overrides) */ +#define LE_C15_LOOP 0x0004 /* loopback enable */ +#define LE_C15_DTX 0x0002 /* disable transmit */ +#define LE_C15_DRX 0x0001 /* disable receiver */ + +#define PORTSEL_AUI 0 +#define PORTSEL_10T 1 +#define PORTSEL_GPSI 2 +#define PORTSEL_MII 3 +#define PORTSEL_MASK 3 + +/* control and status register 80 (csr80) */ +#define LE_C80_RCVFW(x) ((x) << 12) /* Receive FIFO Watermark */ +#define LE_C80_RCVFW_MAX 3 +#define LE_C80_XMTSP(x) ((x) << 10) /* Transmit Start Point */ +#define LE_C80_XMTSP_MAX 3 +#define LE_C80_XMTFW(x) ((x) << 8) /* Transmit FIFO Watermark */ +#define LE_C80_XMTFW_MAX 3 +#define LE_C80_DMATC 0x00ff /* DMA transfer counter */ + +/* control and status register 116 (csr116) */ +#define LE_C116_PME_EN_OVR 0x0400 /* PME_EN overwrite */ +#define LE_C116_LCDET 0x0200 /* link change detected */ +#define LE_C116_LCMODE 0x0100 /* link change wakeup mode */ +#define LE_C116_PMAT 0x0080 /* pattern matched */ +#define LE_C116_EMPPLBA 0x0040 /* magic packet physical logical + broadcast accept */ +#define LE_C116_MPMAT 0x0020 /* magic packet match */ +#define LE_C116_MPPEN 0x0010 /* magic packet pin enable */ +#define LE_C116_RST_POL 0x0001 /* PHY_RST pin polarity */ + +/* control and status register 122 (csr122) */ +#define LE_C122_RCVALGN 0x0001 /* receive packet align */ + +/* control and status register 124 (csr124) */ +#define LE_C124_RPA 0x0008 /* runt packet accept */ + +/* control and status register 125 (csr125) */ +#define LE_C125_IPG 0xff00 /* inter-packet gap */ +#define LE_C125_IFS1 0x00ff /* inter-frame spacing part 1 */ + +/* bus configuration register 0 (bcr0) */ +#define LE_B0_MSRDA 0xffff /* reserved locations */ + +/* bus configuration register 1 (bcr1) */ +#define LE_B1_MSWRA 0xffff /* reserved locations */ + +/* bus configuration register 2 (bcr2) */ +#define LE_B2_PHYSSELEN 0x2000 /* enable writes to BCR18[4:3] */ +#define LE_B2_LEDPE 0x1000 /* LED program enable */ +#define LE_B2_APROMWE 0x0100 /* Address PROM Write Enable */ +#define LE_B2_INTLEVEL 0x0080 /* 1 == edge triggered */ +#define LE_B2_DXCVRCTL 0x0020 /* DXCVR control */ +#define LE_B2_DXCVRPOL 0x0010 /* DXCVR polarity */ +#define LE_B2_EADISEL 0x0008 /* EADI select */ +#define LE_B2_AWAKE 0x0004 /* power saving mode select */ +#define LE_B2_ASEL 0x0002 /* auto-select PORTSEL */ +#define LE_B2_XMAUSEL 0x0001 /* reserved location */ + +/* bus configuration register 4 (bcr4) */ +/* bus configuration register 5 (bcr5) */ +/* bus configuration register 6 (bcr6) */ +/* bus configuration register 7 (bcr7) */ +/* bus configuration register 48 (bcr48) */ +#define LE_B4_LEDOUT 0x8000 /* LED output active */ +#define LE_B4_LEDPOL 0x4000 /* LED polarity */ +#define LE_B4_LEDDIS 0x2000 /* LED disable */ +#define LE_B4_100E 0x1000 /* 100Mb/s enable */ +#define LE_B4_MPSE 0x0200 /* magic packet status enable */ +#define LE_B4_FDLSE 0x0100 /* full-duplex link status enable */ +#define LE_B4_PSE 0x0080 /* pulse stretcher enable */ +#define LE_B4_LNKSE 0x0040 /* link status enable */ +#define LE_B4_RCVME 0x0020 /* receive match status enable */ +#define LE_B4_XMTE 0x0010 /* transmit status enable */ +#define LE_B4_POWER 0x0008 /* power enable */ +#define LE_B4_RCVE 0x0004 /* receive status enable */ +#define LE_B4_SPEED 0x0002 /* high speed enable */ +#define LE_B4_COLE 0x0001 /* collision status enable */ + +/* bus configuration register 9 (bcr9) */ +#define LE_B9_FDRPAD 0x0004 /* full-duplex runt packet accept + disable */ +#define LE_B9_AUIFD 0x0002 /* AUI full-duplex */ +#define LE_B9_FDEN 0x0001 /* full-duplex enable */ + +/* bus configuration register 18 (bcr18) */ +#define LE_B18_ROMTMG 0xf000 /* expansion rom timing */ +#define LE_B18_NOUFLO 0x0800 /* no underflow on transmit */ +#define LE_B18_MEMCMD 0x0200 /* memory read multiple enable */ +#define LE_B18_EXTREQ 0x0100 /* extended request */ +#define LE_B18_DWIO 0x0080 /* double-word I/O */ +#define LE_B18_BREADE 0x0040 /* burst read enable */ +#define LE_B18_BWRITE 0x0020 /* burst write enable */ +#define LE_B18_PHYSEL1 0x0010 /* PHYSEL 1 */ +#define LE_B18_PHYSEL0 0x0008 /* PHYSEL 0 */ + /* 00 ex ROM/Flash */ + /* 01 EADI/MII snoop */ + /* 10 reserved */ + /* 11 reserved */ +#define LE_B18_LINBC 0x0007 /* reserved locations */ + +/* bus configuration register 19 (bcr19) */ +#define LE_B19_PVALID 0x8000 /* EEPROM status valid */ +#define LE_B19_PREAD 0x4000 /* EEPROM read command */ +#define LE_B19_EEDET 0x2000 /* EEPROM detect */ +#define LE_B19_EEN 0x0010 /* EEPROM port enable */ +#define LE_B19_ECS 0x0004 /* EEPROM chip select */ +#define LE_B19_ESK 0x0002 /* EEPROM serial clock */ +#define LE_B19_EDI 0x0001 /* EEPROM data in */ +#define LE_B19_EDO 0x0001 /* EEPROM data out */ + +/* bus configuration register 20 (bcr20) */ +#define LE_B20_APERREN 0x0400 /* Advanced parity error handling */ +#define LE_B20_CSRPCNET 0x0200 /* PCnet-style CSRs (0 = ILACC) */ +#define LE_B20_SSIZE32 0x0100 /* Software Size 32-bit */ +#define LE_B20_SSTYLE 0x0007 /* Software Style */ +#define LE_B20_SSTYLE_LANCE 0 /* LANCE/PCnet-ISA (16-bit) */ +#define LE_B20_SSTYPE_ILACC 1 /* ILACC (32-bit) */ +#define LE_B20_SSTYLE_PCNETPCI2 2 /* PCnet-PCI (32-bit) */ +#define LE_B20_SSTYLE_PCNETPCI3 3 /* PCnet-PCI II (32-bit) */ + +/* bus configuration register 25 (bcr25) */ +#define LE_B25_SRAM_SIZE 0x00ff /* SRAM size */ + +/* bus configuration register 26 (bcr26) */ +#define LE_B26_SRAM_BND 0x00ff /* SRAM boundary */ + +/* bus configuration register 27 (bcr27) */ +#define LE_B27_PTRTST 0x8000 /* reserved for manuf. tests */ +#define LE_B27_LOLATRX 0x4000 /* low latency receive */ +#define LE_B27_EBCS 0x0038 /* expansion bus clock source */ + /* 000 CLK pin */ + /* 001 time base clock */ + /* 010 EBCLK pin */ + /* 011 reserved */ + /* 1xx reserved */ +#define LE_B27_CLK_FAC 0x0007 /* clock factor */ + /* 000 1 */ + /* 001 1/2 */ + /* 010 reserved */ + /* 011 1/4 */ + /* 1xx reserved */ + +/* bus configuration register 28 (bcr28) */ +#define LE_B28_EADDRL 0xffff /* expansion port address lower */ + +/* bus configuration register 29 (bcr29) */ +#define LE_B29_FLASH 0x8000 /* flash access */ +#define LE_B29_LAAINC 0x4000 /* lower address auto increment */ +#define LE_B29_EPADDRU 0x0007 /* expansion port address upper */ + +/* bus configuration register 30 (bcr30) */ +#define LE_B30_EBDATA 0xffff /* expansion bus data port */ + +/* bus configuration register 31 (bcr31) */ +#define LE_B31_STVAL 0xffff /* software timer value */ + +/* bus configuration register 32 (bcr32) */ +#define LE_B32_ANTST 0x8000 /* reserved for manuf. tests */ +#define LE_B32_MIIPD 0x4000 /* MII PHY Detect (manuf. tests) */ +#define LE_B32_FMDC 0x3000 /* fast management data clock */ +#define LE_B32_APEP 0x0800 /* auto-poll PHY */ +#define LE_B32_APDW 0x0700 /* auto-poll dwell time */ +#define LE_B32_DANAS 0x0080 /* disable autonegotiation */ +#define LE_B32_XPHYRST 0x0040 /* PHY reset */ +#define LE_B32_XPHYANE 0x0020 /* PHY autonegotiation enable */ +#define LE_B32_XPHYFD 0x0010 /* PHY full-duplex */ +#define LE_B32_XPHYSP 0x0008 /* PHY speed */ +#define LE_B32_MIIILP 0x0002 /* MII internal loopback */ + +/* bus configuration register 33 (bcr33) */ +#define LE_B33_SHADOW 0x8000 /* shadow enable */ +#define LE_B33_MII_SEL 0x4000 /* MII selected */ +#define LE_B33_ACOMP 0x2000 /* internal PHY autonegotiation comp */ +#define LE_B33_LINK 0x1000 /* link status */ +#define LE_B33_FDX 0x0800 /* full-duplex */ +#define LE_B33_SPEED 0x0400 /* 1 == high speed */ +#define LE_B33_PHYAD 0x03e0 /* PHY address */ +#define PHYAD_SHIFT 5 +#define LE_B33_REGAD 0x001f /* register address */ + +/* bus configuration register 34 (bcr34) */ +#define LE_B34_MIIMD 0xffff /* MII data */ + +/* bus configuration register 49 (bcr49) */ +#define LE_B49_PCNET 0x8000 /* PCnet mode - Must Be One */ +#define LE_B49_PHYSEL_D 0x0300 /* PHY_SEL_Default */ +#define LE_B49_PHYSEL_L 0x0010 /* PHY_SEL_Lock */ +#define LE_B49_PHYSEL 0x0003 /* PHYSEL */ + /* 00 10baseT PHY */ + /* 01 HomePNA PYY */ + /* 10 external PHY */ + /* 11 reserved */ + +/* Initialization block (mode) */ +#define LE_MODE_PROM 0x8000 /* promiscuous mode */ +/* 0x7f80 reserved, must be zero */ +/* 0x4000 - 0x0080 are not available on LANCE 7990 */ +#define LE_MODE_DRCVBC 0x4000 /* disable receive brodcast */ +#define LE_MODE_DRCVPA 0x2000 /* disable physical address detection */ +#define LE_MODE_DLNKTST 0x1000 /* disable link status */ +#define LE_MODE_DAPC 0x0800 /* disable automatic polarity correction */ +#define LE_MODE_MENDECL 0x0400 /* MENDEC loopback mode */ +#define LE_MODE_LRTTSEL 0x0200 /* lower receive threshold / + transmit mode selection */ +#define LE_MODE_PSEL1 0x0100 /* port selection bit1 */ +#define LE_MODE_PSEL0 0x0080 /* port selection bit0 */ +#define LE_MODE_INTL 0x0040 /* internal loopback */ +#define LE_MODE_DRTY 0x0020 /* disable retry */ +#define LE_MODE_COLL 0x0010 /* force a collision */ +#define LE_MODE_DTCR 0x0008 /* disable transmit CRC */ +#define LE_MODE_LOOP 0x0004 /* loopback mode */ +#define LE_MODE_DTX 0x0002 /* disable transmitter */ +#define LE_MODE_DRX 0x0001 /* disable receiver */ +#define LE_MODE_NORMAL 0 /* none of the above */ + +/* + * Chip ID (CSR88 IDL, CSR89 IDU) values for various AMD PCnet parts. + */ +#define CHIPID_MANFID(x) (((x) >> 1) & 0x3ff) +#define CHIPID_PARTID(x) (((x) >> 12) & 0xffff) +#define CHIPID_VER(x) (((x) >> 28) & 0x7) + +#define PARTID_Am79c960 0x0003 +#define PARTID_Am79c961 0x2260 +#define PARTID_Am79c961A 0x2261 +#define PARTID_Am79c965 0x2430 /* yes, these... */ +#define PARTID_Am79c970 0x2430 /* ...are the same */ +#define PARTID_Am79c970A 0x2621 +#define PARTID_Am79c971 0x2623 +#define PARTID_Am79c972 0x2624 +#define PARTID_Am79c973 0x2625 +#define PARTID_Am79c978 0x2626 +#define PARTID_Am79c975 0x2627 +#define PARTID_Am79c976 0x2628 diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 141d37ba316..da65edd9f32 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.179 2005/07/06 01:52:13 mickey Exp $ +# $OpenBSD: files.pci,v 1.180 2005/07/28 01:31:22 brad Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -202,6 +202,12 @@ file dev/pci/if_fpa.c fpa attach le at pci with le_pci file dev/pci/if_le_pci.c le_pci +# AMD PCnet-PCI Ethernet controller family +# Supersedes if_le_pci.c +device pcn: ether, ifnet, ifmedia, mii +attach pcn at pci +file dev/pci/if_pcn.c + # common code for siop/esiop pci front end define siop_pci_common file dev/pci/siop_pci_common.c siop_pci_common diff --git a/sys/dev/pci/if_pcn.c b/sys/dev/pci/if_pcn.c new file mode 100644 index 00000000000..d20112a961c --- /dev/null +++ b/sys/dev/pci/if_pcn.c @@ -0,0 +1,2128 @@ +/* $OpenBSD: if_pcn.c,v 1.1 2005/07/28 01:31:22 brad Exp $ */ +/* $NetBSD: if_pcn.c,v 1.26 2005/05/07 09:15:44 is Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + */ + +/* + * Device driver for the AMD PCnet-PCI series of Ethernet + * chips: + * + * * Am79c970 PCnet-PCI Single-Chip Ethernet Controller for PCI + * Local Bus + * + * * Am79c970A PCnet-PCI II Single-Chip Full-Duplex Ethernet Controller + * for PCI Local Bus + * + * * Am79c971 PCnet-FAST Single-Chip Full-Duplex 10/100Mbps + * Ethernet Controller for PCI Local Bus + * + * * Am79c972 PCnet-FAST+ Enhanced 10/100Mbps PCI Ethernet Controller + * with OnNow Support + * + * * Am79c973/Am79c975 PCnet-FAST III Single-Chip 10/100Mbps PCI + * Ethernet Controller with Integrated PHY + * + * This also supports the virtual PCnet-PCI Ethernet interface found + * in VMware. + * + * TODO: + * + * * Split this into bus-specific and bus-independent portions. + * The core could also be used for the ILACC (Am79900) 32-bit + * Ethernet chip (XXX only if we use an ILACC-compatible SWSTYLE). + */ + +#if 0 +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: if_pcn.c,v 1.26 2005/05/07 09:15:44 is Exp $"); +#endif + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/timeout.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/if_dl.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <net/if_media.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/endian.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/ic/am79900reg.h> +#include <dev/ic/lancereg.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/pci/if_pcnreg.h> + +/* + * Transmit descriptor list size. This is arbitrary, but allocate + * enough descriptors for 128 pending transmissions, and 4 segments + * per packet. This MUST work out to a power of 2. + * + * NOTE: We can't have any more than 512 Tx descriptors, SO BE CAREFUL! + * + * So we play a little trick here. We give each packet up to 16 + * DMA segments, but only allocate the max of 512 descriptors. The + * transmit logic can deal with this, we just are hoping to sneak by. + */ +#define PCN_NTXSEGS 16 + +#define PCN_TXQUEUELEN 128 +#define PCN_TXQUEUELEN_MASK (PCN_TXQUEUELEN - 1) +#define PCN_NTXDESC 512 +#define PCN_NTXDESC_MASK (PCN_NTXDESC - 1) +#define PCN_NEXTTX(x) (((x) + 1) & PCN_NTXDESC_MASK) +#define PCN_NEXTTXS(x) (((x) + 1) & PCN_TXQUEUELEN_MASK) + +/* Tx interrupt every N + 1 packets. */ +#define PCN_TXINTR_MASK 7 + +/* + * Receive descriptor list size. We have one Rx buffer per incoming + * packet, so this logic is a little simpler. + */ +#define PCN_NRXDESC 128 +#define PCN_NRXDESC_MASK (PCN_NRXDESC - 1) +#define PCN_NEXTRX(x) (((x) + 1) & PCN_NRXDESC_MASK) + +/* + * Control structures are DMA'd to the PCnet chip. We allocate them in + * a single clump that maps to a single DMA segment to make several things + * easier. + */ +struct pcn_control_data { + /* The transmit descriptors. */ + struct letmd pcd_txdescs[PCN_NTXDESC]; + + /* The receive descriptors. */ + struct lermd pcd_rxdescs[PCN_NRXDESC]; + + /* The init block. */ + struct leinit pcd_initblock; +}; + +#define PCN_CDOFF(x) offsetof(struct pcn_control_data, x) +#define PCN_CDTXOFF(x) PCN_CDOFF(pcd_txdescs[(x)]) +#define PCN_CDRXOFF(x) PCN_CDOFF(pcd_rxdescs[(x)]) +#define PCN_CDINITOFF PCN_CDOFF(pcd_initblock) + +/* + * Software state for transmit jobs. + */ +struct pcn_txsoft { + struct mbuf *txs_mbuf; /* head of our mbuf chain */ + bus_dmamap_t txs_dmamap; /* our DMA map */ + int txs_firstdesc; /* first descriptor in packet */ + int txs_lastdesc; /* last descriptor in packet */ +}; + +/* + * Software state for receive jobs. + */ +struct pcn_rxsoft { + struct mbuf *rxs_mbuf; /* head of our mbuf chain */ + bus_dmamap_t rxs_dmamap; /* our DMA map */ +}; + +/* + * Description of Rx FIFO watermarks for various revisions. + */ +static const char * const pcn_79c970_rcvfw[] = { + "16 bytes", + "64 bytes", + "128 bytes", + NULL, +}; + +static const char * const pcn_79c971_rcvfw[] = { + "16 bytes", + "64 bytes", + "112 bytes", + NULL, +}; + +/* + * Description of Tx start points for various revisions. + */ +static const char * const pcn_79c970_xmtsp[] = { + "8 bytes", + "64 bytes", + "128 bytes", + "248 bytes", +}; + +static const char * const pcn_79c971_xmtsp[] = { + "20 bytes", + "64 bytes", + "128 bytes", + "248 bytes", +}; + +static const char * const pcn_79c971_xmtsp_sram[] = { + "44 bytes", + "64 bytes", + "128 bytes", + "store-and-forward", +}; + +/* + * Description of Tx FIFO watermarks for various revisions. + */ +static const char * const pcn_79c970_xmtfw[] = { + "16 bytes", + "64 bytes", + "128 bytes", + NULL, +}; + +static const char * const pcn_79c971_xmtfw[] = { + "16 bytes", + "64 bytes", + "108 bytes", + NULL, +}; + +/* + * Software state per device. + */ +struct pcn_softc { + struct device sc_dev; /* generic device information */ + bus_space_tag_t sc_st; /* bus space tag */ + bus_space_handle_t sc_sh; /* bus space handle */ + bus_dma_tag_t sc_dmat; /* bus DMA tag */ + struct arpcom sc_arpcom; /* Ethernet common data */ + void *sc_sdhook; /* shutdown hook */ + + /* Points to our media routines, etc. */ + const struct pcn_variant *sc_variant; + + void *sc_ih; /* interrupt cookie */ + + struct mii_data sc_mii; /* MII/media information */ + + struct timeout sc_tick_timeout; /* tick timeout */ + + bus_dmamap_t sc_cddmamap; /* control data DMA map */ +#define sc_cddma sc_cddmamap->dm_segs[0].ds_addr + + /* Software state for transmit and receive descriptors. */ + struct pcn_txsoft sc_txsoft[PCN_TXQUEUELEN]; + struct pcn_rxsoft sc_rxsoft[PCN_NRXDESC]; + + /* Control data structures */ + struct pcn_control_data *sc_control_data; +#define sc_txdescs sc_control_data->pcd_txdescs +#define sc_rxdescs sc_control_data->pcd_rxdescs +#define sc_initblock sc_control_data->pcd_initblock + + const char * const *sc_rcvfw_desc; /* Rx FIFO watermark info */ + int sc_rcvfw; + + const char * const *sc_xmtsp_desc; /* Tx start point info */ + int sc_xmtsp; + + const char * const *sc_xmtfw_desc; /* Tx FIFO watermark info */ + int sc_xmtfw; + + int sc_flags; /* misc. flags; see below */ + int sc_swstyle; /* the software style in use */ + + int sc_txfree; /* number of free Tx descriptors */ + int sc_txnext; /* next ready Tx descriptor */ + + int sc_txsfree; /* number of free Tx jobs */ + int sc_txsnext; /* next free Tx job */ + int sc_txsdirty; /* dirty Tx jobs */ + + int sc_rxptr; /* next ready Rx descriptor/job */ + + uint32_t sc_csr5; /* prototype CSR5 register */ + uint32_t sc_mode; /* prototype MODE register */ +}; + +/* sc_flags */ +#define PCN_F_HAS_MII 0x0001 /* has MII */ + +#define PCN_CDTXADDR(sc, x) ((sc)->sc_cddma + PCN_CDTXOFF((x))) +#define PCN_CDRXADDR(sc, x) ((sc)->sc_cddma + PCN_CDRXOFF((x))) +#define PCN_CDINITADDR(sc) ((sc)->sc_cddma + PCN_CDINITOFF) + +#define PCN_CDTXSYNC(sc, x, n, ops) \ +do { \ + int __x, __n; \ + \ + __x = (x); \ + __n = (n); \ + \ + /* If it will wrap around, sync to the end of the ring. */ \ + if ((__x + __n) > PCN_NTXDESC) { \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + PCN_CDTXOFF(__x), sizeof(struct letmd) * \ + (PCN_NTXDESC - __x), (ops)); \ + __n -= (PCN_NTXDESC - __x); \ + __x = 0; \ + } \ + \ + /* Now sync whatever is left. */ \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + PCN_CDTXOFF(__x), sizeof(struct letmd) * __n, (ops)); \ +} while (/*CONSTCOND*/0) + +#define PCN_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + PCN_CDRXOFF((x)), sizeof(struct lermd), (ops)) + +#define PCN_CDINITSYNC(sc, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cddmamap, \ + PCN_CDINITOFF, sizeof(struct leinit), (ops)) + +#define PCN_INIT_RXDESC(sc, x) \ +do { \ + struct pcn_rxsoft *__rxs = &(sc)->sc_rxsoft[(x)]; \ + struct lermd *__rmd = &(sc)->sc_rxdescs[(x)]; \ + struct mbuf *__m = __rxs->rxs_mbuf; \ + \ + /* \ + * Note: We scoot the packet forward 2 bytes in the buffer \ + * so that the payload after the Ethernet header is aligned \ + * to a 4-byte boundary. \ + */ \ + __m->m_data = __m->m_ext.ext_buf + 2; \ + \ + if ((sc)->sc_swstyle == LE_B20_SSTYLE_PCNETPCI3) { \ + __rmd->rmd2 = \ + htole32(__rxs->rxs_dmamap->dm_segs[0].ds_addr + 2); \ + __rmd->rmd0 = 0; \ + } else { \ + __rmd->rmd2 = 0; \ + __rmd->rmd0 = \ + htole32(__rxs->rxs_dmamap->dm_segs[0].ds_addr + 2); \ + } \ + __rmd->rmd1 = htole32(LE_R1_OWN|LE_R1_ONES| \ + (LE_BCNT(MCLBYTES - 2) & LE_R1_BCNT_MASK)); \ + PCN_CDRXSYNC((sc), (x), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);\ +} while(/*CONSTCOND*/0) + +void pcn_start(struct ifnet *); +void pcn_watchdog(struct ifnet *); +int pcn_ioctl(struct ifnet *, u_long, caddr_t); +int pcn_init(struct ifnet *); +void pcn_stop(struct ifnet *, int); + +void pcn_shutdown(void *); + +void pcn_reset(struct pcn_softc *); +void pcn_rxdrain(struct pcn_softc *); +int pcn_add_rxbuf(struct pcn_softc *, int); +void pcn_tick(void *); + +void pcn_spnd(struct pcn_softc *); + +void pcn_set_filter(struct pcn_softc *); + +int pcn_intr(void *); +void pcn_txintr(struct pcn_softc *); +int pcn_rxintr(struct pcn_softc *); + +int pcn_mii_readreg(struct device *, int, int); +void pcn_mii_writereg(struct device *, int, int, int); +void pcn_mii_statchg(struct device *); + +void pcn_79c970_mediainit(struct pcn_softc *); +int pcn_79c970_mediachange(struct ifnet *); +void pcn_79c970_mediastatus(struct ifnet *, struct ifmediareq *); + +void pcn_79c971_mediainit(struct pcn_softc *); +int pcn_79c971_mediachange(struct ifnet *); +void pcn_79c971_mediastatus(struct ifnet *, struct ifmediareq *); + +/* + * Description of a PCnet-PCI variant. Used to select media access + * method, mostly, and to print a nice description of the chip. + */ +static const struct pcn_variant { + const char *pcv_desc; + void (*pcv_mediainit)(struct pcn_softc *); + uint16_t pcv_chipid; +} pcn_variants[] = { + { "Am79c970", + pcn_79c970_mediainit, + PARTID_Am79c970 }, + + { "Am79c970A", + pcn_79c970_mediainit, + PARTID_Am79c970A }, + + { "Am79c971", + pcn_79c971_mediainit, + PARTID_Am79c971 }, + + { "Am79c972", + pcn_79c971_mediainit, + PARTID_Am79c972 }, + + { "Am79c973", + pcn_79c971_mediainit, + PARTID_Am79c973 }, + + { "Am79c975", + pcn_79c971_mediainit, + PARTID_Am79c975 }, + + { "Unknown", + pcn_79c971_mediainit, + 0 }, +}; + +int pcn_copy_small = 0; + +int pcn_match(struct device *, void *, void *); +void pcn_attach(struct device *, struct device *, void *); + +struct cfattach pcn_ca = { + sizeof(struct pcn_softc), pcn_match, pcn_attach, +}; + +struct cfdriver pcn_cd = { + 0, "pcn", DV_IFNET +}; + +/* + * Routines to read and write the PCnet-PCI CSR/BCR space. + */ + +static __inline uint32_t +pcn_csr_read(struct pcn_softc *sc, int reg) +{ + + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RAP, reg); + return (bus_space_read_4(sc->sc_st, sc->sc_sh, PCN32_RDP)); +} + +static __inline void +pcn_csr_write(struct pcn_softc *sc, int reg, uint32_t val) +{ + + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RAP, reg); + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RDP, val); +} + +static __inline uint32_t +pcn_bcr_read(struct pcn_softc *sc, int reg) +{ + + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RAP, reg); + return (bus_space_read_4(sc->sc_st, sc->sc_sh, PCN32_BDP)); +} + +static __inline void +pcn_bcr_write(struct pcn_softc *sc, int reg, uint32_t val) +{ + + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RAP, reg); + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_BDP, val); +} + +static const struct pcn_variant * +pcn_lookup_variant(uint16_t chipid) +{ + const struct pcn_variant *pcv; + + for (pcv = pcn_variants; pcv->pcv_chipid != 0; pcv++) { + if (chipid == pcv->pcv_chipid) + return (pcv); + } + + /* + * This covers unknown chips, which we simply treat like + * a generic PCnet-FAST. + */ + return (pcv); +} + +int +pcn_match(struct device *parent, void *match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_AMD) + return (0); + + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_AMD_PCNET_PCI: + /* Beat if_le_pci.c */ + return (10); + } + + return (0); +} + +void +pcn_attach(struct device *parent, struct device *self, void *aux) +{ + struct pcn_softc *sc = (struct pcn_softc *) self; + struct pci_attach_args *pa = aux; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr = NULL; + bus_space_tag_t iot, memt; + bus_space_handle_t ioh, memh; + bus_dma_segment_t seg; + int ioh_valid, memh_valid; + int i, rseg, error; + pcireg_t pmode; + uint32_t chipid, reg; + uint8_t enaddr[ETHER_ADDR_LEN]; + int pmreg; + + timeout_set(&sc->sc_tick_timeout, pcn_tick, sc); + + /* + * Map the device. + */ + ioh_valid = (pci_mapreg_map(pa, PCN_PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL, 0) == 0); + memh_valid = (pci_mapreg_map(pa, PCN_PCI_CBMEM, + PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, + &memt, &memh, NULL, NULL, 0) == 0); + + if (memh_valid) { + sc->sc_st = memt; + sc->sc_sh = memh; + } else if (ioh_valid) { + sc->sc_st = iot; + sc->sc_sh = ioh; + } else { + printf("%s: unable to map device registers\n", + sc->sc_dev.dv_xname); + return; + } + + sc->sc_dmat = pa->pa_dmat; + + /* Get it out of power save mode, if needed. */ + if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PWRMGMT, &pmreg, 0)) { + pmode = pci_conf_read(pc, pa->pa_tag, pmreg + PCI_PMCSR) & + PCI_PMCSR_STATE_MASK; + if (pmode == PCI_PMCSR_STATE_D3) { + /* + * The card has lost all configuration data in + * this state, so punt. + */ + printf(": unable to wake from power state D3\n"); + return; + } + if (pmode != PCI_PMCSR_STATE_D0) { + printf(": waking up from power date D%d", + pmode); + pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR, + PCI_PMCSR_STATE_D0); + } + } + + /* + * Reset the chip to a known state. This also puts the + * chip into 32-bit mode. + */ + pcn_reset(sc); + +#if !defined(PCN_NO_PROM) + + /* + * Read the Ethernet address from the EEPROM. + */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + enaddr[i] = bus_space_read_1(sc->sc_st, sc->sc_sh, + PCN32_APROM + i); +#else + /* + * The PROM is not used; instead we assume that the MAC address + * has been programmed into the device's physical address + * registers by the boot firmware + */ + + for (i=0; i < 3; i++) { + uint32_t val; + val = pcn_csr_read(sc, LE_CSR12 + i); + enaddr[2*i] = val & 0x0ff; + enaddr[2*i+1] = (val >> 8) & 0x0ff; + } +#endif + + /* + * Now that the device is mapped, attempt to figure out what + * kind of chip we have. Note that IDL has all 32 bits of + * the chip ID when we're in 32-bit mode. + */ + chipid = pcn_csr_read(sc, LE_CSR88); + sc->sc_variant = pcn_lookup_variant(CHIPID_PARTID(chipid)); + + /* + * Map and establish our interrupt. + */ + if (pci_intr_map(pa, &ih)) { + printf(": unable to map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, pcn_intr, sc, + self->dv_xname); + if (sc->sc_ih == NULL) { + printf("%s: unable to establish interrupt", + sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf(", %s, rev %d : %s, address %s\n", sc->sc_variant->pcv_desc, + CHIPID_VER(chipid), intrstr, ether_sprintf(enaddr)); + + /* + * Allocate the control data structures, and create and load the + * DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct pcn_control_data), PAGE_SIZE, 0, &seg, 1, &rseg, + 0)) != 0) { + printf("%s: unable to allocate control data, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_0; + } + + if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, + sizeof(struct pcn_control_data), (caddr_t *)&sc->sc_control_data, + BUS_DMA_COHERENT)) != 0) { + printf("%s: unable to map control data, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_1; + } + + if ((error = bus_dmamap_create(sc->sc_dmat, + sizeof(struct pcn_control_data), 1, + sizeof(struct pcn_control_data), 0, 0, &sc->sc_cddmamap)) != 0) { + printf("%s: unable to create control data DMA map, " + "error = %d\n", sc->sc_dev.dv_xname, error); + goto fail_2; + } + + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap, + sc->sc_control_data, sizeof(struct pcn_control_data), NULL, + 0)) != 0) { + printf("%s: unable to load control data DMA map, error = %d\n", + sc->sc_dev.dv_xname, error); + goto fail_3; + } + + /* Create the transmit buffer DMA maps. */ + for (i = 0; i < PCN_TXQUEUELEN; i++) { + if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + PCN_NTXSEGS, MCLBYTES, 0, 0, + &sc->sc_txsoft[i].txs_dmamap)) != 0) { + printf("%s: unable to create tx DMA map %d, " + "error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail_4; + } + } + + /* Create the receive buffer DMA maps. */ + for (i = 0; i < PCN_NRXDESC; i++) { + if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, + MCLBYTES, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) { + printf("%s: unable to create rx DMA map %d, " + "error = %d\n", sc->sc_dev.dv_xname, i, error); + goto fail_5; + } + sc->sc_rxsoft[i].rxs_mbuf = NULL; + } + + /* Initialize our media structures. */ + (*sc->sc_variant->pcv_mediainit)(sc); + + /* + * Initialize FIFO watermark info. + */ + switch (sc->sc_variant->pcv_chipid) { + case PARTID_Am79c970: + case PARTID_Am79c970A: + sc->sc_rcvfw_desc = pcn_79c970_rcvfw; + sc->sc_xmtsp_desc = pcn_79c970_xmtsp; + sc->sc_xmtfw_desc = pcn_79c970_xmtfw; + break; + + default: + sc->sc_rcvfw_desc = pcn_79c971_rcvfw; + /* + * Read BCR25 to determine how much SRAM is + * on the board. If > 0, then we the chip + * uses different Start Point thresholds. + * + * Note BCR25 and BCR26 are loaded from the + * EEPROM on RST, and unaffected by S_RESET, + * so we don't really have to worry about + * them except for this. + */ + reg = pcn_bcr_read(sc, LE_BCR25) & 0x00ff; + if (reg != 0) + sc->sc_xmtsp_desc = pcn_79c971_xmtsp_sram; + else + sc->sc_xmtsp_desc = pcn_79c971_xmtsp; + sc->sc_xmtfw_desc = pcn_79c971_xmtfw; + break; + } + + /* + * Set up defaults -- see the tables above for what these + * values mean. + * + * XXX How should we tune RCVFW and XMTFW? + */ + sc->sc_rcvfw = 1; /* minimum for full-duplex */ + sc->sc_xmtsp = 1; + sc->sc_xmtfw = 0; + + ifp = &sc->sc_arpcom.ac_if; + bcopy(enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN); + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = pcn_ioctl; + ifp->if_start = pcn_start; + ifp->if_watchdog = pcn_watchdog; + IFQ_SET_MAXLEN(&ifp->if_snd, PCN_NTXDESC -1); + IFQ_SET_READY(&ifp->if_snd); + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp); + + /* Make sure the interface is shutdown during reboot. */ + sc->sc_sdhook = shutdownhook_establish(pcn_shutdown, sc); + if (sc->sc_sdhook == NULL) + printf("%s: WARNING: unable to establish shutdown hook\n", + sc->sc_dev.dv_xname); + return; + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + fail_5: + for (i = 0; i < PCN_NRXDESC; i++) { + if (sc->sc_rxsoft[i].rxs_dmamap != NULL) + bus_dmamap_destroy(sc->sc_dmat, + sc->sc_rxsoft[i].rxs_dmamap); + } + fail_4: + for (i = 0; i < PCN_TXQUEUELEN; i++) { + if (sc->sc_txsoft[i].txs_dmamap != NULL) + bus_dmamap_destroy(sc->sc_dmat, + sc->sc_txsoft[i].txs_dmamap); + } + bus_dmamap_unload(sc->sc_dmat, sc->sc_cddmamap); + fail_3: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_cddmamap); + fail_2: + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_control_data, + sizeof(struct pcn_control_data)); + fail_1: + bus_dmamem_free(sc->sc_dmat, &seg, rseg); + fail_0: + return; +} + +/* + * pcn_shutdown: + * + * Make sure the interface is stopped at reboot time. + */ +void +pcn_shutdown(void *arg) +{ + struct pcn_softc *sc = arg; + + pcn_stop(&sc->sc_arpcom.ac_if, 1); +} + +/* + * pcn_start: [ifnet interface function] + * + * Start packet transmission on the interface. + */ +void +pcn_start(struct ifnet *ifp) +{ + struct pcn_softc *sc = ifp->if_softc; + struct mbuf *m0, *m; + struct pcn_txsoft *txs; + bus_dmamap_t dmamap; + int error, nexttx, lasttx = -1, ofree, seg; + + if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) + return; + + /* + * Remember the previous number of free descriptors and + * the first descriptor we'll use. + */ + ofree = sc->sc_txfree; + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + for (;;) { + /* Grab a packet off the queue. */ + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + m = NULL; + + /* Get a work queue entry. */ + if (sc->sc_txsfree == 0) + break; + + txs = &sc->sc_txsoft[sc->sc_txsnext]; + dmamap = txs->txs_dmamap; + + /* + * Load the DMA map. If this fails, the packet either + * didn't fit in the alloted number of segments, or we + * were short on resources. In this case, we'll copy + * and try again. + */ + if (bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_WRITE|BUS_DMA_NOWAIT) != 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + break; + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + break; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t)); + m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len; + error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, + m, BUS_DMA_WRITE|BUS_DMA_NOWAIT); + if (error) + break; + } + + /* + * Ensure we have enough descriptors free to describe + * the packet. Note, we always reserve one descriptor + * at the end of the ring as a termination point, to + * prevent wrap-around. + */ + if (dmamap->dm_nsegs > (sc->sc_txfree - 1)) { + /* + * Not enough free descriptors to transmit this + * packet. We haven't committed anything yet, + * so just unload the DMA map, put the packet + * back on the queue, and punt. Notify the upper + * layer that there are not more slots left. + * + * XXX We could allocate an mbuf and copy, but + * XXX is it worth it? + */ + ifp->if_flags |= IFF_OACTIVE; + bus_dmamap_unload(sc->sc_dmat, dmamap); + if (m != NULL) + m_freem(m); + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m0); + if (m != NULL) { + m_freem(m0); + m0 = m; + } + + /* + * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET. + */ + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Initialize the transmit descriptors. + */ + if (sc->sc_swstyle == LE_B20_SSTYLE_PCNETPCI3) { + for (nexttx = sc->sc_txnext, seg = 0; + seg < dmamap->dm_nsegs; + seg++, nexttx = PCN_NEXTTX(nexttx)) { + /* + * If this is the first descriptor we're + * enqueueing, don't set the OWN bit just + * yet. That could cause a race condition. + * We'll do it below. + */ + sc->sc_txdescs[nexttx].tmd0 = 0; + sc->sc_txdescs[nexttx].tmd2 = + htole32(dmamap->dm_segs[seg].ds_addr); + sc->sc_txdescs[nexttx].tmd1 = + htole32(LE_T1_ONES | + (nexttx == sc->sc_txnext ? 0 : LE_T1_OWN) | + (LE_BCNT(dmamap->dm_segs[seg].ds_len) & + LE_T1_BCNT_MASK)); + lasttx = nexttx; + } + } else { + for (nexttx = sc->sc_txnext, seg = 0; + seg < dmamap->dm_nsegs; + seg++, nexttx = PCN_NEXTTX(nexttx)) { + /* + * If this is the first descriptor we're + * enqueueing, don't set the OWN bit just + * yet. That could cause a race condition. + * We'll do it below. + */ + sc->sc_txdescs[nexttx].tmd0 = + htole32(dmamap->dm_segs[seg].ds_addr); + sc->sc_txdescs[nexttx].tmd2 = 0; + sc->sc_txdescs[nexttx].tmd1 = + htole32(LE_T1_ONES | + (nexttx == sc->sc_txnext ? 0 : LE_T1_OWN) | + (LE_BCNT(dmamap->dm_segs[seg].ds_len) & + LE_T1_BCNT_MASK)); + lasttx = nexttx; + } + } + + KASSERT(lasttx != -1); + /* Interrupt on the packet, if appropriate. */ + if ((sc->sc_txsnext & PCN_TXINTR_MASK) == 0) + sc->sc_txdescs[lasttx].tmd1 |= htole32(LE_T1_LTINT); + + /* Set `start of packet' and `end of packet' appropriately. */ + sc->sc_txdescs[lasttx].tmd1 |= htole32(LE_T1_ENP); + sc->sc_txdescs[sc->sc_txnext].tmd1 |= + htole32(LE_T1_OWN|LE_T1_STP); + + /* Sync the descriptors we're using. */ + PCN_CDTXSYNC(sc, sc->sc_txnext, dmamap->dm_nsegs, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + + /* Kick the transmitter. */ + pcn_csr_write(sc, LE_CSR0, LE_C0_INEA|LE_C0_TDMD); + + /* + * Store a pointer to the packet so we can free it later, + * and remember what txdirty will be once the packet is + * done. + */ + txs->txs_mbuf = m0; + txs->txs_firstdesc = sc->sc_txnext; + txs->txs_lastdesc = lasttx; + + /* Advance the tx pointer. */ + sc->sc_txfree -= dmamap->dm_nsegs; + sc->sc_txnext = nexttx; + + sc->sc_txsfree--; + sc->sc_txsnext = PCN_NEXTTXS(sc->sc_txsnext); + +#if NBPFILTER > 0 + /* Pass the packet to any BPF listeners. */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif /* NBPFILTER > 0 */ + } + + if (sc->sc_txsfree == 0 || sc->sc_txfree == 0) { + /* No more slots left; notify upper layer. */ + ifp->if_flags |= IFF_OACTIVE; + } + + if (sc->sc_txfree != ofree) { + /* Set a watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } +} + +/* + * pcn_watchdog: [ifnet interface function] + * + * Watchdog timer handler. + */ +void +pcn_watchdog(struct ifnet *ifp) +{ + struct pcn_softc *sc = ifp->if_softc; + + /* + * Since we're not interrupting every packet, sweep + * up before we report an error. + */ + pcn_txintr(sc); + + if (sc->sc_txfree != PCN_NTXDESC) { + printf("%s: device timeout (txfree %d txsfree %d)\n", + sc->sc_dev.dv_xname, sc->sc_txfree, sc->sc_txsfree); + ifp->if_oerrors++; + + /* Reset the interface. */ + (void) pcn_init(ifp); + } + + /* Try to get more packets going. */ + pcn_start(ifp); +} + +/* + * pcn_ioctl: [ifnet interface function] + * + * Handle control requests from the operator. + */ +int +pcn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct pcn_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error; + + s = splnet(); + + if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { + /* Try to get more packets going. */ + pcn_start(ifp); + + splx(s); + return (error); + } + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + pcn_init(ifp); + arp_ifinit(&sc->sc_arpcom, ifa); + break; +#endif + default: + pcn_init(ifp); + break; + } + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) + error = EINVAL; + else if (ifp->if_mtu != ifr->ifr_mtu) + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked up and not running, then start it. + * If it is marked down and running, stop it. + * XXX If it's up then re-initialize it. This is so flags + * such as IFF_PROMISC are handled. + */ + if (ifp->if_flags & IFF_UP) + pcn_init(ifp); + else if (ifp->if_flags & IFF_RUNNING) + pcn_stop(ifp, 1); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_arpcom) : + ether_delmulti(ifr, &sc->sc_arpcom); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. + */ + if (ifp->if_flags & IFF_RUNNING) + error = pcn_init(ifp); + else + error = 0; + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); + break; + + default: + error = EINVAL; + } + + /* Try to get more packets going. */ + pcn_start(ifp); + + splx(s); + return (error); +} + +/* + * pcn_intr: + * + * Interrupt service routine. + */ +int +pcn_intr(void *arg) +{ + struct pcn_softc *sc = arg; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + uint32_t csr0; + int wantinit, handled = 0; + + for (wantinit = 0; wantinit == 0;) { + csr0 = pcn_csr_read(sc, LE_CSR0); + if ((csr0 & LE_C0_INTR) == 0) + break; + + /* ACK the bits and re-enable interrupts. */ + pcn_csr_write(sc, LE_CSR0, csr0 & + (LE_C0_INEA|LE_C0_BABL|LE_C0_MISS|LE_C0_MERR|LE_C0_RINT| + LE_C0_TINT|LE_C0_IDON)); + + handled = 1; + + if (csr0 & LE_C0_RINT) + wantinit = pcn_rxintr(sc); + + if (csr0 & LE_C0_TINT) + pcn_txintr(sc); + + if (csr0 & LE_C0_ERR) { + if (csr0 & LE_C0_BABL) + ifp->if_oerrors++; + if (csr0 & LE_C0_MISS) + ifp->if_ierrors++; + if (csr0 & LE_C0_MERR) { + printf("%s: memory error\n", + sc->sc_dev.dv_xname); + wantinit = 1; + break; + } + } + + if ((csr0 & LE_C0_RXON) == 0) { + printf("%s: receiver disabled\n", + sc->sc_dev.dv_xname); + ifp->if_ierrors++; + wantinit = 1; + } + + if ((csr0 & LE_C0_TXON) == 0) { + printf("%s: transmitter disabled\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; + wantinit = 1; + } + } + + if (handled) { + if (wantinit) + pcn_init(ifp); + + /* Try to get more packets going. */ + pcn_start(ifp); + } + + return (handled); +} + +/* + * pcn_spnd: + * + * Suspend the chip. + */ +void +pcn_spnd(struct pcn_softc *sc) +{ + int i; + + pcn_csr_write(sc, LE_CSR5, sc->sc_csr5 | LE_C5_SPND); + + for (i = 0; i < 10000; i++) { + if (pcn_csr_read(sc, LE_CSR5) & LE_C5_SPND) + return; + delay(5); + } + + printf("%s: WARNING: chip failed to enter suspended state\n", + sc->sc_dev.dv_xname); +} + +/* + * pcn_txintr: + * + * Helper; handle transmit interrupts. + */ +void +pcn_txintr(struct pcn_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct pcn_txsoft *txs; + uint32_t tmd1, tmd2, tmd; + int i, j; + + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Go through our Tx list and free mbufs for those + * frames which have been transmitted. + */ + for (i = sc->sc_txsdirty; sc->sc_txsfree != PCN_TXQUEUELEN; + i = PCN_NEXTTXS(i), sc->sc_txsfree++) { + txs = &sc->sc_txsoft[i]; + + PCN_CDTXSYNC(sc, txs->txs_firstdesc, txs->txs_dmamap->dm_nsegs, + BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + tmd1 = letoh32(sc->sc_txdescs[txs->txs_lastdesc].tmd1); + if (tmd1 & LE_T1_OWN) + break; + + /* + * Slightly annoying -- we have to loop through the + * descriptors we've used looking for ERR, since it + * can appear on any descriptor in the chain. + */ + for (j = txs->txs_firstdesc;; j = PCN_NEXTTX(j)) { + tmd = letoh32(sc->sc_txdescs[j].tmd1); + if (tmd & LE_T1_ERR) { + ifp->if_oerrors++; + if (sc->sc_swstyle == LE_B20_SSTYLE_PCNETPCI3) + tmd2 = letoh32(sc->sc_txdescs[j].tmd0); + else + tmd2 = letoh32(sc->sc_txdescs[j].tmd2); + if (tmd2 & LE_T2_UFLO) { + if (sc->sc_xmtsp < LE_C80_XMTSP_MAX) { + sc->sc_xmtsp++; + printf("%s: transmit " + "underrun; new threshold: " + "%s\n", + sc->sc_dev.dv_xname, + sc->sc_xmtsp_desc[ + sc->sc_xmtsp]); + pcn_spnd(sc); + pcn_csr_write(sc, LE_CSR80, + LE_C80_RCVFW(sc->sc_rcvfw) | + LE_C80_XMTSP(sc->sc_xmtsp) | + LE_C80_XMTFW(sc->sc_xmtfw)); + pcn_csr_write(sc, LE_CSR5, + sc->sc_csr5); + } else { + printf("%s: transmit " + "underrun\n", + sc->sc_dev.dv_xname); + } + } else if (tmd2 & LE_T2_BUFF) { + printf("%s: transmit buffer error\n", + sc->sc_dev.dv_xname); + } + if (tmd2 & LE_T2_LCOL) + ifp->if_collisions++; + if (tmd2 & LE_T2_RTRY) + ifp->if_collisions += 16; + goto next_packet; + } + if (j == txs->txs_lastdesc) + break; + } + if (tmd1 & LE_T1_ONE) + ifp->if_collisions++; + else if (tmd & LE_T1_MORE) { + /* Real number is unknown. */ + ifp->if_collisions += 2; + } + ifp->if_opackets++; + next_packet: + sc->sc_txfree += txs->txs_dmamap->dm_nsegs; + bus_dmamap_sync(sc->sc_dmat, txs->txs_dmamap, + 0, txs->txs_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, txs->txs_dmamap); + m_freem(txs->txs_mbuf); + txs->txs_mbuf = NULL; + } + + /* Update the dirty transmit buffer pointer. */ + sc->sc_txsdirty = i; + + /* + * If there are no more pending transmissions, cancel the watchdog + * timer. + */ + if (sc->sc_txsfree == PCN_TXQUEUELEN) + ifp->if_timer = 0; +} + +/* + * pcn_rxintr: + * + * Helper; handle receive interrupts. + */ +int +pcn_rxintr(struct pcn_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct pcn_rxsoft *rxs; + struct mbuf *m; + uint32_t rmd1; + int i, len; + + for (i = sc->sc_rxptr;; i = PCN_NEXTRX(i)) { + rxs = &sc->sc_rxsoft[i]; + + PCN_CDRXSYNC(sc, i, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); + + rmd1 = letoh32(sc->sc_rxdescs[i].rmd1); + + if (rmd1 & LE_R1_OWN) + break; + + /* + * Check for errors and make sure the packet fit into + * a single buffer. We have structured this block of + * code the way it is in order to compress it into + * one test in the common case (no error). + */ + if (__predict_false((rmd1 & (LE_R1_STP|LE_R1_ENP|LE_R1_ERR)) != + (LE_R1_STP|LE_R1_ENP))) { + /* Make sure the packet is in a single buffer. */ + if ((rmd1 & (LE_R1_STP|LE_R1_ENP)) != + (LE_R1_STP|LE_R1_ENP)) { + printf("%s: packet spilled into next buffer\n", + sc->sc_dev.dv_xname); + return (1); /* pcn_intr() will re-init */ + } + + /* + * If the packet had an error, simple recycle the + * buffer. + */ + if (rmd1 & LE_R1_ERR) { + ifp->if_ierrors++; + /* + * If we got an overflow error, chances + * are there will be a CRC error. In + * this case, just print the overflow + * error, and skip the others. + */ + if (rmd1 & LE_R1_OFLO) + printf("%s: overflow error\n", + sc->sc_dev.dv_xname); + else { +#define PRINTIT(x, str) \ + if (rmd1 & (x)) \ + printf("%s: %s\n", \ + sc->sc_dev.dv_xname, str); + PRINTIT(LE_R1_FRAM, "framing error"); + PRINTIT(LE_R1_CRC, "CRC error"); + PRINTIT(LE_R1_BUFF, "buffer error"); + } +#undef PRINTIT + PCN_INIT_RXDESC(sc, i); + continue; + } + } + + bus_dmamap_sync(sc->sc_dmat, rxs->rxs_dmamap, 0, + rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + + /* + * No errors; receive the packet. + */ + if (sc->sc_swstyle == LE_B20_SSTYLE_PCNETPCI3) + len = letoh32(sc->sc_rxdescs[i].rmd0) & LE_R1_BCNT_MASK; + else + len = letoh32(sc->sc_rxdescs[i].rmd2) & LE_R1_BCNT_MASK; + + /* + * The LANCE family includes the CRC with every packet; + * trim it off here. + */ + len -= ETHER_CRC_LEN; + + /* + * If the packet is small enough to fit in a + * single header mbuf, allocate one and copy + * the data into it. This greatly reduces + * memory consumption when we receive lots + * of small packets. + * + * Otherwise, we add a new buffer to the receive + * chain. If this fails, we drop the packet and + * recycle the old buffer. + */ + if (pcn_copy_small != 0 && len <= (MHLEN - 2)) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + goto dropit; + m->m_data += 2; + memcpy(mtod(m, caddr_t), + mtod(rxs->rxs_mbuf, caddr_t), len); + PCN_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->sc_dmat, rxs->rxs_dmamap, 0, + rxs->rxs_dmamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + } else { + m = rxs->rxs_mbuf; + if (pcn_add_rxbuf(sc, i) != 0) { + dropit: + ifp->if_ierrors++; + PCN_INIT_RXDESC(sc, i); + bus_dmamap_sync(sc->sc_dmat, + rxs->rxs_dmamap, 0, + rxs->rxs_dmamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + continue; + } + } + + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + +#if NBPFILTER > 0 + /* Pass this up to any BPF listeners. */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif /* NBPFILTER > 0 */ + + /* Pass it on. */ + ether_input_mbuf(ifp, m); + ifp->if_ipackets++; + } + + /* Update the receive pointer. */ + sc->sc_rxptr = i; + return (0); +} + +/* + * pcn_tick: + * + * One second timer, used to tick the MII. + */ +void +pcn_tick(void *arg) +{ + struct pcn_softc *sc = arg; + int s; + + s = splnet(); + mii_tick(&sc->sc_mii); + splx(s); + + timeout_add(&sc->sc_tick_timeout, hz); +} + +/* + * pcn_reset: + * + * Perform a soft reset on the PCnet-PCI. + */ +void +pcn_reset(struct pcn_softc *sc) +{ + + /* + * The PCnet-PCI chip is reset by reading from the + * RESET register. Note that while the NE2100 LANCE + * boards require a write after the read, the PCnet-PCI + * chips do not require this. + * + * Since we don't know if we're in 16-bit or 32-bit + * mode right now, issue both (it's safe) in the + * hopes that one will succeed. + */ + (void) bus_space_read_2(sc->sc_st, sc->sc_sh, PCN16_RESET); + (void) bus_space_read_4(sc->sc_st, sc->sc_sh, PCN32_RESET); + + /* Wait 1ms for it to finish. */ + delay(1000); + + /* + * Select 32-bit I/O mode by issuing a 32-bit write to the + * RDP. Since the RAP is 0 after a reset, writing a 0 + * to RDP is safe (since it simply clears CSR0). + */ + bus_space_write_4(sc->sc_st, sc->sc_sh, PCN32_RDP, 0); +} + +/* + * pcn_init: [ifnet interface function] + * + * Initialize the interface. Must be called at splnet(). + */ +int +pcn_init(struct ifnet *ifp) +{ + struct pcn_softc *sc = ifp->if_softc; + struct pcn_rxsoft *rxs; + uint8_t *enaddr = LLADDR(ifp->if_sadl); + int i, error = 0; + uint32_t reg; + + /* Cancel any pending I/O. */ + pcn_stop(ifp, 0); + + /* Reset the chip to a known state. */ + pcn_reset(sc); + + /* + * On the Am79c970, select SSTYLE 2, and SSTYLE 3 on everything + * else. + * + * XXX It'd be really nice to use SSTYLE 2 on all the chips, + * because the structure layout is compatible with ILACC, + * but the burst mode is only available in SSTYLE 3, and + * burst mode should provide some performance enhancement. + */ + if (sc->sc_variant->pcv_chipid == PARTID_Am79c970) + sc->sc_swstyle = LE_B20_SSTYLE_PCNETPCI2; + else + sc->sc_swstyle = LE_B20_SSTYLE_PCNETPCI3; + pcn_bcr_write(sc, LE_BCR20, sc->sc_swstyle); + + /* Initialize the transmit descriptor ring. */ + memset(sc->sc_txdescs, 0, sizeof(sc->sc_txdescs)); + PCN_CDTXSYNC(sc, 0, PCN_NTXDESC, + BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); + sc->sc_txfree = PCN_NTXDESC; + sc->sc_txnext = 0; + + /* Initialize the transmit job descriptors. */ + for (i = 0; i < PCN_TXQUEUELEN; i++) + sc->sc_txsoft[i].txs_mbuf = NULL; + sc->sc_txsfree = PCN_TXQUEUELEN; + sc->sc_txsnext = 0; + sc->sc_txsdirty = 0; + + /* + * Initialize the receive descriptor and receive job + * descriptor rings. + */ + for (i = 0; i < PCN_NRXDESC; i++) { + rxs = &sc->sc_rxsoft[i]; + if (rxs->rxs_mbuf == NULL) { + if ((error = pcn_add_rxbuf(sc, i)) != 0) { + printf("%s: unable to allocate or map rx " + "buffer %d, error = %d\n", + sc->sc_dev.dv_xname, i, error); + /* + * XXX Should attempt to run with fewer receive + * XXX buffers instead of just failing. + */ + pcn_rxdrain(sc); + goto out; + } + } else + PCN_INIT_RXDESC(sc, i); + } + sc->sc_rxptr = 0; + + /* Initialize MODE for the initialization block. */ + sc->sc_mode = 0; + if (ifp->if_flags & IFF_PROMISC) + sc->sc_mode |= LE_C15_PROM; + if ((ifp->if_flags & IFF_BROADCAST) == 0) + sc->sc_mode |= LE_C15_DRCVBC; + + /* + * If we have MII, simply select MII in the MODE register, + * and clear ASEL. Otherwise, let ASEL stand (for now), + * and leave PORTSEL alone (it is ignored with ASEL is set). + */ + if (sc->sc_flags & PCN_F_HAS_MII) { + pcn_bcr_write(sc, LE_BCR2, + pcn_bcr_read(sc, LE_BCR2) & ~LE_B2_ASEL); + sc->sc_mode |= LE_C15_PORTSEL(PORTSEL_MII); + + /* + * Disable MII auto-negotiation. We handle that in + * our own MII layer. + */ + pcn_bcr_write(sc, LE_BCR32, + pcn_bcr_read(sc, LE_BCR32) | LE_B32_DANAS); + } + + /* + * Set the Tx and Rx descriptor ring addresses in the init + * block, the TLEN and RLEN other fields of the init block + * MODE register. + */ + sc->sc_initblock.init_rdra = htole32(PCN_CDRXADDR(sc, 0)); + sc->sc_initblock.init_tdra = htole32(PCN_CDTXADDR(sc, 0)); + sc->sc_initblock.init_mode = htole32(sc->sc_mode | + ((ffs(PCN_NTXDESC) - 1) << 28) | + ((ffs(PCN_NRXDESC) - 1) << 20)); + + /* Set the station address in the init block. */ + sc->sc_initblock.init_padr[0] = htole32(enaddr[0] | + (enaddr[1] << 8) | (enaddr[2] << 16) | (enaddr[3] << 24)); + sc->sc_initblock.init_padr[1] = htole32(enaddr[4] | + (enaddr[5] << 8)); + + /* Set the multicast filter in the init block. */ + pcn_set_filter(sc); + + /* Initialize CSR3. */ + pcn_csr_write(sc, LE_CSR3, LE_C3_MISSM|LE_C3_IDONM|LE_C3_DXSUFLO); + + /* Initialize CSR4. */ + pcn_csr_write(sc, LE_CSR4, LE_C4_DMAPLUS|LE_C4_APAD_XMT| + LE_C4_MFCOM|LE_C4_RCVCCOM|LE_C4_TXSTRTM); + + /* Initialize CSR5. */ + sc->sc_csr5 = LE_C5_LTINTEN|LE_C5_SINTE; + pcn_csr_write(sc, LE_CSR5, sc->sc_csr5); + + /* + * If we have an Am79c971 or greater, initialize CSR7. + * + * XXX Might be nice to use the MII auto-poll interrupt someday. + */ + switch (sc->sc_variant->pcv_chipid) { + case PARTID_Am79c970: + case PARTID_Am79c970A: + /* Not available on these chips. */ + break; + + default: + pcn_csr_write(sc, LE_CSR7, LE_C7_FASTSPNDE); + break; + } + + /* + * On the Am79c970A and greater, initialize BCR18 to + * enable burst mode. + * + * Also enable the "no underflow" option on the Am79c971 and + * higher, which prevents the chip from generating transmit + * underflows, yet sill provides decent performance. Note if + * chip is not connected to external SRAM, then we still have + * to handle underflow errors (the NOUFLO bit is ignored in + * that case). + */ + reg = pcn_bcr_read(sc, LE_BCR18); + switch (sc->sc_variant->pcv_chipid) { + case PARTID_Am79c970: + break; + + case PARTID_Am79c970A: + reg |= LE_B18_BREADE|LE_B18_BWRITE; + break; + + default: + reg |= LE_B18_BREADE|LE_B18_BWRITE|LE_B18_NOUFLO; + break; + } + pcn_bcr_write(sc, LE_BCR18, reg); + + /* + * Initialize CSR80 (FIFO thresholds for Tx and Rx). + */ + pcn_csr_write(sc, LE_CSR80, LE_C80_RCVFW(sc->sc_rcvfw) | + LE_C80_XMTSP(sc->sc_xmtsp) | LE_C80_XMTFW(sc->sc_xmtfw)); + + /* + * Send the init block to the chip, and wait for it + * to be processed. + */ + PCN_CDINITSYNC(sc, BUS_DMASYNC_PREWRITE); + pcn_csr_write(sc, LE_CSR1, PCN_CDINITADDR(sc) & 0xffff); + pcn_csr_write(sc, LE_CSR2, (PCN_CDINITADDR(sc) >> 16) & 0xffff); + pcn_csr_write(sc, LE_CSR0, LE_C0_INIT); + delay(100); + for (i = 0; i < 10000; i++) { + if (pcn_csr_read(sc, LE_CSR0) & LE_C0_IDON) + break; + delay(10); + } + PCN_CDINITSYNC(sc, BUS_DMASYNC_POSTWRITE); + if (i == 10000) { + printf("%s: timeout processing init block\n", + sc->sc_dev.dv_xname); + error = EIO; + goto out; + } + + /* Set the media. */ + (void) (*sc->sc_mii.mii_media.ifm_change)(ifp); + + /* Enable interrupts and external activity (and ACK IDON). */ + pcn_csr_write(sc, LE_CSR0, LE_C0_INEA|LE_C0_STRT|LE_C0_IDON); + + if (sc->sc_flags & PCN_F_HAS_MII) { + /* Start the one second MII clock. */ + timeout_add(&sc->sc_tick_timeout, hz); + } + + /* ...all done! */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + out: + if (error) + printf("%s: interface not running\n", sc->sc_dev.dv_xname); + return (error); +} + +/* + * pcn_rxdrain: + * + * Drain the receive queue. + */ +void +pcn_rxdrain(struct pcn_softc *sc) +{ + struct pcn_rxsoft *rxs; + int i; + + for (i = 0; i < PCN_NRXDESC; i++) { + rxs = &sc->sc_rxsoft[i]; + if (rxs->rxs_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, rxs->rxs_dmamap); + m_freem(rxs->rxs_mbuf); + rxs->rxs_mbuf = NULL; + } + } +} + +/* + * pcn_stop: [ifnet interface function] + * + * Stop transmission on the interface. + */ +void +pcn_stop(struct ifnet *ifp, int disable) +{ + struct pcn_softc *sc = ifp->if_softc; + struct pcn_txsoft *txs; + int i; + + if (sc->sc_flags & PCN_F_HAS_MII) { + /* Stop the one second clock. */ + timeout_del(&sc->sc_tick_timeout); + + /* Down the MII. */ + mii_down(&sc->sc_mii); + } + + /* Mark the interface as down and cancel the watchdog timer. */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + + /* Stop the chip. */ + pcn_csr_write(sc, LE_CSR0, LE_C0_STOP); + + /* Release any queued transmit buffers. */ + for (i = 0; i < PCN_TXQUEUELEN; i++) { + txs = &sc->sc_txsoft[i]; + if (txs->txs_mbuf != NULL) { + bus_dmamap_unload(sc->sc_dmat, txs->txs_dmamap); + m_freem(txs->txs_mbuf); + txs->txs_mbuf = NULL; + } + } + + if (disable) + pcn_rxdrain(sc); +} + +/* + * pcn_add_rxbuf: + * + * Add a receive buffer to the indicated descriptor. + */ +int +pcn_add_rxbuf(struct pcn_softc *sc, int idx) +{ + struct pcn_rxsoft *rxs = &sc->sc_rxsoft[idx]; + struct mbuf *m; + int error; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return (ENOBUFS); + } + + if (rxs->rxs_mbuf != NULL) + bus_dmamap_unload(sc->sc_dmat, rxs->rxs_dmamap); + + rxs->rxs_mbuf = m; + + error = bus_dmamap_load(sc->sc_dmat, rxs->rxs_dmamap, + m->m_ext.ext_buf, m->m_ext.ext_size, NULL, + BUS_DMA_READ|BUS_DMA_NOWAIT); + if (error) { + printf("%s: can't load rx DMA map %d, error = %d\n", + sc->sc_dev.dv_xname, idx, error); + panic("pcn_add_rxbuf"); + } + + bus_dmamap_sync(sc->sc_dmat, rxs->rxs_dmamap, 0, + rxs->rxs_dmamap->dm_mapsize, BUS_DMASYNC_PREREAD); + + PCN_INIT_RXDESC(sc, idx); + + return (0); +} + +/* + * pcn_set_filter: + * + * Set up the receive filter. + */ +void +pcn_set_filter(struct pcn_softc *sc) +{ + struct arpcom *ac = &sc->sc_arpcom; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct ether_multi *enm; + struct ether_multistep step; + uint32_t crc; + + /* + * Set up the multicast address filter by passing all multicast + * addresses through a CRC generator, and then using the high + * order 6 bits as an index into the 64-bit logical address + * filter. The high order bits select the word, while the rest + * of the bits select the bit within the word. + */ + + if (ifp->if_flags & IFF_PROMISC) + goto allmulti; + + sc->sc_initblock.init_ladrf[0] = + sc->sc_initblock.init_ladrf[1] = + sc->sc_initblock.init_ladrf[2] = + sc->sc_initblock.init_ladrf[3] = 0; + + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + goto allmulti; + } + + crc = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN); + + /* Just want the 6 most significant bits. */ + crc >>= 26; + + /* Set the corresponding bit in the filter. */ + sc->sc_initblock.init_ladrf[crc >> 4] |= + htole16(1 << (crc & 0xf)); + + ETHER_NEXT_MULTI(step, enm); + } + + ifp->if_flags &= ~IFF_ALLMULTI; + return; + + allmulti: + ifp->if_flags |= IFF_ALLMULTI; + sc->sc_initblock.init_ladrf[0] = + sc->sc_initblock.init_ladrf[1] = + sc->sc_initblock.init_ladrf[2] = + sc->sc_initblock.init_ladrf[3] = 0xffff; +} + +/* + * pcn_79c970_mediainit: + * + * Initialize media for the Am79c970. + */ +void +pcn_79c970_mediainit(struct pcn_softc *sc) +{ + ifmedia_init(&sc->sc_mii.mii_media, IFM_IMASK, pcn_79c970_mediachange, + pcn_79c970_mediastatus); + + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_10_5, + PORTSEL_AUI, NULL); + if (sc->sc_variant->pcv_chipid == PARTID_Am79c970A) + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_10_5|IFM_FDX, + PORTSEL_AUI, NULL); + + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_10_T, + PORTSEL_10T, NULL); + if (sc->sc_variant->pcv_chipid == PARTID_Am79c970A) + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_10_T|IFM_FDX, + PORTSEL_10T, NULL); + + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO, + 0, NULL); + if (sc->sc_variant->pcv_chipid == PARTID_Am79c970A) + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO|IFM_FDX, + 0, NULL); + + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); +} + +/* + * pcn_79c970_mediastatus: [ifmedia interface function] + * + * Get the current interface media status (Am79c970 version). + */ +void +pcn_79c970_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct pcn_softc *sc = ifp->if_softc; + + /* + * The currently selected media is always the active media. + * Note: We have no way to determine what media the AUTO + * process picked. + */ + ifmr->ifm_active = sc->sc_mii.mii_media.ifm_media; +} + +/* + * pcn_79c970_mediachange: [ifmedia interface function] + * + * Set hardware to newly-selected media (Am79c970 version). + */ +int +pcn_79c970_mediachange(struct ifnet *ifp) +{ + struct pcn_softc *sc = ifp->if_softc; + uint32_t reg; + + if (IFM_SUBTYPE(sc->sc_mii.mii_media.ifm_media) == IFM_AUTO) { + /* + * CSR15:PORTSEL doesn't matter. Just set BCR2:ASEL. + */ + reg = pcn_bcr_read(sc, LE_BCR2); + reg |= LE_B2_ASEL; + pcn_bcr_write(sc, LE_BCR2, reg); + } else { + /* + * Clear BCR2:ASEL and set the new CSR15:PORTSEL value. + */ + reg = pcn_bcr_read(sc, LE_BCR2); + reg &= ~LE_B2_ASEL; + pcn_bcr_write(sc, LE_BCR2, reg); + + reg = pcn_csr_read(sc, LE_CSR15); + reg = (reg & ~LE_C15_PORTSEL(PORTSEL_MASK)) | + LE_C15_PORTSEL(sc->sc_mii.mii_media.ifm_cur->ifm_data); + pcn_csr_write(sc, LE_CSR15, reg); + } + + if ((sc->sc_mii.mii_media.ifm_media & IFM_FDX) != 0) { + reg = LE_B9_FDEN; + if (IFM_SUBTYPE(sc->sc_mii.mii_media.ifm_media) == IFM_10_5) + reg |= LE_B9_AUIFD; + pcn_bcr_write(sc, LE_BCR9, reg); + } else + pcn_bcr_write(sc, LE_BCR9, 0); + + return (0); +} + +/* + * pcn_79c971_mediainit: + * + * Initialize media for the Am79c971. + */ +void +pcn_79c971_mediainit(struct pcn_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + + /* We have MII. */ + sc->sc_flags |= PCN_F_HAS_MII; + + /* + * The built-in 10BASE-T interface is mapped to the MII + * on the PCNet-FAST. Unfortunately, there's no EEPROM + * word that tells us which PHY to use. + * This driver used to ignore all but the first PHY to + * answer, but this code was removed to support multiple + * external PHYs. As the default instance will be the first + * one to answer, no harm is done by letting the possibly + * non-connected internal PHY show up. + */ + + /* Initialize our media structures and probe the MII. */ + sc->sc_mii.mii_ifp = ifp; + sc->sc_mii.mii_readreg = pcn_mii_readreg; + sc->sc_mii.mii_writereg = pcn_mii_writereg; + sc->sc_mii.mii_statchg = pcn_mii_statchg; + ifmedia_init(&sc->sc_mii.mii_media, 0, pcn_79c971_mediachange, + pcn_79c971_mediastatus); + + mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY, + MII_OFFSET_ANY, 0); + if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { + ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE); + } else + ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); +} + +/* + * pcn_79c971_mediastatus: [ifmedia interface function] + * + * Get the current interface media status (Am79c971 version). + */ +void +pcn_79c971_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct pcn_softc *sc = ifp->if_softc; + + mii_pollstat(&sc->sc_mii); + ifmr->ifm_status = sc->sc_mii.mii_media_status; + ifmr->ifm_active = sc->sc_mii.mii_media_active; +} + +/* + * pcn_79c971_mediachange: [ifmedia interface function] + * + * Set hardware to newly-selected media (Am79c971 version). + */ +int +pcn_79c971_mediachange(struct ifnet *ifp) +{ + struct pcn_softc *sc = ifp->if_softc; + + if (ifp->if_flags & IFF_UP) + mii_mediachg(&sc->sc_mii); + return (0); +} + +/* + * pcn_mii_readreg: [mii interface function] + * + * Read a PHY register on the MII. + */ +int +pcn_mii_readreg(struct device *self, int phy, int reg) +{ + struct pcn_softc *sc = (void *) self; + uint32_t rv; + + pcn_bcr_write(sc, LE_BCR33, reg | (phy << PHYAD_SHIFT)); + rv = pcn_bcr_read(sc, LE_BCR34) & LE_B34_MIIMD; + if (rv == 0xffff) + return (0); + + return (rv); +} + +/* + * pcn_mii_writereg: [mii interface function] + * + * Write a PHY register on the MII. + */ +void +pcn_mii_writereg(struct device *self, int phy, int reg, int val) +{ + struct pcn_softc *sc = (void *) self; + + pcn_bcr_write(sc, LE_BCR33, reg | (phy << PHYAD_SHIFT)); + pcn_bcr_write(sc, LE_BCR34, val); +} + +/* + * pcn_mii_statchg: [mii interface function] + * + * Callback from MII layer when media changes. + */ +void +pcn_mii_statchg(struct device *self) +{ + struct pcn_softc *sc = (void *) self; + + if ((sc->sc_mii.mii_media_active & IFM_FDX) != 0) + pcn_bcr_write(sc, LE_BCR9, LE_B9_FDEN); + else + pcn_bcr_write(sc, LE_BCR9, 0); +} diff --git a/sys/dev/pci/if_pcnreg.h b/sys/dev/pci/if_pcnreg.h new file mode 100644 index 00000000000..b6a4fe80204 --- /dev/null +++ b/sys/dev/pci/if_pcnreg.h @@ -0,0 +1,78 @@ +/* $OpenBSD: if_pcnreg.h,v 1.1 2005/07/28 01:31:22 brad Exp $ */ +/* $NetBSD: if_pcnreg.h,v 1.3 2002/09/04 01:36:07 thorpej Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + */ + +#ifndef _DEV_PCI_IF_PCNREG_H_ +#define _DEV_PCI_IF_PCNREG_H_ + +/* + * Register definitions for the AMD PCnet-PCI series of Ethernet + * chips. + * + * These are only the registers that we access directly from PCI + * space. Everything else (accessed via the RAP + RDP/BDP) is + * defined in <dev/ic/lancereg.h>. + */ + +/* + * PCI configuration space. + */ + +#define PCN_PCI_CBIO (PCI_MAPREG_START + 0x00) +#define PCN_PCI_CBMEM (PCI_MAPREG_START + 0x04) + +/* + * I/O map in Word I/O mode. + */ + +#define PCN16_APROM 0x00 +#define PCN16_RDP 0x10 +#define PCN16_RAP 0x12 +#define PCN16_RESET 0x14 +#define PCN16_BDP 0x16 + +/* + * I/O map in DWord I/O mode. + */ + +#define PCN32_APROM 0x00 +#define PCN32_RDP 0x10 +#define PCN32_RAP 0x14 +#define PCN32_RESET 0x18 +#define PCN32_BDP 0x1c + +#endif /* _DEV_PCI_IF_PCNREG_H_ */ |