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