summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ic/am79900reg.h150
-rw-r--r--sys/dev/ic/lancereg.h612
-rw-r--r--sys/dev/pci/files.pci8
-rw-r--r--sys/dev/pci/if_pcn.c2128
-rw-r--r--sys/dev/pci/if_pcnreg.h78
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_ */