diff options
50 files changed, 8845 insertions, 13949 deletions
diff --git a/release/sysinstall/devices.c b/release/sysinstall/devices.c index 6bbe45b..e9f2694 100644 --- a/release/sysinstall/devices.c +++ b/release/sysinstall/devices.c @@ -91,14 +91,12 @@ static struct _devname { { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 2, 0, 64, 4, 'b' }, { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 1, 0, 8, 4, 'b' }, { DEVICE_TYPE_FLOPPY, "worm%d", "SCSI optical disk / CDR", 23, 0, 1, 4, 'b' }, - { DEVICE_TYPE_NETWORK, "al", "ADMtek AL981/AN985 PCI ethernet card" }, - { DEVICE_TYPE_NETWORK, "ax", "ASIX AX88140A PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" }, { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" }, { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" }, { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" }, + { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" }, { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" }, - { DEVICE_TYPE_NETWORK, "dm", "Davicom DM9100/DM9102 PCI fast ethernet card" }, { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" }, { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" }, { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" }, @@ -109,8 +107,6 @@ static struct _devname { { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" }, { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" }, { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" }, - { DEVICE_TYPE_NETWORK, "mx", "Macronix 98713/98715/98725 PCI ethernet card" }, - { DEVICE_TYPE_NETWORK, "pn", "Lite-On 82168/82169 PNIC PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" }, diff --git a/release/texts/alpha/HARDWARE.TXT b/release/texts/alpha/HARDWARE.TXT index aa4b8eb..c07bea0 100644 --- a/release/texts/alpha/HARDWARE.TXT +++ b/release/texts/alpha/HARDWARE.TXT @@ -85,11 +85,9 @@ sio1 2f8 3 n/a n/a Serial Port 1 (COM2) lpt0 dyn 7 n/a n/a Printer Port 0 lpt1 dyn dyn n/a n/a Printer Port 1 -al0 dyn dyn n/a dyn ADMtek AL981/AN985 PCI based cards -ax0 dyn dyn n/a dyn ASIX AX88140A PCI based cards +dc0 n/a n/a n/a n/a DEC/Intel 21143 and workalikes de0 n/a n/a n/a n/a DEC DC21x40 PCI based cards (including 21140 100bT cards) -dm0 n/a n/a n/a n/a Davicom DM9100/DM9102 PCI based cards ed0 280 10 dyn d8000 WD & SMC 80xx; Novell NE1000 & NE2000; 3Com 3C503; HP PC Lan+ eg0 310 5 dyn dyn 3Com 3C505 @@ -106,8 +104,6 @@ le0 300 5 dyn d0000 Digital Equipment EtherWorks lnc0 280 10 n/a dyn Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, some PCnet-PCI cards) -mx0 dyn dyn n/a dyn Macronix 98713/15/25 PCI based cards -pn0 dyn dyn n/a dyn Lite-On PNIC PCI based cards rl0 dyn dyn n/a dyn RealTek 8129/8139 fast ethernet sf0 dyn dyn n/a dyn Adaptec AIC-6915 fast ethernet sis0 dyn dyn n/a dyn SiS 900/SiS 7016 fast ethernet diff --git a/release/texts/alpha/RELNOTES.TXT b/release/texts/alpha/RELNOTES.TXT index 5c0f802..350db50 100644 --- a/release/texts/alpha/RELNOTES.TXT +++ b/release/texts/alpha/RELNOTES.TXT @@ -120,6 +120,10 @@ Any usage of "/dev/sd*" in ``/etc/fstab'' must be replace by "/dev/da*". In addition, any useage of "/dev/*sd*" in scripts need to be changed. Even if you have old `sd' device entries in /dev, they will no longer work. +The `al' `ax' `dm' `pn' and `mx' drivers have been removed and replaced +with a single driver (`dc') in order to reduce code duplication. The +new driver handles all chipsets supported by the older driver, and it +offers improved support for 10/100 cards based on the DEC/Intel 21143. 1.2. SECURITY FIXES ------------------- diff --git a/release/texts/i386/HARDWARE.TXT b/release/texts/i386/HARDWARE.TXT index d477082..0ca490f 100644 --- a/release/texts/i386/HARDWARE.TXT +++ b/release/texts/i386/HARDWARE.TXT @@ -87,11 +87,9 @@ sio1 2f8 3 n/a n/a Serial Port 1 (COM2) ppc0 dyn 7 n/a n/a Printer ports -al0 dyn dyn n/a dyn ADMtek AL981/AN985 PCI based cards -ax0 dyn dyn n/a dyn ASIX AX88140A PCI based cards +dc0 n/a n/a n/a n/a DEC/Intel 21143 cards and workalikes de0 n/a n/a n/a n/a DEC DC21x40 PCI based cards (including 21140 100bT cards) -dm0 n/a n/a n/a n/a Davicom DM9100/DM9102 PCI based cards ed0 280 10 dyn d8000 WD & SMC 80xx; Novell NE1000 & NE2000; 3Com 3C503; HP PC Lan+ ep0 300 10 dyn dyn 3Com 3C509 @@ -108,8 +106,6 @@ le0 300 5 dyn d0000 Digital Equipment EtherWorks lnc0 280 10 n/a dyn Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, some PCnet-PCI cards) -mx0 dyn dyn n/a dyn Macronix 98713/15/25 PCI based cards -pn0 dyn dyn n/a dyn Lite-On PNIC PCI based cards rl0 dyn dyn n/a dyn RealTek 8129/8139 fast ethernet sf0 dyn dyn n/a dyn Adaptec AIC-6915 fast ethernet sis0 dyn dyn n/a dyn SiS 900/SiS 7016 fast ethernet diff --git a/release/texts/i386/RELNOTES.TXT b/release/texts/i386/RELNOTES.TXT index 816edc7..df3871f 100644 --- a/release/texts/i386/RELNOTES.TXT +++ b/release/texts/i386/RELNOTES.TXT @@ -122,6 +122,10 @@ Any usage of "/dev/sd*" in ``/etc/fstab'' must be replace by "/dev/da*". In addition, any useage of "/dev/*sd*" in scripts need to be changed. Even if you have old `sd' device entries in /dev, they will no longer work. +The `al' `ax' `dm' `pn' and `mx' drivers have been removed and replaced +with a single driver (`dc') in order to reduce code duplication. The +new driver handles all chipsets supported by the older drivers, and it +offers improved support for 10/100 cards based on the DEC/Intel 21143. 1.2. SECURITY FIXES ------------------- diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index a3bf04d..06c0592 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,14 +1,14 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD$ -MAN4= ahc.4 al.4 alpm.4 atkbd.4 atkbdc.4 ax.4 blackhole.4 bpf.4 \ - bridge.4 ccd.4 cd.4 ch.4 da.4 ddb.4 de.4 \ - divert.4 dm.4 drum.4 dummynet.4 fd.4 fdc.4 fpa.4 fxp.4 \ +MAN4= ahc.4 alpm.4 atkbd.4 atkbdc.4 blackhole.4 bpf.4 \ + bridge.4 ccd.4 cd.4 ch.4 da.4 dc.4 ddb.4 de.4 \ + divert.4 drum.4 dummynet.4 fd.4 fdc.4 fpa.4 fxp.4 \ icmp.4 ifmib.4 iic.4 iicbb.4 iicbus.4 iicsmb.4 \ inet.4 intpm.4 intro.4 ip.4 ipfirewall.4 keyboard.4 kld.4 \ - lo.4 lp.4 lpbb.4 lpt.4 mem.4 mouse.4 mtio.4 mx.4 natm.4 ncr.4 \ + lo.4 lp.4 lpbb.4 lpt.4 mem.4 mouse.4 mtio.4 natm.4 ncr.4 \ netintro.4 null.4 pass.4 ppbus.4 ppi.4 ppp.4 pt.4 pty.4 \ - route.4 ohci.4 pcm.4 pcvt.4 pn.4 psm.4 rl.4 sa.4 screen.4 scsi.4 \ + route.4 ohci.4 pcm.4 pcvt.4 psm.4 rl.4 sa.4 screen.4 scsi.4 \ sd.4 sf.4 si.4 sio.4 sis.4 sk.4 sl.4 smb.4 smbus.4 smp.4 snp.4 \ splash.4 sppp.4 ssc.4 st.4 ste.4 su.4 syscons.4 sysmouse.4 tcp.4 \ termios.4 ti.4 tl.4 ttcp.4 tty.4 tun.4 udp.4 uhci.4 uk.4 ukbd.4 \ diff --git a/share/man/man4/al.4 b/share/man/man4/al.4 deleted file mode 100644 index f2094db..0000000 --- a/share/man/man4/al.4 +++ /dev/null @@ -1,151 +0,0 @@ -.\" Copyright (c) 1997, 1998, 1999 -.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. 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 Bill Paul. -.\" 4. Neither the name of the author nor the names of any co-contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd May 20, 1999 -.Dt AL 4 -.Os FreeBSD -.Sh NAME -.Nm al -.Nd ADMtek Inc. AL981 Comet and AN985 Centaur fast ethernet device driver -.Sh SYNOPSIS -.Cd "device al0" -.Sh DESCRIPTION -The -.Nm -driver provides support for PCI ethernet adapters and embedded -controllers based on the ADMtek Inc. AL981 Comet and AN 985 -Centaur fast ethernet controller chips. -.Pp -The ADMtek chip uses bus master DMA and is designed to be a -DEC 21x4x workalike. The only major difference between the DEC -and ADMtek parts is that the ADMtek receiver filter is programmed -using two special registers where as the DEC chip is programmed -by uploading a special setup frame via the transmit DMA engine. -The AL981 and AN985 can only be programmed with a single -perfect filter entry for the local station address and a 64-bit -multicast hash table; the DEC filter supports several other -options. The ADMtek fast ethernet controllers support both -10 and 100Mbps speeds in either full or half duplex using -an internal MII transceiver. -.Pp -The -.Nm -driver supports the following media types: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It autoselect -Enable autoselection of the media type and options. -The user can manually override -the autoselected mode by adding media options to the -.Pa /etc/rc.conf -fine. -.It 10baseT/UTP -Set 10Mbps operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex modes. -.It 100baseTX -Set 100Mbps (fast ethernet) operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex -modes. -.El -.Pp -The -.Nm -driver supports the following media options: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It full-duplex -Force full duplex operation -.It half-duplex -Force half duplex operation. -.El -.Pp -For more information on configuring this device, see -.Xr ifconfig 8 . -.Sh DIAGNOSTICS -.Bl -diag -.It "ax%d: couldn't map memory" -A fatal initialization error has occurred. -.It "ax%d: couldn't map interrupt" -A fatal initialization error has occurred. -.It "ax%d: watchdog timeout" -The device has stopped responding to the network, or there is a problem with -the network connection (cable). -.It "ax%d: no memory for rx list" -The driver failed to allocate an mbuf for the receiver ring. -.It "ax%d: no memory for tx list" -The driver failed to allocate an mbuf for the transmitter ring when -allocating a pad buffer or collapsing an mbuf chain into a cluster. -.It "ax%d: chip is in D3 power state -- setting to D0" -This message applies only to adapters which support power -management. Some operating systems place the controller in low power -mode when shutting down, and some PCI BIOSes fail to bring the chip -out of this state before configuring it. The controller loses all of -its PCI configuration in the D3 state, so if the BIOS does not set -it back to full power mode in time, it won't be able to configure it -correctly. The driver tries to detect this condition and bring -the adapter back to the D0 (full power) state, but this may not be -enough to return the driver to a fully operational condition. If -you see this message at boot time and the driver fails to attach -the device as a network interface, you will have to perform second -warm boot to have the device properly configured. -.Pp -Note that this condition only occurs when warm booting from another -operating system. If you power down your system prior to booting -.Fx , -the card should be configured correctly. -.El -.Sh SEE ALSO -.Xr arp 4 , -.Xr netintro 4 , -.Xr ifconfig 8 -.Rs -.%T ADMtek AL981 data sheet -.%O http://www.admtek.com.tw -.Re -.Sh HISTORY -The -.Nm -device driver first appeared in -.Fx 3.0 . -.Sh AUTHORS -The -.Nm -driver was written by -.An Bill Paul Aq wpaul@ctr.columbia.edu . diff --git a/share/man/man4/ax.4 b/share/man/man4/ax.4 deleted file mode 100644 index 6056f1a..0000000 --- a/share/man/man4/ax.4 +++ /dev/null @@ -1,155 +0,0 @@ -.\" Copyright (c) 1997, 1998, 1999 -.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. 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 Bill Paul. -.\" 4. Neither the name of the author nor the names of any co-contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd January 2, 1999 -.Dt AX 4 -.Os FreeBSD -.Sh NAME -.Nm ax -.Nd -ASIX Electronics AX88140A and AX88141 fast ethernet device driver -.Sh SYNOPSIS -.Cd "device ax0" -.Sh DESCRIPTION -The -.Nm -driver provides support for PCI ethernet adapters and embedded -controllers based on the ASIX AX88140A and AX88141 fast ethernet -controller chips, including the Alfa Inc. GFC2204 and the CNet Pro110B. -The AX88141 is a new version of the AX88140A with power management -and magic packet support. -.Pp -The ASIX chip uses bus master DMA and is designed to be a -DEC 21x4x workalike. The only major difference between the DEC -and ASIX parts is that the ASIX receiver filter is programmed -using two special registers where as the DEC chip is programmed -by uploading a special setup frame via the transmit DMA engine. -The ASIX receive filter can only be programmed with a single -perfect filter entry for the local station address and a 64-bit -multicast hash table; the DEC filter supports several other -options. The ASIX fast ethernet controller supports both -10 and 100Mbps speeds in either full or half duplex using -an external MII transceiver. -.Pp -The -.Nm -driver supports the following media types: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It autoselect -Enable autoselection of the media type and options. -The user can manually override -the autoselected mode by adding media options to the -.Pa /etc/rc.conf -file. -.It 10baseT/UTP -Set 10Mbps operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex -modes. -.It 100baseTX -Set 100Mbps (fast ethernet) operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex -modes. -.El -.Pp -The -.Nm -driver supports the following media options: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It full-duplex -Force full duplex operation -.It half-duplex -Force half duplex operation. -.El -.Pp -For more information on configuring this device, see -.Xr ifconfig 8 . -.Sh DIAGNOSTICS -.Bl -diag -.It "ax%d: couldn't map memory" -A fatal initialization error has occurred. -.It "ax%d: couldn't map interrupt" -A fatal initialization error has occurred. -.It "ax%d: watchdog timeout" -The device has stopped responding to the network, or there is a problem with -the network connection (cable). -.It "ax%d: no memory for rx list" -The driver failed to allocate an mbuf for the receiver ring. -.It "ax%d: no memory for tx list" -The driver failed to allocate an mbuf for the transmitter ring when -allocating a pad buffer or collapsing an mbuf chain into a cluster. -.It "ax%d: chip is in D3 power state -- setting to D0" -This message applies only to adapters which support power -management. Some operating systems place the controller in low power -mode when shutting down, and some PCI BIOSes fail to bring the chip -out of this state before configuring it. The controller loses all of -its PCI configuration in the D3 state, so if the BIOS does not set -it back to full power mode in time, it won't be able to configure it -correctly. The driver tries to detect this condition and bring -the adapter back to the D0 (full power) state, but this may not be -enough to return the driver to a fully operational condition. If -you see this message at boot time and the driver fails to attach -the device as a network interface, you will have to perform second -warm boot to have the device properly configured. -.Pp -Note that this condition only occurs when warm booting from another -operating system. If you power down your system prior to booting -.Fx , -the card should be configured correctly. -.El -.Sh SEE ALSO -.Xr arp 4 , -.Xr netintro 4 , -.Xr ifconfig 8 -.Rs -.%T ASIX AX81140A data sheet -.%O http://www.asix.com.tw -.Re -.Sh HISTORY -The -.Nm -device driver first appeared in -.Fx 3.0 . -.Sh AUTHORS -The -.Nm -driver was written by -.An Bill Paul Aq wpaul@ctr.columbia.edu . diff --git a/share/man/man4/dc.4 b/share/man/man4/dc.4 new file mode 100644 index 0000000..f0a441d --- /dev/null +++ b/share/man/man4/dc.4 @@ -0,0 +1,322 @@ +.\" Copyright (c) 1997, 1998, 1999 +.\" Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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 Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd November 20, 1999 +.Dt DC 4 i386 +.Os FreeBSD +.Sh NAME +.Nm dc +.Nd +DEC/Intel 21143 and clone 10/100 ethernet driver +.Sh SYNOPSIS +.Cd "controller miibus0" +.Cd "device dc0" +.Sh DESCRIPTION +The +.Nm +driver provides support for several PCI fast ethernet adapters and +embedded controllers based on the following chipsets: +.Pp +.Bl -bullet -compact -offset indent +.It +DEC/Intel 21143 +.It +Macronix 98713, 98713A, 98715, 98715A and 98725 +.It +Davicom DM9100 and DM9102 +.It +ASIX Electronics AX88140A and AX88141 +.It +ADMtek AL981 Comet and AN985 Centaur +.It +Lite-On 82c168 and 82c169 PNIC +.It +Lite-On/Macronix 82c115 PNIC II +.El +.Pp +All of these chips have the same general register layout, DMA +descriptor format and method of operation. All of the clone chips +are based on the 21143 design with various modifications. The +21143 itself has support for 10baseT, BNC, AUI, MII and symbol +media attachments, 10 and 100Mbps speeds in full or half duplex, +built in NWAY autonegotiation and wake on LAN. The 21143 also +offers several receive filter programming options including +perfect filtering, inverse perfect filtering and hash table +filtering. +.Pp +Some clone chips duplicate the 21143 fairly closely while others +only maintain superficial simularities. Some support only MII +media attachments. Others use different receiver filter programming +mechanisms. At least one supports only chained DMA descriptors +(most support both chained descriptors and contiguously allocated +fixed size rings). Some chips (especially the PNIC) also have +peculiar bugs. The +.Nm +driver does its best to provide generalized support for all +of these chipsets in order to keep special case code to a minimun. +.Pp +These chips are used by many vendors which makes it +difficult provide a complete list of all supported cards. The +following NICs are known to work with the +.Nm +driver at this time: +.Pp +.Bl -bullet -compact -offset indent +.It +Digital DE500-BA 10/100 (21143, non-MII) +.It +Built in DE500-BA on DEC Alpha workstations (21143, non-MII) +.It +Kingston KNE100TX (21143, MII) +.It +D-Link DFE-570TX (21143, MII, quad port) +.It +NDC SOHOware SFA110 (98513A) +.It +SVEC PN102-TX (98713) +.It +CNet Pro120A (98715A or 9713A) and CNet Pro120B (98715) +.It +Compex RL100-TX (98713 or 98713A) +.It +LinkSys LNE100TX (PNIC 82c168, 82c169) +.It +NetGear FA310-TX Rev. D1, D2 or D3 (PNIC 82c169) +.It +Matrox FastNIC 10/100 (PNIC 82c168, 82c169) +.It +Kingston KNE110TX (PNIC 82c169) +.It +LinkSys LNE100TX v2.0 (PNIC II 82c115) +.It +Jaton XpressNet (Davicom DM9102) +.It +Alfa Inc GFC2204 (ASIX AX88140A) +.It +CNet Pro110B (ASIX AX88140A) +.El +.Pp +The +.Nm +driver supports the following media types: +.Pp +.Bl -tag -width xxxxxxxxxxxxxxxxxxxx +.It autoselect +Enable autoselection of the media type and options. +The user can manually override +the autoselected mode by adding media options to the +.Pa /etc/rc.conf +file. +.Pp +Note: the built-in NWAY autonegotiation on the original PNIC 82c168 +chip is horribly broken and is not supported by the +.Nm +driver at this time: the chip will operate in any speed or duplex +mode, however these must be set manually. The original 82c168 appears +on very early revisions of the LinkSys LNE100TX and Matrox FastNIC. +.It 10baseT/UTP +Set 10Mbps operation. The +.Ar mediaopt +option can also be used to enable +.Ar full-duplex +operation. Not specifying +.Ar full duplex +implies +.Ar half-duplex +mode. +.It 100baseTX +Set 100Mbps (fast ethernet) operation. The +.Ar mediaopt +option can also be used to enable +.Ar full-duplex +operation. Not specifying +.Ar full duplex +implies +.Ar half-duplex +mode. +.El +.Pp +The +.Nm +driver supports the following media options: +.Pp +.Bl -tag -width xxxxxxxxxxxxxxxxxxxx +.It full-duplex +Force full duplex operation. The interface will operate in +half duplex mode if this media option is not specified. +.El +.Pp +Note that the 100baseTX media type may not be available on certain +Intel 21143 adapters which support 10mbps media attachments only. +For more information on configuring this device, see +.Xr ifconfig 8 . +.Sh DIAGNOSTICS +.Bl -diag +.It "dc%d: couldn't map ports/memory" +A fatal initialization error has occurred. +.It "dc%d: couldn't map interrupt" +A fatal initialization error has occurred. +.It "dc%d: watchdog timeout" +A packet was queued for transmission and a transmit command was +issued, however the device failed to acknowledge the transmission +before a timeout expired. This can happen if the device is unable +to deliver interrupts for some reason, of if there is a problem with +the network connection (cable). +.It "dc%d: no memory for rx list" +The driver failed to allocate an mbuf for the receiver ring. +.It "dc%d: TX underrun -- increasing TX threshold" +The device generated a transmit underrun error while attempting to +DMA and transmit a packet. This happens if the host is not able to +DMA the packet data into the NIC's FIFO fast enough. The driver +will dynamically increase the transmit start threshold so that +more data must be DMAed into the FIFO before the NIC will start +transmitting it onto the wire. +.It "dc%d: TX underrun -- using store and forward mode" +The device continued to generate transmit underruns even after all +possible transmit start threshold settings had been tried, so the +driver programmed the chip for store and forward mode. In this mode, +the NIC will not begin transmission until the entire packet has been +transfered into its FIFO memory. +.It "dc%d: chip is in D3 power state -- setting to D0" +This message applies only to adapters which support power +management. Some operating systems place the controller in low power +mode when shutting down, and some PCI BIOSes fail to bring the chip +out of this state before configuring it. The controller loses all of +its PCI configuration in the D3 state, so if the BIOS does not set +it back to full power mode in time, it won't be able to configure it +correctly. The driver tries to detect this condition and bring +the adapter back to the D0 (full power) state, but this may not be +enough to return the driver to a fully operational condition. If +you see this message at boot time and the driver fails to attach +the device as a network interface, you will have to perform second +warm boot to have the device properly configured. +.Pp +Note that this condition only occurs when warm booting from another +operating system. If you power down your system prior to booting +.Fx , +the card should be configured correctly. +.El +.Sh SEE ALSO +.Xr arp 4 , +.Xr netintro 4 , +.Xr ifconfig 8 +.Rs +.%T ADMtek AL981, AL983 and AL985 data sheets +.%O http://www.admtek.com.tw +.Re +.Rs +.%T ASIX Electronics AX88140A and AX88141 data sheets +.%O http://www.asix.com.tw +.Re +.Rs +.%T Davicom DM9102 data sheet +.%O http://www.davicom8.com +.Re +.Rs +.%T Intel 21143 Hardware Reference Manual +.%O http://developer.intel.com +.Re +.Rs +.%T Macronix 98713/A, 98715/A and 98725 data sheets +.%O http://www.macronix.com +.Re +.Rs +.%T Macronix 98713/A and 98715/A app notes +.%O http://www.macronix.com +.Re +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +The +.Nm +driver was written by +.An Bill Paul Aq wpaul@ee.columbia.edu . +.Sh BUGS +The Macronix application notes claim that in order to put the +chips in normal operation, the driver must write a certian magic +number into the CSR16 register. The numbers are documented in +the app notes, but the exact meaning of the bits is not. +.Pp +The 98713A seems to have a problem with 10Mbps full duplex mode. +The transmitter works but the receiver tends to produce many +unexplained errors leading to very poor overall performance. The +98715A does not exhibit this problem. All other modes on the +98713A seem to work correctly. +.Pp +The original 82c168 PNIC chip has built in NWAY support which is +used on certain early LinkSys LNE100TX and Matrox FastNIC cards, +however it is horribly broken and difficult to use reliably. +Consequently, autonegotiation is not currently supported for this +chipset: the driver defaults the NIC to 10baseT half duplex, and it's +up to the operator to manually select a different mode if necessary. +(Later cards use an external MII transceiver to implement NWAY +autonegotiation and work correctly.) +.Pp +The +.Nm +driver programs 82c168 and 82c169 PNIC chips to use the store and +forward setting for the transmit start threshold by default. This +is to work around problems with some NIC/PCI bus combinations where +the PNIC can transmit corrupt frames when operating at 100Mbps, +probably due to PCI DMA burst transfer errors. +.Pp +The 82c168 abd 82c169 PNIC chips also have a receiver bug that +sometimes manifests during periods of heavy receive and transmit +activity, where the chip will improperly DMA received frames to +the host. The chips appear to upload several kilobytes of garbage +data along with the received frame data, dirtying several RX buffers +instead of just the expected one. The +.Nm +driver detects this condition and will salvage the frame, however +it incurs a serious performance penalty in the process. +.Pp +The PNIC chips also sometimes generate a transmit underrun error when +the driver attempts to download the receiver filter setup frame, which +can result in the receive filter being incorrectly programmed. The +.Nm +driver will watch for this condition and requeue the setup frame until +it is transfered successfully. +.Pp +The ADMtek AL981 chip (and possibly the AN985 as well) has been observed +to sometimes wedge on transmit: this appears to happen when the driver +queues a sequence of frames which cause it to wrap from the end of the +the transmit descriptor ring back to the beginning. The +.Nm +driver attempts to avoid this condition by not queing any frames past +the end of the transmit ring during a single invocation of the +.Fn dc_start +routine. This workaround has a negligible impact on transmit performance. + + diff --git a/share/man/man4/dm.4 b/share/man/man4/dm.4 deleted file mode 100644 index b095c80..0000000 --- a/share/man/man4/dm.4 +++ /dev/null @@ -1,150 +0,0 @@ -.\" Copyright (c) 1997, 1998, 1999 -.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. 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 Bill Paul. -.\" 4. Neither the name of the author nor the names of any co-contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd September 4, 1999 -.Dt DM 4 i386 -.Os FreeBSD -.Sh NAME -.Nm dm -.Nd -Davicom DM9100/DM9102 fast ethernet device driver -.Sh SYNOPSIS -.Cd "controller miibus0" -.Cd "device dm0" -.Sh DESCRIPTION -The -.Nm -driver provides support for PCI ethernet adapters and embedded -controllers based on the Davicom DM9100 and DM9102 PCI -fast ethernet controller chips including the Jaton Corporation -XPressNet. -.Pp -The DM9100 and DM9102 are designed to be DEC 21x4x workalikes. The -register layout, DMA descriptor scheme and receive filter programming -are identical to the DEC part. The DM9102 -is a 100Mbps ethernet MAC and MII-compliant transceiver -in a single package. The DM9100 is similar to the DM9102 except -that it has no internal PHY, requiring instead an external transceiver -to be attached to its MII interface. -.Pp -The -.Nm -driver supports the following media types: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It autoselect -Enable autoselection of the media type and options. -The user can manually override -the autoselected mode by adding media options to the -.Pa /etc/rc.conf -fine. -.It 10baseT/UTP -Set 10Mbps operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex modes. -.It 100baseTX -Set 100Mbps (fast ethernet) operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex -modes. -.El -.Pp -The -.Nm -driver supports the following media options: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It full-duplex -Force full duplex operation -.It half-duplex -Force half duplex operation. -.El -.Pp -For more information on configuring this device, see -.Xr ifconfig 8 . -.Sh DIAGNOSTICS -.Bl -diag -.It "dm%d: couldn't map ports/memory" -A fatal initialization error has occurred. -.It "dm%d: couldn't map interrupt" -A fatal initialization error has occurred. -.It "dm%d: watchdog timeout" -The device has stopped responding to the network, or there is a problem with -the network connection (cable). -.It "dm%d: no memory for rx list" -The driver failed to allocate an mbuf for the receiver ring. -.It "dm%d: no memory for tx list" -The driver failed to allocate an mbuf for the transmitter ring when -allocating a pad buffer or collapsing an mbuf chain into a cludmr. -.It "dm%d: chip is in D3 power state -- setting to D0" -This message applies only to adapters which support power -management. Some operating sydmms place the controller in low power -mode when shutting down, and some PCI BIOSes fail to bring the chip -out of this state before configuring it. The controller loses all of -its PCI configuration in the D3 state, so if the BIOS does not set -it back to full power mode in time, it won't be able to configure it -correctly. The driver tries to detect this condition and bring -the adapter back to the D0 (full power) state, but this may not be -enough to return the driver to a fully operational condition. If -you see this message at boot time and the driver fails to attach -the device as a network interface, you will have to perform second -warm boot to have the device properly configured. -.Pp -Note that this condition only occurs when warm booting from another -operating sydmm. If you power down your sydmm prior to booting -.Fx , -the card should be configured correctly. -.El -.Sh SEE ALSO -.Xr arp 4 , -.Xr netintro 4 , -.Xr ifconfig 8 -.Rs -.%T Davicom DM9102 datasheet -.%O http://www.davicom8.com -.Re -.Sh HISTORY -The -.Nm -device driver first appeared in -.Fx 3.0 . -.Sh AUTHORS -The -.Nm -driver was written by -.An Bill Paul Aq wpaul@ee.columbia.edu . diff --git a/share/man/man4/mx.4 b/share/man/man4/mx.4 deleted file mode 100644 index e9e3b2c..0000000 --- a/share/man/man4/mx.4 +++ /dev/null @@ -1,173 +0,0 @@ -.\" Copyright (c) 1997, 1998 -.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. 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 Bill Paul. -.\" 4. Neither the name of the author nor the names of any co-contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd November 5, 1998 -.Dt MX 4 -.Os FreeBSD -.Sh NAME -.Nm mx -.Nd -Macronix 98713/98715/98725 fast ethernet device driver -.Sh SYNOPSIS -.Cd "controller miibus0" -.Cd "device mx0" -.Sh DESCRIPTION -The -.Nm -driver provides support for PCI ethernet adapters and embedded -controllers based on the Macronix 98713, 98713A, 98715, 98715A and -98725 fast ethernet controller chips. This includes the NDC -Communications SOHOware SFA110, the SVEC PN102-TX -fast ethernet card, and various other adapters. The -.Nm -driver also supports the Lite-On 82c115 PNIC II chip, which is -actually similar in design to the Macronix 98715A with the addition -of wake on LAN support. Supported PNIC II cards include the -LinkSys LNE100TX Version 2. -.Pp -The Macronix chips use bus master DMA and are designed to be -DEC 'tulip' workalikes. The original 98713 had an MII bus for -controlling an external PHY, however the 98713A and up use an -internal transceiver with NWAY support. The Macronix parts are -advertised as being register compatible with the DEC 21x4x -controllers. All of the Macronix controllers support both -10 and 100Mbps speeds in either full or half duplex. -.Pp -The -.Nm -driver supports the following media types: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It autoselect -Enable autoselection of the media type and options. -The user can manually override -the autoselected mode by adding media options to the -.Pa /etc/rc.conf -file. -.It 10baseT/UTP -Set 10Mbps operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex modes. -.It 100baseTX -Set 100Mbps (fast ethernet) operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex -modes. -.El -.Pp -The -.Nm -driver supports the following media options: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It full-duplex -Force full duplex operation -.It half-duplex -Force half duplex operation. -.El -.Pp -Note that the 100baseTX media type is only available if supported -by the adapter. -For more information on configuring this device, see -.Xr ifconfig 8 . -.Sh DIAGNOSTICS -.Bl -diag -.It "mx%d: couldn't map memory" -A fatal initialization error has occurred. -.It "mx%d: couldn't map interrupt" -A fatal initialization error has occurred. -.It "mx%d: watchdog timeout" -The device has stopped responding to the network, or there is a problem with -the network connection (cable). -.It "mx%d: no memory for rx list" -The driver failed to allocate an mbuf for the receiver ring. -.It "mx%d: no memory for tx list" -The driver failed to allocate an mbuf for the transmitter ring when -allocating a pad buffer or collapsing an mbuf chain into a cluster. -.It "mx%d: chip is in D3 power state -- setting to D0" -This message applies only to adapters which support power -management. Some operating systems place the controller in low power -mode when shutting down, and some PCI BIOSes fail to bring the chip -out of this state before configuring it. The controller loses all of -its PCI configuration in the D3 state, so if the BIOS does not set -it back to full power mode in time, it won't be able to configure it -correctly. The driver tries to detect this condition and bring -the adapter back to the D0 (full power) state, but this may not be -enough to return the driver to a fully operational condition. If -you see this message at boot time and the driver fails to attach -the device as a network interface, you will have to perform second -warm boot to have the device properly configured. -.Pp -Note that this condition only occurs when warm booting from another -operating system. If you power down your system prior to booting -.Fx , -the card should be configured correctly. -.El -.Sh SEE ALSO -.Xr arp 4 , -.Xr netintro 4 , -.Xr ifconfig 8 -.Rs -.%T Macronix 98713/A, 98715/A and 98725 data sheets -.%O http://www.macronix.com -.Re -.Rs -.%T Macronix 98713/A and 98715/A app notes -.%O http://www.macronix.com -.Re -.Sh HISTORY -The -.Nm -device driver first appeared in -.Fx 3.0 . -.Sh AUTHORS -The -.Nm -driver was written by -.An Bill Paul Aq wpaul@ctr.columbia.edu . -.Sh BUGS -The Macronix application notes claim that in order to put the -chips in normal operation, the driver must write a certian magic -number into the CSR16 register. The numbers are documented in -the app notes, but the exact meaning of the bits is not. -.Pp -The 98713A seems to have a problem with 10Mbps full duplex mode. -The transmitter works but the receiver tends to produce many -unexplained errors leading to very poor overall performance. The -98715A does not exhibit this problem. All other modes on the -98713A seem to work correctly. diff --git a/share/man/man4/pn.4 b/share/man/man4/pn.4 deleted file mode 100644 index d48e661..0000000 --- a/share/man/man4/pn.4 +++ /dev/null @@ -1,160 +0,0 @@ -.\" Copyright (c) 1997, 1998 -.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. 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 Bill Paul. -.\" 4. Neither the name of the author nor the names of any co-contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD -.\" 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. -.\" -.\" $FreeBSD$ -.\" -.Dd November 7, 1998 -.Dt PN 4 -.Os FreeBSD -.Sh NAME -.Nm pn -.Nd -Lite-On 82c168/82c169 PNIC fast ethernet device driver -.Sh SYNOPSIS -.Cd "device pn0" -.Sh DESCRIPTION -The -.Nm -driver provides support for PCI ethernet adapters and embedded -controllers based on the Lite-On 82c168 and 82c169 fast ethernet -controller chips. This includes the LinkSys LNE100TX, the -Bay Networks Netgear FA310TX revision D1, the Matrox Networks -FastNIC 10/100, the Kingston KNE110TX (EtherRx VP), -and various other commodity fast ethernet cards. -.Pp -The Lite-On chips use bus master DMA and are designed to be -DEC 'tulip' workalikes. Many vendors that formerly based their -designs around the DEC 21x4x devices are now using the PNIC -instead. The chips support both an internal transceiver -and external transceivers via an MII bus. The Lite-On parts are -advertised as being register compatible with the DEC 21x4x -controllers, however there are some differences in the way the -EEPROM and MII access is done. The PNIC controllers support both -10 and 100Mbps speeds in either full or half duplex. -.Pp -The -.Nm -driver supports the following media types: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It autoselect -Enable autoselection of the media type and options. -The user can manually override -the autoselected mode by adding media options to the -.Pa /etc/rc.conf -fine. -.It 10baseT/UTP -Set 10Mbps operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex modes. -.It 100baseTX -Set 100Mbps (fast ethernet) operation. The -.Ar mediaopt -option can also be used to select either -.Ar full-duplex -or -.Ar half-duplex -modes. -.El -.Pp -The -.Nm -driver supports the following media options: -.Pp -.Bl -tag -width xxxxxxxxxxxxxxxxxxxx -.It full-duplex -Force full duplex operation -.It half-duplex -Force half duplex operation. -.El -.Pp -Note that the 100baseTX media type is only available if supported -by the adapter. -For more information on configuring this device, see -.Xr ifconfig 8 . -.Sh DIAGNOSTICS -.Bl -diag -.It "pn%d: couldn't map memory" -A fatal initialization error has occurred. -.It "pn%d: couldn't map interrupt" -A fatal initialization error has occurred. -.It "pn%d: watchdog timeout" -The device has stopped responding to the network, or there is a problem with -the network connection (cable). -.It "pn%d: no memory for rx list" -The driver failed to allocate an mbuf for the receiver ring. -.It "pn%d: no memory for tx list" -The driver failed to allocate an mbuf for the transmitter ring when -allocating a pad buffer or collapsing an mbuf chain into a cluster. -.It "pn%d: chip is in D3 power state -- setting to D0" -This message applies only to adapters which support power -management. Some operating systems place the controller in low power -mode when shutting down, and some PCI BIOSes fail to bring the chip -out of this state before configuring it. The controller loses all of -its PCI configuration in the D3 state, so if the BIOS does not set -it back to full power mode in time, it won't be able to configure it -correctly. The driver tries to detect this condition and bring -the adapter back to the D0 (full power) state, but this may not be -enough to return the driver to a fully operational condition. If -you see this message at boot time and the driver fails to attach -the device as a network interface, you will have to perform second -warm boot to have the device properly configured. -.Pp -Note that this condition only occurs when warm booting from another -operating system. If you power down your system prior to booting -.Fx , -the card should be configured correctly. -.El -.Sh SEE ALSO -.Xr arp 4 , -.Xr netintro 4 , -.Xr ifconfig 8 -.Sh HISTORY -The -.Nm -device driver first appeared in -.Fx 3.0 . -.Sh AUTHORS -The -.Nm -driver was written by -.An Bill Paul Aq wpaul@ctr.columbia.edu . -.Sh BUGS -The internal NWAY support on the 82c168 chip is horribly broken, which -means that autoselection will not work, except maybe for half-duplex -10Mbps links. In order to use other modes (e.g. 100Mbps) it will be -necessary to -use -.Xr ifconfig 8 -to set the interface manually. Autoselection for 82c169 boards using -MII transceivers should work correctly. diff --git a/sys/alpha/conf/GENERIC b/sys/alpha/conf/GENERIC index 785ff00..fc9c4be 100644 --- a/sys/alpha/conf/GENERIC +++ b/sys/alpha/conf/GENERIC @@ -114,17 +114,13 @@ device sio0 at isa0 port IO_COM1 irq 4 device sio1 at isa0 port IO_COM2 irq 3 flags 0x50 # PCI Ethernet NICs. -device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) device le0 # Lance -device pn0 # Lite-On 82c168/82c169 (``PNIC'') # PCI Ethernet NICs that use the common MII bus controller code. controller miibus0 # MII bus support -device al0 # ADMtek AL981/AN985 (``Comet''/``Centaur'') -device dm0 # Davicom DM9100/DM9102 -device mx0 # Macronix 98713/98715/98725 (``PMAC'') +device dc0 # DEC/Intel 21143 and workalikes device rl0 # RealTek 8129/8139 device sf0 # Adaptec AIC-6915 (``Starfire'') device sis0 # Silicon Integrated Systems SiS 900/SiS 7016 diff --git a/sys/alpha/conf/NOTES b/sys/alpha/conf/NOTES index 785ff00..fc9c4be 100644 --- a/sys/alpha/conf/NOTES +++ b/sys/alpha/conf/NOTES @@ -114,17 +114,13 @@ device sio0 at isa0 port IO_COM1 irq 4 device sio1 at isa0 port IO_COM2 irq 3 flags 0x50 # PCI Ethernet NICs. -device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) device le0 # Lance -device pn0 # Lite-On 82c168/82c169 (``PNIC'') # PCI Ethernet NICs that use the common MII bus controller code. controller miibus0 # MII bus support -device al0 # ADMtek AL981/AN985 (``Comet''/``Centaur'') -device dm0 # Davicom DM9100/DM9102 -device mx0 # Macronix 98713/98715/98725 (``PMAC'') +device dc0 # DEC/Intel 21143 and workalikes device rl0 # RealTek 8129/8139 device sf0 # Adaptec AIC-6915 (``Starfire'') device sis0 # Silicon Integrated Systems SiS 900/SiS 7016 diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC index 9f6702e..dcdee22 100644 --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -175,18 +175,14 @@ device ppi0 # Parallel port interface device # PCI Ethernet NICs. -device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) -device pn0 # Lite-On 82c168/82c169 (``PNIC'') device tx0 # SMC 9432TX (83c170 ``EPIC'') device vx0 # 3Com 3c590, 3c595 (``Vortex'') # PCI Ethernet NICs that use the common MII bus controller code. controller miibus0 # MII bus support -device al0 # ADMtek AL981/AN985 (``Comet''/``Centaur'') -device dm0 # Davicom DM9100/DM9102 -device mx0 # Macronix 98713/98715/98725 (``PMAC'') +device dc0 # DEC/Intel 21143 and various workalikes device rl0 # RealTek 8129/8139 device sf0 # Adaptec AIC-6915 (``Starfire'') device sis0 # Silicon Integrated Systems SiS 900/SiS 7016 diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 70df1ae..c52b4c1 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1646,31 +1646,20 @@ controller miibus0 # nd 1040B PCI SCSI host adapters, as well as the Qlogic ISP 2100 # FC/AL Host Adapter. # -# The `al' device provides support for PCI fast ethernet adapters -# based on the ADMtek Inc. AL981 "Comet" and the AN985 "Centaur" chips. -# -# The `ax' device provides support for PCI fast ethernet adapters -# based on the ASIX Electronics AX88140A chip, including the Alfa -# Inc. GFC2204. +# The `dc' device provides support for PCI fast ethernet adapters +# based on the DEC/Intel 21143 and various workalikes including: +# the ADMtek AL981 Comet and AN985 Centaur, the ASIX Electronics +# AX88140A and AX88141, the Davicom DM9100 and DM9102, the Lite-On +# 82c168 and 82c169 PNIC, the Lite-On/Macronix LC82C115 PNIC II +# and the Macronix 98713/98713A/98715/98715A/98725 PMAC. This driver +# replaces the old al, ax, dm, pn and mx drivers. # # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # -# The `dm' device provides support for PCI fast ethernet adapters -# based on the the Davicom DM9100 and DM9102 controller chips, including -# the Jaton Corporation XPressNet. -# # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # -# The `mx' device provides support for various fast ethernet adapters -# based on the Macronix 98713, 987615 and 98725 series chips. -# -# The `pn' device provides support for various fast ethernet adapters -# based on the Lite-On 82c168 and 82c169 PNIC chips, including the -# LinkSys LNE100TX, the NetGear FA310TX rev. D1 and the Matrox -# FastNIC 10/100. -# # The 'rl' device provides support for PCI fast ethernet adapters based # on the RealTek 8129/8139 chipset. Note that the RealTek driver defaults # to using programmed I/O to do register accesses because memory mapped @@ -1852,13 +1841,9 @@ options SCSI_ISP_WWN="0x5000000099990000" #options ISP_COMPILE_2100_FW=1 #options ISP_COMPILE_2200_FW=1 -device al0 -device ax0 +device dc0 device de0 -device dm0 device fxp0 -device mx0 -device pn0 device rl0 device sf0 device sis0 diff --git a/sys/conf/files b/sys/conf/files index ef04f36..08ee5cd 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -715,18 +715,14 @@ dev/bktr/bktr_os.c optional bktr pci pci/pccbb.c optional pccbb cardbus pci/cy_pci.c optional cy pci pci/ida_pci.c optional ida pci -pci/if_al.c optional al pci/if_ar_p.c optional ar pci -pci/if_ax.c optional ax +pci/if_dc.c optional dc pci/if_de.c optional de -pci/if_dm.c optional dm pci/if_en_pci.c optional en pci pci/if_fpa.c optional fpa pci pci/if_fxp.c optional fxp pci/if_lnc_p.c optional lnc pci pci/if_mn.c optional mn -pci/if_mx.c optional mx -pci/if_pn.c optional pn pci/if_rl.c optional rl pci/if_sf.c optional sf pci/if_sis.c optional sis diff --git a/sys/dev/mii/mxphy.c b/sys/dev/dc/dcphy.c index fbd5573..2543e87 100644 --- a/sys/dev/mii/mxphy.c +++ b/sys/dev/dc/dcphy.c @@ -33,13 +33,12 @@ */ /* - * Pseudo-driver for internal NWAY support on Macronix 98713/98715/98725 - * PMAC controller chips. The Macronix chips use the same internal - * NWAY register layout as the DEC/Intel 21143. Technically we're - * abusing the miibus code to handle the media selection and NWAY - * support here since there is no MII interface. However the logical - * operations are roughly the same, and the alternative is to create - * a fake MII interface in the driver, which is harder to do. + * Pseudo-driver for internal NWAY support on DEC 21143 and workalike + * controllers. Technically we're abusing the miibus code to handle + * media selection and NWAY support here since there is no MII + * interface. However the logical operations are roughly the same, + * and the alternative is to create a fake MII interface in the driver, + * which is harder to do. */ #include <sys/param.h> @@ -66,7 +65,9 @@ #include <machine/resource.h> #include <sys/bus.h> -#include <pci/if_mxreg.h> +#include <pci/pcivar.h> + +#include <pci/if_dcreg.h> #include "miibus_if.h" @@ -75,45 +76,45 @@ static const char rcsid[] = "$FreeBSD$"; #endif -#define MX_SETBIT(sc, reg, x) \ +#define DC_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | x) -#define MX_CLRBIT(sc, reg, x) \ +#define DC_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) & ~x) #define MIIF_AUTOTIMEOUT 0x0004 -static int mxphy_probe __P((device_t)); -static int mxphy_attach __P((device_t)); -static int mxphy_detach __P((device_t)); +static int dcphy_probe __P((device_t)); +static int dcphy_attach __P((device_t)); +static int dcphy_detach __P((device_t)); -static device_method_t mxphy_methods[] = { +static device_method_t dcphy_methods[] = { /* device interface */ - DEVMETHOD(device_probe, mxphy_probe), - DEVMETHOD(device_attach, mxphy_attach), - DEVMETHOD(device_detach, mxphy_detach), + DEVMETHOD(device_probe, dcphy_probe), + DEVMETHOD(device_attach, dcphy_attach), + DEVMETHOD(device_detach, dcphy_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), { 0, 0 } }; -static devclass_t mxphy_devclass; +static devclass_t dcphy_devclass; -static driver_t mxphy_driver = { - "mxphy", - mxphy_methods, +static driver_t dcphy_driver = { + "dcphy", + dcphy_methods, sizeof(struct mii_softc) }; -DRIVER_MODULE(mxphy, miibus, mxphy_driver, mxphy_devclass, 0, 0); +DRIVER_MODULE(dcphy, miibus, dcphy_driver, dcphy_devclass, 0, 0); -int mxphy_service __P((struct mii_softc *, struct mii_data *, int)); -void mxphy_status __P((struct mii_softc *)); -static int mxphy_auto __P((struct mii_softc *, int)); -static void mxphy_reset __P((struct mii_softc *)); +int dcphy_service __P((struct mii_softc *, struct mii_data *, int)); +void dcphy_status __P((struct mii_softc *)); +static int dcphy_auto __P((struct mii_softc *, int)); +static void dcphy_reset __P((struct mii_softc *)); -static int mxphy_probe(dev) +static int dcphy_probe(dev) device_t dev; { struct mii_attach_args *ma; @@ -121,25 +122,25 @@ static int mxphy_probe(dev) ma = device_get_ivars(dev); /* - * The mx driver will report a Macronix vendor and device + * The dc driver will report the 21143 vendor and device * ID to let us know that it wants us to attach. */ - if (ma->mii_id1 != MX_VENDORID || - ma->mii_id2 != MX_DEVICEID_987x5) + if (ma->mii_id1 != DC_VENDORID_DEC || + ma->mii_id2 != DC_DEVICEID_21143) return(ENXIO); - device_set_desc(dev, "Macronix NWAY media interface"); + device_set_desc(dev, "Intel 21143 NWAY media interface"); return (0); } -static int mxphy_attach(dev) +static int dcphy_attach(dev) device_t dev; { struct mii_softc *sc; struct mii_attach_args *ma; struct mii_data *mii; - struct mx_softc *mx_sc; + struct dc_softc *dc_sc; sc = device_get_softc(dev); ma = device_get_ivars(dev); @@ -149,7 +150,7 @@ static int mxphy_attach(dev) sc->mii_inst = mii->mii_instance; sc->mii_phy = ma->mii_phyno; - sc->mii_service = mxphy_service; + sc->mii_service = dcphy_service; sc->mii_pdata = mii; sc->mii_flags |= MIIF_NOISOLATE; @@ -163,13 +164,24 @@ static int mxphy_attach(dev) ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), BMCR_LOOP|BMCR_S100); - /*mxphy_reset(sc);*/ - mx_sc = mii->mii_ifp->if_softc; - CSR_WRITE_4(mx_sc, MX_10BTSTAT, 0); - CSR_WRITE_4(mx_sc, MX_10BTCTRL, 0); + /*dcphy_reset(sc);*/ + dc_sc = mii->mii_ifp->if_softc; + CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); + CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); + + switch(pci_read_config(device_get_parent(sc->mii_dev), + DC_PCI_CSID, 4)) { + case 0x99999999: + /* Example of how to only allow 10Mbps modes. */ + sc->mii_capabilities = BMSR_10TFDX|BMSR_10THDX; + break; + default: + sc->mii_capabilities = + BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| + BMSR_10TFDX|BMSR_10THDX; + break; + } - sc->mii_capabilities = - BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX|BMSR_10TFDX|BMSR_10THDX; sc->mii_capabilities &= ma->mii_capmask; device_printf(dev, " "); if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) @@ -183,7 +195,7 @@ static int mxphy_attach(dev) return(0); } -static int mxphy_detach(dev) +static int dcphy_detach(dev) device_t dev; { struct mii_softc *sc; @@ -198,17 +210,17 @@ static int mxphy_detach(dev) } int -mxphy_service(sc, mii, cmd) +dcphy_service(sc, mii, cmd) struct mii_softc *sc; struct mii_data *mii; int cmd; { - struct mx_softc *mx_sc; + struct dc_softc *dc_sc; struct ifmedia_entry *ife = mii->mii_media.ifm_cur; int reg; u_int32_t mode; - mx_sc = mii->mii_ifp->if_softc; + dc_sc = mii->mii_ifp->if_softc; switch (cmd) { case MII_POLLSTAT: @@ -237,14 +249,14 @@ mxphy_service(sc, mii, cmd) sc->mii_flags = 0; mii->mii_media_active = IFM_NONE; - mode = CSR_READ_4(mx_sc, MX_NETCFG); - mode &= ~(MX_NETCFG_FULLDUPLEX|MX_NETCFG_PORTSEL| - MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER|MX_NETCFG_SPEEDSEL); + mode = CSR_READ_4(dc_sc, DC_NETCFG); + mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL| + DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL); switch (IFM_SUBTYPE(ife->ifm_media)) { case IFM_AUTO: - mxphy_reset(sc); - (void) mxphy_auto(sc, 0); + /*dcphy_reset(sc);*/ + (void) dcphy_auto(sc, 0); break; case IFM_100_T4: /* @@ -252,26 +264,32 @@ mxphy_service(sc, mii, cmd) */ return (EINVAL); case IFM_100_TX: - mxphy_reset(sc); - MX_CLRBIT(mx_sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); - mode |= MX_NETCFG_PORTSEL|MX_NETCFG_PCS| - MX_NETCFG_SCRAMBLER; + dcphy_reset(sc); + DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS| + DC_NETCFG_SCRAMBLER; if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) - mode |= MX_NETCFG_FULLDUPLEX; + mode |= DC_NETCFG_FULLDUPLEX; else - mode &= ~MX_NETCFG_FULLDUPLEX; - CSR_WRITE_4(mx_sc, MX_NETCFG, mode); + mode &= ~DC_NETCFG_FULLDUPLEX; + CSR_WRITE_4(dc_sc, DC_NETCFG, mode); break; case IFM_10_T: - mxphy_reset(sc); - MX_CLRBIT(mx_sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); - mode &= ~MX_NETCFG_PORTSEL; - mode |= MX_NETCFG_SPEEDSEL; + DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); + else + DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); + DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + mode &= ~DC_NETCFG_PORTSEL; + mode |= DC_NETCFG_SPEEDSEL; if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) - mode |= MX_NETCFG_FULLDUPLEX; + mode |= DC_NETCFG_FULLDUPLEX; else - mode &= ~MX_NETCFG_FULLDUPLEX; - CSR_WRITE_4(mx_sc, MX_NETCFG, mode); + mode &= ~DC_NETCFG_FULLDUPLEX; + CSR_WRITE_4(dc_sc, DC_NETCFG, mode); break; default: return(EINVAL); @@ -315,25 +333,31 @@ mxphy_service(sc, mii, cmd) * need to restart the autonegotiation process. Read * the BMSR twice in case it's latched. */ - reg = CSR_READ_4(mx_sc, MX_10BTSTAT) & - (MX_TSTAT_LS10|MX_TSTAT_LS100); + reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & + (DC_TSTAT_LS10|DC_TSTAT_LS100); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX && - !(reg & MX_TSTAT_LS100)) { + !(reg & DC_TSTAT_LS100)) { if (sc->mii_flags & MIIF_AUTOTIMEOUT) { sc->mii_flags &= ~MIIF_AUTOTIMEOUT; break; } else return(0); } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T && - !(reg & MX_TSTAT_LS10)) { + !(reg & DC_TSTAT_LS10)) { if (sc->mii_flags & MIIF_AUTOTIMEOUT) { sc->mii_flags &= ~MIIF_AUTOTIMEOUT; break; } else return(0); } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE && - (!(reg & MX_TSTAT_LS10) || !(reg & MX_TSTAT_LS100))) { + (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))) { + if (sc->mii_flags & MIIF_AUTOTIMEOUT) { + sc->mii_flags &= ~MIIF_AUTOTIMEOUT; + break; + } else + return(0); + } else if (CSR_READ_4(dc_sc, DC_ISR) & DC_ISR_LINKGOOD) { if (sc->mii_flags & MIIF_AUTOTIMEOUT) { sc->mii_flags &= ~MIIF_AUTOTIMEOUT; break; @@ -342,14 +366,14 @@ mxphy_service(sc, mii, cmd) } sc->mii_ticks = 0; - mxphy_reset(sc); - mxphy_auto(sc, 0); + /*dcphy_reset(sc);*/ + dcphy_auto(sc, 0); break; } /* Update the media status. */ - mxphy_status(sc); + dcphy_status(sc); /* Callback if something changed. */ if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { @@ -360,22 +384,22 @@ mxphy_service(sc, mii, cmd) } void -mxphy_status(sc) +dcphy_status(sc) struct mii_softc *sc; { struct mii_data *mii = sc->mii_pdata; int reg, anlpar; - struct mx_softc *mx_sc; + struct dc_softc *dc_sc; - mx_sc = mii->mii_ifp->if_softc; + dc_sc = mii->mii_ifp->if_softc; mii->mii_media_status = IFM_AVALID; mii->mii_media_active = IFM_ETHER; - reg = CSR_READ_4(mx_sc, MX_10BTSTAT) & - (MX_TSTAT_LS10|MX_TSTAT_LS100); + reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & + (DC_TSTAT_LS10|DC_TSTAT_LS100); - if (!(reg & MX_TSTAT_LS10) || !(reg & MX_TSTAT_LS100)) + if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) mii->mii_media_status |= IFM_ACTIVE; if (sc->mii_flags & MIIF_DOINGAUTO) { @@ -383,17 +407,17 @@ mxphy_status(sc) return; } - if (CSR_READ_4(mx_sc, MX_10BTCTRL) & MX_TCTL_AUTONEGENBL && - CSR_READ_4(mx_sc, MX_10BTSTAT) & MX_TSTAT_ANEGSTAT) { + if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL && + CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) { /* Erg, still trying, I guess... */ - if ((CSR_READ_4(mx_sc, MX_10BTSTAT) & - MX_ASTAT_AUTONEGCMP) != MX_ASTAT_AUTONEGCMP) { + if ((CSR_READ_4(dc_sc, DC_10BTSTAT) & + DC_ASTAT_AUTONEGCMP) != DC_ASTAT_AUTONEGCMP) { mii->mii_media_active |= IFM_NONE; return; } - if (CSR_READ_4(mx_sc, MX_10BTSTAT) & MX_TSTAT_LP_CAN_NWAY) { - anlpar = CSR_READ_4(mx_sc, MX_10BTSTAT) >> 16; + if (CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_LP_CAN_NWAY) { + anlpar = CSR_READ_4(dc_sc, DC_10BTSTAT) >> 16; if (anlpar & ANLPAR_T4) mii->mii_media_active |= IFM_100_T4; else if (anlpar & ANLPAR_TX_FD) @@ -406,59 +430,65 @@ mxphy_status(sc) mii->mii_media_active |= IFM_10_T; else mii->mii_media_active |= IFM_NONE; + if (DC_IS_INTEL(dc_sc)) + DC_CLRBIT(dc_sc, DC_10BTCTRL, + DC_TCTL_AUTONEGENBL); return; } /* - * If the other side doesn't support NWAY, then the + * If the other side doesn't support NWAY, then the * best we can do is determine if we have a 10Mbps or - * 100Mbps link. There's no way to know if the link + * 100Mbps link. There's no way to know if the link * is full or half duplex, so we default to half duplex * and hope that the user is clever enough to manually * change the media settings if we're wrong. */ - if (!(reg & MX_TSTAT_LS100)) + if (!(reg & DC_TSTAT_LS100)) mii->mii_media_active |= IFM_100_TX; - else if (!(reg & MX_TSTAT_LS10)) + else if (!(reg & DC_TSTAT_LS10)) mii->mii_media_active |= IFM_10_T; else mii->mii_media_active |= IFM_NONE; + if (DC_IS_INTEL(dc_sc)) + DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); return; } - if (CSR_READ_4(mx_sc, MX_NETCFG) & MX_NETCFG_SCRAMBLER) + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SCRAMBLER) mii->mii_media_active |= IFM_100_TX; else mii->mii_media_active |= IFM_10_T; - if (CSR_READ_4(mx_sc, MX_NETCFG) & MX_NETCFG_FULLDUPLEX) + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) mii->mii_media_active |= IFM_FDX; - return; } static int -mxphy_auto(mii, waitfor) +dcphy_auto(mii, waitfor) struct mii_softc *mii; int waitfor; { int i; - struct mx_softc *sc; + struct dc_softc *sc; sc = mii->mii_pdata->mii_ifp->if_softc; if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { - CSR_WRITE_4(sc, MX_10BTCTRL, 0x3FFFF); - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); - MX_SETBIT(sc, MX_10BTCTRL, MX_TCTL_AUTONEGENBL); - MX_SETBIT(sc, MX_10BTCTRL, MX_ASTAT_TXDISABLE); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); + CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); } if (waitfor) { /* Wait 500ms for it to complete. */ for (i = 0; i < 500; i++) { - if ((CSR_READ_4(sc, MX_10BTSTAT) & MX_TSTAT_ANEGSTAT) - == MX_ASTAT_AUTONEGCMP) + if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) + == DC_ASTAT_AUTONEGCMP) return(0); DELAY(1000); } @@ -482,16 +512,16 @@ mxphy_auto(mii, waitfor) } static void -mxphy_reset(mii) +dcphy_reset(mii) struct mii_softc *mii; { - struct mx_softc *sc; + struct dc_softc *sc; sc = mii->mii_pdata->mii_ifp->if_softc; - MX_SETBIT(sc, MX_SIARESET, MX_SIA_RESET_NWAY); + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); DELAY(1000); - MX_CLRBIT(sc, MX_SIARESET, MX_SIA_RESET_NWAY); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); return; } diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c new file mode 100644 index 0000000..25491e0 --- /dev/null +++ b/sys/dev/dc/if_dc.c @@ -0,0 +1,2689 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143 + * series chips and several workalikes including the following: + * + * Macronix 98713/98715/98725 PMAC (www.macronix.com) + * Macronix/Lite-On 82c115 PNIC II (www.macronix.com) + * Lite-On 82c168/82c169 PNIC (www.litecom.com) + * ASIX Electronics AX88140A (www.asix.com.tw) + * ASIX Electronics AX88141 (www.asix.com.tw) + * ADMtek AL981 (www.admtek.com.tw) + * ADMtek AN985 (www.admtek.com.tw) + * Davicom DM9100, DM9102 (www.davicom8.com) + * + * Datasheets for the 21143 are available at developer.intel.com. + * Datasheets for the clone parts can be found at their respective sites. + * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.) + * The PNIC II is essentially a Macronix 98715A chip; the only difference + * worth noting is that its multicast hash table is only 128 bits wide + * instead of 512. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The Intel 21143 is the successor to the DEC 21140. It is basically + * the same as the 21140 but with a few new features. The 21143 supports + * three kinds of media attachments: + * + * o MII port, for 10Mbps and 100Mbps support and NWAY + * autonegotiation provided by an external PHY. + * o SYM port, for symbol mode 100Mbps support. + * o 10baseT port. + * o AUI/BNC port. + * + * The 100Mbps SYM port and 10baseT port can be used together in + * combination with the internal NWAY support to create a 10/100 + * autosensing configuration. + * + * Knowing which media is available on a given card is tough: you're + * supposed to go slogging through the EEPROM looking for media + * description structures. Unfortunately, some card vendors that use + * the 21143 don't obey the DEC SROM spec correctly, which means that + * what you find in the EEPROM may not agree with reality. Fortunately, + * the 21143 provides us a way to get around this issue: lurking in + * PCI configuration space is the Configuration Wake-Up Command Register. + * This register is loaded with a value from the EEPROM when wake on LAN + * mode is enabled; this value tells us quite clearly what kind of media + * is attached to the NIC. The main purpose of this register is to tell + * the NIC what media to scan when in wake on LAN mode, however by + * forcibly enabling wake on LAN mode, we can use to learn what kind of + * media a given NIC has available and adapt ourselves accordingly. + * + * Of course, if the media description blocks in the EEPROM are bogus. + * what are the odds that the CWUC aren't bogus as well, right? Well, + * the CWUC value is more likely to be correct since wake on LAN mode + * won't work correctly without it, and wake on LAN is a big selling + * point these days. It's also harder to screw up a single byte than + * a whole media descriptor block. + * + * Note that not all tulip workalikes are handled in this driver: we only + * deal with those which are relatively well behaved. The Winbond is + * handled separately due to its different register offsets and the + * special handling needed for its various bugs. The PNIC is handled + * here, but I'm not thrilled about it. + * + * All of the workalike chips use some form of MII transceiver support + * with the exception of the Macronix chips, which also have a SYM port. + * The ASIX AX88140A is also documented to have a SYM port, but all + * the cards I've seen use an MII transceiver, probably because the + * AX88140A doesn't support internal NWAY. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/clock.h> /* for DELAY */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define DC_USEIOSPACE + +#include <pci/if_dcreg.h> + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct dc_type dc_devs[] = { + { DC_VENDORID_DEC, DC_DEVICEID_21143, + "Intel 21143 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100, + "Davicom DM9100 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, + "Davicom DM9102 10/100BaseTX" }, + { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981, + "ADMtek AL981 10/100BaseTX" }, + { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985, + "ADMtek AN985 10/100BaseTX" }, + { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, + "ASIX AX88140A 10/100BaseTX" }, + { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, + "ASIX AX88141 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98713, + "Macronix 98713 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98713, + "Macronix 98713A 10/100BaseTX" }, + { DC_VENDORID_CP, DC_DEVICEID_98713_CP, + "Compex RL100-TX 10/100BaseTX" }, + { DC_VENDORID_CP, DC_DEVICEID_98713_CP, + "Compex RL100-TX 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98715/98715A 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98725 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C115, + "LC82C115 PNIC II 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C168, + "82c168 PNIC 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C168, + "82c169 PNIC 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int dc_probe __P((device_t)); +static int dc_attach __P((device_t)); +static int dc_detach __P((device_t)); +static void dc_acpi __P((device_t)); +static struct dc_type *dc_devtype __P((device_t)); +static int dc_newbuf __P((struct dc_softc *, int, struct mbuf *)); +static int dc_encap __P((struct dc_softc *, struct mbuf *, + u_int32_t *)); +static void dc_pnic_rx_bug_war __P((struct dc_softc *, int)); +static void dc_rxeof __P((struct dc_softc *)); +static void dc_txeof __P((struct dc_softc *)); +static void dc_tick __P((void *)); +static void dc_intr __P((void *)); +static void dc_start __P((struct ifnet *)); +static int dc_ioctl __P((struct ifnet *, u_long, caddr_t)); +static void dc_init __P((void *)); +static void dc_stop __P((struct dc_softc *)); +static void dc_watchdog __P((struct ifnet *)); +static void dc_shutdown __P((device_t)); +static int dc_ifmedia_upd __P((struct ifnet *)); +static void dc_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); + +static void dc_delay __P((struct dc_softc *)); +static void dc_eeprom_idle __P((struct dc_softc *)); +static void dc_eeprom_putbyte __P((struct dc_softc *, int)); +static void dc_eeprom_getword __P((struct dc_softc *, int, u_int16_t *)); +static void dc_eeprom_getword_pnic + __P((struct dc_softc *, int, u_int16_t *)); +static void dc_read_eeprom __P((struct dc_softc *, caddr_t, int, + int, int)); + +static void dc_mii_writebit __P((struct dc_softc *, int)); +static int dc_mii_readbit __P((struct dc_softc *)); +static void dc_mii_sync __P((struct dc_softc *)); +static void dc_mii_send __P((struct dc_softc *, u_int32_t, int)); +static int dc_mii_readreg __P((struct dc_softc *, struct dc_mii_frame *)); +static int dc_mii_writereg __P((struct dc_softc *, struct dc_mii_frame *)); +static int dc_miibus_readreg __P((device_t, int, int)); +static int dc_miibus_writereg __P((device_t, int, int, int)); +static void dc_miibus_statchg __P((device_t)); + +static void dc_setcfg __P((struct dc_softc *, int)); +static u_int32_t dc_crc_le __P((struct dc_softc *, caddr_t)); +static u_int32_t dc_crc_be __P((caddr_t)); +static void dc_setfilt_21143 __P((struct dc_softc *)); +static void dc_setfilt_asix __P((struct dc_softc *)); +static void dc_setfilt_admtek __P((struct dc_softc *)); + +static void dc_setfilt __P((struct dc_softc *)); + +static void dc_reset __P((struct dc_softc *)); +static int dc_list_rx_init __P((struct dc_softc *)); +static int dc_list_tx_init __P((struct dc_softc *)); + +#ifdef DC_USEIOSPACE +#define DC_RES SYS_RES_IOPORT +#define DC_RID DC_PCI_CFBIO +#else +#define DC_RES SYS_RES_MEMORY +#define DC_RID DC_PCI_CFBMA +#endif + +static device_method_t dc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dc_probe), + DEVMETHOD(device_attach, dc_attach), + DEVMETHOD(device_detach, dc_detach), + DEVMETHOD(device_shutdown, dc_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, dc_miibus_readreg), + DEVMETHOD(miibus_writereg, dc_miibus_writereg), + DEVMETHOD(miibus_statchg, dc_miibus_statchg), + + { 0, 0 } +}; + +static driver_t dc_driver = { + "dc", + dc_methods, + sizeof(struct dc_softc) +}; + +static devclass_t dc_devclass; + +DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0); +DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0); + +#define DC_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) + +#define DC_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) + +#define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x)) +#define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x)) + +static void dc_delay(sc) + struct dc_softc *sc; +{ + int idx; + + for (idx = (300 / 33) + 1; idx > 0; idx--) + CSR_READ_4(sc, DC_BUSCTL); +} + +static void dc_eeprom_idle(sc) + struct dc_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + + for (i = 0; i < 25; i++) { + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + } + + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + CSR_WRITE_4(sc, DC_SIO, 0x00000000); + + return; +} + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void dc_eeprom_putbyte(sc, addr) + struct dc_softc *sc; + int addr; +{ + register int d, i; + + /* + * The AN985 has a 93C66 EEPROM on it instead of + * a 93C46. It uses a different bit sequence for + * specifying the "read" opcode. + */ + if (DC_IS_CENTAUR(sc)) + d = addr | (DC_EECMD_READ << 2); + else + d = addr | DC_EECMD_READ; + + /* + * Feed in each bit and strobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + SIO_SET(DC_SIO_EE_DATAIN); + } else { + SIO_CLR(DC_SIO_EE_DATAIN); + } + dc_delay(sc); + SIO_SET(DC_SIO_EE_CLK); + dc_delay(sc); + SIO_CLR(DC_SIO_EE_CLK); + dc_delay(sc); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + * The PNIC 82c168/82c169 has its own non-standard way to read + * the EEPROM. + */ +static void dc_eeprom_getword_pnic(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int32_t r; + + CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ|addr); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(1); + r = CSR_READ_4(sc, DC_SIO); + if (!(r & DC_PN_SIOCTL_BUSY)) { + *dest = (u_int16_t)(r & 0xFFFF); + return; + } + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void dc_eeprom_getword(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* Force EEPROM to idle state. */ + dc_eeprom_idle(sc); + + /* Enter EEPROM access mode. */ + CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + + /* + * Send address of word we want to read. + */ + dc_eeprom_putbyte(sc, addr); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + SIO_SET(DC_SIO_EE_CLK); + dc_delay(sc); + if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT) + word |= i; + dc_delay(sc); + SIO_CLR(DC_SIO_EE_CLK); + dc_delay(sc); + } + + /* Turn off EEPROM access mode. */ + dc_eeprom_idle(sc); + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void dc_read_eeprom(sc, dest, off, cnt, swap) + struct dc_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + if (DC_IS_PNIC(sc)) + dc_eeprom_getword_pnic(sc, off + i, &word); + else + dc_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + +/* + * The following two routines are taken from the Macronix 98713 + * Application Notes pp.19-21. + */ +/* + * Write a bit to the MII bus. + */ +static void dc_mii_writebit(sc, bit) + struct dc_softc *sc; + int bit; +{ + if (bit) + CSR_WRITE_4(sc, DC_SIO, + DC_SIO_ROMCTL_WRITE|DC_SIO_MII_DATAOUT); + else + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); + + DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); + DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); + + return; +} + +/* + * Read a bit from the MII bus. + */ +static int dc_mii_readbit(sc) + struct dc_softc *sc; +{ + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ|DC_SIO_MII_DIR); + CSR_READ_4(sc, DC_SIO); + DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); + DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); + if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN) + return(1); + + return(0); +} + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void dc_mii_sync(sc) + struct dc_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); + + for (i = 0; i < 32; i++) + dc_mii_writebit(sc, 1); + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void dc_mii_send(sc, bits, cnt) + struct dc_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) + dc_mii_writebit(sc, bits & i); +} + +/* + * Read an PHY register through the MII. + */ +static int dc_mii_readreg(sc, frame) + struct dc_softc *sc; + struct dc_mii_frame *frame; + +{ + int i, ack, s; + + s = splimp(); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = DC_MII_STARTDELIM; + frame->mii_opcode = DC_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Sync the PHYs. + */ + dc_mii_sync(sc); + + /* + * Send command/address info. + */ + dc_mii_send(sc, frame->mii_stdelim, 2); + dc_mii_send(sc, frame->mii_opcode, 2); + dc_mii_send(sc, frame->mii_phyaddr, 5); + dc_mii_send(sc, frame->mii_regaddr, 5); + +#ifdef notdef + /* Idle bit */ + dc_mii_writebit(sc, 1); + dc_mii_writebit(sc, 0); +#endif + + /* Check for ack */ + ack = dc_mii_readbit(sc); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + dc_mii_readbit(sc); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + if (!ack) { + if (dc_mii_readbit(sc)) + frame->mii_data |= i; + } + } + +fail: + + dc_mii_writebit(sc, 0); + dc_mii_writebit(sc, 0); + + splx(s); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int dc_mii_writereg(sc, frame) + struct dc_softc *sc; + struct dc_mii_frame *frame; + +{ + int s; + + s = splimp(); + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = DC_MII_STARTDELIM; + frame->mii_opcode = DC_MII_WRITEOP; + frame->mii_turnaround = DC_MII_TURNAROUND; + + /* + * Sync the PHYs. + */ + dc_mii_sync(sc); + + dc_mii_send(sc, frame->mii_stdelim, 2); + dc_mii_send(sc, frame->mii_opcode, 2); + dc_mii_send(sc, frame->mii_phyaddr, 5); + dc_mii_send(sc, frame->mii_regaddr, 5); + dc_mii_send(sc, frame->mii_turnaround, 2); + dc_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + dc_mii_writebit(sc, 0); + dc_mii_writebit(sc, 0); + + splx(s); + + return(0); +} + +static int dc_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct dc_mii_frame frame; + struct dc_softc *sc; + int i, rval, phy_reg; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + /* + * Note: both the AL981 and AN985 have internal PHYs, + * however the AL981 provides direct access to the PHY + * registers while the AN985 uses a serial MII interface. + * The AN985's MII interface is also buggy in that you + * can read from any MII address (0 to 31), but only address 1 + * behaves normally. To deal with both cases, we pretend + * that the PHY is at MII address 1. + */ + if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) + return(0); + + if (sc->dc_pmode == DC_PMODE_SYM) { + if (phy == (MII_NPHY - 1)) { + switch(reg) { + case MII_BMSR: + /* + * Fake something to make the probe + * code think there's a PHY here. + */ + return(BMSR_MEDIAMASK); + break; + case MII_PHYIDR1: + if (DC_IS_PNIC(sc)) + return(DC_VENDORID_LO); + return(DC_VENDORID_DEC); + break; + case MII_PHYIDR2: + if (DC_IS_PNIC(sc)) + return(DC_DEVICEID_82C168); + return(DC_DEVICEID_21143); + break; + default: + return(0); + break; + } + } else + return(0); + } + + if (DC_IS_PNIC(sc)) { + CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ | + (phy << 23) | (reg << 18)); + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(1); + rval = CSR_READ_4(sc, DC_PN_MII); + if (!(rval & DC_PN_MII_BUSY)) { + rval &= 0xFFFF; + return(rval == 0xFFFF ? 0 : rval); + } + } + return(0); + } + + if (DC_IS_COMET(sc)) { + switch(reg) { + case MII_BMCR: + phy_reg = DC_AL_BMCR; + break; + case MII_BMSR: + phy_reg = DC_AL_BMSR; + break; + case MII_PHYIDR1: + phy_reg = DC_AL_VENID; + break; + case MII_PHYIDR2: + phy_reg = DC_AL_DEVID; + break; + case MII_ANAR: + phy_reg = DC_AL_ANAR; + break; + case MII_ANLPAR: + phy_reg = DC_AL_LPAR; + break; + case MII_ANER: + phy_reg = DC_AL_ANER; + break; + default: + printf("dc%d: phy_read: bad phy register %x\n", + sc->dc_unit, reg); + return(0); + break; + } + + rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; + + if (rval == 0xFFFF) + return(0); + return(rval); + } + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + dc_mii_readreg(sc, &frame); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + + return(frame.mii_data); +} + +static int dc_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct dc_softc *sc; + struct dc_mii_frame frame; + int i, phy_reg; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) + return(0); + + if (DC_IS_PNIC(sc)) { + CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE | + (phy << 23) | (reg << 10) | data); + for (i = 0; i < DC_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY)) + break; + } + return(0); + } + + if (DC_IS_COMET(sc)) { + switch(reg) { + case MII_BMCR: + phy_reg = DC_AL_BMCR; + break; + case MII_BMSR: + phy_reg = DC_AL_BMSR; + break; + case MII_PHYIDR1: + phy_reg = DC_AL_VENID; + break; + case MII_PHYIDR2: + phy_reg = DC_AL_DEVID; + break; + case MII_ANAR: + phy_reg = DC_AL_ANAR; + break; + case MII_ANLPAR: + phy_reg = DC_AL_LPAR; + break; + case MII_ANER: + phy_reg = DC_AL_ANER; + break; + default: + printf("dc%d: phy_write: bad phy register %x\n", + sc->dc_unit, reg); + return(0); + break; + } + + CSR_WRITE_4(sc, phy_reg, data); + return(0); + } + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + dc_mii_writereg(sc, &frame); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + + return(0); +} + +static void dc_miibus_statchg(dev) + device_t dev; +{ + struct dc_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + if (DC_IS_ADMTEK(sc)) + return; + mii = device_get_softc(sc->dc_miibus); + dc_setcfg(sc, mii->mii_media_active); + sc->dc_if_media = mii->mii_media_active; + + return; +} + +#define DC_POLY 0xEDB88320 +#define DC_BITS 9 +#define DC_BITS_PNIC_II 7 + +static u_int32_t dc_crc_le(sc, addr) + struct dc_softc *sc; + caddr_t addr; +{ + u_int32_t idx, bit, data, crc; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); + } + + /* The hash table on the PNIC II is only 128 bits wide. */ + if (DC_IS_PNICII(sc)) + return (crc & ((1 << DC_BITS_PNIC_II) - 1)); + + return (crc & ((1 << DC_BITS) - 1)); +} + +/* + * Calculate CRC of a multicast group address, return the lower 6 bits. + */ +static u_int32_t dc_crc_be(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return((crc >> 26) & 0x0000003F); +} + +/* + * 21143-style RX filter setup routine. Filter programming is done by + * downloading a special setup frame into the TX engine. 21143, Macronix, + * PNIC, PNIC II and Davicom chips are programmed this way. + * + * We always program the chip using 'hash perfect' mode, i.e. one perfect + * address (our node address) and a 512-bit hash filter for multicast + * frames. We also sneak the broadcast address into the hash filter since + * we need that too. + */ +void dc_setfilt_21143(sc) + struct dc_softc *sc; +{ + struct dc_desc *sframe; + u_int32_t h, *sp; + struct ifmultiaddr *ifma; + struct ifnet *ifp; + int i; + + ifp = &sc->arpcom.ac_if; + + i = sc->dc_cdata.dc_tx_prod; + DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); + sc->dc_cdata.dc_tx_cnt++; + sframe = &sc->dc_ldata->dc_tx_list[i]; + sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf; + bzero((char *)sp, DC_SFRAME_LEN); + + sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf); + sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | + DC_FILTER_HASHPERF | DC_TXCTL_FINT; + + sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_le(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + sp[h >> 4] |= 1 << (h & 0xF); + } + + if (ifp->if_flags & IFF_BROADCAST) { + h = dc_crc_le(sc, (caddr_t)ðerbroadcastaddr); + sp[h >> 4] |= 1 << (h & 0xF); + } + + /* Set our MAC address */ + sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; + sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; + sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; + + sframe->dc_status = DC_TXSTAT_OWN; + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * The PNIC takes an exceedingly long time to process its + * setup frame; wait 10ms after posting the setup frame + * before proceeding, just so it has time to swallow its + * medicine. + */ + DELAY(10000); + + ifp->if_timer = 5; + + return; +} + +void dc_setfilt_admtek(sc) + struct dc_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + + /* Init our MAC address */ + CSR_WRITE_4(sc, DC_AL_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + CSR_WRITE_4(sc, DC_AL_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, DC_AL_MAR0, 0); + CSR_WRITE_4(sc, DC_AL_MAR1, 0); + + /* + * If we're already in promisc or allmulti mode, we + * don't have to bother programming the multicast filter. + */ + if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) + return; + + /* now program new ones */ + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]); + CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]); + + return; +} + +void dc_setfilt_asix(sc) + struct dc_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + + /* Init our MAC address */ + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, + *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, + *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + /* + * The ASIX chip has a special bit to enable reception + * of broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) + DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); + else + DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); + + /* + * If we're already in promisc or allmulti mode, we + * don't have to bother programming the multicast filter. + */ + if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) + return; + + /* now program new ones */ + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]); + + return; +} + +static void dc_setfilt(sc) + struct dc_softc *sc; +{ + if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) || + DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc)) + dc_setfilt_21143(sc); + + if (DC_IS_ASIX(sc)) + dc_setfilt_asix(sc); + + if (DC_IS_ADMTEK(sc)) + dc_setfilt_admtek(sc); + + return; +} + +/* + * In order to fiddle with the + * 'full-duplex' and '100Mbps' bits in the netconfig register, we + * first have to put the transmit and/or receive logic in the idle state. + */ +static void dc_setcfg(sc, media) + struct dc_softc *sc; + int media; +{ + int i, restart = 0; + u_int32_t isr; + + if (IFM_SUBTYPE(media) == IFM_NONE) + return; + + if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)) { + restart = 1; + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(10); + isr = CSR_READ_4(sc, DC_ISR); + if (isr & DC_ISR_TX_IDLE || + (isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED) + break; + } + + if (i == DC_TIMEOUT) + printf("dc%d: failed to force tx and " + "rx to idle state\n", sc->dc_unit); + + } + + if (IFM_SUBTYPE(media) == IFM_100_TX) { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); + if (sc->dc_pmode == DC_PMODE_MII) { + DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER)); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_SCRAMBLER)); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + } else { + if (DC_IS_PNIC(sc)) { + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL); + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); + DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); + } + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL| + DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER); + } + } + + if (IFM_SUBTYPE(media) == IFM_10_T) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); + if (sc->dc_pmode == DC_PMODE_MII) { + DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER)); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + } else { + if (DC_IS_PNIC(sc)) { + DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL); + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); + DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); + } + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + } + } + + if ((media & IFM_GMASK) == IFM_FDX) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) + DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) + DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); + } + + if (restart) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON|DC_NETCFG_RX_ON); + + return; +} + +static void dc_reset(sc) + struct dc_softc *sc; +{ + register int i; + + DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET)) + break; + } + + if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc)) { + DELAY(10000); + DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); + i = 0; + } + + if (i == DC_TIMEOUT) + printf("dc%d: reset never completed!\n", sc->dc_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000); + CSR_WRITE_4(sc, DC_NETCFG, 0x00000000); + + return; +} + +static struct dc_type *dc_devtype(dev) + device_t dev; +{ + struct dc_type *t; + u_int32_t rev; + + t = dc_devs; + + while(t->dc_name != NULL) { + if ((pci_get_vendor(dev) == t->dc_vid) && + (pci_get_device(dev) == t->dc_did)) { + /* Check the PCI revision */ + rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; + if (t->dc_did == DC_DEVICEID_98713 && + rev >= DC_REVISION_98713A) + t++; + if (t->dc_did == DC_DEVICEID_98713_CP && + rev >= DC_REVISION_98713A) + t++; + if (t->dc_did == DC_DEVICEID_987x5 && + rev >= DC_REVISION_98725) + t++; + if (t->dc_did == DC_DEVICEID_AX88140A && + rev >= DC_REVISION_88141) + t++; + if (t->dc_did == DC_DEVICEID_82C168 && + rev >= DC_REVISION_82C169) + t++; + return(t); + } + t++; + } + + return(NULL); +} + +/* + * Probe for a 21143 or clone chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + * We do a little bit of extra work to identify the exact type of + * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, + * but different revision IDs. The same is true for 98715/98715A + * chips and the 98725, as well as the ASIX and ADMtek chips. In some + * cases, the exact chip revision affects driver behavior. + */ +static int dc_probe(dev) + device_t dev; +{ + struct dc_type *t; + + t = dc_devtype(dev); + + if (t != NULL) { + device_set_desc(dev, t->dc_name); + return(0); + } + + return(ENXIO); +} + +static void dc_acpi(dev) + device_t dev; +{ + u_int32_t r, cptr; + int unit; + + unit = device_get_unit(dev); + + /* Find the location of the capabilities block */ + cptr = pci_read_config(dev, DC_PCI_CCAP, 4) & 0xFF; + + r = pci_read_config(dev, cptr, 4) & 0xFF; + if (r == 0x01) { + + r = pci_read_config(dev, cptr + 4, 4); + if (r & DC_PSTATE_D3) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, DC_PCI_CFBIO, 4); + membase = pci_read_config(dev, DC_PCI_CFBMA, 4); + irq = pci_read_config(dev, DC_PCI_CFIT, 4); + + /* Reset the power state. */ + printf("dc%d: chip is in D%d power mode " + "-- setting to D0\n", unit, r & DC_PSTATE_D3); + r &= 0xFFFFFFFC; + pci_write_config(dev, cptr + 4, r, 4); + + /* Restore PCI config data. */ + pci_write_config(dev, DC_PCI_CFBIO, iobase, 4); + pci_write_config(dev, DC_PCI_CFBMA, membase, 4); + pci_write_config(dev, DC_PCI_CFIT, irq, 4); + } + } + return; +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int dc_attach(dev) + device_t dev; +{ + int s; + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct dc_softc *sc; + struct ifnet *ifp; + u_int32_t revision; + int unit, error = 0, rid, mac_offset; + + s = splimp(); + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct dc_softc)); + + /* + * Handle power management nonsense. + */ + dc_acpi(dev); + + /* + * Map control/status registers. + */ + command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); + command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); + command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); + +#ifdef DC_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("dc%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("dc%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = DC_RID; + sc->dc_res = bus_alloc_resource(dev, DC_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->dc_res == NULL) { + printf("dc%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->dc_btag = rman_get_bustag(sc->dc_res); + sc->dc_bhandle = rman_get_bushandle(sc->dc_res); + + /* Allocate interrupt */ + rid = 0; + sc->dc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->dc_irq == NULL) { + printf("dc%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET, + dc_intr, sc, &sc->dc_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + printf("dc%d: couldn't set up irq\n", unit); + goto fail; + } + + /* Need this info to decide on a chip type. */ + sc->dc_info = dc_devtype(dev); + revision = pci_read_config(dev, DC_PCI_CFRV, 4) & 0x000000FF; + + switch(sc->dc_info->dc_did) { + case DC_DEVICEID_21143: + sc->dc_type = DC_TYPE_21143; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL; + break; + case DC_DEVICEID_DM9100: + case DC_DEVICEID_DM9102: + sc->dc_type = DC_TYPE_DM9102; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_AL981: + sc->dc_type = DC_TYPE_AL981; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_TX_ADMTEK_WAR; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_AN985: + sc->dc_type = DC_TYPE_AN985; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_TX_ADMTEK_WAR; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_98713: + case DC_DEVICEID_98713_CP: + if (revision < DC_REVISION_98713A) { + sc->dc_type = DC_TYPE_98713; + sc->dc_flags |= DC_REDUCED_MII_POLL; + } + if (revision >= DC_REVISION_98713A) + sc->dc_type = DC_TYPE_98713A; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_987x5: + sc->dc_type = DC_TYPE_987x5; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_82C115: + sc->dc_type = DC_TYPE_PNICII; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_82C168: + sc->dc_type = DC_TYPE_PNIC; + sc->dc_flags |= DC_TX_STORENFWD|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_PNIC_RX_BUG_WAR; + sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT); + if (revision < DC_REVISION_82C169) + sc->dc_pmode = DC_PMODE_SYM; + break; + case DC_DEVICEID_AX88140A: + sc->dc_type = DC_TYPE_ASIX; + sc->dc_flags |= DC_TX_USE_TX_INTR|DC_TX_INTR_FIRSTFRAG; + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_pmode = DC_PMODE_MII; + break; + default: + printf("dc%d: unknown device: %x\n", sc->dc_unit, + sc->dc_info->dc_did); + break; + } + + /* Save the cache line size. */ + sc->dc_cachesize = pci_read_config(dev, DC_PCI_CFLT, 4) & 0xFF; + + /* Reset the adapter. */ + dc_reset(sc); + + /* Take 21143 out of snooze mode */ + if (DC_IS_INTEL(sc)) { + command = pci_read_config(dev, DC_PCI_CFDD, 4); + command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE); + pci_write_config(dev, DC_PCI_CFDD, command, 4); + } + + /* + * Try to learn something about the supported media. + * We know that ASIX and ADMtek and Davicom devices + * will *always* be using MII media, so that's a no-brainer. + * The tricky ones are the Macronix/PNIC II and the + * Intel 21143. + */ + if (DC_IS_INTEL(sc)) { + u_int32_t media, cwuc; + cwuc = pci_read_config(dev, DC_PCI_CWUC, 4); + cwuc |= DC_CWUC_FORCE_WUL; + pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); + DELAY(10000); + media = pci_read_config(dev, DC_PCI_CWUC, 4); + cwuc &= ~DC_CWUC_FORCE_WUL; + pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); + DELAY(10000); + if (media & DC_CWUC_MII_ABILITY) + sc->dc_pmode = DC_PMODE_MII; + if (media & DC_CWUC_SYM_ABILITY) + sc->dc_pmode = DC_PMODE_SYM; + /* + * If none of the bits are set, then this NIC + * isn't meant to support 'wake up LAN' mode. + * This is usually only the case on multiport + * cards, and these cards almost always have + * MII transceivers. + */ + if (media == 0) + sc->dc_pmode = DC_PMODE_MII; + } else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + if (sc->dc_type == DC_TYPE_98713) + sc->dc_pmode = DC_PMODE_MII; + else + sc->dc_pmode = DC_PMODE_SYM; + } else if (!sc->dc_pmode) + sc->dc_pmode = DC_PMODE_MII; + + /* + * Get station address from the EEPROM. + */ + switch(sc->dc_type) { + case DC_TYPE_98713: + case DC_TYPE_98713A: + case DC_TYPE_987x5: + case DC_TYPE_PNICII: + dc_read_eeprom(sc, (caddr_t)&mac_offset, + (DC_EE_NODEADDR_OFFSET / 2), 1, 0); + dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0); + break; + case DC_TYPE_PNIC: + dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); + break; + case DC_TYPE_DM9102: + case DC_TYPE_21143: + case DC_TYPE_ASIX: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); + break; + case DC_TYPE_AL981: + case DC_TYPE_AN985: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0); + break; + default: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); + break; + } + + /* + * A 21143 or clone chip was detected. Inform the world. + */ + printf("dc%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->dc_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->dc_ldata = contigmalloc(sizeof(struct dc_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->dc_ldata == NULL) { + printf("dc%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + bzero(sc->dc_ldata, sizeof(struct dc_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "dc"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = dc_ioctl; + ifp->if_output = ether_output; + ifp->if_start = dc_start; + ifp->if_watchdog = dc_watchdog; + ifp->if_init = dc_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + error = mii_phy_probe(dev, &sc->dc_miibus, + dc_ifmedia_upd, dc_ifmedia_sts); + + if (error && DC_IS_INTEL(sc)) { + sc->dc_pmode = DC_PMODE_SYM; + mii_phy_probe(dev, &sc->dc_miibus, + dc_ifmedia_upd, dc_ifmedia_sts); + error = 0; + } + + if (error) { + printf("dc%d: MII without any PHY!\n", sc->dc_unit); + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + /* + * Call MI attach routines. + */ + if_attach(ifp); + ether_ifattach(ifp); + callout_handle_init(&sc->dc_stat_ch); + + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); + +fail: + splx(s); + + return(error); +} + +static int dc_detach(dev) + device_t dev; +{ + struct dc_softc *sc; + struct ifnet *ifp; + int s; + + s = splimp(); + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + dc_stop(sc); + if_detach(ifp); + + bus_generic_detach(dev); + device_delete_child(dev, sc->dc_miibus); + + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + + contigfree(sc->dc_ldata, sizeof(struct dc_list_data), M_DEVBUF); + if (sc->dc_pnic_rx_buf != NULL) + free(sc->dc_pnic_rx_buf, M_DEVBUF); + + splx(s); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int dc_list_tx_init(sc) + struct dc_softc *sc; +{ + struct dc_chain_data *cd; + struct dc_list_data *ld; + int i; + + cd = &sc->dc_cdata; + ld = sc->dc_ldata; + for (i = 0; i < DC_TX_LIST_CNT; i++) { + if (i == (DC_TX_LIST_CNT - 1)) { + ld->dc_tx_list[i].dc_next = + vtophys(&ld->dc_tx_list[0]); + } else { + ld->dc_tx_list[i].dc_next = + vtophys(&ld->dc_tx_list[i + 1]); + } + cd->dc_tx_chain[i] = NULL; + ld->dc_tx_list[i].dc_data = 0; + ld->dc_tx_list[i].dc_ctl = 0; + } + + cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0; + + return(0); +} + + +/* + * Initialize the RX descriptors and allocate mbufs for them. Note that + * we arrange the descriptors in a closed ring, so that the last descriptor + * points back to the first. + */ +static int dc_list_rx_init(sc) + struct dc_softc *sc; +{ + struct dc_chain_data *cd; + struct dc_list_data *ld; + int i; + + cd = &sc->dc_cdata; + ld = sc->dc_ldata; + + for (i = 0; i < DC_RX_LIST_CNT; i++) { + if (dc_newbuf(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + if (i == (DC_RX_LIST_CNT - 1)) { + ld->dc_rx_list[i].dc_next = + vtophys(&ld->dc_rx_list[0]); + } else { + ld->dc_rx_list[i].dc_next = + vtophys(&ld->dc_rx_list[i + 1]); + } + } + + cd->dc_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int dc_newbuf(sc, i, m) + struct dc_softc *sc; + int i; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct dc_desc *c; + + c = &sc->dc_ldata->dc_rx_list[i]; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("dc%d: no memory for rx list " + "-- packet dropped!\n", sc->dc_unit); + return(ENOBUFS); + } + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + printf("dc%d: no memory for rx list " + "-- packet dropped!\n", sc->dc_unit); + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + /* + * If this is a PNIC chip, zero the buffer. This is part + * of the workaround for the receive bug in the 82c168 and + * 82c169 chips. + */ + if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) + bzero((char *)mtod(m_new, char *), m_new->m_len); + + sc->dc_cdata.dc_rx_chain[i] = m_new; + c->dc_data = vtophys(mtod(m_new, caddr_t)); + c->dc_ctl = DC_RXCTL_RLINK | DC_RXLEN; + c->dc_status = DC_RXSTAT_OWN; + + return(0); +} + +/* + * Grrrrr. + * The PNIC chip has a terrible bug in it that manifests itself during + * periods of heavy activity. The exact mode of failure if difficult to + * pinpoint: sometimes it only happens in promiscuous mode, sometimes it + * will happen on slow machines. The bug is that sometimes instead of + * uploading one complete frame during reception, it uploads what looks + * like the entire contents of its FIFO memory. The frame we want is at + * the end of the whole mess, but we never know exactly how much data has + * been uploaded, so salvaging the frame is hard. + * + * There is only one way to do it reliably, and it's disgusting. + * Here's what we know: + * + * - We know there will always be somewhere between one and three extra + * descriptors uploaded. + * + * - We know the desired received frame will always be at the end of the + * total data upload. + * + * - We know the size of the desired received frame because it will be + * provided in the length field of the status word in the last descriptor. + * + * Here's what we do: + * + * - When we allocate buffers for the receive ring, we bzero() them. + * This means that we know that the buffer contents should be all + * zeros, except for data uploaded by the chip. + * + * - We also force the PNIC chip to upload frames that include the + * ethernet CRC at the end. + * + * - We gather all of the bogus frame data into a single buffer. + * + * - We then position a pointer at the end of this buffer and scan + * backwards until we encounter the first non-zero byte of data. + * This is the end of the received frame. We know we will encounter + * some data at the end of the frame because the CRC will always be + * there, so even if the sender transmits a packet of all zeros, + * we won't be fooled. + * + * - We know the size of the actual received frame, so we subtract + * that value from the current pointer location. This brings us + * to the start of the actual received packet. + * + * - We copy this into an mbuf and pass it on, along with the actual + * frame length. + * + * The performance hit is tremendous, but it beats dropping frames all + * the time. + */ + +#define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG) +static void dc_pnic_rx_bug_war(sc, idx) + struct dc_softc *sc; + int idx; +{ + struct dc_desc *cur_rx; + struct dc_desc *c = NULL; + struct mbuf *m = NULL; + unsigned char *ptr; + int i, total_len; + u_int32_t rxstat = 0; + + i = sc->dc_pnic_rx_bug_save; + cur_rx = &sc->dc_ldata->dc_rx_list[idx]; + ptr = sc->dc_pnic_rx_buf; + bzero(ptr, sizeof(DC_RXLEN * 5)); + + /* Copy all the bytes from the bogus buffers. */ + while (1) { + c = &sc->dc_ldata->dc_rx_list[i]; + rxstat = c->dc_status; + m = sc->dc_cdata.dc_rx_chain[i]; + bcopy(mtod(m, char *), ptr, DC_RXLEN); + ptr += DC_RXLEN; + /* If this is the last buffer, break out. */ + if (i == idx || rxstat & DC_RXSTAT_LASTFRAG) + break; + dc_newbuf(sc, i, m); + DC_INC(i, DC_RX_LIST_CNT); + } + + /* Find the length of the actual receive frame. */ + total_len = DC_RXBYTES(rxstat); + + /* Scan backwards until we hit a non-zero byte. */ + while(*ptr == 0x00) + ptr--; + + /* Round off. */ + if ((uintptr_t)(ptr) & 0x3) + ptr -= 1; + + /* Now find the start of the frame. */ + ptr -= total_len; + if (ptr < sc->dc_pnic_rx_buf) + ptr = sc->dc_pnic_rx_buf; + + /* + * Now copy the salvaged frame to the last mbuf and fake up + * the status word to make it look like a successful + * frame reception. + */ + dc_newbuf(sc, i, m); + bcopy(ptr, mtod(m, char *), total_len); + cur_rx->dc_status = rxstat | DC_RXSTAT_FIRSTFRAG; + + return; +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void dc_rxeof(sc) + struct dc_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct dc_desc *cur_rx; + int i, total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + i = sc->dc_cdata.dc_rx_prod; + + while(!(sc->dc_ldata->dc_rx_list[i].dc_status & DC_RXSTAT_OWN)) { + struct mbuf *m0 = NULL; + + cur_rx = &sc->dc_ldata->dc_rx_list[i]; + rxstat = cur_rx->dc_status; + m = sc->dc_cdata.dc_rx_chain[i]; + total_len = DC_RXBYTES(rxstat); + + if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) { + if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) { + if (rxstat & DC_RXSTAT_FIRSTFRAG) + sc->dc_pnic_rx_bug_save = i; + if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) { + DC_INC(i, DC_RX_LIST_CNT); + continue; + } + dc_pnic_rx_bug_war(sc, i); + rxstat = cur_rx->dc_status; + total_len = DC_RXBYTES(rxstat); + } + } + + sc->dc_cdata.dc_rx_chain[i] = NULL; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & DC_RXSTAT_RXERR) { + ifp->if_ierrors++; + if (rxstat & DC_RXSTAT_COLLSEEN) + ifp->if_collisions++; + dc_newbuf(sc, i, m); + if (rxstat & DC_RXSTAT_CRCERR) { + DC_INC(i, DC_RX_LIST_CNT); + continue; + } else { + dc_init(sc); + return; + } + } + + /* No errors; receive the packet. */ + total_len -= ETHER_CRC_LEN; + + m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, + total_len + ETHER_ALIGN, 0, ifp, NULL); + dc_newbuf(sc, i, m); + DC_INC(i, DC_RX_LIST_CNT); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m_adj(m0, ETHER_ALIGN); + m = m0; + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* + * Handle BPF listeners. Let the BPF user see the packet, but + * don't pass it up to the ether_input() layer unless it's + * a broadcast packet, multicast packet, matches our ethernet + * address or the interface is in promiscuous mode. + */ + if (ifp->if_bpf) { + bpf_mtap(ifp, m); + if (ifp->if_flags & IFF_PROMISC && + (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + ETHER_ADDR_LEN) && + (eh->ether_dhost[0] & 1) == 0)) { + m_freem(m); + continue; + } + } + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + sc->dc_cdata.dc_rx_prod = i; + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void dc_txeof(sc) + struct dc_softc *sc; +{ + struct dc_desc *cur_tx = NULL; + struct ifnet *ifp; + int idx; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->dc_cdata.dc_tx_cons; + while(idx != sc->dc_cdata.dc_tx_prod) { + u_int32_t txstat; + + cur_tx = &sc->dc_ldata->dc_tx_list[idx]; + txstat = cur_tx->dc_status; + + if (txstat & DC_TXSTAT_OWN) + break; + + if (!(cur_tx->dc_ctl & DC_TXCTL_LASTFRAG) || + cur_tx->dc_ctl & DC_TXCTL_SETUP) { + sc->dc_cdata.dc_tx_cnt--; + if (cur_tx->dc_ctl & DC_TXCTL_SETUP) { + /* + * Yes, the PNIC is so brain damaged + * that it will sometimes generate a TX + * underrun error while DMAing the RX + * filter setup frame. If we detect this, + * we have to send the setup frame again, + * or else the filter won't be programmed + * correctly. + */ + if (DC_IS_PNIC(sc)) { + if (txstat & DC_TXSTAT_ERRSUM) + dc_setfilt(sc); + } + sc->dc_cdata.dc_tx_chain[idx] = NULL; + } + DC_INC(idx, DC_TX_LIST_CNT); + continue; + } + + if (/*sc->dc_type == DC_TYPE_21143 &&*/ + sc->dc_pmode == DC_PMODE_MII && + ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM| + DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST))) + txstat &= ~DC_TXSTAT_ERRSUM; + + if (txstat & DC_TXSTAT_ERRSUM) { + ifp->if_oerrors++; + if (txstat & DC_TXSTAT_EXCESSCOLL) + ifp->if_collisions++; + if (txstat & DC_TXSTAT_LATECOLL) + ifp->if_collisions++; + if (!(txstat & DC_TXSTAT_UNDERRUN)) { + dc_init(sc); + return; + } + } + + ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3; + + ifp->if_opackets++; + if (sc->dc_cdata.dc_tx_chain[idx] != NULL) { + m_freem(sc->dc_cdata.dc_tx_chain[idx]); + sc->dc_cdata.dc_tx_chain[idx] = NULL; + } + + sc->dc_cdata.dc_tx_cnt--; + DC_INC(idx, DC_TX_LIST_CNT); + } + + sc->dc_cdata.dc_tx_cons = idx; + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void dc_tick(xsc) + void *xsc; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + int s; + u_int32_t r; + + s = splimp(); + + sc = xsc; + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->dc_miibus); + + if (sc->dc_flags & DC_REDUCED_MII_POLL) { + r = CSR_READ_4(sc, DC_ISR); + if (DC_IS_INTEL(sc)) { + if (r & DC_ISR_LINKFAIL) { + sc->dc_link = 0; + mii_tick(mii); + } + } else { + if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT && + sc->dc_cdata.dc_tx_prod == 0) + mii_tick(mii); + } + } else + mii_tick(mii); + + /* + * When the init routine completes, we expect to be able to send + * packets right away, and in fact the network code will send a + * gratuitous ARP the moment the init routine marks the interface + * as running. However, even though the MAC may have been initialized, + * there may be a delay of a few seconds before the PHY completes + * autonegotiation and the link is brought up. Any transmissions + * made during that delay will be lost. Dealing with this is tricky: + * we can't just pause in the init routine while waiting for the + * PHY to come ready since that would bring the whole system to + * a screeching halt for several seconds. + * + * What we do here is prevent the TX start routine from sending + * any packets until a link has been established. After the + * interface has been initialized, the tick routine will poll + * the state of the PHY until the IFM_ACTIVE flag is set. Until + * that time, packets will stay in the send queue, and once the + * link comes up, they will be flushed out to the wire. + */ + if (!sc->dc_link) { + mii_pollstat(mii); + if (mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->dc_link++; + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + } + } + + sc->dc_stat_ch = timeout(dc_tick, sc, hz); + + splx(s); + + return; +} + +static void dc_intr(arg) + void *arg; +{ + struct dc_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = &sc->arpcom.ac_if; + + /* Supress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + if (CSR_READ_4(sc, DC_ISR) & DC_INTRS) + dc_stop(sc); + return; + } + + /* Disable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + + while((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) { + + CSR_WRITE_4(sc, DC_ISR, status); + + if (status & DC_ISR_RX_OK) + dc_rxeof(sc); + + if (status & (DC_ISR_TX_OK|DC_ISR_TX_NOBUF)) + dc_txeof(sc); + + if (status & DC_ISR_TX_IDLE) { + dc_txeof(sc); + if (sc->dc_cdata.dc_tx_cnt) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + } + } + + if (status & DC_ISR_TX_UNDERRUN) { + u_int32_t cfg; + + printf("dc%d: TX underrun -- ", sc->dc_unit); + if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) + dc_init(sc); + cfg = CSR_READ_4(sc, DC_NETCFG); + cfg &= ~DC_NETCFG_TX_THRESH; + if (sc->dc_txthresh == DC_TXTHRESH_160BYTES) { + printf("using store and forward mode\n"); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } else { + sc->dc_txthresh += 0x4000; + printf("increasing TX threshold\n"); + CSR_WRITE_4(sc, DC_NETCFG, cfg); + DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } + } + + if ((status & DC_ISR_RX_WATDOGTIMEO) + || (status & DC_ISR_RX_NOBUF)) + dc_rxeof(sc); + + if (status & DC_ISR_BUS_ERR) { + dc_reset(sc); + dc_init(sc); + } + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int dc_encap(sc, m_head, txidx) + struct dc_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct dc_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (sc->dc_flags & DC_TX_ADMTEK_WAR) { + if (*txidx != sc->dc_cdata.dc_tx_prod && + frag == (DC_TX_LIST_CNT - 1)) + return(ENOBUFS); + } + if ((DC_TX_LIST_CNT - + (sc->dc_cdata.dc_tx_cnt + cnt)) < 5) + return(ENOBUFS); + + f = &sc->dc_ldata->dc_tx_list[frag]; + f->dc_ctl = DC_TXCTL_TLINK | m->m_len; + if (cnt == 0) { + f->dc_status = 0; + f->dc_ctl |= DC_TXCTL_FIRSTFRAG; + } else + f->dc_status = DC_TXSTAT_OWN; + f->dc_data = vtophys(mtod(m, vm_offset_t)); + cur = frag; + DC_INC(frag, DC_TX_LIST_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + sc->dc_cdata.dc_tx_cnt += cnt; + sc->dc_cdata.dc_tx_chain[cur] = m_head; + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_LASTFRAG; + if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG) + sc->dc_ldata->dc_tx_list[*txidx].dc_ctl |= DC_TXCTL_FINT; + if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64) + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT; + sc->dc_ldata->dc_tx_list[*txidx].dc_status = DC_TXSTAT_OWN; + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ + +static void dc_start(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + struct mbuf *m_head = NULL; + int idx; + + sc = ifp->if_softc; + + if (!sc->dc_link) + return; + + if (ifp->if_flags & IFF_OACTIVE) + return; + + idx = sc->dc_cdata.dc_tx_prod; + + while(sc->dc_cdata.dc_tx_chain[idx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (dc_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + } + + /* Transmit */ + sc->dc_cdata.dc_tx_prod = idx; + if (!(sc->dc_flags & DC_TX_POLL)) + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + return; +} + +static void dc_init(xsc) + void *xsc; +{ + struct dc_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + int s; + + s = splimp(); + + mii = device_get_softc(sc->dc_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + dc_stop(sc); + dc_reset(sc); + + /* + * Set cache alignment and burst length. + */ + if (DC_IS_ASIX(sc)) + CSR_WRITE_4(sc, DC_BUSCTL, 0); + else + CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME|DC_BUSCTL_MRLE); + if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) { + DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA); + } else { + DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG); + } + if (sc->dc_flags & DC_TX_POLL) + DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1); + switch(sc->dc_cachesize) { + case 32: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG); + break; + case 16: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG); + break; + case 8: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG); + break; + case 0: + default: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE); + break; + } + + if (sc->dc_flags & DC_TX_STORENFWD) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + else { + if (sc->dc_txthresh == DC_TXTHRESH_160BYTES) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); + } + } + + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF); + + if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + /* + * The app notes for the 98713 and 98715A say that + * in order to have the chips operate properly, a magic + * number must be written to CSR16. Macronix does not + * document the meaning of these bits so there's no way + * to know exactly what they do. The 98713 has a magic + * number all its own; the rest all use a different one. + */ + DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713); + else + DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715); + } + + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); + DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_72BYTES); + + /* Init circular RX list. */ + if (dc_list_rx_init(sc) == ENOBUFS) { + printf("dc%d: initialization failed: no " + "memory for rx buffers\n", sc->dc_unit); + dc_stop(sc); + (void)splx(s); + return; + } + + /* + * Init tx descriptors. + */ + dc_list_tx_init(sc); + + /* + * Load the address of the RX list. + */ + CSR_WRITE_4(sc, DC_RXADDR, vtophys(&sc->dc_ldata->dc_rx_list[0])); + CSR_WRITE_4(sc, DC_TXADDR, vtophys(&sc->dc_ldata->dc_tx_list[0])); + + /* + * Enable interrupts. + */ + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF); + + /* Enable transmitter. */ + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + + /* + * Load the RX/multicast filter. We do this sort of late + * because the filter programming scheme on the 21143 and + * some clones requires DMAing a setup frame via the TX + * engine, and we need the transmitter enabled for that. + */ + dc_setfilt(sc); + + /* Enable receiver. */ + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); + CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF); + + mii_mediachg(mii); + dc_setcfg(sc, sc->dc_if_media); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + (void)splx(s); + + sc->dc_stat_ch = timeout(dc_tick, sc, hz); + + return; +} + +/* + * Set media options. + */ +static int dc_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->dc_miibus); + mii_mediachg(mii); + sc->dc_link = 0; + + return(0); +} + +/* + * Report current media status. + */ +static void dc_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct dc_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->dc_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int dc_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct dc_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int s, error = 0; + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->dc_if_flags & IFF_PROMISC)) { + dc_setfilt(sc); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->dc_if_flags & IFF_PROMISC) { + dc_setfilt(sc); + } else if (!(ifp->if_flags & IFF_RUNNING)) { + sc->dc_txthresh = 0; + dc_init(sc); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + dc_stop(sc); + } + sc->dc_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + dc_setfilt(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->dc_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + (void)splx(s); + + return(error); +} + +static void dc_watchdog(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + + sc = ifp->if_softc; + + ifp->if_oerrors++; + printf("dc%d: watchdog timeout\n", sc->dc_unit); + + dc_stop(sc); + dc_reset(sc); + dc_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void dc_stop(sc) + struct dc_softc *sc; +{ + register int i; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(dc_tick, sc, sc->dc_stat_ch); + + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON|DC_NETCFG_TX_ON)); + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + CSR_WRITE_4(sc, DC_TXADDR, 0x00000000); + CSR_WRITE_4(sc, DC_RXADDR, 0x00000000); + sc->dc_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < DC_RX_LIST_CNT; i++) { + if (sc->dc_cdata.dc_rx_chain[i] != NULL) { + m_freem(sc->dc_cdata.dc_rx_chain[i]); + sc->dc_cdata.dc_rx_chain[i] = NULL; + } + } + bzero((char *)&sc->dc_ldata->dc_rx_list, + sizeof(sc->dc_ldata->dc_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < DC_TX_LIST_CNT; i++) { + if (sc->dc_cdata.dc_tx_chain[i] != NULL) { + if (sc->dc_ldata->dc_tx_list[i].dc_ctl & + DC_TXCTL_SETUP) { + sc->dc_cdata.dc_tx_chain[i] = NULL; + continue; + } + m_freem(sc->dc_cdata.dc_tx_chain[i]); + sc->dc_cdata.dc_tx_chain[i] = NULL; + } + } + + bzero((char *)&sc->dc_ldata->dc_tx_list, + sizeof(sc->dc_ldata->dc_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void dc_shutdown(dev) + device_t dev; +{ + struct dc_softc *sc; + + sc = device_get_softc(dev); + + dc_stop(sc); + + return; +} diff --git a/sys/dev/dc/if_dcreg.h b/sys/dev/dc/if_dcreg.h new file mode 100644 index 0000000..7994e94 --- /dev/null +++ b/sys/dev/dc/if_dcreg.h @@ -0,0 +1,903 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * 21143 and clone common register definitions. + */ + +#define DC_BUSCTL 0x00 /* bus control */ +#define DC_TXSTART 0x08 /* tx start demand */ +#define DC_RXSTART 0x10 /* rx start demand */ +#define DC_RXADDR 0x18 /* rx descriptor list start addr */ +#define DC_TXADDR 0x20 /* tx descriptor list start addr */ +#define DC_ISR 0x28 /* interrupt status register */ +#define DC_NETCFG 0x30 /* network config register */ +#define DC_IMR 0x38 /* interrupt mask */ +#define DC_FRAMESDISCARDED 0x40 /* # of discarded frames */ +#define DC_SIO 0x48 /* MII and ROM/EEPROM access */ +#define DC_ROM 0x50 /* ROM programming address */ +#define DC_TIMER 0x58 /* general timer */ +#define DC_10BTSTAT 0x60 /* SIA status */ +#define DC_SIARESET 0x68 /* SIA connectivity */ +#define DC_10BTCTRL 0x70 /* SIA transmit and receive */ +#define DC_WATCHDOG 0x78 /* SIA and general purpose port */ + +/* + * There are two general 'types' of MX chips that we need to be + * concerned with. One is the original 98713, which has its internal + * NWAY support controlled via the MDIO bits in the serial I/O + * register. The other is everything else (from the 98713A on up), + * which has its internal NWAY controlled via CSR13, CSR14 and CSR15, + * just like the 21143. This type setting also governs which of the + * 'magic' numbers we write to CSR16. The PNIC II falls into the + * 98713A/98715/98715A/98725 category. + */ +#define DC_TYPE_98713 0x1 +#define DC_TYPE_98713A 0x2 +#define DC_TYPE_987x5 0x3 + +/* Other type of supported chips. */ +#define DC_TYPE_21143 0x4 /* Intel 21143 */ +#define DC_TYPE_ASIX 0x5 /* ASIX AX88140A/AX88141 */ +#define DC_TYPE_AL981 0x6 /* ADMtek AL981 Comet */ +#define DC_TYPE_AN985 0x7 /* ADMtek AN985 Centaur */ +#define DC_TYPE_DM9102 0x8 /* Davicom DM9102 */ +#define DC_TYPE_PNICII 0x9 /* 82c115 PNIC II */ +#define DC_TYPE_PNIC 0xA /* 82c168/82c169 PNIC I */ + +#define DC_IS_MACRONIX(x) \ + (x->dc_type == DC_TYPE_98713 || \ + x->dc_type == DC_TYPE_98713A || \ + x->dc_type == DC_TYPE_987x5) + +#define DC_IS_ADMTEK(x) \ + (x->dc_type == DC_TYPE_AL981 || \ + x->dc_type == DC_TYPE_AN985) + +#define DC_IS_INTEL(x) (x->dc_type == DC_TYPE_21143) +#define DC_IS_ASIX(x) (x->dc_type == DC_TYPE_ASIX) +#define DC_IS_COMET(x) (x->dc_type == DC_TYPE_AL981) +#define DC_IS_CENTAUR(x) (x->dc_type == DC_TYPE_AN985) +#define DC_IS_DAVICOM(x) (x->dc_type == DC_TYPE_DM9102) +#define DC_IS_PNICII(x) (x->dc_type == DC_TYPE_PNICII) +#define DC_IS_PNIC(x) (x->dc_type == DC_TYPE_PNIC) + +/* MII/symbol mode port types */ +#define DC_PMODE_MII 0x1 +#define DC_PMODE_SYM 0x2 + +/* + * Bus control bits. + */ +#define DC_BUSCTL_RESET 0x00000001 +#define DC_BUSCTL_ARBITRATION 0x00000002 +#define DC_BUSCTL_SKIPLEN 0x0000007C +#define DC_BUSCTL_BUF_BIGENDIAN 0x00000080 +#define DC_BUSCTL_BURSTLEN 0x00003F00 +#define DC_BUSCTL_CACHEALIGN 0x0000C000 +#define DC_BUSCTL_TXPOLL 0x000E0000 +#define DC_BUSCTL_DBO 0x00100000 +#define DC_BUSCTL_MRME 0x00200000 +#define DC_BUSCTL_MRLE 0x00800000 +#define DC_BUSCTL_MWIE 0x01000000 +#define DC_BUSCTL_ONNOW_ENB 0x04000000 + +#define DC_SKIPLEN_1LONG 0x00000004 +#define DC_SKIPLEN_2LONG 0x00000008 +#define DC_SKIPLEN_3LONG 0x00000010 +#define DC_SKIPLEN_4LONG 0x00000020 +#define DC_SKIPLEN_5LONG 0x00000040 + +#define DC_CACHEALIGN_NONE 0x00000000 +#define DC_CACHEALIGN_8LONG 0x00004000 +#define DC_CACHEALIGN_16LONG 0x00008000 +#define DC_CACHEALIGN_32LONG 0x0000C000 + +#define DC_BURSTLEN_USECA 0x00000000 +#define DC_BURSTLEN_1LONG 0x00000100 +#define DC_BURSTLEN_2LONG 0x00000200 +#define DC_BURSTLEN_4LONG 0x00000400 +#define DC_BURSTLEN_8LONG 0x00000800 +#define DC_BURSTLEN_16LONG 0x00001000 +#define DC_BURSTLEN_32LONG 0x00002000 + +#define DC_TXPOLL_OFF 0x00000000 +#define DC_TXPOLL_1 0x00020000 +#define DC_TXPOLL_2 0x00040000 +#define DC_TXPOLL_3 0x00060000 +#define DC_TXPOLL_4 0x00080000 +#define DC_TXPOLL_5 0x000A0000 +#define DC_TXPOLL_6 0x000C0000 +#define DC_TXPOLL_7 0x000E0000 + +/* + * Interrupt status bits. + */ +#define DC_ISR_TX_OK 0x00000001 +#define DC_ISR_TX_IDLE 0x00000002 +#define DC_ISR_TX_NOBUF 0x00000004 +#define DC_ISR_TX_JABBERTIMEO 0x00000008 +#define DC_ISR_LINKGOOD 0x00000010 +#define DC_ISR_TX_UNDERRUN 0x00000020 +#define DC_ISR_RX_OK 0x00000040 +#define DC_ISR_RX_NOBUF 0x00000080 +#define DC_ISR_RX_READ 0x00000100 +#define DC_ISR_RX_WATDOGTIMEO 0x00000200 +#define DC_ISR_TX_EARLY 0x00000400 +#define DC_ISR_TIMER_EXPIRED 0x00000800 +#define DC_ISR_LINKFAIL 0x00001000 +#define DC_ISR_BUS_ERR 0x00002000 +#define DC_ISR_RX_EARLY 0x00004000 +#define DC_ISR_ABNORMAL 0x00008000 +#define DC_ISR_NORMAL 0x00010000 +#define DC_ISR_RX_STATE 0x000E0000 +#define DC_ISR_TX_STATE 0x00700000 +#define DC_ISR_BUSERRTYPE 0x03800000 +#define DC_ISR_100MBPSLINK 0x08000000 +#define DC_ISR_MAGICKPACK 0x10000000 + +#define DC_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ +#define DC_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ +#define DC_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ +#define DC_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ +#define DC_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ +#define DC_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ +#define DC_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ +#define DC_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ + +#define DC_TXSTATE_RESET 0x00000000 /* 000 - reset */ +#define DC_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ +#define DC_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ +#define DC_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ +#define DC_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ +#define DC_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ +#define DC_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ +#define DC_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ + +/* + * Network config bits. + */ +#define DC_NETCFG_RX_HASHPERF 0x00000001 +#define DC_NETCFG_RX_ON 0x00000002 +#define DC_NETCFG_RX_HASHONLY 0x00000004 +#define DC_NETCFG_RX_BADFRAMES 0x00000008 +#define DC_NETCFG_RX_INVFILT 0x00000010 +#define DC_NETCFG_BACKOFFCNT 0x00000020 +#define DC_NETCFG_RX_PROMISC 0x00000040 +#define DC_NETCFG_RX_ALLMULTI 0x00000080 +#define DC_NETCFG_FULLDUPLEX 0x00000200 +#define DC_NETCFG_LOOPBACK 0x00000C00 +#define DC_NETCFG_FORCECOLL 0x00001000 +#define DC_NETCFG_TX_ON 0x00002000 +#define DC_NETCFG_TX_THRESH 0x0000C000 +#define DC_NETCFG_TX_BACKOFF 0x00020000 +#define DC_NETCFG_PORTSEL 0x00040000 /* 0 == 10, 1 == 100 */ +#define DC_NETCFG_HEARTBEAT 0x00080000 +#define DC_NETCFG_STORENFWD 0x00200000 +#define DC_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */ +#define DC_NETCFG_PCS 0x00800000 +#define DC_NETCFG_SCRAMBLER 0x01000000 +#define DC_NETCFG_NO_RXCRC 0x02000000 +#define DC_NETCFG_RX_ALL 0x40000000 +#define DC_NETCFG_CAPEFFECT 0x80000000 + +#define DC_OPMODE_NORM 0x00000000 +#define DC_OPMODE_INTLOOP 0x00000400 +#define DC_OPMODE_EXTLOOP 0x00000800 + +#define DC_TXTHRESH_72BYTES 0x00000000 +#define DC_TXTHRESH_96BYTES 0x00004000 +#define DC_TXTHRESH_128BYTES 0x00008000 +#define DC_TXTHRESH_160BYTES 0x0000C000 + + +/* + * Interrupt mask bits. + */ +#define DC_IMR_TX_OK 0x00000001 +#define DC_IMR_TX_IDLE 0x00000002 +#define DC_IMR_TX_NOBUF 0x00000004 +#define DC_IMR_TX_JABBERTIMEO 0x00000008 +#define DC_IMR_LINKGOOD 0x00000010 +#define DC_IMR_TX_UNDERRUN 0x00000020 +#define DC_IMR_RX_OK 0x00000040 +#define DC_IMR_RX_NOBUF 0x00000080 +#define DC_IMR_RX_READ 0x00000100 +#define DC_IMR_RX_WATDOGTIMEO 0x00000200 +#define DC_IMR_TX_EARLY 0x00000400 +#define DC_IMR_TIMER_EXPIRED 0x00000800 +#define DC_IMR_LINKFAIL 0x00001000 +#define DC_IMR_BUS_ERR 0x00002000 +#define DC_IMR_RX_EARLY 0x00004000 +#define DC_IMR_ABNORMAL 0x00008000 +#define DC_IMR_NORMAL 0x00010000 +#define DC_IMR_100MBPSLINK 0x08000000 +#define DC_IMR_MAGICKPACK 0x10000000 + +#define DC_INTRS \ + (DC_IMR_RX_OK|DC_IMR_TX_OK|DC_IMR_RX_NOBUF|DC_IMR_RX_WATDOGTIMEO|\ + DC_IMR_TX_NOBUF|DC_IMR_TX_UNDERRUN|DC_IMR_BUS_ERR| \ + DC_IMR_ABNORMAL|DC_IMR_NORMAL/*|DC_IMR_TX_EARLY*/) +/* + * Serial I/O (EEPROM/ROM) bits. + */ +#define DC_SIO_EE_CS 0x00000001 /* EEPROM chip select */ +#define DC_SIO_EE_CLK 0x00000002 /* EEPROM clock */ +#define DC_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ +#define DC_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ +#define DC_SIO_ROMDATA4 0x00000010 +#define DC_SIO_ROMDATA5 0x00000020 +#define DC_SIO_ROMDATA6 0x00000040 +#define DC_SIO_ROMDATA7 0x00000080 +#define DC_SIO_EESEL 0x00000800 +#define DC_SIO_ROMSEL 0x00001000 +#define DC_SIO_ROMCTL_WRITE 0x00002000 +#define DC_SIO_ROMCTL_READ 0x00004000 +#define DC_SIO_MII_CLK 0x00010000 /* MDIO clock */ +#define DC_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ +#define DC_SIO_MII_DIR 0x00040000 /* MDIO dir */ +#define DC_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ + +#define DC_EECMD_WRITE 0x140 +#define DC_EECMD_READ 0x180 +#define DC_EECMD_ERASE 0x1c0 + +#define DC_EE_NODEADDR_OFFSET 0x70 +#define DC_EE_NODEADDR 10 + +/* + * General purpose timer register + */ +#define DC_TIMER_VALUE 0x0000FFFF +#define DC_TIMER_CONTINUOUS 0x00010000 + +/* + * 10baseT status register + */ +#define DC_TSTAT_MIIACT 0x00000001 /* MII port activity */ +#define DC_TSTAT_LS100 0x00000002 /* link status of 100baseTX */ +#define DC_TSTAT_LS10 0x00000004 /* link status of 10baseT */ +#define DC_TSTAT_AUTOPOLARITY 0x00000008 +#define DC_TSTAT_AUIACT 0x00000100 /* AUI activity */ +#define DC_TSTAT_10BTACT 0x00000200 /* 10baseT activity */ +#define DC_TSTAT_NSN 0x00000400 /* non-stable FLPs detected */ +#define DC_TSTAT_REMFAULT 0x00000800 +#define DC_TSTAT_ANEGSTAT 0x00007000 +#define DC_TSTAT_LP_CAN_NWAY 0x00008000 /* link partner supports NWAY */ +#define DC_TSTAT_LPCODEWORD 0xFFFF0000 /* link partner's code word */ + +#define DC_ASTAT_DISABLE 0x00000000 +#define DC_ASTAT_TXDISABLE 0x00001000 +#define DC_ASTAT_ABDETECT 0x00002000 +#define DC_ASTAT_ACKDETECT 0x00003000 +#define DC_ASTAT_CMPACKDETECT 0x00004000 +#define DC_ASTAT_AUTONEGCMP 0x00005000 +#define DC_ASTAT_LINKCHECK 0x00006000 + +/* + * PHY reset register + */ +#define DC_SIA_RESET 0x00000001 +#define DC_SIA_AUI 0x00000008 /* AUI or 10baseT */ + +/* + * 10baseT control register + */ +#define DC_TCTL_ENCODER_ENB 0x00000001 +#define DC_TCTL_LOOPBACK 0x00000002 +#define DC_TCTL_DRIVER_ENB 0x00000004 +#define DC_TCTL_LNKPULSE_ENB 0x00000008 +#define DC_TCTL_HALFDUPLEX 0x00000040 +#define DC_TCTL_AUTONEGENBL 0x00000080 +#define DC_TCTL_RX_SQUELCH 0x00000100 +#define DC_TCTL_COLL_SQUELCH 0x00000200 +#define DC_TCTL_COLL_DETECT 0x00000400 +#define DC_TCTL_SQE_ENB 0x00000800 +#define DC_TCTL_LINKTEST 0x00001000 +#define DC_TCTL_AUTOPOLARITY 0x00002000 +#define DC_TCTL_SET_POL_PLUS 0x00004000 +#define DC_TCTL_AUTOSENSE 0x00008000 /* 10bt/AUI autosense */ +#define DC_TCTL_100BTXHALF 0x00010000 +#define DC_TCTL_100BTXFULL 0x00020000 +#define DC_TCTL_100BT4 0x00040000 + +/* + * Watchdog timer register + */ +#define DC_WDOG_JABBERDIS 0x00000001 +#define DC_WDOG_HOSTUNJAB 0x00000002 +#define DC_WDOG_JABBERCLK 0x00000004 +#define DC_WDOG_RXWDOGDIS 0x00000010 +#define DC_WDOG_RXWDOGCLK 0x00000020 +#define DC_WDOG_MUSTBEZERO 0x00000100 + +/* + * Size of a setup frame. + */ +#define DC_SFRAME_LEN 192 + +/* + * 21x4x TX/RX list structure. + */ + +struct dc_desc { + u_int32_t dc_status; + u_int32_t dc_ctl; + u_int32_t dc_ptr1; + u_int32_t dc_ptr2; +}; + +#define dc_data dc_ptr1 +#define dc_next dc_ptr2 + +#define DC_RXSTAT_FIFOOFLOW 0x00000001 +#define DC_RXSTAT_CRCERR 0x00000002 +#define DC_RXSTAT_DRIBBLE 0x00000004 +#define DC_RXSTAT_WATCHDOG 0x00000010 +#define DC_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ +#define DC_RXSTAT_COLLSEEN 0x00000040 +#define DC_RXSTAT_GIANT 0x00000080 +#define DC_RXSTAT_LASTFRAG 0x00000100 +#define DC_RXSTAT_FIRSTFRAG 0x00000200 +#define DC_RXSTAT_MULTICAST 0x00000400 +#define DC_RXSTAT_RUNT 0x00000800 +#define DC_RXSTAT_RXTYPE 0x00003000 +#define DC_RXSTAT_RXERR 0x00008000 +#define DC_RXSTAT_RXLEN 0x3FFF0000 +#define DC_RXSTAT_OWN 0x80000000 + +#define DC_RXBYTES(x) ((x & DC_RXSTAT_RXLEN) >> 16) +#define DC_RXSTAT (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG|DC_RXSTAT_OWN) + +#define DC_RXCTL_BUFLEN1 0x00000FFF +#define DC_RXCTL_BUFLEN2 0x00FFF000 +#define DC_RXCTL_RLINK 0x01000000 +#define DC_RXCTL_RLAST 0x02000000 + +#define DC_TXSTAT_DEFER 0x00000001 +#define DC_TXSTAT_UNDERRUN 0x00000002 +#define DC_TXSTAT_LINKFAIL 0x00000003 +#define DC_TXSTAT_COLLCNT 0x00000078 +#define DC_TXSTAT_SQE 0x00000080 +#define DC_TXSTAT_EXCESSCOLL 0x00000100 +#define DC_TXSTAT_LATECOLL 0x00000200 +#define DC_TXSTAT_NOCARRIER 0x00000400 +#define DC_TXSTAT_CARRLOST 0x00000800 +#define DC_TXSTAT_JABTIMEO 0x00004000 +#define DC_TXSTAT_ERRSUM 0x00008000 +#define DC_TXSTAT_OWN 0x80000000 + +#define DC_TXCTL_BUFLEN1 0x000007FF +#define DC_TXCTL_BUFLEN2 0x003FF800 +#define DC_TXCTL_FILTTYPE0 0x00400000 +#define DC_TXCTL_PAD 0x00800000 +#define DC_TXCTL_TLINK 0x01000000 +#define DC_TXCTL_TLAST 0x02000000 +#define DC_TXCTL_NOCRC 0x04000000 +#define DC_TXCTL_SETUP 0x08000000 +#define DC_TXCTL_FILTTYPE1 0x10000000 +#define DC_TXCTL_FIRSTFRAG 0x20000000 +#define DC_TXCTL_LASTFRAG 0x40000000 +#define DC_TXCTL_FINT 0x80000000 + +#define DC_FILTER_PERFECT 0x00000000 +#define DC_FILTER_HASHPERF 0x00400000 +#define DC_FILTER_INVERSE 0x10000000 +#define DC_FILTER_HASHONLY 0x10400000 + +#define DC_MAXFRAGS 16 +#define DC_RX_LIST_CNT 64 +#define DC_TX_LIST_CNT 256 +#define DC_MIN_FRAMELEN 60 +#define DC_RXLEN 1536 + +#define DC_INC(x, y) (x) = (x + 1) % y + +struct dc_list_data { + struct dc_desc dc_rx_list[DC_RX_LIST_CNT]; + struct dc_desc dc_tx_list[DC_TX_LIST_CNT]; +}; + +struct dc_chain_data { + struct mbuf *dc_rx_chain[DC_RX_LIST_CNT]; + struct mbuf *dc_tx_chain[DC_TX_LIST_CNT]; + u_int32_t dc_sbuf[DC_SFRAME_LEN/sizeof(u_int32_t)]; + u_int8_t dc_pad[DC_MIN_FRAMELEN]; + int dc_tx_prod; + int dc_tx_cons; + int dc_tx_cnt; + int dc_rx_prod; +}; + +struct dc_type { + u_int16_t dc_vid; + u_int16_t dc_did; + char *dc_name; +}; + +struct dc_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define DC_MII_STARTDELIM 0x01 +#define DC_MII_READOP 0x02 +#define DC_MII_WRITEOP 0x01 +#define DC_MII_TURNAROUND 0x02 + + +/* + * Registers specific to clone devices. + * This mainly relates to RX filter programming: not all 21x4x clones + * use the standard DEC filter programming mechanism. + */ + +/* + * ADMtek specific registers and constants for the AL981 and AN985. + * The AN985 doesn't use the magic PHY registers. + */ +#define DC_AL_PAR0 0xA4 /* station address */ +#define DC_AL_PAR1 0xA8 /* station address */ +#define DC_AL_MAR0 0xAC /* multicast hash filter */ +#define DC_AL_MAR1 0xB0 /* multicast hash filter */ +#define DC_AL_BMCR 0xB4 /* built in PHY control */ +#define DC_AL_BMSR 0xB8 /* built in PHY status */ +#define DC_AL_VENID 0xBC /* built in PHY ID0 */ +#define DC_AL_DEVID 0xC0 /* built in PHY ID1 */ +#define DC_AL_ANAR 0xC4 /* built in PHY autoneg advert */ +#define DC_AL_LPAR 0xC8 /* bnilt in PHY link part. ability */ +#define DC_AL_ANER 0xCC /* built in PHY autoneg expansion */ + +#define DC_ADMTEK_PHYADDR 0x1 +#define DC_AL_EE_NODEADDR 4 +/* End of ADMtek specific registers */ + +/* + * ASIX specific registers. + */ +#define DC_AX_FILTIDX 0x68 /* RX filter index */ +#define DC_AX_FILTDATA 0x70 /* RX filter data */ + +/* + * Special ASIX-specific bits in the ASIX NETCFG register (CSR6). + */ +#define DC_AX_NETCFG_RX_BROAD 0x00000100 + +/* + * RX Filter Index Register values + */ +#define DC_AX_FILTIDX_PAR0 0x00000000 +#define DC_AX_FILTIDX_PAR1 0x00000001 +#define DC_AX_FILTIDX_MAR0 0x00000002 +#define DC_AX_FILTIDX_MAR1 0x00000003 +/* End of ASIX specific registers */ + +/* + * Macronix specific registers. The Macronix chips have a special + * register for reading the NWAY status, which we don't use, plus + * a magic packet register, which we need to tweak a bit per the + * Macronix application notes. + */ +#define DC_MX_MAGICPACKET 0x80 +#define DC_MX_NWAYSTAT 0xA0 + +/* + * Magic packet register + */ +#define DC_MX_MPACK_DISABLE 0x00400000 + +/* + * NWAY status register. + */ +#define DC_MX_NWAY_10BTHALF 0x08000000 +#define DC_MX_NWAY_10BTFULL 0x10000000 +#define DC_MX_NWAY_100BTHALF 0x20000000 +#define DC_MX_NWAY_100BTFULL 0x40000000 +#define DC_MX_NWAY_100BT4 0x80000000 + +/* + * These are magic values that must be written into CSR16 + * (DC_MX_MAGICPACKET) in order to put the chip into proper + * operating mode. The magic numbers are documented in the + * Macronix 98715 application notes. + */ +#define DC_MX_MAGIC_98713 0x0F370000 +#define DC_MX_MAGIC_98713A 0x0B3C0000 +#define DC_MX_MAGIC_98715 0x0B3C0000 +#define DC_MX_MAGIC_98725 0x0B3C0000 +/* End of Macronix specific registers */ + +/* + * PNIC 82c168/82c169 specific registers. + * The PNIC has its own special NWAY support, which doesn't work, + * and shortcut ways of reading the EEPROM and MII bus. + */ +#define DC_PN_GPIO 0x60 /* general purpose pins control */ +#define DC_PN_PWRUP_CFG 0x90 /* config register, set by EEPROM */ +#define DC_PN_SIOCTL 0x98 /* serial EEPROM control register */ +#define DC_PN_MII 0xA0 /* MII access register */ +#define DC_PN_NWAY 0xB8 /* Internal NWAY register */ + +/* Serial I/O EEPROM register */ +#define DC_PN_SIOCTL_DATA 0x0000003F +#define DC_PN_SIOCTL_OPCODE 0x00000300 +#define DC_PN_SIOCTL_BUSY 0x80000000 + +#define DC_PN_EEOPCODE_ERASE 0x00000300 +#define DC_PN_EEOPCODE_READ 0x00000600 +#define DC_PN_EEOPCODE_WRITE 0x00000100 + +/* + * The first two general purpose pins control speed selection and + * 100Mbps loopback on the 82c168 chip. The control bits should always + * be set (to make the data pins outputs) and the speed selction and + * loopback bits set accordingly when changing media. Physically, this + * will set the state of a relay mounted on the card. + */ +#define DC_PN_GPIO_DATA0 0x000000001 +#define DC_PN_GPIO_DATA1 0x000000002 +#define DC_PN_GPIO_DATA2 0x000000004 +#define DC_PN_GPIO_DATA3 0x000000008 +#define DC_PN_GPIO_CTL0 0x000000010 +#define DC_PN_GPIO_CTL1 0x000000020 +#define DC_PN_GPIO_CTL2 0x000000040 +#define DC_PN_GPIO_CTL3 0x000000080 +#define DC_PN_GPIO_SPEEDSEL DC_PN_GPIO_DATA0/* 1 == 100Mbps, 0 == 10Mbps */ +#define DC_PN_GPIO_100TX_LOOP DC_PN_GPIO_DATA1/* 1 == normal, 0 == loop */ +#define DC_PN_GPIO_BNC_ENB DC_PN_GPIO_DATA2 +#define DC_PN_GPIO_100TX_LNK DC_PN_GPIO_DATA3 +#define DC_PN_GPIO_SETBIT(sc, r) \ + DC_SETBIT(sc, DC_PN_GPIO, ((r) | (r << 4))) +#define DC_PN_GPIO_CLRBIT(sc, r) \ + { \ + DC_SETBIT(sc, DC_PN_GPIO, ((r) << 4)); \ + DC_CLRBIT(sc, DC_PN_GPIO, (r)); \ + } + +/* shortcut MII access register */ +#define DC_PN_MII_DATA 0x0000FFFF +#define DC_PN_MII_RESERVER 0x00020000 +#define DC_PN_MII_REGADDR 0x007C0000 +#define DC_PN_MII_PHYADDR 0x0F800000 +#define DC_PN_MII_OPCODE 0x30000000 +#define DC_PN_MII_BUSY 0x80000000 + +#define DC_PN_MIIOPCODE_READ 0x60020000 +#define DC_PN_MIIOPCODE_WRITE 0x50020000 + +/* Internal NWAY bits */ +#define DC_PN_NWAY_RESET 0x00000001 /* reset */ +#define DC_PN_NWAY_PDOWN 0x00000002 /* power down */ +#define DC_PN_NWAY_BYPASS 0x00000004 /* bypass */ +#define DC_PN_NWAY_AUILOWCUR 0x00000008 /* AUI low current */ +#define DC_PN_NWAY_TPEXTEND 0x00000010 /* low squelch voltage */ +#define DC_PN_NWAY_POLARITY 0x00000020 /* 0 == on, 1 == off */ +#define DC_PN_NWAY_TP 0x00000040 /* 1 == tp, 0 == AUI */ +#define DC_PN_NWAY_AUIVOLT 0x00000080 /* 1 == full, 0 == half */ +#define DC_PN_NWAY_DUPLEX 0x00000100 /* LED, 1 == full, 0 == half */ +#define DC_PN_NWAY_LINKTEST 0x00000200 /* 0 == on, 1 == off */ +#define DC_PN_NWAY_AUTODETECT 0x00000400 /* 1 == off, 0 == on */ +#define DC_PN_NWAY_SPEEDSEL 0x00000800 /* LED, 0 = 10, 1 == 100 */ +#define DC_PN_NWAY_NWAY_ENB 0x00001000 /* 0 == off, 1 == on */ +#define DC_PN_NWAY_CAP10HDX 0x00002000 +#define DC_PN_NWAY_CAP10FDX 0x00004000 +#define DC_PN_NWAY_CAP100FDX 0x00008000 +#define DC_PN_NWAY_CAP100HDX 0x00010000 +#define DC_PN_NWAY_CAP100T4 0x00020000 +#define DC_PN_NWAY_ANEGRESTART 0x02000000 /* resets when aneg done */ +#define DC_PN_NWAY_REMFAULT 0x04000000 +#define DC_PN_NWAY_LPAR10HDX 0x08000000 +#define DC_PN_NWAY_LPAR10FDX 0x10000000 +#define DC_PN_NWAY_LPAR100FDX 0x20000000 +#define DC_PN_NWAY_LPAR100HDX 0x40000000 +#define DC_PN_NWAY_LPAR100T4 0x80000000 + +/* End of PNIC specific registers */ + +struct dc_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t dc_bhandle; /* bus space handle */ + bus_space_tag_t dc_btag; /* bus space tag */ + void *dc_intrhand; + struct resource *dc_irq; + struct resource *dc_res; + struct dc_type *dc_info; /* adapter info */ + device_t dc_miibus; + u_int8_t dc_unit; /* interface number */ + u_int8_t dc_type; + u_int8_t dc_pmode; + u_int8_t dc_link; + u_int8_t dc_cachesize; + int dc_pnic_rx_bug_save; + unsigned char *dc_pnic_rx_buf; + int dc_if_flags; + int dc_if_media; + u_int32_t dc_flags; + u_int32_t dc_txthresh; + struct dc_list_data *dc_ldata; + struct dc_chain_data dc_cdata; + struct callout_handle dc_stat_ch; +}; + +#define DC_TX_POLL 0x00000001 +#define DC_TX_COALESCE 0x00000002 +#define DC_TX_ADMTEK_WAR 0x00000004 +#define DC_TX_USE_TX_INTR 0x00000008 +#define DC_RX_FILTER_TULIP 0x00000010 +#define DC_TX_INTR_FIRSTFRAG 0x00000020 +#define DC_PNIC_RX_BUG_WAR 0x00000040 +#define DC_TX_FIXED_RING 0x00000080 +#define DC_TX_STORENFWD 0x00000100 +#define DC_REDUCED_MII_POLL 0x00000200 + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->dc_btag, sc->dc_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->dc_btag, sc->dc_bhandle, reg) + +#define DC_TIMEOUT 1000 +#define ETHER_ALIGN 2 + +/* + * General constants that are fun to know. + */ + +/* + * DEC PCI vendor ID + */ +#define DC_VENDORID_DEC 0x1011 + +/* + * DEC/Intel 21143 PCI device ID + */ +#define DC_DEVICEID_21143 0x0019 + +/* + * Macronix PCI vendor ID + */ +#define DC_VENDORID_MX 0x10D9 + +/* + * Macronix PMAC device IDs. + */ +#define DC_DEVICEID_98713 0x0512 +#define DC_DEVICEID_987x5 0x0531 + +/* Macronix PCI revision codes. */ +#define DC_REVISION_98713 0x00 +#define DC_REVISION_98713A 0x10 +#define DC_REVISION_98715 0x20 +#define DC_REVISION_98725 0x30 + +/* + * Compex PCI vendor ID. + */ +#define DC_VENDORID_CP 0x11F6 + +/* + * Compex PMAC PCI device IDs. + */ +#define DC_DEVICEID_98713_CP 0x9881 + +/* + * Lite-On PNIC PCI vendor ID + */ +#define DC_VENDORID_LO 0x11AD + +/* + * 82c168/82c169 PNIC device IDs. Both chips have the same device + * ID but different revisions. Revision 0x10 is the 82c168, and + * 0x20 is the 82c169. + */ +#define DC_DEVICEID_82C168 0x0002 + +#define DC_REVISION_82C168 0x10 +#define DC_REVISION_82C169 0x20 + +/* + * Lite-On PNIC II device ID. Note: this is actually a Macronix 98715A + * with wake on lan/magic packet support. + */ +#define DC_DEVICEID_82C115 0xc115 + +/* + * Davicom vendor ID. + */ +#define DC_VENDORID_DAVICOM 0x1282 + +/* + * Davicom device IDs. + */ +#define DC_DEVICEID_DM9100 0x9100 +#define DC_DEVICEID_DM9102 0x9102 + +/* + * ADMtek vendor ID. + */ +#define DC_VENDORID_ADMTEK 0x1317 + +/* + * ADMtek device IDs. + */ +#define DC_DEVICEID_AL981 0x0981 +#define DC_DEVICEID_AN985 0x0985 + +/* + * ASIX vendor ID. + */ +#define DC_VENDORID_ASIX 0x125B + +/* + * ASIX device IDs. + */ +#define DC_DEVICEID_AX88140A 0x1400 + +/* + * The ASIX AX88140 and ASIX AX88141 have the same vendor and + * device IDs but different revision values. + */ +#define DC_REVISION_88140 0x00 +#define DC_REVISION_88141 0x10 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define DC_PCI_CFID 0x00 /* Id */ +#define DC_PCI_CFCS 0x04 /* Command and status */ +#define DC_PCI_CFRV 0x08 /* Revision */ +#define DC_PCI_CFLT 0x0C /* Latency timer */ +#define DC_PCI_CFBIO 0x10 /* Base I/O address */ +#define DC_PCI_CFBMA 0x14 /* Base memory address */ +#define DC_PCI_CCIS 0x28 /* Card info struct */ +#define DC_PCI_CSID 0x2C /* Subsystem ID */ +#define DC_PCI_CBER 0x30 /* Expansion ROM base address */ +#define DC_PCI_CCAP 0x34 /* Caps pointer - PD/TD chip only */ +#define DC_PCI_CFIT 0x3C /* Interrupt */ +#define DC_PCI_CFDD 0x40 /* Device and driver area */ +#define DC_PCI_CWUA0 0x44 /* Wake-Up LAN addr 0 */ +#define DC_PCI_CWUA1 0x48 /* Wake-Up LAN addr 1 */ +#define DC_PCI_SOP0 0x4C /* SecureON passwd 0 */ +#define DC_PCI_SOP1 0x50 /* SecureON passwd 1 */ +#define DC_PCI_CWUC 0x54 /* Configuration Wake-Up cmd */ +#define DC_PCI_CCID 0xDC /* Capability ID - PD/TD only */ +#define DC_PCI_CPMC 0xE0 /* Pwrmgmt ctl & sts - PD/TD only */ + +/* PCI ID register */ +#define DC_CFID_VENDOR 0x0000FFFF +#define DC_CFID_DEVICE 0xFFFF0000 + +/* PCI command/status register */ +#define DC_CFCS_IOSPACE 0x00000001 /* I/O space enable */ +#define DC_CFCS_MEMSPACE 0x00000002 /* memory space enable */ +#define DC_CFCS_BUSMASTER 0x00000004 /* bus master enable */ +#define DC_CFCS_MWI_ENB 0x00000008 /* mem write and inval enable */ +#define DC_CFCS_PARITYERR_ENB 0x00000020 /* parity error enable */ +#define DC_CFCS_SYSERR_ENB 0x00000080 /* system error enable */ +#define DC_CFCS_NEWCAPS 0x00100000 /* new capabilities */ +#define DC_CFCS_FAST_B2B 0x00800000 /* fast back-to-back capable */ +#define DC_CFCS_DATAPARITY 0x01000000 /* Parity error report */ +#define DC_CFCS_DEVSELTIM 0x06000000 /* devsel timing */ +#define DC_CFCS_TGTABRT 0x10000000 /* received target abort */ +#define DC_CFCS_MASTERABRT 0x20000000 /* received master abort */ +#define DC_CFCS_SYSERR 0x40000000 /* asserted system error */ +#define DC_CFCS_PARITYERR 0x80000000 /* asserted parity error */ + +/* PCI revision register */ +#define DC_CFRV_STEPPING 0x0000000F +#define DC_CFRV_REVISION 0x000000F0 +#define DC_CFRV_SUBCLASS 0x00FF0000 +#define DC_CFRV_BASECLASS 0xFF000000 + +#define DC_21143_PB_REV 0x00000030 +#define DC_21143_TB_REV 0x00000030 +#define DC_21143_PC_REV 0x00000030 +#define DC_21143_TC_REV 0x00000030 +#define DC_21143_PD_REV 0x00000041 +#define DC_21143_TD_REV 0x00000041 + +/* PCI latency timer register */ +#define DC_CFLT_CACHELINESIZE 0x000000FF +#define DC_CFLT_LATENCYTIMER 0x0000FF00 + +/* PCI subsystem ID register */ +#define DC_CSID_VENDOR 0x0000FFFF +#define DC_CSID_DEVICE 0xFFFF0000 + +/* PCI cababilities pointer */ +#define DC_CCAP_OFFSET 0x000000FF + +/* PCI interrupt config register */ +#define DC_CFIT_INTLINE 0x000000FF +#define DC_CFIT_INTPIN 0x0000FF00 +#define DC_CFIT_MIN_GNT 0x00FF0000 +#define DC_CFIT_MAX_LAT 0xFF000000 + +/* PCI capability register */ +#define DC_CCID_CAPID 0x000000FF +#define DC_CCID_NEXTPTR 0x0000FF00 +#define DC_CCID_PM_VERS 0x00070000 +#define DC_CCID_PME_CLK 0x00080000 +#define DC_CCID_DVSPEC_INT 0x00200000 +#define DC_CCID_STATE_D1 0x02000000 +#define DC_CCID_STATE_D2 0x04000000 +#define DC_CCID_PME_D0 0x08000000 +#define DC_CCID_PME_D1 0x10000000 +#define DC_CCID_PME_D2 0x20000000 +#define DC_CCID_PME_D3HOT 0x40000000 +#define DC_CCID_PME_D3COLD 0x80000000 + +/* PCI power management control/status register */ +#define DC_CPMC_STATE 0x00000003 +#define DC_CPMC_PME_ENB 0x00000100 +#define DC_CPMC_PME_STS 0x00008000 + +#define DC_PSTATE_D0 0x0 +#define DC_PSTATE_D1 0x1 +#define DC_PSTATE_D2 0x2 +#define DC_PSTATE_D3 0x3 + +/* Device specific region */ +/* Configuration and driver area */ +#define DC_CFDD_DRVUSE 0x0000FFFF +#define DC_CFDD_SNOOZE_MODE 0x40000000 +#define DC_CFDD_SLEEP_MODE 0x80000000 + +/* Configuration wake-up command register */ +#define DC_CWUC_MUST_BE_ZERO 0x00000001 +#define DC_CWUC_SECUREON_ENB 0x00000002 +#define DC_CWUC_FORCE_WUL 0x00000004 +#define DC_CWUC_BNC_ABILITY 0x00000008 +#define DC_CWUC_AUI_ABILITY 0x00000010 +#define DC_CWUC_TP10_ABILITY 0x00000020 +#define DC_CWUC_MII_ABILITY 0x00000040 +#define DC_CWUC_SYM_ABILITY 0x00000080 +#define DC_CWUC_LOCK 0x00000100 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/dev/dc/pnphy.c b/sys/dev/dc/pnphy.c new file mode 100644 index 0000000..0f8498e --- /dev/null +++ b/sys/dev/dc/pnphy.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * Pseudo-driver for media selection on the Lite-On PNIC 82c168 + * chip. The NWAY support on this chip is horribly broken, so we + * only support manual mode selection. This is lame, but getting + * NWAY to work right is amazingly difficult. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/miidevs.h> + +#include <machine/clock.h> +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> + +#include <pci/if_dcreg.h> + +#include "miibus_if.h" + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#define DC_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | x) + +#define DC_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~x) + +static int pnphy_probe __P((device_t)); +static int pnphy_attach __P((device_t)); +static int pnphy_detach __P((device_t)); + +static device_method_t pnphy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, pnphy_probe), + DEVMETHOD(device_attach, pnphy_attach), + DEVMETHOD(device_detach, pnphy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static devclass_t pnphy_devclass; + +static driver_t pnphy_driver = { + "pnphy", + pnphy_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(pnphy, miibus, pnphy_driver, pnphy_devclass, 0, 0); + +int pnphy_service __P((struct mii_softc *, struct mii_data *, int)); +void pnphy_status __P((struct mii_softc *)); + +static int pnphy_probe(dev) + device_t dev; +{ + struct mii_attach_args *ma; + + ma = device_get_ivars(dev); + + /* + * The dc driver will report the 82c168 vendor and device + * ID to let us know that it wants us to attach. + */ + if (ma->mii_id1 != DC_VENDORID_LO || + ma->mii_id2 != DC_DEVICEID_82C168) + return(ENXIO); + + device_set_desc(dev, "PNIC 82c168 media interface"); + + return (0); +} + +static int pnphy_attach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = pnphy_service; + sc->mii_pdata = mii; + + sc->mii_flags |= MIIF_NOISOLATE; + mii->mii_instance++; + +#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) + + sc->mii_capabilities = + BMSR_100TXFDX|BMSR_100TXHDX|BMSR_10TFDX|BMSR_10THDX; + sc->mii_capabilities &= ma->mii_capmask; + device_printf(dev, " "); + if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) + printf("no media present"); + else + mii_add_media(mii, sc->mii_capabilities, sc->mii_inst); + printf("\n"); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), + BMCR_ISO); + + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), + BMCR_LOOP|BMCR_S100); + +#undef ADD + + MIIBUS_MEDIAINIT(sc->mii_dev); + return(0); +} + +static int pnphy_detach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(device_get_parent(dev)); + sc->mii_dev = NULL; + LIST_REMOVE(sc, mii_list); + + return(0); +} + +int +pnphy_service(sc, mii, cmd) + struct mii_softc *sc; + struct mii_data *mii; + int cmd; +{ + struct dc_softc *dc_sc; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + + dc_sc = mii->mii_ifp->if_softc; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + return (0); + } + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + sc->mii_flags = 0; + + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: + /* NWAY is busted on this chip */ + case IFM_100_T4: + /* + * XXX Not supported as a manual setting right now. + */ + return (EINVAL); + case IFM_100_TX: + mii->mii_media_active = IFM_ETHER|IFM_100_TX; + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + mii->mii_media_active |= IFM_FDX; + MIIBUS_STATCHG(sc->mii_dev); + return(0); + break; + case IFM_10_T: + mii->mii_media_active = IFM_ETHER|IFM_10_T; + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + mii->mii_media_active |= IFM_FDX; + MIIBUS_STATCHG(sc->mii_dev); + return(0); + break; + default: + return(EINVAL); + break; + } + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) + return (0); + + /* + * Is the interface even up? + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + return (0); + + return(0); + } + + /* Update the media status. */ + pnphy_status(sc); + + /* Callback if something changed. */ + if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { + MIIBUS_STATCHG(sc->mii_dev); + sc->mii_active = mii->mii_media_active; + } + return (0); +} + +void +pnphy_status(sc) + struct mii_softc *sc; +{ + struct mii_data *mii = sc->mii_pdata; + int reg; + struct dc_softc *dc_sc; + + dc_sc = mii->mii_ifp->if_softc; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + reg = CSR_READ_4(dc_sc, DC_ISR); + + if (!(reg & DC_ISR_LINKFAIL)) + mii->mii_media_status |= IFM_ACTIVE; + + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) + mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_100_TX; + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) + mii->mii_media_active |= IFM_FDX; + + return; +} diff --git a/sys/dev/mii/dcphy.c b/sys/dev/mii/dcphy.c new file mode 100644 index 0000000..2543e87 --- /dev/null +++ b/sys/dev/mii/dcphy.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * Pseudo-driver for internal NWAY support on DEC 21143 and workalike + * controllers. Technically we're abusing the miibus code to handle + * media selection and NWAY support here since there is no MII + * interface. However the logical operations are roughly the same, + * and the alternative is to create a fake MII interface in the driver, + * which is harder to do. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/miidevs.h> + +#include <machine/clock.h> +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> + +#include <pci/pcivar.h> + +#include <pci/if_dcreg.h> + +#include "miibus_if.h" + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#define DC_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | x) + +#define DC_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~x) + +#define MIIF_AUTOTIMEOUT 0x0004 + +static int dcphy_probe __P((device_t)); +static int dcphy_attach __P((device_t)); +static int dcphy_detach __P((device_t)); + +static device_method_t dcphy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, dcphy_probe), + DEVMETHOD(device_attach, dcphy_attach), + DEVMETHOD(device_detach, dcphy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static devclass_t dcphy_devclass; + +static driver_t dcphy_driver = { + "dcphy", + dcphy_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(dcphy, miibus, dcphy_driver, dcphy_devclass, 0, 0); + +int dcphy_service __P((struct mii_softc *, struct mii_data *, int)); +void dcphy_status __P((struct mii_softc *)); +static int dcphy_auto __P((struct mii_softc *, int)); +static void dcphy_reset __P((struct mii_softc *)); + +static int dcphy_probe(dev) + device_t dev; +{ + struct mii_attach_args *ma; + + ma = device_get_ivars(dev); + + /* + * The dc driver will report the 21143 vendor and device + * ID to let us know that it wants us to attach. + */ + if (ma->mii_id1 != DC_VENDORID_DEC || + ma->mii_id2 != DC_DEVICEID_21143) + return(ENXIO); + + device_set_desc(dev, "Intel 21143 NWAY media interface"); + + return (0); +} + +static int dcphy_attach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + struct dc_softc *dc_sc; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = dcphy_service; + sc->mii_pdata = mii; + + sc->mii_flags |= MIIF_NOISOLATE; + mii->mii_instance++; + +#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) + + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), + BMCR_ISO); + + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), + BMCR_LOOP|BMCR_S100); + + /*dcphy_reset(sc);*/ + dc_sc = mii->mii_ifp->if_softc; + CSR_WRITE_4(dc_sc, DC_10BTSTAT, 0); + CSR_WRITE_4(dc_sc, DC_10BTCTRL, 0); + + switch(pci_read_config(device_get_parent(sc->mii_dev), + DC_PCI_CSID, 4)) { + case 0x99999999: + /* Example of how to only allow 10Mbps modes. */ + sc->mii_capabilities = BMSR_10TFDX|BMSR_10THDX; + break; + default: + sc->mii_capabilities = + BMSR_ANEG|BMSR_100TXFDX|BMSR_100TXHDX| + BMSR_10TFDX|BMSR_10THDX; + break; + } + + sc->mii_capabilities &= ma->mii_capmask; + device_printf(dev, " "); + if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) + printf("no media present"); + else + mii_add_media(mii, sc->mii_capabilities, sc->mii_inst); + printf("\n"); +#undef ADD + + MIIBUS_MEDIAINIT(sc->mii_dev); + return(0); +} + +static int dcphy_detach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(device_get_parent(dev)); + sc->mii_dev = NULL; + LIST_REMOVE(sc, mii_list); + + return(0); +} + +int +dcphy_service(sc, mii, cmd) + struct mii_softc *sc; + struct mii_data *mii; + int cmd; +{ + struct dc_softc *dc_sc; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + int reg; + u_int32_t mode; + + dc_sc = mii->mii_ifp->if_softc; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + return (0); + } + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + return (0); + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + sc->mii_flags = 0; + mii->mii_media_active = IFM_NONE; + mode = CSR_READ_4(dc_sc, DC_NETCFG); + mode &= ~(DC_NETCFG_FULLDUPLEX|DC_NETCFG_PORTSEL| + DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER|DC_NETCFG_SPEEDSEL); + + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: + /*dcphy_reset(sc);*/ + (void) dcphy_auto(sc, 0); + break; + case IFM_100_T4: + /* + * XXX Not supported as a manual setting right now. + */ + return (EINVAL); + case IFM_100_TX: + dcphy_reset(sc); + DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + mode |= DC_NETCFG_PORTSEL|DC_NETCFG_PCS| + DC_NETCFG_SCRAMBLER; + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + mode |= DC_NETCFG_FULLDUPLEX; + else + mode &= ~DC_NETCFG_FULLDUPLEX; + CSR_WRITE_4(dc_sc, DC_NETCFG, mode); + break; + case IFM_10_T: + DC_CLRBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(dc_sc, DC_10BTCTRL, 0xFFFF); + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3D); + else + DC_SETBIT(dc_sc, DC_10BTCTRL, 0x7F3F); + DC_SETBIT(dc_sc, DC_SIARESET, DC_SIA_RESET); + DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + mode &= ~DC_NETCFG_PORTSEL; + mode |= DC_NETCFG_SPEEDSEL; + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + mode |= DC_NETCFG_FULLDUPLEX; + else + mode &= ~DC_NETCFG_FULLDUPLEX; + CSR_WRITE_4(dc_sc, DC_NETCFG, mode); + break; + default: + return(EINVAL); + break; + } + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) + return (0); + + /* + * Is the interface even up? + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + return (0); + + if (sc->mii_flags & MIIF_DOINGAUTO) { + if (++sc->mii_ticks != 5) + return(0); + else { + sc->mii_ticks = 0; + sc->mii_flags &= ~MIIF_DOINGAUTO; + sc->mii_flags |= MIIF_AUTOTIMEOUT; + } + } + + sc->mii_flags &= ~MIIF_DOINGAUTO; + + /* + * Check to see if we have link. If we do, we don't + * need to restart the autonegotiation process. Read + * the BMSR twice in case it's latched. + */ + reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & + (DC_TSTAT_LS10|DC_TSTAT_LS100); + + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX && + !(reg & DC_TSTAT_LS100)) { + if (sc->mii_flags & MIIF_AUTOTIMEOUT) { + sc->mii_flags &= ~MIIF_AUTOTIMEOUT; + break; + } else + return(0); + } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T && + !(reg & DC_TSTAT_LS10)) { + if (sc->mii_flags & MIIF_AUTOTIMEOUT) { + sc->mii_flags &= ~MIIF_AUTOTIMEOUT; + break; + } else + return(0); + } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_NONE && + (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100))) { + if (sc->mii_flags & MIIF_AUTOTIMEOUT) { + sc->mii_flags &= ~MIIF_AUTOTIMEOUT; + break; + } else + return(0); + } else if (CSR_READ_4(dc_sc, DC_ISR) & DC_ISR_LINKGOOD) { + if (sc->mii_flags & MIIF_AUTOTIMEOUT) { + sc->mii_flags &= ~MIIF_AUTOTIMEOUT; + break; + } else + return(0); + } + + sc->mii_ticks = 0; + /*dcphy_reset(sc);*/ + dcphy_auto(sc, 0); + + break; + } + + /* Update the media status. */ + dcphy_status(sc); + + /* Callback if something changed. */ + if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { + MIIBUS_STATCHG(sc->mii_dev); + sc->mii_active = mii->mii_media_active; + } + return (0); +} + +void +dcphy_status(sc) + struct mii_softc *sc; +{ + struct mii_data *mii = sc->mii_pdata; + int reg, anlpar; + struct dc_softc *dc_sc; + + dc_sc = mii->mii_ifp->if_softc; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + reg = CSR_READ_4(dc_sc, DC_10BTSTAT) & + (DC_TSTAT_LS10|DC_TSTAT_LS100); + + if (!(reg & DC_TSTAT_LS10) || !(reg & DC_TSTAT_LS100)) + mii->mii_media_status |= IFM_ACTIVE; + + if (sc->mii_flags & MIIF_DOINGAUTO) { + mii->mii_media_active |= IFM_NONE; + return; + } + + if (CSR_READ_4(dc_sc, DC_10BTCTRL) & DC_TCTL_AUTONEGENBL && + CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) { + /* Erg, still trying, I guess... */ + if ((CSR_READ_4(dc_sc, DC_10BTSTAT) & + DC_ASTAT_AUTONEGCMP) != DC_ASTAT_AUTONEGCMP) { + mii->mii_media_active |= IFM_NONE; + return; + } + + if (CSR_READ_4(dc_sc, DC_10BTSTAT) & DC_TSTAT_LP_CAN_NWAY) { + anlpar = CSR_READ_4(dc_sc, DC_10BTSTAT) >> 16; + if (anlpar & ANLPAR_T4) + mii->mii_media_active |= IFM_100_T4; + else if (anlpar & ANLPAR_TX_FD) + mii->mii_media_active |= IFM_100_TX|IFM_FDX; + else if (anlpar & ANLPAR_TX) + mii->mii_media_active |= IFM_100_TX; + else if (anlpar & ANLPAR_10_FD) + mii->mii_media_active |= IFM_10_T|IFM_FDX; + else if (anlpar & ANLPAR_10) + mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_NONE; + if (DC_IS_INTEL(dc_sc)) + DC_CLRBIT(dc_sc, DC_10BTCTRL, + DC_TCTL_AUTONEGENBL); + return; + } + /* + * If the other side doesn't support NWAY, then the + * best we can do is determine if we have a 10Mbps or + * 100Mbps link. There's no way to know if the link + * is full or half duplex, so we default to half duplex + * and hope that the user is clever enough to manually + * change the media settings if we're wrong. + */ + if (!(reg & DC_TSTAT_LS100)) + mii->mii_media_active |= IFM_100_TX; + else if (!(reg & DC_TSTAT_LS10)) + mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_NONE; + if (DC_IS_INTEL(dc_sc)) + DC_CLRBIT(dc_sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + return; + } + + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SCRAMBLER) + mii->mii_media_active |= IFM_100_TX; + else + mii->mii_media_active |= IFM_10_T; + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) + mii->mii_media_active |= IFM_FDX; + + return; +} + +static int +dcphy_auto(mii, waitfor) + struct mii_softc *mii; + int waitfor; +{ + int i; + struct dc_softc *sc; + + sc = mii->mii_pdata->mii_ifp->if_softc; + + if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); + CSR_WRITE_4(sc, DC_10BTCTRL, 0x3FFFF); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + DC_SETBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); + DC_SETBIT(sc, DC_10BTSTAT, DC_ASTAT_TXDISABLE); + } + + if (waitfor) { + /* Wait 500ms for it to complete. */ + for (i = 0; i < 500; i++) { + if ((CSR_READ_4(sc, DC_10BTSTAT) & DC_TSTAT_ANEGSTAT) + == DC_ASTAT_AUTONEGCMP) + return(0); + DELAY(1000); + } + /* + * Don't need to worry about clearing MIIF_DOINGAUTO. + * If that's set, a timeout is pending, and it will + * clear the flag. + */ + return(EIO); + } + + /* + * Just let it finish asynchronously. This is for the benefit of + * the tick handler driving autonegotiation. Don't want 500ms + * delays all the time while the system is running! + */ + if ((mii->mii_flags & MIIF_DOINGAUTO) == 0) + mii->mii_flags |= MIIF_DOINGAUTO; + + return(EJUSTRETURN); +} + +static void +dcphy_reset(mii) + struct mii_softc *mii; +{ + struct dc_softc *sc; + + sc = mii->mii_pdata->mii_ifp->if_softc; + + DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); + DELAY(1000); + DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); + + return; +} + diff --git a/sys/dev/mii/pnphy.c b/sys/dev/mii/pnphy.c new file mode 100644 index 0000000..0f8498e --- /dev/null +++ b/sys/dev/mii/pnphy.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * Pseudo-driver for media selection on the Lite-On PNIC 82c168 + * chip. The NWAY support on this chip is horribly broken, so we + * only support manual mode selection. This is lame, but getting + * NWAY to work right is amazingly difficult. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/module.h> +#include <sys/bus.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/miidevs.h> + +#include <machine/clock.h> +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> + +#include <pci/if_dcreg.h> + +#include "miibus_if.h" + +#if !defined(lint) +static const char rcsid[] = + "$FreeBSD$"; +#endif + +#define DC_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) | x) + +#define DC_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, \ + CSR_READ_4(sc, reg) & ~x) + +static int pnphy_probe __P((device_t)); +static int pnphy_attach __P((device_t)); +static int pnphy_detach __P((device_t)); + +static device_method_t pnphy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, pnphy_probe), + DEVMETHOD(device_attach, pnphy_attach), + DEVMETHOD(device_detach, pnphy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + { 0, 0 } +}; + +static devclass_t pnphy_devclass; + +static driver_t pnphy_driver = { + "pnphy", + pnphy_methods, + sizeof(struct mii_softc) +}; + +DRIVER_MODULE(pnphy, miibus, pnphy_driver, pnphy_devclass, 0, 0); + +int pnphy_service __P((struct mii_softc *, struct mii_data *, int)); +void pnphy_status __P((struct mii_softc *)); + +static int pnphy_probe(dev) + device_t dev; +{ + struct mii_attach_args *ma; + + ma = device_get_ivars(dev); + + /* + * The dc driver will report the 82c168 vendor and device + * ID to let us know that it wants us to attach. + */ + if (ma->mii_id1 != DC_VENDORID_LO || + ma->mii_id2 != DC_DEVICEID_82C168) + return(ENXIO); + + device_set_desc(dev, "PNIC 82c168 media interface"); + + return (0); +} + +static int pnphy_attach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_attach_args *ma; + struct mii_data *mii; + + sc = device_get_softc(dev); + ma = device_get_ivars(dev); + sc->mii_dev = device_get_parent(dev); + mii = device_get_softc(sc->mii_dev); + LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_service = pnphy_service; + sc->mii_pdata = mii; + + sc->mii_flags |= MIIF_NOISOLATE; + mii->mii_instance++; + +#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL) + + sc->mii_capabilities = + BMSR_100TXFDX|BMSR_100TXHDX|BMSR_10TFDX|BMSR_10THDX; + sc->mii_capabilities &= ma->mii_capmask; + device_printf(dev, " "); + if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0) + printf("no media present"); + else + mii_add_media(mii, sc->mii_capabilities, sc->mii_inst); + printf("\n"); + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), + BMCR_ISO); + + ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst), + BMCR_LOOP|BMCR_S100); + +#undef ADD + + MIIBUS_MEDIAINIT(sc->mii_dev); + return(0); +} + +static int pnphy_detach(dev) + device_t dev; +{ + struct mii_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + mii = device_get_softc(device_get_parent(dev)); + sc->mii_dev = NULL; + LIST_REMOVE(sc, mii_list); + + return(0); +} + +int +pnphy_service(sc, mii, cmd) + struct mii_softc *sc; + struct mii_data *mii; + int cmd; +{ + struct dc_softc *dc_sc; + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + + dc_sc = mii->mii_ifp->if_softc; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + return (0); + } + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + sc->mii_flags = 0; + + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: + /* NWAY is busted on this chip */ + case IFM_100_T4: + /* + * XXX Not supported as a manual setting right now. + */ + return (EINVAL); + case IFM_100_TX: + mii->mii_media_active = IFM_ETHER|IFM_100_TX; + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + mii->mii_media_active |= IFM_FDX; + MIIBUS_STATCHG(sc->mii_dev); + return(0); + break; + case IFM_10_T: + mii->mii_media_active = IFM_ETHER|IFM_10_T; + if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) + mii->mii_media_active |= IFM_FDX; + MIIBUS_STATCHG(sc->mii_dev); + return(0); + break; + default: + return(EINVAL); + break; + } + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) + return (0); + + /* + * Is the interface even up? + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + return (0); + + return(0); + } + + /* Update the media status. */ + pnphy_status(sc); + + /* Callback if something changed. */ + if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) { + MIIBUS_STATCHG(sc->mii_dev); + sc->mii_active = mii->mii_media_active; + } + return (0); +} + +void +pnphy_status(sc) + struct mii_softc *sc; +{ + struct mii_data *mii = sc->mii_pdata; + int reg; + struct dc_softc *dc_sc; + + dc_sc = mii->mii_ifp->if_softc; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + reg = CSR_READ_4(dc_sc, DC_ISR); + + if (!(reg & DC_ISR_LINKFAIL)) + mii->mii_media_status |= IFM_ACTIVE; + + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_SPEEDSEL) + mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_100_TX; + if (CSR_READ_4(dc_sc, DC_NETCFG) & DC_NETCFG_FULLDUPLEX) + mii->mii_media_active |= IFM_FDX; + + return; +} diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 9f6702e..dcdee22 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -175,18 +175,14 @@ device ppi0 # Parallel port interface device # PCI Ethernet NICs. -device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) -device pn0 # Lite-On 82c168/82c169 (``PNIC'') device tx0 # SMC 9432TX (83c170 ``EPIC'') device vx0 # 3Com 3c590, 3c595 (``Vortex'') # PCI Ethernet NICs that use the common MII bus controller code. controller miibus0 # MII bus support -device al0 # ADMtek AL981/AN985 (``Comet''/``Centaur'') -device dm0 # Davicom DM9100/DM9102 -device mx0 # Macronix 98713/98715/98725 (``PMAC'') +device dc0 # DEC/Intel 21143 and various workalikes device rl0 # RealTek 8129/8139 device sf0 # Adaptec AIC-6915 (``Starfire'') device sis0 # Silicon Integrated Systems SiS 900/SiS 7016 diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 70df1ae..c52b4c1 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -1646,31 +1646,20 @@ controller miibus0 # nd 1040B PCI SCSI host adapters, as well as the Qlogic ISP 2100 # FC/AL Host Adapter. # -# The `al' device provides support for PCI fast ethernet adapters -# based on the ADMtek Inc. AL981 "Comet" and the AN985 "Centaur" chips. -# -# The `ax' device provides support for PCI fast ethernet adapters -# based on the ASIX Electronics AX88140A chip, including the Alfa -# Inc. GFC2204. +# The `dc' device provides support for PCI fast ethernet adapters +# based on the DEC/Intel 21143 and various workalikes including: +# the ADMtek AL981 Comet and AN985 Centaur, the ASIX Electronics +# AX88140A and AX88141, the Davicom DM9100 and DM9102, the Lite-On +# 82c168 and 82c169 PNIC, the Lite-On/Macronix LC82C115 PNIC II +# and the Macronix 98713/98713A/98715/98715A/98725 PMAC. This driver +# replaces the old al, ax, dm, pn and mx drivers. # # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # -# The `dm' device provides support for PCI fast ethernet adapters -# based on the the Davicom DM9100 and DM9102 controller chips, including -# the Jaton Corporation XPressNet. -# # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # -# The `mx' device provides support for various fast ethernet adapters -# based on the Macronix 98713, 987615 and 98725 series chips. -# -# The `pn' device provides support for various fast ethernet adapters -# based on the Lite-On 82c168 and 82c169 PNIC chips, including the -# LinkSys LNE100TX, the NetGear FA310TX rev. D1 and the Matrox -# FastNIC 10/100. -# # The 'rl' device provides support for PCI fast ethernet adapters based # on the RealTek 8129/8139 chipset. Note that the RealTek driver defaults # to using programmed I/O to do register accesses because memory mapped @@ -1852,13 +1841,9 @@ options SCSI_ISP_WWN="0x5000000099990000" #options ISP_COMPILE_2100_FW=1 #options ISP_COMPILE_2200_FW=1 -device al0 -device ax0 +device dc0 device de0 -device dm0 device fxp0 -device mx0 -device pn0 device rl0 device sf0 device sis0 diff --git a/sys/i386/conf/NEWCARD b/sys/i386/conf/NEWCARD index 9f90731..bae4f12 100644 --- a/sys/i386/conf/NEWCARD +++ b/sys/i386/conf/NEWCARD @@ -169,13 +169,9 @@ device ppi0 # Parallel port interface device controller miibus0 # PCI Ethernet NICs. -device al0 # ADMtek AL981 (``Comet'') -device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') -device dm0 # Davicom DM9100/DM9102 +device dc0 # DEC/Intel 21143 and various workalikes device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) -device mx0 # Macronix 98713/98715/98725 (``PMAC'') -device pn0 # Lite-On 82c168/82c169 (``PNIC'') device rl0 # RealTek 8129/8139 device sf0 # Adaptec AIC-6915 (``Starfire'') device sis0 # Silicon Integrated Systems SiS 900/SiS 7016 diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 70df1ae..c52b4c1 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -1646,31 +1646,20 @@ controller miibus0 # nd 1040B PCI SCSI host adapters, as well as the Qlogic ISP 2100 # FC/AL Host Adapter. # -# The `al' device provides support for PCI fast ethernet adapters -# based on the ADMtek Inc. AL981 "Comet" and the AN985 "Centaur" chips. -# -# The `ax' device provides support for PCI fast ethernet adapters -# based on the ASIX Electronics AX88140A chip, including the Alfa -# Inc. GFC2204. +# The `dc' device provides support for PCI fast ethernet adapters +# based on the DEC/Intel 21143 and various workalikes including: +# the ADMtek AL981 Comet and AN985 Centaur, the ASIX Electronics +# AX88140A and AX88141, the Davicom DM9100 and DM9102, the Lite-On +# 82c168 and 82c169 PNIC, the Lite-On/Macronix LC82C115 PNIC II +# and the Macronix 98713/98713A/98715/98715A/98725 PMAC. This driver +# replaces the old al, ax, dm, pn and mx drivers. # # The `de' device provides support for the Digital Equipment DC21040 # self-contained Ethernet adapter. # -# The `dm' device provides support for PCI fast ethernet adapters -# based on the the Davicom DM9100 and DM9102 controller chips, including -# the Jaton Corporation XPressNet. -# # The `fxp' device provides support for the Intel EtherExpress Pro/100B # PCI Fast Ethernet adapters. # -# The `mx' device provides support for various fast ethernet adapters -# based on the Macronix 98713, 987615 and 98725 series chips. -# -# The `pn' device provides support for various fast ethernet adapters -# based on the Lite-On 82c168 and 82c169 PNIC chips, including the -# LinkSys LNE100TX, the NetGear FA310TX rev. D1 and the Matrox -# FastNIC 10/100. -# # The 'rl' device provides support for PCI fast ethernet adapters based # on the RealTek 8129/8139 chipset. Note that the RealTek driver defaults # to using programmed I/O to do register accesses because memory mapped @@ -1852,13 +1841,9 @@ options SCSI_ISP_WWN="0x5000000099990000" #options ISP_COMPILE_2100_FW=1 #options ISP_COMPILE_2200_FW=1 -device al0 -device ax0 +device dc0 device de0 -device dm0 device fxp0 -device mx0 -device pn0 device rl0 device sf0 device sis0 diff --git a/sys/i386/conf/PCCARD b/sys/i386/conf/PCCARD index e783794..b8bb803 100644 --- a/sys/i386/conf/PCCARD +++ b/sys/i386/conf/PCCARD @@ -159,18 +159,14 @@ device ppi0 # Parallel port interface device # PCI Ethernet NICs. -device ax0 # ASIX AX88140A device de0 # DEC/Intel DC21x4x (``Tulip'') device fxp0 # Intel EtherExpress PRO/100B (82557, 82558) -device pn0 # Lite-On 82c168/82c169 (``PNIC'') device tx0 # SMC 9432TX (83c170 ``EPIC'') device vx0 # 3Com 3c590, 3c595 (``Vortex'') # PCI Ethernet NICs that use the common MII bus controller code. controller miibus0 # MII bus support -device al0 # ADMtek AL981/AN985 (``Comet''/``Centaur'') -device dm0 # Davicom DM9100/DM9102 -device mx0 # Macronix 98713/98715/98725 (``PMAC'') +device dc0 # DEC/Intrl 21143 and various workalikes device rl0 # RealTek 8129/8139 device sf0 # Adaptec AIC-6915 (``Starfire'') device sis0 # Silicon Integrated Systems SiS 900/SiS 7016 diff --git a/sys/i386/i386/userconfig.c b/sys/i386/i386/userconfig.c index 909303d..7ac1d89 100644 --- a/sys/i386/i386/userconfig.c +++ b/sys/i386/i386/userconfig.c @@ -401,14 +401,10 @@ static DEV_INFO device_info[] = { {"xe", "Xircom PC Card Ethernet adapter", 0, CLS_NETWORK}, {"ze", "IBM/National Semiconductor PCMCIA Ethernet adapter",0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, -{"al", "ADMtek AL981/AN985 ethernet adapter", FLG_FIXED, CLS_NETWORK}, -{"ax", "ASIX AX88140A ethernet adapter", FLG_FIXED, CLS_NETWORK}, +{"dc", "DEC/Intel 21143 or clone Ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK}, -{"dm", "Davicom DM910x Ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"fpa", "DEC DEFPA PCI FDDI adapter", FLG_FIXED, CLS_NETWORK}, {"rl", "RealTek 8129/8139 ethernet adapter", FLG_FIXED, CLS_NETWORK}, -{"mx", "Macronix PMAC ethernet adapter", FLG_FIXED, CLS_NETWORK}, -{"pn", "Lite-On 82c168/82c169 PNIC adapter", FLG_FIXED, CLS_NETWORK}, {"tl", "Texas Instruments ThunderLAN ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"vr", "VIA Rhine/Rhine II ethernet adapter", FLG_FIXED, CLS_NETWORK}, {"wb", "Winbond W89C840F ethernet adapter", FLG_FIXED, CLS_NETWORK}, diff --git a/sys/modules/Makefile b/sys/modules/Makefile index bc97db6..e6cd0cf 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -2,9 +2,9 @@ # XXX present but broken: atapi ip_mroute_mod joy pcic -SUBDIR= aha al ax ccd cd9660 coda dm fdesc fxp if_disc if_ppp if_sl if_tun \ - ipfilter ipfw kernfs md mfs mii msdos mx netgraph nfs ntfs nullfs \ - pn portal procfs rl sf sis sk ste ti tl umapfs union vn vr wb xl +SUBDIR= aha ccd dc cd9660 coda fdesc fxp if_disc if_ppp if_sl if_tun \ + ipfilter ipfw kernfs md mfs mii msdos netgraph nfs ntfs nullfs \ + portal procfs rl sf sis sk ste ti tl umapfs union vn vr wb xl SUBDIR+=usb ugen uhid ukbd ulpt ums umodem umass diff --git a/sys/modules/ax/Makefile b/sys/modules/ax/Makefile deleted file mode 100644 index 3368a7f..0000000 --- a/sys/modules/ax/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../../pci -KMOD = if_ax -SRCS = if_ax.c opt_bdg.h device_if.h bus_if.h pci_if.h -CFLAGS += ${DEBUG_FLAGS} - -.include <bsd.kmod.mk> diff --git a/sys/modules/al/Makefile b/sys/modules/dc/Makefile index 58170cf..2f87272 100644 --- a/sys/modules/al/Makefile +++ b/sys/modules/dc/Makefile @@ -1,8 +1,8 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../pci -KMOD = al -SRCS = if_al.c opt_bdg.h device_if.h bus_if.h pci_if.h +KMOD = if_dc +SRCS = if_dc.c opt_bdg.h device_if.h bus_if.h pci_if.h SRCS += miibus_if.h CFLAGS += ${DEBUG_FLAGS} KMODDEPS = miibus diff --git a/sys/modules/dm/Makefile b/sys/modules/dm/Makefile deleted file mode 100644 index e0e19ba..0000000 --- a/sys/modules/dm/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../../pci -KMOD = if_dm -SRCS = if_dm.c opt_bdg.h device_if.h bus_if.h pci_if.h -SRCS += miibus_if.h -CFLAGS += ${DEBUG_FLAGS} -KMODDEPS = miibus - -.include <bsd.kmod.mk> diff --git a/sys/modules/mii/Makefile b/sys/modules/mii/Makefile index 72eb718..407f3c3 100644 --- a/sys/modules/mii/Makefile +++ b/sys/modules/mii/Makefile @@ -4,7 +4,7 @@ KMOD = miibus SRCS = mii.c mii_physubr.c ukphy.c ukphy_subr.c bus_if.h SRCS += miibus_if.h device_if.h miibus_if.c exphy.c nsphy.c -SRCS += mlphy.c tlphy.c rlphy.c amphy.c mxphy.c +SRCS += mlphy.c tlphy.c rlphy.c amphy.c dcphy.c pnphy.c CFLAGS += ${DEBUG_FLAGS} .include <bsd.kmod.mk> diff --git a/sys/modules/mx/Makefile b/sys/modules/mx/Makefile deleted file mode 100644 index bbb49c0..0000000 --- a/sys/modules/mx/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../../pci -KMOD = if_mx -SRCS = if_mx.c opt_bdg.h device_if.h bus_if.h pci_if.h miibus_if.h -CFLAGS += ${DEBUG_FLAGS} - -.include <bsd.kmod.mk> diff --git a/sys/modules/pn/Makefile b/sys/modules/pn/Makefile deleted file mode 100644 index 51909c0..0000000 --- a/sys/modules/pn/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../../pci -KMOD = if_pn -SRCS = if_pn.c opt_bdg.h device_if.h bus_if.h pci_if.h -CFLAGS += ${DEBUG_FLAGS} - -.include <bsd.kmod.mk> diff --git a/sys/pci/if_al.c b/sys/pci/if_al.c deleted file mode 100644 index 246a58e..0000000 --- a/sys/pci/if_al.c +++ /dev/null @@ -1,1762 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * ADMtek AL981 Comet and AN985 Centaur fast ethernet PCI NIC driver. - * Datasheets for the AL981 are available from http://www.admtek.com.tw. - * - * Written by Bill Paul <wpaul@ee.columbia.edu> - * Electrical Engineering Department - * Columbia University, New York City - */ - -/* - * The ADMtek AL981 Comet is still another DEC 21x4x clone. It's - * a reasonably close copy of the tulip, except for the receiver filter - * programming. Where the DEC chip has a special setup frame that - * needs to be downloaded into the transmit DMA engine, the ADMtek chip - * has physical address and multicast address registers. - * - * The AN985 is an update to the AL981 which is mostly the same, except - * for the following things: - * - The AN985 uses a 99C66 EEPROM which requires a slightly different - * bit sequence to initiate a read. - * - The AN985 uses a serial MII interface instead of providing direct - * access to the PHY registers (it uses an internal PHY though). - * Although the datasheet for the AN985 is not yet available, you can - * use an AL981 datasheet as a reference for most of the chip functions, - * except for the MII interface which matches the DEC 21x4x specification - * (bits 16, 17, 18 and 19 in the serial I/O register control the MII). - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <net/ethernet.h> -#include <net/if_dl.h> -#include <net/if_media.h> - -#include <net/bpf.h> - -#include <vm/vm.h> /* for vtophys */ -#include <vm/pmap.h> /* for vtophys */ -#include <machine/clock.h> /* for DELAY */ -#include <machine/bus_pio.h> -#include <machine/bus_memio.h> -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/bus.h> -#include <sys/rman.h> - -#include <dev/mii/mii.h> -#include <dev/mii/miivar.h> - -#include <pci/pcireg.h> -#include <pci/pcivar.h> - -/* Enable workaround for small transmitter bug. */ -#define AL_TX_STALL_WAR - -#define AL_USEIOSPACE - -#include <pci/if_alreg.h> - -#include "miibus_if.h" - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - -/* - * Various supported device vendors/types and their names. - */ -static struct al_type al_devs[] = { - { AL_VENDORID, AL_DEVICEID_AL981, "ADMtek AL981 10/100BaseTX" }, - { AL_VENDORID, AL_DEVICEID_AN985, "ADMtek AN985 10/100BaseTX" }, - { 0, 0, NULL } -}; - -static int al_probe __P((device_t)); -static int al_attach __P((device_t)); -static int al_detach __P((device_t)); - -static int al_newbuf __P((struct al_softc *, - struct al_desc *, - struct mbuf *)); -static int al_encap __P((struct al_softc *, - struct mbuf *, u_int32_t *)); - -static void al_rxeof __P((struct al_softc *)); -static void al_txeof __P((struct al_softc *)); -static void al_tick __P((void *)); -static void al_intr __P((void *)); -static void al_start __P((struct ifnet *)); -static int al_ioctl __P((struct ifnet *, u_long, caddr_t)); -static void al_init __P((void *)); -static void al_stop __P((struct al_softc *)); -static void al_watchdog __P((struct ifnet *)); -static void al_shutdown __P((device_t)); -static int al_ifmedia_upd __P((struct ifnet *)); -static void al_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); - -static void al_delay __P((struct al_softc *)); -static void al_eeprom_idle __P((struct al_softc *)); -static void al_eeprom_putbyte __P((struct al_softc *, int)); -static void al_eeprom_getword __P((struct al_softc *, int, u_int16_t *)); -static void al_read_eeprom __P((struct al_softc *, caddr_t, int, - int, int)); - -static void al_mii_writebit __P((struct al_softc *, int)); -static int al_mii_readbit __P((struct al_softc *)); -static void al_mii_sync __P((struct al_softc *)); -static void al_mii_send __P((struct al_softc *, u_int32_t, int)); -static int al_mii_readreg __P((struct al_softc *, struct al_mii_frame *)); -static int al_mii_writereg __P((struct al_softc *, struct al_mii_frame *)); - -static int al_miibus_readreg __P((device_t, int, int)); -static int al_miibus_writereg __P((device_t, int, int, int)); -static void al_miibus_statchg __P((device_t)); - -static u_int32_t al_calchash __P((caddr_t)); -static void al_setmulti __P((struct al_softc *)); -static void al_reset __P((struct al_softc *)); -static int al_list_rx_init __P((struct al_softc *)); -static int al_list_tx_init __P((struct al_softc *)); - -#ifdef AL_USEIOSPACE -#define AL_RES SYS_RES_IOPORT -#define AL_RID AL_PCI_LOIO -#else -#define AL_RES SYS_RES_MEMORY -#define AL_RID AL_PCI_LOMEM -#endif - -static device_method_t al_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, al_probe), - DEVMETHOD(device_attach, al_attach), - DEVMETHOD(device_detach, al_detach), - DEVMETHOD(device_shutdown, al_shutdown), - - /* bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), - - /* MII interface */ - DEVMETHOD(miibus_readreg, al_miibus_readreg), - DEVMETHOD(miibus_writereg, al_miibus_writereg), - DEVMETHOD(miibus_statchg, al_miibus_statchg), - - { 0, 0 } -}; - -static driver_t al_driver = { - "al", - al_methods, - sizeof(struct al_softc), -}; - -static devclass_t al_devclass; - -DRIVER_MODULE(if_al, pci, al_driver, al_devclass, 0, 0); -DRIVER_MODULE(miibus, al, miibus_driver, miibus_devclass, 0, 0); - -#define AL_SETBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) | x) - -#define AL_CLRBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) & ~x) - -#define SIO_SET(x) \ - CSR_WRITE_4(sc, AL_SIO, \ - CSR_READ_4(sc, AL_SIO) | x) - -#define SIO_CLR(x) \ - CSR_WRITE_4(sc, AL_SIO, \ - CSR_READ_4(sc, AL_SIO) & ~x) - -static void al_delay(sc) - struct al_softc *sc; -{ - int idx; - - for (idx = (300 / 33) + 1; idx > 0; idx--) - CSR_READ_4(sc, AL_BUSCTL); -} - -static void al_eeprom_idle(sc) - struct al_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, AL_SIO, AL_SIO_EESEL); - al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_ROMCTL_READ); - al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_EE_CS); - al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_EE_CLK); - al_delay(sc); - - for (i = 0; i < 25; i++) { - AL_CLRBIT(sc, AL_SIO, AL_SIO_EE_CLK); - al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_EE_CLK); - al_delay(sc); - } - - AL_CLRBIT(sc, AL_SIO, AL_SIO_EE_CLK); - al_delay(sc); - AL_CLRBIT(sc, AL_SIO, AL_SIO_EE_CS); - al_delay(sc); - CSR_WRITE_4(sc, AL_SIO, 0x00000000); - - return; -} - -/* - * Send a read command and address to the EEPROM, check for ACK. - */ -static void al_eeprom_putbyte(sc, addr) - struct al_softc *sc; - int addr; -{ - register int d, i; - - /* - * The AN985 has a 99C66 EEPROM on it instead of - * a 99C64. It uses a different bit sequence for - * specifying the "read" opcode. - */ - if (sc->al_did == AL_DEVICEID_AN985) - d = addr | (AL_EECMD_READ << 2); - else - d = addr | AL_EECMD_READ; - - /* - * Feed in each bit and stobe the clock. - */ - for (i = 0x400; i; i >>= 1) { - if (d & i) { - SIO_SET(AL_SIO_EE_DATAIN); - } else { - SIO_CLR(AL_SIO_EE_DATAIN); - } - al_delay(sc); - SIO_SET(AL_SIO_EE_CLK); - al_delay(sc); - SIO_CLR(AL_SIO_EE_CLK); - al_delay(sc); - } - - return; -} - -/* - * Read a word of data stored in the EEPROM at address 'addr.' - */ -static void al_eeprom_getword(sc, addr, dest) - struct al_softc *sc; - int addr; - u_int16_t *dest; -{ - register int i; - u_int16_t word = 0; - - /* Force EEPROM to idle state. */ - al_eeprom_idle(sc); - - /* Enter EEPROM access mode. */ - CSR_WRITE_4(sc, AL_SIO, AL_SIO_EESEL); - al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_ROMCTL_READ); - al_delay(sc); - AL_SETBIT(sc, AL_SIO, AL_SIO_EE_CS); - al_delay(sc); - AL_CLRBIT(sc, AL_SIO, AL_SIO_EE_CLK); - al_delay(sc); - - /* - * Send address of word we want to read. - */ - al_eeprom_putbyte(sc, addr); - - /* - * Start reading bits from EEPROM. - */ - for (i = 0x8000; i; i >>= 1) { - SIO_SET(AL_SIO_EE_CLK); - al_delay(sc); - if (CSR_READ_4(sc, AL_SIO) & AL_SIO_EE_DATAOUT) - word |= i; - al_delay(sc); - SIO_CLR(AL_SIO_EE_CLK); - al_delay(sc); - } - - /* Turn off EEPROM access mode. */ - al_eeprom_idle(sc); - - *dest = word; - - return; -} - -/* - * Read a sequence of words from the EEPROM. - */ -static void al_read_eeprom(sc, dest, off, cnt, swap) - struct al_softc *sc; - caddr_t dest; - int off; - int cnt; - int swap; -{ - int i; - u_int16_t word = 0, *ptr; - - for (i = 0; i < cnt; i++) { - al_eeprom_getword(sc, off + i, &word); - ptr = (u_int16_t *)(dest + (i * 2)); - if (swap) - *ptr = ntohs(word); - else - *ptr = word; - } - - return; -} - -/* - * Write a bit to the MII bus. - */ -static void al_mii_writebit(sc, bit) - struct al_softc *sc; - int bit; -{ - if (bit) - CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_WRITE|AL_SIO_MII_DATAOUT); - else - CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_WRITE); - - AL_SETBIT(sc, AL_SIO, AL_SIO_MII_CLK); - AL_CLRBIT(sc, AL_SIO, AL_SIO_MII_CLK); - - return; -} - -/* - * Read a bit from the MII bus. - */ -static int al_mii_readbit(sc) - struct al_softc *sc; -{ - CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_READ|AL_SIO_MII_DIR); - CSR_READ_4(sc, AL_SIO); - AL_SETBIT(sc, AL_SIO, AL_SIO_MII_CLK); - AL_CLRBIT(sc, AL_SIO, AL_SIO_MII_CLK); - if (CSR_READ_4(sc, AL_SIO) & AL_SIO_MII_DATAIN) - return(1); - - return(0); -} - -/* - * Sync the PHYs by setting data bit and strobing the clock 32 times. - */ -static void al_mii_sync(sc) - struct al_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, AL_SIO, AL_SIO_ROMCTL_WRITE); - - for (i = 0; i < 32; i++) - al_mii_writebit(sc, 1); - - return; -} - -/* - * Clock a series of bits through the MII. - */ -static void al_mii_send(sc, bits, cnt) - struct al_softc *sc; - u_int32_t bits; - int cnt; -{ - int i; - - for (i = (0x1 << (cnt - 1)); i; i >>= 1) - al_mii_writebit(sc, bits & i); -} - -/* - * Read an PHY register through the MII. - */ -static int al_mii_readreg(sc, frame) - struct al_softc *sc; - struct al_mii_frame *frame; - -{ - int i, ack, s; - - s = splimp(); - - /* - * Set up frame for RX. - */ - frame->mii_stdelim = AL_MII_STARTDELIM; - frame->mii_opcode = AL_MII_READOP; - frame->mii_turnaround = 0; - frame->mii_data = 0; - - /* - * Sync the PHYs. - */ - al_mii_sync(sc); - - /* - * Send command/address info. - */ - al_mii_send(sc, frame->mii_stdelim, 2); - al_mii_send(sc, frame->mii_opcode, 2); - al_mii_send(sc, frame->mii_phyaddr, 5); - al_mii_send(sc, frame->mii_regaddr, 5); - -#ifdef notdef - /* Idle bit */ - al_mii_writebit(sc, 1); - al_mii_writebit(sc, 0); -#endif - - /* Check for ack */ - ack = al_mii_readbit(sc); - - /* - * Now try reading data bits. If the ack failed, we still - * need to clock through 16 cycles to keep the PHY(s) in sync. - */ - if (ack) { - for(i = 0; i < 16; i++) { - al_mii_readbit(sc); - } - goto fail; - } - - for (i = 0x8000; i; i >>= 1) { - if (!ack) { - if (al_mii_readbit(sc)) - frame->mii_data |= i; - } - } - -fail: - - al_mii_writebit(sc, 0); - al_mii_writebit(sc, 0); - - splx(s); - - if (ack) - return(1); - return(0); -} - -/* - * Write to a PHY register through the MII. - */ -static int al_mii_writereg(sc, frame) - struct al_softc *sc; - struct al_mii_frame *frame; - -{ - int s; - - s = splimp(); - /* - * Set up frame for TX. - */ - - frame->mii_stdelim = AL_MII_STARTDELIM; - frame->mii_opcode = AL_MII_WRITEOP; - frame->mii_turnaround = AL_MII_TURNAROUND; - - /* - * Sync the PHYs. - */ - al_mii_sync(sc); - - al_mii_send(sc, frame->mii_stdelim, 2); - al_mii_send(sc, frame->mii_opcode, 2); - al_mii_send(sc, frame->mii_phyaddr, 5); - al_mii_send(sc, frame->mii_regaddr, 5); - al_mii_send(sc, frame->mii_turnaround, 2); - al_mii_send(sc, frame->mii_data, 16); - - /* Idle bit. */ - al_mii_writebit(sc, 0); - al_mii_writebit(sc, 0); - - splx(s); - - return(0); -} - -static int al_miibus_readreg(dev, phy, reg) - device_t dev; - int phy, reg; -{ - struct al_mii_frame frame; - u_int16_t rval = 0; - u_int16_t phy_reg = 0; - struct al_softc *sc; - - sc = device_get_softc(dev); - - /* - * Note: both the AL981 and AN985 have internal PHYs, - * however the AL981 provides direct access to the PHY - * registers while the AN985 uses a serial MII interface. - * The AN985's MII interface is also buggy in that you - * can read from any MII address (0 to 31), but only address 1 - * behaves normally. To deal with both cases, we pretend - * that the PHY is at MII address 1. - */ - if (phy != 1) - return(0); - - if (sc->al_did == AL_DEVICEID_AN985) { - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - al_mii_readreg(sc, &frame); - - return(frame.mii_data); - } - - switch(reg) { - case MII_BMCR: - phy_reg = AL_BMCR; - break; - case MII_BMSR: - phy_reg = AL_BMSR; - break; - case MII_PHYIDR1: - phy_reg = AL_VENID; - break; - case MII_PHYIDR2: - phy_reg = AL_DEVID; - break; - case MII_ANAR: - phy_reg = AL_ANAR; - break; - case MII_ANLPAR: - phy_reg = AL_LPAR; - break; - case MII_ANER: - phy_reg = AL_ANER; - break; - default: - printf("al%d: read: bad phy register %x\n", - sc->al_unit, reg); - return(0); - break; - } - - rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; - - if (rval == 0xFFFF) - return(0); - - return(rval); -} - -static int al_miibus_writereg(dev, phy, reg, data) - device_t dev; - int phy, reg, data; -{ - struct al_mii_frame frame; - struct al_softc *sc; - u_int16_t phy_reg = 0; - - sc = device_get_softc(dev); - - if (phy != 1) - return(0); - - if (sc->al_did == AL_DEVICEID_AN985) { - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - frame.mii_data = data; - - al_mii_writereg(sc, &frame); - return(0); - } - - switch(reg) { - case MII_BMCR: - phy_reg = AL_BMCR; - break; - case MII_BMSR: - phy_reg = AL_BMSR; - break; - case MII_PHYIDR1: - phy_reg = AL_VENID; - break; - case MII_PHYIDR2: - phy_reg = AL_DEVID; - break; - case MII_ANAR: - phy_reg = AL_ANAR; - break; - case MII_ANLPAR: - phy_reg = AL_LPAR; - break; - case MII_ANER: - phy_reg = AL_ANER; - break; - default: - printf("al%d: phy_write: bad phy register %x\n", - sc->al_unit, reg); - return(0); - break; - } - - CSR_WRITE_4(sc, phy_reg, data); - - return(0); -} - -static void al_miibus_statchg(dev) - device_t dev; -{ - return; -} - -/* - * Calculate CRC of a multicast group address, return the lower 6 bits. - */ -static u_int32_t al_calchash(addr) - caddr_t addr; -{ - u_int32_t crc, carry; - int i, j; - u_int8_t c; - - /* Compute CRC for the address value. */ - crc = 0xFFFFFFFF; /* initial value */ - - for (i = 0; i < 6; i++) { - c = *(addr + i); - for (j = 0; j < 8; j++) { - carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); - crc <<= 1; - c >>= 1; - if (carry) - crc = (crc ^ 0x04c11db6) | carry; - } - } - - /* return the filter bit position */ - return((crc >> 26) & 0x0000003F); -} - -static void al_setmulti(sc) - struct al_softc *sc; -{ - struct ifnet *ifp; - int h = 0; - u_int32_t hashes[2] = { 0, 0 }; - struct ifmultiaddr *ifma; - u_int32_t rxfilt; - - ifp = &sc->arpcom.ac_if; - - rxfilt = CSR_READ_4(sc, AL_NETCFG); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - rxfilt |= AL_NETCFG_RX_ALLMULTI; - CSR_WRITE_4(sc, AL_NETCFG, rxfilt); - return; - } else - rxfilt &= ~AL_NETCFG_RX_ALLMULTI; - - /* first, zot all the existing hash bits */ - CSR_WRITE_4(sc, AL_MAR0, 0); - CSR_WRITE_4(sc, AL_MAR1, 0); - - /* now program new ones */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = al_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - if (h < 32) - hashes[0] |= (1 << h); - else - hashes[1] |= (1 << (h - 32)); - } - - CSR_WRITE_4(sc, AL_MAR0, hashes[0]); - CSR_WRITE_4(sc, AL_MAR1, hashes[1]); - CSR_WRITE_4(sc, AL_NETCFG, rxfilt); - - return; -} - -static void al_reset(sc) - struct al_softc *sc; -{ - register int i; - - AL_SETBIT(sc, AL_BUSCTL, AL_BUSCTL_RESET); - - for (i = 0; i < AL_TIMEOUT; i++) { - DELAY(10); - if (!(CSR_READ_4(sc, AL_BUSCTL) & AL_BUSCTL_RESET)) - break; - } -#ifdef notdef - if (i == AL_TIMEOUT) - printf("al%d: reset never completed!\n", sc->al_unit); -#endif - CSR_WRITE_4(sc, AL_BUSCTL, AL_BUSCTL_ARBITRATION); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); - return; -} - -/* - * Probe for an ADMtek chip. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - */ -static int al_probe(dev) - device_t dev; -{ - struct al_type *t; - - t = al_devs; - - while(t->al_name != NULL) { - if ((pci_get_vendor(dev) == t->al_vid) && - (pci_get_device(dev) == t->al_did)) { - device_set_desc(dev, t->al_name); - return(0); - } - t++; - } - - return(ENXIO); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -static int al_attach(dev) - device_t dev; -{ - int s; - u_char eaddr[ETHER_ADDR_LEN]; - u_int32_t command; - struct al_softc *sc; - struct ifnet *ifp; - int unit, error = 0, rid; - - s = splimp(); - - sc = device_get_softc(dev); - unit = device_get_unit(dev); - bzero(sc, sizeof(struct al_softc)); - sc->al_did = pci_get_device(dev); - - /* - * Handle power management nonsense. - */ - - command = pci_read_config(dev, AL_PCI_CAPID, 4) & 0x000000FF; - if (command == 0x01) { - - command = pci_read_config(dev, AL_PCI_PWRMGMTCTRL, 4); - if (command & AL_PSTATE_MASK) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, AL_PCI_LOIO, 4); - membase = pci_read_config(dev, AL_PCI_LOMEM, 4); - irq = pci_read_config(dev, AL_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("al%d: chip is in D%d power mode " - "-- setting to D0\n", unit, command & AL_PSTATE_MASK); - command &= 0xFFFFFFFC; - pci_write_config(dev, AL_PCI_PWRMGMTCTRL, command, 4); - - /* Restore PCI config data. */ - pci_write_config(dev, AL_PCI_LOIO, iobase, 4); - pci_write_config(dev, AL_PCI_LOMEM, membase, 4); - pci_write_config(dev, AL_PCI_INTLINE, irq, 4); - } - } - - /* - * Map control/status registers. - */ - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - -#ifdef AL_USEIOSPACE - if (!(command & PCIM_CMD_PORTEN)) { - printf("al%d: failed to enable I/O ports!\n", unit); - error = ENXIO;; - goto fail; - } -#else - if (!(command & PCIM_CMD_MEMEN)) { - printf("al%d: failed to enable memory mapping!\n", unit); - error = ENXIO;; - goto fail; - } -#endif - - rid = AL_RID; - sc->al_res = bus_alloc_resource(dev, AL_RES, &rid, - 0, ~0, 1, RF_ACTIVE); - - if (sc->al_res == NULL) { - printf("al%d: couldn't map ports/memory\n", unit); - error = ENXIO; - goto fail; - } - - sc->al_btag = rman_get_bustag(sc->al_res); - sc->al_bhandle = rman_get_bushandle(sc->al_res); - - /* Allocate interrupt */ - rid = 0; - sc->al_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, - RF_SHAREABLE | RF_ACTIVE); - - if (sc->al_irq == NULL) { - printf("al%d: couldn't map interrupt\n", unit); - bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); - error = ENXIO; - goto fail; - } - - error = bus_setup_intr(dev, sc->al_irq, INTR_TYPE_NET, - al_intr, sc, &sc->al_intrhand); - - if (error) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_res); - bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); - printf("al%d: couldn't set up irq\n", unit); - goto fail; - } - - /* Save the cache line size. */ - sc->al_cachesize = pci_read_config(dev, AL_PCI_CACHELEN, 4) & 0xFF; - - /* Reset the adapter. */ - al_reset(sc); - - /* - * Get station address from the EEPROM. - */ - al_read_eeprom(sc, (caddr_t)&eaddr, AL_EE_NODEADDR, 3, 0); - - /* - * An ADMtek chip was detected. Inform the world. - */ - printf("al%d: Ethernet address: %6D\n", unit, eaddr, ":"); - - sc->al_unit = unit; - callout_handle_init(&sc->al_stat_ch); - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - - sc->al_ldata = contigmalloc(sizeof(struct al_list_data), M_DEVBUF, - M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); - - if (sc->al_ldata == NULL) { - printf("al%d: no memory for list buffers!\n", unit); - bus_teardown_intr(dev, sc->al_irq, sc->al_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_irq); - bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); - error = ENXIO; - goto fail; - } - bzero(sc->al_ldata, sizeof(struct al_list_data)); - - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = "al"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = al_ioctl; - ifp->if_output = ether_output; - ifp->if_start = al_start; - ifp->if_watchdog = al_watchdog; - ifp->if_init = al_init; - ifp->if_baudrate = 10000000; - ifp->if_snd.ifq_maxlen = AL_TX_LIST_CNT - 1; - - /* - * Do MII setup. - */ - if (mii_phy_probe(dev, &sc->al_miibus, - al_ifmedia_upd, al_ifmedia_sts)) { - printf("al%d: MII without any PHY!\n", sc->al_unit); - bus_teardown_intr(dev, sc->al_irq, sc->al_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_irq); - bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); - error = ENXIO; - goto fail; - } - - /* - * Call MI attach routines. - */ - if_attach(ifp); - ether_ifattach(ifp); - - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - -fail: - splx(s); - return(error); -} - -static int al_detach(dev) - device_t dev; -{ - struct al_softc *sc; - struct ifnet *ifp; - int s; - - s = splimp(); - - sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - - al_reset(sc); - al_stop(sc); - if_detach(ifp); - - bus_generic_detach(dev); - device_delete_child(dev, sc->al_miibus); - - bus_teardown_intr(dev, sc->al_irq, sc->al_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->al_irq); - bus_release_resource(dev, AL_RES, AL_RID, sc->al_res); - - contigfree(sc->al_ldata, sizeof(struct al_list_data), M_DEVBUF); - - splx(s); - - return(0); -} - -/* - * Initialize the transmit descriptors. - */ -static int al_list_tx_init(sc) - struct al_softc *sc; -{ - struct al_chain_data *cd; - struct al_list_data *ld; - int i; - - cd = &sc->al_cdata; - ld = sc->al_ldata; - for (i = 0; i < AL_TX_LIST_CNT; i++) { - if (i == (AL_TX_LIST_CNT - 1)) { - ld->al_tx_list[i].al_nextdesc = - &ld->al_tx_list[0]; - ld->al_tx_list[i].al_next = - vtophys(&ld->al_tx_list[0]); - } else { - ld->al_tx_list[i].al_nextdesc = - &ld->al_tx_list[i + 1]; - ld->al_tx_list[i].al_next = - vtophys(&ld->al_tx_list[i + 1]); - } - ld->al_tx_list[i].al_mbuf = NULL; - ld->al_tx_list[i].al_data = 0; - ld->al_tx_list[i].al_ctl = 0; - } - - cd->al_tx_prod = cd->al_tx_cons = cd->al_tx_cnt = 0; - - return(0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int al_list_rx_init(sc) - struct al_softc *sc; -{ - struct al_chain_data *cd; - struct al_list_data *ld; - int i; - - cd = &sc->al_cdata; - ld = sc->al_ldata; - - for (i = 0; i < AL_RX_LIST_CNT; i++) { - if (al_newbuf(sc, &ld->al_rx_list[i], NULL) == ENOBUFS) - return(ENOBUFS); - if (i == (AL_RX_LIST_CNT - 1)) { - ld->al_rx_list[i].al_nextdesc = - &ld->al_rx_list[0]; - ld->al_rx_list[i].al_next = - vtophys(&ld->al_rx_list[0]); - } else { - ld->al_rx_list[i].al_nextdesc = - &ld->al_rx_list[i + 1]; - ld->al_rx_list[i].al_next = - vtophys(&ld->al_rx_list[i + 1]); - } - } - - cd->al_rx_prod = 0; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - */ -static int al_newbuf(sc, c, m) - struct al_softc *sc; - struct al_desc *c; - struct mbuf *m; -{ - struct mbuf *m_new = NULL; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("al%d: no memory for rx list " - "-- packet dropped!\n", sc->al_unit); - return(ENOBUFS); - } - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("al%d: no memory for rx list " - "-- packet dropped!\n", sc->al_unit); - m_freem(m_new); - return(ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - - m_adj(m_new, sizeof(u_int64_t)); - - c->al_mbuf = m_new; - c->al_data = vtophys(mtod(m_new, caddr_t)); - c->al_ctl = AL_RXCTL_RLINK | AL_RXLEN; - c->al_status = AL_RXSTAT_OWN; - - return(0); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void al_rxeof(sc) - struct al_softc *sc; -{ - struct ether_header *eh; - struct mbuf *m; - struct ifnet *ifp; - struct al_desc *cur_rx; - int i, total_len = 0; - u_int32_t rxstat; - - ifp = &sc->arpcom.ac_if; - - i = sc->al_cdata.al_rx_prod; - - while(!(sc->al_ldata->al_rx_list[i].al_status & AL_RXSTAT_OWN)) { - struct mbuf *m0 = NULL; - - cur_rx = &sc->al_ldata->al_rx_list[i]; - rxstat = cur_rx->al_status; - m = cur_rx->al_mbuf; - cur_rx->al_mbuf = NULL; - total_len = AL_RXBYTES(rxstat); - AL_INC(i, AL_RX_LIST_CNT); - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (rxstat & AL_RXSTAT_RXERR) { - ifp->if_ierrors++; - if (rxstat & AL_RXSTAT_COLLSEEN) - ifp->if_collisions++; - al_newbuf(sc, cur_rx, m); - al_init(sc); - return; - } - - /* No errors; receive the packet. */ - total_len -= ETHER_CRC_LEN; - - m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, - total_len + ETHER_ALIGN, 0, ifp, NULL); - al_newbuf(sc, cur_rx, m); - if (m0 == NULL) { - ifp->if_ierrors++; - continue; - } - m_adj(m0, ETHER_ALIGN); - m = m0; - - ifp->if_ipackets++; - eh = mtod(m, struct ether_header *); - - /* - * Handle BPF listeners. Let the BPF user see the packet, but - * don't pass it up to the ether_input() layer unless it's - * a broadcast packet, multicast packet, matches our ethernet - * address or the interface is in promiscuous mode. - */ - if (ifp->if_bpf) { - bpf_mtap(ifp, m); - if (ifp->if_flags & IFF_PROMISC && - (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0)) { - m_freem(m); - continue; - } - } - - /* Remove header from mbuf and pass it on. */ - m_adj(m, sizeof(struct ether_header)); - ether_input(ifp, eh, m); - } - - sc->al_cdata.al_rx_prod = i; - - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ - -static void al_txeof(sc) - struct al_softc *sc; -{ - struct al_desc *cur_tx = NULL; - struct ifnet *ifp; - u_int32_t idx; - - ifp = &sc->arpcom.ac_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; - - /* - * Go through our tx list and free mbufs for those - * frames that have been transmitted. - */ - idx = sc->al_cdata.al_tx_cons; - while(idx != sc->al_cdata.al_tx_prod) { - u_int32_t txstat; - - cur_tx = &sc->al_ldata->al_tx_list[idx]; - txstat = cur_tx->al_status; - - if (txstat & AL_TXSTAT_OWN) - break; - - if (!(cur_tx->al_ctl & AL_TXCTL_LASTFRAG)) { - sc->al_cdata.al_tx_cnt--; - AL_INC(idx, AL_TX_LIST_CNT); - continue; - } - - if (txstat & AL_TXSTAT_ERRSUM) { - ifp->if_oerrors++; - if (txstat & AL_TXSTAT_EXCESSCOLL) - ifp->if_collisions++; - if (txstat & AL_TXSTAT_LATECOLL) - ifp->if_collisions++; - al_init(sc); - return; - } - - ifp->if_collisions += (txstat & AL_TXSTAT_COLLCNT) >> 3; - - ifp->if_opackets++; - if (cur_tx->al_mbuf != NULL) { - m_freem(cur_tx->al_mbuf); - cur_tx->al_mbuf = NULL; - } - - sc->al_cdata.al_tx_cnt--; - AL_INC(idx, AL_TX_LIST_CNT); - ifp->if_timer = 0; - } - - sc->al_cdata.al_tx_cons = idx; - - if (cur_tx != NULL) - ifp->if_flags &= ~IFF_OACTIVE; - - return; -} - -static void al_tick(xsc) - void *xsc; -{ - struct al_softc *sc; - struct mii_data *mii; - int s; - - s = splimp(); - - sc = xsc; - mii = device_get_softc(sc->al_miibus); - mii_tick(mii); - - sc->al_stat_ch = timeout(al_tick, sc, hz); - - splx(s); - - return; -}; - -static void al_intr(arg) - void *arg; -{ - struct al_softc *sc; - struct ifnet *ifp; - u_int32_t status; - - - sc = arg; - ifp = &sc->arpcom.ac_if; - - /* Supress unwanted interrupts */ - if (!(ifp->if_flags & IFF_UP)) { - al_stop(sc); - return; - } - - /* Disable interrupts. */ - CSR_WRITE_4(sc, AL_IMR, 0x00000000); - - for (;;) { - status = CSR_READ_4(sc, AL_ISR); - if (status) - CSR_WRITE_4(sc, AL_ISR, status); - - if ((status & AL_INTRS) == 0) - break; - - if ((status & AL_ISR_TX_OK) || - (status & AL_ISR_TX_NOBUF)) - al_txeof(sc); - - if (status & AL_ISR_TX_IDLE) { - al_txeof(sc); - if (sc->al_cdata.al_tx_cnt) { - AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_TX_ON); - CSR_WRITE_4(sc, AL_TXSTART, 0xFFFFFFFF); - } - } - - if (status & AL_ISR_TX_UNDERRUN) { - u_int32_t cfg; - cfg = CSR_READ_4(sc, AL_NETCFG); - if ((cfg & AL_NETCFG_TX_THRESH) == AL_TXTHRESH_160BYTES) - AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_STORENFWD); - else - CSR_WRITE_4(sc, AL_NETCFG, cfg + 0x4000); - } - - if (status & AL_ISR_RX_OK) - al_rxeof(sc); - - if ((status & AL_ISR_RX_WATDOGTIMEO) || - (status & AL_ISR_RX_IDLE) || - (status & AL_ISR_RX_NOBUF)) { - al_rxeof(sc); - al_init(sc); - } - - if (status & AL_ISR_BUS_ERR) { - al_reset(sc); - al_init(sc); - } - } - - /* Re-enable interrupts. */ - CSR_WRITE_4(sc, AL_IMR, AL_INTRS); - - if (ifp->if_snd.ifq_head != NULL) { - al_start(ifp); - } - - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int al_encap(sc, m_head, txidx) - struct al_softc *sc; - struct mbuf *m_head; - u_int32_t *txidx; -{ - struct al_desc *f = NULL; - struct mbuf *m; - int frag, cur, cnt = 0; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - m = m_head; - cur = frag = *txidx; - - for (m = m_head; m != NULL; m = m->m_next) { - if (m->m_len != 0) { -#ifdef AL_TX_STALL_WAR - /* - * Work around some strange behavior in the Comet. For - * some reason, the transmitter will sometimes wedge if - * we queue up a descriptor chain that wraps from the end - * of the transmit list back to the beginning. If we reach - * the end of the list and still have more packets to queue, - * don't queue them now: end the transmit session here and - * then wait until it finishes before sending the other - * packets. - */ - if (*txidx != sc->al_cdata.al_tx_prod && - frag == (AL_TX_LIST_CNT - 1)) - return(ENOBUFS); -#endif - if ((AL_TX_LIST_CNT - - (sc->al_cdata.al_tx_cnt + cnt)) < 2) - return(ENOBUFS); - f = &sc->al_ldata->al_tx_list[frag]; - f->al_ctl = AL_TXCTL_TLINK | m->m_len; - if (cnt == 0) { - f->al_status = 0; - f->al_ctl |= AL_TXCTL_FIRSTFRAG; - } else - f->al_status = AL_TXSTAT_OWN; - f->al_data = vtophys(mtod(m, vm_offset_t)); - cur = frag; - AL_INC(frag, AL_TX_LIST_CNT); - cnt++; - } - } - - if (m != NULL) - return(ENOBUFS); - - sc->al_ldata->al_tx_list[cur].al_mbuf = m_head; - sc->al_ldata->al_tx_list[cur].al_ctl |= - AL_TXCTL_LASTFRAG|AL_TXCTL_FINT; - sc->al_ldata->al_tx_list[*txidx].al_status |= AL_TXSTAT_OWN; - sc->al_cdata.al_tx_cnt += cnt; - *txidx = frag; - - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ - -static void al_start(ifp) - struct ifnet *ifp; -{ - struct al_softc *sc; - struct mbuf *m_head = NULL; - u_int32_t idx; - - sc = ifp->if_softc; - - if (ifp->if_flags & IFF_OACTIVE) - return; - - idx = sc->al_cdata.al_tx_prod; - - while(sc->al_ldata->al_tx_list[idx].al_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - if (al_encap(sc, m_head, &idx)) { - IF_PREPEND(&ifp->if_snd, m_head); - ifp->if_flags |= IFF_OACTIVE; - break; - } - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp, m_head); - } - - /* Transmit */ - sc->al_cdata.al_tx_prod = idx; - CSR_WRITE_4(sc, AL_TXSTART, 0xFFFFFFFF); - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; -} - -static void al_init(xsc) - void *xsc; -{ - struct al_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - struct mii_data *mii; - int s; - - s = splimp(); - - mii = device_get_softc(sc->al_miibus); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - al_stop(sc); - al_reset(sc); - - /* - * Set cache alignment and burst length. - */ - CSR_WRITE_4(sc, AL_BUSCTL, AL_BUSCTL_ARBITRATION); - AL_SETBIT(sc, AL_BUSCTL, AL_BURSTLEN_16LONG); - switch(sc->al_cachesize) { - case 32: - AL_SETBIT(sc, AL_BUSCTL, AL_CACHEALIGN_32LONG); - break; - case 16: - AL_SETBIT(sc, AL_BUSCTL, AL_CACHEALIGN_16LONG); - break; - case 8: - AL_SETBIT(sc, AL_BUSCTL, AL_CACHEALIGN_8LONG); - break; - case 0: - default: - AL_SETBIT(sc, AL_BUSCTL, AL_CACHEALIGN_NONE); - break; - } - - AL_CLRBIT(sc, AL_NETCFG, AL_NETCFG_HEARTBEAT); - AL_CLRBIT(sc, AL_NETCFG, AL_NETCFG_STORENFWD); - - AL_CLRBIT(sc, AL_NETCFG, AL_NETCFG_TX_THRESH); - - if (IFM_SUBTYPE(sc->ifmedia.ifm_media) == IFM_10_T) - AL_SETBIT(sc, AL_NETCFG, AL_TXTHRESH_160BYTES); - else - AL_SETBIT(sc, AL_NETCFG, AL_TXTHRESH_72BYTES); - - /* Init our MAC address */ - CSR_WRITE_4(sc, AL_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); - CSR_WRITE_4(sc, AL_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); - - /* Init circular RX list. */ - if (al_list_rx_init(sc) == ENOBUFS) { - printf("al%d: initialization failed: no " - "memory for rx buffers\n", sc->al_unit); - al_stop(sc); - (void)splx(s); - return; - } - - /* - * Init tx descriptors. - */ - al_list_tx_init(sc); - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) { - AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_RX_PROMISC); - } else { - AL_CLRBIT(sc, AL_NETCFG, AL_NETCFG_RX_PROMISC); - } - - /* - * Load the multicast filter. - */ - al_setmulti(sc); - - /* - * Load the address of the RX list. - */ - CSR_WRITE_4(sc, AL_RXADDR, vtophys(&sc->al_ldata->al_rx_list[0])); - CSR_WRITE_4(sc, AL_TXADDR, vtophys(&sc->al_ldata->al_tx_list[0])); - - /* - * Enable interrupts. - */ - CSR_WRITE_4(sc, AL_IMR, AL_INTRS); - CSR_WRITE_4(sc, AL_ISR, 0xFFFFFFFF); - - /* Enable receiver and transmitter. */ - AL_SETBIT(sc, AL_NETCFG, AL_NETCFG_TX_ON|AL_NETCFG_RX_ON); - CSR_WRITE_4(sc, AL_RXSTART, 0xFFFFFFFF); - - mii_mediachg(mii); - - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - (void)splx(s); - - sc->al_stat_ch = timeout(al_tick, sc, hz); - - return; -} - -/* - * Set media options. - */ -static int al_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct al_softc *sc; - - sc = ifp->if_softc; - - if (ifp->if_flags & IFF_UP) - al_init(sc); - - return(0); -} - -/* - * Report current media status. - */ -static void al_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct al_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; - - mii = device_get_softc(sc->al_miibus); - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - - return; -} - -static int al_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct al_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *) data; - struct mii_data *mii; - int s, error = 0; - - s = splimp(); - - switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - al_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - al_stop(sc); - } - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - al_setmulti(sc); - error = 0; - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - mii = device_get_softc(sc->al_miibus); - error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); - break; - default: - error = EINVAL; - break; - } - - (void)splx(s); - - return(error); -} - -static void al_watchdog(ifp) - struct ifnet *ifp; -{ - struct al_softc *sc; - - sc = ifp->if_softc; - - ifp->if_oerrors++; - printf("al%d: watchdog timeout\n", sc->al_unit); - - al_stop(sc); - al_reset(sc); - al_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - al_start(ifp); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void al_stop(sc) - struct al_softc *sc; -{ - register int i; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - untimeout(al_tick, sc, sc->al_stat_ch); - AL_CLRBIT(sc, AL_NETCFG, (AL_NETCFG_RX_ON|AL_NETCFG_TX_ON)); - CSR_WRITE_4(sc, AL_IMR, 0x00000000); - CSR_WRITE_4(sc, AL_TXADDR, 0x00000000); - CSR_WRITE_4(sc, AL_RXADDR, 0x00000000); - - /* - * Free data in the RX lists. - */ - for (i = 0; i < AL_RX_LIST_CNT; i++) { - if (sc->al_ldata->al_rx_list[i].al_mbuf != NULL) { - m_freem(sc->al_ldata->al_rx_list[i].al_mbuf); - sc->al_ldata->al_rx_list[i].al_mbuf = NULL; - } - } - bzero((char *)&sc->al_ldata->al_rx_list, - sizeof(sc->al_ldata->al_rx_list)); - - /* - * Free the TX list buffers. - */ - for (i = 0; i < AL_TX_LIST_CNT; i++) { - if (sc->al_ldata->al_tx_list[i].al_mbuf != NULL) { - m_freem(sc->al_ldata->al_tx_list[i].al_mbuf); - sc->al_ldata->al_tx_list[i].al_mbuf = NULL; - } - } - - bzero((char *)&sc->al_ldata->al_tx_list, - sizeof(sc->al_ldata->al_tx_list)); - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - return; -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static void al_shutdown(dev) - device_t dev; -{ - struct al_softc *sc; - - sc = device_get_softc(dev); - /*al_stop(sc); */ - - return; -} diff --git a/sys/pci/if_alreg.h b/sys/pci/if_alreg.h deleted file mode 100644 index 243903e..0000000 --- a/sys/pci/if_alreg.h +++ /dev/null @@ -1,553 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * COMET register definitions. - */ - -#define AL_BUSCTL 0x00 /* bus control */ -#define AL_TXSTART 0x08 /* tx start demand */ -#define AL_RXSTART 0x10 /* rx start demand */ -#define AL_RXADDR 0x18 /* rx descriptor list start addr */ -#define AL_TXADDR 0x20 /* tx descriptor list start addr */ -#define AL_ISR 0x28 /* interrupt status register */ -#define AL_NETCFG 0x30 /* network config register */ -#define AL_IMR 0x38 /* interrupt mask */ -#define AL_FRAMESDISCARDED 0x40 /* # of discarded frames */ -#define AL_SIO 0x48 /* MII and ROM/EEPROM access */ -#define AL_RESERVED 0x50 -#define AL_GENTIMER 0x58 /* general timer */ -#define AL_GENPORT 0x60 /* general purpose port */ -#define AL_WAKEUP_CTL 0x68 /* wake-up control/status register */ -#define AL_WAKEUP_PAT 0x70 /* wake-up pattern data register */ -#define AL_WATCHDOG 0x78 /* watchdog timer */ -#define AL_ISR2 0x80 /* ISR assist register */ -#define AL_IMR2 0x84 /* IRM assist register */ -#define AL_COMMAND 0x88 /* command register */ -#define AL_PCIPERF 0x8C /* pci perf counter */ -#define AL_PWRMGMT 0x90 /* pwr management command/status */ -#define AL_TXBURST 0x9C /* tx burst counter/timeout */ -#define AL_FLASHPROM 0xA0 /* flash(boot) PROM port */ -#define AL_PAR0 0xA4 /* station address */ -#define AL_PAR1 0xA8 /* station address */ -#define AL_MAR0 0xAC /* multicast hash filter */ -#define AL_MAR1 0xB0 /* multicast hash filter */ -#define AL_BMCR 0xB4 /* built in PHY control */ -#define AL_BMSR 0xB8 /* built in PHY status */ -#define AL_VENID 0xBC /* built in PHY ID0 */ -#define AL_DEVID 0xC0 /* built in PHY ID1 */ -#define AL_ANAR 0xC4 /* built in PHY autoneg advert */ -#define AL_LPAR 0xC8 /* bnilt in PHY link part. ability */ -#define AL_ANER 0xCC /* built in PHY autoneg expansion */ -#define AL_PHY_MODECTL 0xD0 /* mode control */ -#define AL_PHY_CONFIG 0xD4 /* config info and inter status */ -#define AL_PHY_INTEN 0xD8 /* interrupto enable */ -#define AL_PHY_MODECTL_100TX 0xDC /* 100baseTX control/status */ - -/* - * Bus control bits. - */ -#define AL_BUSCTL_RESET 0x00000001 -#define AL_BUSCTL_ARBITRATION 0x00000002 -#define AL_BUSCTL_SKIPLEN 0x0000007C -#define AL_BUSCTL_BIGENDIAN 0x00000080 -#define AL_BUSCTL_BURSTLEN 0x00003F00 -#define AL_BUSCTL_CACHEALIGN 0x0000C000 -#define AL_BUSCTL_XMITPOLL 0x00060000 -#define AL_BUSCTL_BUF_BIGENDIAN 0x00100000 -#define AL_BUSCTL_READMULTI 0x00200000 -#define AL_BUSCTL_READLINE 0x00800000 -#define AL_BUSCTL_WRITEINVAL 0x01000000 - -#define AL_SKIPLEN_1LONG 0x00000004 -#define AL_SKIPLEN_2LONG 0x00000008 -#define AL_SKIPLEN_3LONG 0x00000010 -#define AL_SKIPLEN_4LONG 0x00000020 -#define AL_SKIPLEN_5LONG 0x00000040 - -#define AL_BURSTLEN_UNLIMIT 0x00000000 -#define AL_BURSTLEN_1LONG 0x00000100 -#define AL_BURSTLEN_2LONG 0x00000200 -#define AL_BURSTLEN_4LONG 0x00000400 -#define AL_BURSTLEN_8LONG 0x00000800 -#define AL_BURSTLEN_16LONG 0x00001000 -#define AL_BURSTLEN_32LONG 0x00002000 - -#define AL_CACHEALIGN_NONE 0x00000000 -#define AL_CACHEALIGN_8LONG 0x00004000 -#define AL_CACHEALIGN_16LONG 0x00008000 -#define AL_CACHEALIGN_32LONG 0x0000C000 - -#define AL_TXPOLL_OFF 0x00000000 -#define AL_TXPOLL_200U 0x00020000 -#define AX_TXPOLL_800U 0x00040000 -#define AL_TXPOLL_1600U 0x00060000 - -/* - * Interrupt status bits. - */ -#define AL_ISR_TX_OK 0x00000001 -#define AL_ISR_TX_IDLE 0x00000002 -#define AL_ISR_TX_NOBUF 0x00000004 -#define AL_ISR_TX_JABBERTIMEO 0x00000008 -#define AL_ISR_TX_UNDERRUN 0x00000020 -#define AL_ISR_RX_OK 0x00000040 -#define AL_ISR_RX_NOBUF 0x00000080 -#define AL_ISR_RX_IDLE 0x00000100 -#define AL_ISR_RX_WATDOGTIMEO 0x00000200 -#define AL_ISR_TIMER_EXPIRED 0x00000800 -#define AL_ISR_BUS_ERR 0x00002000 -#define AL_ISR_ABNORMAL 0x00008000 -#define AL_ISR_NORMAL 0x00010000 -#define AL_ISR_RX_STATE 0x000E0000 -#define AL_ISR_TX_STATE 0x00700000 -#define AL_ISR_BUSERRTYPE 0x03800000 - -#define AL_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ -#define AL_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ -#define AL_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ -#define AL_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ -#define AL_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ -#define AL_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ -#define AL_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ -#define AL_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ - -#define AL_TXSTATE_RESET 0x00000000 /* 000 - reset */ -#define AL_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ -#define AL_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ -#define AL_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ -#define AL_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ -#define AL_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ -#define AL_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ -#define AL_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ - -/* - * Network config bits. - */ -#define AL_NETCFG_RX_ON 0x00000002 -#define AL_NETCFG_RX_BADFRAMES 0x00000008 -#define AL_NETCFG_RX_BACKOFF 0x00000020 -#define AL_NETCFG_RX_PROMISC 0x00000040 -#define AL_NETCFG_RX_ALLMULTI 0x00000080 -#define AL_NETCFG_OPMODE 0x00000C00 -#define AL_NETCFG_FORCECOLL 0x00001000 -#define AL_NETCFG_TX_ON 0x00002000 -#define AL_NETCFG_TX_THRESH 0x0000C000 -#define AL_NETCFG_HEARTBEAT 0x00080000 /* 0 == ON, 1 == OFF */ -#define AL_NETCFG_STORENFWD 0x00200000 - -#define AL_OPMODE_NORM 0x00000000 -#define AL_OPMODE_INTLOOP 0x00000400 -#define AL_OPMODE_EXTLOOP 0x00000800 - -#define AL_TXTHRESH_72BYTES 0x00000000 -#define AL_TXTHRESH_96BYTES 0x00004000 -#define AL_TXTHRESH_128BYTES 0x00008000 -#define AL_TXTHRESH_160BYTES 0x0000C000 - -/* - * Interrupt mask bits. - */ -#define AL_IMR_TX_OK 0x00000001 -#define AL_IMR_TX_IDLE 0x00000002 -#define AL_IMR_TX_NOBUF 0x00000004 -#define AL_IMR_TX_JABBERTIMEO 0x00000008 -#define AL_IMR_TX_UNDERRUN 0x00000020 -#define AL_IMR_RX_OK 0x00000040 -#define AL_IMR_RX_NOBUF 0x00000080 -#define AL_IMR_RX_IDLE 0x00000100 -#define AL_IMR_RX_WATDOGTIMEO 0x00000200 -#define AL_IMR_TIMER_EXPIRED 0x00000800 -#define AL_IMR_BUS_ERR 0x00002000 -#define AL_IMR_ABNORMAL 0x00008000 -#define AL_IMR_NORMAL 0x00010000 - -#define AL_INTRS \ - (AL_IMR_RX_OK|AL_IMR_TX_OK|AL_IMR_RX_NOBUF|AL_IMR_RX_WATDOGTIMEO|\ - AL_IMR_TX_NOBUF|AL_IMR_TX_UNDERRUN|AL_IMR_BUS_ERR| \ - AL_IMR_ABNORMAL|AL_IMR_NORMAL|AL_IMR_TX_IDLE|AL_IMR_RX_IDLE) - -/* - * Missed packer register. - */ -#define AL_MISSEDPKT_CNT 0x0000FFFF -#define AL_MISSEDPKT_OFLOW 0x00010000 - -/* - * Serial I/O (EEPROM/ROM) bits. - */ -#define AL_SIO_EE_CS 0x00000001 /* EEPROM chip select */ -#define AL_SIO_EE_CLK 0x00000002 /* EEPROM clock */ -#define AL_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ -#define AL_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ -#define AL_SIO_EESEL 0x00000800 -#define AL_SIO_ROMCTL_WRITE 0x00002000 -#define AL_SIO_ROMCTL_READ 0x00004000 -#define AL_SIO_MII_CLK 0x00010000 /* MDIO clock */ -#define AL_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ -#define AL_SIO_MII_DIR 0x00040000 /* MDIO dir */ -#define AL_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ - -#define AL_EECMD_WRITE 0x140 -#define AL_EECMD_READ 0x180 -#define AL_EECMD_ERASE 0x1c0 - -#define AL_EE_NODEADDR_OFFSET 0x70 -#define AL_EE_NODEADDR 4 - -/* - * General purpose timer register - */ -#define AL_TIMER_VALUE 0x0000FFFF -#define AL_TIMER_CONTINUOUS 0x00010000 - -/* - * Wakeup control/status register. - */ -#define AL_WU_LINKSTS 0x00000001 /* link status changed */ -#define AL_WU_MAGICPKT 0x00000002 /* magic packet received */ -#define AL_WU_WUPKT 0x00000004 /* wake up pkt received */ -#define AL_WU_LINKSTS_ENB 0x00000100 /* enable linksts event */ -#define AL_WU_MAGICPKT_ENB 0x00000200 /* enable magicpkt event */ -#define AL_WU_WUPKT_ENB 0x00000400 /* enable wakeup pkt event */ -#define AL_WU_LINKON_ENB 0x00010000 /* enable link on detect */ -#define AL_WU_LINKOFF_ENB 0x00020000 /* enable link off detect */ -#define AL_WU_WKUPMATCH_PAT5 0x02000000 /* enable wkup pat 5 match */ -#define AL_WU_WKUPMATCH_PAT4 0x04000000 /* enable wkup pat 4 match */ -#define AL_WU_WKUPMATCH_PAT3 0x08000000 /* enable wkup pat 3 match */ -#define AL_WU_WKUPMATCH_PAT2 0x10000000 /* enable wkup pat 2 match */ -#define AL_WU_WKUPMATCH_PAT1 0x20000000 /* enable wkup pat 1 match */ -#define AL_WU_CRCTYPE 0x40000000 /* crc: 0=0000, 1=ffff */ - -/* - * Wakeup pattern structure. - */ -struct al_wu_pattern { - u_int32_t al_wu_bits[4]; -}; - -struct al_wakeup { - struct al_wu_pattern al_wu_pat; - u_int16_t al_wu_crc1; - u_int16_t al_wu_offset1; -}; - -struct al_wakup_record { - struct al_wakeup al_wakeup[5]; -}; - -/* - * Watchdog timer register. - */ -#define AL_WDOG_JABDISABLE 0x00000001 -#define AL_WDOG_NONJABBER 0x00000002 -#define AL_WDOG_JABCLK 0x00000004 -#define AL_WDOG_RXWDOG_DIS 0x00000010 -#define AL_WDOG_RXWDOG_REL 0x00000020 - -/* - * Assistant status register. - */ -#define AL_ISR2_ABNORMAL 0x00008000 -#define AL_ISR2_NORMAL 0x00010000 -#define AL_ISR2_RX_STATE 0x000E0000 -#define AL_ISR2_TX_STATE 0x00700000 -#define AL_ISR2_BUSERRTYPE 0x03800000 -#define AL_ISR2_PAUSE 0x04000000 /* PAUSE frame received */ -#define AL_ISR2_TX_DEFER 0x10000000 -#define AL_ISR2_XCVR_INT 0x20000000 -#define AL_ISR2_RX_EARLY 0x40000000 -#define AL_ISR2_TX_EARLY 0x80000000 - -/* - * Assistant mask register. - */ -#define AL_IMR2_ABNORMAL 0x00008000 -#define AL_IMR2_NORMAL 0x00010000 -#define AL_IMR2_PAUSE 0x04000000 /* PAUSE frame received */ -#define AL_IMR2_TX_DEFER 0x10000000 -#define AL_IMR2_XCVR_INT 0x20000000 -#define AL_IMR2_RX_EARLY 0x40000000 -#define AL_IMR2_TX_EARLY 0x80000000 - -/* - * Command register, some bits loaded from EEPROM. - */ -#define AL_CMD_TXURUN_REC 0x00000001 /* enable TX underflow recovery */ -#define AL_CMD_SOFTWARE_INT 0x00000002 /* software interrupt */ -#define AL_CMD_DRT 0x0000000C /* drain receive threshold */ -#define AL_CMD_RXTHRESH_ENB 0x00000010 /* rx threshold enable */ -#define AL_CMD_PAUSE 0x00000020 -#define AL_CMD_RST_WU_PTR 0x00000040 /* reset wakeup pattern reg. */ -/* Values below loaded from EEPROM. */ -#define AL_CMD_WOL_ENB 0x00040000 /* WOL enable */ -#define AL_CMD_PM_ENB 0x00080000 /* pwr mgmt enable */ -#define AL_CMD_RX_FIFO 0x00300000 -#define AL_CMD_LED_MODE 0x00400000 -#define AL_CMD_CURRENT_MODE 0x70000000 -#define AL_CMD_D3COLD 0x80000000 - -/* - * PCI performance counter. - */ -#define AL_PCI_DW_CNT 0x000000FF -#define AL_PCI_CLK 0xFFFF0000 - -/* - * Power management command and status. - */ -#define AL_PWRM_PWR_STATE 0x00000003 -#define AL_PWRM_PME_EN 0x00000100 -#define AL_PWRM_DSEL 0x00001E00 -#define AL_PWRM_DSCALE 0x00006000 -#define AL_PWRM_PME_STAT 0x00008000 - -/* - * TX burst count / timeout register. - */ -#define AL_TXB_TIMEO 0x00000FFF -#define AL_TXB_BURSTCNT 0x0000F000 - -/* - * Flash PROM register. - */ -#define AL_PROM_DATA 0x0000000F -#define AL_PROM_ADDR 0x01FFFFF0 -#define AL_PROM_WR_ENB 0x04000000 -#define AL_PROM_BRA16_ON 0x80000000 - -/* - * COMET TX/RX list structure. - */ - -struct al_desc { - u_int32_t al_status; - u_int32_t al_ctl; - u_int32_t al_ptr1; - u_int32_t al_ptr2; - /* Driver specific stuff. */ -#ifdef __i386__ - u_int32_t al_pad; -#endif - struct mbuf *al_mbuf; - struct al_desc *al_nextdesc; -}; - -#define al_data al_ptr1 -#define al_next al_ptr2 - -#define AL_RXSTAT_FIFOOFLOW 0x00000001 -#define AL_RXSTAT_CRCERR 0x00000002 -#define AL_RXSTAT_DRIBBLE 0x00000004 -#define AL_RXSTAT_WATCHDOG 0x00000010 -#define AL_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ -#define AL_RXSTAT_COLLSEEN 0x00000040 -#define AL_RXSTAT_GIANT 0x00000080 -#define AL_RXSTAT_LASTFRAG 0x00000100 -#define AL_RXSTAT_FIRSTFRAG 0x00000200 -#define AL_RXSTAT_MULTICAST 0x00000400 -#define AL_RXSTAT_RUNT 0x00000800 -#define AL_RXSTAT_RXTYPE 0x00003000 -#define AL_RXSTAT_RXERR 0x00008000 -#define AL_RXSTAT_RXLEN 0x3FFF0000 -#define AL_RXSTAT_OWN 0x80000000 - -#define AL_RXBYTES(x) ((x & AL_RXSTAT_RXLEN) >> 16) -#define AL_RXSTAT (AL_RXSTAT_FIRSTFRAG|AL_RXSTAT_LASTFRAG|AL_RXSTAT_OWN) - -#define AL_RXCTL_BUFLEN1 0x00000FFF -#define AL_RXCTL_BUFLEN2 0x00FFF000 -#define AL_RXCTL_RLINK 0x01000000 -#define AL_RXCTL_RLAST 0x02000000 - -#define AL_TXSTAT_DEFER 0x00000001 -#define AL_TXSTAT_UNDERRUN 0x00000002 -#define AL_TXSTAT_LINKFAIL 0x00000003 -#define AL_TXSTAT_COLLCNT 0x00000078 -#define AL_TXSTAT_SQE 0x00000080 -#define AL_TXSTAT_EXCESSCOLL 0x00000100 -#define AL_TXSTAT_LATECOLL 0x00000200 -#define AL_TXSTAT_NOCARRIER 0x00000400 -#define AL_TXSTAT_CARRLOST 0x00000800 -#define AL_TXSTAT_JABTIMEO 0x00004000 -#define AL_TXSTAT_ERRSUM 0x00008000 -#define AL_TXSTAT_OWN 0x80000000 - -#define AL_TXCTL_BUFLEN1 0x000007FF -#define AL_TXCTL_BUFLEN2 0x003FF800 -#define AL_TXCTL_PAD 0x00800000 -#define AL_TXCTL_TLINK 0x01000000 -#define AL_TXCTL_TLAST 0x02000000 -#define AL_TXCTL_NOCRC 0x04000000 -#define AL_TXCTL_FIRSTFRAG 0x20000000 -#define AL_TXCTL_LASTFRAG 0x40000000 -#define AL_TXCTL_FINT 0x80000000 - -#define AL_MAXFRAGS 16 -#define AL_RX_LIST_CNT 64 -#define AL_TX_LIST_CNT 128 -#define AL_MIN_FRAMELEN 60 -#define AL_RXLEN 1536 - -#define AL_INC(x, y) (x) = (x + 1) % y - -struct al_list_data { - struct al_desc al_rx_list[AL_RX_LIST_CNT]; - struct al_desc al_tx_list[AL_TX_LIST_CNT]; -}; - -struct al_chain_data { - int al_tx_prod; - int al_tx_cons; - int al_tx_cnt; - int al_rx_prod; -}; - -struct al_type { - u_int16_t al_vid; - u_int16_t al_did; - char *al_name; -}; - -struct al_mii_frame { - u_int8_t mii_stdelim; - u_int8_t mii_opcode; - u_int8_t mii_phyaddr; - u_int8_t mii_regaddr; - u_int8_t mii_turnaround; - u_int16_t mii_data; -}; - -#define AL_MII_STARTDELIM 0x01 -#define AL_MII_READOP 0x02 -#define AL_MII_WRITEOP 0x01 -#define AL_MII_TURNAROUND 0x02 - -struct al_softc { - struct arpcom arpcom; /* interface info */ - struct ifmedia ifmedia; /* media info */ - bus_space_handle_t al_bhandle; /* bus space handle */ - bus_space_tag_t al_btag; /* bus space tag */ - struct resource *al_res; - struct resource *al_irq; - void *al_intrhand; - device_t al_miibus; - struct al_type *al_info; /* COMET adapter info */ - int al_did; - u_int8_t al_unit; /* interface number */ - struct al_list_data *al_ldata; - struct al_chain_data al_cdata; - u_int8_t al_cachesize; - struct callout_handle al_stat_ch; -}; - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->al_btag, sc->al_bhandle, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->al_btag, sc->al_bbhandle, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->al_btag, sc->al_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->al_btag, sc->al_bhandle, reg) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->al_btag, sc->al_bhandle, reg) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->al_btag, sc->al_bhandle, reg) - -#define AL_TIMEOUT 1000 -#define ETHER_ALIGN 2 - -/* - * General constants that are fun to know. - * - * ADMtek PCI vendor ID - */ -#define AL_VENDORID 0x1317 - -/* - * AL981 device IDs. - */ -#define AL_DEVICEID_AL981 0x0981 - -/* - * AN985 device IDs. - */ -#define AL_DEVICEID_AN985 0x0985 - -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. - */ - -#define AL_PCI_VENDOR_ID 0x00 -#define AL_PCI_DEVICE_ID 0x02 -#define AL_PCI_COMMAND 0x04 -#define AL_PCI_STATUS 0x06 -#define AL_PCI_REVID 0x08 -#define AL_PCI_CLASSCODE 0x09 -#define AL_PCI_CACHELEN 0x0C -#define AL_PCI_LATENCY_TIMER 0x0D -#define AL_PCI_HEADER_TYPE 0x0E -#define AL_PCI_LOIO 0x10 -#define AL_PCI_LOMEM 0x14 -#define AL_PCI_BIOSROM 0x30 -#define AL_PCI_INTLINE 0x3C -#define AL_PCI_INTPIN 0x3D -#define AL_PCI_MINGNT 0x3E -#define AL_PCI_MINLAT 0x0F -#define AL_PCI_RESETOPT 0x48 -#define AL_PCI_EEPROM_DATA 0x4C - -/* power management registers */ -#define AL_PCI_CAPID 0x44 /* 8 bits */ -#define AL_PCI_NEXTPTR 0x45 /* 8 bits */ -#define AL_PCI_PWRMGMTCAP 0x46 /* 16 bits */ -#define AL_PCI_PWRMGMTCTRL 0x48 /* 16 bits */ - -#define AL_PSTATE_MASK 0x0003 -#define AL_PSTATE_D0 0x0000 -#define AL_PSTATE_D1 0x0001 -#define AL_PSTATE_D2 0x0002 -#define AL_PSTATE_D3 0x0003 -#define AL_PME_EN 0x0010 -#define AL_PME_STATUS 0x8000 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif diff --git a/sys/pci/if_ax.c b/sys/pci/if_ax.c deleted file mode 100644 index e380a2b..0000000 --- a/sys/pci/if_ax.c +++ /dev/null @@ -1,2233 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * ASIX AX88140A and AX88141 fast ethernet PCI NIC driver. - * - * Written by Bill Paul <wpaul@ctr.columbia.edu> - * Electrical Engineering Department - * Columbia University, New York City - */ - -/* - * The ASIX Electronics AX88140A is still another DEC 21x4x clone. It's - * a reasonably close copy of the tulip, except for the receiver filter - * programming. Where the DEC chip has a special setup frame that - * needs to be downloaded into the transmit DMA engine, the ASIX chip - * has a less complicated setup frame which is written into one of - * the registers. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <net/ethernet.h> -#include <net/if_dl.h> -#include <net/if_media.h> - -#include <net/bpf.h> - -#include <vm/vm.h> /* for vtophys */ -#include <vm/pmap.h> /* for vtophys */ -#include <machine/clock.h> /* for DELAY */ -#include <machine/bus_pio.h> -#include <machine/bus_memio.h> -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/bus.h> -#include <sys/rman.h> - -#include <pci/pcireg.h> -#include <pci/pcivar.h> - -#define AX_USEIOSPACE - -/* #define AX_BACKGROUND_AUTONEG */ - -#include <pci/if_axreg.h> - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - -/* - * Various supported device vendors/types and their names. - */ -static struct ax_type ax_devs[] = { - { AX_VENDORID, AX_DEVICEID_AX88140A, - "ASIX AX88140A 10/100BaseTX" }, - { AX_VENDORID, AX_DEVICEID_AX88140A, - "ASIX AX88141 10/100BaseTX" }, - { 0, 0, NULL } -}; - -/* - * Various supported PHY vendors/types and their names. Note that - * this driver will work with pretty much any MII-compliant PHY, - * so failure to positively identify the chip is not a fatal error. - */ - -static struct ax_type ax_phys[] = { - { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, - { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, - { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, - { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, - { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, - { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, - { 0, 0, "<MII-compliant physical interface>" } -}; - -static int ax_probe __P((device_t)); -static int ax_attach __P((device_t)); -static int ax_detach __P((device_t)); - -static int ax_newbuf __P((struct ax_softc *, - struct ax_chain_onefrag *, - struct mbuf *)); -static int ax_encap __P((struct ax_softc *, struct ax_chain *, - struct mbuf *)); - -static void ax_rxeof __P((struct ax_softc *)); -static void ax_rxeoc __P((struct ax_softc *)); -static void ax_txeof __P((struct ax_softc *)); -static void ax_txeoc __P((struct ax_softc *)); -static void ax_intr __P((void *)); -static void ax_start __P((struct ifnet *)); -static int ax_ioctl __P((struct ifnet *, u_long, caddr_t)); -static void ax_init __P((void *)); -static void ax_stop __P((struct ax_softc *)); -static void ax_watchdog __P((struct ifnet *)); -static void ax_shutdown __P((device_t)); -static int ax_ifmedia_upd __P((struct ifnet *)); -static void ax_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); - -static void ax_delay __P((struct ax_softc *)); -static void ax_eeprom_idle __P((struct ax_softc *)); -static void ax_eeprom_putbyte __P((struct ax_softc *, int)); -static void ax_eeprom_getword __P((struct ax_softc *, int, u_int16_t *)); -static void ax_read_eeprom __P((struct ax_softc *, caddr_t, int, - int, int)); - -static void ax_mii_writebit __P((struct ax_softc *, int)); -static int ax_mii_readbit __P((struct ax_softc *)); -static void ax_mii_sync __P((struct ax_softc *)); -static void ax_mii_send __P((struct ax_softc *, u_int32_t, int)); -static int ax_mii_readreg __P((struct ax_softc *, struct ax_mii_frame *)); -static int ax_mii_writereg __P((struct ax_softc *, struct ax_mii_frame *)); -static u_int16_t ax_phy_readreg __P((struct ax_softc *, int)); -static void ax_phy_writereg __P((struct ax_softc *, int, int)); - -static void ax_autoneg_xmit __P((struct ax_softc *)); -static void ax_autoneg_mii __P((struct ax_softc *, int, int)); -static void ax_setmode_mii __P((struct ax_softc *, int)); -static void ax_setmode __P((struct ax_softc *, int, int)); -static void ax_getmode_mii __P((struct ax_softc *)); -static void ax_setcfg __P((struct ax_softc *, int)); -static u_int32_t ax_calchash __P((caddr_t)); -static void ax_setmulti __P((struct ax_softc *)); -static void ax_reset __P((struct ax_softc *)); -static int ax_list_rx_init __P((struct ax_softc *)); -static int ax_list_tx_init __P((struct ax_softc *)); - -#ifdef AX_USEIOSPACE -#define AX_RES SYS_RES_IOPORT -#define AX_RID AX_PCI_LOIO -#else -#define AX_RES SYS_RES_IOPORT -#define AX_RID AX_PCI_LOIO -#endif - -static device_method_t ax_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, ax_probe), - DEVMETHOD(device_attach, ax_attach), - DEVMETHOD(device_detach, ax_detach), - DEVMETHOD(device_shutdown, ax_shutdown), - { 0, 0 } -}; - -static driver_t ax_driver = { - "ax", - ax_methods, - sizeof(struct ax_softc) -}; - -static devclass_t ax_devclass; - -DRIVER_MODULE(if_ax, pci, ax_driver, ax_devclass, 0, 0); - -#define AX_SETBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) | x) - -#define AX_CLRBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) & ~x) - -#define SIO_SET(x) \ - CSR_WRITE_4(sc, AX_SIO, \ - CSR_READ_4(sc, AX_SIO) | x) - -#define SIO_CLR(x) \ - CSR_WRITE_4(sc, AX_SIO, \ - CSR_READ_4(sc, AX_SIO) & ~x) - -static void ax_delay(sc) - struct ax_softc *sc; -{ - int idx; - - for (idx = (300 / 33) + 1; idx > 0; idx--) - CSR_READ_4(sc, AX_BUSCTL); -} - -static void ax_eeprom_idle(sc) - struct ax_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, AX_SIO, AX_SIO_EESEL); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_ROMCTL_READ); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_EE_CS); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_EE_CLK); - ax_delay(sc); - - for (i = 0; i < 25; i++) { - AX_CLRBIT(sc, AX_SIO, AX_SIO_EE_CLK); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_EE_CLK); - ax_delay(sc); - } - - AX_CLRBIT(sc, AX_SIO, AX_SIO_EE_CLK); - ax_delay(sc); - AX_CLRBIT(sc, AX_SIO, AX_SIO_EE_CS); - ax_delay(sc); - CSR_WRITE_4(sc, AX_SIO, 0x00000000); - - return; -} - -/* - * Send a read command and address to the EEPROM, check for ACK. - */ -static void ax_eeprom_putbyte(sc, addr) - struct ax_softc *sc; - int addr; -{ - register int d, i; - - d = addr | AX_EECMD_READ; - - /* - * Feed in each bit and stobe the clock. - */ - for (i = 0x400; i; i >>= 1) { - if (d & i) { - SIO_SET(AX_SIO_EE_DATAIN); - } else { - SIO_CLR(AX_SIO_EE_DATAIN); - } - ax_delay(sc); - SIO_SET(AX_SIO_EE_CLK); - ax_delay(sc); - SIO_CLR(AX_SIO_EE_CLK); - ax_delay(sc); - } - - return; -} - -/* - * Read a word of data stored in the EEPROM at address 'addr.' - */ -static void ax_eeprom_getword(sc, addr, dest) - struct ax_softc *sc; - int addr; - u_int16_t *dest; -{ - register int i; - u_int16_t word = 0; - - /* Force EEPROM to idle state. */ - ax_eeprom_idle(sc); - - /* Enter EEPROM access mode. */ - CSR_WRITE_4(sc, AX_SIO, AX_SIO_EESEL); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_ROMCTL_READ); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_EE_CS); - ax_delay(sc); - AX_SETBIT(sc, AX_SIO, AX_SIO_EE_CLK); - ax_delay(sc); - - /* - * Send address of word we want to read. - */ - ax_eeprom_putbyte(sc, addr); - - /* - * Start reading bits from EEPROM. - */ - for (i = 0x8000; i; i >>= 1) { - SIO_SET(AX_SIO_EE_CLK); - ax_delay(sc); - if (CSR_READ_4(sc, AX_SIO) & AX_SIO_EE_DATAOUT) - word |= i; - ax_delay(sc); - SIO_CLR(AX_SIO_EE_CLK); - ax_delay(sc); - } - - /* Turn off EEPROM access mode. */ - ax_eeprom_idle(sc); - - *dest = word; - - return; -} - -/* - * Read a sequence of words from the EEPROM. - */ -static void ax_read_eeprom(sc, dest, off, cnt, swap) - struct ax_softc *sc; - caddr_t dest; - int off; - int cnt; - int swap; -{ - int i; - u_int16_t word = 0, *ptr; - - for (i = 0; i < cnt; i++) { - ax_eeprom_getword(sc, off + i, &word); - ptr = (u_int16_t *)(dest + (i * 2)); - if (swap) - *ptr = ntohs(word); - else - *ptr = word; - } - - return; -} - -/* - * Write a bit to the MII bus. - */ -static void ax_mii_writebit(sc, bit) - struct ax_softc *sc; - int bit; -{ - if (bit) - CSR_WRITE_4(sc, AX_SIO, AX_SIO_ROMCTL_WRITE|AX_SIO_MII_DATAOUT); - else - CSR_WRITE_4(sc, AX_SIO, AX_SIO_ROMCTL_WRITE); - - AX_SETBIT(sc, AX_SIO, AX_SIO_MII_CLK); - AX_CLRBIT(sc, AX_SIO, AX_SIO_MII_CLK); - - return; -} - -/* - * Read a bit from the MII bus. - */ -static int ax_mii_readbit(sc) - struct ax_softc *sc; -{ - CSR_WRITE_4(sc, AX_SIO, AX_SIO_ROMCTL_READ|AX_SIO_MII_DIR); - CSR_READ_4(sc, AX_SIO); - AX_SETBIT(sc, AX_SIO, AX_SIO_MII_CLK); - AX_CLRBIT(sc, AX_SIO, AX_SIO_MII_CLK); - if (CSR_READ_4(sc, AX_SIO) & AX_SIO_MII_DATAIN) - return(1); - - return(0); -} - -/* - * Sync the PHYs by setting data bit and strobing the clock 32 times. - */ -static void ax_mii_sync(sc) - struct ax_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, AX_SIO, AX_SIO_ROMCTL_WRITE); - - for (i = 0; i < 32; i++) - ax_mii_writebit(sc, 1); - - return; -} - -/* - * Clock a series of bits through the MII. - */ -static void ax_mii_send(sc, bits, cnt) - struct ax_softc *sc; - u_int32_t bits; - int cnt; -{ - int i; - - for (i = (0x1 << (cnt - 1)); i; i >>= 1) - ax_mii_writebit(sc, bits & i); -} - -/* - * Read an PHY register through the MII. - */ -static int ax_mii_readreg(sc, frame) - struct ax_softc *sc; - struct ax_mii_frame *frame; - -{ - int i, ack, s; - - s = splimp(); - - /* - * Set up frame for RX. - */ - frame->mii_stdelim = AX_MII_STARTDELIM; - frame->mii_opcode = AX_MII_READOP; - frame->mii_turnaround = 0; - frame->mii_data = 0; - - /* - * Sync the PHYs. - */ - ax_mii_sync(sc); - - /* - * Send command/address info. - */ - ax_mii_send(sc, frame->mii_stdelim, 2); - ax_mii_send(sc, frame->mii_opcode, 2); - ax_mii_send(sc, frame->mii_phyaddr, 5); - ax_mii_send(sc, frame->mii_regaddr, 5); - -#ifdef notdef - /* Idle bit */ - ax_mii_writebit(sc, 1); - ax_mii_writebit(sc, 0); -#endif - - /* Check for ack */ - ack = ax_mii_readbit(sc); - - /* - * Now try reading data bits. If the ack failed, we still - * need to clock through 16 cycles to keep the PHY(s) in sync. - */ - if (ack) { - for(i = 0; i < 16; i++) { - ax_mii_readbit(sc); - } - goto fail; - } - - for (i = 0x8000; i; i >>= 1) { - if (!ack) { - if (ax_mii_readbit(sc)) - frame->mii_data |= i; - } - } - -fail: - - ax_mii_writebit(sc, 0); - ax_mii_writebit(sc, 0); - - splx(s); - - if (ack) - return(1); - return(0); -} - -/* - * Write to a PHY register through the MII. - */ -static int ax_mii_writereg(sc, frame) - struct ax_softc *sc; - struct ax_mii_frame *frame; - -{ - int s; - - s = splimp(); - /* - * Set up frame for TX. - */ - - frame->mii_stdelim = AX_MII_STARTDELIM; - frame->mii_opcode = AX_MII_WRITEOP; - frame->mii_turnaround = AX_MII_TURNAROUND; - - /* - * Sync the PHYs. - */ - ax_mii_sync(sc); - - ax_mii_send(sc, frame->mii_stdelim, 2); - ax_mii_send(sc, frame->mii_opcode, 2); - ax_mii_send(sc, frame->mii_phyaddr, 5); - ax_mii_send(sc, frame->mii_regaddr, 5); - ax_mii_send(sc, frame->mii_turnaround, 2); - ax_mii_send(sc, frame->mii_data, 16); - - /* Idle bit. */ - ax_mii_writebit(sc, 0); - ax_mii_writebit(sc, 0); - - splx(s); - - return(0); -} - -static u_int16_t ax_phy_readreg(sc, reg) - struct ax_softc *sc; - int reg; -{ - struct ax_mii_frame frame; - - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = sc->ax_phy_addr; - frame.mii_regaddr = reg; - ax_mii_readreg(sc, &frame); - - return(frame.mii_data); -} - -static void ax_phy_writereg(sc, reg, data) - struct ax_softc *sc; - int reg; - int data; -{ - struct ax_mii_frame frame; - - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = sc->ax_phy_addr; - frame.mii_regaddr = reg; - frame.mii_data = data; - - ax_mii_writereg(sc, &frame); - - return; -} - -/* - * Calculate CRC of a multicast group address, return the lower 6 bits. - */ -static u_int32_t ax_calchash(addr) - caddr_t addr; -{ - u_int32_t crc, carry; - int i, j; - u_int8_t c; - - /* Compute CRC for the address value. */ - crc = 0xFFFFFFFF; /* initial value */ - - for (i = 0; i < 6; i++) { - c = *(addr + i); - for (j = 0; j < 8; j++) { - carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); - crc <<= 1; - c >>= 1; - if (carry) - crc = (crc ^ 0x04c11db6) | carry; - } - } - - /* return the filter bit position */ - return((crc >> 26) & 0x0000003F); -} - -static void ax_setmulti(sc) - struct ax_softc *sc; -{ - struct ifnet *ifp; - int h = 0; - u_int32_t hashes[2] = { 0, 0 }; - struct ifmultiaddr *ifma; - u_int32_t rxfilt; - - ifp = &sc->arpcom.ac_if; - - rxfilt = CSR_READ_4(sc, AX_NETCFG); - - if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - rxfilt |= AX_NETCFG_RX_ALLMULTI; - CSR_WRITE_4(sc, AX_NETCFG, rxfilt); - return; - } else - rxfilt &= ~AX_NETCFG_RX_ALLMULTI; - - /* first, zot all the existing hash bits */ - CSR_WRITE_4(sc, AX_FILTIDX, AX_FILTIDX_MAR0); - CSR_WRITE_4(sc, AX_FILTDATA, 0); - CSR_WRITE_4(sc, AX_FILTIDX, AX_FILTIDX_MAR1); - CSR_WRITE_4(sc, AX_FILTDATA, 0); - - /* now program new ones */ - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = ax_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - if (h < 32) - hashes[0] |= (1 << h); - else - hashes[1] |= (1 << (h - 32)); - } - - CSR_WRITE_4(sc, AX_FILTIDX, AX_FILTIDX_MAR0); - CSR_WRITE_4(sc, AX_FILTDATA, hashes[0]); - CSR_WRITE_4(sc, AX_FILTIDX, AX_FILTIDX_MAR1); - CSR_WRITE_4(sc, AX_FILTDATA, hashes[1]); - CSR_WRITE_4(sc, AX_NETCFG, rxfilt); - - return; -} - -/* - * Initiate an autonegotiation session. - */ -static void ax_autoneg_xmit(sc) - struct ax_softc *sc; -{ - u_int16_t phy_sts; - - ax_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); - DELAY(500); - while(ax_phy_readreg(sc, PHY_BMCR) - & PHY_BMCR_RESET); - - phy_sts = ax_phy_readreg(sc, PHY_BMCR); - phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; - ax_phy_writereg(sc, PHY_BMCR, phy_sts); - - return; -} - -/* - * Invoke autonegotiation on a PHY. - */ -static void ax_autoneg_mii(sc, flag, verbose) - struct ax_softc *sc; - int flag; - int verbose; -{ - u_int16_t phy_sts = 0, media, advert, ability; - struct ifnet *ifp; - struct ifmedia *ifm; - - ifm = &sc->ifmedia; - ifp = &sc->arpcom.ac_if; - - ifm->ifm_media = IFM_ETHER | IFM_AUTO; - - /* - * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' - * bit cleared in the status register, but has the 'autoneg enabled' - * bit set in the control register. This is a contradiction, and - * I'm not sure how to handle it. If you want to force an attempt - * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR - * and see what happens. - */ -#ifndef FORCE_AUTONEG_TFOUR - /* - * First, see if autoneg is supported. If not, there's - * no point in continuing. - */ - phy_sts = ax_phy_readreg(sc, PHY_BMSR); - if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { - if (verbose) - printf("ax%d: autonegotiation not supported\n", - sc->ax_unit); - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - return; - } -#endif - - switch (flag) { - case AX_FLAG_FORCEDELAY: - /* - * XXX Never use this option anywhere but in the probe - * routine: making the kernel stop dead in its tracks - * for three whole seconds after we've gone multi-user - * is really bad manners. - */ - ax_autoneg_xmit(sc); - DELAY(5000000); - break; - case AX_FLAG_SCHEDDELAY: - /* - * Wait for the transmitter to go idle before starting - * an autoneg session, otherwise ax_start() may clobber - * our timeout, and we don't want to allow transmission - * during an autoneg session since that can screw it up. - */ - if (sc->ax_cdata.ax_tx_head != NULL) { - sc->ax_want_auto = 1; - return; - } - ax_autoneg_xmit(sc); - ifp->if_timer = 5; - sc->ax_autoneg = 1; - sc->ax_want_auto = 0; - return; - break; - case AX_FLAG_DELAYTIMEO: - ifp->if_timer = 0; - sc->ax_autoneg = 0; - break; - default: - printf("ax%d: invalid autoneg flag: %d\n", sc->ax_unit, flag); - return; - } - - if (ax_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { - if (verbose) - printf("ax%d: autoneg complete, ", sc->ax_unit); - phy_sts = ax_phy_readreg(sc, PHY_BMSR); - } else { - if (verbose) - printf("ax%d: autoneg not complete, ", sc->ax_unit); - } - - media = ax_phy_readreg(sc, PHY_BMCR); - - /* Link is good. Report modes and set duplex mode. */ - if (ax_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { - if (verbose) - printf("link status good "); - advert = ax_phy_readreg(sc, PHY_ANAR); - ability = ax_phy_readreg(sc, PHY_LPAR); - - if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { - ifm->ifm_media = IFM_ETHER|IFM_100_T4; - media |= PHY_BMCR_SPEEDSEL; - media &= ~PHY_BMCR_DUPLEX; - printf("(100baseT4)\n"); - } else if (advert & PHY_ANAR_100BTXFULL && - ability & PHY_ANAR_100BTXFULL) { - ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; - media |= PHY_BMCR_SPEEDSEL; - media |= PHY_BMCR_DUPLEX; - printf("(full-duplex, 100Mbps)\n"); - } else if (advert & PHY_ANAR_100BTXHALF && - ability & PHY_ANAR_100BTXHALF) { - ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; - media |= PHY_BMCR_SPEEDSEL; - media &= ~PHY_BMCR_DUPLEX; - printf("(half-duplex, 100Mbps)\n"); - } else if (advert & PHY_ANAR_10BTFULL && - ability & PHY_ANAR_10BTFULL) { - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; - media &= ~PHY_BMCR_SPEEDSEL; - media |= PHY_BMCR_DUPLEX; - printf("(full-duplex, 10Mbps)\n"); - } else if (advert & PHY_ANAR_10BTHALF && - ability & PHY_ANAR_10BTHALF) { - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - media &= ~PHY_BMCR_SPEEDSEL; - media &= ~PHY_BMCR_DUPLEX; - printf("(half-duplex, 10Mbps)\n"); - } - - media &= ~PHY_BMCR_AUTONEGENBL; - - /* Set ASIC's duplex mode to match the PHY. */ - ax_setcfg(sc, media); - ax_phy_writereg(sc, PHY_BMCR, media); - } else { - if (verbose) - printf("no carrier\n"); - } - - ax_init(sc); - - if (sc->ax_tx_pend) { - sc->ax_autoneg = 0; - sc->ax_tx_pend = 0; - ax_start(ifp); - } - - return; -} - -static void ax_getmode_mii(sc) - struct ax_softc *sc; -{ - u_int16_t bmsr; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - bmsr = ax_phy_readreg(sc, PHY_BMSR); - if (bootverbose) - printf("ax%d: PHY status word: %x\n", sc->ax_unit, bmsr); - - /* fallback */ - sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - - if (bmsr & PHY_BMSR_10BTHALF) { - if (bootverbose) - printf("ax%d: 10Mbps half-duplex mode supported\n", - sc->ax_unit); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); - } - - if (bmsr & PHY_BMSR_10BTFULL) { - if (bootverbose) - printf("ax%d: 10Mbps full-duplex mode supported\n", - sc->ax_unit); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; - } - - if (bmsr & PHY_BMSR_100BTXHALF) { - if (bootverbose) - printf("ax%d: 100Mbps half-duplex mode supported\n", - sc->ax_unit); - ifp->if_baudrate = 100000000; - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; - } - - if (bmsr & PHY_BMSR_100BTXFULL) { - if (bootverbose) - printf("ax%d: 100Mbps full-duplex mode supported\n", - sc->ax_unit); - ifp->if_baudrate = 100000000; - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; - } - - /* Some also support 100BaseT4. */ - if (bmsr & PHY_BMSR_100BT4) { - if (bootverbose) - printf("ax%d: 100baseT4 mode supported\n", sc->ax_unit); - ifp->if_baudrate = 100000000; - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4; -#ifdef FORCE_AUTONEG_TFOUR - if (bootverbose) - printf("ax%d: forcing on autoneg support for BT4\n", - sc->ax_unit); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): - sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; -#endif - } - - if (bmsr & PHY_BMSR_CANAUTONEG) { - if (bootverbose) - printf("ax%d: autoneg supported\n", sc->ax_unit); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; - } - - return; -} - -/* - * Set speed and duplex mode. - */ -static void ax_setmode_mii(sc, media) - struct ax_softc *sc; - int media; -{ - u_int16_t bmcr; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* - * If an autoneg session is in progress, stop it. - */ - if (sc->ax_autoneg) { - printf("ax%d: canceling autoneg session\n", sc->ax_unit); - ifp->if_timer = sc->ax_autoneg = sc->ax_want_auto = 0; - bmcr = ax_phy_readreg(sc, PHY_BMCR); - bmcr &= ~PHY_BMCR_AUTONEGENBL; - ax_phy_writereg(sc, PHY_BMCR, bmcr); - } - - printf("ax%d: selecting MII, ", sc->ax_unit); - - bmcr = ax_phy_readreg(sc, PHY_BMCR); - - bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| - PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); - - if (IFM_SUBTYPE(media) == IFM_100_T4) { - printf("100Mbps/T4, half-duplex\n"); - bmcr |= PHY_BMCR_SPEEDSEL; - bmcr &= ~PHY_BMCR_DUPLEX; - } - - if (IFM_SUBTYPE(media) == IFM_100_TX) { - printf("100Mbps, "); - bmcr |= PHY_BMCR_SPEEDSEL; - } - - if (IFM_SUBTYPE(media) == IFM_10_T) { - printf("10Mbps, "); - bmcr &= ~PHY_BMCR_SPEEDSEL; - } - - if ((media & IFM_GMASK) == IFM_FDX) { - printf("full duplex\n"); - bmcr |= PHY_BMCR_DUPLEX; - } else { - printf("half duplex\n"); - bmcr &= ~PHY_BMCR_DUPLEX; - } - - ax_setcfg(sc, bmcr); - ax_phy_writereg(sc, PHY_BMCR, bmcr); - - return; -} - -/* - * Set speed and duplex mode on internal transceiver. - */ -static void ax_setmode(sc, media, verbose) - struct ax_softc *sc; - int media; - int verbose; -{ - struct ifnet *ifp; - u_int32_t mode; - - ifp = &sc->arpcom.ac_if; - - if (verbose) - printf("ax%d: selecting internal xcvr, ", sc->ax_unit); - - mode = CSR_READ_4(sc, AX_NETCFG); - - mode &= ~(AX_NETCFG_FULLDUPLEX|AX_NETCFG_PORTSEL| - AX_NETCFG_PCS|AX_NETCFG_SCRAMBLER|AX_NETCFG_SPEEDSEL); - - if (IFM_SUBTYPE(media) == IFM_100_T4) { - if (verbose) - printf("100Mbps/T4, half-duplex\n"); - mode |= AX_NETCFG_PORTSEL|AX_NETCFG_PCS|AX_NETCFG_SCRAMBLER; - } - - if (IFM_SUBTYPE(media) == IFM_100_TX) { - if (verbose) - printf("100Mbps, "); - mode |= AX_NETCFG_PORTSEL|AX_NETCFG_PCS|AX_NETCFG_SCRAMBLER; - } - - if (IFM_SUBTYPE(media) == IFM_10_T) { - if (verbose) - printf("10Mbps, "); - mode &= ~AX_NETCFG_PORTSEL; - mode |= AX_NETCFG_SPEEDSEL; - } - - if ((media & IFM_GMASK) == IFM_FDX) { - if (verbose) - printf("full duplex\n"); - mode |= AX_NETCFG_FULLDUPLEX; - } else { - if (verbose) - printf("half duplex\n"); - mode &= ~AX_NETCFG_FULLDUPLEX; - } - - CSR_WRITE_4(sc, AX_NETCFG, mode); - - return; -} - -/* - * In order to fiddle with the - * 'full-duplex' and '100Mbps' bits in the netconfig register, we - * first have to put the transmit and/or receive logic in the idle state. - */ -static void ax_setcfg(sc, bmcr) - struct ax_softc *sc; - int bmcr; -{ - int i, restart = 0; - - if (CSR_READ_4(sc, AX_NETCFG) & (AX_NETCFG_TX_ON|AX_NETCFG_RX_ON)) { - restart = 1; - AX_CLRBIT(sc, AX_NETCFG, (AX_NETCFG_TX_ON|AX_NETCFG_RX_ON)); - - for (i = 0; i < AX_TIMEOUT; i++) { - DELAY(10); - if (CSR_READ_4(sc, AX_ISR) & AX_ISR_TX_IDLE) - break; - } - - if (i == AX_TIMEOUT) - printf("ax%d: failed to force tx and " - "rx to idle state\n", sc->ax_unit); - - } - - if (bmcr & PHY_BMCR_SPEEDSEL) - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_SPEEDSEL); - else - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_SPEEDSEL); - - if (bmcr & PHY_BMCR_DUPLEX) - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_FULLDUPLEX); - else - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_FULLDUPLEX); - - if (restart) - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_TX_ON|AX_NETCFG_RX_ON); - - return; -} - -static void ax_reset(sc) - struct ax_softc *sc; -{ - register int i; - - AX_SETBIT(sc, AX_BUSCTL, AX_BUSCTL_RESET); - - for (i = 0; i < AX_TIMEOUT; i++) { - DELAY(10); - if (!(CSR_READ_4(sc, AX_BUSCTL) & AX_BUSCTL_RESET)) - break; - } -#ifdef notdef - if (i == AX_TIMEOUT) - printf("ax%d: reset never completed!\n", sc->ax_unit); -#endif - CSR_WRITE_4(sc, AX_BUSCTL, AX_BUSCTL_CONFIG); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); - return; -} - -/* - * Probe for an ASIX chip. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - */ -int ax_probe(dev) - device_t dev; -{ - struct ax_type *t; - u_int32_t rev; - - t = ax_devs; - - while(t->ax_name != NULL) { - if ((pci_get_vendor(dev) == t->ax_vid) && - (pci_get_device(dev) == t->ax_did)) { - /* Check the PCI revision */ - rev = pci_read_config(dev, AX_PCI_REVID, 4) & 0xFF; - if (rev >= AX_REVISION_88141) - t++; - device_set_desc(dev, t->ax_name); - return(0); - } - t++; - } - - return(ENXIO); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -static int ax_attach(dev) - device_t dev; -{ - int s, i; - u_char eaddr[ETHER_ADDR_LEN]; - u_int32_t command; - struct ax_softc *sc; - struct ifnet *ifp; - int media = IFM_ETHER|IFM_100_TX|IFM_FDX; - unsigned int round; - caddr_t roundptr; - struct ax_type *p; - u_int16_t phy_vid, phy_did, phy_sts; - int unit, error = 0, rid; - - s = splimp(); - - sc = device_get_softc(dev); - unit = device_get_unit(dev); - bzero(sc, sizeof(struct ax_softc)); - - /* - * Handle power management nonsense. - */ - - command = pci_read_config(dev, AX_PCI_CAPID, 4) & 0x000000FF; - if (command == 0x01) { - - command = pci_read_config(dev, AX_PCI_PWRMGMTCTRL, 4); - if (command & AX_PSTATE_MASK) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, AX_PCI_LOIO, 4); - membase = pci_read_config(dev, AX_PCI_LOMEM, 4); - irq = pci_read_config(dev, AX_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("ax%d: chip is in D%d power mode " - "-- setting to D0\n", unit, command & AX_PSTATE_MASK); - command &= 0xFFFFFFFC; - pci_write_config(dev, AX_PCI_PWRMGMTCTRL, command, 4); - - /* Restore PCI config data. */ - pci_write_config(dev, AX_PCI_LOIO, iobase, 4); - pci_write_config(dev, AX_PCI_LOMEM, membase, 4); - pci_write_config(dev, AX_PCI_INTLINE, irq, 4); - } - } - - /* - * Map control/status registers. - */ - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - -#ifdef AX_USEIOSPACE - if (!(command & PCIM_CMD_PORTEN)) { - printf("ax%d: failed to enable I/O ports!\n", unit); - error = ENXIO;; - goto fail; - } -#else - if (!(command & PCIM_CMD_MEMEN)) { - printf("ax%d: failed to enable memory mapping!\n", unit); - error = ENXIO;; - goto fail; - } -#endif - - rid = AX_RID; - sc->ax_res = bus_alloc_resource(dev, AX_RES, &rid, - 0, ~0, 1, RF_ACTIVE); - - if (sc->ax_res == NULL) { - printf("ax%d: couldn't map ports/memory\n", unit); - error = ENXIO; - goto fail; - } - - sc->ax_btag = rman_get_bustag(sc->ax_res); - sc->ax_bhandle = rman_get_bushandle(sc->ax_res); - - /* Allocate interrupt */ - rid = 0; - sc->ax_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, - RF_SHAREABLE | RF_ACTIVE); - - if (sc->ax_irq == NULL) { - printf("ax%d: couldn't map interrupt\n", unit); - bus_release_resource(dev, AX_RES, AX_RID, sc->ax_res); - error = ENXIO; - goto fail; - } - - error = bus_setup_intr(dev, sc->ax_irq, INTR_TYPE_NET, - ax_intr, sc, &sc->ax_intrhand); - - if (error) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ax_res); - bus_release_resource(dev, AX_RES, AX_RID, sc->ax_res); - printf("ax%d: couldn't set up irq\n", unit); - goto fail; - } - - /* Reset the adapter. */ - ax_reset(sc); - - /* - * Get station address from the EEPROM. - */ - ax_read_eeprom(sc, (caddr_t)&eaddr, AX_EE_NODEADDR, 3, 0); - - /* - * An ASIX chip was detected. Inform the world. - */ - printf("ax%d: Ethernet address: %6D\n", unit, eaddr, ":"); - - sc->ax_unit = unit; - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - - sc->ax_ldata_ptr = malloc(sizeof(struct ax_list_data) + 8, - M_DEVBUF, M_NOWAIT); - if (sc->ax_ldata_ptr == NULL) { - printf("ax%d: no memory for list buffers!\n", unit); - bus_teardown_intr(dev, sc->ax_irq, sc->ax_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ax_res); - bus_release_resource(dev, AX_RES, AX_RID, sc->ax_res); - error = ENXIO; - goto fail; - } - - sc->ax_ldata = (struct ax_list_data *)sc->ax_ldata_ptr; - round = (uintptr_t)sc->ax_ldata_ptr & 0xF; - roundptr = sc->ax_ldata_ptr; - for (i = 0; i < 8; i++) { - if (round % 8) { - round++; - roundptr++; - } else - break; - } - sc->ax_ldata = (struct ax_list_data *)roundptr; - bzero(sc->ax_ldata, sizeof(struct ax_list_data)); - - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = "ax"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = ax_ioctl; - ifp->if_output = ether_output; - ifp->if_start = ax_start; - ifp->if_watchdog = ax_watchdog; - ifp->if_init = ax_init; - ifp->if_baudrate = 10000000; - ifp->if_snd.ifq_maxlen = AX_TX_LIST_CNT - 1; - - if (bootverbose) - printf("ax%d: probing for a PHY\n", sc->ax_unit); - for (i = AX_PHYADDR_MIN; i < AX_PHYADDR_MAX + 1; i++) { - if (bootverbose) - printf("ax%d: checking address: %d\n", - sc->ax_unit, i); - sc->ax_phy_addr = i; - ax_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); - DELAY(500); - while(ax_phy_readreg(sc, PHY_BMCR) - & PHY_BMCR_RESET); - if ((phy_sts = ax_phy_readreg(sc, PHY_BMSR))) - break; - } - if (phy_sts) { - phy_vid = ax_phy_readreg(sc, PHY_VENID); - phy_did = ax_phy_readreg(sc, PHY_DEVID); - if (bootverbose) - printf("ax%d: found PHY at address %d, ", - sc->ax_unit, sc->ax_phy_addr); - if (bootverbose) - printf("vendor id: %x device id: %x\n", - phy_vid, phy_did); - p = ax_phys; - while(p->ax_vid) { - if (phy_vid == p->ax_vid && - (phy_did | 0x000F) == p->ax_did) { - sc->ax_pinfo = p; - break; - } - p++; - } - if (sc->ax_pinfo == NULL) - sc->ax_pinfo = &ax_phys[PHY_UNKNOWN]; - if (bootverbose) - printf("ax%d: PHY type: %s\n", - sc->ax_unit, sc->ax_pinfo->ax_name); - } else { -#ifdef DIAGNOSTIC - printf("ax%d: MII without any phy!\n", sc->ax_unit); -#endif - } - - /* - * Do ifmedia setup. - */ - ifmedia_init(&sc->ifmedia, 0, ax_ifmedia_upd, ax_ifmedia_sts); - - if (sc->ax_pinfo != NULL) { - ax_getmode_mii(sc); - if (cold) { - ax_autoneg_mii(sc, AX_FLAG_FORCEDELAY, 1); - ax_stop(sc); - } else { - ax_init(sc); - ax_autoneg_mii(sc, AX_FLAG_SCHEDDELAY, 1); - } - } else { - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); - } - - media = sc->ifmedia.ifm_media; - ifmedia_set(&sc->ifmedia, media); - - /* - * Call MI attach routines. - */ - if_attach(ifp); - ether_ifattach(ifp); - - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - -fail: - splx(s); - return(error); -} - -static int ax_detach(dev) - device_t dev; -{ - struct ax_softc *sc; - struct ifnet *ifp; - int s; - - s = splimp(); - - sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - - ax_stop(sc); - if_detach(ifp); - - bus_teardown_intr(dev, sc->ax_irq, sc->ax_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ax_res); - bus_release_resource(dev, AX_RES, AX_RID, sc->ax_res); - - free(sc->ax_ldata_ptr, M_DEVBUF); - ifmedia_removeall(&sc->ifmedia); - - splx(s); - - return(0); -} - -/* - * Initialize the transmit descriptors. - */ -static int ax_list_tx_init(sc) - struct ax_softc *sc; -{ - struct ax_chain_data *cd; - struct ax_list_data *ld; - int i; - - cd = &sc->ax_cdata; - ld = sc->ax_ldata; - for (i = 0; i < AX_TX_LIST_CNT; i++) { - cd->ax_tx_chain[i].ax_ptr = &ld->ax_tx_list[i]; - if (i == (AX_TX_LIST_CNT - 1)) - cd->ax_tx_chain[i].ax_nextdesc = - &cd->ax_tx_chain[0]; - else - cd->ax_tx_chain[i].ax_nextdesc = - &cd->ax_tx_chain[i + 1]; - } - - cd->ax_tx_free = &cd->ax_tx_chain[0]; - cd->ax_tx_tail = cd->ax_tx_head = NULL; - - return(0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int ax_list_rx_init(sc) - struct ax_softc *sc; -{ - struct ax_chain_data *cd; - struct ax_list_data *ld; - int i; - - cd = &sc->ax_cdata; - ld = sc->ax_ldata; - - for (i = 0; i < AX_RX_LIST_CNT; i++) { - cd->ax_rx_chain[i].ax_ptr = - (volatile struct ax_desc *)&ld->ax_rx_list[i]; - if (ax_newbuf(sc, &cd->ax_rx_chain[i], NULL) == ENOBUFS) - return(ENOBUFS); - if (i == (AX_RX_LIST_CNT - 1)) { - cd->ax_rx_chain[i].ax_nextdesc = - &cd->ax_rx_chain[0]; - ld->ax_rx_list[i].ax_next = - vtophys(&ld->ax_rx_list[0]); - } else { - cd->ax_rx_chain[i].ax_nextdesc = - &cd->ax_rx_chain[i + 1]; - ld->ax_rx_list[i].ax_next = - vtophys(&ld->ax_rx_list[i + 1]); - } - } - - cd->ax_rx_head = &cd->ax_rx_chain[0]; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - * Note: the length fields are only 11 bits wide, which means the - * largest size we can specify is 2047. This is important because - * MCLBYTES is 2048, so we have to subtract one otherwise we'll - * overflow the field and make a mess. - */ -static int ax_newbuf(sc, c, m) - struct ax_softc *sc; - struct ax_chain_onefrag *c; - struct mbuf *m; -{ - struct mbuf *m_new = NULL; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("ax%d: no memory for rx list " - "-- packet dropped!\n", sc->ax_unit); - return(ENOBUFS); - } - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("ax%d: no memory for rx list " - "-- packet dropped!\n", sc->ax_unit); - m_freem(m_new); - return(ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - - m_adj(m_new, sizeof(u_int64_t)); - c->ax_mbuf = m_new; - c->ax_ptr->ax_status = AX_RXSTAT; - c->ax_ptr->ax_data = vtophys(mtod(m_new, caddr_t)); - c->ax_ptr->ax_ctl = MCLBYTES - 1; - - return(0); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void ax_rxeof(sc) - struct ax_softc *sc; -{ - struct ether_header *eh; - struct mbuf *m; - struct ifnet *ifp; - struct ax_chain_onefrag *cur_rx; - int total_len = 0; - u_int32_t rxstat; - - ifp = &sc->arpcom.ac_if; - - while(!((rxstat = sc->ax_cdata.ax_rx_head->ax_ptr->ax_status) & - AX_RXSTAT_OWN)) { - struct mbuf *m0 = NULL; - - cur_rx = sc->ax_cdata.ax_rx_head; - sc->ax_cdata.ax_rx_head = cur_rx->ax_nextdesc; - m = cur_rx->ax_mbuf; - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (rxstat & AX_RXSTAT_RXERR) { - ifp->if_ierrors++; - if (rxstat & AX_RXSTAT_COLLSEEN) - ifp->if_collisions++; - ax_newbuf(sc, cur_rx, m); - continue; - } - - /* No errors; receive the packet. */ - total_len = AX_RXBYTES(cur_rx->ax_ptr->ax_status); - - total_len -= ETHER_CRC_LEN; - - m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, - total_len + ETHER_ALIGN, 0, ifp, NULL); - ax_newbuf(sc, cur_rx, m); - if (m0 == NULL) { - ifp->if_ierrors++; - continue; - } - m_adj(m0, ETHER_ALIGN); - m = m0; - - ifp->if_ipackets++; - eh = mtod(m, struct ether_header *); - - /* - * Handle BPF listeners. Let the BPF user see the packet, but - * don't pass it up to the ether_input() layer unless it's - * a broadcast packet, multicast packet, matches our ethernet - * address or the interface is in promiscuous mode. - */ - if (ifp->if_bpf) { - bpf_mtap(ifp, m); - if (ifp->if_flags & IFF_PROMISC && - (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0)) { - m_freem(m); - continue; - } - } - - /* Remove header from mbuf and pass it on. */ - m_adj(m, sizeof(struct ether_header)); - ether_input(ifp, eh, m); - } - - return; -} - -void ax_rxeoc(sc) - struct ax_softc *sc; -{ - - ax_rxeof(sc); - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_RX_ON); - CSR_WRITE_4(sc, AX_RXADDR, vtophys(sc->ax_cdata.ax_rx_head->ax_ptr)); - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_RX_ON); - CSR_WRITE_4(sc, AX_RXSTART, 0xFFFFFFFF); - - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ - -static void ax_txeof(sc) - struct ax_softc *sc; -{ - struct ax_chain *cur_tx; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; - - if (sc->ax_cdata.ax_tx_head == NULL) - return; - - /* - * Go through our tx list and free mbufs for those - * frames that have been transmitted. - */ - while(sc->ax_cdata.ax_tx_head->ax_mbuf != NULL) { - u_int32_t txstat; - - cur_tx = sc->ax_cdata.ax_tx_head; - txstat = AX_TXSTATUS(cur_tx); - - if (txstat & AX_TXSTAT_OWN) - break; - - if (txstat & AX_TXSTAT_ERRSUM) { - ifp->if_oerrors++; - if (txstat & AX_TXSTAT_EXCESSCOLL) - ifp->if_collisions++; - if (txstat & AX_TXSTAT_LATECOLL) - ifp->if_collisions++; - } - - ifp->if_collisions += (txstat & AX_TXSTAT_COLLCNT) >> 3; - - ifp->if_opackets++; - m_freem(cur_tx->ax_mbuf); - cur_tx->ax_mbuf = NULL; - - if (sc->ax_cdata.ax_tx_head == sc->ax_cdata.ax_tx_tail) { - sc->ax_cdata.ax_tx_head = NULL; - sc->ax_cdata.ax_tx_tail = NULL; - break; - } - - sc->ax_cdata.ax_tx_head = cur_tx->ax_nextdesc; - } - - return; -} - -/* - * TX 'end of channel' interrupt handler. - */ -static void ax_txeoc(sc) - struct ax_softc *sc; -{ - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - ifp->if_timer = 0; - - if (sc->ax_cdata.ax_tx_head == NULL) { - ifp->if_flags &= ~IFF_OACTIVE; - sc->ax_cdata.ax_tx_tail = NULL; - if (sc->ax_want_auto) - ax_autoneg_mii(sc, AX_FLAG_DELAYTIMEO, 1); - } - - return; -} - -static void ax_intr(arg) - void *arg; -{ - struct ax_softc *sc; - struct ifnet *ifp; - u_int32_t status; - - sc = arg; - ifp = &sc->arpcom.ac_if; - - /* Supress unwanted interrupts */ - if (!(ifp->if_flags & IFF_UP)) { - ax_stop(sc); - return; - } - - /* Disable interrupts. */ - CSR_WRITE_4(sc, AX_IMR, 0x00000000); - - for (;;) { - status = CSR_READ_4(sc, AX_ISR); - if (status) - CSR_WRITE_4(sc, AX_ISR, status); - - if ((status & AX_INTRS) == 0) - break; - - if ((status & AX_ISR_TX_OK) || (status & AX_ISR_TX_EARLY)) - ax_txeof(sc); - - if (status & AX_ISR_TX_NOBUF) - ax_txeoc(sc); - - if (status & AX_ISR_TX_IDLE) { - ax_txeof(sc); - if (sc->ax_cdata.ax_tx_head != NULL) { - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_TX_ON); - CSR_WRITE_4(sc, AX_TXSTART, 0xFFFFFFFF); - } - } - - if (status & AX_ISR_TX_UNDERRUN) { - u_int32_t cfg; - cfg = CSR_READ_4(sc, AX_NETCFG); - if ((cfg & AX_NETCFG_TX_THRESH) == AX_TXTHRESH_160BYTES) - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_STORENFWD); - else - CSR_WRITE_4(sc, AX_NETCFG, cfg + 0x4000); - } - - if (status & AX_ISR_RX_OK) - ax_rxeof(sc); - - if ((status & AX_ISR_RX_WATDOGTIMEO) - || (status & AX_ISR_RX_NOBUF)) - ax_rxeoc(sc); - - if (status & AX_ISR_BUS_ERR) { - ax_reset(sc); - ax_init(sc); - } - } - - /* Re-enable interrupts. */ - CSR_WRITE_4(sc, AX_IMR, AX_INTRS); - - if (ifp->if_snd.ifq_head != NULL) { - ax_start(ifp); - } - - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int ax_encap(sc, c, m_head) - struct ax_softc *sc; - struct ax_chain *c; - struct mbuf *m_head; -{ - int frag = 0; - volatile struct ax_desc *f = NULL; - int total_len; - struct mbuf *m; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - m = m_head; - total_len = 0; - - for (m = m_head, frag = 0; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if (frag == AX_MAXFRAGS) - break; - total_len += m->m_len; - f = &c->ax_ptr->ax_frag[frag]; - f->ax_ctl = m->m_len; - if (frag == 0) { - f->ax_status = 0; - f->ax_ctl |= AX_TXCTL_FIRSTFRAG; - } else - f->ax_status = AX_TXSTAT_OWN; - f->ax_next = vtophys(&c->ax_ptr->ax_frag[frag + 1]); - f->ax_data = vtophys(mtod(m, vm_offset_t)); - frag++; - } - } - - /* - * Handle special case: we ran out of fragments, - * but we have more mbufs left in the chain. Copy the - * data into an mbuf cluster. Note that we don't - * bother clearing the values in the other fragment - * pointers/counters; it wouldn't gain us anything, - * and would waste cycles. - */ - if (m != NULL) { - struct mbuf *m_new = NULL; - - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("ax%d: no memory for tx list", sc->ax_unit); - return(1); - } - if (m_head->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - printf("ax%d: no memory for tx list", - sc->ax_unit); - return(1); - } - } - m_copydata(m_head, 0, m_head->m_pkthdr.len, - mtod(m_new, caddr_t)); - m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; - m_freem(m_head); - m_head = m_new; - f = &c->ax_ptr->ax_frag[0]; - f->ax_status = 0; - f->ax_data = vtophys(mtod(m_new, caddr_t)); - f->ax_ctl = total_len = m_new->m_len; - f->ax_ctl |= AX_TXCTL_FIRSTFRAG; - frag = 1; - } - - c->ax_mbuf = m_head; - c->ax_lastdesc = frag - 1; - AX_TXCTL(c) |= AX_TXCTL_LASTFRAG|AX_TXCTL_FINT; - c->ax_ptr->ax_frag[0].ax_ctl |= AX_TXCTL_FINT; - AX_TXNEXT(c) = vtophys(&c->ax_nextdesc->ax_ptr->ax_frag[0]); - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ - -static void ax_start(ifp) - struct ifnet *ifp; -{ - struct ax_softc *sc; - struct mbuf *m_head = NULL; - struct ax_chain *cur_tx = NULL, *start_tx; - - sc = ifp->if_softc; - - if (sc->ax_autoneg) { - sc->ax_tx_pend = 1; - return; - } - - /* - * Check for an available queue slot. If there are none, - * punt. - */ - if (sc->ax_cdata.ax_tx_free->ax_mbuf != NULL) { - ifp->if_flags |= IFF_OACTIVE; - return; - } - - start_tx = sc->ax_cdata.ax_tx_free; - - while(sc->ax_cdata.ax_tx_free->ax_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - /* Pick a descriptor off the free list. */ - cur_tx = sc->ax_cdata.ax_tx_free; - sc->ax_cdata.ax_tx_free = cur_tx->ax_nextdesc; - - /* Pack the data into the descriptor. */ - ax_encap(sc, cur_tx, m_head); - if (cur_tx != start_tx) - AX_TXOWN(cur_tx) = AX_TXSTAT_OWN; - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp, cur_tx->ax_mbuf); - - AX_TXOWN(cur_tx) = AX_TXSTAT_OWN; - CSR_WRITE_4(sc, AX_TXSTART, 0xFFFFFFFF); - } - - sc->ax_cdata.ax_tx_tail = cur_tx; - if (sc->ax_cdata.ax_tx_head == NULL) - sc->ax_cdata.ax_tx_head = start_tx; - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; -} - -static void ax_init(xsc) - void *xsc; -{ - struct ax_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - u_int16_t phy_bmcr = 0; - int s; - - if (sc->ax_autoneg) - return; - - s = splimp(); - - if (sc->ax_pinfo != NULL) - phy_bmcr = ax_phy_readreg(sc, PHY_BMCR); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - ax_stop(sc); - ax_reset(sc); - - /* - * Set cache alignment and burst length. - */ - CSR_WRITE_4(sc, AX_BUSCTL, AX_BUSCTL_CONFIG); - - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_HEARTBEAT); - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_STORENFWD); - - if (sc->ax_pinfo != NULL) { - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_PORTSEL); - ax_setcfg(sc, ax_phy_readreg(sc, PHY_BMCR)); - } else - ax_setmode(sc, sc->ifmedia.ifm_media, 0); - - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_TX_THRESH); - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_SPEEDSEL); - - if (IFM_SUBTYPE(sc->ifmedia.ifm_media) == IFM_10_T) - AX_SETBIT(sc, AX_NETCFG, AX_TXTHRESH_160BYTES); - else - AX_SETBIT(sc, AX_NETCFG, AX_TXTHRESH_72BYTES); - - /* Init our MAC address */ - CSR_WRITE_4(sc, AX_FILTIDX, AX_FILTIDX_PAR0); - CSR_WRITE_4(sc, AX_FILTDATA, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); - CSR_WRITE_4(sc, AX_FILTIDX, AX_FILTIDX_PAR1); - CSR_WRITE_4(sc, AX_FILTDATA, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); - - /* Init circular RX list. */ - if (ax_list_rx_init(sc) == ENOBUFS) { - printf("ax%d: initialization failed: no " - "memory for rx buffers\n", sc->ax_unit); - ax_stop(sc); - (void)splx(s); - return; - } - - /* - * Init tx descriptors. - */ - ax_list_tx_init(sc); - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) { - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_RX_PROMISC); - } else { - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_RX_PROMISC); - } - - /* - * Set the capture broadcast bit to capture broadcast frames. - */ - if (ifp->if_flags & IFF_BROADCAST) { - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_RX_BROAD); - } else { - AX_CLRBIT(sc, AX_NETCFG, AX_NETCFG_RX_BROAD); - } - - /* - * Load the multicast filter. - */ - ax_setmulti(sc); - - /* - * Load the address of the RX list. - */ - CSR_WRITE_4(sc, AX_RXADDR, vtophys(sc->ax_cdata.ax_rx_head->ax_ptr)); - CSR_WRITE_4(sc, AX_TXADDR, vtophys(&sc->ax_ldata->ax_tx_list[0])); - - /* - * Enable interrupts. - */ - CSR_WRITE_4(sc, AX_IMR, AX_INTRS); - CSR_WRITE_4(sc, AX_ISR, 0xFFFFFFFF); - - /* Enable receiver and transmitter. */ - AX_SETBIT(sc, AX_NETCFG, AX_NETCFG_TX_ON|AX_NETCFG_RX_ON); - CSR_WRITE_4(sc, AX_RXSTART, 0xFFFFFFFF); - - /* Restore state of BMCR */ - if (sc->ax_pinfo != NULL) - ax_phy_writereg(sc, PHY_BMCR, phy_bmcr); - - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - (void)splx(s); - - return; -} - -/* - * Set media options. - */ -static int ax_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct ax_softc *sc; - struct ifmedia *ifm; - - sc = ifp->if_softc; - ifm = &sc->ifmedia; - - if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) - return(EINVAL); - - if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) - ax_autoneg_mii(sc, AX_FLAG_SCHEDDELAY, 1); - else { - if (sc->ax_pinfo == NULL) - ax_setmode(sc, ifm->ifm_media, 1); - else - ax_setmode_mii(sc, ifm->ifm_media); - } - - return(0); -} - -/* - * Report current media status. - */ -static void ax_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct ax_softc *sc; - u_int16_t advert = 0, ability = 0; - u_int32_t media = 0; - - sc = ifp->if_softc; - - ifmr->ifm_active = IFM_ETHER; - - if (sc->ax_pinfo == NULL) { - media = CSR_READ_4(sc, AX_NETCFG); - if (media & AX_NETCFG_PORTSEL) - ifmr->ifm_active = IFM_ETHER|IFM_100_TX; - else - ifmr->ifm_active = IFM_ETHER|IFM_10_T; - if (media & AX_NETCFG_FULLDUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_HDX; - return; - } - - if (!(ax_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { - if (ax_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL) - ifmr->ifm_active = IFM_ETHER|IFM_100_TX; - else - ifmr->ifm_active = IFM_ETHER|IFM_10_T; - if (ax_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_HDX; - return; - } - - ability = ax_phy_readreg(sc, PHY_LPAR); - advert = ax_phy_readreg(sc, PHY_ANAR); - if (advert & PHY_ANAR_100BT4 && - ability & PHY_ANAR_100BT4) { - ifmr->ifm_active = IFM_ETHER|IFM_100_T4; - } else if (advert & PHY_ANAR_100BTXFULL && - ability & PHY_ANAR_100BTXFULL) { - ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_FDX; - } else if (advert & PHY_ANAR_100BTXHALF && - ability & PHY_ANAR_100BTXHALF) { - ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_HDX; - } else if (advert & PHY_ANAR_10BTFULL && - ability & PHY_ANAR_10BTFULL) { - ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_FDX; - } else if (advert & PHY_ANAR_10BTHALF && - ability & PHY_ANAR_10BTHALF) { - ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_HDX; - } - - return; -} - -static int ax_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct ax_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *) data; - int s, error = 0; - - s = splimp(); - - switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - ax_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - ax_stop(sc); - } - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - ax_setmulti(sc); - error = 0; - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); - break; - default: - error = EINVAL; - break; - } - - (void)splx(s); - - return(error); -} - -static void ax_watchdog(ifp) - struct ifnet *ifp; -{ - struct ax_softc *sc; - - sc = ifp->if_softc; - - if (sc->ax_autoneg) { - ax_autoneg_mii(sc, AX_FLAG_DELAYTIMEO, 1); - if (!(ifp->if_flags & IFF_UP)) - ax_stop(sc); - return; - } - - ifp->if_oerrors++; - printf("ax%d: watchdog timeout\n", sc->ax_unit); - - if (sc->ax_pinfo != NULL) { - if (!(ax_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) - printf("ax%d: no carrier - transceiver " - "cable problem?\n", sc->ax_unit); - } - - ax_stop(sc); - ax_reset(sc); - ax_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - ax_start(ifp); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void ax_stop(sc) - struct ax_softc *sc; -{ - register int i; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - AX_CLRBIT(sc, AX_NETCFG, (AX_NETCFG_RX_ON|AX_NETCFG_TX_ON)); - CSR_WRITE_4(sc, AX_IMR, 0x00000000); - CSR_WRITE_4(sc, AX_TXADDR, 0x00000000); - CSR_WRITE_4(sc, AX_RXADDR, 0x00000000); - - /* - * Free data in the RX lists. - */ - for (i = 0; i < AX_RX_LIST_CNT; i++) { - if (sc->ax_cdata.ax_rx_chain[i].ax_mbuf != NULL) { - m_freem(sc->ax_cdata.ax_rx_chain[i].ax_mbuf); - sc->ax_cdata.ax_rx_chain[i].ax_mbuf = NULL; - } - } - bzero((char *)&sc->ax_ldata->ax_rx_list, - sizeof(sc->ax_ldata->ax_rx_list)); - - /* - * Free the TX list buffers. - */ - for (i = 0; i < AX_TX_LIST_CNT; i++) { - if (sc->ax_cdata.ax_tx_chain[i].ax_mbuf != NULL) { - m_freem(sc->ax_cdata.ax_tx_chain[i].ax_mbuf); - sc->ax_cdata.ax_tx_chain[i].ax_mbuf = NULL; - } - } - - bzero((char *)&sc->ax_ldata->ax_tx_list, - sizeof(sc->ax_ldata->ax_tx_list)); - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - return; -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static void ax_shutdown(dev) - device_t dev; -{ - struct ax_softc *sc; - - sc = device_get_softc(dev); - - ax_stop(sc); - - return; -} diff --git a/sys/pci/if_axreg.h b/sys/pci/if_axreg.h deleted file mode 100644 index 1d3c3a6..0000000 --- a/sys/pci/if_axreg.h +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * ASIX register definitions. - */ - -#define AX_BUSCTL 0x00 /* bus control */ -#define AX_TXSTART 0x08 /* tx start demand */ -#define AX_RXSTART 0x10 /* rx start demand */ -#define AX_RXADDR 0x18 /* rx descriptor list start addr */ -#define AX_TXADDR 0x20 /* tx descriptor list start addr */ -#define AX_ISR 0x28 /* interrupt status register */ -#define AX_NETCFG 0x30 /* network config register */ -#define AX_IMR 0x38 /* interrupt mask */ -#define AX_FRAMESDISCARDED 0x40 /* # of discarded frames */ -#define AX_SIO 0x48 /* MII and ROM/EEPROM access */ -#define AX_RESERVED 0x50 -#define AX_GENTIMER 0x58 /* general timer */ -#define AX_GENPORT 0x60 /* general purpose port */ -#define AX_FILTIDX 0x68 /* RX filter index */ -#define AX_FILTDATA 0x70 /* RX filter data */ - -/* - * Bus control bits. - */ -#define AX_BUSCTL_RESET 0x00000001 -#define AX_BUSCTL_ARBITRATION 0x00000002 -#define AX_BUSCTL_BIGENDIAN 0x00000080 -#define AX_BUSCTL_BURSTLEN 0x00003F00 -#define AX_BUSCTL_BUF_BIGENDIAN 0x00100000 -#define AX_BISCTL_READMULTI 0x00200000 - -#define AX_BURSTLEN_UNLIMIT 0x00000000 -#define AX_BURSTLEN_1LONG 0x00000100 -#define AX_BURSTLEN_2LONG 0x00000200 -#define AX_BURSTLEN_4LONG 0x00000400 -#define AX_BURSTLEN_8LONG 0x00000800 -#define AX_BURSTLEN_16LONG 0x00001000 -#define AX_BURSTLEN_32LONG 0x00002000 - -#define AX_BUSCTL_CONFIG (AX_BUSCTL_ARBITRATION|AX_BURSTLEN_8LONG|AX_BURSTLEN_8LONG) - -/* - * Interrupt status bits. - */ -#define AX_ISR_TX_OK 0x00000001 -#define AX_ISR_TX_IDLE 0x00000002 -#define AX_ISR_TX_NOBUF 0x00000004 -#define AX_ISR_TX_JABBERTIMEO 0x00000008 -#define AX_ISR_TX_UNDERRUN 0x00000020 -#define AX_ISR_RX_OK 0x00000040 -#define AX_ISR_RX_NOBUF 0x00000080 -#define AX_ISR_RX_IDLE 0x00000100 -#define AX_ISR_RX_WATDOGTIMEO 0x00000200 -#define AX_ISR_TX_EARLY 0x00000400 -#define AX_ISR_TIMER_EXPIRED 0x00000800 -#define AX_ISR_BUS_ERR 0x00002000 -#define AX_ISR_ABNORMAL 0x00008000 -#define AX_ISR_NORMAL 0x00010000 -#define AX_ISR_RX_STATE 0x000E0000 -#define AX_ISR_TX_STATE 0x00700000 -#define AX_ISR_BUSERRTYPE 0x03800000 - -#define AX_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ -#define AX_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ -#define AX_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ -#define AX_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ -#define AX_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ -#define AX_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ -#define AX_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ -#define AX_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ - -#define AX_TXSTATE_RESET 0x00000000 /* 000 - reset */ -#define AX_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ -#define AX_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ -#define AX_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ -#define AX_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ -#define AX_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ -#define AX_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ -#define AX_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ - -/* - * Network config bits. - */ -#define AX_NETCFG_LINKSTAT_PCS 0x00000001 -#define AX_NETCFG_RX_ON 0x00000002 -#define AX_NETCFG_RX_BADFRAMES 0x00000008 -#define AX_NETCFG_RX_PROMISC 0x00000040 -#define AX_NETCFG_RX_ALLMULTI 0x00000080 -#define AX_NETCFG_RX_BROAD 0x00000100 -#define AX_NETCFG_FULLDUPLEX 0x00000200 -#define AX_NETCFG_LOOPBACK 0x00000C00 -#define AX_NETCFG_FORCECOLL 0x00001000 -#define AX_NETCFG_TX_ON 0x00002000 -#define AX_NETCFG_TX_THRESH 0x0000C000 -#define AX_NETCFG_PORTSEL 0x00040000 /* 0 == SRL, 1 == MII/SYM */ -#define AX_NETCFG_HEARTBEAT 0x00080000 /* 0 == ON, 1 == OFF */ -#define AX_NETCFG_STORENFWD 0x00200000 -#define AX_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */ -#define AX_NETCFG_PCS 0x00800000 -#define AX_NETCFG_SCRAMBLER 0x01000000 -#define AX_NETCFG_RX_ALL 0x40000000 - -#define AX_OPMODE_NORM 0x00000000 -#define AX_OPMODE_INTLOOP 0x00000400 -#define AX_OPMODE_EXTLOOP 0x00000800 - -#define AX_TXTHRESH_72BYTES 0x00000000 -#define AX_TXTHRESH_96BYTES 0x00004000 -#define AX_TXTHRESH_128BYTES 0x00008000 -#define AX_TXTHRESH_160BYTES 0x0000C000 - -/* - * Interrupt mask bits. - */ -#define AX_IMR_TX_OK 0x00000001 -#define AX_IMR_TX_IDLE 0x00000002 -#define AX_IMR_TX_NOBUF 0x00000004 -#define AX_IMR_TX_JABBERTIMEO 0x00000008 -#define AX_IMR_TX_UNDERRUN 0x00000020 -#define AX_IMR_RX_OK 0x00000040 -#define AX_IMR_RX_NOBUF 0x00000080 -#define AX_IMR_RX_IDLE 0x00000100 -#define AX_IMR_RX_WATDOGTIMEO 0x00000200 -#define AX_IMR_TX_EARLY 0x00000400 -#define AX_IMR_TIMER_EXPIRED 0x00000800 -#define AX_IMR_BUS_ERR 0x00002000 -#define AX_IMR_RX_EARLY 0x00004000 -#define AX_IMR_ABNORMAL 0x00008000 -#define AX_IMR_NORMAL 0x00010000 - -#define AX_INTRS \ - (AX_IMR_RX_OK|AX_IMR_TX_OK|AX_IMR_RX_NOBUF|AX_IMR_RX_WATDOGTIMEO|\ - AX_IMR_TX_NOBUF|AX_IMR_TX_UNDERRUN|AX_IMR_BUS_ERR| \ - AX_IMR_ABNORMAL|AX_IMR_NORMAL|/*AX_IMR_TX_EARLY*/ \ - AX_IMR_TX_IDLE|AX_IMR_RX_IDLE) - -/* - * Serial I/O (EEPROM/ROM) bits. - */ -#define AX_SIO_EE_CS 0x00000001 /* EEPROM chip select */ -#define AX_SIO_EE_CLK 0x00000002 /* EEPROM clock */ -#define AX_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ -#define AX_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ -#define AX_SIO_EESEL 0x00000800 -#define AX_SIO_ROMSEL 0x00001000 -#define AX_SIO_ROMCTL_WRITE 0x00002000 -#define AX_SIO_ROMCTL_READ 0x00004000 -#define AX_SIO_MII_CLK 0x00010000 /* MDIO clock */ -#define AX_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ -#define AX_SIO_MII_DIR 0x00040000 /* MDIO dir */ -#define AX_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ - -#define AX_EECMD_WRITE 0x140 -#define AX_EECMD_READ 0x180 -#define AX_EECMD_ERASE 0x1c0 - -#define AX_EE_NODEADDR_OFFSET 0x70 -#define AX_EE_NODEADDR 10 - -/* - * General purpose timer register - */ -#define AX_TIMER_VALUE 0x0000FFFF -#define AX_TIMER_CONTINUOUS 0x00010000 - -/* - * RX Filter Index Register values - */ -#define AX_FILTIDX_PAR0 0x00000000 -#define AX_FILTIDX_PAR1 0x00000001 -#define AX_FILTIDX_MAR0 0x00000002 -#define AX_FILTIDX_MAR1 0x00000003 - -/* - * ASIX TX/RX list structure. - */ - -struct ax_desc { - volatile u_int32_t ax_status; - volatile u_int32_t ax_ctl; - volatile u_int32_t ax_ptr1; - volatile u_int32_t ax_ptr2; -}; - -#define ax_data ax_ptr1 -#define ax_next ax_ptr2 - -#define AX_RXSTAT_FIFOOFLOW 0x00000001 -#define AX_RXSTAT_CRCERR 0x00000002 -#define AX_RXSTAT_DRIBBLE 0x00000004 -#define AX_RXSTAT_WATCHDOG 0x00000010 -#define AX_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ -#define AX_RXSTAT_COLLSEEN 0x00000040 -#define AX_RXSTAT_GIANT 0x00000080 -#define AX_RXSTAT_LASTFRAG 0x00000100 -#define AX_RXSTAT_FIRSTFRAG 0x00000200 -#define AX_RXSTAT_MULTICAST 0x00000400 -#define AX_RXSTAT_RUNT 0x00000800 -#define AX_RXSTAT_RXTYPE 0x00003000 -#define AX_RXSTAT_RXERR 0x00008000 -#define AX_RXSTAT_RXLEN 0x3FFF0000 -#define AX_RXSTAT_OWN 0x80000000 - -#define AX_RXBYTES(x) ((x & AX_RXSTAT_RXLEN) >> 16) -#define AX_RXSTAT (AX_RXSTAT_FIRSTFRAG|AX_RXSTAT_LASTFRAG|AX_RXSTAT_OWN) - -#define AX_RXCTL_BUFLEN1 0x00000FFF -#define AX_RXCTL_BUFLEN2 0x00FFF000 -#define AX_RXCTL_RLAST 0x02000000 - -#define AX_TXSTAT_DEFER 0x00000001 -#define AX_TXSTAT_UNDERRUN 0x00000002 -#define AX_TXSTAT_LINKFAIL 0x00000003 -#define AX_TXSTAT_COLLCNT 0x00000078 -#define AX_TXSTAT_SQE 0x00000080 -#define AX_TXSTAT_EXCESSCOLL 0x00000100 -#define AX_TXSTAT_LATECOLL 0x00000200 -#define AX_TXSTAT_NOCARRIER 0x00000400 -#define AX_TXSTAT_CARRLOST 0x00000800 -#define AX_TXSTAT_JABTIMEO 0x00004000 -#define AX_TXSTAT_ERRSUM 0x00008000 -#define AX_TXSTAT_OWN 0x80000000 - -#define AX_TXCTL_BUFLEN1 0x000007FF -#define AX_TXCTL_BUFLEN2 0x003FF800 -#define AX_TXCTL_PAD 0x00800000 -#define AX_TXCTL_TLAST 0x02000000 -#define AX_TXCTL_NOCRC 0x04000000 -#define AX_TXCTL_FIRSTFRAG 0x20000000 -#define AX_TXCTL_LASTFRAG 0x40000000 -#define AX_TXCTL_FINT 0x80000000 - -#define AX_MAXFRAGS 16 -#define AX_RX_LIST_CNT 64 -#define AX_TX_LIST_CNT 128 -#define AX_MIN_FRAMELEN 60 - -/* - * A tx 'super descriptor' is actually 16 regular descriptors - * back to back. - */ -struct ax_txdesc { - volatile struct ax_desc ax_frag[AX_MAXFRAGS]; -}; - -#define AX_TXNEXT(x) x->ax_ptr->ax_frag[x->ax_lastdesc].ax_next -#define AX_TXSTATUS(x) x->ax_ptr->ax_frag[x->ax_lastdesc].ax_status -#define AX_TXCTL(x) x->ax_ptr->ax_frag[x->ax_lastdesc].ax_ctl -#define AX_TXDATA(x) x->ax_ptr->ax_frag[x->ax_lastdesc].ax_data - -#define AX_TXOWN(x) x->ax_ptr->ax_frag[0].ax_status - -#define AX_UNSENT 0x12341234 - -struct ax_list_data { - volatile struct ax_desc ax_rx_list[AX_RX_LIST_CNT]; - volatile struct ax_txdesc ax_tx_list[AX_TX_LIST_CNT]; -}; - -struct ax_chain { - volatile struct ax_txdesc *ax_ptr; - struct mbuf *ax_mbuf; - struct ax_chain *ax_nextdesc; - u_int8_t ax_lastdesc; -}; - -struct ax_chain_onefrag { - volatile struct ax_desc *ax_ptr; - struct mbuf *ax_mbuf; - struct ax_chain_onefrag *ax_nextdesc; -}; - -struct ax_chain_data { - struct ax_chain_onefrag ax_rx_chain[AX_RX_LIST_CNT]; - struct ax_chain ax_tx_chain[AX_TX_LIST_CNT]; - - struct ax_chain_onefrag *ax_rx_head; - - struct ax_chain *ax_tx_head; - struct ax_chain *ax_tx_tail; - struct ax_chain *ax_tx_free; -}; - -struct ax_type { - u_int16_t ax_vid; - u_int16_t ax_did; - char *ax_name; -}; - -struct ax_mii_frame { - u_int8_t mii_stdelim; - u_int8_t mii_opcode; - u_int8_t mii_phyaddr; - u_int8_t mii_regaddr; - u_int8_t mii_turnaround; - u_int16_t mii_data; -}; - -/* - * MII constants - */ -#define AX_MII_STARTDELIM 0x01 -#define AX_MII_READOP 0x02 -#define AX_MII_WRITEOP 0x01 -#define AX_MII_TURNAROUND 0x02 - -#define AX_FLAG_FORCEDELAY 1 -#define AX_FLAG_SCHEDDELAY 2 -#define AX_FLAG_DELAYTIMEO 3 - -struct ax_softc { - struct arpcom arpcom; /* interface info */ - struct ifmedia ifmedia; /* media info */ - bus_space_handle_t ax_bhandle; /* bus space handle */ - bus_space_tag_t ax_btag; /* bus space tag */ - void *ax_intrhand; - struct resource *ax_irq; - struct resource *ax_res; - struct ax_type *ax_info; /* ASIX adapter info */ - struct ax_type *ax_pinfo; /* phy info */ - u_int8_t ax_unit; /* interface number */ - u_int8_t ax_type; - u_int8_t ax_phy_addr; /* PHY address */ - u_int8_t ax_tx_pend; /* TX pending */ - u_int8_t ax_want_auto; - u_int8_t ax_autoneg; - caddr_t ax_ldata_ptr; - struct ax_list_data *ax_ldata; - struct ax_chain_data ax_cdata; -}; - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->ax_btag, sc->ax_bhandle, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->ax_btag, sc->ax_bbhandle, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->ax_btag, sc->ax_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->ax_btag, sc->ax_bhandle, reg) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->ax_btag, sc->ax_bhandle, reg) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->ax_btag, sc->ax_bhandle, reg) - -#define AX_TIMEOUT 1000 -#define ETHER_ALIGN 2 - -/* - * General constants that are fun to know. - * - * ASIX PCI vendor ID - */ -#define AX_VENDORID 0x125B - -/* - * ASIX device IDs. - */ -#define AX_DEVICEID_AX88140A 0x1400 - -/* - * The ASIX AX88140 and ASIX AX88141 have the same vendor and - * device IDs but different revision values. - */ -#define AX_REVISION_88140 0x00 -#define AX_REVISION_88141 0x10 - -/* - * Texas Instruments PHY identifiers - */ -#define TI_PHY_VENDORID 0x4000 -#define TI_PHY_10BT 0x501F -#define TI_PHY_100VGPMI 0x502F - -/* - * These ID values are for the NS DP83840A 10/100 PHY - */ -#define NS_PHY_VENDORID 0x2000 -#define NS_PHY_83840A 0x5C0F - -/* - * Level 1 10/100 PHY - */ -#define LEVEL1_PHY_VENDORID 0x7810 -#define LEVEL1_PHY_LXT970 0x000F - -/* - * Intel 82555 10/100 PHY - */ -#define INTEL_PHY_VENDORID 0x0A28 -#define INTEL_PHY_82555 0x015F - -/* - * SEEQ 80220 10/100 PHY - */ -#define SEEQ_PHY_VENDORID 0x0016 -#define SEEQ_PHY_80220 0xF83F - - -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. - */ - -#define AX_PCI_VENDOR_ID 0x00 -#define AX_PCI_DEVICE_ID 0x02 -#define AX_PCI_COMMAND 0x04 -#define AX_PCI_STATUS 0x06 -#define AX_PCI_REVID 0x08 -#define AX_PCI_CLASSCODE 0x09 -#define AX_PCI_LATENCY_TIMER 0x0D -#define AX_PCI_HEADER_TYPE 0x0E -#define AX_PCI_LOIO 0x10 -#define AX_PCI_LOMEM 0x14 -#define AX_PCI_BIOSROM 0x30 -#define AX_PCI_INTLINE 0x3C -#define AX_PCI_INTPIN 0x3D -#define AX_PCI_MINGNT 0x3E -#define AX_PCI_MINLAT 0x0F -#define AX_PCI_RESETOPT 0x48 -#define AX_PCI_EEPROM_DATA 0x4C - -/* power management registers */ -#define AX_PCI_CAPID 0x44 /* 8 bits */ -#define AX_PCI_NEXTPTR 0x45 /* 8 bits */ -#define AX_PCI_PWRMGMTCAP 0x46 /* 16 bits */ -#define AX_PCI_PWRMGMTCTRL 0x48 /* 16 bits */ - -#define AX_PSTATE_MASK 0x0003 -#define AX_PSTATE_D0 0x0000 -#define AX_PSTATE_D1 0x0001 -#define AX_PSTATE_D2 0x0002 -#define AX_PSTATE_D3 0x0003 -#define AX_PME_EN 0x0010 -#define AX_PME_STATUS 0x8000 - -#define PHY_UNKNOWN 6 - -#define AX_PHYADDR_MIN 0x00 -#define AX_PHYADDR_MAX 0x1F - -#define PHY_BMCR 0x00 -#define PHY_BMSR 0x01 -#define PHY_VENID 0x02 -#define PHY_DEVID 0x03 -#define PHY_ANAR 0x04 -#define PHY_LPAR 0x05 -#define PHY_ANEXP 0x06 - -#define PHY_ANAR_NEXTPAGE 0x8000 -#define PHY_ANAR_RSVD0 0x4000 -#define PHY_ANAR_TLRFLT 0x2000 -#define PHY_ANAR_RSVD1 0x1000 -#define PHY_ANAR_RSVD2 0x0800 -#define PHY_ANAR_RSVD3 0x0400 -#define PHY_ANAR_100BT4 0x0200 -#define PHY_ANAR_100BTXFULL 0x0100 -#define PHY_ANAR_100BTXHALF 0x0080 -#define PHY_ANAR_10BTFULL 0x0040 -#define PHY_ANAR_10BTHALF 0x0020 -#define PHY_ANAR_PROTO4 0x0010 -#define PHY_ANAR_PROTO3 0x0008 -#define PHY_ANAR_PROTO2 0x0004 -#define PHY_ANAR_PROTO1 0x0002 -#define PHY_ANAR_PROTO0 0x0001 - -/* - * These are the register definitions for the PHY (physical layer - * interface chip). - */ -/* - * PHY BMCR Basic Mode Control Register - */ -#define PHY_BMCR_RESET 0x8000 -#define PHY_BMCR_LOOPBK 0x4000 -#define PHY_BMCR_SPEEDSEL 0x2000 -#define PHY_BMCR_AUTONEGENBL 0x1000 -#define PHY_BMCR_RSVD0 0x0800 /* write as zero */ -#define PHY_BMCR_ISOLATE 0x0400 -#define PHY_BMCR_AUTONEGRSTR 0x0200 -#define PHY_BMCR_DUPLEX 0x0100 -#define PHY_BMCR_COLLTEST 0x0080 -#define PHY_BMCR_RSVD1 0x0040 /* write as zero, don't care */ -#define PHY_BMCR_RSVD2 0x0020 /* write as zero, don't care */ -#define PHY_BMCR_RSVD3 0x0010 /* write as zero, don't care */ -#define PHY_BMCR_RSVD4 0x0008 /* write as zero, don't care */ -#define PHY_BMCR_RSVD5 0x0004 /* write as zero, don't care */ -#define PHY_BMCR_RSVD6 0x0002 /* write as zero, don't care */ -#define PHY_BMCR_RSVD7 0x0001 /* write as zero, don't care */ -/* - * RESET: 1 == software reset, 0 == normal operation - * Resets status and control registers to default values. - * Relatches all hardware config values. - * - * LOOPBK: 1 == loopback operation enabled, 0 == normal operation - * - * SPEEDSEL: 1 == 100Mb/s, 0 == 10Mb/s - * Link speed is selected byt his bit or if auto-negotiation if bit - * 12 (AUTONEGENBL) is set (in which case the value of this register - * is ignored). - * - * AUTONEGENBL: 1 == Autonegotiation enabled, 0 == Autonegotiation disabled - * Bits 8 and 13 are ignored when autoneg is set, otherwise bits 8 and 13 - * determine speed and mode. Should be cleared and then set if PHY configured - * for no autoneg on startup. - * - * ISOLATE: 1 == isolate PHY from MII, 0 == normal operation - * - * AUTONEGRSTR: 1 == restart autonegotiation, 0 = normal operation - * - * DUPLEX: 1 == full duplex mode, 0 == half duplex mode - * - * COLLTEST: 1 == collision test enabled, 0 == normal operation - */ - -/* - * PHY, BMSR Basic Mode Status Register - */ -#define PHY_BMSR_100BT4 0x8000 -#define PHY_BMSR_100BTXFULL 0x4000 -#define PHY_BMSR_100BTXHALF 0x2000 -#define PHY_BMSR_10BTFULL 0x1000 -#define PHY_BMSR_10BTHALF 0x0800 -#define PHY_BMSR_RSVD1 0x0400 /* write as zero, don't care */ -#define PHY_BMSR_RSVD2 0x0200 /* write as zero, don't care */ -#define PHY_BMSR_RSVD3 0x0100 /* write as zero, don't care */ -#define PHY_BMSR_RSVD4 0x0080 /* write as zero, don't care */ -#define PHY_BMSR_MFPRESUP 0x0040 -#define PHY_BMSR_AUTONEGCOMP 0x0020 -#define PHY_BMSR_REMFAULT 0x0010 -#define PHY_BMSR_CANAUTONEG 0x0008 -#define PHY_BMSR_LINKSTAT 0x0004 -#define PHY_BMSR_JABBER 0x0002 -#define PHY_BMSR_EXTENDED 0x0001 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c new file mode 100644 index 0000000..25491e0 --- /dev/null +++ b/sys/pci/if_dc.c @@ -0,0 +1,2689 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143 + * series chips and several workalikes including the following: + * + * Macronix 98713/98715/98725 PMAC (www.macronix.com) + * Macronix/Lite-On 82c115 PNIC II (www.macronix.com) + * Lite-On 82c168/82c169 PNIC (www.litecom.com) + * ASIX Electronics AX88140A (www.asix.com.tw) + * ASIX Electronics AX88141 (www.asix.com.tw) + * ADMtek AL981 (www.admtek.com.tw) + * ADMtek AN985 (www.admtek.com.tw) + * Davicom DM9100, DM9102 (www.davicom8.com) + * + * Datasheets for the 21143 are available at developer.intel.com. + * Datasheets for the clone parts can be found at their respective sites. + * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.) + * The PNIC II is essentially a Macronix 98715A chip; the only difference + * worth noting is that its multicast hash table is only 128 bits wide + * instead of 512. + * + * Written by Bill Paul <wpaul@ee.columbia.edu> + * Electrical Engineering Department + * Columbia University, New York City + */ + +/* + * The Intel 21143 is the successor to the DEC 21140. It is basically + * the same as the 21140 but with a few new features. The 21143 supports + * three kinds of media attachments: + * + * o MII port, for 10Mbps and 100Mbps support and NWAY + * autonegotiation provided by an external PHY. + * o SYM port, for symbol mode 100Mbps support. + * o 10baseT port. + * o AUI/BNC port. + * + * The 100Mbps SYM port and 10baseT port can be used together in + * combination with the internal NWAY support to create a 10/100 + * autosensing configuration. + * + * Knowing which media is available on a given card is tough: you're + * supposed to go slogging through the EEPROM looking for media + * description structures. Unfortunately, some card vendors that use + * the 21143 don't obey the DEC SROM spec correctly, which means that + * what you find in the EEPROM may not agree with reality. Fortunately, + * the 21143 provides us a way to get around this issue: lurking in + * PCI configuration space is the Configuration Wake-Up Command Register. + * This register is loaded with a value from the EEPROM when wake on LAN + * mode is enabled; this value tells us quite clearly what kind of media + * is attached to the NIC. The main purpose of this register is to tell + * the NIC what media to scan when in wake on LAN mode, however by + * forcibly enabling wake on LAN mode, we can use to learn what kind of + * media a given NIC has available and adapt ourselves accordingly. + * + * Of course, if the media description blocks in the EEPROM are bogus. + * what are the odds that the CWUC aren't bogus as well, right? Well, + * the CWUC value is more likely to be correct since wake on LAN mode + * won't work correctly without it, and wake on LAN is a big selling + * point these days. It's also harder to screw up a single byte than + * a whole media descriptor block. + * + * Note that not all tulip workalikes are handled in this driver: we only + * deal with those which are relatively well behaved. The Winbond is + * handled separately due to its different register offsets and the + * special handling needed for its various bugs. The PNIC is handled + * here, but I'm not thrilled about it. + * + * All of the workalike chips use some form of MII transceiver support + * with the exception of the Macronix chips, which also have a SYM port. + * The ASIX AX88140A is also documented to have a SYM port, but all + * the cards I've seen use an MII transceiver, probably because the + * AX88140A doesn't support internal NWAY. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#include <net/bpf.h> + +#include <vm/vm.h> /* for vtophys */ +#include <vm/pmap.h> /* for vtophys */ +#include <machine/clock.h> /* for DELAY */ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#define DC_USEIOSPACE + +#include <pci/if_dcreg.h> + +/* "controller miibus0" required. See GENERIC if you get errors here. */ +#include "miibus_if.h" + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * Various supported device vendors/types and their names. + */ +static struct dc_type dc_devs[] = { + { DC_VENDORID_DEC, DC_DEVICEID_21143, + "Intel 21143 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100, + "Davicom DM9100 10/100BaseTX" }, + { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, + "Davicom DM9102 10/100BaseTX" }, + { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981, + "ADMtek AL981 10/100BaseTX" }, + { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985, + "ADMtek AN985 10/100BaseTX" }, + { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, + "ASIX AX88140A 10/100BaseTX" }, + { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, + "ASIX AX88141 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98713, + "Macronix 98713 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_98713, + "Macronix 98713A 10/100BaseTX" }, + { DC_VENDORID_CP, DC_DEVICEID_98713_CP, + "Compex RL100-TX 10/100BaseTX" }, + { DC_VENDORID_CP, DC_DEVICEID_98713_CP, + "Compex RL100-TX 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98715/98715A 10/100BaseTX" }, + { DC_VENDORID_MX, DC_DEVICEID_987x5, + "Macronix 98725 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C115, + "LC82C115 PNIC II 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C168, + "82c168 PNIC 10/100BaseTX" }, + { DC_VENDORID_LO, DC_DEVICEID_82C168, + "82c169 PNIC 10/100BaseTX" }, + { 0, 0, NULL } +}; + +static int dc_probe __P((device_t)); +static int dc_attach __P((device_t)); +static int dc_detach __P((device_t)); +static void dc_acpi __P((device_t)); +static struct dc_type *dc_devtype __P((device_t)); +static int dc_newbuf __P((struct dc_softc *, int, struct mbuf *)); +static int dc_encap __P((struct dc_softc *, struct mbuf *, + u_int32_t *)); +static void dc_pnic_rx_bug_war __P((struct dc_softc *, int)); +static void dc_rxeof __P((struct dc_softc *)); +static void dc_txeof __P((struct dc_softc *)); +static void dc_tick __P((void *)); +static void dc_intr __P((void *)); +static void dc_start __P((struct ifnet *)); +static int dc_ioctl __P((struct ifnet *, u_long, caddr_t)); +static void dc_init __P((void *)); +static void dc_stop __P((struct dc_softc *)); +static void dc_watchdog __P((struct ifnet *)); +static void dc_shutdown __P((device_t)); +static int dc_ifmedia_upd __P((struct ifnet *)); +static void dc_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); + +static void dc_delay __P((struct dc_softc *)); +static void dc_eeprom_idle __P((struct dc_softc *)); +static void dc_eeprom_putbyte __P((struct dc_softc *, int)); +static void dc_eeprom_getword __P((struct dc_softc *, int, u_int16_t *)); +static void dc_eeprom_getword_pnic + __P((struct dc_softc *, int, u_int16_t *)); +static void dc_read_eeprom __P((struct dc_softc *, caddr_t, int, + int, int)); + +static void dc_mii_writebit __P((struct dc_softc *, int)); +static int dc_mii_readbit __P((struct dc_softc *)); +static void dc_mii_sync __P((struct dc_softc *)); +static void dc_mii_send __P((struct dc_softc *, u_int32_t, int)); +static int dc_mii_readreg __P((struct dc_softc *, struct dc_mii_frame *)); +static int dc_mii_writereg __P((struct dc_softc *, struct dc_mii_frame *)); +static int dc_miibus_readreg __P((device_t, int, int)); +static int dc_miibus_writereg __P((device_t, int, int, int)); +static void dc_miibus_statchg __P((device_t)); + +static void dc_setcfg __P((struct dc_softc *, int)); +static u_int32_t dc_crc_le __P((struct dc_softc *, caddr_t)); +static u_int32_t dc_crc_be __P((caddr_t)); +static void dc_setfilt_21143 __P((struct dc_softc *)); +static void dc_setfilt_asix __P((struct dc_softc *)); +static void dc_setfilt_admtek __P((struct dc_softc *)); + +static void dc_setfilt __P((struct dc_softc *)); + +static void dc_reset __P((struct dc_softc *)); +static int dc_list_rx_init __P((struct dc_softc *)); +static int dc_list_tx_init __P((struct dc_softc *)); + +#ifdef DC_USEIOSPACE +#define DC_RES SYS_RES_IOPORT +#define DC_RID DC_PCI_CFBIO +#else +#define DC_RES SYS_RES_MEMORY +#define DC_RID DC_PCI_CFBMA +#endif + +static device_method_t dc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dc_probe), + DEVMETHOD(device_attach, dc_attach), + DEVMETHOD(device_detach, dc_detach), + DEVMETHOD(device_shutdown, dc_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, dc_miibus_readreg), + DEVMETHOD(miibus_writereg, dc_miibus_writereg), + DEVMETHOD(miibus_statchg, dc_miibus_statchg), + + { 0, 0 } +}; + +static driver_t dc_driver = { + "dc", + dc_methods, + sizeof(struct dc_softc) +}; + +static devclass_t dc_devclass; + +DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0); +DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0); + +#define DC_SETBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) + +#define DC_CLRBIT(sc, reg, x) \ + CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) + +#define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x)) +#define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x)) + +static void dc_delay(sc) + struct dc_softc *sc; +{ + int idx; + + for (idx = (300 / 33) + 1; idx > 0; idx--) + CSR_READ_4(sc, DC_BUSCTL); +} + +static void dc_eeprom_idle(sc) + struct dc_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + + for (i = 0; i < 25; i++) { + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + } + + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + CSR_WRITE_4(sc, DC_SIO, 0x00000000); + + return; +} + +/* + * Send a read command and address to the EEPROM, check for ACK. + */ +static void dc_eeprom_putbyte(sc, addr) + struct dc_softc *sc; + int addr; +{ + register int d, i; + + /* + * The AN985 has a 93C66 EEPROM on it instead of + * a 93C46. It uses a different bit sequence for + * specifying the "read" opcode. + */ + if (DC_IS_CENTAUR(sc)) + d = addr | (DC_EECMD_READ << 2); + else + d = addr | DC_EECMD_READ; + + /* + * Feed in each bit and strobe the clock. + */ + for (i = 0x400; i; i >>= 1) { + if (d & i) { + SIO_SET(DC_SIO_EE_DATAIN); + } else { + SIO_CLR(DC_SIO_EE_DATAIN); + } + dc_delay(sc); + SIO_SET(DC_SIO_EE_CLK); + dc_delay(sc); + SIO_CLR(DC_SIO_EE_CLK); + dc_delay(sc); + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + * The PNIC 82c168/82c169 has its own non-standard way to read + * the EEPROM. + */ +static void dc_eeprom_getword_pnic(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int32_t r; + + CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ|addr); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(1); + r = CSR_READ_4(sc, DC_SIO); + if (!(r & DC_PN_SIOCTL_BUSY)) { + *dest = (u_int16_t)(r & 0xFFFF); + return; + } + } + + return; +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void dc_eeprom_getword(sc, addr, dest) + struct dc_softc *sc; + int addr; + u_int16_t *dest; +{ + register int i; + u_int16_t word = 0; + + /* Force EEPROM to idle state. */ + dc_eeprom_idle(sc); + + /* Enter EEPROM access mode. */ + CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); + dc_delay(sc); + DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); + dc_delay(sc); + DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); + dc_delay(sc); + + /* + * Send address of word we want to read. + */ + dc_eeprom_putbyte(sc, addr); + + /* + * Start reading bits from EEPROM. + */ + for (i = 0x8000; i; i >>= 1) { + SIO_SET(DC_SIO_EE_CLK); + dc_delay(sc); + if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT) + word |= i; + dc_delay(sc); + SIO_CLR(DC_SIO_EE_CLK); + dc_delay(sc); + } + + /* Turn off EEPROM access mode. */ + dc_eeprom_idle(sc); + + *dest = word; + + return; +} + +/* + * Read a sequence of words from the EEPROM. + */ +static void dc_read_eeprom(sc, dest, off, cnt, swap) + struct dc_softc *sc; + caddr_t dest; + int off; + int cnt; + int swap; +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + if (DC_IS_PNIC(sc)) + dc_eeprom_getword_pnic(sc, off + i, &word); + else + dc_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + +/* + * The following two routines are taken from the Macronix 98713 + * Application Notes pp.19-21. + */ +/* + * Write a bit to the MII bus. + */ +static void dc_mii_writebit(sc, bit) + struct dc_softc *sc; + int bit; +{ + if (bit) + CSR_WRITE_4(sc, DC_SIO, + DC_SIO_ROMCTL_WRITE|DC_SIO_MII_DATAOUT); + else + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); + + DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); + DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); + + return; +} + +/* + * Read a bit from the MII bus. + */ +static int dc_mii_readbit(sc) + struct dc_softc *sc; +{ + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ|DC_SIO_MII_DIR); + CSR_READ_4(sc, DC_SIO); + DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); + DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); + if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN) + return(1); + + return(0); +} + +/* + * Sync the PHYs by setting data bit and strobing the clock 32 times. + */ +static void dc_mii_sync(sc) + struct dc_softc *sc; +{ + register int i; + + CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); + + for (i = 0; i < 32; i++) + dc_mii_writebit(sc, 1); + + return; +} + +/* + * Clock a series of bits through the MII. + */ +static void dc_mii_send(sc, bits, cnt) + struct dc_softc *sc; + u_int32_t bits; + int cnt; +{ + int i; + + for (i = (0x1 << (cnt - 1)); i; i >>= 1) + dc_mii_writebit(sc, bits & i); +} + +/* + * Read an PHY register through the MII. + */ +static int dc_mii_readreg(sc, frame) + struct dc_softc *sc; + struct dc_mii_frame *frame; + +{ + int i, ack, s; + + s = splimp(); + + /* + * Set up frame for RX. + */ + frame->mii_stdelim = DC_MII_STARTDELIM; + frame->mii_opcode = DC_MII_READOP; + frame->mii_turnaround = 0; + frame->mii_data = 0; + + /* + * Sync the PHYs. + */ + dc_mii_sync(sc); + + /* + * Send command/address info. + */ + dc_mii_send(sc, frame->mii_stdelim, 2); + dc_mii_send(sc, frame->mii_opcode, 2); + dc_mii_send(sc, frame->mii_phyaddr, 5); + dc_mii_send(sc, frame->mii_regaddr, 5); + +#ifdef notdef + /* Idle bit */ + dc_mii_writebit(sc, 1); + dc_mii_writebit(sc, 0); +#endif + + /* Check for ack */ + ack = dc_mii_readbit(sc); + + /* + * Now try reading data bits. If the ack failed, we still + * need to clock through 16 cycles to keep the PHY(s) in sync. + */ + if (ack) { + for(i = 0; i < 16; i++) { + dc_mii_readbit(sc); + } + goto fail; + } + + for (i = 0x8000; i; i >>= 1) { + if (!ack) { + if (dc_mii_readbit(sc)) + frame->mii_data |= i; + } + } + +fail: + + dc_mii_writebit(sc, 0); + dc_mii_writebit(sc, 0); + + splx(s); + + if (ack) + return(1); + return(0); +} + +/* + * Write to a PHY register through the MII. + */ +static int dc_mii_writereg(sc, frame) + struct dc_softc *sc; + struct dc_mii_frame *frame; + +{ + int s; + + s = splimp(); + /* + * Set up frame for TX. + */ + + frame->mii_stdelim = DC_MII_STARTDELIM; + frame->mii_opcode = DC_MII_WRITEOP; + frame->mii_turnaround = DC_MII_TURNAROUND; + + /* + * Sync the PHYs. + */ + dc_mii_sync(sc); + + dc_mii_send(sc, frame->mii_stdelim, 2); + dc_mii_send(sc, frame->mii_opcode, 2); + dc_mii_send(sc, frame->mii_phyaddr, 5); + dc_mii_send(sc, frame->mii_regaddr, 5); + dc_mii_send(sc, frame->mii_turnaround, 2); + dc_mii_send(sc, frame->mii_data, 16); + + /* Idle bit. */ + dc_mii_writebit(sc, 0); + dc_mii_writebit(sc, 0); + + splx(s); + + return(0); +} + +static int dc_miibus_readreg(dev, phy, reg) + device_t dev; + int phy, reg; +{ + struct dc_mii_frame frame; + struct dc_softc *sc; + int i, rval, phy_reg; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + /* + * Note: both the AL981 and AN985 have internal PHYs, + * however the AL981 provides direct access to the PHY + * registers while the AN985 uses a serial MII interface. + * The AN985's MII interface is also buggy in that you + * can read from any MII address (0 to 31), but only address 1 + * behaves normally. To deal with both cases, we pretend + * that the PHY is at MII address 1. + */ + if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) + return(0); + + if (sc->dc_pmode == DC_PMODE_SYM) { + if (phy == (MII_NPHY - 1)) { + switch(reg) { + case MII_BMSR: + /* + * Fake something to make the probe + * code think there's a PHY here. + */ + return(BMSR_MEDIAMASK); + break; + case MII_PHYIDR1: + if (DC_IS_PNIC(sc)) + return(DC_VENDORID_LO); + return(DC_VENDORID_DEC); + break; + case MII_PHYIDR2: + if (DC_IS_PNIC(sc)) + return(DC_DEVICEID_82C168); + return(DC_DEVICEID_21143); + break; + default: + return(0); + break; + } + } else + return(0); + } + + if (DC_IS_PNIC(sc)) { + CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ | + (phy << 23) | (reg << 18)); + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(1); + rval = CSR_READ_4(sc, DC_PN_MII); + if (!(rval & DC_PN_MII_BUSY)) { + rval &= 0xFFFF; + return(rval == 0xFFFF ? 0 : rval); + } + } + return(0); + } + + if (DC_IS_COMET(sc)) { + switch(reg) { + case MII_BMCR: + phy_reg = DC_AL_BMCR; + break; + case MII_BMSR: + phy_reg = DC_AL_BMSR; + break; + case MII_PHYIDR1: + phy_reg = DC_AL_VENID; + break; + case MII_PHYIDR2: + phy_reg = DC_AL_DEVID; + break; + case MII_ANAR: + phy_reg = DC_AL_ANAR; + break; + case MII_ANLPAR: + phy_reg = DC_AL_LPAR; + break; + case MII_ANER: + phy_reg = DC_AL_ANER; + break; + default: + printf("dc%d: phy_read: bad phy register %x\n", + sc->dc_unit, reg); + return(0); + break; + } + + rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; + + if (rval == 0xFFFF) + return(0); + return(rval); + } + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + dc_mii_readreg(sc, &frame); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + + return(frame.mii_data); +} + +static int dc_miibus_writereg(dev, phy, reg, data) + device_t dev; + int phy, reg, data; +{ + struct dc_softc *sc; + struct dc_mii_frame frame; + int i, phy_reg; + + sc = device_get_softc(dev); + bzero((char *)&frame, sizeof(frame)); + + if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) + return(0); + + if (DC_IS_PNIC(sc)) { + CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE | + (phy << 23) | (reg << 10) | data); + for (i = 0; i < DC_TIMEOUT; i++) { + if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY)) + break; + } + return(0); + } + + if (DC_IS_COMET(sc)) { + switch(reg) { + case MII_BMCR: + phy_reg = DC_AL_BMCR; + break; + case MII_BMSR: + phy_reg = DC_AL_BMSR; + break; + case MII_PHYIDR1: + phy_reg = DC_AL_VENID; + break; + case MII_PHYIDR2: + phy_reg = DC_AL_DEVID; + break; + case MII_ANAR: + phy_reg = DC_AL_ANAR; + break; + case MII_ANLPAR: + phy_reg = DC_AL_LPAR; + break; + case MII_ANER: + phy_reg = DC_AL_ANER; + break; + default: + printf("dc%d: phy_write: bad phy register %x\n", + sc->dc_unit, reg); + return(0); + break; + } + + CSR_WRITE_4(sc, phy_reg, data); + return(0); + } + + frame.mii_phyaddr = phy; + frame.mii_regaddr = reg; + frame.mii_data = data; + + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + dc_mii_writereg(sc, &frame); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + + return(0); +} + +static void dc_miibus_statchg(dev) + device_t dev; +{ + struct dc_softc *sc; + struct mii_data *mii; + + sc = device_get_softc(dev); + if (DC_IS_ADMTEK(sc)) + return; + mii = device_get_softc(sc->dc_miibus); + dc_setcfg(sc, mii->mii_media_active); + sc->dc_if_media = mii->mii_media_active; + + return; +} + +#define DC_POLY 0xEDB88320 +#define DC_BITS 9 +#define DC_BITS_PNIC_II 7 + +static u_int32_t dc_crc_le(sc, addr) + struct dc_softc *sc; + caddr_t addr; +{ + u_int32_t idx, bit, data, crc; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (idx = 0; idx < 6; idx++) { + for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0); + } + + /* The hash table on the PNIC II is only 128 bits wide. */ + if (DC_IS_PNICII(sc)) + return (crc & ((1 << DC_BITS_PNIC_II) - 1)); + + return (crc & ((1 << DC_BITS) - 1)); +} + +/* + * Calculate CRC of a multicast group address, return the lower 6 bits. + */ +static u_int32_t dc_crc_be(addr) + caddr_t addr; +{ + u_int32_t crc, carry; + int i, j; + u_int8_t c; + + /* Compute CRC for the address value. */ + crc = 0xFFFFFFFF; /* initial value */ + + for (i = 0; i < 6; i++) { + c = *(addr + i); + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + } + + /* return the filter bit position */ + return((crc >> 26) & 0x0000003F); +} + +/* + * 21143-style RX filter setup routine. Filter programming is done by + * downloading a special setup frame into the TX engine. 21143, Macronix, + * PNIC, PNIC II and Davicom chips are programmed this way. + * + * We always program the chip using 'hash perfect' mode, i.e. one perfect + * address (our node address) and a 512-bit hash filter for multicast + * frames. We also sneak the broadcast address into the hash filter since + * we need that too. + */ +void dc_setfilt_21143(sc) + struct dc_softc *sc; +{ + struct dc_desc *sframe; + u_int32_t h, *sp; + struct ifmultiaddr *ifma; + struct ifnet *ifp; + int i; + + ifp = &sc->arpcom.ac_if; + + i = sc->dc_cdata.dc_tx_prod; + DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); + sc->dc_cdata.dc_tx_cnt++; + sframe = &sc->dc_ldata->dc_tx_list[i]; + sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf; + bzero((char *)sp, DC_SFRAME_LEN); + + sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf); + sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | + DC_FILTER_HASHPERF | DC_TXCTL_FINT; + + sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf; + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_le(sc, + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + sp[h >> 4] |= 1 << (h & 0xF); + } + + if (ifp->if_flags & IFF_BROADCAST) { + h = dc_crc_le(sc, (caddr_t)ðerbroadcastaddr); + sp[h >> 4] |= 1 << (h & 0xF); + } + + /* Set our MAC address */ + sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; + sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; + sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; + + sframe->dc_status = DC_TXSTAT_OWN; + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * The PNIC takes an exceedingly long time to process its + * setup frame; wait 10ms after posting the setup frame + * before proceeding, just so it has time to swallow its + * medicine. + */ + DELAY(10000); + + ifp->if_timer = 5; + + return; +} + +void dc_setfilt_admtek(sc) + struct dc_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + + /* Init our MAC address */ + CSR_WRITE_4(sc, DC_AL_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + CSR_WRITE_4(sc, DC_AL_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, DC_AL_MAR0, 0); + CSR_WRITE_4(sc, DC_AL_MAR1, 0); + + /* + * If we're already in promisc or allmulti mode, we + * don't have to bother programming the multicast filter. + */ + if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) + return; + + /* now program new ones */ + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]); + CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]); + + return; +} + +void dc_setfilt_asix(sc) + struct dc_softc *sc; +{ + struct ifnet *ifp; + int h = 0; + u_int32_t hashes[2] = { 0, 0 }; + struct ifmultiaddr *ifma; + + ifp = &sc->arpcom.ac_if; + + /* Init our MAC address */ + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, + *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, + *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); + + /* If we want promiscuous mode, set the allframes bit. */ + if (ifp->if_flags & IFF_PROMISC) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); + + if (ifp->if_flags & IFF_ALLMULTI) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + else + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); + + /* + * The ASIX chip has a special bit to enable reception + * of broadcast frames. + */ + if (ifp->if_flags & IFF_BROADCAST) + DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); + else + DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); + + /* first, zot all the existing hash bits */ + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); + + /* + * If we're already in promisc or allmulti mode, we + * don't have to bother programming the multicast filter. + */ + if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) + return; + + /* now program new ones */ + for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; + ifma = ifma->ifma_link.le_next) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + if (h < 32) + hashes[0] |= (1 << h); + else + hashes[1] |= (1 << (h - 32)); + } + + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); + CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]); + CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); + CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]); + + return; +} + +static void dc_setfilt(sc) + struct dc_softc *sc; +{ + if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) || + DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc)) + dc_setfilt_21143(sc); + + if (DC_IS_ASIX(sc)) + dc_setfilt_asix(sc); + + if (DC_IS_ADMTEK(sc)) + dc_setfilt_admtek(sc); + + return; +} + +/* + * In order to fiddle with the + * 'full-duplex' and '100Mbps' bits in the netconfig register, we + * first have to put the transmit and/or receive logic in the idle state. + */ +static void dc_setcfg(sc, media) + struct dc_softc *sc; + int media; +{ + int i, restart = 0; + u_int32_t isr; + + if (IFM_SUBTYPE(media) == IFM_NONE) + return; + + if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)) { + restart = 1; + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(10); + isr = CSR_READ_4(sc, DC_ISR); + if (isr & DC_ISR_TX_IDLE || + (isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED) + break; + } + + if (i == DC_TIMEOUT) + printf("dc%d: failed to force tx and " + "rx to idle state\n", sc->dc_unit); + + } + + if (IFM_SUBTYPE(media) == IFM_100_TX) { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); + if (sc->dc_pmode == DC_PMODE_MII) { + DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER)); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_SCRAMBLER)); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + } else { + if (DC_IS_PNIC(sc)) { + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL); + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); + DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); + } + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL| + DC_NETCFG_PCS|DC_NETCFG_SCRAMBLER); + } + } + + if (IFM_SUBTYPE(media) == IFM_10_T) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); + if (sc->dc_pmode == DC_PMODE_MII) { + DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS| + DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER)); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); + } else { + if (DC_IS_PNIC(sc)) { + DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL); + DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); + DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); + } + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); + } + } + + if ((media & IFM_GMASK) == IFM_FDX) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) + DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); + if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) + DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); + } + + if (restart) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON|DC_NETCFG_RX_ON); + + return; +} + +static void dc_reset(sc) + struct dc_softc *sc; +{ + register int i; + + DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); + + for (i = 0; i < DC_TIMEOUT; i++) { + DELAY(10); + if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET)) + break; + } + + if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc)) { + DELAY(10000); + DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); + i = 0; + } + + if (i == DC_TIMEOUT) + printf("dc%d: reset never completed!\n", sc->dc_unit); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(1000); + + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000); + CSR_WRITE_4(sc, DC_NETCFG, 0x00000000); + + return; +} + +static struct dc_type *dc_devtype(dev) + device_t dev; +{ + struct dc_type *t; + u_int32_t rev; + + t = dc_devs; + + while(t->dc_name != NULL) { + if ((pci_get_vendor(dev) == t->dc_vid) && + (pci_get_device(dev) == t->dc_did)) { + /* Check the PCI revision */ + rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; + if (t->dc_did == DC_DEVICEID_98713 && + rev >= DC_REVISION_98713A) + t++; + if (t->dc_did == DC_DEVICEID_98713_CP && + rev >= DC_REVISION_98713A) + t++; + if (t->dc_did == DC_DEVICEID_987x5 && + rev >= DC_REVISION_98725) + t++; + if (t->dc_did == DC_DEVICEID_AX88140A && + rev >= DC_REVISION_88141) + t++; + if (t->dc_did == DC_DEVICEID_82C168 && + rev >= DC_REVISION_82C169) + t++; + return(t); + } + t++; + } + + return(NULL); +} + +/* + * Probe for a 21143 or clone chip. Check the PCI vendor and device + * IDs against our list and return a device name if we find a match. + * We do a little bit of extra work to identify the exact type of + * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, + * but different revision IDs. The same is true for 98715/98715A + * chips and the 98725, as well as the ASIX and ADMtek chips. In some + * cases, the exact chip revision affects driver behavior. + */ +static int dc_probe(dev) + device_t dev; +{ + struct dc_type *t; + + t = dc_devtype(dev); + + if (t != NULL) { + device_set_desc(dev, t->dc_name); + return(0); + } + + return(ENXIO); +} + +static void dc_acpi(dev) + device_t dev; +{ + u_int32_t r, cptr; + int unit; + + unit = device_get_unit(dev); + + /* Find the location of the capabilities block */ + cptr = pci_read_config(dev, DC_PCI_CCAP, 4) & 0xFF; + + r = pci_read_config(dev, cptr, 4) & 0xFF; + if (r == 0x01) { + + r = pci_read_config(dev, cptr + 4, 4); + if (r & DC_PSTATE_D3) { + u_int32_t iobase, membase, irq; + + /* Save important PCI config data. */ + iobase = pci_read_config(dev, DC_PCI_CFBIO, 4); + membase = pci_read_config(dev, DC_PCI_CFBMA, 4); + irq = pci_read_config(dev, DC_PCI_CFIT, 4); + + /* Reset the power state. */ + printf("dc%d: chip is in D%d power mode " + "-- setting to D0\n", unit, r & DC_PSTATE_D3); + r &= 0xFFFFFFFC; + pci_write_config(dev, cptr + 4, r, 4); + + /* Restore PCI config data. */ + pci_write_config(dev, DC_PCI_CFBIO, iobase, 4); + pci_write_config(dev, DC_PCI_CFBMA, membase, 4); + pci_write_config(dev, DC_PCI_CFIT, irq, 4); + } + } + return; +} + +/* + * Attach the interface. Allocate softc structures, do ifmedia + * setup and ethernet/BPF attach. + */ +static int dc_attach(dev) + device_t dev; +{ + int s; + u_char eaddr[ETHER_ADDR_LEN]; + u_int32_t command; + struct dc_softc *sc; + struct ifnet *ifp; + u_int32_t revision; + int unit, error = 0, rid, mac_offset; + + s = splimp(); + + sc = device_get_softc(dev); + unit = device_get_unit(dev); + bzero(sc, sizeof(struct dc_softc)); + + /* + * Handle power management nonsense. + */ + dc_acpi(dev); + + /* + * Map control/status registers. + */ + command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); + command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); + command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); + +#ifdef DC_USEIOSPACE + if (!(command & PCIM_CMD_PORTEN)) { + printf("dc%d: failed to enable I/O ports!\n", unit); + error = ENXIO; + goto fail; + } +#else + if (!(command & PCIM_CMD_MEMEN)) { + printf("dc%d: failed to enable memory mapping!\n", unit); + error = ENXIO; + goto fail; + } +#endif + + rid = DC_RID; + sc->dc_res = bus_alloc_resource(dev, DC_RES, &rid, + 0, ~0, 1, RF_ACTIVE); + + if (sc->dc_res == NULL) { + printf("dc%d: couldn't map ports/memory\n", unit); + error = ENXIO; + goto fail; + } + + sc->dc_btag = rman_get_bustag(sc->dc_res); + sc->dc_bhandle = rman_get_bushandle(sc->dc_res); + + /* Allocate interrupt */ + rid = 0; + sc->dc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + + if (sc->dc_irq == NULL) { + printf("dc%d: couldn't map interrupt\n", unit); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET, + dc_intr, sc, &sc->dc_intrhand); + + if (error) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + printf("dc%d: couldn't set up irq\n", unit); + goto fail; + } + + /* Need this info to decide on a chip type. */ + sc->dc_info = dc_devtype(dev); + revision = pci_read_config(dev, DC_PCI_CFRV, 4) & 0x000000FF; + + switch(sc->dc_info->dc_did) { + case DC_DEVICEID_21143: + sc->dc_type = DC_TYPE_21143; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL; + break; + case DC_DEVICEID_DM9100: + case DC_DEVICEID_DM9102: + sc->dc_type = DC_TYPE_DM9102; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_AL981: + sc->dc_type = DC_TYPE_AL981; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_TX_ADMTEK_WAR; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_AN985: + sc->dc_type = DC_TYPE_AN985; + sc->dc_flags |= DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_TX_ADMTEK_WAR; + sc->dc_pmode = DC_PMODE_MII; + break; + case DC_DEVICEID_98713: + case DC_DEVICEID_98713_CP: + if (revision < DC_REVISION_98713A) { + sc->dc_type = DC_TYPE_98713; + sc->dc_flags |= DC_REDUCED_MII_POLL; + } + if (revision >= DC_REVISION_98713A) + sc->dc_type = DC_TYPE_98713A; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_987x5: + sc->dc_type = DC_TYPE_987x5; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_82C115: + sc->dc_type = DC_TYPE_PNICII; + sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR; + break; + case DC_DEVICEID_82C168: + sc->dc_type = DC_TYPE_PNIC; + sc->dc_flags |= DC_TX_STORENFWD|DC_TX_USE_TX_INTR; + sc->dc_flags |= DC_PNIC_RX_BUG_WAR; + sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT); + if (revision < DC_REVISION_82C169) + sc->dc_pmode = DC_PMODE_SYM; + break; + case DC_DEVICEID_AX88140A: + sc->dc_type = DC_TYPE_ASIX; + sc->dc_flags |= DC_TX_USE_TX_INTR|DC_TX_INTR_FIRSTFRAG; + sc->dc_flags |= DC_REDUCED_MII_POLL; + sc->dc_pmode = DC_PMODE_MII; + break; + default: + printf("dc%d: unknown device: %x\n", sc->dc_unit, + sc->dc_info->dc_did); + break; + } + + /* Save the cache line size. */ + sc->dc_cachesize = pci_read_config(dev, DC_PCI_CFLT, 4) & 0xFF; + + /* Reset the adapter. */ + dc_reset(sc); + + /* Take 21143 out of snooze mode */ + if (DC_IS_INTEL(sc)) { + command = pci_read_config(dev, DC_PCI_CFDD, 4); + command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE); + pci_write_config(dev, DC_PCI_CFDD, command, 4); + } + + /* + * Try to learn something about the supported media. + * We know that ASIX and ADMtek and Davicom devices + * will *always* be using MII media, so that's a no-brainer. + * The tricky ones are the Macronix/PNIC II and the + * Intel 21143. + */ + if (DC_IS_INTEL(sc)) { + u_int32_t media, cwuc; + cwuc = pci_read_config(dev, DC_PCI_CWUC, 4); + cwuc |= DC_CWUC_FORCE_WUL; + pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); + DELAY(10000); + media = pci_read_config(dev, DC_PCI_CWUC, 4); + cwuc &= ~DC_CWUC_FORCE_WUL; + pci_write_config(dev, DC_PCI_CWUC, cwuc, 4); + DELAY(10000); + if (media & DC_CWUC_MII_ABILITY) + sc->dc_pmode = DC_PMODE_MII; + if (media & DC_CWUC_SYM_ABILITY) + sc->dc_pmode = DC_PMODE_SYM; + /* + * If none of the bits are set, then this NIC + * isn't meant to support 'wake up LAN' mode. + * This is usually only the case on multiport + * cards, and these cards almost always have + * MII transceivers. + */ + if (media == 0) + sc->dc_pmode = DC_PMODE_MII; + } else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + if (sc->dc_type == DC_TYPE_98713) + sc->dc_pmode = DC_PMODE_MII; + else + sc->dc_pmode = DC_PMODE_SYM; + } else if (!sc->dc_pmode) + sc->dc_pmode = DC_PMODE_MII; + + /* + * Get station address from the EEPROM. + */ + switch(sc->dc_type) { + case DC_TYPE_98713: + case DC_TYPE_98713A: + case DC_TYPE_987x5: + case DC_TYPE_PNICII: + dc_read_eeprom(sc, (caddr_t)&mac_offset, + (DC_EE_NODEADDR_OFFSET / 2), 1, 0); + dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0); + break; + case DC_TYPE_PNIC: + dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); + break; + case DC_TYPE_DM9102: + case DC_TYPE_21143: + case DC_TYPE_ASIX: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); + break; + case DC_TYPE_AL981: + case DC_TYPE_AN985: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0); + break; + default: + dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); + break; + } + + /* + * A 21143 or clone chip was detected. Inform the world. + */ + printf("dc%d: Ethernet address: %6D\n", unit, eaddr, ":"); + + sc->dc_unit = unit; + bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + + sc->dc_ldata = contigmalloc(sizeof(struct dc_list_data), M_DEVBUF, + M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); + + if (sc->dc_ldata == NULL) { + printf("dc%d: no memory for list buffers!\n", unit); + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + bzero(sc->dc_ldata, sizeof(struct dc_list_data)); + + ifp = &sc->arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_unit = unit; + ifp->if_name = "dc"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = dc_ioctl; + ifp->if_output = ether_output; + ifp->if_start = dc_start; + ifp->if_watchdog = dc_watchdog; + ifp->if_init = dc_init; + ifp->if_baudrate = 10000000; + ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; + + /* + * Do MII setup. + */ + error = mii_phy_probe(dev, &sc->dc_miibus, + dc_ifmedia_upd, dc_ifmedia_sts); + + if (error && DC_IS_INTEL(sc)) { + sc->dc_pmode = DC_PMODE_SYM; + mii_phy_probe(dev, &sc->dc_miibus, + dc_ifmedia_upd, dc_ifmedia_sts); + error = 0; + } + + if (error) { + printf("dc%d: MII without any PHY!\n", sc->dc_unit); + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + error = ENXIO; + goto fail; + } + + /* + * Call MI attach routines. + */ + if_attach(ifp); + ether_ifattach(ifp); + callout_handle_init(&sc->dc_stat_ch); + + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); + +fail: + splx(s); + + return(error); +} + +static int dc_detach(dev) + device_t dev; +{ + struct dc_softc *sc; + struct ifnet *ifp; + int s; + + s = splimp(); + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + dc_stop(sc); + if_detach(ifp); + + bus_generic_detach(dev); + device_delete_child(dev, sc->dc_miibus); + + bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); + bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); + + contigfree(sc->dc_ldata, sizeof(struct dc_list_data), M_DEVBUF); + if (sc->dc_pnic_rx_buf != NULL) + free(sc->dc_pnic_rx_buf, M_DEVBUF); + + splx(s); + + return(0); +} + +/* + * Initialize the transmit descriptors. + */ +static int dc_list_tx_init(sc) + struct dc_softc *sc; +{ + struct dc_chain_data *cd; + struct dc_list_data *ld; + int i; + + cd = &sc->dc_cdata; + ld = sc->dc_ldata; + for (i = 0; i < DC_TX_LIST_CNT; i++) { + if (i == (DC_TX_LIST_CNT - 1)) { + ld->dc_tx_list[i].dc_next = + vtophys(&ld->dc_tx_list[0]); + } else { + ld->dc_tx_list[i].dc_next = + vtophys(&ld->dc_tx_list[i + 1]); + } + cd->dc_tx_chain[i] = NULL; + ld->dc_tx_list[i].dc_data = 0; + ld->dc_tx_list[i].dc_ctl = 0; + } + + cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0; + + return(0); +} + + +/* + * Initialize the RX descriptors and allocate mbufs for them. Note that + * we arrange the descriptors in a closed ring, so that the last descriptor + * points back to the first. + */ +static int dc_list_rx_init(sc) + struct dc_softc *sc; +{ + struct dc_chain_data *cd; + struct dc_list_data *ld; + int i; + + cd = &sc->dc_cdata; + ld = sc->dc_ldata; + + for (i = 0; i < DC_RX_LIST_CNT; i++) { + if (dc_newbuf(sc, i, NULL) == ENOBUFS) + return(ENOBUFS); + if (i == (DC_RX_LIST_CNT - 1)) { + ld->dc_rx_list[i].dc_next = + vtophys(&ld->dc_rx_list[0]); + } else { + ld->dc_rx_list[i].dc_next = + vtophys(&ld->dc_rx_list[i + 1]); + } + } + + cd->dc_rx_prod = 0; + + return(0); +} + +/* + * Initialize an RX descriptor and attach an MBUF cluster. + */ +static int dc_newbuf(sc, i, m) + struct dc_softc *sc; + int i; + struct mbuf *m; +{ + struct mbuf *m_new = NULL; + struct dc_desc *c; + + c = &sc->dc_ldata->dc_rx_list[i]; + + if (m == NULL) { + MGETHDR(m_new, M_DONTWAIT, MT_DATA); + if (m_new == NULL) { + printf("dc%d: no memory for rx list " + "-- packet dropped!\n", sc->dc_unit); + return(ENOBUFS); + } + + MCLGET(m_new, M_DONTWAIT); + if (!(m_new->m_flags & M_EXT)) { + printf("dc%d: no memory for rx list " + "-- packet dropped!\n", sc->dc_unit); + m_freem(m_new); + return(ENOBUFS); + } + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + } else { + m_new = m; + m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; + m_new->m_data = m_new->m_ext.ext_buf; + } + + m_adj(m_new, sizeof(u_int64_t)); + + /* + * If this is a PNIC chip, zero the buffer. This is part + * of the workaround for the receive bug in the 82c168 and + * 82c169 chips. + */ + if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) + bzero((char *)mtod(m_new, char *), m_new->m_len); + + sc->dc_cdata.dc_rx_chain[i] = m_new; + c->dc_data = vtophys(mtod(m_new, caddr_t)); + c->dc_ctl = DC_RXCTL_RLINK | DC_RXLEN; + c->dc_status = DC_RXSTAT_OWN; + + return(0); +} + +/* + * Grrrrr. + * The PNIC chip has a terrible bug in it that manifests itself during + * periods of heavy activity. The exact mode of failure if difficult to + * pinpoint: sometimes it only happens in promiscuous mode, sometimes it + * will happen on slow machines. The bug is that sometimes instead of + * uploading one complete frame during reception, it uploads what looks + * like the entire contents of its FIFO memory. The frame we want is at + * the end of the whole mess, but we never know exactly how much data has + * been uploaded, so salvaging the frame is hard. + * + * There is only one way to do it reliably, and it's disgusting. + * Here's what we know: + * + * - We know there will always be somewhere between one and three extra + * descriptors uploaded. + * + * - We know the desired received frame will always be at the end of the + * total data upload. + * + * - We know the size of the desired received frame because it will be + * provided in the length field of the status word in the last descriptor. + * + * Here's what we do: + * + * - When we allocate buffers for the receive ring, we bzero() them. + * This means that we know that the buffer contents should be all + * zeros, except for data uploaded by the chip. + * + * - We also force the PNIC chip to upload frames that include the + * ethernet CRC at the end. + * + * - We gather all of the bogus frame data into a single buffer. + * + * - We then position a pointer at the end of this buffer and scan + * backwards until we encounter the first non-zero byte of data. + * This is the end of the received frame. We know we will encounter + * some data at the end of the frame because the CRC will always be + * there, so even if the sender transmits a packet of all zeros, + * we won't be fooled. + * + * - We know the size of the actual received frame, so we subtract + * that value from the current pointer location. This brings us + * to the start of the actual received packet. + * + * - We copy this into an mbuf and pass it on, along with the actual + * frame length. + * + * The performance hit is tremendous, but it beats dropping frames all + * the time. + */ + +#define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG) +static void dc_pnic_rx_bug_war(sc, idx) + struct dc_softc *sc; + int idx; +{ + struct dc_desc *cur_rx; + struct dc_desc *c = NULL; + struct mbuf *m = NULL; + unsigned char *ptr; + int i, total_len; + u_int32_t rxstat = 0; + + i = sc->dc_pnic_rx_bug_save; + cur_rx = &sc->dc_ldata->dc_rx_list[idx]; + ptr = sc->dc_pnic_rx_buf; + bzero(ptr, sizeof(DC_RXLEN * 5)); + + /* Copy all the bytes from the bogus buffers. */ + while (1) { + c = &sc->dc_ldata->dc_rx_list[i]; + rxstat = c->dc_status; + m = sc->dc_cdata.dc_rx_chain[i]; + bcopy(mtod(m, char *), ptr, DC_RXLEN); + ptr += DC_RXLEN; + /* If this is the last buffer, break out. */ + if (i == idx || rxstat & DC_RXSTAT_LASTFRAG) + break; + dc_newbuf(sc, i, m); + DC_INC(i, DC_RX_LIST_CNT); + } + + /* Find the length of the actual receive frame. */ + total_len = DC_RXBYTES(rxstat); + + /* Scan backwards until we hit a non-zero byte. */ + while(*ptr == 0x00) + ptr--; + + /* Round off. */ + if ((uintptr_t)(ptr) & 0x3) + ptr -= 1; + + /* Now find the start of the frame. */ + ptr -= total_len; + if (ptr < sc->dc_pnic_rx_buf) + ptr = sc->dc_pnic_rx_buf; + + /* + * Now copy the salvaged frame to the last mbuf and fake up + * the status word to make it look like a successful + * frame reception. + */ + dc_newbuf(sc, i, m); + bcopy(ptr, mtod(m, char *), total_len); + cur_rx->dc_status = rxstat | DC_RXSTAT_FIRSTFRAG; + + return; +} + +/* + * A frame has been uploaded: pass the resulting mbuf chain up to + * the higher level protocols. + */ +static void dc_rxeof(sc) + struct dc_softc *sc; +{ + struct ether_header *eh; + struct mbuf *m; + struct ifnet *ifp; + struct dc_desc *cur_rx; + int i, total_len = 0; + u_int32_t rxstat; + + ifp = &sc->arpcom.ac_if; + i = sc->dc_cdata.dc_rx_prod; + + while(!(sc->dc_ldata->dc_rx_list[i].dc_status & DC_RXSTAT_OWN)) { + struct mbuf *m0 = NULL; + + cur_rx = &sc->dc_ldata->dc_rx_list[i]; + rxstat = cur_rx->dc_status; + m = sc->dc_cdata.dc_rx_chain[i]; + total_len = DC_RXBYTES(rxstat); + + if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) { + if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) { + if (rxstat & DC_RXSTAT_FIRSTFRAG) + sc->dc_pnic_rx_bug_save = i; + if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) { + DC_INC(i, DC_RX_LIST_CNT); + continue; + } + dc_pnic_rx_bug_war(sc, i); + rxstat = cur_rx->dc_status; + total_len = DC_RXBYTES(rxstat); + } + } + + sc->dc_cdata.dc_rx_chain[i] = NULL; + + /* + * If an error occurs, update stats, clear the + * status word and leave the mbuf cluster in place: + * it should simply get re-used next time this descriptor + * comes up in the ring. + */ + if (rxstat & DC_RXSTAT_RXERR) { + ifp->if_ierrors++; + if (rxstat & DC_RXSTAT_COLLSEEN) + ifp->if_collisions++; + dc_newbuf(sc, i, m); + if (rxstat & DC_RXSTAT_CRCERR) { + DC_INC(i, DC_RX_LIST_CNT); + continue; + } else { + dc_init(sc); + return; + } + } + + /* No errors; receive the packet. */ + total_len -= ETHER_CRC_LEN; + + m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, + total_len + ETHER_ALIGN, 0, ifp, NULL); + dc_newbuf(sc, i, m); + DC_INC(i, DC_RX_LIST_CNT); + if (m0 == NULL) { + ifp->if_ierrors++; + continue; + } + m_adj(m0, ETHER_ALIGN); + m = m0; + + ifp->if_ipackets++; + eh = mtod(m, struct ether_header *); + + /* + * Handle BPF listeners. Let the BPF user see the packet, but + * don't pass it up to the ether_input() layer unless it's + * a broadcast packet, multicast packet, matches our ethernet + * address or the interface is in promiscuous mode. + */ + if (ifp->if_bpf) { + bpf_mtap(ifp, m); + if (ifp->if_flags & IFF_PROMISC && + (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + ETHER_ADDR_LEN) && + (eh->ether_dhost[0] & 1) == 0)) { + m_freem(m); + continue; + } + } + + /* Remove header from mbuf and pass it on. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); + } + + sc->dc_cdata.dc_rx_prod = i; + + return; +} + +/* + * A frame was downloaded to the chip. It's safe for us to clean up + * the list buffers. + */ + +static void dc_txeof(sc) + struct dc_softc *sc; +{ + struct dc_desc *cur_tx = NULL; + struct ifnet *ifp; + int idx; + + ifp = &sc->arpcom.ac_if; + + /* Clear the timeout timer. */ + ifp->if_timer = 0; + + /* + * Go through our tx list and free mbufs for those + * frames that have been transmitted. + */ + idx = sc->dc_cdata.dc_tx_cons; + while(idx != sc->dc_cdata.dc_tx_prod) { + u_int32_t txstat; + + cur_tx = &sc->dc_ldata->dc_tx_list[idx]; + txstat = cur_tx->dc_status; + + if (txstat & DC_TXSTAT_OWN) + break; + + if (!(cur_tx->dc_ctl & DC_TXCTL_LASTFRAG) || + cur_tx->dc_ctl & DC_TXCTL_SETUP) { + sc->dc_cdata.dc_tx_cnt--; + if (cur_tx->dc_ctl & DC_TXCTL_SETUP) { + /* + * Yes, the PNIC is so brain damaged + * that it will sometimes generate a TX + * underrun error while DMAing the RX + * filter setup frame. If we detect this, + * we have to send the setup frame again, + * or else the filter won't be programmed + * correctly. + */ + if (DC_IS_PNIC(sc)) { + if (txstat & DC_TXSTAT_ERRSUM) + dc_setfilt(sc); + } + sc->dc_cdata.dc_tx_chain[idx] = NULL; + } + DC_INC(idx, DC_TX_LIST_CNT); + continue; + } + + if (/*sc->dc_type == DC_TYPE_21143 &&*/ + sc->dc_pmode == DC_PMODE_MII && + ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM| + DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST))) + txstat &= ~DC_TXSTAT_ERRSUM; + + if (txstat & DC_TXSTAT_ERRSUM) { + ifp->if_oerrors++; + if (txstat & DC_TXSTAT_EXCESSCOLL) + ifp->if_collisions++; + if (txstat & DC_TXSTAT_LATECOLL) + ifp->if_collisions++; + if (!(txstat & DC_TXSTAT_UNDERRUN)) { + dc_init(sc); + return; + } + } + + ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3; + + ifp->if_opackets++; + if (sc->dc_cdata.dc_tx_chain[idx] != NULL) { + m_freem(sc->dc_cdata.dc_tx_chain[idx]); + sc->dc_cdata.dc_tx_chain[idx] = NULL; + } + + sc->dc_cdata.dc_tx_cnt--; + DC_INC(idx, DC_TX_LIST_CNT); + } + + sc->dc_cdata.dc_tx_cons = idx; + if (cur_tx != NULL) + ifp->if_flags &= ~IFF_OACTIVE; + + return; +} + +static void dc_tick(xsc) + void *xsc; +{ + struct dc_softc *sc; + struct mii_data *mii; + struct ifnet *ifp; + int s; + u_int32_t r; + + s = splimp(); + + sc = xsc; + ifp = &sc->arpcom.ac_if; + mii = device_get_softc(sc->dc_miibus); + + if (sc->dc_flags & DC_REDUCED_MII_POLL) { + r = CSR_READ_4(sc, DC_ISR); + if (DC_IS_INTEL(sc)) { + if (r & DC_ISR_LINKFAIL) { + sc->dc_link = 0; + mii_tick(mii); + } + } else { + if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT && + sc->dc_cdata.dc_tx_prod == 0) + mii_tick(mii); + } + } else + mii_tick(mii); + + /* + * When the init routine completes, we expect to be able to send + * packets right away, and in fact the network code will send a + * gratuitous ARP the moment the init routine marks the interface + * as running. However, even though the MAC may have been initialized, + * there may be a delay of a few seconds before the PHY completes + * autonegotiation and the link is brought up. Any transmissions + * made during that delay will be lost. Dealing with this is tricky: + * we can't just pause in the init routine while waiting for the + * PHY to come ready since that would bring the whole system to + * a screeching halt for several seconds. + * + * What we do here is prevent the TX start routine from sending + * any packets until a link has been established. After the + * interface has been initialized, the tick routine will poll + * the state of the PHY until the IFM_ACTIVE flag is set. Until + * that time, packets will stay in the send queue, and once the + * link comes up, they will be flushed out to the wire. + */ + if (!sc->dc_link) { + mii_pollstat(mii); + if (mii->mii_media_status & IFM_ACTIVE && + IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { + sc->dc_link++; + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + } + } + + sc->dc_stat_ch = timeout(dc_tick, sc, hz); + + splx(s); + + return; +} + +static void dc_intr(arg) + void *arg; +{ + struct dc_softc *sc; + struct ifnet *ifp; + u_int32_t status; + + sc = arg; + ifp = &sc->arpcom.ac_if; + + /* Supress unwanted interrupts */ + if (!(ifp->if_flags & IFF_UP)) { + if (CSR_READ_4(sc, DC_ISR) & DC_INTRS) + dc_stop(sc); + return; + } + + /* Disable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + + while((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) { + + CSR_WRITE_4(sc, DC_ISR, status); + + if (status & DC_ISR_RX_OK) + dc_rxeof(sc); + + if (status & (DC_ISR_TX_OK|DC_ISR_TX_NOBUF)) + dc_txeof(sc); + + if (status & DC_ISR_TX_IDLE) { + dc_txeof(sc); + if (sc->dc_cdata.dc_tx_cnt) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + } + } + + if (status & DC_ISR_TX_UNDERRUN) { + u_int32_t cfg; + + printf("dc%d: TX underrun -- ", sc->dc_unit); + if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) + dc_init(sc); + cfg = CSR_READ_4(sc, DC_NETCFG); + cfg &= ~DC_NETCFG_TX_THRESH; + if (sc->dc_txthresh == DC_TXTHRESH_160BYTES) { + printf("using store and forward mode\n"); + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } else { + sc->dc_txthresh += 0x4000; + printf("increasing TX threshold\n"); + CSR_WRITE_4(sc, DC_NETCFG, cfg); + DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } + } + + if ((status & DC_ISR_RX_WATDOGTIMEO) + || (status & DC_ISR_RX_NOBUF)) + dc_rxeof(sc); + + if (status & DC_ISR_BUS_ERR) { + dc_reset(sc); + dc_init(sc); + } + } + + /* Re-enable interrupts. */ + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + + return; +} + +/* + * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data + * pointers to the fragment pointers. + */ +static int dc_encap(sc, m_head, txidx) + struct dc_softc *sc; + struct mbuf *m_head; + u_int32_t *txidx; +{ + struct dc_desc *f = NULL; + struct mbuf *m; + int frag, cur, cnt = 0; + + /* + * Start packing the mbufs in this chain into + * the fragment pointers. Stop when we run out + * of fragments or hit the end of the mbuf chain. + */ + m = m_head; + cur = frag = *txidx; + + for (m = m_head; m != NULL; m = m->m_next) { + if (m->m_len != 0) { + if (sc->dc_flags & DC_TX_ADMTEK_WAR) { + if (*txidx != sc->dc_cdata.dc_tx_prod && + frag == (DC_TX_LIST_CNT - 1)) + return(ENOBUFS); + } + if ((DC_TX_LIST_CNT - + (sc->dc_cdata.dc_tx_cnt + cnt)) < 5) + return(ENOBUFS); + + f = &sc->dc_ldata->dc_tx_list[frag]; + f->dc_ctl = DC_TXCTL_TLINK | m->m_len; + if (cnt == 0) { + f->dc_status = 0; + f->dc_ctl |= DC_TXCTL_FIRSTFRAG; + } else + f->dc_status = DC_TXSTAT_OWN; + f->dc_data = vtophys(mtod(m, vm_offset_t)); + cur = frag; + DC_INC(frag, DC_TX_LIST_CNT); + cnt++; + } + } + + if (m != NULL) + return(ENOBUFS); + + sc->dc_cdata.dc_tx_cnt += cnt; + sc->dc_cdata.dc_tx_chain[cur] = m_head; + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_LASTFRAG; + if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG) + sc->dc_ldata->dc_tx_list[*txidx].dc_ctl |= DC_TXCTL_FINT; + if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64) + sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT; + sc->dc_ldata->dc_tx_list[*txidx].dc_status = DC_TXSTAT_OWN; + *txidx = frag; + + return(0); +} + +/* + * Main transmit routine. To avoid having to do mbuf copies, we put pointers + * to the mbuf data regions directly in the transmit lists. We also save a + * copy of the pointers since the transmit list fragment pointers are + * physical addresses. + */ + +static void dc_start(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + struct mbuf *m_head = NULL; + int idx; + + sc = ifp->if_softc; + + if (!sc->dc_link) + return; + + if (ifp->if_flags & IFF_OACTIVE) + return; + + idx = sc->dc_cdata.dc_tx_prod; + + while(sc->dc_cdata.dc_tx_chain[idx] == NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (dc_encap(sc, m_head, &idx)) { + IF_PREPEND(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ + if (ifp->if_bpf) + bpf_mtap(ifp, m_head); + } + + /* Transmit */ + sc->dc_cdata.dc_tx_prod = idx; + if (!(sc->dc_flags & DC_TX_POLL)) + CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); + + /* + * Set a timeout in case the chip goes out to lunch. + */ + ifp->if_timer = 5; + + return; +} + +static void dc_init(xsc) + void *xsc; +{ + struct dc_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct mii_data *mii; + int s; + + s = splimp(); + + mii = device_get_softc(sc->dc_miibus); + + /* + * Cancel pending I/O and free all RX/TX buffers. + */ + dc_stop(sc); + dc_reset(sc); + + /* + * Set cache alignment and burst length. + */ + if (DC_IS_ASIX(sc)) + CSR_WRITE_4(sc, DC_BUSCTL, 0); + else + CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME|DC_BUSCTL_MRLE); + if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) { + DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA); + } else { + DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG); + } + if (sc->dc_flags & DC_TX_POLL) + DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1); + switch(sc->dc_cachesize) { + case 32: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG); + break; + case 16: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG); + break; + case 8: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG); + break; + case 0: + default: + DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE); + break; + } + + if (sc->dc_flags & DC_TX_STORENFWD) + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + else { + if (sc->dc_txthresh == DC_TXTHRESH_160BYTES) { + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + } else { + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); + DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); + } + } + + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC); + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF); + + if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { + /* + * The app notes for the 98713 and 98715A say that + * in order to have the chips operate properly, a magic + * number must be written to CSR16. Macronix does not + * document the meaning of these bits so there's no way + * to know exactly what they do. The 98713 has a magic + * number all its own; the rest all use a different one. + */ + DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000); + if (sc->dc_type == DC_TYPE_98713) + DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713); + else + DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715); + } + + DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); + DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_72BYTES); + + /* Init circular RX list. */ + if (dc_list_rx_init(sc) == ENOBUFS) { + printf("dc%d: initialization failed: no " + "memory for rx buffers\n", sc->dc_unit); + dc_stop(sc); + (void)splx(s); + return; + } + + /* + * Init tx descriptors. + */ + dc_list_tx_init(sc); + + /* + * Load the address of the RX list. + */ + CSR_WRITE_4(sc, DC_RXADDR, vtophys(&sc->dc_ldata->dc_rx_list[0])); + CSR_WRITE_4(sc, DC_TXADDR, vtophys(&sc->dc_ldata->dc_tx_list[0])); + + /* + * Enable interrupts. + */ + CSR_WRITE_4(sc, DC_IMR, DC_INTRS); + CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF); + + /* Enable transmitter. */ + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); + + /* + * Load the RX/multicast filter. We do this sort of late + * because the filter programming scheme on the 21143 and + * some clones requires DMAing a setup frame via the TX + * engine, and we need the transmitter enabled for that. + */ + dc_setfilt(sc); + + /* Enable receiver. */ + DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); + CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF); + + mii_mediachg(mii); + dc_setcfg(sc, sc->dc_if_media); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + (void)splx(s); + + sc->dc_stat_ch = timeout(dc_tick, sc, hz); + + return; +} + +/* + * Set media options. + */ +static int dc_ifmedia_upd(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->dc_miibus); + mii_mediachg(mii); + sc->dc_link = 0; + + return(0); +} + +/* + * Report current media status. + */ +static void dc_ifmedia_sts(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct dc_softc *sc; + struct mii_data *mii; + + sc = ifp->if_softc; + mii = device_get_softc(sc->dc_miibus); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + + return; +} + +static int dc_ioctl(ifp, command, data) + struct ifnet *ifp; + u_long command; + caddr_t data; +{ + struct dc_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + struct mii_data *mii; + int s, error = 0; + + s = splimp(); + + switch(command) { + case SIOCSIFADDR: + case SIOCGIFADDR: + case SIOCSIFMTU: + error = ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING && + ifp->if_flags & IFF_PROMISC && + !(sc->dc_if_flags & IFF_PROMISC)) { + dc_setfilt(sc); + } else if (ifp->if_flags & IFF_RUNNING && + !(ifp->if_flags & IFF_PROMISC) && + sc->dc_if_flags & IFF_PROMISC) { + dc_setfilt(sc); + } else if (!(ifp->if_flags & IFF_RUNNING)) { + sc->dc_txthresh = 0; + dc_init(sc); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + dc_stop(sc); + } + sc->dc_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + dc_setfilt(sc); + error = 0; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + mii = device_get_softc(sc->dc_miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); + break; + default: + error = EINVAL; + break; + } + + (void)splx(s); + + return(error); +} + +static void dc_watchdog(ifp) + struct ifnet *ifp; +{ + struct dc_softc *sc; + + sc = ifp->if_softc; + + ifp->if_oerrors++; + printf("dc%d: watchdog timeout\n", sc->dc_unit); + + dc_stop(sc); + dc_reset(sc); + dc_init(sc); + + if (ifp->if_snd.ifq_head != NULL) + dc_start(ifp); + + return; +} + +/* + * Stop the adapter and free any mbufs allocated to the + * RX and TX lists. + */ +static void dc_stop(sc) + struct dc_softc *sc; +{ + register int i; + struct ifnet *ifp; + + ifp = &sc->arpcom.ac_if; + ifp->if_timer = 0; + + untimeout(dc_tick, sc, sc->dc_stat_ch); + + DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON|DC_NETCFG_TX_ON)); + CSR_WRITE_4(sc, DC_IMR, 0x00000000); + CSR_WRITE_4(sc, DC_TXADDR, 0x00000000); + CSR_WRITE_4(sc, DC_RXADDR, 0x00000000); + sc->dc_link = 0; + + /* + * Free data in the RX lists. + */ + for (i = 0; i < DC_RX_LIST_CNT; i++) { + if (sc->dc_cdata.dc_rx_chain[i] != NULL) { + m_freem(sc->dc_cdata.dc_rx_chain[i]); + sc->dc_cdata.dc_rx_chain[i] = NULL; + } + } + bzero((char *)&sc->dc_ldata->dc_rx_list, + sizeof(sc->dc_ldata->dc_rx_list)); + + /* + * Free the TX list buffers. + */ + for (i = 0; i < DC_TX_LIST_CNT; i++) { + if (sc->dc_cdata.dc_tx_chain[i] != NULL) { + if (sc->dc_ldata->dc_tx_list[i].dc_ctl & + DC_TXCTL_SETUP) { + sc->dc_cdata.dc_tx_chain[i] = NULL; + continue; + } + m_freem(sc->dc_cdata.dc_tx_chain[i]); + sc->dc_cdata.dc_tx_chain[i] = NULL; + } + } + + bzero((char *)&sc->dc_ldata->dc_tx_list, + sizeof(sc->dc_ldata->dc_tx_list)); + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + return; +} + +/* + * Stop all chip I/O so that the kernel's probe routines don't + * get confused by errant DMAs when rebooting. + */ +static void dc_shutdown(dev) + device_t dev; +{ + struct dc_softc *sc; + + sc = device_get_softc(dev); + + dc_stop(sc); + + return; +} diff --git a/sys/pci/if_dcreg.h b/sys/pci/if_dcreg.h new file mode 100644 index 0000000..7994e94 --- /dev/null +++ b/sys/pci/if_dcreg.h @@ -0,0 +1,903 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD + * 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. + * + * $FreeBSD$ + */ + +/* + * 21143 and clone common register definitions. + */ + +#define DC_BUSCTL 0x00 /* bus control */ +#define DC_TXSTART 0x08 /* tx start demand */ +#define DC_RXSTART 0x10 /* rx start demand */ +#define DC_RXADDR 0x18 /* rx descriptor list start addr */ +#define DC_TXADDR 0x20 /* tx descriptor list start addr */ +#define DC_ISR 0x28 /* interrupt status register */ +#define DC_NETCFG 0x30 /* network config register */ +#define DC_IMR 0x38 /* interrupt mask */ +#define DC_FRAMESDISCARDED 0x40 /* # of discarded frames */ +#define DC_SIO 0x48 /* MII and ROM/EEPROM access */ +#define DC_ROM 0x50 /* ROM programming address */ +#define DC_TIMER 0x58 /* general timer */ +#define DC_10BTSTAT 0x60 /* SIA status */ +#define DC_SIARESET 0x68 /* SIA connectivity */ +#define DC_10BTCTRL 0x70 /* SIA transmit and receive */ +#define DC_WATCHDOG 0x78 /* SIA and general purpose port */ + +/* + * There are two general 'types' of MX chips that we need to be + * concerned with. One is the original 98713, which has its internal + * NWAY support controlled via the MDIO bits in the serial I/O + * register. The other is everything else (from the 98713A on up), + * which has its internal NWAY controlled via CSR13, CSR14 and CSR15, + * just like the 21143. This type setting also governs which of the + * 'magic' numbers we write to CSR16. The PNIC II falls into the + * 98713A/98715/98715A/98725 category. + */ +#define DC_TYPE_98713 0x1 +#define DC_TYPE_98713A 0x2 +#define DC_TYPE_987x5 0x3 + +/* Other type of supported chips. */ +#define DC_TYPE_21143 0x4 /* Intel 21143 */ +#define DC_TYPE_ASIX 0x5 /* ASIX AX88140A/AX88141 */ +#define DC_TYPE_AL981 0x6 /* ADMtek AL981 Comet */ +#define DC_TYPE_AN985 0x7 /* ADMtek AN985 Centaur */ +#define DC_TYPE_DM9102 0x8 /* Davicom DM9102 */ +#define DC_TYPE_PNICII 0x9 /* 82c115 PNIC II */ +#define DC_TYPE_PNIC 0xA /* 82c168/82c169 PNIC I */ + +#define DC_IS_MACRONIX(x) \ + (x->dc_type == DC_TYPE_98713 || \ + x->dc_type == DC_TYPE_98713A || \ + x->dc_type == DC_TYPE_987x5) + +#define DC_IS_ADMTEK(x) \ + (x->dc_type == DC_TYPE_AL981 || \ + x->dc_type == DC_TYPE_AN985) + +#define DC_IS_INTEL(x) (x->dc_type == DC_TYPE_21143) +#define DC_IS_ASIX(x) (x->dc_type == DC_TYPE_ASIX) +#define DC_IS_COMET(x) (x->dc_type == DC_TYPE_AL981) +#define DC_IS_CENTAUR(x) (x->dc_type == DC_TYPE_AN985) +#define DC_IS_DAVICOM(x) (x->dc_type == DC_TYPE_DM9102) +#define DC_IS_PNICII(x) (x->dc_type == DC_TYPE_PNICII) +#define DC_IS_PNIC(x) (x->dc_type == DC_TYPE_PNIC) + +/* MII/symbol mode port types */ +#define DC_PMODE_MII 0x1 +#define DC_PMODE_SYM 0x2 + +/* + * Bus control bits. + */ +#define DC_BUSCTL_RESET 0x00000001 +#define DC_BUSCTL_ARBITRATION 0x00000002 +#define DC_BUSCTL_SKIPLEN 0x0000007C +#define DC_BUSCTL_BUF_BIGENDIAN 0x00000080 +#define DC_BUSCTL_BURSTLEN 0x00003F00 +#define DC_BUSCTL_CACHEALIGN 0x0000C000 +#define DC_BUSCTL_TXPOLL 0x000E0000 +#define DC_BUSCTL_DBO 0x00100000 +#define DC_BUSCTL_MRME 0x00200000 +#define DC_BUSCTL_MRLE 0x00800000 +#define DC_BUSCTL_MWIE 0x01000000 +#define DC_BUSCTL_ONNOW_ENB 0x04000000 + +#define DC_SKIPLEN_1LONG 0x00000004 +#define DC_SKIPLEN_2LONG 0x00000008 +#define DC_SKIPLEN_3LONG 0x00000010 +#define DC_SKIPLEN_4LONG 0x00000020 +#define DC_SKIPLEN_5LONG 0x00000040 + +#define DC_CACHEALIGN_NONE 0x00000000 +#define DC_CACHEALIGN_8LONG 0x00004000 +#define DC_CACHEALIGN_16LONG 0x00008000 +#define DC_CACHEALIGN_32LONG 0x0000C000 + +#define DC_BURSTLEN_USECA 0x00000000 +#define DC_BURSTLEN_1LONG 0x00000100 +#define DC_BURSTLEN_2LONG 0x00000200 +#define DC_BURSTLEN_4LONG 0x00000400 +#define DC_BURSTLEN_8LONG 0x00000800 +#define DC_BURSTLEN_16LONG 0x00001000 +#define DC_BURSTLEN_32LONG 0x00002000 + +#define DC_TXPOLL_OFF 0x00000000 +#define DC_TXPOLL_1 0x00020000 +#define DC_TXPOLL_2 0x00040000 +#define DC_TXPOLL_3 0x00060000 +#define DC_TXPOLL_4 0x00080000 +#define DC_TXPOLL_5 0x000A0000 +#define DC_TXPOLL_6 0x000C0000 +#define DC_TXPOLL_7 0x000E0000 + +/* + * Interrupt status bits. + */ +#define DC_ISR_TX_OK 0x00000001 +#define DC_ISR_TX_IDLE 0x00000002 +#define DC_ISR_TX_NOBUF 0x00000004 +#define DC_ISR_TX_JABBERTIMEO 0x00000008 +#define DC_ISR_LINKGOOD 0x00000010 +#define DC_ISR_TX_UNDERRUN 0x00000020 +#define DC_ISR_RX_OK 0x00000040 +#define DC_ISR_RX_NOBUF 0x00000080 +#define DC_ISR_RX_READ 0x00000100 +#define DC_ISR_RX_WATDOGTIMEO 0x00000200 +#define DC_ISR_TX_EARLY 0x00000400 +#define DC_ISR_TIMER_EXPIRED 0x00000800 +#define DC_ISR_LINKFAIL 0x00001000 +#define DC_ISR_BUS_ERR 0x00002000 +#define DC_ISR_RX_EARLY 0x00004000 +#define DC_ISR_ABNORMAL 0x00008000 +#define DC_ISR_NORMAL 0x00010000 +#define DC_ISR_RX_STATE 0x000E0000 +#define DC_ISR_TX_STATE 0x00700000 +#define DC_ISR_BUSERRTYPE 0x03800000 +#define DC_ISR_100MBPSLINK 0x08000000 +#define DC_ISR_MAGICKPACK 0x10000000 + +#define DC_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ +#define DC_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ +#define DC_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ +#define DC_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ +#define DC_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ +#define DC_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ +#define DC_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ +#define DC_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ + +#define DC_TXSTATE_RESET 0x00000000 /* 000 - reset */ +#define DC_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ +#define DC_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ +#define DC_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ +#define DC_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ +#define DC_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ +#define DC_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ +#define DC_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ + +/* + * Network config bits. + */ +#define DC_NETCFG_RX_HASHPERF 0x00000001 +#define DC_NETCFG_RX_ON 0x00000002 +#define DC_NETCFG_RX_HASHONLY 0x00000004 +#define DC_NETCFG_RX_BADFRAMES 0x00000008 +#define DC_NETCFG_RX_INVFILT 0x00000010 +#define DC_NETCFG_BACKOFFCNT 0x00000020 +#define DC_NETCFG_RX_PROMISC 0x00000040 +#define DC_NETCFG_RX_ALLMULTI 0x00000080 +#define DC_NETCFG_FULLDUPLEX 0x00000200 +#define DC_NETCFG_LOOPBACK 0x00000C00 +#define DC_NETCFG_FORCECOLL 0x00001000 +#define DC_NETCFG_TX_ON 0x00002000 +#define DC_NETCFG_TX_THRESH 0x0000C000 +#define DC_NETCFG_TX_BACKOFF 0x00020000 +#define DC_NETCFG_PORTSEL 0x00040000 /* 0 == 10, 1 == 100 */ +#define DC_NETCFG_HEARTBEAT 0x00080000 +#define DC_NETCFG_STORENFWD 0x00200000 +#define DC_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */ +#define DC_NETCFG_PCS 0x00800000 +#define DC_NETCFG_SCRAMBLER 0x01000000 +#define DC_NETCFG_NO_RXCRC 0x02000000 +#define DC_NETCFG_RX_ALL 0x40000000 +#define DC_NETCFG_CAPEFFECT 0x80000000 + +#define DC_OPMODE_NORM 0x00000000 +#define DC_OPMODE_INTLOOP 0x00000400 +#define DC_OPMODE_EXTLOOP 0x00000800 + +#define DC_TXTHRESH_72BYTES 0x00000000 +#define DC_TXTHRESH_96BYTES 0x00004000 +#define DC_TXTHRESH_128BYTES 0x00008000 +#define DC_TXTHRESH_160BYTES 0x0000C000 + + +/* + * Interrupt mask bits. + */ +#define DC_IMR_TX_OK 0x00000001 +#define DC_IMR_TX_IDLE 0x00000002 +#define DC_IMR_TX_NOBUF 0x00000004 +#define DC_IMR_TX_JABBERTIMEO 0x00000008 +#define DC_IMR_LINKGOOD 0x00000010 +#define DC_IMR_TX_UNDERRUN 0x00000020 +#define DC_IMR_RX_OK 0x00000040 +#define DC_IMR_RX_NOBUF 0x00000080 +#define DC_IMR_RX_READ 0x00000100 +#define DC_IMR_RX_WATDOGTIMEO 0x00000200 +#define DC_IMR_TX_EARLY 0x00000400 +#define DC_IMR_TIMER_EXPIRED 0x00000800 +#define DC_IMR_LINKFAIL 0x00001000 +#define DC_IMR_BUS_ERR 0x00002000 +#define DC_IMR_RX_EARLY 0x00004000 +#define DC_IMR_ABNORMAL 0x00008000 +#define DC_IMR_NORMAL 0x00010000 +#define DC_IMR_100MBPSLINK 0x08000000 +#define DC_IMR_MAGICKPACK 0x10000000 + +#define DC_INTRS \ + (DC_IMR_RX_OK|DC_IMR_TX_OK|DC_IMR_RX_NOBUF|DC_IMR_RX_WATDOGTIMEO|\ + DC_IMR_TX_NOBUF|DC_IMR_TX_UNDERRUN|DC_IMR_BUS_ERR| \ + DC_IMR_ABNORMAL|DC_IMR_NORMAL/*|DC_IMR_TX_EARLY*/) +/* + * Serial I/O (EEPROM/ROM) bits. + */ +#define DC_SIO_EE_CS 0x00000001 /* EEPROM chip select */ +#define DC_SIO_EE_CLK 0x00000002 /* EEPROM clock */ +#define DC_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ +#define DC_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ +#define DC_SIO_ROMDATA4 0x00000010 +#define DC_SIO_ROMDATA5 0x00000020 +#define DC_SIO_ROMDATA6 0x00000040 +#define DC_SIO_ROMDATA7 0x00000080 +#define DC_SIO_EESEL 0x00000800 +#define DC_SIO_ROMSEL 0x00001000 +#define DC_SIO_ROMCTL_WRITE 0x00002000 +#define DC_SIO_ROMCTL_READ 0x00004000 +#define DC_SIO_MII_CLK 0x00010000 /* MDIO clock */ +#define DC_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ +#define DC_SIO_MII_DIR 0x00040000 /* MDIO dir */ +#define DC_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ + +#define DC_EECMD_WRITE 0x140 +#define DC_EECMD_READ 0x180 +#define DC_EECMD_ERASE 0x1c0 + +#define DC_EE_NODEADDR_OFFSET 0x70 +#define DC_EE_NODEADDR 10 + +/* + * General purpose timer register + */ +#define DC_TIMER_VALUE 0x0000FFFF +#define DC_TIMER_CONTINUOUS 0x00010000 + +/* + * 10baseT status register + */ +#define DC_TSTAT_MIIACT 0x00000001 /* MII port activity */ +#define DC_TSTAT_LS100 0x00000002 /* link status of 100baseTX */ +#define DC_TSTAT_LS10 0x00000004 /* link status of 10baseT */ +#define DC_TSTAT_AUTOPOLARITY 0x00000008 +#define DC_TSTAT_AUIACT 0x00000100 /* AUI activity */ +#define DC_TSTAT_10BTACT 0x00000200 /* 10baseT activity */ +#define DC_TSTAT_NSN 0x00000400 /* non-stable FLPs detected */ +#define DC_TSTAT_REMFAULT 0x00000800 +#define DC_TSTAT_ANEGSTAT 0x00007000 +#define DC_TSTAT_LP_CAN_NWAY 0x00008000 /* link partner supports NWAY */ +#define DC_TSTAT_LPCODEWORD 0xFFFF0000 /* link partner's code word */ + +#define DC_ASTAT_DISABLE 0x00000000 +#define DC_ASTAT_TXDISABLE 0x00001000 +#define DC_ASTAT_ABDETECT 0x00002000 +#define DC_ASTAT_ACKDETECT 0x00003000 +#define DC_ASTAT_CMPACKDETECT 0x00004000 +#define DC_ASTAT_AUTONEGCMP 0x00005000 +#define DC_ASTAT_LINKCHECK 0x00006000 + +/* + * PHY reset register + */ +#define DC_SIA_RESET 0x00000001 +#define DC_SIA_AUI 0x00000008 /* AUI or 10baseT */ + +/* + * 10baseT control register + */ +#define DC_TCTL_ENCODER_ENB 0x00000001 +#define DC_TCTL_LOOPBACK 0x00000002 +#define DC_TCTL_DRIVER_ENB 0x00000004 +#define DC_TCTL_LNKPULSE_ENB 0x00000008 +#define DC_TCTL_HALFDUPLEX 0x00000040 +#define DC_TCTL_AUTONEGENBL 0x00000080 +#define DC_TCTL_RX_SQUELCH 0x00000100 +#define DC_TCTL_COLL_SQUELCH 0x00000200 +#define DC_TCTL_COLL_DETECT 0x00000400 +#define DC_TCTL_SQE_ENB 0x00000800 +#define DC_TCTL_LINKTEST 0x00001000 +#define DC_TCTL_AUTOPOLARITY 0x00002000 +#define DC_TCTL_SET_POL_PLUS 0x00004000 +#define DC_TCTL_AUTOSENSE 0x00008000 /* 10bt/AUI autosense */ +#define DC_TCTL_100BTXHALF 0x00010000 +#define DC_TCTL_100BTXFULL 0x00020000 +#define DC_TCTL_100BT4 0x00040000 + +/* + * Watchdog timer register + */ +#define DC_WDOG_JABBERDIS 0x00000001 +#define DC_WDOG_HOSTUNJAB 0x00000002 +#define DC_WDOG_JABBERCLK 0x00000004 +#define DC_WDOG_RXWDOGDIS 0x00000010 +#define DC_WDOG_RXWDOGCLK 0x00000020 +#define DC_WDOG_MUSTBEZERO 0x00000100 + +/* + * Size of a setup frame. + */ +#define DC_SFRAME_LEN 192 + +/* + * 21x4x TX/RX list structure. + */ + +struct dc_desc { + u_int32_t dc_status; + u_int32_t dc_ctl; + u_int32_t dc_ptr1; + u_int32_t dc_ptr2; +}; + +#define dc_data dc_ptr1 +#define dc_next dc_ptr2 + +#define DC_RXSTAT_FIFOOFLOW 0x00000001 +#define DC_RXSTAT_CRCERR 0x00000002 +#define DC_RXSTAT_DRIBBLE 0x00000004 +#define DC_RXSTAT_WATCHDOG 0x00000010 +#define DC_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ +#define DC_RXSTAT_COLLSEEN 0x00000040 +#define DC_RXSTAT_GIANT 0x00000080 +#define DC_RXSTAT_LASTFRAG 0x00000100 +#define DC_RXSTAT_FIRSTFRAG 0x00000200 +#define DC_RXSTAT_MULTICAST 0x00000400 +#define DC_RXSTAT_RUNT 0x00000800 +#define DC_RXSTAT_RXTYPE 0x00003000 +#define DC_RXSTAT_RXERR 0x00008000 +#define DC_RXSTAT_RXLEN 0x3FFF0000 +#define DC_RXSTAT_OWN 0x80000000 + +#define DC_RXBYTES(x) ((x & DC_RXSTAT_RXLEN) >> 16) +#define DC_RXSTAT (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG|DC_RXSTAT_OWN) + +#define DC_RXCTL_BUFLEN1 0x00000FFF +#define DC_RXCTL_BUFLEN2 0x00FFF000 +#define DC_RXCTL_RLINK 0x01000000 +#define DC_RXCTL_RLAST 0x02000000 + +#define DC_TXSTAT_DEFER 0x00000001 +#define DC_TXSTAT_UNDERRUN 0x00000002 +#define DC_TXSTAT_LINKFAIL 0x00000003 +#define DC_TXSTAT_COLLCNT 0x00000078 +#define DC_TXSTAT_SQE 0x00000080 +#define DC_TXSTAT_EXCESSCOLL 0x00000100 +#define DC_TXSTAT_LATECOLL 0x00000200 +#define DC_TXSTAT_NOCARRIER 0x00000400 +#define DC_TXSTAT_CARRLOST 0x00000800 +#define DC_TXSTAT_JABTIMEO 0x00004000 +#define DC_TXSTAT_ERRSUM 0x00008000 +#define DC_TXSTAT_OWN 0x80000000 + +#define DC_TXCTL_BUFLEN1 0x000007FF +#define DC_TXCTL_BUFLEN2 0x003FF800 +#define DC_TXCTL_FILTTYPE0 0x00400000 +#define DC_TXCTL_PAD 0x00800000 +#define DC_TXCTL_TLINK 0x01000000 +#define DC_TXCTL_TLAST 0x02000000 +#define DC_TXCTL_NOCRC 0x04000000 +#define DC_TXCTL_SETUP 0x08000000 +#define DC_TXCTL_FILTTYPE1 0x10000000 +#define DC_TXCTL_FIRSTFRAG 0x20000000 +#define DC_TXCTL_LASTFRAG 0x40000000 +#define DC_TXCTL_FINT 0x80000000 + +#define DC_FILTER_PERFECT 0x00000000 +#define DC_FILTER_HASHPERF 0x00400000 +#define DC_FILTER_INVERSE 0x10000000 +#define DC_FILTER_HASHONLY 0x10400000 + +#define DC_MAXFRAGS 16 +#define DC_RX_LIST_CNT 64 +#define DC_TX_LIST_CNT 256 +#define DC_MIN_FRAMELEN 60 +#define DC_RXLEN 1536 + +#define DC_INC(x, y) (x) = (x + 1) % y + +struct dc_list_data { + struct dc_desc dc_rx_list[DC_RX_LIST_CNT]; + struct dc_desc dc_tx_list[DC_TX_LIST_CNT]; +}; + +struct dc_chain_data { + struct mbuf *dc_rx_chain[DC_RX_LIST_CNT]; + struct mbuf *dc_tx_chain[DC_TX_LIST_CNT]; + u_int32_t dc_sbuf[DC_SFRAME_LEN/sizeof(u_int32_t)]; + u_int8_t dc_pad[DC_MIN_FRAMELEN]; + int dc_tx_prod; + int dc_tx_cons; + int dc_tx_cnt; + int dc_rx_prod; +}; + +struct dc_type { + u_int16_t dc_vid; + u_int16_t dc_did; + char *dc_name; +}; + +struct dc_mii_frame { + u_int8_t mii_stdelim; + u_int8_t mii_opcode; + u_int8_t mii_phyaddr; + u_int8_t mii_regaddr; + u_int8_t mii_turnaround; + u_int16_t mii_data; +}; + +/* + * MII constants + */ +#define DC_MII_STARTDELIM 0x01 +#define DC_MII_READOP 0x02 +#define DC_MII_WRITEOP 0x01 +#define DC_MII_TURNAROUND 0x02 + + +/* + * Registers specific to clone devices. + * This mainly relates to RX filter programming: not all 21x4x clones + * use the standard DEC filter programming mechanism. + */ + +/* + * ADMtek specific registers and constants for the AL981 and AN985. + * The AN985 doesn't use the magic PHY registers. + */ +#define DC_AL_PAR0 0xA4 /* station address */ +#define DC_AL_PAR1 0xA8 /* station address */ +#define DC_AL_MAR0 0xAC /* multicast hash filter */ +#define DC_AL_MAR1 0xB0 /* multicast hash filter */ +#define DC_AL_BMCR 0xB4 /* built in PHY control */ +#define DC_AL_BMSR 0xB8 /* built in PHY status */ +#define DC_AL_VENID 0xBC /* built in PHY ID0 */ +#define DC_AL_DEVID 0xC0 /* built in PHY ID1 */ +#define DC_AL_ANAR 0xC4 /* built in PHY autoneg advert */ +#define DC_AL_LPAR 0xC8 /* bnilt in PHY link part. ability */ +#define DC_AL_ANER 0xCC /* built in PHY autoneg expansion */ + +#define DC_ADMTEK_PHYADDR 0x1 +#define DC_AL_EE_NODEADDR 4 +/* End of ADMtek specific registers */ + +/* + * ASIX specific registers. + */ +#define DC_AX_FILTIDX 0x68 /* RX filter index */ +#define DC_AX_FILTDATA 0x70 /* RX filter data */ + +/* + * Special ASIX-specific bits in the ASIX NETCFG register (CSR6). + */ +#define DC_AX_NETCFG_RX_BROAD 0x00000100 + +/* + * RX Filter Index Register values + */ +#define DC_AX_FILTIDX_PAR0 0x00000000 +#define DC_AX_FILTIDX_PAR1 0x00000001 +#define DC_AX_FILTIDX_MAR0 0x00000002 +#define DC_AX_FILTIDX_MAR1 0x00000003 +/* End of ASIX specific registers */ + +/* + * Macronix specific registers. The Macronix chips have a special + * register for reading the NWAY status, which we don't use, plus + * a magic packet register, which we need to tweak a bit per the + * Macronix application notes. + */ +#define DC_MX_MAGICPACKET 0x80 +#define DC_MX_NWAYSTAT 0xA0 + +/* + * Magic packet register + */ +#define DC_MX_MPACK_DISABLE 0x00400000 + +/* + * NWAY status register. + */ +#define DC_MX_NWAY_10BTHALF 0x08000000 +#define DC_MX_NWAY_10BTFULL 0x10000000 +#define DC_MX_NWAY_100BTHALF 0x20000000 +#define DC_MX_NWAY_100BTFULL 0x40000000 +#define DC_MX_NWAY_100BT4 0x80000000 + +/* + * These are magic values that must be written into CSR16 + * (DC_MX_MAGICPACKET) in order to put the chip into proper + * operating mode. The magic numbers are documented in the + * Macronix 98715 application notes. + */ +#define DC_MX_MAGIC_98713 0x0F370000 +#define DC_MX_MAGIC_98713A 0x0B3C0000 +#define DC_MX_MAGIC_98715 0x0B3C0000 +#define DC_MX_MAGIC_98725 0x0B3C0000 +/* End of Macronix specific registers */ + +/* + * PNIC 82c168/82c169 specific registers. + * The PNIC has its own special NWAY support, which doesn't work, + * and shortcut ways of reading the EEPROM and MII bus. + */ +#define DC_PN_GPIO 0x60 /* general purpose pins control */ +#define DC_PN_PWRUP_CFG 0x90 /* config register, set by EEPROM */ +#define DC_PN_SIOCTL 0x98 /* serial EEPROM control register */ +#define DC_PN_MII 0xA0 /* MII access register */ +#define DC_PN_NWAY 0xB8 /* Internal NWAY register */ + +/* Serial I/O EEPROM register */ +#define DC_PN_SIOCTL_DATA 0x0000003F +#define DC_PN_SIOCTL_OPCODE 0x00000300 +#define DC_PN_SIOCTL_BUSY 0x80000000 + +#define DC_PN_EEOPCODE_ERASE 0x00000300 +#define DC_PN_EEOPCODE_READ 0x00000600 +#define DC_PN_EEOPCODE_WRITE 0x00000100 + +/* + * The first two general purpose pins control speed selection and + * 100Mbps loopback on the 82c168 chip. The control bits should always + * be set (to make the data pins outputs) and the speed selction and + * loopback bits set accordingly when changing media. Physically, this + * will set the state of a relay mounted on the card. + */ +#define DC_PN_GPIO_DATA0 0x000000001 +#define DC_PN_GPIO_DATA1 0x000000002 +#define DC_PN_GPIO_DATA2 0x000000004 +#define DC_PN_GPIO_DATA3 0x000000008 +#define DC_PN_GPIO_CTL0 0x000000010 +#define DC_PN_GPIO_CTL1 0x000000020 +#define DC_PN_GPIO_CTL2 0x000000040 +#define DC_PN_GPIO_CTL3 0x000000080 +#define DC_PN_GPIO_SPEEDSEL DC_PN_GPIO_DATA0/* 1 == 100Mbps, 0 == 10Mbps */ +#define DC_PN_GPIO_100TX_LOOP DC_PN_GPIO_DATA1/* 1 == normal, 0 == loop */ +#define DC_PN_GPIO_BNC_ENB DC_PN_GPIO_DATA2 +#define DC_PN_GPIO_100TX_LNK DC_PN_GPIO_DATA3 +#define DC_PN_GPIO_SETBIT(sc, r) \ + DC_SETBIT(sc, DC_PN_GPIO, ((r) | (r << 4))) +#define DC_PN_GPIO_CLRBIT(sc, r) \ + { \ + DC_SETBIT(sc, DC_PN_GPIO, ((r) << 4)); \ + DC_CLRBIT(sc, DC_PN_GPIO, (r)); \ + } + +/* shortcut MII access register */ +#define DC_PN_MII_DATA 0x0000FFFF +#define DC_PN_MII_RESERVER 0x00020000 +#define DC_PN_MII_REGADDR 0x007C0000 +#define DC_PN_MII_PHYADDR 0x0F800000 +#define DC_PN_MII_OPCODE 0x30000000 +#define DC_PN_MII_BUSY 0x80000000 + +#define DC_PN_MIIOPCODE_READ 0x60020000 +#define DC_PN_MIIOPCODE_WRITE 0x50020000 + +/* Internal NWAY bits */ +#define DC_PN_NWAY_RESET 0x00000001 /* reset */ +#define DC_PN_NWAY_PDOWN 0x00000002 /* power down */ +#define DC_PN_NWAY_BYPASS 0x00000004 /* bypass */ +#define DC_PN_NWAY_AUILOWCUR 0x00000008 /* AUI low current */ +#define DC_PN_NWAY_TPEXTEND 0x00000010 /* low squelch voltage */ +#define DC_PN_NWAY_POLARITY 0x00000020 /* 0 == on, 1 == off */ +#define DC_PN_NWAY_TP 0x00000040 /* 1 == tp, 0 == AUI */ +#define DC_PN_NWAY_AUIVOLT 0x00000080 /* 1 == full, 0 == half */ +#define DC_PN_NWAY_DUPLEX 0x00000100 /* LED, 1 == full, 0 == half */ +#define DC_PN_NWAY_LINKTEST 0x00000200 /* 0 == on, 1 == off */ +#define DC_PN_NWAY_AUTODETECT 0x00000400 /* 1 == off, 0 == on */ +#define DC_PN_NWAY_SPEEDSEL 0x00000800 /* LED, 0 = 10, 1 == 100 */ +#define DC_PN_NWAY_NWAY_ENB 0x00001000 /* 0 == off, 1 == on */ +#define DC_PN_NWAY_CAP10HDX 0x00002000 +#define DC_PN_NWAY_CAP10FDX 0x00004000 +#define DC_PN_NWAY_CAP100FDX 0x00008000 +#define DC_PN_NWAY_CAP100HDX 0x00010000 +#define DC_PN_NWAY_CAP100T4 0x00020000 +#define DC_PN_NWAY_ANEGRESTART 0x02000000 /* resets when aneg done */ +#define DC_PN_NWAY_REMFAULT 0x04000000 +#define DC_PN_NWAY_LPAR10HDX 0x08000000 +#define DC_PN_NWAY_LPAR10FDX 0x10000000 +#define DC_PN_NWAY_LPAR100FDX 0x20000000 +#define DC_PN_NWAY_LPAR100HDX 0x40000000 +#define DC_PN_NWAY_LPAR100T4 0x80000000 + +/* End of PNIC specific registers */ + +struct dc_softc { + struct arpcom arpcom; /* interface info */ + bus_space_handle_t dc_bhandle; /* bus space handle */ + bus_space_tag_t dc_btag; /* bus space tag */ + void *dc_intrhand; + struct resource *dc_irq; + struct resource *dc_res; + struct dc_type *dc_info; /* adapter info */ + device_t dc_miibus; + u_int8_t dc_unit; /* interface number */ + u_int8_t dc_type; + u_int8_t dc_pmode; + u_int8_t dc_link; + u_int8_t dc_cachesize; + int dc_pnic_rx_bug_save; + unsigned char *dc_pnic_rx_buf; + int dc_if_flags; + int dc_if_media; + u_int32_t dc_flags; + u_int32_t dc_txthresh; + struct dc_list_data *dc_ldata; + struct dc_chain_data dc_cdata; + struct callout_handle dc_stat_ch; +}; + +#define DC_TX_POLL 0x00000001 +#define DC_TX_COALESCE 0x00000002 +#define DC_TX_ADMTEK_WAR 0x00000004 +#define DC_TX_USE_TX_INTR 0x00000008 +#define DC_RX_FILTER_TULIP 0x00000010 +#define DC_TX_INTR_FIRSTFRAG 0x00000020 +#define DC_PNIC_RX_BUG_WAR 0x00000040 +#define DC_TX_FIXED_RING 0x00000080 +#define DC_TX_STORENFWD 0x00000100 +#define DC_REDUCED_MII_POLL 0x00000200 + +/* + * register space access macros + */ +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4(sc->dc_btag, sc->dc_bhandle, reg, val) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4(sc->dc_btag, sc->dc_bhandle, reg) + +#define DC_TIMEOUT 1000 +#define ETHER_ALIGN 2 + +/* + * General constants that are fun to know. + */ + +/* + * DEC PCI vendor ID + */ +#define DC_VENDORID_DEC 0x1011 + +/* + * DEC/Intel 21143 PCI device ID + */ +#define DC_DEVICEID_21143 0x0019 + +/* + * Macronix PCI vendor ID + */ +#define DC_VENDORID_MX 0x10D9 + +/* + * Macronix PMAC device IDs. + */ +#define DC_DEVICEID_98713 0x0512 +#define DC_DEVICEID_987x5 0x0531 + +/* Macronix PCI revision codes. */ +#define DC_REVISION_98713 0x00 +#define DC_REVISION_98713A 0x10 +#define DC_REVISION_98715 0x20 +#define DC_REVISION_98725 0x30 + +/* + * Compex PCI vendor ID. + */ +#define DC_VENDORID_CP 0x11F6 + +/* + * Compex PMAC PCI device IDs. + */ +#define DC_DEVICEID_98713_CP 0x9881 + +/* + * Lite-On PNIC PCI vendor ID + */ +#define DC_VENDORID_LO 0x11AD + +/* + * 82c168/82c169 PNIC device IDs. Both chips have the same device + * ID but different revisions. Revision 0x10 is the 82c168, and + * 0x20 is the 82c169. + */ +#define DC_DEVICEID_82C168 0x0002 + +#define DC_REVISION_82C168 0x10 +#define DC_REVISION_82C169 0x20 + +/* + * Lite-On PNIC II device ID. Note: this is actually a Macronix 98715A + * with wake on lan/magic packet support. + */ +#define DC_DEVICEID_82C115 0xc115 + +/* + * Davicom vendor ID. + */ +#define DC_VENDORID_DAVICOM 0x1282 + +/* + * Davicom device IDs. + */ +#define DC_DEVICEID_DM9100 0x9100 +#define DC_DEVICEID_DM9102 0x9102 + +/* + * ADMtek vendor ID. + */ +#define DC_VENDORID_ADMTEK 0x1317 + +/* + * ADMtek device IDs. + */ +#define DC_DEVICEID_AL981 0x0981 +#define DC_DEVICEID_AN985 0x0985 + +/* + * ASIX vendor ID. + */ +#define DC_VENDORID_ASIX 0x125B + +/* + * ASIX device IDs. + */ +#define DC_DEVICEID_AX88140A 0x1400 + +/* + * The ASIX AX88140 and ASIX AX88141 have the same vendor and + * device IDs but different revision values. + */ +#define DC_REVISION_88140 0x00 +#define DC_REVISION_88141 0x10 + +/* + * PCI low memory base and low I/O base register, and + * other PCI registers. + */ + +#define DC_PCI_CFID 0x00 /* Id */ +#define DC_PCI_CFCS 0x04 /* Command and status */ +#define DC_PCI_CFRV 0x08 /* Revision */ +#define DC_PCI_CFLT 0x0C /* Latency timer */ +#define DC_PCI_CFBIO 0x10 /* Base I/O address */ +#define DC_PCI_CFBMA 0x14 /* Base memory address */ +#define DC_PCI_CCIS 0x28 /* Card info struct */ +#define DC_PCI_CSID 0x2C /* Subsystem ID */ +#define DC_PCI_CBER 0x30 /* Expansion ROM base address */ +#define DC_PCI_CCAP 0x34 /* Caps pointer - PD/TD chip only */ +#define DC_PCI_CFIT 0x3C /* Interrupt */ +#define DC_PCI_CFDD 0x40 /* Device and driver area */ +#define DC_PCI_CWUA0 0x44 /* Wake-Up LAN addr 0 */ +#define DC_PCI_CWUA1 0x48 /* Wake-Up LAN addr 1 */ +#define DC_PCI_SOP0 0x4C /* SecureON passwd 0 */ +#define DC_PCI_SOP1 0x50 /* SecureON passwd 1 */ +#define DC_PCI_CWUC 0x54 /* Configuration Wake-Up cmd */ +#define DC_PCI_CCID 0xDC /* Capability ID - PD/TD only */ +#define DC_PCI_CPMC 0xE0 /* Pwrmgmt ctl & sts - PD/TD only */ + +/* PCI ID register */ +#define DC_CFID_VENDOR 0x0000FFFF +#define DC_CFID_DEVICE 0xFFFF0000 + +/* PCI command/status register */ +#define DC_CFCS_IOSPACE 0x00000001 /* I/O space enable */ +#define DC_CFCS_MEMSPACE 0x00000002 /* memory space enable */ +#define DC_CFCS_BUSMASTER 0x00000004 /* bus master enable */ +#define DC_CFCS_MWI_ENB 0x00000008 /* mem write and inval enable */ +#define DC_CFCS_PARITYERR_ENB 0x00000020 /* parity error enable */ +#define DC_CFCS_SYSERR_ENB 0x00000080 /* system error enable */ +#define DC_CFCS_NEWCAPS 0x00100000 /* new capabilities */ +#define DC_CFCS_FAST_B2B 0x00800000 /* fast back-to-back capable */ +#define DC_CFCS_DATAPARITY 0x01000000 /* Parity error report */ +#define DC_CFCS_DEVSELTIM 0x06000000 /* devsel timing */ +#define DC_CFCS_TGTABRT 0x10000000 /* received target abort */ +#define DC_CFCS_MASTERABRT 0x20000000 /* received master abort */ +#define DC_CFCS_SYSERR 0x40000000 /* asserted system error */ +#define DC_CFCS_PARITYERR 0x80000000 /* asserted parity error */ + +/* PCI revision register */ +#define DC_CFRV_STEPPING 0x0000000F +#define DC_CFRV_REVISION 0x000000F0 +#define DC_CFRV_SUBCLASS 0x00FF0000 +#define DC_CFRV_BASECLASS 0xFF000000 + +#define DC_21143_PB_REV 0x00000030 +#define DC_21143_TB_REV 0x00000030 +#define DC_21143_PC_REV 0x00000030 +#define DC_21143_TC_REV 0x00000030 +#define DC_21143_PD_REV 0x00000041 +#define DC_21143_TD_REV 0x00000041 + +/* PCI latency timer register */ +#define DC_CFLT_CACHELINESIZE 0x000000FF +#define DC_CFLT_LATENCYTIMER 0x0000FF00 + +/* PCI subsystem ID register */ +#define DC_CSID_VENDOR 0x0000FFFF +#define DC_CSID_DEVICE 0xFFFF0000 + +/* PCI cababilities pointer */ +#define DC_CCAP_OFFSET 0x000000FF + +/* PCI interrupt config register */ +#define DC_CFIT_INTLINE 0x000000FF +#define DC_CFIT_INTPIN 0x0000FF00 +#define DC_CFIT_MIN_GNT 0x00FF0000 +#define DC_CFIT_MAX_LAT 0xFF000000 + +/* PCI capability register */ +#define DC_CCID_CAPID 0x000000FF +#define DC_CCID_NEXTPTR 0x0000FF00 +#define DC_CCID_PM_VERS 0x00070000 +#define DC_CCID_PME_CLK 0x00080000 +#define DC_CCID_DVSPEC_INT 0x00200000 +#define DC_CCID_STATE_D1 0x02000000 +#define DC_CCID_STATE_D2 0x04000000 +#define DC_CCID_PME_D0 0x08000000 +#define DC_CCID_PME_D1 0x10000000 +#define DC_CCID_PME_D2 0x20000000 +#define DC_CCID_PME_D3HOT 0x40000000 +#define DC_CCID_PME_D3COLD 0x80000000 + +/* PCI power management control/status register */ +#define DC_CPMC_STATE 0x00000003 +#define DC_CPMC_PME_ENB 0x00000100 +#define DC_CPMC_PME_STS 0x00008000 + +#define DC_PSTATE_D0 0x0 +#define DC_PSTATE_D1 0x1 +#define DC_PSTATE_D2 0x2 +#define DC_PSTATE_D3 0x3 + +/* Device specific region */ +/* Configuration and driver area */ +#define DC_CFDD_DRVUSE 0x0000FFFF +#define DC_CFDD_SNOOZE_MODE 0x40000000 +#define DC_CFDD_SLEEP_MODE 0x80000000 + +/* Configuration wake-up command register */ +#define DC_CWUC_MUST_BE_ZERO 0x00000001 +#define DC_CWUC_SECUREON_ENB 0x00000002 +#define DC_CWUC_FORCE_WUL 0x00000004 +#define DC_CWUC_BNC_ABILITY 0x00000008 +#define DC_CWUC_AUI_ABILITY 0x00000010 +#define DC_CWUC_TP10_ABILITY 0x00000020 +#define DC_CWUC_MII_ABILITY 0x00000040 +#define DC_CWUC_SYM_ABILITY 0x00000080 +#define DC_CWUC_LOCK 0x00000100 + +#ifdef __alpha__ +#undef vtophys +#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) +#endif diff --git a/sys/pci/if_dm.c b/sys/pci/if_dm.c deleted file mode 100644 index 96cd885..0000000 --- a/sys/pci/if_dm.c +++ /dev/null @@ -1,1709 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * Davicom DM9102 fast ethernet PCI NIC driver. - * - * Written by Bill Paul <wpaul@ee.columbia.edu> - * Electrical Engineering Department - * Columbia University, New York City - */ - -/* - * The Davicom DM9102 is yet another DEC 21x4x clone. This one is actually - * a pretty faithful copy. Same RX filter programming, same SROM layout, - * same everything. Datasheets available from www.davicom8.com. Only - * MII-based transceivers are supported. - * - * The DM9102's DMA engine seems pretty weak. Multi-fragment transmits - * don't seem to work well, and on slow machines you get lots of RX - * overruns. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <net/ethernet.h> -#include <net/if_dl.h> -#include <net/if_media.h> - -#include <net/bpf.h> - -#include <vm/vm.h> /* for vtophys */ -#include <vm/pmap.h> /* for vtophys */ -#include <machine/clock.h> /* for DELAY */ -#include <machine/bus_pio.h> -#include <machine/bus_memio.h> -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/bus.h> -#include <sys/rman.h> - -#include <dev/mii/mii.h> -#include <dev/mii/miivar.h> - -#include <pci/pcireg.h> -#include <pci/pcivar.h> - -#define DM_USEIOSPACE - -#include <pci/if_dmreg.h> - -/* "controller miibus0" required. See GENERIC if you get errors here. */ -#include "miibus_if.h" - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - -/* - * Various supported device vendors/types and their names. - */ -static struct dm_type dm_devs[] = { - { DM_VENDORID, DM_DEVICEID_DM9100, "Davicom DM9100 10/100BaseTX" }, - { DM_VENDORID, DM_DEVICEID_DM9102, "Davicom DM9102 10/100BaseTX" }, - { 0, 0, NULL } -}; - -static int dm_probe __P((device_t)); -static int dm_attach __P((device_t)); -static int dm_detach __P((device_t)); - -static int dm_newbuf __P((struct dm_softc *, - struct dm_desc *, - struct mbuf *)); -static int dm_encap __P((struct dm_softc *, - struct mbuf **, u_int32_t *)); - -static void dm_rxeof __P((struct dm_softc *)); -static void dm_rxeoc __P((struct dm_softc *)); -static void dm_txeof __P((struct dm_softc *)); -static void dm_intr __P((void *)); -static void dm_tick __P((void *)); -static void dm_start __P((struct ifnet *)); -static int dm_ioctl __P((struct ifnet *, u_long, caddr_t)); -static void dm_init __P((void *)); -static void dm_stop __P((struct dm_softc *)); -static void dm_watchdog __P((struct ifnet *)); -static void dm_shutdown __P((device_t)); -static int dm_ifmedia_upd __P((struct ifnet *)); -static void dm_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); - -static void dm_delay __P((struct dm_softc *)); -static void dm_eeprom_idle __P((struct dm_softc *)); -static void dm_eeprom_putbyte __P((struct dm_softc *, int)); -static void dm_eeprom_getword __P((struct dm_softc *, int, u_int16_t *)); -static void dm_read_eeprom __P((struct dm_softc *, caddr_t, int, - int, int)); - -static void dm_mii_writebit __P((struct dm_softc *, int)); -static int dm_mii_readbit __P((struct dm_softc *)); -static void dm_mii_sync __P((struct dm_softc *)); -static void dm_mii_send __P((struct dm_softc *, u_int32_t, int)); -static int dm_mii_readreg __P((struct dm_softc *, struct dm_mii_frame *)); -static int dm_mii_writereg __P((struct dm_softc *, struct dm_mii_frame *)); -static int dm_miibus_readreg __P((device_t, int, int)); -static int dm_miibus_writereg __P((device_t, int, int, int)); -static void dm_miibus_statchg __P((device_t)); - -static u_int32_t dm_calchash __P((caddr_t)); -static void dm_setfilt __P((struct dm_softc *)); -static void dm_reset __P((struct dm_softc *)); -static int dm_list_rx_init __P((struct dm_softc *)); -static int dm_list_tx_init __P((struct dm_softc *)); - -#ifdef DM_USEIOSPACE -#define DM_RES SYS_RES_IOPORT -#define DM_RID DM_PCI_LOIO -#else -#define DM_RES SYS_RES_MEMORY -#define DM_RID DM_PCI_LOMEM -#endif - -static device_method_t dm_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, dm_probe), - DEVMETHOD(device_attach, dm_attach), - DEVMETHOD(device_detach, dm_detach), - DEVMETHOD(device_shutdown, dm_shutdown), - - /* bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), - - /* MII interface */ - DEVMETHOD(miibus_readreg, dm_miibus_readreg), - DEVMETHOD(miibus_writereg, dm_miibus_writereg), - DEVMETHOD(miibus_statchg, dm_miibus_statchg), - - { 0, 0 } -}; - -static driver_t dm_driver = { - "dm", - dm_methods, - sizeof(struct dm_softc) -}; - -static devclass_t dm_devclass; - -DRIVER_MODULE(if_dm, pci, dm_driver, dm_devclass, 0, 0); -DRIVER_MODULE(miibus, dm, miibus_driver, miibus_devclass, 0, 0); - -#define DM_SETBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) | x) - -#define DM_CLRBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) & ~x) - -#define SIO_SET(x) \ - CSR_WRITE_4(sc, DM_SIO, \ - CSR_READ_4(sc, DM_SIO) | x) - -#define SIO_CLR(x) \ - CSR_WRITE_4(sc, DM_SIO, \ - CSR_READ_4(sc, DM_SIO) & ~x) - -static void dm_delay(sc) - struct dm_softc *sc; -{ - int idx; - - for (idx = (300 / 33) + 1; idx > 0; idx--) - CSR_READ_4(sc, DM_BUSCTL); -} - -static void dm_eeprom_idle(sc) - struct dm_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, DM_SIO, DM_SIO_EESEL); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_ROMCTL_READ); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_EE_CS); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_EE_CLK); - dm_delay(sc); - - for (i = 0; i < 25; i++) { - DM_CLRBIT(sc, DM_SIO, DM_SIO_EE_CLK); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_EE_CLK); - dm_delay(sc); - } - - DM_CLRBIT(sc, DM_SIO, DM_SIO_EE_CLK); - dm_delay(sc); - DM_CLRBIT(sc, DM_SIO, DM_SIO_EE_CS); - dm_delay(sc); - CSR_WRITE_4(sc, DM_SIO, 0x00000000); - - return; -} - -/* - * Send a read command and address to the EEPROM, check for ACK. - */ -static void dm_eeprom_putbyte(sc, addr) - struct dm_softc *sc; - int addr; -{ - register int d, i; - - d = addr | DM_EECMD_READ; - - /* - * Feed in each bit and stobe the clock. - */ - for (i = 0x400; i; i >>= 1) { - if (d & i) { - SIO_SET(DM_SIO_EE_DATAIN); - } else { - SIO_CLR(DM_SIO_EE_DATAIN); - } - dm_delay(sc); - SIO_SET(DM_SIO_EE_CLK); - dm_delay(sc); - SIO_CLR(DM_SIO_EE_CLK); - dm_delay(sc); - } - - return; -} - -/* - * Read a word of data stored in the EEPROM at address 'addr.' - */ -static void dm_eeprom_getword(sc, addr, dest) - struct dm_softc *sc; - int addr; - u_int16_t *dest; -{ - register int i; - u_int16_t word = 0; - - /* Force EEPROM to idle state. */ - dm_eeprom_idle(sc); - - /* Enter EEPROM access mode. */ - CSR_WRITE_4(sc, DM_SIO, DM_SIO_EESEL); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_ROMCTL_READ); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_EE_CS); - dm_delay(sc); - DM_SETBIT(sc, DM_SIO, DM_SIO_EE_CLK); - dm_delay(sc); - - /* - * Send address of word we want to read. - */ - dm_eeprom_putbyte(sc, addr); - - /* - * Start reading bits from EEPROM. - */ - for (i = 0x8000; i; i >>= 1) { - SIO_SET(DM_SIO_EE_CLK); - dm_delay(sc); - if (CSR_READ_4(sc, DM_SIO) & DM_SIO_EE_DATAOUT) - word |= i; - dm_delay(sc); - SIO_CLR(DM_SIO_EE_CLK); - dm_delay(sc); - } - - /* Turn off EEPROM access mode. */ - dm_eeprom_idle(sc); - - *dest = word; - - return; -} - -/* - * Read a sequence of words from the EEPROM. - */ -static void dm_read_eeprom(sc, dest, off, cnt, swap) - struct dm_softc *sc; - caddr_t dest; - int off; - int cnt; - int swap; -{ - int i; - u_int16_t word = 0, *ptr; - - for (i = 0; i < cnt; i++) { - dm_eeprom_getword(sc, off + i, &word); - ptr = (u_int16_t *)(dest + (i * 2)); - if (swap) - *ptr = ntohs(word); - else - *ptr = word; - } - - return; -} - -/* - * Write a bit to the MII bus. - */ -static void dm_mii_writebit(sc, bit) - struct dm_softc *sc; - int bit; -{ - if (bit) - CSR_WRITE_4(sc, DM_SIO, DM_SIO_ROMCTL_WRITE|DM_SIO_MII_DATAOUT); - else - CSR_WRITE_4(sc, DM_SIO, DM_SIO_ROMCTL_WRITE); - - DM_SETBIT(sc, DM_SIO, DM_SIO_MII_CLK); - DM_CLRBIT(sc, DM_SIO, DM_SIO_MII_CLK); - - return; -} - -/* - * Read a bit from the MII bus. - */ -static int dm_mii_readbit(sc) - struct dm_softc *sc; -{ - CSR_WRITE_4(sc, DM_SIO, DM_SIO_ROMCTL_READ|DM_SIO_MII_DIR); - CSR_READ_4(sc, DM_SIO); - DM_SETBIT(sc, DM_SIO, DM_SIO_MII_CLK); - DM_CLRBIT(sc, DM_SIO, DM_SIO_MII_CLK); - if (CSR_READ_4(sc, DM_SIO) & DM_SIO_MII_DATAIN) - return(1); - - return(0); -} - -/* - * Sync the PHYs by setting data bit and strobing the clock 32 times. - */ -static void dm_mii_sync(sc) - struct dm_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, DM_SIO, DM_SIO_ROMCTL_WRITE); - - for (i = 0; i < 32; i++) - dm_mii_writebit(sc, 1); - - return; -} - -/* - * Clock a series of bits through the MII. - */ -static void dm_mii_send(sc, bits, cnt) - struct dm_softc *sc; - u_int32_t bits; - int cnt; -{ - int i; - - for (i = (0x1 << (cnt - 1)); i; i >>= 1) - dm_mii_writebit(sc, bits & i); -} - -/* - * Read an PHY register through the MII. - */ -static int dm_mii_readreg(sc, frame) - struct dm_softc *sc; - struct dm_mii_frame *frame; - -{ - int i, ack, s; - - s = splimp(); - - /* - * Set up frame for RX. - */ - frame->mii_stdelim = DM_MII_STARTDELIM; - frame->mii_opcode = DM_MII_READOP; - frame->mii_turnaround = 0; - frame->mii_data = 0; - - /* - * Sync the PHYs. - */ - dm_mii_sync(sc); - - /* - * Send command/address info. - */ - dm_mii_send(sc, frame->mii_stdelim, 2); - dm_mii_send(sc, frame->mii_opcode, 2); - dm_mii_send(sc, frame->mii_phyaddr, 5); - dm_mii_send(sc, frame->mii_regaddr, 5); - -#ifdef notdef - /* Idle bit */ - dm_mii_writebit(sc, 1); - dm_mii_writebit(sc, 0); -#endif - - /* Check for ack */ - ack = dm_mii_readbit(sc); - - /* - * Now try reading data bits. If the ack failed, we still - * need to clock through 16 cycles to keep the PHY(s) in sync. - */ - if (ack) { - for(i = 0; i < 16; i++) { - dm_mii_readbit(sc); - } - goto fail; - } - - for (i = 0x8000; i; i >>= 1) { - if (!ack) { - if (dm_mii_readbit(sc)) - frame->mii_data |= i; - } - } - -fail: - - dm_mii_writebit(sc, 0); - dm_mii_writebit(sc, 0); - - splx(s); - - if (ack) - return(1); - return(0); -} - -/* - * Write to a PHY register through the MII. - */ -static int dm_mii_writereg(sc, frame) - struct dm_softc *sc; - struct dm_mii_frame *frame; - -{ - int s; - - s = splimp(); - /* - * Set up frame for TX. - */ - - frame->mii_stdelim = DM_MII_STARTDELIM; - frame->mii_opcode = DM_MII_WRITEOP; - frame->mii_turnaround = DM_MII_TURNAROUND; - - /* - * Sync the PHYs. - */ - dm_mii_sync(sc); - - dm_mii_send(sc, frame->mii_stdelim, 2); - dm_mii_send(sc, frame->mii_opcode, 2); - dm_mii_send(sc, frame->mii_phyaddr, 5); - dm_mii_send(sc, frame->mii_regaddr, 5); - dm_mii_send(sc, frame->mii_turnaround, 2); - dm_mii_send(sc, frame->mii_data, 16); - - /* Idle bit. */ - dm_mii_writebit(sc, 0); - dm_mii_writebit(sc, 0); - - splx(s); - - return(0); -} - -static int dm_miibus_readreg(dev, phy, reg) - device_t dev; - int phy, reg; -{ - struct dm_softc *sc; - struct dm_mii_frame frame; - - sc = device_get_softc(dev); - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - dm_mii_readreg(sc, &frame); - - return(frame.mii_data); -} - -static int dm_miibus_writereg(dev, phy, reg, data) - device_t dev; - int phy, reg, data; -{ - struct dm_softc *sc; - struct dm_mii_frame frame; - - sc = device_get_softc(dev); - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - frame.mii_data = data; - - dm_mii_writereg(sc, &frame); - - return(0); -} - -static void dm_miibus_statchg(dev) - device_t dev; -{ - struct dm_softc *sc; - struct mii_data *mii; - - sc = device_get_softc(dev); - mii = device_get_softc(sc->dm_miibus); - - if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_SPEEDSEL); - else - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_SPEEDSEL); - - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_FULLDUPLEX); - else - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_FULLDUPLEX); - - return; -} - -#define DM_POLY 0xEDB88320 -#define DM_BITS 9 - -static u_int32_t dm_calchash(addr) - caddr_t addr; -{ - u_int32_t idx, bit, data, crc; - - /* Compute CRC for the address value. */ - crc = 0xFFFFFFFF; /* initial value */ - - for (idx = 0; idx < 6; idx++) { - for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) - crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DM_POLY : 0); - } - - return (crc & ((1 << DM_BITS) - 1)); -} - -void dm_setfilt(sc) - struct dm_softc *sc; -{ - struct dm_desc *sframe; - u_int32_t h, *sp; - struct ifmultiaddr *ifma; - struct ifnet *ifp; - int i; - - ifp = &sc->arpcom.ac_if; - - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_TX_ON); - DM_SETBIT(sc, DM_ISR, DM_ISR_TX_IDLE); - - sframe = &sc->dm_ldata->dm_sframe; - sp = (u_int32_t *)&sc->dm_cdata.dm_sbuf; - bzero((char *)sp, DM_SFRAME_LEN); - - sframe->dm_next = vtophys(&sc->dm_ldata->dm_tx_list[0]); - sframe->dm_data = vtophys(&sc->dm_cdata.dm_sbuf); - sframe->dm_ctl = DM_SFRAME_LEN | DM_TXCTL_TLINK | - DM_TXCTL_SETUP | DM_FILTER_HASHPERF; - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_RX_PROMISC); - else - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_RX_PROMISC); - - if (ifp->if_flags & IFF_ALLMULTI) - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_RX_ALLMULTI); - - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = dm_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - sp[h >> 4] |= 1 << (h & 0xF); - } - - if (ifp->if_flags & IFF_BROADCAST) { - h = dm_calchash((caddr_t)ðerbroadcastaddr); - sp[h >> 4] |= 1 << (h & 0xF); - } - - sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; - sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; - sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; - - CSR_WRITE_4(sc, DM_TXADDR, vtophys(sframe)); - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_TX_ON); - sframe->dm_status = DM_TXSTAT_OWN; - CSR_WRITE_4(sc, DM_TXSTART, 0xFFFFFFFF); - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_TX_ON); - - /* - * Wait for chip to clear the 'own' bit. - */ - for (i = 0; i < DM_TIMEOUT; i++) { - DELAY(10); - if (sframe->dm_status != DM_TXSTAT_OWN) - break; - } - - if (i == DM_TIMEOUT) - printf("dm%d: failed to send setup frame\n", sc->dm_unit); - - DM_SETBIT(sc, DM_ISR, DM_ISR_TX_NOBUF|DM_ISR_TX_IDLE); - - return; -} - -static void dm_reset(sc) - struct dm_softc *sc; -{ - register int i; - - DM_SETBIT(sc, DM_BUSCTL, DM_BUSCTL_RESET); - - for (i = 0; i < DM_TIMEOUT; i++) { - DELAY(10); - if (!(CSR_READ_4(sc, DM_BUSCTL) & DM_BUSCTL_RESET)) - break; - } - - if (i == DM_TIMEOUT) - printf("dm%d: reset never completed!\n", sc->dm_unit); - - CSR_WRITE_4(sc, DM_BUSCTL, 0); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); - return; -} - -/* - * Probe for an Davicom chip. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - */ -static int dm_probe(dev) - device_t dev; -{ - struct dm_type *t; - - t = dm_devs; - - while(t->dm_name != NULL) { - if ((pci_get_vendor(dev) == t->dm_vid) && - (pci_get_device(dev) == t->dm_did)) { - device_set_desc(dev, t->dm_name); - return(0); - } - t++; - } - - return(ENXIO); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -static int dm_attach(dev) - device_t dev; -{ - int s; - u_char eaddr[ETHER_ADDR_LEN]; - u_int32_t command; - struct dm_softc *sc; - struct ifnet *ifp; - int unit, error = 0, rid; - - s = splimp(); - - sc = device_get_softc(dev); - unit = device_get_unit(dev); - bzero(sc, sizeof(struct dm_softc)); - - /* - * Handle power management nonsense. - */ - - command = pci_read_config(dev, DM_PCI_CAPID, 4) & 0x000000FF; - if (command == 0x01) { - - command = pci_read_config(dev, DM_PCI_PWRMGMTCTRL, 4); - if (command & DM_PSTATE_MASK) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, DM_PCI_LOIO, 4); - membase = pci_read_config(dev, DM_PCI_LOMEM, 4); - irq = pci_read_config(dev, DM_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("dm%d: chip is in D%d power mode " - "-- setting to D0\n", unit, command & DM_PSTATE_MASK); - command &= 0xFFFFFFFC; - pci_write_config(dev, DM_PCI_PWRMGMTCTRL, command, 4); - - /* Restore PCI config data. */ - pci_write_config(dev, DM_PCI_LOIO, iobase, 4); - pci_write_config(dev, DM_PCI_LOMEM, membase, 4); - pci_write_config(dev, DM_PCI_INTLINE, irq, 4); - } - } - - /* - * Map control/status registers. - */ - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - -#ifdef DM_USEIOSPACE - if (!(command & PCIM_CMD_PORTEN)) { - printf("dm%d: failed to enable I/O ports!\n", unit); - error = ENXIO;; - goto fail; - } -#else - if (!(command & PCIM_CMD_MEMEN)) { - printf("dm%d: failed to enable memory mapping!\n", unit); - error = ENXIO;; - goto fail; - } -#endif - - rid = DM_RID; - sc->dm_res = bus_alloc_resource(dev, DM_RES, &rid, - 0, ~0, 1, RF_ACTIVE); - - if (sc->dm_res == NULL) { - printf("dm%d: couldn't map ports/memory\n", unit); - error = ENXIO; - goto fail; - } - - sc->dm_btag = rman_get_bustag(sc->dm_res); - sc->dm_bhandle = rman_get_bushandle(sc->dm_res); - - /* Allocate interrupt */ - rid = 0; - sc->dm_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, - RF_SHAREABLE | RF_ACTIVE); - - if (sc->dm_irq == NULL) { - printf("dm%d: couldn't map interrupt\n", unit); - bus_release_resource(dev, DM_RES, DM_RID, sc->dm_res); - error = ENXIO; - goto fail; - } - - error = bus_setup_intr(dev, sc->dm_irq, INTR_TYPE_NET, - dm_intr, sc, &sc->dm_intrhand); - - if (error) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dm_res); - bus_release_resource(dev, DM_RES, DM_RID, sc->dm_res); - printf("dm%d: couldn't set up irq\n", unit); - goto fail; - } - - /* Save the cache line size. */ - sc->dm_cachesize = pci_read_config(dev, DM_PCI_CACHELEN, 4) & 0xFF; - - /* Reset the adapter. */ - dm_reset(sc); - - /* - * Get station address from the EEPROM. - */ - dm_read_eeprom(sc, (caddr_t)&eaddr, DM_EE_NODEADDR, 3, 0); - - /* - * A Davicom chip was detected. Inform the world. - */ - printf("dm%d: Ethernet address: %6D\n", unit, eaddr, ":"); - - sc->dm_unit = unit; - callout_handle_init(&sc->dm_stat_ch); - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - - sc->dm_ldata = contigmalloc(sizeof(struct dm_list_data), M_DEVBUF, - M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); - - if (sc->dm_ldata == NULL) { - printf("dm%d: no memory for list buffers!\n", unit); - bus_teardown_intr(dev, sc->dm_irq, sc->dm_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dm_irq); - bus_release_resource(dev, DM_RES, DM_RID, sc->dm_res); - error = ENXIO; - goto fail; - } - bzero(sc->dm_ldata, sizeof(struct dm_list_data)); - - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = "dm"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = dm_ioctl; - ifp->if_output = ether_output; - ifp->if_start = dm_start; - ifp->if_watchdog = dm_watchdog; - ifp->if_init = dm_init; - ifp->if_baudrate = 10000000; - ifp->if_snd.ifq_maxlen = DM_TX_LIST_CNT - 1; - - /* - * Do MII setup. - */ - if (mii_phy_probe(dev, &sc->dm_miibus, - dm_ifmedia_upd, dm_ifmedia_sts)) { - printf("dm%d: MII without any PHY!\n", sc->dm_unit); - bus_teardown_intr(dev, sc->dm_irq, sc->dm_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dm_irq); - bus_release_resource(dev, DM_RES, DM_RID, sc->dm_res); - error = ENXIO; - goto fail; - } - - /* - * Call MI attach routines. - */ - if_attach(ifp); - ether_ifattach(ifp); - - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - -fail: - splx(s); - return(error); -} - -static int dm_detach(dev) - device_t dev; -{ - struct dm_softc *sc; - struct ifnet *ifp; - int s; - - s = splimp(); - - sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - - dm_reset(sc); - dm_stop(sc); - if_detach(ifp); - - bus_generic_detach(dev); - device_delete_child(dev, sc->dm_miibus); - - bus_teardown_intr(dev, sc->dm_irq, sc->dm_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dm_irq); - bus_release_resource(dev, DM_RES, DM_RID, sc->dm_res); - - contigfree(sc->dm_ldata, sizeof(struct dm_list_data), M_DEVBUF); - - splx(s); - - return(0); -} - -/* - * Initialize the transmit descriptors. - */ -static int dm_list_tx_init(sc) - struct dm_softc *sc; -{ - struct dm_chain_data *cd; - struct dm_list_data *ld; - int i; - - cd = &sc->dm_cdata; - ld = sc->dm_ldata; - for (i = 0; i < DM_TX_LIST_CNT; i++) { - if (i == (DM_TX_LIST_CNT - 1)) { - ld->dm_tx_list[i].dm_nextdesc = - &ld->dm_tx_list[0]; - ld->dm_tx_list[i].dm_next = - vtophys(&ld->dm_tx_list[0]); - } else { - ld->dm_tx_list[i].dm_nextdesc = - &ld->dm_tx_list[i + 1]; - ld->dm_tx_list[i].dm_next = - vtophys(&ld->dm_tx_list[i + 1]); - } - ld->dm_tx_list[i].dm_mbuf = NULL; - ld->dm_tx_list[i].dm_data = 0; - ld->dm_tx_list[i].dm_ctl = 0; - } - - cd->dm_tx_prod = cd->dm_tx_cons = cd->dm_tx_cnt = 0; - - return(0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int dm_list_rx_init(sc) - struct dm_softc *sc; -{ - struct dm_chain_data *cd; - struct dm_list_data *ld; - int i; - - cd = &sc->dm_cdata; - ld = sc->dm_ldata; - - for (i = 0; i < DM_RX_LIST_CNT; i++) { - if (dm_newbuf(sc, &ld->dm_rx_list[i], NULL) == ENOBUFS) - return(ENOBUFS); - if (i == (DM_RX_LIST_CNT - 1)) { - ld->dm_rx_list[i].dm_nextdesc = - &ld->dm_rx_list[0]; - ld->dm_rx_list[i].dm_next = - vtophys(&ld->dm_rx_list[0]); - } else { - ld->dm_rx_list[i].dm_nextdesc = - &ld->dm_rx_list[i + 1]; - ld->dm_rx_list[i].dm_next = - vtophys(&ld->dm_rx_list[i + 1]); - } - } - - cd->dm_rx_prod = 0; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - * Note: the length fields are only 11 bits wide, which means the - * largest size we can specify is 2047. This is important because - * MCLBYTES is 2048, so we have to subtract one otherwise we'll - * overflow the field and make a mess. - */ -static int dm_newbuf(sc, c, m) - struct dm_softc *sc; - struct dm_desc *c; - struct mbuf *m; -{ - struct mbuf *m_new = NULL; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("dm%d: no memory for rx list " - "-- packet dropped!\n", sc->dm_unit); - return(ENOBUFS); - } - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("dm%d: no memory for rx list " - "-- packet dropped!\n", sc->dm_unit); - m_freem(m_new); - return(ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - - m_adj(m_new, sizeof(u_int64_t)); - - c->dm_mbuf = m_new; - c->dm_data = vtophys(mtod(m_new, caddr_t)); - c->dm_ctl = DM_RXCTL_RLINK | DM_RXLEN; - c->dm_status = DM_RXSTAT_OWN; - - return(0); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void dm_rxeof(sc) - struct dm_softc *sc; -{ - struct ether_header *eh; - struct mbuf *m; - struct ifnet *ifp; - struct dm_desc *cur_rx; - int i, total_len = 0; - u_int32_t rxstat; - - ifp = &sc->arpcom.ac_if; - i = sc->dm_cdata.dm_rx_prod; - - while(!(sc->dm_ldata->dm_rx_list[i].dm_status & DM_RXSTAT_OWN)) { - struct mbuf *m0 = NULL; - - cur_rx = &sc->dm_ldata->dm_rx_list[i]; - rxstat = cur_rx->dm_status; - m = cur_rx->dm_mbuf; - cur_rx->dm_mbuf = NULL; - total_len = DM_RXBYTES(rxstat); - DM_INC(i, DM_RX_LIST_CNT); - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (rxstat & DM_RXSTAT_RXERR) { - ifp->if_ierrors++; - if (rxstat & DM_RXSTAT_COLLSEEN) - ifp->if_collisions++; - dm_newbuf(sc, cur_rx, m); - dm_init(sc); - return; - } - - /* No errors; receive the packet. */ - total_len -= ETHER_CRC_LEN; - - m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, - total_len + ETHER_ALIGN, 0, ifp, NULL); - dm_newbuf(sc, cur_rx, m); - if (m0 == NULL) { - ifp->if_ierrors++; - continue; - } - m_adj(m0, ETHER_ALIGN); - m = m0; - - ifp->if_ipackets++; - eh = mtod(m, struct ether_header *); - - /* - * Handle BPF listeners. Let the BPF user see the packet, but - * don't pass it up to the ether_input() layer unless it's - * a broadcast packet, multicast packet, matches our ethernet - * address or the interface is in promiscuous mode. - */ - if (ifp->if_bpf) { - bpf_mtap(ifp, m); - if (ifp->if_flags & IFF_PROMISC && - (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0)) { - m_freem(m); - continue; - } - } - - /* Remove header from mbuf and pass it on. */ - m_adj(m, sizeof(struct ether_header)); - ether_input(ifp, eh, m); - } - - sc->dm_cdata.dm_rx_prod = i; - - return; -} - -void dm_rxeoc(sc) - struct dm_softc *sc; -{ - dm_rxeof(sc); - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_RX_ON); - CSR_WRITE_4(sc, DM_RXSTART, 0xFFFFFFFF); - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ - -static void dm_txeof(sc) - struct dm_softc *sc; -{ - struct dm_desc *cur_tx = NULL; - struct ifnet *ifp; - int idx; - - ifp = &sc->arpcom.ac_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; - - /* - * Go through our tx list and free mbufs for those - * frames that have been transmitted. - */ - idx = sc->dm_cdata.dm_tx_cons; - while(idx != sc->dm_cdata.dm_tx_prod) { - u_int32_t txstat; - - cur_tx = &sc->dm_ldata->dm_tx_list[idx]; - txstat = cur_tx->dm_status; - - if (txstat & DM_TXSTAT_OWN) - break; - - if (!(cur_tx->dm_ctl & DM_TXCTL_LASTFRAG)) { - sc->dm_cdata.dm_tx_cnt--; - DM_INC(idx, DM_TX_LIST_CNT); - continue; - } - - if (txstat & DM_TXSTAT_ERRSUM) { - ifp->if_oerrors++; - if (txstat & DM_TXSTAT_EXCESSCOLL) - ifp->if_collisions++; - if (txstat & DM_TXSTAT_LATECOLL) - ifp->if_collisions++; - dm_init(sc); - return; - } - - ifp->if_collisions += (txstat & DM_TXSTAT_COLLCNT) >> 3; - - ifp->if_opackets++; - if (cur_tx->dm_mbuf != NULL) { - m_freem(cur_tx->dm_mbuf); - cur_tx->dm_mbuf = NULL; - } - - sc->dm_cdata.dm_tx_cnt--; - DM_INC(idx, DM_TX_LIST_CNT); - ifp->if_timer = 0; - } - - sc->dm_cdata.dm_tx_cons = idx; - - if (cur_tx != NULL) - ifp->if_flags &= ~IFF_OACTIVE; - - return; -} - -static void dm_tick(xsc) - void *xsc; -{ - struct dm_softc *sc; - struct mii_data *mii; - int s; - - s = splimp(); - - sc = xsc; - mii = device_get_softc(sc->dm_miibus); - mii_tick(mii); - - sc->dm_stat_ch = timeout(dm_tick, sc, hz); - - splx(s); - - return; -} - -static void dm_intr(arg) - void *arg; -{ - struct dm_softc *sc; - struct ifnet *ifp; - u_int32_t status; - - sc = arg; - ifp = &sc->arpcom.ac_if; - - /* Supress unwanted interrupts */ - if (!(ifp->if_flags & IFF_UP)) { - dm_stop(sc); - return; - } - - /* Disable interrupts. */ - CSR_WRITE_4(sc, DM_IMR, 0x00000000); - - for (;;) { - status = CSR_READ_4(sc, DM_ISR); - if (status) - CSR_WRITE_4(sc, DM_ISR, status); - - if ((status & DM_INTRS) == 0) - break; - - if ((status & DM_ISR_TX_OK) || (status & DM_ISR_TX_EARLY)) - dm_txeof(sc); - - if (status & DM_ISR_TX_NOBUF) - dm_txeof(sc); - - if (status & DM_ISR_TX_IDLE) { - dm_txeof(sc); - if (sc->dm_cdata.dm_tx_cnt) { - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_TX_ON); - CSR_WRITE_4(sc, DM_TXSTART, 0xFFFFFFFF); - } - } - - if (status & DM_ISR_TX_UNDERRUN) { - u_int32_t cfg; - cfg = CSR_READ_4(sc, DM_NETCFG); - if ((cfg & DM_NETCFG_TX_THRESH) == DM_TXTHRESH_160BYTES) - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_STORENFWD); - else - CSR_WRITE_4(sc, DM_NETCFG, cfg + 0x4000); - } - - if (status & DM_ISR_RX_OK) { - dm_rxeof(sc); - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_RX_ON); - CSR_WRITE_4(sc, DM_RXSTART, 0xFFFFFFFF); - } - - if ((status & DM_ISR_RX_WATDOGTIMEO) - || (status & DM_ISR_RX_NOBUF)) - dm_rxeoc(sc); - - if (status & DM_ISR_BUS_ERR) { - dm_reset(sc); - dm_init(sc); - } - } - - /* Re-enable interrupts. */ - CSR_WRITE_4(sc, DM_IMR, DM_INTRS); - - if (ifp->if_snd.ifq_head != NULL) - dm_start(ifp); - - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int dm_encap(sc, m_head, txidx) - struct dm_softc *sc; - struct mbuf **m_head; - u_int32_t *txidx; -{ - struct dm_desc *f = NULL; - struct mbuf *m; - int frag, cur, cnt = 0; - struct mbuf *m_new = NULL; - - m = *m_head; - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("dm%d: no memory for tx list", sc->dm_unit); - return(ENOBUFS); - } - if (m->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - printf("dm%d: no memory for tx list", sc->dm_unit); - return(ENOBUFS); - } - } - m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t)); - m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len; - m_freem(m); - *m_head = m_new; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - cur = frag = *txidx; - - for (m = m_new; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if ((DM_TX_LIST_CNT - - (sc->dm_cdata.dm_tx_cnt + cnt)) < 2) - return(ENOBUFS); - f = &sc->dm_ldata->dm_tx_list[frag]; - f->dm_ctl = DM_TXCTL_TLINK | m->m_len; - if (cnt == 0) { - f->dm_status = 0; - f->dm_ctl |= DM_TXCTL_FIRSTFRAG; - } else - f->dm_status = DM_TXSTAT_OWN; - f->dm_data = vtophys(mtod(m, vm_offset_t)); - cur = frag; - DM_INC(frag, DM_TX_LIST_CNT); - cnt++; - } - } - - if (m != NULL) - return(ENOBUFS); - - sc->dm_ldata->dm_tx_list[cur].dm_mbuf = *m_head; - sc->dm_ldata->dm_tx_list[cur].dm_ctl |= - DM_TXCTL_LASTFRAG|DM_TXCTL_FINT; - sc->dm_ldata->dm_tx_list[*txidx].dm_status |= DM_TXSTAT_OWN; - sc->dm_cdata.dm_tx_cnt += cnt; - *txidx = frag; - - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ - -static void dm_start(ifp) - struct ifnet *ifp; -{ - struct dm_softc *sc; - struct mbuf *m_head = NULL; - u_int32_t idx; - - sc = ifp->if_softc; - - if (ifp->if_flags & IFF_OACTIVE) - return; - - idx = sc->dm_cdata.dm_tx_prod; - - while(sc->dm_ldata->dm_tx_list[idx].dm_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - if (dm_encap(sc, &m_head, &idx)) { - IF_PREPEND(&ifp->if_snd, m_head); - ifp->if_flags |= IFF_OACTIVE; - break; - } - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp, m_head); - } - - sc->dm_cdata.dm_tx_prod = idx; - CSR_WRITE_4(sc, DM_TXSTART, 0xFFFFFFFF); - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; -} - -static void dm_init(xsc) - void *xsc; -{ - struct dm_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - struct mii_data *mii; - int s; - - s = splimp(); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - dm_stop(sc); - dm_reset(sc); - - mii = device_get_softc(sc->dm_miibus); - - /* - * Set cache alignment and burst length. - */ - CSR_WRITE_4(sc, DM_BUSCTL, DM_BURSTLEN_32LONG); - switch(sc->dm_cachesize) { - case 32: - DM_SETBIT(sc, DM_BUSCTL, DM_CACHEALIGN_32LONG); - break; - case 16: - DM_SETBIT(sc, DM_BUSCTL, DM_CACHEALIGN_16LONG); - break; - case 8: - DM_SETBIT(sc, DM_BUSCTL, DM_CACHEALIGN_8LONG); - break; - case 0: - default: - DM_SETBIT(sc, DM_BUSCTL, DM_CACHEALIGN_NONE); - break; - } - - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_HEARTBEAT); - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_STORENFWD); - - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_TX_THRESH); - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_SPEEDSEL); - - if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_10_T) - DM_SETBIT(sc, DM_NETCFG, DM_TXTHRESH_160BYTES); - else - DM_SETBIT(sc, DM_NETCFG, DM_TXTHRESH_72BYTES); - - /* Init circular RX list. */ - if (dm_list_rx_init(sc) == ENOBUFS) { - printf("dm%d: initialization failed: no " - "memory for rx buffers\n", sc->dm_unit); - dm_stop(sc); - (void)splx(s); - return; - } - - /* - * Init tx descriptors. - */ - dm_list_tx_init(sc); - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) { - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_RX_PROMISC); - } else { - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_RX_PROMISC); - } - - /* - * Set the capture broadcast bit to capture broadcast frames. - */ - if (ifp->if_flags & IFF_BROADCAST) { - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_RX_BROAD); - } else { - DM_CLRBIT(sc, DM_NETCFG, DM_NETCFG_RX_BROAD); - } - - /* - * Load the RX/multicast filter. - */ - dm_setfilt(sc); - - /* - * Load the address of the RX and TX lists. - */ - CSR_WRITE_4(sc, DM_RXADDR, vtophys(&sc->dm_ldata->dm_rx_list[0])); - /*CSR_WRITE_4(sc, DM_TXADDR, vtophys(&sc->dm_ldata->dm_tx_list[0]));*/ - - /* - * Enable interrupts. - */ - CSR_WRITE_4(sc, DM_IMR, DM_INTRS); - CSR_WRITE_4(sc, DM_ISR, 0xFFFFFFFF); - - /* Enable receiver and transmitter. */ - DM_SETBIT(sc, DM_NETCFG, DM_NETCFG_TX_ON|DM_NETCFG_RX_ON); - CSR_WRITE_4(sc, DM_RXSTART, 0xFFFFFFFF); - - mii_mediachg(mii); - - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - (void)splx(s); - - sc->dm_stat_ch = timeout(dm_tick, sc, hz); - - return; -} - -/* - * Set media options. - */ -static int dm_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct dm_softc *sc; - - sc = ifp->if_softc; - - if (ifp->if_flags & IFF_UP) - dm_init(sc); - - return(0); -} - -/* - * Report current media status. - */ -static void dm_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct dm_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; - - mii = device_get_softc(sc->dm_miibus); - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - - return; -} - -static int dm_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct dm_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *) data; - struct mii_data *mii; - int s, error = 0; - - s = splimp(); - - switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - dm_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - dm_stop(sc); - } - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - dm_init(sc); - error = 0; - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - mii = device_get_softc(sc->dm_miibus); - error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); - break; - default: - error = EINVAL; - break; - } - - (void)splx(s); - - return(error); -} - -static void dm_watchdog(ifp) - struct ifnet *ifp; -{ - struct dm_softc *sc; - - sc = ifp->if_softc; - - ifp->if_oerrors++; - printf("dm%d: watchdog timeout\n", sc->dm_unit); - - dm_stop(sc); - dm_reset(sc); - dm_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - dm_start(ifp); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void dm_stop(sc) - struct dm_softc *sc; -{ - register int i; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - untimeout(dm_tick, sc, sc->dm_stat_ch); - - DM_CLRBIT(sc, DM_NETCFG, (DM_NETCFG_RX_ON|DM_NETCFG_TX_ON)); - CSR_WRITE_4(sc, DM_IMR, 0x00000000); - CSR_WRITE_4(sc, DM_TXADDR, 0x00000000); - CSR_WRITE_4(sc, DM_RXADDR, 0x00000000); - - /* - * Free data in the RX lists. - */ - for (i = 0; i < DM_RX_LIST_CNT; i++) { - if (sc->dm_ldata->dm_rx_list[i].dm_mbuf != NULL) { - m_freem(sc->dm_ldata->dm_rx_list[i].dm_mbuf); - sc->dm_ldata->dm_rx_list[i].dm_mbuf = NULL; - } - } - bzero((char *)&sc->dm_ldata->dm_rx_list, - sizeof(sc->dm_ldata->dm_rx_list)); - - /* - * Free the TX list buffers. - */ - for (i = 0; i < DM_TX_LIST_CNT; i++) { - if (sc->dm_ldata->dm_tx_list[i].dm_mbuf != NULL) { - m_freem(sc->dm_ldata->dm_tx_list[i].dm_mbuf); - sc->dm_ldata->dm_tx_list[i].dm_mbuf = NULL; - } - } - - bzero((char *)&sc->dm_ldata->dm_tx_list, - sizeof(sc->dm_ldata->dm_tx_list)); - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - return; -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static void dm_shutdown(dev) - device_t dev; -{ - struct dm_softc *sc; - - sc = device_get_softc(dev); - - dm_reset(sc); - dm_stop(sc); - - return; -} diff --git a/sys/pci/if_dmreg.h b/sys/pci/if_dmreg.h deleted file mode 100644 index 2d79ebb..0000000 --- a/sys/pci/if_dmreg.h +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * Davicom register definitions. - */ - -#define DM_BUSCTL 0x00 /* bus control */ -#define DM_TXSTART 0x08 /* tx start demand */ -#define DM_RXSTART 0x10 /* rx start demand */ -#define DM_RXADDR 0x18 /* rx descriptor list start addr */ -#define DM_TXADDR 0x20 /* tx descriptor list start addr */ -#define DM_ISR 0x28 /* interrupt status register */ -#define DM_NETCFG 0x30 /* network config register */ -#define DM_IMR 0x38 /* interrupt mask */ -#define DM_FRAMESDISCARDED 0x40 /* # of discarded frames */ -#define DM_SIO 0x48 /* MII and ROM/EEPROM access */ -#define DM_RESERVED 0x50 -#define DM_GENTIMER 0x58 /* general timer */ -#define DM_GENPORT 0x60 /* general purpose port */ - -/* - * Bus control bits. - */ -#define DM_BUSCTL_RESET 0x00000001 -#define DM_BUSCTL_ARBITRATION 0x00000002 -#define WB_BUSCTL_SKIPLEN 0x0000007C -#define DM_BUSCTL_BIGENDIAN 0x00000080 -#define DM_BUSCTL_BURSTLEN 0x00003F00 -#define DM_BUSCTL_CACHEALIGN 0x0000C000 -#define DM_BUSCTL_BUF_BIGENDIAN 0x00100000 -#define DM_BUSCTL_READMULTI 0x00200000 - -#define DM_SKIPLEN_1LONG 0x00000004 -#define DM_SKIPLEN_2LONG 0x00000008 -#define DM_SKIPLEN_3LONG 0x00000010 -#define DM_SKIPLEN_4LONG 0x00000020 -#define DM_SKIPLEN_5LONG 0x00000040 - -#define DM_CACHEALIGN_NONE 0x00000000 -#define DM_CACHEALIGN_8LONG 0x00004000 -#define DM_CACHEALIGN_16LONG 0x00008000 -#define DM_CACHEALIGN_32LONG 0x0000C000 - -#define DM_BURSTLEN_UNLIMIT 0x00000000 -#define DM_BURSTLEN_1LONG 0x00000100 -#define DM_BURSTLEN_2LONG 0x00000200 -#define DM_BURSTLEN_4LONG 0x00000400 -#define DM_BURSTLEN_8LONG 0x00000800 -#define DM_BURSTLEN_16LONG 0x00001000 -#define DM_BURSTLEN_32LONG 0x00002000 - -/* - * Interrupt status bits. - */ -#define DM_ISR_TX_OK 0x00000001 -#define DM_ISR_TX_IDLE 0x00000002 -#define DM_ISR_TX_NOBUF 0x00000004 -#define DM_ISR_TX_JABBERTIMEO 0x00000008 -#define DM_ISR_TX_UNDERRUN 0x00000020 -#define DM_ISR_RX_OK 0x00000040 -#define DM_ISR_RX_NOBUF 0x00000080 -#define DM_ISR_RX_IDLE 0x00000100 -#define DM_ISR_RX_WATDOGTIMEO 0x00000200 -#define DM_ISR_TX_EARLY 0x00000400 -#define DM_ISR_TIMER_EXPIRED 0x00000800 -#define DM_ISR_BUS_ERR 0x00002000 -#define DM_ISR_ABNORMAL 0x00008000 -#define DM_ISR_NORMAL 0x00010000 -#define DM_ISR_RX_STATE 0x000E0000 -#define DM_ISR_TX_STATE 0x00700000 -#define DM_ISR_BUSERRTYPE 0x03800000 - -#define DM_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ -#define DM_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ -#define DM_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ -#define DM_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ -#define DM_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ -#define DM_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ -#define DM_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ -#define DM_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ - -#define DM_TXSTATE_RESET 0x00000000 /* 000 - reset */ -#define DM_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ -#define DM_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ -#define DM_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ -#define DM_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ -#define DM_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ -#define DM_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ -#define DM_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ - -/* - * Network config bits. - */ -#define DM_NETCFG_LINKSTAT_PCS 0x00000001 -#define DM_NETCFG_RX_ON 0x00000002 -#define DM_NETCFG_RX_BADFRAMES 0x00000008 -#define DM_NETCFG_RX_PROMISC 0x00000040 -#define DM_NETCFG_RX_ALLMULTI 0x00000080 -#define DM_NETCFG_RX_BROAD 0x00000100 -#define DM_NETCFG_FULLDUPLEX 0x00000200 -#define DM_NETCFG_LOOPBACK 0x00000C00 -#define DM_NETCFG_FORCECOLL 0x00001000 -#define DM_NETCFG_TX_ON 0x00002000 -#define DM_NETCFG_TX_THRESH 0x0000C000 -#define DM_NETCFG_PORTSEL 0x00040000 /* 0 == SRL, 1 == MII/SYM */ -#define DM_NETCFG_HEARTBEAT 0x00080000 /* 0 == ON, 1 == OFF */ -#define DM_NETCFG_STORENFWD 0x00200000 -#define DM_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */ -#define DM_NETCFG_PCS 0x00800000 -#define DM_NETCFG_SCRAMBLER 0x01000000 -#define DM_NETCFG_RX_ALL 0x40000000 - -#define DM_OPMODE_NORM 0x00000000 -#define DM_OPMODE_INTLOOP 0x00000400 -#define DM_OPMODE_EXTLOOP 0x00000800 - -#define DM_TXTHRESH_72BYTES 0x00000000 -#define DM_TXTHRESH_96BYTES 0x00004000 -#define DM_TXTHRESH_128BYTES 0x00008000 -#define DM_TXTHRESH_160BYTES 0x0000C000 - -/* - * Interrupt mask bits. - */ -#define DM_IMR_TX_OK 0x00000001 -#define DM_IMR_TX_IDLE 0x00000002 -#define DM_IMR_TX_NOBUF 0x00000004 -#define DM_IMR_TX_JABBERTIMEO 0x00000008 -#define DM_IMR_TX_UNDERRUN 0x00000020 -#define DM_IMR_RX_OK 0x00000040 -#define DM_IMR_RX_NOBUF 0x00000080 -#define DM_IMR_RX_IDLE 0x00000100 -#define DM_IMR_RX_WATDOGTIMEO 0x00000200 -#define DM_IMR_TX_EARLY 0x00000400 -#define DM_IMR_TIMER_EXPIRED 0x00000800 -#define DM_IMR_BUS_ERR 0x00002000 -#define DM_IMR_RX_EARLY 0x00004000 -#define DM_IMR_ABNORMAL 0x00008000 -#define DM_IMR_NORMAL 0x00010000 - -#define DM_INTRS \ - (DM_IMR_RX_OK|DM_IMR_TX_OK|DM_IMR_RX_NOBUF|DM_IMR_RX_WATDOGTIMEO|\ - DM_IMR_TX_NOBUF|DM_IMR_TX_UNDERRUN|DM_IMR_BUS_ERR| \ - DM_IMR_ABNORMAL|DM_IMR_NORMAL|/*DM_IMR_TX_EARLY*/ \ - DM_IMR_TX_IDLE|DM_IMR_RX_IDLE) - -/* - * Serial I/O (EEPROM/ROM) bits. - */ -#define DM_SIO_EE_CS 0x00000001 /* EEPROM chip select */ -#define DM_SIO_EE_CLK 0x00000002 /* EEPROM clock */ -#define DM_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ -#define DM_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ -#define DM_SIO_EESEL 0x00000800 -#define DM_SIO_ROMSEL 0x00001000 -#define DM_SIO_ROMCTL_WRITE 0x00002000 -#define DM_SIO_ROMCTL_READ 0x00004000 -#define DM_SIO_MII_CLK 0x00010000 /* MDIO clock */ -#define DM_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ -#define DM_SIO_MII_DIR 0x00040000 /* MDIO dir */ -#define DM_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ - -#define DM_EECMD_WRITE 0x140 -#define DM_EECMD_READ 0x180 -#define DM_EECMD_ERASE 0x1c0 - -#define DM_EE_NODEADDR_OFFSET 0x70 -#define DM_EE_NODEADDR 10 - -/* - * General purpose timer register - */ -#define DM_TIMER_VALUE 0x0000FFFF -#define DM_TIMER_CONTINUOUS 0x00010000 - -/* - * Size of a setup frame. - */ -#define DM_SFRAME_LEN 192 - -/* - * Davicom TX/RX list structure. - */ - -struct dm_desc { - u_int32_t dm_status; - u_int32_t dm_ctl; - u_int32_t dm_ptr1; - u_int32_t dm_ptr2; - struct mbuf *dm_mbuf; - struct dm_desc *dm_nextdesc; -}; - -#define dm_data dm_ptr1 -#define dm_next dm_ptr2 - -#define DM_RXSTAT_FIFOOFLOW 0x00000001 -#define DM_RXSTAT_CRCERR 0x00000002 -#define DM_RXSTAT_DRIBBLE 0x00000004 -#define DM_RXSTAT_WATCHDOG 0x00000010 -#define DM_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ -#define DM_RXSTAT_COLLSEEN 0x00000040 -#define DM_RXSTAT_GIANT 0x00000080 -#define DM_RXSTAT_LASTFRAG 0x00000100 -#define DM_RXSTAT_FIRSTFRAG 0x00000200 -#define DM_RXSTAT_MULTICAST 0x00000400 -#define DM_RXSTAT_RUNT 0x00000800 -#define DM_RXSTAT_RXTYPE 0x00003000 -#define DM_RXSTAT_RXERR 0x00008000 -#define DM_RXSTAT_RXLEN 0x3FFF0000 -#define DM_RXSTAT_OWN 0x80000000 - -#define DM_RXBYTES(x) ((x & DM_RXSTAT_RXLEN) >> 16) -#define DM_RXSTAT (DM_RXSTAT_FIRSTFRAG|DM_RXSTAT_LASTFRAG|DM_RXSTAT_OWN) - -#define DM_RXCTL_BUFLEN1 0x00000FFF -#define DM_RXCTL_BUFLEN2 0x00FFF000 -#define DM_RXCTL_RLINK 0x01000000 -#define DM_RXCTL_RLAST 0x02000000 - -#define DM_TXSTAT_DEFER 0x00000001 -#define DM_TXSTAT_UNDERRUN 0x00000002 -#define DM_TXSTAT_LINKFAIL 0x00000003 -#define DM_TXSTAT_COLLCNT 0x00000078 -#define DM_TXSTAT_SQE 0x00000080 -#define DM_TXSTAT_EXCESSCOLL 0x00000100 -#define DM_TXSTAT_LATECOLL 0x00000200 -#define DM_TXSTAT_NOCARRIER 0x00000400 -#define DM_TXSTAT_CARRLOST 0x00000800 -#define DM_TXSTAT_JABTIMEO 0x00004000 -#define DM_TXSTAT_ERRSUM 0x00008000 -#define DM_TXSTAT_OWN 0x80000000 - -#define DM_TXCTL_BUFLEN1 0x000007FF -#define DM_TXCTL_BUFLEN2 0x003FF800 -#define DM_TXCTL_FILTTYPE0 0x00400000 -#define DM_TXCTL_PAD 0x00800000 -#define DM_TXCTL_TLINK 0x01000000 -#define DM_TXCTL_TLAST 0x02000000 -#define DM_TXCTL_NOCRC 0x04000000 -#define DM_TXCTL_SETUP 0x08000000 -#define DM_TXCTL_FILTTYPE1 0x10000000 -#define DM_TXCTL_FIRSTFRAG 0x20000000 -#define DM_TXCTL_LASTFRAG 0x40000000 -#define DM_TXCTL_FINT 0x80000000 - -#define DM_FILTER_PERFECT 0x00000000 -#define DM_FILTER_HASHPERF 0x00400000 -#define DM_FILTER_INVERSE 0x10000000 -#define DM_FILTER_HASHONLY 0x10400000 - -#define DM_MAXFRAGS 16 -#define DM_RX_LIST_CNT 64 -#define DM_TX_LIST_CNT 128 -#define DM_MIN_FRAMELEN 60 -#define DM_RXLEN 1536 - -#define DM_INC(x, y) (x) = (x + 1) % y - -struct dm_list_data { - struct dm_desc dm_rx_list[DM_RX_LIST_CNT]; - struct dm_desc dm_tx_list[DM_TX_LIST_CNT]; - struct dm_desc dm_sframe; -}; - -struct dm_chain_data { - u_int32_t dm_sbuf[DM_SFRAME_LEN/sizeof(u_int32_t)]; - u_int8_t dm_pad[DM_MIN_FRAMELEN]; - int dm_tx_prod; - int dm_tx_cons; - int dm_tx_cnt; - int dm_rx_prod; -}; - -struct dm_type { - u_int16_t dm_vid; - u_int16_t dm_did; - char *dm_name; -}; - -struct dm_mii_frame { - u_int8_t mii_stdelim; - u_int8_t mii_opcode; - u_int8_t mii_phyaddr; - u_int8_t mii_regaddr; - u_int8_t mii_turnaround; - u_int16_t mii_data; -}; - -/* - * MII constants - */ -#define DM_MII_STARTDELIM 0x01 -#define DM_MII_READOP 0x02 -#define DM_MII_WRITEOP 0x01 -#define DM_MII_TURNAROUND 0x02 - -struct dm_softc { - struct arpcom arpcom; /* interface info */ - bus_space_handle_t dm_bhandle; /* bus space handle */ - bus_space_tag_t dm_btag; /* bus space tag */ - void *dm_intrhand; - struct resource *dm_irq; - struct resource *dm_res; - device_t dm_miibus; - u_int8_t dm_unit; /* interface number */ - int dm_cachesize; - struct dm_list_data *dm_ldata; - struct dm_chain_data dm_cdata; - struct callout_handle dm_stat_ch; -}; - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->dm_btag, sc->dm_bhandle, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->dm_btag, sc->dm_bbhandle, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->dm_btag, sc->dm_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->dm_btag, sc->dm_bhandle, reg) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->dm_btag, sc->dm_bhandle, reg) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->dm_btag, sc->dm_bhandle, reg) - -#define DM_TIMEOUT 1000 -#define ETHER_ALIGN 2 - -/* - * General constants that are fun to know. - * - * Davicom PCI vendor ID - */ -#define DM_VENDORID 0x1282 - -/* - * Davicom DM9102 device ID. - */ -#define DM_DEVICEID_DM9102 0x9102 -#define DM_DEVICEID_DM9100 0x9100 - -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. - */ - -#define DM_PCI_VENDOR_ID 0x00 -#define DM_PCI_DEVICE_ID 0x02 -#define DM_PCI_COMMAND 0x04 -#define DM_PCI_STATUS 0x06 -#define DM_PCI_REVID 0x08 -#define DM_PCI_CLASSCODE 0x09 -#define DM_PCI_CACHELEN 0x0C -#define DM_PCI_LATENCY_TIMER 0x0D -#define DM_PCI_HEADER_TYPE 0x0E -#define DM_PCI_LOIO 0x10 -#define DM_PCI_LOMEM 0x14 -#define DM_PCI_BIOSROM 0x30 -#define DM_PCI_INTLINE 0x3C -#define DM_PCI_INTPIN 0x3D -#define DM_PCI_MINGNT 0x3E -#define DM_PCI_MINLAT 0x0F -#define DM_PCI_RESETOPT 0x48 -#define DM_PCI_EEPROM_DATA 0x4C - -/* power management registers */ -#define DM_PCI_CAPID 0x50 /* 8 bits */ -#define DM_PCI_NEXTPTR 0x51 /* 8 bits */ -#define DM_PCI_PWRMGMTCAP 0x52 /* 16 bits */ -#define DM_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ - -#define DM_PSTATE_MASK 0x0003 -#define DM_PSTATE_D0 0x0000 -#define DM_PSTATE_D1 0x0001 -#define DM_PSTATE_D2 0x0002 -#define DM_PSTATE_D3 0x0003 -#define DM_PME_EN 0x0010 -#define DM_PME_STATUS 0x8000 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif diff --git a/sys/pci/if_mx.c b/sys/pci/if_mx.c deleted file mode 100644 index b1fe66f..0000000 --- a/sys/pci/if_mx.c +++ /dev/null @@ -1,1992 +0,0 @@ -/* - * Copyright (c) 1997, 1998, 1999 - * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * Macronix PMAC fast ethernet PCI NIC driver - * - * Written by Bill Paul <wpaul@ee.columbia.edu> - * Electrical Engineering Department - * Columbia University, New York City - */ - -/* - * The Macronix 98713, 98715 and 98725 chips are still more tulip clones. - * The 98713 has an internal transceiver and an MII bus for external PHYs. - * The other two chips have only the internal transceiver. All have - * support for built-in autonegotiation. Additionally, there are 98713A - * and 98715A chips which support power management. The 98725 chip - * supports power management as well. - * - * Datasheets for the Macronix parts can be obtained from www.macronix.com. - * Note however that the datasheets do not describe the TX and RX - * descriptor structures or the setup frame format(s). For this, you should - * obtain a DEC 21x4x datasheet from developer.intel.com. The Macronix - * chips look to be fairly straightforward tulip clones, except for - * the NWAY support. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <net/ethernet.h> -#include <net/if_dl.h> -#include <net/if_media.h> - -#include <net/bpf.h> - -#include <vm/vm.h> /* for vtophys */ -#include <vm/pmap.h> /* for vtophys */ -#include <machine/clock.h> /* for DELAY */ -#include <machine/bus_pio.h> -#include <machine/bus_memio.h> -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/bus.h> -#include <sys/rman.h> - -#include <dev/mii/mii.h> -#include <dev/mii/miivar.h> - -#include <pci/pcireg.h> -#include <pci/pcivar.h> - -#define MX_USEIOSPACE - -#include <pci/if_mxreg.h> - -/* "controller miibus0" required. See GENERIC if you get errors here. */ -#include "miibus_if.h" - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - -/* - * Various supported device vendors/types and their names. - */ -static struct mx_type mx_devs[] = { - { MX_VENDORID, MX_DEVICEID_98713, - "Macronix 98713 10/100BaseTX" }, - { MX_VENDORID, MX_DEVICEID_98713, - "Macronix 98713A 10/100BaseTX" }, - { CP_VENDORID, CP_DEVICEID_98713, - "Compex RL100-TX 10/100BaseTX" }, - { CP_VENDORID, CP_DEVICEID_98713, - "Compex RL100-TX 10/100BaseTX" }, - { MX_VENDORID, MX_DEVICEID_987x5, - "Macronix 98715/98715A 10/100BaseTX" }, - { MX_VENDORID, MX_DEVICEID_987x5, - "Macronix 98725 10/100BaseTX" }, - { PN_VENDORID, PN_DEVICEID_PNIC_II, - "LC82C115 PNIC II 10/100BaseTX" }, - { 0, 0, NULL } -}; - -static int mx_probe __P((device_t)); -static int mx_attach __P((device_t)); -static int mx_detach __P((device_t)); -static struct mx_type *mx_devtype __P((device_t)); -static int mx_newbuf __P((struct mx_softc *, - struct mx_chain_onefrag *, - struct mbuf *)); -static int mx_encap __P((struct mx_softc *, struct mx_chain *, - struct mbuf *)); - -static void mx_rxeof __P((struct mx_softc *)); -static void mx_rxeoc __P((struct mx_softc *)); -static void mx_txeof __P((struct mx_softc *)); -static void mx_txeoc __P((struct mx_softc *)); -static void mx_tick __P((void *)); -static void mx_intr __P((void *)); -static void mx_start __P((struct ifnet *)); -static int mx_ioctl __P((struct ifnet *, u_long, caddr_t)); -static void mx_init __P((void *)); -static void mx_stop __P((struct mx_softc *)); -static void mx_watchdog __P((struct ifnet *)); -static void mx_shutdown __P((device_t)); -static int mx_ifmedia_upd __P((struct ifnet *)); -static void mx_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); - -static void mx_delay __P((struct mx_softc *)); -static void mx_eeprom_idle __P((struct mx_softc *)); -static void mx_eeprom_putbyte __P((struct mx_softc *, int)); -static void mx_eeprom_getword __P((struct mx_softc *, int, u_int16_t *)); -static void mx_read_eeprom __P((struct mx_softc *, caddr_t, int, - int, int)); - -static void mx_mii_writebit __P((struct mx_softc *, int)); -static int mx_mii_readbit __P((struct mx_softc *)); -static void mx_mii_sync __P((struct mx_softc *)); -static void mx_mii_send __P((struct mx_softc *, u_int32_t, int)); -static int mx_mii_readreg __P((struct mx_softc *, struct mx_mii_frame *)); -static int mx_mii_writereg __P((struct mx_softc *, struct mx_mii_frame *)); - -static int mx_miibus_readreg __P((device_t, int, int)); -static int mx_miibus_writereg __P((device_t, int, int, int)); -static void mx_miibus_statchg __P((device_t)); - -static void mx_setcfg __P((struct mx_softc *, int)); -static u_int32_t mx_calchash __P((struct mx_softc *, caddr_t)); -static void mx_setfilt __P((struct mx_softc *)); -static void mx_reset __P((struct mx_softc *)); -static int mx_list_rx_init __P((struct mx_softc *)); -static int mx_list_tx_init __P((struct mx_softc *)); - -#ifdef MX_USEIOSPACE -#define MX_RES SYS_RES_IOPORT -#define MX_RID MX_PCI_LOIO -#else -#define MX_RES SYS_RES_MEMORY -#define MX_RID MX_PCI_LOMEM -#endif - -static device_method_t mx_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, mx_probe), - DEVMETHOD(device_attach, mx_attach), - DEVMETHOD(device_detach, mx_detach), - DEVMETHOD(device_shutdown, mx_shutdown), - - /* bus interface */ - DEVMETHOD(bus_print_child, bus_generic_print_child), - DEVMETHOD(bus_driver_added, bus_generic_driver_added), - - /* MII interface */ - DEVMETHOD(miibus_readreg, mx_miibus_readreg), - DEVMETHOD(miibus_writereg, mx_miibus_writereg), - DEVMETHOD(miibus_statchg, mx_miibus_statchg), - - { 0, 0 } -}; - -static driver_t mx_driver = { - "mx", - mx_methods, - sizeof(struct mx_softc) -}; - -static devclass_t mx_devclass; - -DRIVER_MODULE(if_mx, pci, mx_driver, mx_devclass, 0, 0); -DRIVER_MODULE(miibus, mx, miibus_driver, miibus_devclass, 0, 0); - -#define MX_SETBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) | x) - -#define MX_CLRBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) & ~x) - -#define SIO_SET(x) \ - CSR_WRITE_4(sc, MX_SIO, \ - CSR_READ_4(sc, MX_SIO) | x) - -#define SIO_CLR(x) \ - CSR_WRITE_4(sc, MX_SIO, \ - CSR_READ_4(sc, MX_SIO) & ~x) - -static void mx_delay(sc) - struct mx_softc *sc; -{ - int idx; - - for (idx = (300 / 33) + 1; idx > 0; idx--) - CSR_READ_4(sc, MX_BUSCTL); -} - -static void mx_eeprom_idle(sc) - struct mx_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, MX_SIO, MX_SIO_EESEL); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_ROMCTL_READ); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_EE_CS); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_EE_CLK); - mx_delay(sc); - - for (i = 0; i < 25; i++) { - MX_CLRBIT(sc, MX_SIO, MX_SIO_EE_CLK); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_EE_CLK); - mx_delay(sc); - } - - MX_CLRBIT(sc, MX_SIO, MX_SIO_EE_CLK); - mx_delay(sc); - MX_CLRBIT(sc, MX_SIO, MX_SIO_EE_CS); - mx_delay(sc); - CSR_WRITE_4(sc, MX_SIO, 0x00000000); - - return; -} - -/* - * Send a read command and address to the EEPROM, check for ACK. - */ -static void mx_eeprom_putbyte(sc, addr) - struct mx_softc *sc; - int addr; -{ - register int d, i; - - d = addr | MX_EECMD_READ; - - /* - * Feed in each bit and stobe the clock. - */ - for (i = 0x400; i; i >>= 1) { - if (d & i) { - SIO_SET(MX_SIO_EE_DATAIN); - } else { - SIO_CLR(MX_SIO_EE_DATAIN); - } - mx_delay(sc); - SIO_SET(MX_SIO_EE_CLK); - mx_delay(sc); - SIO_CLR(MX_SIO_EE_CLK); - mx_delay(sc); - } - - return; -} - -/* - * Read a word of data stored in the EEPROM at address 'addr.' - */ -static void mx_eeprom_getword(sc, addr, dest) - struct mx_softc *sc; - int addr; - u_int16_t *dest; -{ - register int i; - u_int16_t word = 0; - - /* Force EEPROM to idle state. */ - mx_eeprom_idle(sc); - - /* Enter EEPROM access mode. */ - CSR_WRITE_4(sc, MX_SIO, MX_SIO_EESEL); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_ROMCTL_READ); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_EE_CS); - mx_delay(sc); - MX_SETBIT(sc, MX_SIO, MX_SIO_EE_CLK); - mx_delay(sc); - - /* - * Send address of word we want to read. - */ - mx_eeprom_putbyte(sc, addr); - - /* - * Start reading bits from EEPROM. - */ - for (i = 0x8000; i; i >>= 1) { - SIO_SET(MX_SIO_EE_CLK); - mx_delay(sc); - if (CSR_READ_4(sc, MX_SIO) & MX_SIO_EE_DATAOUT) - word |= i; - mx_delay(sc); - SIO_CLR(MX_SIO_EE_CLK); - mx_delay(sc); - } - - /* Turn off EEPROM access mode. */ - mx_eeprom_idle(sc); - - *dest = word; - - return; -} - -/* - * Read a sequence of words from the EEPROM. - */ -static void mx_read_eeprom(sc, dest, off, cnt, swap) - struct mx_softc *sc; - caddr_t dest; - int off; - int cnt; - int swap; -{ - int i; - u_int16_t word = 0, *ptr; - - for (i = 0; i < cnt; i++) { - mx_eeprom_getword(sc, off + i, &word); - ptr = (u_int16_t *)(dest + (i * 2)); - if (swap) - *ptr = ntohs(word); - else - *ptr = word; - } - - return; -} - -/* - * The following two routines are taken from the Macronix 98713 - * Application Notes pp.19-21. - */ -/* - * Write a bit to the MII bus. - */ -static void mx_mii_writebit(sc, bit) - struct mx_softc *sc; - int bit; -{ - if (bit) - CSR_WRITE_4(sc, MX_SIO, MX_SIO_ROMCTL_WRITE|MX_SIO_MII_DATAOUT); - else - CSR_WRITE_4(sc, MX_SIO, MX_SIO_ROMCTL_WRITE); - - MX_SETBIT(sc, MX_SIO, MX_SIO_MII_CLK); - MX_CLRBIT(sc, MX_SIO, MX_SIO_MII_CLK); - - return; -} - -/* - * Read a bit from the MII bus. - */ -static int mx_mii_readbit(sc) - struct mx_softc *sc; -{ - CSR_WRITE_4(sc, MX_SIO, MX_SIO_ROMCTL_READ|MX_SIO_MII_DIR); - CSR_READ_4(sc, MX_SIO); - MX_SETBIT(sc, MX_SIO, MX_SIO_MII_CLK); - MX_CLRBIT(sc, MX_SIO, MX_SIO_MII_CLK); - if (CSR_READ_4(sc, MX_SIO) & MX_SIO_MII_DATAIN) - return(1); - - return(0); -} - -/* - * Sync the PHYs by setting data bit and strobing the clock 32 times. - */ -static void mx_mii_sync(sc) - struct mx_softc *sc; -{ - register int i; - - CSR_WRITE_4(sc, MX_SIO, MX_SIO_ROMCTL_WRITE); - - for (i = 0; i < 32; i++) - mx_mii_writebit(sc, 1); - - return; -} - -/* - * Clock a series of bits through the MII. - */ -static void mx_mii_send(sc, bits, cnt) - struct mx_softc *sc; - u_int32_t bits; - int cnt; -{ - int i; - - for (i = (0x1 << (cnt - 1)); i; i >>= 1) - mx_mii_writebit(sc, bits & i); -} - -/* - * Read an PHY register through the MII. - */ -static int mx_mii_readreg(sc, frame) - struct mx_softc *sc; - struct mx_mii_frame *frame; - -{ - int i, ack, s; - - s = splimp(); - - /* - * Set up frame for RX. - */ - frame->mii_stdelim = MX_MII_STARTDELIM; - frame->mii_opcode = MX_MII_READOP; - frame->mii_turnaround = 0; - frame->mii_data = 0; - - /* - * Sync the PHYs. - */ - mx_mii_sync(sc); - - /* - * Send command/address info. - */ - mx_mii_send(sc, frame->mii_stdelim, 2); - mx_mii_send(sc, frame->mii_opcode, 2); - mx_mii_send(sc, frame->mii_phyaddr, 5); - mx_mii_send(sc, frame->mii_regaddr, 5); - -#ifdef notdef - /* Idle bit */ - mx_mii_writebit(sc, 1); - mx_mii_writebit(sc, 0); -#endif - - /* Check for ack */ - ack = mx_mii_readbit(sc); - - /* - * Now try reading data bits. If the ack failed, we still - * need to clock through 16 cycles to keep the PHY(s) in sync. - */ - if (ack) { - for(i = 0; i < 16; i++) { - mx_mii_readbit(sc); - } - goto fail; - } - - for (i = 0x8000; i; i >>= 1) { - if (!ack) { - if (mx_mii_readbit(sc)) - frame->mii_data |= i; - } - } - -fail: - - mx_mii_writebit(sc, 0); - mx_mii_writebit(sc, 0); - - splx(s); - - if (ack) - return(1); - return(0); -} - -/* - * Write to a PHY register through the MII. - */ -static int mx_mii_writereg(sc, frame) - struct mx_softc *sc; - struct mx_mii_frame *frame; - -{ - int s; - - s = splimp(); - /* - * Set up frame for TX. - */ - - frame->mii_stdelim = MX_MII_STARTDELIM; - frame->mii_opcode = MX_MII_WRITEOP; - frame->mii_turnaround = MX_MII_TURNAROUND; - - /* - * Sync the PHYs. - */ - mx_mii_sync(sc); - - mx_mii_send(sc, frame->mii_stdelim, 2); - mx_mii_send(sc, frame->mii_opcode, 2); - mx_mii_send(sc, frame->mii_phyaddr, 5); - mx_mii_send(sc, frame->mii_regaddr, 5); - mx_mii_send(sc, frame->mii_turnaround, 2); - mx_mii_send(sc, frame->mii_data, 16); - - /* Idle bit. */ - mx_mii_writebit(sc, 0); - mx_mii_writebit(sc, 0); - - splx(s); - - return(0); -} - -static int mx_miibus_readreg(dev, phy, reg) - device_t dev; - int phy, reg; -{ - struct mx_softc *sc; - struct mx_mii_frame frame; - - sc = device_get_softc(dev); - bzero((char *)&frame, sizeof(frame)); - - if (sc->mx_type != MX_TYPE_98713) { - if (phy == (MII_NPHY - 1)) { - switch(reg) { - case MII_BMSR: - /* - * Fake something to make the probe - * code think there's a PHY here. - */ - return(BMSR_MEDIAMASK); - break; - case MII_PHYIDR1: - return(MX_VENDORID); - break; - case MII_PHYIDR2: - return(MX_DEVICEID_987x5); - break; - default: - return(0); - break; - } - } - } - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); - mx_mii_readreg(sc, &frame); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); - - return(frame.mii_data); -} - -static int mx_miibus_writereg(dev, phy, reg, data) - device_t dev; - int phy, reg, data; -{ - struct mx_mii_frame frame; - struct mx_softc *sc; - - sc = device_get_softc(dev); - bzero((char *)&frame, sizeof(frame)); - - frame.mii_phyaddr = phy; - frame.mii_regaddr = reg; - frame.mii_data = data; - - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); - mx_mii_writereg(sc, &frame); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); - - return(0); -} - -static void mx_miibus_statchg(dev) - device_t dev; -{ - struct mx_softc *sc; - struct mii_data *mii; - - sc = device_get_softc(dev); - mii = device_get_softc(sc->mx_miibus); - mx_setcfg(sc, mii->mii_media_active); - - return; -} - -#define MX_POLY 0xEDB88320 -#define MX_BITS 9 -#define MX_BITS_PNIC_II 7 - -static u_int32_t mx_calchash(sc, addr) - struct mx_softc *sc; - caddr_t addr; -{ - u_int32_t idx, bit, data, crc; - - /* Compute CRC for the address value. */ - crc = 0xFFFFFFFF; /* initial value */ - - for (idx = 0; idx < 6; idx++) { - for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) - crc = (crc >> 1) ^ (((crc ^ data) & 1) ? MX_POLY : 0); - } - - /* The hash table on the PNIC II is only 128 bits wide. */ - if (sc->mx_info->mx_vid == PN_VENDORID) - return (crc & ((1 << MX_BITS_PNIC_II) - 1)); - - return (crc & ((1 << MX_BITS) - 1)); -} - -/* - * Programming the receiver filter on the tulip/PMAC is gross. You - * have to construct a special setup frame and download it to the - * chip via the transmit DMA engine. This routine is also somewhat - * gross, as the setup frame is sent synchronously rather than putting - * on the transmit queue. The transmitter has to be stopped, then we - * can download the frame and wait for the 'owned' bit to clear. - * - * We always program the chip using 'hash perfect' mode, i.e. one perfect - * address (our node address) and a 512-bit hash filter for multicast - * frames. We also sneak the broadcast address into the hash filter since - * we need that too. - */ -void mx_setfilt(sc) - struct mx_softc *sc; -{ - struct mx_desc *sframe; - u_int32_t h, *sp; - struct ifmultiaddr *ifma; - struct ifnet *ifp; - int i; - - ifp = &sc->arpcom.ac_if; - - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON); - MX_SETBIT(sc, MX_ISR, MX_ISR_TX_IDLE); - - sframe = &sc->mx_cdata.mx_sframe; - sp = (u_int32_t *)&sc->mx_cdata.mx_sbuf; - bzero((char *)sp, MX_SFRAME_LEN); - - sframe->mx_next = vtophys(&sc->mx_ldata->mx_tx_list[0]); - sframe->mx_data = vtophys(&sc->mx_cdata.mx_sbuf); - sframe->mx_ctl = MX_SFRAME_LEN | MX_TXCTL_TLINK | - MX_TXCTL_SETUP | MX_FILTER_HASHPERF; - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC); - else - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC); - - if (ifp->if_flags & IFF_ALLMULTI) - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_ALLMULTI); - - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = mx_calchash(sc, - LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - sp[h >> 4] |= 1 << (h & 0xF); - } - - if (ifp->if_flags & IFF_BROADCAST) { - h = mx_calchash(sc, (caddr_t)ðerbroadcastaddr); - sp[h >> 4] |= 1 << (h & 0xF); - } - - sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; - sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; - sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; - - CSR_WRITE_4(sc, MX_TXADDR, vtophys(sframe)); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON); - sframe->mx_status = MX_TXSTAT_OWN; - CSR_WRITE_4(sc, MX_TXSTART, 0xFFFFFFFF); - - /* - * Wait for chip to clear the 'own' bit. - */ - for (i = 0; i < MX_TIMEOUT; i++) { - DELAY(10); - if (sframe->mx_status != MX_TXSTAT_OWN) - break; - } - - if (i == MX_TIMEOUT) - printf("mx%d: failed to send setup frame\n", sc->mx_unit); - - MX_SETBIT(sc, MX_ISR, MX_ISR_TX_NOBUF|MX_ISR_TX_IDLE); - - return; -} - -/* - * In order to fiddle with the - * 'full-duplex' and '100Mbps' bits in the netconfig register, we - * first have to put the transmit and/or receive logic in the idle state. - */ -static void mx_setcfg(sc, media) - struct mx_softc *sc; - int media; -{ - int i, restart = 0; - - if (CSR_READ_4(sc, MX_NETCFG) & (MX_NETCFG_TX_ON|MX_NETCFG_RX_ON)) { - restart = 1; - MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_TX_ON|MX_NETCFG_RX_ON)); - - for (i = 0; i < MX_TIMEOUT; i++) { - DELAY(10); - if (CSR_READ_4(sc, MX_ISR) & MX_ISR_TX_IDLE) - break; - } - - if (i == MX_TIMEOUT) - printf("mx%d: failed to force tx and " - "rx to idle state\n", sc->mx_unit); - - } - - if (IFM_SUBTYPE(media) == IFM_100_TX) { - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_HEARTBEAT); - if (sc->mx_type == MX_TYPE_98713) { - MX_SETBIT(sc, MX_WATCHDOG, MX_WDOG_JABBERDIS); - MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_PCS| - MX_NETCFG_PORTSEL|MX_NETCFG_SCRAMBLER)); - MX_SETBIT(sc, MX_NETCFG, (MX_NETCFG_PCS| - MX_NETCFG_SCRAMBLER)); - } else { - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL| - MX_NETCFG_PCS|MX_NETCFG_SCRAMBLER); - } - } - - if (IFM_SUBTYPE(media) == IFM_10_T) { - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL); - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_HEARTBEAT); - if (sc->mx_type == MX_TYPE_98713) { - MX_SETBIT(sc, MX_WATCHDOG, MX_WDOG_JABBERDIS); - MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_PCS| - MX_NETCFG_PORTSEL|MX_NETCFG_SCRAMBLER)); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PCS); - } else { - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_PORTSEL); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_PCS); - } - } - - if ((media & IFM_GMASK) == IFM_FDX) { - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); - } else { - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_FULLDUPLEX); - } - - if (restart) - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON|MX_NETCFG_RX_ON); - - return; -} - -static void mx_reset(sc) - struct mx_softc *sc; -{ - register int i; - - MX_SETBIT(sc, MX_BUSCTL, MX_BUSCTL_RESET); - - for (i = 0; i < MX_TIMEOUT; i++) { - DELAY(10); - if (!(CSR_READ_4(sc, MX_BUSCTL) & MX_BUSCTL_RESET)) - break; - } - if (i == MX_TIMEOUT) - printf("mx%d: reset never completed!\n", sc->mx_unit); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); - return; -} - -static struct mx_type *mx_devtype(dev) - device_t dev; -{ - struct mx_type *t; - u_int32_t rev; - - t = mx_devs; - - while(t->mx_name != NULL) { - if ((pci_get_vendor(dev) == t->mx_vid) && - (pci_get_device(dev) == t->mx_did)) { - /* Check the PCI revision */ - rev = pci_read_config(dev, MX_PCI_REVID, 4) & 0xFF; - if (t->mx_did == MX_DEVICEID_98713 && - rev >= MX_REVISION_98713A) - t++; - if (t->mx_did == CP_DEVICEID_98713 && - rev >= MX_REVISION_98713A) - t++; - if (t->mx_did == MX_DEVICEID_987x5 && - rev >= MX_REVISION_98725) - t++; - return(t); - } - t++; - } - - return(NULL); -} - -/* - * Probe for a Macronix PMAC chip. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - * We do a little bit of extra work to identify the exact type of - * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, - * but different revision IDs. The same is true for 98715/98715A - * chips and the 98725. This doesn't affect a whole lot, but it - * lets us tell the user exactly what type of device they have - * in the probe output. - */ -int mx_probe(dev) - device_t dev; -{ - struct mx_type *t; - - t = mx_devtype(dev); - - if (t != NULL) { - device_set_desc(dev, t->mx_name); - return(0); - } - - return(ENXIO); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -int mx_attach(dev) - device_t dev; -{ - int s, i; - u_char eaddr[ETHER_ADDR_LEN]; - u_int32_t command; - struct mx_softc *sc; - struct ifnet *ifp; - unsigned int round; - caddr_t roundptr; - u_int16_t mac_offset = 0; - u_int32_t revision, pci_id; - int unit, error = 0, rid; - - s = splimp(); - - sc = device_get_softc(dev); - unit = device_get_unit(dev); - bzero(sc, sizeof(struct mx_softc)); - - /* - * Handle power management nonsense. - */ - - command = pci_read_config(dev, MX_PCI_CAPID, 4) & 0x000000FF; - if (command == 0x01) { - - command = pci_read_config(dev, MX_PCI_PWRMGMTCTRL, 4); - if (command & MX_PSTATE_MASK) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, MX_PCI_LOIO, 4); - membase = pci_read_config(dev, MX_PCI_LOMEM, 4); - irq = pci_read_config(dev, MX_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("mx%d: chip is in D%d power mode " - "-- setting to D0\n", unit, command & MX_PSTATE_MASK); - command &= 0xFFFFFFFC; - pci_write_config(dev, MX_PCI_PWRMGMTCTRL, command, 4); - - /* Restore PCI config data. */ - pci_write_config(dev, MX_PCI_LOIO, iobase, 4); - pci_write_config(dev, MX_PCI_LOMEM, membase, 4); - pci_write_config(dev, MX_PCI_INTLINE, irq, 4); - } - } - - /* - * Map control/status registers. - */ - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - -#ifdef MX_USEIOSPACE - if (!(command & PCIM_CMD_PORTEN)) { - printf("mx%d: failed to enable I/O ports!\n", unit); - error = ENXIO; - goto fail; - } -#else - if (!(command & PCIM_CMD_MEMEN)) { - printf("mx%d: failed to enable memory mapping!\n", unit); - error = ENXIO; - goto fail; - } -#endif - - rid = MX_RID; - sc->mx_res = bus_alloc_resource(dev, MX_RES, &rid, - 0, ~0, 1, RF_ACTIVE); - - if (sc->mx_res == NULL) { - printf("mx%d: couldn't map ports/memory\n", unit); - error = ENXIO; - goto fail; - } - - sc->mx_btag = rman_get_bustag(sc->mx_res); - sc->mx_bhandle = rman_get_bushandle(sc->mx_res); - - /* Allocate interrupt */ - rid = 0; - sc->mx_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, - RF_SHAREABLE | RF_ACTIVE); - - if (sc->mx_irq == NULL) { - printf("mx%d: couldn't map interrupt\n", unit); - bus_release_resource(dev, MX_RES, MX_RID, sc->mx_res); - error = ENXIO; - goto fail; - } - - error = bus_setup_intr(dev, sc->mx_irq, INTR_TYPE_NET, - mx_intr, sc, &sc->mx_intrhand); - - if (error) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->mx_irq); - bus_release_resource(dev, MX_RES, MX_RID, sc->mx_res); - printf("mx%d: couldn't set up irq\n", unit); - goto fail; - } - - /* Need this info to decide on a chip type. */ - revision = pci_read_config(dev, MX_PCI_REVID, 4) & 0x000000FF; - pci_id = (pci_read_config(dev,MX_PCI_VENDOR_ID, 4) >> 16) & 0x0000FFFF; - - if (pci_id == MX_DEVICEID_98713 && revision < MX_REVISION_98713A) - sc->mx_type = MX_TYPE_98713; - else if (pci_id == CP_DEVICEID_98713 && revision < MX_REVISION_98713A) - sc->mx_type = MX_TYPE_98713; - else if (pci_id == MX_DEVICEID_98713 && revision >= MX_REVISION_98713A) - sc->mx_type = MX_TYPE_98713A; - else - sc->mx_type = MX_TYPE_987x5; - - /* Save the cache line size. */ - sc->mx_cachesize = pci_read_config(dev, MX_PCI_CACHELEN, 4) & 0xFF; - - /* Save the device info; the PNIC II requires special handling. */ - pci_id = pci_read_config(dev,MX_PCI_VENDOR_ID, 4); - sc->mx_info = mx_devtype(dev); - - /* Reset the adapter. */ - mx_reset(sc); - - /* - * Get station address from the EEPROM. - */ - mx_read_eeprom(sc, (caddr_t)&mac_offset, - (MX_EE_NODEADDR_OFFSET / 2), 1, 0); - mx_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0); - - /* - * A PMAC chip was detected. Inform the world. - */ - printf("mx%d: Ethernet address: %6D\n", unit, eaddr, ":"); - - sc->mx_unit = unit; - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - - sc->mx_ldata_ptr = malloc(sizeof(struct mx_list_data) + 8, - M_DEVBUF, M_NOWAIT); - if (sc->mx_ldata_ptr == NULL) { - printf("mx%d: no memory for list buffers!\n", unit); - bus_teardown_intr(dev, sc->mx_irq, sc->mx_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->mx_irq); - bus_release_resource(dev, MX_RES, MX_RID, sc->mx_res); - error = ENXIO; - goto fail; - } - - sc->mx_ldata = (struct mx_list_data *)sc->mx_ldata_ptr; - round = (uintptr_t)sc->mx_ldata_ptr & 0xF; - roundptr = sc->mx_ldata_ptr; - for (i = 0; i < 8; i++) { - if (round % 8) { - round++; - roundptr++; - } - break; - } - sc->mx_ldata = (struct mx_list_data *)roundptr; - bzero(sc->mx_ldata, sizeof(struct mx_list_data)); - - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = "mx"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = mx_ioctl; - ifp->if_output = ether_output; - ifp->if_start = mx_start; - ifp->if_watchdog = mx_watchdog; - ifp->if_init = mx_init; - ifp->if_baudrate = 10000000; - ifp->if_snd.ifq_maxlen = MX_TX_LIST_CNT - 1; - - /* - * Do ifmedia setup. - */ - - if (mii_phy_probe(dev, &sc->mx_miibus, - mx_ifmedia_upd, mx_ifmedia_sts)) { - printf("mx%d: MII without any PHY!\n", sc->mx_unit); - bus_teardown_intr(dev, sc->mx_irq, sc->mx_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->mx_irq); - bus_release_resource(dev, MX_RES, MX_RID, sc->mx_res); - error = ENXIO; - goto fail; - } - - /* - * Call MI attach routines. - */ - if_attach(ifp); - ether_ifattach(ifp); - callout_handle_init(&sc->mx_stat_ch); - - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - -fail: - splx(s); - - return(error); -} - -static int mx_detach(dev) - device_t dev; -{ - struct mx_softc *sc; - struct ifnet *ifp; - int s; - - s = splimp(); - - sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - - mx_stop(sc); - if_detach(ifp); - - bus_generic_detach(dev); - device_delete_child(dev, sc->mx_miibus); - - bus_teardown_intr(dev, sc->mx_irq, sc->mx_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->mx_irq); - bus_release_resource(dev, MX_RES, MX_RID, sc->mx_res); - - free(sc->mx_ldata_ptr, M_DEVBUF); - - splx(s); - - return(0); -} - -/* - * Initialize the transmit descriptors. - */ -static int mx_list_tx_init(sc) - struct mx_softc *sc; -{ - struct mx_chain_data *cd; - struct mx_list_data *ld; - int i; - - cd = &sc->mx_cdata; - ld = sc->mx_ldata; - for (i = 0; i < MX_TX_LIST_CNT; i++) { - cd->mx_tx_chain[i].mx_ptr = &ld->mx_tx_list[i]; - if (i == (MX_TX_LIST_CNT - 1)) - cd->mx_tx_chain[i].mx_nextdesc = - &cd->mx_tx_chain[0]; - else - cd->mx_tx_chain[i].mx_nextdesc = - &cd->mx_tx_chain[i + 1]; - } - - cd->mx_tx_free = &cd->mx_tx_chain[0]; - cd->mx_tx_tail = cd->mx_tx_head = NULL; - - return(0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int mx_list_rx_init(sc) - struct mx_softc *sc; -{ - struct mx_chain_data *cd; - struct mx_list_data *ld; - int i; - - cd = &sc->mx_cdata; - ld = sc->mx_ldata; - - for (i = 0; i < MX_RX_LIST_CNT; i++) { - cd->mx_rx_chain[i].mx_ptr = - (struct mx_desc *)&ld->mx_rx_list[i]; - if (mx_newbuf(sc, &cd->mx_rx_chain[i], NULL) == ENOBUFS) - return(ENOBUFS); - if (i == (MX_RX_LIST_CNT - 1)) { - cd->mx_rx_chain[i].mx_nextdesc = - &cd->mx_rx_chain[0]; - ld->mx_rx_list[i].mx_next = - vtophys(&ld->mx_rx_list[0]); - } else { - cd->mx_rx_chain[i].mx_nextdesc = - &cd->mx_rx_chain[i + 1]; - ld->mx_rx_list[i].mx_next = - vtophys(&ld->mx_rx_list[i + 1]); - } - } - - cd->mx_rx_head = &cd->mx_rx_chain[0]; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - * Note: the length fields are only 11 bits wide, which means the - * largest size we can specify is 2047. This is important because - * MCLBYTES is 2048, so we have to subtract one otherwise we'll - * overflow the field and make a mess. - */ -static int mx_newbuf(sc, c, m) - struct mx_softc *sc; - struct mx_chain_onefrag *c; - struct mbuf *m; -{ - struct mbuf *m_new = NULL; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("mx%d: no memory for rx list " - "-- packet dropped!\n", sc->mx_unit); - return(ENOBUFS); - } - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("mx%d: no memory for rx list " - "-- packet dropped!\n", sc->mx_unit); - m_freem(m_new); - return(ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - - m_adj(m_new, sizeof(u_int64_t)); - - c->mx_mbuf = m_new; - c->mx_ptr->mx_status = MX_RXSTAT; - c->mx_ptr->mx_data = vtophys(mtod(m_new, caddr_t)); - c->mx_ptr->mx_ctl = MX_RXCTL_RLINK | (MCLBYTES - 1); - - return(0); -} - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void mx_rxeof(sc) - struct mx_softc *sc; -{ - struct ether_header *eh; - struct mbuf *m; - struct ifnet *ifp; - struct mx_chain_onefrag *cur_rx; - int total_len = 0; - u_int32_t rxstat; - - ifp = &sc->arpcom.ac_if; - - while(!((rxstat = sc->mx_cdata.mx_rx_head->mx_ptr->mx_status) & - MX_RXSTAT_OWN)) { - struct mbuf *m0 = NULL; - - cur_rx = sc->mx_cdata.mx_rx_head; - sc->mx_cdata.mx_rx_head = cur_rx->mx_nextdesc; - m = cur_rx->mx_mbuf; - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (rxstat & MX_RXSTAT_RXERR) { - ifp->if_ierrors++; - if (rxstat & MX_RXSTAT_COLLSEEN) - ifp->if_collisions++; - mx_newbuf(sc, cur_rx, m); - continue; - } - - /* No errors; receive the packet. */ - total_len = MX_RXBYTES(cur_rx->mx_ptr->mx_status); - - /* - * XXX The Macronix chips includes the CRC with every - * received frame, and there's no way to turn this - * behavior off (at least, I can't find anything in - * the manual that explains how to do it) so we have - * to trim off the CRC manually. - */ - total_len -= ETHER_CRC_LEN; - - m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, - total_len + ETHER_ALIGN, 0, ifp, NULL); - mx_newbuf(sc, cur_rx, m); - if (m0 == NULL) { - ifp->if_ierrors++; - continue; - } - m_adj(m0, ETHER_ALIGN); - m = m0; - - ifp->if_ipackets++; - eh = mtod(m, struct ether_header *); - - /* - * Handle BPF listeners. Let the BPF user see the packet, but - * don't pass it up to the ether_input() layer unless it's - * a broadcast packet, multicast packet, matches our ethernet - * address or the interface is in promiscuous mode. - */ - if (ifp->if_bpf) { - bpf_mtap(ifp, m); - if (ifp->if_flags & IFF_PROMISC && - (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0)) { - m_freem(m); - continue; - } - } - - /* Remove header from mbuf and pass it on. */ - m_adj(m, sizeof(struct ether_header)); - ether_input(ifp, eh, m); - } - - return; -} - -void mx_rxeoc(sc) - struct mx_softc *sc; -{ - - mx_rxeof(sc); - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_RX_ON); - CSR_WRITE_4(sc, MX_RXADDR, vtophys(sc->mx_cdata.mx_rx_head->mx_ptr)); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_ON); - CSR_WRITE_4(sc, MX_RXSTART, 0xFFFFFFFF); - - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ - -static void mx_txeof(sc) - struct mx_softc *sc; -{ - struct mx_chain *cur_tx; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; - - if (sc->mx_cdata.mx_tx_head == NULL) - return; - - /* - * Go through our tx list and free mbufs for those - * frames that have been transmitted. - */ - while(sc->mx_cdata.mx_tx_head->mx_mbuf != NULL) { - u_int32_t txstat; - - cur_tx = sc->mx_cdata.mx_tx_head; - txstat = MX_TXSTATUS(cur_tx); - - if (txstat & MX_TXSTAT_OWN) - break; - - if (txstat & MX_TXSTAT_ERRSUM) { - ifp->if_oerrors++; - if (txstat & MX_TXSTAT_EXCESSCOLL) - ifp->if_collisions++; - if (txstat & MX_TXSTAT_LATECOLL) - ifp->if_collisions++; - } - - ifp->if_collisions += (txstat & MX_TXSTAT_COLLCNT) >> 3; - - ifp->if_opackets++; - m_freem(cur_tx->mx_mbuf); - cur_tx->mx_mbuf = NULL; - - if (sc->mx_cdata.mx_tx_head == sc->mx_cdata.mx_tx_tail) { - sc->mx_cdata.mx_tx_head = NULL; - sc->mx_cdata.mx_tx_tail = NULL; - break; - } - - sc->mx_cdata.mx_tx_head = cur_tx->mx_nextdesc; - } - - return; -} - -/* - * TX 'end of channel' interrupt handler. - */ -static void mx_txeoc(sc) - struct mx_softc *sc; -{ - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - ifp->if_timer = 0; - - if (sc->mx_cdata.mx_tx_head == NULL) { - ifp->if_flags &= ~IFF_OACTIVE; - sc->mx_cdata.mx_tx_tail = NULL; - } - - return; -} - -static void mx_tick(xsc) - void *xsc; -{ - struct mx_softc *sc; - struct mii_data *mii; - struct ifnet *ifp; - int s; - - s = splimp(); - - sc = xsc; - ifp = &sc->arpcom.ac_if; - mii = device_get_softc(sc->mx_miibus); - mii_tick(mii); - - if (!sc->mx_link) { - mii_pollstat(mii); - if (mii->mii_media_status & IFM_ACTIVE && - IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { - sc->mx_link++; - if (ifp->if_snd.ifq_head != NULL) - mx_start(ifp); - } - } - - sc->mx_stat_ch = timeout(mx_tick, sc, hz); - - splx(s); - - return; -} - -static void mx_intr(arg) - void *arg; -{ - struct mx_softc *sc; - struct ifnet *ifp; - u_int32_t status; - - sc = arg; - ifp = &sc->arpcom.ac_if; - - /* Supress unwanted interrupts */ - if (!(ifp->if_flags & IFF_UP)) { - mx_stop(sc); - return; - } - - /* Disable interrupts. */ - CSR_WRITE_4(sc, MX_IMR, 0x00000000); - - for (;;) { - status = CSR_READ_4(sc, MX_ISR); - if (status) - CSR_WRITE_4(sc, MX_ISR, status); - - if ((status & MX_INTRS) == 0) - break; - - if (status & MX_ISR_TX_OK) - mx_txeof(sc); - - if (status & MX_ISR_TX_NOBUF) - mx_txeoc(sc); - - if (status & MX_ISR_TX_IDLE) { - mx_txeof(sc); - if (sc->mx_cdata.mx_tx_head != NULL) { - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON); - CSR_WRITE_4(sc, MX_TXSTART, 0xFFFFFFFF); - } - } - - if (status & MX_ISR_TX_UNDERRUN) { - u_int32_t cfg; - cfg = CSR_READ_4(sc, MX_NETCFG); - if ((cfg & MX_NETCFG_TX_THRESH) == MX_TXTHRESH_160BYTES) - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_STORENFWD); - else - CSR_WRITE_4(sc, MX_NETCFG, cfg + 0x4000); - } - - if (status & MX_ISR_RX_OK) - mx_rxeof(sc); - - if ((status & MX_ISR_RX_WATDOGTIMEO) - || (status & MX_ISR_RX_NOBUF)) - mx_rxeoc(sc); - - if (status & MX_ISR_BUS_ERR) { - mx_reset(sc); - mx_init(sc); - } - } - - /* Re-enable interrupts. */ - CSR_WRITE_4(sc, MX_IMR, MX_INTRS); - - if (ifp->if_snd.ifq_head != NULL) { - mx_start(ifp); - } - - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int mx_encap(sc, c, m_head) - struct mx_softc *sc; - struct mx_chain *c; - struct mbuf *m_head; -{ - int frag = 0; - struct mx_desc *f = NULL; - int total_len; - struct mbuf *m; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - m = m_head; - total_len = 0; - - for (m = m_head, frag = 0; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if (frag == MX_MAXFRAGS) - break; - total_len += m->m_len; - f = &c->mx_ptr->mx_frag[frag]; - f->mx_ctl = MX_TXCTL_TLINK | m->m_len; - if (frag == 0) { - f->mx_status = 0; - f->mx_ctl |= MX_TXCTL_FIRSTFRAG; - } else - f->mx_status = MX_TXSTAT_OWN; - f->mx_next = vtophys(&c->mx_ptr->mx_frag[frag + 1]); - f->mx_data = vtophys(mtod(m, vm_offset_t)); - frag++; - } - } - - /* - * Handle special case: we ran out of fragments, - * but we have more mbufs left in the chain. Copy the - * data into an mbuf cluster. Note that we don't - * bother clearing the values in the other fragment - * pointers/counters; it wouldn't gain us anything, - * and would waste cycles. - */ - if (m != NULL) { - struct mbuf *m_new = NULL; - - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("mx%d: no memory for tx list", sc->mx_unit); - return(1); - } - if (m_head->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - printf("mx%d: no memory for tx list", - sc->mx_unit); - return(1); - } - } - m_copydata(m_head, 0, m_head->m_pkthdr.len, - mtod(m_new, caddr_t)); - m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; - m_freem(m_head); - m_head = m_new; - f = &c->mx_ptr->mx_frag[0]; - f->mx_status = 0; - f->mx_data = vtophys(mtod(m_new, caddr_t)); - f->mx_ctl = total_len = m_new->m_len; - f->mx_ctl |= MX_TXCTL_TLINK|MX_TXCTL_FIRSTFRAG; - frag = 1; - } - - - if (total_len < MX_MIN_FRAMELEN) { - f = &c->mx_ptr->mx_frag[frag]; - f->mx_ctl = MX_MIN_FRAMELEN - total_len; - f->mx_data = vtophys(&sc->mx_cdata.mx_pad); - f->mx_ctl |= MX_TXCTL_TLINK; - f->mx_status = MX_TXSTAT_OWN; - frag++; - } - - c->mx_mbuf = m_head; - c->mx_lastdesc = frag - 1; - MX_TXCTL(c) |= MX_TXCTL_LASTFRAG|MX_TXCTL_FINT; - MX_TXNEXT(c) = vtophys(&c->mx_nextdesc->mx_ptr->mx_frag[0]); - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ - -static void mx_start(ifp) - struct ifnet *ifp; -{ - struct mx_softc *sc; - struct mbuf *m_head = NULL; - struct mx_chain *cur_tx = NULL, *start_tx; - - sc = ifp->if_softc; - - if (!sc->mx_link) - return; - - if (ifp->if_flags & IFF_OACTIVE) - return; - - /* - * Check for an available queue slot. If there are none, - * punt. - */ - if (sc->mx_cdata.mx_tx_free->mx_mbuf != NULL) { - ifp->if_flags |= IFF_OACTIVE; - return; - } - - start_tx = sc->mx_cdata.mx_tx_free; - - while(sc->mx_cdata.mx_tx_free->mx_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - /* Pick a descriptor off the free list. */ - cur_tx = sc->mx_cdata.mx_tx_free; - sc->mx_cdata.mx_tx_free = cur_tx->mx_nextdesc; - - /* Pack the data into the descriptor. */ - mx_encap(sc, cur_tx, m_head); - if (cur_tx != start_tx) - MX_TXOWN(cur_tx) = MX_TXSTAT_OWN; - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp, cur_tx->mx_mbuf); - - MX_TXOWN(cur_tx) = MX_TXSTAT_OWN; - CSR_WRITE_4(sc, MX_TXSTART, 0xFFFFFFFF); - - } - - /* - * If there are no frames queued, bail. - */ - if (cur_tx == NULL) - return; - - sc->mx_cdata.mx_tx_tail = cur_tx; - - if (sc->mx_cdata.mx_tx_head == NULL) - sc->mx_cdata.mx_tx_head = start_tx; - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; -} - -static void mx_init(xsc) - void *xsc; -{ - struct mx_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - struct mii_data *mii; - int s; - - s = splimp(); - - mii = device_get_softc(sc->mx_miibus); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - mx_stop(sc); - mx_reset(sc); - - /* - * Set cache alignment and burst length. - */ - CSR_WRITE_4(sc, MX_BUSCTL, MX_BUSCTL_MUSTBEONE|MX_BUSCTL_ARBITRATION); - MX_SETBIT(sc, MX_BUSCTL, MX_BURSTLEN_16LONG); - switch(sc->mx_cachesize) { - case 32: - MX_SETBIT(sc, MX_BUSCTL, MX_CACHEALIGN_32LONG); - break; - case 16: - MX_SETBIT(sc, MX_BUSCTL, MX_CACHEALIGN_16LONG); - break; - case 8: - MX_SETBIT(sc, MX_BUSCTL, MX_CACHEALIGN_8LONG); - break; - case 0: - default: - MX_SETBIT(sc, MX_BUSCTL, MX_CACHEALIGN_NONE); - break; - } - - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_NO_RXCRC); - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_STORENFWD); - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_TX_BACKOFF); - - /* - * The app notes for the 98713 and 98715A say that - * in order to have the chips operate properly, a magic - * number must be written to CSR16. Macronix does not - * document the meaning of these bits so there's no way - * to know exactly what they mean. The 98713 has a magic - * number all its own; the rest all use a different one. - */ - MX_CLRBIT(sc, MX_MAGICPACKET, 0xFFFF0000); - if (sc->mx_type == MX_TYPE_98713) - MX_SETBIT(sc, MX_MAGICPACKET, MX_MAGIC_98713); - else - MX_SETBIT(sc, MX_MAGICPACKET, MX_MAGIC_98715); - -#ifdef notdef - if (sc->mx_type == MX_TYPE_98713) { - MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_PCS| - MX_NETCFG_PORTSEL|MX_NETCFG_SCRAMBLER)); - MX_SETBIT(sc, MX_NETCFG, (MX_NETCFG_PCS| - MX_NETCFG_PORTSEL|MX_NETCFG_SCRAMBLER)); - } -#endif - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_TX_THRESH); - /*MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_SPEEDSEL);*/ - - if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_10_T) - MX_SETBIT(sc, MX_NETCFG, MX_TXTHRESH_160BYTES); - else - MX_SETBIT(sc, MX_NETCFG, MX_TXTHRESH_72BYTES); - - /* Init circular RX list. */ - if (mx_list_rx_init(sc) == ENOBUFS) { - printf("mx%d: initialization failed: no " - "memory for rx buffers\n", sc->mx_unit); - mx_stop(sc); - (void)splx(s); - return; - } - - /* - * Init tx descriptors. - */ - mx_list_tx_init(sc); - - /* - * Load the address of the RX list. - */ - CSR_WRITE_4(sc, MX_RXADDR, vtophys(sc->mx_cdata.mx_rx_head->mx_ptr)); - - /* - * Load the RX/multicast filter. - */ - mx_setfilt(sc); - - mii_mediachg(mii); - - /* - * Enable interrupts. - */ - CSR_WRITE_4(sc, MX_IMR, MX_INTRS); - CSR_WRITE_4(sc, MX_ISR, 0xFFFFFFFF); - - /* Enable receiver and transmitter. */ - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_TX_ON|MX_NETCFG_RX_ON); - CSR_WRITE_4(sc, MX_RXSTART, 0xFFFFFFFF); - - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - (void)splx(s); - - sc->mx_stat_ch = timeout(mx_tick, sc, hz); - - return; -} - -/* - * Set media options. - */ -static int mx_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct mx_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; - mii = device_get_softc(sc->mx_miibus); - mii_mediachg(mii); - sc->mx_link = 0; - - return(0); -} - -/* - * Report current media status. - */ -static void mx_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct mx_softc *sc; - struct mii_data *mii; - - sc = ifp->if_softc; - mii = device_get_softc(sc->mx_miibus); - mii_pollstat(mii); - ifmr->ifm_active = mii->mii_media_active; - ifmr->ifm_status = mii->mii_media_status; - - return; -} - -static int mx_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct mx_softc *sc = ifp->if_softc; - struct mii_data *mii; - struct ifreq *ifr = (struct ifreq *) data; - int s, error = 0; - - s = splimp(); - - switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - if (ifp->if_flags & IFF_RUNNING && - ifp->if_flags & IFF_PROMISC && - !(sc->mx_if_flags & IFF_PROMISC)) { - MX_SETBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC); - } else if (ifp->if_flags & IFF_RUNNING && - !(ifp->if_flags & IFF_PROMISC) && - sc->mx_if_flags & IFF_PROMISC) { - MX_CLRBIT(sc, MX_NETCFG, MX_NETCFG_RX_PROMISC); - } else - mx_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - mx_stop(sc); - } - sc->mx_if_flags = ifp->if_flags; - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - mx_init(sc); - error = 0; - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - mii = device_get_softc(sc->mx_miibus); - error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); - break; - default: - error = EINVAL; - break; - } - - (void)splx(s); - - return(error); -} - -static void mx_watchdog(ifp) - struct ifnet *ifp; -{ - struct mx_softc *sc; - - sc = ifp->if_softc; - - ifp->if_oerrors++; - printf("mx%d: watchdog timeout\n", sc->mx_unit); - - mx_stop(sc); - mx_reset(sc); - mx_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - mx_start(ifp); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void mx_stop(sc) - struct mx_softc *sc; -{ - register int i; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - untimeout(mx_tick, sc, sc->mx_stat_ch); - - MX_CLRBIT(sc, MX_NETCFG, (MX_NETCFG_RX_ON|MX_NETCFG_TX_ON)); - CSR_WRITE_4(sc, MX_IMR, 0x00000000); - CSR_WRITE_4(sc, MX_TXADDR, 0x00000000); - CSR_WRITE_4(sc, MX_RXADDR, 0x00000000); - sc->mx_link = 0; - - /* - * Free data in the RX lists. - */ - for (i = 0; i < MX_RX_LIST_CNT; i++) { - if (sc->mx_cdata.mx_rx_chain[i].mx_mbuf != NULL) { - m_freem(sc->mx_cdata.mx_rx_chain[i].mx_mbuf); - sc->mx_cdata.mx_rx_chain[i].mx_mbuf = NULL; - } - } - bzero((char *)&sc->mx_ldata->mx_rx_list, - sizeof(sc->mx_ldata->mx_rx_list)); - - /* - * Free the TX list buffers. - */ - for (i = 0; i < MX_TX_LIST_CNT; i++) { - if (sc->mx_cdata.mx_tx_chain[i].mx_mbuf != NULL) { - m_freem(sc->mx_cdata.mx_tx_chain[i].mx_mbuf); - sc->mx_cdata.mx_tx_chain[i].mx_mbuf = NULL; - } - } - - bzero((char *)&sc->mx_ldata->mx_tx_list, - sizeof(sc->mx_ldata->mx_tx_list)); - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - return; -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static void mx_shutdown(dev) - device_t dev; -{ - struct mx_softc *sc; - - sc = device_get_softc(dev); - - mx_stop(sc); - - return; -} diff --git a/sys/pci/if_mxreg.h b/sys/pci/if_mxreg.h deleted file mode 100644 index 5a3c368..0000000 --- a/sys/pci/if_mxreg.h +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (c) 1997, 1998 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * Macronix register definitions. - */ - -#define MX_BUSCTL 0x00 /* bus control */ -#define MX_TXSTART 0x08 /* tx start demand */ -#define MX_RXSTART 0x10 /* rx start demand */ -#define MX_RXADDR 0x18 /* rx descriptor list start addr */ -#define MX_TXADDR 0x20 /* tx descriptor list start addr */ -#define MX_ISR 0x28 /* interrupt status register */ -#define MX_NETCFG 0x30 /* network config register */ -#define MX_IMR 0x38 /* interrupt mask */ -#define MX_FRAMESDISCARDED 0x40 /* # of discarded frames */ -#define MX_SIO 0x48 /* MII and ROM/EEPROM access */ -#define MX_RESERVED 0x50 -#define MX_TIMER 0x58 /* general timer */ -#define MX_10BTSTAT 0x60 -#define MX_SIARESET 0x68 -#define MX_10BTCTRL 0x70 -#define MX_WATCHDOG 0x78 -#define MX_MAGICPACKET 0x80 -#define MX_NWAYSTAT 0xA0 - -/* - * These are magic values that must be written into CSR16 - * (MX_MAGICPACKET) in order to put the chip into proper - * operating mode. The magic numbers are documented in the - * Macronix 98715 application notes. - */ -#define MX_MAGIC_98713 0x0F370000 -#define MX_MAGIC_98713A 0x0B3C0000 -#define MX_MAGIC_98715 0x0B3C0000 -#define MX_MAGIC_98725 0x0B3C0000 - -#define MX_REVISION_98713 0x00 -#define MX_REVISION_98713A 0x10 -#define MX_REVISION_98715 0x20 -#define MX_REVISION_98725 0x30 - -/* - * As far as the driver is concerned, there are two 'types' of - * chips to be concerned with. One is a 98713 with an external - * PHY on the MII. The other covers pretty much everything else, - * since all the other Macronix chips have built-in transceivers. - * This type setting governs what which mode selection routines - * we use (MII or built-in). It also govers which of the 'magic' - * numbers we write into CSR16. - */ -#define MX_TYPE_98713 0x1 -#define MX_TYPE_98713A 0x2 -#define MX_TYPE_987x5 0x3 - -/* - * Bus control bits. - */ -#define MX_BUSCTL_RESET 0x00000001 -#define MX_BUSCTL_ARBITRATION 0x00000002 -#define MX_BUSCTL_SKIPLEN 0x0000007C -#define MX_BUSCTL_BUF_BIGENDIAN 0x00000080 -#define MX_BUSCTL_BURSTLEN 0x00003F00 -#define MX_BUSCTL_CACHEALIGN 0x0000C000 -#define MX_BUSCTL_TXPOLL 0x000E0000 -#define MX_BUSCTL_MUSTBEONE 0x04000000 - -#define MX_SKIPLEN_1LONG 0x00000004 -#define MX_SKIPLEN_2LONG 0x00000008 -#define MX_SKIPLEN_3LONG 0x00000010 -#define MX_SKIPLEN_4LONG 0x00000020 -#define MX_SKIPLEN_5LONG 0x00000040 - -#define MX_CACHEALIGN_NONE 0x00000000 -#define MX_CACHEALIGN_8LONG 0x00004000 -#define MX_CACHEALIGN_16LONG 0x00008000 -#define MX_CACHEALIGN_32LONG 0x0000C000 - -#define MX_BURSTLEN_USECA 0x00000000 -#define MX_BURSTLEN_1LONG 0x00000100 -#define MX_BURSTLEN_2LONG 0x00000200 -#define MX_BURSTLEN_4LONG 0x00000400 -#define MX_BURSTLEN_8LONG 0x00000800 -#define MX_BURSTLEN_16LONG 0x00001000 -#define MX_BURSTLEN_32LONG 0x00002000 - -#define MX_TXPOLL_OFF 0x00000000 -#define MX_TXPOLL_200U 0x00020000 -#define MX_TXPOLL_800U 0x00040000 -#define MX_TXPOLL_1600U 0x00060000 - -#define MX_BUSCTL_CONFIG (MX_BUSCTL_ARBITRATION|MX_CACHEALIGN_8LONG| \ - MX_BURSTLEN_8LONG) - -/* - * Interrupt status bits. - */ -#define MX_ISR_TX_OK 0x00000001 -#define MX_ISR_TX_IDLE 0x00000002 -#define MX_ISR_TX_NOBUF 0x00000004 -#define MX_ISR_TX_JABBERTIMEO 0x00000008 -#define MX_ISR_LINKGOOD 0x00000010 -#define MX_ISR_TX_UNDERRUN 0x00000020 -#define MX_ISR_RX_OK 0x00000040 -#define MX_ISR_RX_NOBUF 0x00000080 -#define MX_ISR_RX_READ 0x00000100 -#define MX_ISR_RX_WATDOGTIMEO 0x00000200 -#define MX_ISR_TX_EARLY 0x00000400 -#define MX_ISR_TIMER_EXPIRED 0x00000800 -#define MX_ISR_LINKFAIL 0x00001000 -#define MX_ISR_BUS_ERR 0x00002000 -#define MX_ISR_RX_EARLY 0x00004000 -#define MX_ISR_ABNORMAL 0x00008000 -#define MX_ISR_NORMAL 0x00010000 -#define MX_ISR_RX_STATE 0x000E0000 -#define MX_ISR_TX_STATE 0x00700000 -#define MX_ISR_BUSERRTYPE 0x03800000 -#define MX_ISR_100MBPSLINK 0x08000000 -#define MX_ISR_MAGICKPACK 0x10000000 - -#define MX_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ -#define MX_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ -#define MX_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ -#define MX_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ -#define MX_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ -#define MX_RXSTATE_CLOSE 0x000A0000 /* 101 - close tx desc */ -#define MX_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ -#define MX_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ - -#define MX_TXSTATE_RESET 0x00000000 /* 000 - reset */ -#define MX_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ -#define MX_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ -#define MX_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ -#define MX_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ -#define MX_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ -#define MX_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ -#define MX_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ - -/* - * Network config bits. - */ -#define MX_NETCFG_RX_HASHPERF 0x00000001 -#define MX_NETCFG_RX_ON 0x00000002 -#define MX_NETCFG_RX_HASHONLY 0x00000004 -#define MX_NETCFG_RX_BADFRAMES 0x00000008 -#define MX_NETCFG_RX_INVFILT 0x00000010 -#define MX_NETCFG_BACKOFFCNT 0x00000020 -#define MX_NETCFG_RX_PROMISC 0x00000040 -#define MX_NETCFG_RX_ALLMULTI 0x00000080 -#define MX_NETCFG_FULLDUPLEX 0x00000200 -#define MX_NETCFG_LOOPBACK 0x00000C00 -#define MX_NETCFG_FORCECOLL 0x00001000 -#define MX_NETCFG_TX_ON 0x00002000 -#define MX_NETCFG_TX_THRESH 0x0000C000 -#define MX_NETCFG_TX_BACKOFF 0x00020000 -#define MX_NETCFG_PORTSEL 0x00040000 /* 0 == 10, 1 == 100 */ -#define MX_NETCFG_HEARTBEAT 0x00080000 -#define MX_NETCFG_STORENFWD 0x00200000 -#define MX_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10, 0 == 100 */ -#define MX_NETCFG_PCS 0x00800000 -#define MX_NETCFG_SCRAMBLER 0x01000000 -#define MX_NETCFG_NO_RXCRC 0x02000000 - -#define MX_OPMODE_NORM 0x00000000 -#define MX_OPMODE_INTLOOP 0x00000400 -#define MX_OPMODE_EXTLOOP 0x00000800 - -#define MX_TXTHRESH_72BYTES 0x00000000 -#define MX_TXTHRESH_96BYTES 0x00004000 -#define MX_TXTHRESH_128BYTES 0x00008000 -#define MX_TXTHRESH_160BYTES 0x0000C000 - - -/* - * Interrupt mask bits. - */ -#define MX_IMR_TX_OK 0x00000001 -#define MX_IMR_TX_IDLE 0x00000002 -#define MX_IMR_TX_NOBUF 0x00000004 -#define MX_IMR_TX_JABBERTIMEO 0x00000008 -#define MX_IMR_LINKGOOD 0x00000010 -#define MX_IMR_TX_UNDERRUN 0x00000020 -#define MX_IMR_RX_OK 0x00000040 -#define MX_IMR_RX_NOBUF 0x00000080 -#define MX_IMR_RX_READ 0x00000100 -#define MX_IMR_RX_WATDOGTIMEO 0x00000200 -#define MX_IMR_TX_EARLY 0x00000400 -#define MX_IMR_TIMER_EXPIRED 0x00000800 -#define MX_IMR_LINKFAIL 0x00001000 -#define MX_IMR_BUS_ERR 0x00002000 -#define MX_IMR_RX_EARLY 0x00004000 -#define MX_IMR_ABNORMAL 0x00008000 -#define MX_IMR_NORMAL 0x00010000 -#define MX_IMR_100MBPSLINK 0x08000000 -#define MX_IMR_MAGICKPACK 0x10000000 - -#define MX_INTRS \ - (MX_IMR_RX_OK|MX_IMR_TX_OK|MX_IMR_RX_NOBUF|MX_IMR_RX_WATDOGTIMEO|\ - MX_IMR_TX_NOBUF|MX_IMR_TX_UNDERRUN|MX_IMR_BUS_ERR| \ - MX_IMR_ABNORMAL|MX_IMR_NORMAL/*|MX_IMR_TX_EARLY*/) -/* - * Serial I/O (EEPROM/ROM) bits. - */ -#define MX_SIO_EE_CS 0x00000001 /* EEPROM chip select */ -#define MX_SIO_EE_CLK 0x00000002 /* EEPROM clock */ -#define MX_SIO_EE_DATAIN 0x00000004 /* EEPROM data output */ -#define MX_SIO_EE_DATAOUT 0x00000008 /* EEPROM data input */ -#define MX_SIO_ROMDATA4 0x00000010 -#define MX_SIO_ROMDATA5 0x00000020 -#define MX_SIO_ROMDATA6 0x00000040 -#define MX_SIO_ROMDATA7 0x00000080 -#define MX_SIO_EESEL 0x00000800 -#define MX_SIO_ROMSEL 0x00001000 -#define MX_SIO_ROMCTL_WRITE 0x00002000 -#define MX_SIO_ROMCTL_READ 0x00004000 -#define MX_SIO_MII_CLK 0x00010000 /* MDIO clock */ -#define MX_SIO_MII_DATAOUT 0x00020000 /* MDIO data out */ -#define MX_SIO_MII_DIR 0x00040000 /* MDIO dir */ -#define MX_SIO_MII_DATAIN 0x00080000 /* MDIO data in */ - -#define MX_EECMD_WRITE 0x140 -#define MX_EECMD_READ 0x180 -#define MX_EECMD_ERASE 0x1c0 - -#define MX_EE_NODEADDR_OFFSET 0x70 -#define MX_EE_NODEADDR 10 - -/* - * General purpose timer register - */ -#define MX_TIMER_VALUE 0x0000FFFF -#define MX_TIMER_CONTINUUS 0x00010000 - -/* - * 10baseT status register - */ -#define MX_TSTAT_LS100 0x00000002 /* link status of 100baseTX */ -#define MX_TSTAT_LS10 0x00000004 /* link status of 10baseT */ -#define MX_TSTAT_AUTOPOLARITY 0x00000008 -#define MX_TSTAT_REMFAULT 0x00000800 -#define MX_TSTAT_ANEGSTAT 0x00007000 -#define MX_TSTAT_LP_CAN_NWAY 0x00008000 /* link partner supports NWAY */ -#define MX_TSTAT_LPCODEWORD 0xFFFF0000 /* link partner's code word */ - -#define MX_ASTAT_DISABLE 0x00000000 -#define MX_ASTAT_TXDISABLE 0x00001000 -#define MX_ASTAT_ABDETECT 0x00002000 -#define MX_ASTAT_ACKDETECT 0x00003000 -#define MX_ASTAT_CMPACKDETECT 0x00004000 -#define MX_ASTAT_AUTONEGCMP 0x00005000 -#define MX_ASTAT_LINKCHECK 0x00006000 - -/* - * PHY reset register - */ -#define MX_SIA_RESET_NWAY 0x00000001 -#define MX_SIA_RESET_100TX 0x00000002 - -/* - * 10baseT control register - */ -#define MX_TCTL_LOOPBACK 0x00000002 -#define MX_TCTL_POWERDOWN 0x00000004 -#define MX_TCTL_HALFDUPLEX 0x00000040 -#define MX_TCTL_AUTONEGENBL 0x00000080 -#define MX_TCTL_RX_SQUELCH 0x00000100 -#define MX_TCTL_LINKTEST 0x00001000 -#define MX_TCTL_100BTXHALF 0x00010000 -#define MX_TCTL_100BTXFULL 0x00020000 -#define MX_TCTL_100BT4 0x00040000 - -/* - * Watchdog timer register - */ -#define MX_WDOG_JABBERDIS 0x00000001 -#define MX_WDOG_HOSTUNJAB 0x00000002 -#define MX_WDOG_JABBERCLK 0x00000004 -#define MX_WDOG_RXWDOGDIS 0x00000010 -#define MX_WDOG_RXWDOGCLK 0x00000020 -#define MX_WDOG_MUSTBEZERO 0x00000100 - -/* - * Magic packet register - */ -#define MX_MPACK_DISABLE 0x00400000 - -/* - * NWAY status register. - */ -#define MX_NWAY_10BTHALF 0x08000000 -#define MX_NWAY_10BTFULL 0x10000000 -#define MX_NWAY_100BTHALF 0x20000000 -#define MX_NWAY_100BTFULL 0x40000000 -#define MX_NWAY_100BT4 0x80000000 - -/* - * Size of a setup frame. - */ -#define MX_SFRAME_LEN 192 - -/* - * Macronix TX/RX list structure. - */ - -struct mx_desc { - u_int32_t mx_status; - u_int32_t mx_ctl; - u_int32_t mx_ptr1; - u_int32_t mx_ptr2; -}; - -#define mx_data mx_ptr1 -#define mx_next mx_ptr2 - -#define MX_RXSTAT_FIFOOFLOW 0x00000001 -#define MX_RXSTAT_CRCERR 0x00000002 -#define MX_RXSTAT_DRIBBLE 0x00000004 -#define MX_RXSTAT_WATCHDOG 0x00000010 -#define MX_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ -#define MX_RXSTAT_COLLSEEN 0x00000040 -#define MX_RXSTAT_GIANT 0x00000080 -#define MX_RXSTAT_LASTFRAG 0x00000100 -#define MX_RXSTAT_FIRSTFRAG 0x00000200 -#define MX_RXSTAT_MULTICAST 0x00000400 -#define MX_RXSTAT_RUNT 0x00000800 -#define MX_RXSTAT_RXTYPE 0x00003000 -#define MX_RXSTAT_RXERR 0x00008000 -#define MX_RXSTAT_RXLEN 0x3FFF0000 -#define MX_RXSTAT_OWN 0x80000000 - -#define MX_RXBYTES(x) ((x & MX_RXSTAT_RXLEN) >> 16) -#define MX_RXSTAT (MX_RXSTAT_FIRSTFRAG|MX_RXSTAT_LASTFRAG|MX_RXSTAT_OWN) - -#define MX_RXCTL_BUFLEN1 0x00000FFF -#define MX_RXCTL_BUFLEN2 0x00FFF000 -#define MX_RXCTL_RLINK 0x01000000 -#define MX_RXCTL_RLAST 0x02000000 - -#define MX_TXSTAT_DEFER 0x00000001 -#define MX_TXSTAT_UNDERRUN 0x00000002 -#define MX_TXSTAT_LINKFAIl 0x00000003 -#define MX_TXSTAT_COLLCNT 0x00000078 -#define MX_TXSTAT_SQE 0x00000080 -#define MX_TXSTAT_EXCESSCOLL 0x00000100 -#define MX_TXSTAT_LATECOLL 0x00000200 -#define MX_TXSTAT_NOCARRIER 0x00000400 -#define MX_TXSTAT_CARRLOST 0x00000800 -#define MX_TXSTAT_JABTIMEO 0x00004000 -#define MX_TXSTAT_ERRSUM 0x00008000 -#define MX_TXSTAT_OWN 0x80000000 - -#define MX_TXCTL_BUFLEN1 0x000007FF -#define MX_TXCTL_BUFLEN2 0x003FF800 -#define MX_TXCTL_FILTTYPE0 0x00400000 -#define MX_TXCTL_PAD 0x00800000 -#define MX_TXCTL_TLINK 0x01000000 -#define MX_TXCTL_TLAST 0x02000000 -#define MX_TXCTL_NOCRC 0x04000000 -#define MX_TXCTL_SETUP 0x08000000 -#define MX_TXCTL_FILTTYPE1 0x10000000 -#define MX_TXCTL_FIRSTFRAG 0x20000000 -#define MX_TXCTL_LASTFRAG 0x40000000 -#define MX_TXCTL_FINT 0x80000000 - -#define MX_FILTER_PERFECT 0x00000000 -#define MX_FILTER_HASHPERF 0x00400000 -#define MX_FILTER_INVERSE 0x10000000 -#define MX_FILTER_HASHONLY 0x10400000 - -#define MX_MAXFRAGS 16 -#define MX_RX_LIST_CNT 64 -#define MX_TX_LIST_CNT 128 -#define MX_MIN_FRAMELEN 60 - -/* - * A tx 'super descriptor' is actually 16 regular descriptors - * back to back. - */ -struct mx_txdesc { - struct mx_desc mx_frag[MX_MAXFRAGS]; -}; - -#define MX_TXNEXT(x) x->mx_ptr->mx_frag[x->mx_lastdesc].mx_next -#define MX_TXSTATUS(x) x->mx_ptr->mx_frag[x->mx_lastdesc].mx_status -#define MX_TXCTL(x) x->mx_ptr->mx_frag[x->mx_lastdesc].mx_ctl -#define MX_TXDATA(x) x->mx_ptr->mx_frag[x->mx_lastdesc].mx_data - -#define MX_TXOWN(x) x->mx_ptr->mx_frag[0].mx_status - -struct mx_list_data { - struct mx_desc mx_rx_list[MX_RX_LIST_CNT]; - struct mx_txdesc mx_tx_list[MX_TX_LIST_CNT]; -}; - -struct mx_chain { - struct mx_txdesc *mx_ptr; - struct mbuf *mx_mbuf; - struct mx_chain *mx_nextdesc; - u_int8_t mx_lastdesc; -}; - -struct mx_chain_onefrag { - struct mx_desc *mx_ptr; - struct mbuf *mx_mbuf; - struct mx_chain_onefrag *mx_nextdesc; -}; - -struct mx_chain_data { - struct mx_desc mx_sframe; - u_int32_t mx_sbuf[MX_SFRAME_LEN/sizeof(u_int32_t)]; - u_int8_t mx_pad[MX_MIN_FRAMELEN]; - struct mx_chain_onefrag mx_rx_chain[MX_RX_LIST_CNT]; - struct mx_chain mx_tx_chain[MX_TX_LIST_CNT]; - - struct mx_chain_onefrag *mx_rx_head; - - struct mx_chain *mx_tx_head; - struct mx_chain *mx_tx_tail; - struct mx_chain *mx_tx_free; -}; - -struct mx_type { - u_int16_t mx_vid; - u_int16_t mx_did; - char *mx_name; -}; - -struct mx_mii_frame { - u_int8_t mii_stdelim; - u_int8_t mii_opcode; - u_int8_t mii_phyaddr; - u_int8_t mii_regaddr; - u_int8_t mii_turnaround; - u_int16_t mii_data; -}; - -/* - * MII constants - */ -#define MX_MII_STARTDELIM 0x01 -#define MX_MII_READOP 0x02 -#define MX_MII_WRITEOP 0x01 -#define MX_MII_TURNAROUND 0x02 - -#define MX_FLAG_FORCEDELAY 1 -#define MX_FLAG_SCHEDDELAY 2 -#define MX_FLAG_DELAYTIMEO 3 - -struct mx_softc { - struct arpcom arpcom; /* interface info */ - bus_space_handle_t mx_bhandle; /* bus space handle */ - bus_space_tag_t mx_btag; /* bus space tag */ - void *mx_intrhand; - struct resource *mx_irq; - struct resource *mx_res; - device_t mx_miibus; - struct mx_type *mx_info; /* Macronix adapter info */ - u_int8_t mx_unit; /* interface number */ - u_int8_t mx_type; - u_int8_t mx_cachesize; - u_int8_t mx_link; - int mx_if_flags; - caddr_t mx_ldata_ptr; - struct mx_list_data *mx_ldata; - struct mx_chain_data mx_cdata; - struct callout_handle mx_stat_ch; -}; - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->mx_btag, sc->mx_bhandle, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->mx_btag, sc->mx_bhandle, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->mx_btag, sc->mx_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->mx_btag, sc->mx_bhandle, reg) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->mx_btag, sc->mx_bhandle, reg) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->mx_btag, sc->mx_bhandle, reg) - -#define MX_TIMEOUT 1000 -#define ETHER_ALIGN 2 - -/* - * General constants that are fun to know. - * - * Macronix PCI vendor ID - */ -#define MX_VENDORID 0x10D9 - -/* - * Macronix PMAC device IDs. - */ -#define MX_DEVICEID_98713 0x0512 -#define MX_DEVICEID_987x5 0x0531 - -/* - * Compex PCI vendor ID. - */ -#define CP_VENDORID 0x11F6 - -/* - * Compex PMAC PCI device IDs. - */ -#define CP_DEVICEID_98713 0x9881 - -/* - * Lite-On PNIC PCI vendor ID - */ -#define PN_VENDORID 0x11AD - -/* - * Lite-On PNIC II device ID. Note: this is actually a Macronix 98715A - * with wake on lan/magic packet support. - */ -#define PN_DEVICEID_PNIC_II 0xc115 - -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. - */ - -#define MX_PCI_VENDOR_ID 0x00 -#define MX_PCI_DEVICE_ID 0x02 -#define MX_PCI_COMMAND 0x04 -#define MX_PCI_STATUS 0x06 -#define MX_PCI_REVID 0x08 -#define MX_PCI_CLASSCODE 0x09 -#define MX_PCI_CACHELEN 0x0C -#define MX_PCI_LATENCY_TIMER 0x0D -#define MX_PCI_HEADER_TYPE 0x0E -#define MX_PCI_LOIO 0x10 -#define MX_PCI_LOMEM 0x14 -#define MX_PCI_BIOSROM 0x30 -#define MX_PCI_INTLINE 0x3C -#define MX_PCI_INTPIN 0x3D -#define MX_PCI_MINGNT 0x3E -#define MX_PCI_MINLAT 0x0F -#define MX_PCI_RESETOPT 0x48 -#define MX_PCI_EEPROM_DATA 0x4C - -/* power management registers */ -#define MX_PCI_CAPID 0x44 /* 8 bits */ -#define MX_PCI_NEXTPTR 0x45 /* 8 bits */ -#define MX_PCI_PWRMGMTCAP 0x46 /* 16 bits */ -#define MX_PCI_PWRMGMTCTRL 0x48 /* 16 bits */ - -#define MX_PSTATE_MASK 0x0003 -#define MX_PSTATE_D0 0x0000 -#define MX_PSTATE_D1 0x0001 -#define MX_PSTATE_D2 0x0002 -#define MX_PSTATE_D3 0x0003 -#define MX_PME_EN 0x0010 -#define MX_PME_STATUS 0x8000 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif diff --git a/sys/pci/if_pn.c b/sys/pci/if_pn.c deleted file mode 100644 index 166afba..0000000 --- a/sys/pci/if_pn.c +++ /dev/null @@ -1,2326 +0,0 @@ -/* - * Copyright (c) 1997, 1998 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * 82c168/82c169 PNIC fast ethernet PCI NIC driver - * - * Supports various network adapters based on the Lite-On PNIC - * PCI network controller chip including the LinkSys LNE100TX. - * - * Written by Bill Paul <wpaul@ctr.columbia.edu> - * Electrical Engineering Department - * Columbia University, New York City - */ - -/* - * The PNIC chip is a DEC tulip clone. This driver uses much of the - * same code from the driver for the Winbond chip (which is also a - * tulip clone) except for the MII, EEPROM and filter programming. - * - * Technically we could merge support for this chip into the 'de' - * driver, but it's such a mess that I'm afraid to go near it. - * - * The PNIC appears to support both an external MII and an internal - * transceiver. I think most 100Mbps implementations use a PHY attached - * the the MII. The LinkSys board that I have uses a Myson MTD972 - * 100BaseTX PHY. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/malloc.h> -#include <sys/kernel.h> -#include <sys/socket.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <net/ethernet.h> -#include <net/if_dl.h> -#include <net/if_media.h> - -#include <net/bpf.h> - -#include "opt_bdg.h" -#ifdef BRIDGE -#include <net/bridge.h> -#endif - -#include <vm/vm.h> /* for vtophys */ -#include <vm/pmap.h> /* for vtophys */ -#include <machine/clock.h> /* for DELAY */ -#include <machine/bus_pio.h> -#include <machine/bus_memio.h> -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/bus.h> -#include <sys/rman.h> - -#include <pci/pcireg.h> -#include <pci/pcivar.h> - -#define PN_USEIOSPACE - -/* #define PN_BACKGROUND_AUTONEG */ - -#define PN_RX_BUG_WAR - -#include <pci/if_pnreg.h> - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - -/* - * Various supported device vendors/types and their names. - */ -static struct pn_type pn_devs[] = { - { PN_VENDORID, PN_DEVICEID_PNIC, - "82c168 PNIC 10/100BaseTX" }, - { PN_VENDORID, PN_DEVICEID_PNIC, - "82c169 PNIC 10/100BaseTX" }, - { 0, 0, NULL } -}; - -/* - * Various supported PHY vendors/types and their names. Note that - * this driver will work with pretty much any MII-compliant PHY, - * so failure to positively identify the chip is not a fatal error. - */ - -static struct pn_type pn_phys[] = { - { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, - { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, - { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, - { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, - { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, - { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, - { 0, 0, "<MII-compliant physical interface>" } -}; - -static int pn_probe __P((device_t)); -static int pn_attach __P((device_t)); -static int pn_detach __P((device_t)); - -static int pn_newbuf __P((struct pn_softc *, - struct pn_chain_onefrag *, - struct mbuf *)); -static int pn_encap __P((struct pn_softc *, struct pn_chain *, - struct mbuf *)); - -#ifdef PN_RX_BUG_WAR -static void pn_rx_bug_war __P((struct pn_softc *, - struct pn_chain_onefrag *)); -#endif -static void pn_rxeof __P((struct pn_softc *)); -static void pn_rxeoc __P((struct pn_softc *)); -static void pn_txeof __P((struct pn_softc *)); -static void pn_txeoc __P((struct pn_softc *)); -static void pn_intr __P((void *)); -static void pn_start __P((struct ifnet *)); -static int pn_ioctl __P((struct ifnet *, u_long, caddr_t)); -static void pn_init __P((void *)); -static void pn_stop __P((struct pn_softc *)); -static void pn_watchdog __P((struct ifnet *)); -static void pn_shutdown __P((device_t)); -static int pn_ifmedia_upd __P((struct ifnet *)); -static void pn_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); - -static void pn_eeprom_getword __P((struct pn_softc *, u_int8_t, u_int16_t *)); -static void pn_read_eeprom __P((struct pn_softc *, caddr_t, int, - int, int)); -static u_int16_t pn_phy_readreg __P((struct pn_softc *, int)); -static void pn_phy_writereg __P((struct pn_softc *, u_int16_t, u_int16_t)); - -static void pn_autoneg_xmit __P((struct pn_softc *)); -static void pn_autoneg_mii __P((struct pn_softc *, int, int)); -static void pn_setmode_mii __P((struct pn_softc *, int)); -static void pn_getmode_mii __P((struct pn_softc *)); -static void pn_autoneg __P((struct pn_softc *, int, int)); -static void pn_setmode __P((struct pn_softc *, int)); -static void pn_setcfg __P((struct pn_softc *, u_int32_t)); -static u_int32_t pn_calchash __P((u_int8_t *)); -static void pn_setfilt __P((struct pn_softc *)); -static void pn_reset __P((struct pn_softc *)); -static int pn_list_rx_init __P((struct pn_softc *)); -static int pn_list_tx_init __P((struct pn_softc *)); - -#ifdef PN_USEIOSPACE -#define PN_RES SYS_RES_IOPORT -#define PN_RID PN_PCI_LOIO -#else -#define PN_RES SYS_RES_MEMORY -#define PN_RID PN_PCI_LOMEM -#endif - -static device_method_t pn_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, pn_probe), - DEVMETHOD(device_attach, pn_attach), - DEVMETHOD(device_detach, pn_detach), - DEVMETHOD(device_shutdown, pn_shutdown), - { 0, 0 } -}; - -static driver_t pn_driver = { - "pn", - pn_methods, - sizeof(struct pn_softc), -}; - -static devclass_t pn_devclass; - -DRIVER_MODULE(if_pn, pci, pn_driver, pn_devclass, 0, 0); - -#define PN_SETBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) | (x)) - -#define PN_CLRBIT(sc, reg, x) \ - CSR_WRITE_4(sc, reg, \ - CSR_READ_4(sc, reg) & ~(x)) - -/* - * Read a word of data stored in the EEPROM at address 'addr.' - */ -static void pn_eeprom_getword(sc, addr, dest) - struct pn_softc *sc; - u_int8_t addr; - u_int16_t *dest; -{ - register int i; - u_int32_t r; - - CSR_WRITE_4(sc, PN_SIOCTL, PN_EE_READ|addr); - - for (i = 0; i < PN_TIMEOUT; i++) { - DELAY(1); - r = CSR_READ_4(sc, PN_SIO); - if (!(r & PN_SIO_BUSY)) { - *dest = (u_int16_t)(r & 0x0000FFFF); - return; - } - } - - return; - -} - -/* - * Read a sequence of words from the EEPROM. - */ -static void pn_read_eeprom(sc, dest, off, cnt, swap) - struct pn_softc *sc; - caddr_t dest; - int off; - int cnt; - int swap; -{ - int i; - u_int16_t word = 0, *ptr; - - for (i = 0; i < cnt; i++) { - pn_eeprom_getword(sc, off + i, &word); - ptr = (u_int16_t *)(dest + (i * 2)); - if (swap) - *ptr = ntohs(word); - else - *ptr = word; - } - - return; -} - -static u_int16_t pn_phy_readreg(sc, reg) - struct pn_softc *sc; - int reg; -{ - int i; - u_int32_t rval; - - CSR_WRITE_4(sc, PN_MII, - PN_MII_READ | (sc->pn_phy_addr << 23) | (reg << 18)); - - for (i = 0; i < PN_TIMEOUT; i++) { - DELAY(1); - rval = CSR_READ_4(sc, PN_MII); - if (!(rval & PN_MII_BUSY)) { - if ((u_int16_t)(rval & 0x0000FFFF) == 0xFFFF) - return(0); - else - return((u_int16_t)(rval & 0x0000FFFF)); - } - } - - return(0); -} - -static void pn_phy_writereg(sc, reg, data) - struct pn_softc *sc; - u_int16_t reg; - u_int16_t data; -{ - int i; - - CSR_WRITE_4(sc, PN_MII, - PN_MII_WRITE | (sc->pn_phy_addr << 23) | (reg << 18) | data); - - - for (i = 0; i < PN_TIMEOUT; i++) { - if (!(CSR_READ_4(sc, PN_MII) & PN_MII_BUSY)) - break; - } - - return; -} - -#define PN_POLY 0xEDB88320 -#define PN_BITS 9 - -static u_int32_t pn_calchash(addr) - u_int8_t *addr; -{ - u_int32_t idx, bit, data, crc; - - /* Compute CRC for the address value. */ - crc = 0xFFFFFFFF; /* initial value */ - - for (idx = 0; idx < 6; idx++) { - for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1) - crc = (crc >> 1) ^ (((crc ^ data) & 1) ? PN_POLY : 0); - } - - return (crc & ((1 << PN_BITS) - 1)); -} - -/* - * Initiate an autonegotiation session. - */ -static void pn_autoneg_xmit(sc) - struct pn_softc *sc; -{ - u_int16_t phy_sts; - - pn_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); - DELAY(500); - while(pn_phy_readreg(sc, PHY_BMCR) - & PHY_BMCR_RESET); - - phy_sts = pn_phy_readreg(sc, PHY_BMCR); - phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; - pn_phy_writereg(sc, PHY_BMCR, phy_sts); - - return; -} - -/* - * Invoke autonegotiation on a PHY. - */ -static void pn_autoneg_mii(sc, flag, verbose) - struct pn_softc *sc; - int flag; - int verbose; -{ - u_int16_t phy_sts = 0, media, advert, ability; - struct ifnet *ifp; - struct ifmedia *ifm; - - ifm = &sc->ifmedia; - ifp = &sc->arpcom.ac_if; - - ifm->ifm_media = IFM_ETHER | IFM_AUTO; - - /* - * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' - * bit cleared in the status register, but has the 'autoneg enabled' - * bit set in the control register. This is a contradiction, and - * I'm not sure how to handle it. If you want to force an attempt - * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR - * and see what happens. - */ -#ifndef FORCE_AUTONEG_TFOUR - /* - * First, see if autoneg is supported. If not, there's - * no point in continuing. - */ - phy_sts = pn_phy_readreg(sc, PHY_BMSR); - if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { - if (verbose) - printf("pn%d: autonegotiation not supported\n", - sc->pn_unit); - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - return; - } -#endif - - switch (flag) { - case PN_FLAG_FORCEDELAY: - /* - * XXX Never use this option anywhere but in the probe - * routine: making the kernel stop dead in its tracks - * for three whole seconds after we've gone multi-user - * is really bad manners. - */ - pn_autoneg_xmit(sc); - DELAY(5000000); - break; - case PN_FLAG_SCHEDDELAY: - /* - * Wait for the transmitter to go idle before starting - * an autoneg session, otherwise pn_start() may clobber - * our timeout, and we don't want to allow transmission - * during an autoneg session since that can screw it up. - */ - if (sc->pn_cdata.pn_tx_head != NULL) { - sc->pn_want_auto = 1; - return; - } - pn_autoneg_xmit(sc); - ifp->if_timer = 5; - sc->pn_autoneg = 1; - sc->pn_want_auto = 0; - return; - break; - case PN_FLAG_DELAYTIMEO: - ifp->if_timer = 0; - sc->pn_autoneg = 0; - break; - default: - printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag); - return; - } - - if (pn_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { - if (verbose) - printf("pn%d: autoneg complete, ", sc->pn_unit); - phy_sts = pn_phy_readreg(sc, PHY_BMSR); - } else { - if (verbose) - printf("pn%d: autoneg not complete, ", sc->pn_unit); - } - - media = pn_phy_readreg(sc, PHY_BMCR); - - /* Link is good. Report modes and set duplex mode. */ - if (pn_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { - if (verbose) - printf("link status good "); - advert = pn_phy_readreg(sc, PHY_ANAR); - ability = pn_phy_readreg(sc, PHY_LPAR); - - if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { - ifm->ifm_media = IFM_ETHER|IFM_100_T4; - media |= PHY_BMCR_SPEEDSEL; - media &= ~PHY_BMCR_DUPLEX; - printf("(100baseT4)\n"); - } else if (advert & PHY_ANAR_100BTXFULL && - ability & PHY_ANAR_100BTXFULL) { - ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; - media |= PHY_BMCR_SPEEDSEL; - media |= PHY_BMCR_DUPLEX; - printf("(full-duplex, 100Mbps)\n"); - } else if (advert & PHY_ANAR_100BTXHALF && - ability & PHY_ANAR_100BTXHALF) { - ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; - media |= PHY_BMCR_SPEEDSEL; - media &= ~PHY_BMCR_DUPLEX; - printf("(half-duplex, 100Mbps)\n"); - } else if (advert & PHY_ANAR_10BTFULL && - ability & PHY_ANAR_10BTFULL) { - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; - media &= ~PHY_BMCR_SPEEDSEL; - media |= PHY_BMCR_DUPLEX; - printf("(full-duplex, 10Mbps)\n"); - } else if (advert & PHY_ANAR_10BTHALF && - ability & PHY_ANAR_10BTHALF) { - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - media &= ~PHY_BMCR_SPEEDSEL; - media &= ~PHY_BMCR_DUPLEX; - printf("(half-duplex, 10Mbps)\n"); - } - - media &= ~PHY_BMCR_AUTONEGENBL; - - /* Set ASIC's duplex mode to match the PHY. */ - pn_setcfg(sc, ifm->ifm_media); - pn_phy_writereg(sc, PHY_BMCR, media); - } else { - if (verbose) - printf("no carrier\n"); - } - - pn_init(sc); - - if (sc->pn_tx_pend) { - sc->pn_autoneg = 0; - sc->pn_tx_pend = 0; - pn_start(ifp); - } - - return; -} - -static void pn_getmode_mii(sc) - struct pn_softc *sc; -{ - u_int16_t bmsr; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - bmsr = pn_phy_readreg(sc, PHY_BMSR); - if (bootverbose) - printf("pn%d: PHY status word: %x\n", sc->pn_unit, bmsr); - - /* fallback */ - sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - - if (bmsr & PHY_BMSR_10BTHALF) { - if (bootverbose) - printf("pn%d: 10Mbps half-duplex mode supported\n", - sc->pn_unit); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); - } - - if (bmsr & PHY_BMSR_10BTFULL) { - if (bootverbose) - printf("pn%d: 10Mbps full-duplex mode supported\n", - sc->pn_unit); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; - } - - if (bmsr & PHY_BMSR_100BTXHALF) { - if (bootverbose) - printf("pn%d: 100Mbps half-duplex mode supported\n", - sc->pn_unit); - ifp->if_baudrate = 100000000; - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; - } - - if (bmsr & PHY_BMSR_100BTXFULL) { - if (bootverbose) - printf("pn%d: 100Mbps full-duplex mode supported\n", - sc->pn_unit); - ifp->if_baudrate = 100000000; - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; - } - - /* Some also support 100BaseT4. */ - if (bmsr & PHY_BMSR_100BT4) { - if (bootverbose) - printf("pn%d: 100baseT4 mode supported\n", sc->pn_unit); - ifp->if_baudrate = 100000000; - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4; -#ifdef FORCE_AUTONEG_TFOUR - if (bootverbose) - printf("pn%d: forcing on autoneg support for BT4\n", - sc->pn_unit); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): - sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; -#endif - } - - if (bmsr & PHY_BMSR_CANAUTONEG) { - if (bootverbose) - printf("pn%d: autoneg supported\n", sc->pn_unit); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); - sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; - } - - return; -} - -static void pn_autoneg(sc, flag, verbose) - struct pn_softc *sc; - int flag; - int verbose; -{ - u_int32_t nway = 0, ability; - struct ifnet *ifp; - struct ifmedia *ifm; - - ifm = &sc->ifmedia; - ifp = &sc->arpcom.ac_if; - - ifm->ifm_media = IFM_ETHER | IFM_AUTO; - - switch (flag) { - case PN_FLAG_FORCEDELAY: - /* - * XXX Never use this option anywhere but in the probe - * routine: making the kernel stop dead in its tracks - * for three whole seconds after we've gone multi-user - * is really bad manners. - */ - CSR_WRITE_4(sc, PN_GEN, - PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); - PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); - PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); - DELAY(5000000); - break; - case PN_FLAG_SCHEDDELAY: - /* - * Wait for the transmitter to go idle before starting - * an autoneg session, otherwise pn_start() may clobber - * our timeout, and we don't want to allow transmission - * during an autoneg session since that can screw it up. - */ - if (sc->pn_cdata.pn_tx_head != NULL) { - sc->pn_want_auto = 1; - return; - } - CSR_WRITE_4(sc, PN_GEN, - PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); - PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); - PN_SETBIT(sc, PN_NWAY, PN_NWAY_AUTOENB); - ifp->if_timer = 5; - sc->pn_autoneg = 1; - sc->pn_want_auto = 0; - return; - break; - case PN_FLAG_DELAYTIMEO: - ifp->if_timer = 0; - sc->pn_autoneg = 0; - break; - default: - printf("pn%d: invalid autoneg flag: %d\n", sc->pn_unit, flag); - return; - } - - if (CSR_READ_4(sc, PN_NWAY) & PN_NWAY_LPAR) { - if (verbose) - printf("pn%d: autoneg complete, ", sc->pn_unit); - } else { - if (verbose) - printf("pn%d: autoneg not complete, ", sc->pn_unit); - } - - /* Link is good. Report modes and set duplex mode. */ - if (CSR_READ_4(sc, PN_ISR) & PN_ISR_LINKPASS) { - if (verbose) - printf("link status good "); - - ability = CSR_READ_4(sc, PN_NWAY); - if (ability & PN_NWAY_LPAR100T4) { - ifm->ifm_media = IFM_ETHER|IFM_100_T4; - nway = PN_NWAY_MODE_100T4; - printf("(100baseT4)\n"); - } else if (ability & PN_NWAY_LPAR100FULL) { - ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; - nway = PN_NWAY_MODE_100FD; - printf("(full-duplex, 100Mbps)\n"); - } else if (ability & PN_NWAY_LPAR100HALF) { - ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; - nway = PN_NWAY_MODE_100HD; - printf("(half-duplex, 100Mbps)\n"); - } else if (ability & PN_NWAY_LPAR10FULL) { - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; - nway = PN_NWAY_MODE_10FD; - printf("(full-duplex, 10Mbps)\n"); - } else if (ability & PN_NWAY_LPAR10HALF) { - ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; - nway = PN_NWAY_MODE_10HD; - printf("(half-duplex, 10Mbps)\n"); - } - - /* Set ASIC's duplex mode to match the PHY. */ - pn_setcfg(sc, ifm->ifm_media); - CSR_WRITE_4(sc, PN_NWAY, nway); - } else { - if (verbose) - printf("no carrier\n"); - } - - pn_init(sc); - - if (sc->pn_tx_pend) { - sc->pn_autoneg = 0; - sc->pn_tx_pend = 0; - pn_start(ifp); - } - - return; -} - -static void pn_setmode(sc, media) - struct pn_softc *sc; - int media; -{ - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* - * If an autoneg session is in progress, stop it. - */ - if (sc->pn_autoneg) { - printf("pn%d: canceling autoneg session\n", sc->pn_unit); - ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; - PN_CLRBIT(sc, PN_NWAY, PN_NWAY_AUTONEGRSTR); - } - - printf("pn%d: selecting NWAY, ", sc->pn_unit); - - if (IFM_SUBTYPE(media) == IFM_100_T4) { - printf("100Mbps/T4, half-duplex\n"); - } - - if (IFM_SUBTYPE(media) == IFM_100_TX) { - printf("100Mbps, "); - } - - if (IFM_SUBTYPE(media) == IFM_10_T) { - printf("10Mbps, "); - } - - if ((media & IFM_GMASK) == IFM_FDX) { - printf("full duplex\n"); - } else { - printf("half duplex\n"); - } - - pn_setcfg(sc, media); - - return; -} - -static void pn_setmode_mii(sc, media) - struct pn_softc *sc; - int media; -{ - u_int16_t bmcr; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* - * If an autoneg session is in progress, stop it. - */ - if (sc->pn_autoneg) { - printf("pn%d: canceling autoneg session\n", sc->pn_unit); - ifp->if_timer = sc->pn_autoneg = sc->pn_want_auto = 0; - bmcr = pn_phy_readreg(sc, PHY_BMCR); - bmcr &= ~PHY_BMCR_AUTONEGENBL; - pn_phy_writereg(sc, PHY_BMCR, bmcr); - } - - printf("pn%d: selecting MII, ", sc->pn_unit); - - bmcr = pn_phy_readreg(sc, PHY_BMCR); - - bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| - PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); - - if (IFM_SUBTYPE(media) == IFM_100_T4) { - printf("100Mbps/T4, half-duplex\n"); - bmcr |= PHY_BMCR_SPEEDSEL; - bmcr &= ~PHY_BMCR_DUPLEX; - } - - if (IFM_SUBTYPE(media) == IFM_100_TX) { - printf("100Mbps, "); - bmcr |= PHY_BMCR_SPEEDSEL; - } - - if (IFM_SUBTYPE(media) == IFM_10_T) { - printf("10Mbps, "); - bmcr &= ~PHY_BMCR_SPEEDSEL; - } - - if ((media & IFM_GMASK) == IFM_FDX) { - printf("full duplex\n"); - bmcr |= PHY_BMCR_DUPLEX; - } else { - printf("half duplex\n"); - bmcr &= ~PHY_BMCR_DUPLEX; - } - - pn_setcfg(sc, media); - pn_phy_writereg(sc, PHY_BMCR, bmcr); - - return; -} - -/* - * Programming the receiver filter on the tulip/PNIC is gross. You - * have to construct a special setup frame and download it to the - * chip via the transmit DMA engine. This routine is also somewhat - * gross, as the setup frame is sent synchronously rather than putting - * on the transmit queue. The transmitter has to be stopped, then we - * can download the frame and wait for the 'owned' bit to clear. - * - * We always program the chip using 'hash perfect' mode, i.e. one perfect - * address (our node address) and a 512-bit hash filter for multicast - * frames. We also sneak the broadcast address into the hash filter since - * we need that too. - */ -void pn_setfilt(sc) - struct pn_softc *sc; -{ - struct pn_desc *sframe; - u_int32_t h, *sp; - struct ifmultiaddr *ifma; - struct ifnet *ifp; - int i; - - ifp = &sc->arpcom.ac_if; - - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); - PN_SETBIT(sc, PN_ISR, PN_ISR_TX_IDLE); - - sframe = &sc->pn_cdata.pn_sframe; - sp = (u_int32_t *)&sc->pn_cdata.pn_sbuf; - bzero((char *)sp, PN_SFRAME_LEN); - - sframe->pn_status = PN_TXSTAT_OWN; - sframe->pn_next = vtophys(&sc->pn_ldata->pn_tx_list[0]); - sframe->pn_data = vtophys(&sc->pn_cdata.pn_sbuf); - sframe->pn_ctl = PN_SFRAME_LEN | PN_TXCTL_TLINK | - PN_TXCTL_SETUP | PN_FILTER_HASHPERF; - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC); - else - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_PROMISC); - - if (ifp->if_flags & IFF_ALLMULTI) - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ALLMULTI); - - for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; - ifma = ifma->ifma_link.le_next) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - h = pn_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); - sp[h >> 4] |= 1 << (h & 0xF); - } - - if (ifp->if_flags & IFF_BROADCAST) { - h = pn_calchash(etherbroadcastaddr); - sp[h >> 4] |= 1 << (h & 0xF); - } - - sp[39] = ((u_int16_t *)sc->arpcom.ac_enaddr)[0]; - sp[40] = ((u_int16_t *)sc->arpcom.ac_enaddr)[1]; - sp[41] = ((u_int16_t *)sc->arpcom.ac_enaddr)[2]; - - CSR_WRITE_4(sc, PN_TXADDR, vtophys(sframe)); - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); - CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); - - /* - * Wait for chip to clear the 'own' bit. - */ - for (i = 0; i < PN_TIMEOUT; i++) { - DELAY(10); - if (sframe->pn_status != PN_TXSTAT_OWN) - break; - } - - if (i == PN_TIMEOUT) - printf("pn%d: failed to send setup frame\n", sc->pn_unit); - - PN_SETBIT(sc, PN_ISR, PN_ISR_TX_NOBUF|PN_ISR_TX_IDLE); - - return; -} - -/* - * In order to fiddle with the - * 'full-duplex' and '100Mbps' bits in the netconfig register, we - * first have to put the transmit and/or receive logic in the idle state. - */ -static void pn_setcfg(sc, media) - struct pn_softc *sc; - u_int32_t media; -{ - int i, restart = 0; - - if (CSR_READ_4(sc, PN_NETCFG) & (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)) { - restart = 1; - PN_CLRBIT(sc, PN_NETCFG, (PN_NETCFG_TX_ON|PN_NETCFG_RX_ON)); - - for (i = 0; i < PN_TIMEOUT; i++) { - DELAY(10); - if ((CSR_READ_4(sc, PN_ISR) & PN_ISR_TX_IDLE) && - (CSR_READ_4(sc, PN_ISR) & PN_ISR_RX_IDLE)) - break; - } - - if (i == PN_TIMEOUT) - printf("pn%d: failed to force tx and " - "rx to idle state\n", sc->pn_unit); - - } - - if (IFM_SUBTYPE(media) == IFM_100_TX) { - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); - if (sc->pn_pinfo == NULL) { - CSR_WRITE_4(sc, PN_GEN, PN_GEN_MUSTBEONE| - PN_GEN_SPEEDSEL|PN_GEN_100TX_LOOP); - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_PCS| - PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB); - PN_SETBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL); - } - } else { - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_SPEEDSEL); - if (sc->pn_pinfo == NULL) { - CSR_WRITE_4(sc, PN_GEN, - PN_GEN_MUSTBEONE|PN_GEN_100TX_LOOP); - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_PCS| - PN_NETCFG_SCRAMBLER|PN_NETCFG_MIIENB); - PN_CLRBIT(sc, PN_NWAY, PN_NWAY_SPEEDSEL); - } - } - - if ((media & IFM_GMASK) == IFM_FDX) { - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); - if (sc->pn_pinfo == NULL) - PN_SETBIT(sc, PN_NWAY, PN_NWAY_DUPLEX); - } else { - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_FULLDUPLEX); - if (sc->pn_pinfo == NULL) - PN_CLRBIT(sc, PN_NWAY, PN_NWAY_DUPLEX); - } - - if (restart) - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON|PN_NETCFG_RX_ON); - - return; -} - -static void pn_reset(sc) - struct pn_softc *sc; -{ - register int i; - - PN_SETBIT(sc, PN_BUSCTL, PN_BUSCTL_RESET); - - for (i = 0; i < PN_TIMEOUT; i++) { - DELAY(10); - if (!(CSR_READ_4(sc, PN_BUSCTL) & PN_BUSCTL_RESET)) - break; - } - if (i == PN_TIMEOUT) - printf("pn%d: reset never completed!\n", sc->pn_unit); - - /* Wait a little while for the chip to get its brains in order. */ - DELAY(1000); - return; -} - -/* - * Probe for a Lite-On PNIC chip. Check the PCI vendor and device - * IDs against our list and return a device name if we find a match. - */ -static int pn_probe(dev) - device_t dev; -{ - struct pn_type *t; - u_int32_t rev; - - t = pn_devs; - - while(t->pn_name != NULL) { - if ((pci_get_vendor(dev) == t->pn_vid) && - (pci_get_device(dev) == t->pn_did)) { - if (t->pn_did == PN_DEVICEID_PNIC) { - rev = pci_read_config(dev, - PN_PCI_REVISION, 4) & 0xFF; - switch(rev & PN_REVMASK) { - case PN_REVID_82C168: - device_set_desc(dev, t->pn_name); - return(0); - break; - case PN_REVID_82C169: - t++; - device_set_desc(dev, t->pn_name); - return(0); - default: - printf("unknown PNIC rev: %x\n", rev); - break; - } - } - device_set_desc(dev, t->pn_name); - return(0); - } - t++; - } - - return(ENXIO); -} - -/* - * Attach the interface. Allocate softc structures, do ifmedia - * setup and ethernet/BPF attach. - */ -static int pn_attach(dev) - device_t dev; -{ - int s, i; - u_char eaddr[ETHER_ADDR_LEN]; - u_int32_t command; - struct pn_softc *sc; - struct ifnet *ifp; - int media = IFM_ETHER|IFM_100_TX|IFM_FDX; - unsigned int round; - caddr_t roundptr; - struct pn_type *p; - u_int16_t phy_vid, phy_did, phy_sts; -#ifdef PN_RX_BUG_WAR - u_int32_t revision = 0; -#endif - int unit, error = 0, rid; - - s = splimp(); - - sc = device_get_softc(dev); - unit = device_get_unit(dev); - bzero(sc, sizeof(struct pn_softc)); - - /* - * Handle power management nonsense. - */ - command = pci_read_config(dev, PN_PCI_CAPID, 4) & 0x000000FF; - if (command == 0x01) { - - command = pci_read_config(dev, PN_PCI_PWRMGMTCTRL, 4); - if (command & PN_PSTATE_MASK) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, PN_PCI_LOIO, 4); - membase = pci_read_config(dev, PN_PCI_LOMEM, 4); - irq = pci_read_config(dev, PN_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("pn%d: chip is in D%d power mode " - "-- setting to D0\n", unit, command & PN_PSTATE_MASK); - command &= 0xFFFFFFFC; - pci_write_config(dev, PN_PCI_PWRMGMTCTRL, command, 4); - - /* Restore PCI config data. */ - pci_write_config(dev, PN_PCI_LOIO, iobase, 4); - pci_write_config(dev, PN_PCI_LOMEM, membase, 4); - pci_write_config(dev, PN_PCI_INTLINE, irq, 4); - } - } - - /* - * Map control/status registers. - */ - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); - command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); - -#ifdef PN_USEIOSPACE - if (!(command & PCIM_CMD_PORTEN)) { - printf("pn%d: failed to enable I/O ports!\n", unit); - error = ENXIO; - goto fail; - } -#else - if (!(command & PCIM_CMD_MEMEN)) { - printf("pn%d: failed to enable memory mapping!\n", unit); - error = ENXIO; - goto fail; - } -#endif - - rid = PN_RID; - sc->pn_res = bus_alloc_resource(dev, PN_RES, &rid, - 0, ~0, 1, RF_ACTIVE); - - if (sc->pn_res == NULL) { - printf ("pn%d: couldn't map ports/memory\n", unit); - error = ENXIO; - goto fail; - } - - sc->pn_btag = rman_get_bustag(sc->pn_res); - sc->pn_bhandle = rman_get_bushandle(sc->pn_res); - - /* Allocate interrupt */ - rid = 0; - sc->pn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, - RF_SHAREABLE | RF_ACTIVE); - - if (sc->pn_irq == NULL) { - printf("pn%d: couldn't map interrupt\n", unit); - bus_release_resource(dev, PN_RES, PN_RID, sc->pn_res); - error = ENXIO; - goto fail; - } - - error = bus_setup_intr(dev, sc->pn_irq, INTR_TYPE_NET, - pn_intr, sc, &sc->pn_intrhand); - - if (error) { - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pn_res); - bus_release_resource(dev, PN_RES, PN_RID, sc->pn_res); - printf("pn%d: couldn't set up irq\n", unit); - goto fail; - } - - /* Save the cache line size. */ - sc->pn_cachesize = pci_read_config(dev, PN_PCI_CACHELEN, 4) & 0xFF; - - /* Reset the adapter. */ - pn_reset(sc); - - /* - * Get station address from the EEPROM. - */ - pn_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); - - /* - * A PNIC chip was detected. Inform the world. - */ - printf("pn%d: Ethernet address: %6D\n", unit, eaddr, ":"); - - sc->pn_unit = unit; - bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); - - sc->pn_ldata_ptr = malloc(sizeof(struct pn_list_data) + 8, - M_DEVBUF, M_NOWAIT); - if (sc->pn_ldata_ptr == NULL) { - printf("pn%d: no memory for list buffers!\n", unit); - bus_teardown_intr(dev, sc->pn_irq, sc->pn_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pn_res); - bus_release_resource(dev, PN_RES, PN_RID, sc->pn_res); - error = ENXIO; - goto fail; - } - - sc->pn_ldata = (struct pn_list_data *)sc->pn_ldata_ptr; - round = (uintptr_t)sc->pn_ldata_ptr & 0xF; - roundptr = sc->pn_ldata_ptr; - for (i = 0; i < 8; i++) { - if (round % 8) { - round++; - roundptr++; - } else - break; - } - sc->pn_ldata = (struct pn_list_data *)roundptr; - bzero(sc->pn_ldata, sizeof(struct pn_list_data)); - -#ifdef PN_RX_BUG_WAR - revision = pci_read_config(dev, PN_PCI_REVISION, 4) & 0x000000FF; - if (revision == PN_169B_REV || revision == PN_169_REV || - (revision & 0xF0) == PN_168_REV) { - sc->pn_rx_war = 1; - sc->pn_rx_buf = malloc(PN_RXLEN * 5, M_DEVBUF, M_NOWAIT); - if (sc->pn_rx_buf == NULL) { - printf("pn%d: no memory for workaround buffer\n", unit); - bus_teardown_intr(dev, sc->pn_irq, sc->pn_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pn_res); - bus_release_resource(dev, PN_RES, PN_RID, sc->pn_res); - free(sc->pn_ldata_ptr, M_DEVBUF); - error = ENXIO; - goto fail; - } - } else { - sc->pn_rx_war = 0; - } -#endif - - ifp = &sc->arpcom.ac_if; - ifp->if_softc = sc; - ifp->if_unit = unit; - ifp->if_name = "pn"; - ifp->if_mtu = ETHERMTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; - ifp->if_ioctl = pn_ioctl; - ifp->if_output = ether_output; - ifp->if_start = pn_start; - ifp->if_watchdog = pn_watchdog; - ifp->if_init = pn_init; - ifp->if_baudrate = 10000000; - ifp->if_snd.ifq_maxlen = PN_TX_LIST_CNT - 1; - - ifmedia_init(&sc->ifmedia, 0, pn_ifmedia_upd, pn_ifmedia_sts); - - if (bootverbose) - printf("pn%d: probing for a PHY\n", sc->pn_unit); - for (i = PN_PHYADDR_MIN; i < PN_PHYADDR_MAX + 1; i++) { - if (bootverbose) - printf("pn%d: checking address: %d\n", - sc->pn_unit, i); - sc->pn_phy_addr = i; - pn_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); - DELAY(500); - while(pn_phy_readreg(sc, PHY_BMCR) - & PHY_BMCR_RESET); - if ((phy_sts = pn_phy_readreg(sc, PHY_BMSR))) - break; - } - if (phy_sts) { - phy_vid = pn_phy_readreg(sc, PHY_VENID); - phy_did = pn_phy_readreg(sc, PHY_DEVID); - if (bootverbose) - printf("pn%d: found PHY at address %d, ", - sc->pn_unit, sc->pn_phy_addr); - if (bootverbose) - printf("vendor id: %x device id: %x\n", - phy_vid, phy_did); - p = pn_phys; - while(p->pn_vid) { - if (phy_vid == p->pn_vid && - (phy_did | 0x000F) == p->pn_did) { - sc->pn_pinfo = p; - break; - } - p++; - } - if (sc->pn_pinfo == NULL) - sc->pn_pinfo = &pn_phys[PHY_UNKNOWN]; - if (bootverbose) - printf("pn%d: PHY type: %s\n", - sc->pn_unit, sc->pn_pinfo->pn_name); - - pn_getmode_mii(sc); - if (cold) { - pn_autoneg_mii(sc, PN_FLAG_FORCEDELAY, 1); - pn_stop(sc); - } else { - pn_init(sc); - pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); - } - } else { - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); - ifmedia_add(&sc->ifmedia, - IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); - ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); - pn_init(sc); - if (cold) { - pn_autoneg(sc, PN_FLAG_FORCEDELAY, 1); - pn_stop(sc); - } else { - pn_init(sc); - pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); - } - } - - media = sc->ifmedia.ifm_media; - ifmedia_set(&sc->ifmedia, media); - - /* - * Call MI attach routines. - */ - if_attach(ifp); - ether_ifattach(ifp); - - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - -fail: - splx(s); - return(error); -} - -static int pn_detach(dev) - device_t dev; -{ - struct pn_softc *sc; - struct ifnet *ifp; - int s; - - s = splimp(); - - sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; - - if_detach(ifp); - pn_stop(sc); - - bus_teardown_intr(dev, sc->pn_irq, sc->pn_intrhand); - bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pn_res); - bus_release_resource(dev, PN_RES, PN_RID, sc->pn_res); - - free(sc->pn_ldata_ptr, M_DEVBUF); -#ifdef PN_RX_BUG_WAR - if (sc->pn_rx_war) - free(sc->pn_rx_buf, M_DEVBUF); -#endif - ifmedia_removeall(&sc->ifmedia); - - splx(s); - - return(0); -} - -/* - * Initialize the transmit descriptors. - */ -static int pn_list_tx_init(sc) - struct pn_softc *sc; -{ - struct pn_chain_data *cd; - struct pn_list_data *ld; - int i; - - cd = &sc->pn_cdata; - ld = sc->pn_ldata; - for (i = 0; i < PN_TX_LIST_CNT; i++) { - cd->pn_tx_chain[i].pn_ptr = &ld->pn_tx_list[i]; - if (i == (PN_TX_LIST_CNT - 1)) - cd->pn_tx_chain[i].pn_nextdesc = - &cd->pn_tx_chain[0]; - else - cd->pn_tx_chain[i].pn_nextdesc = - &cd->pn_tx_chain[i + 1]; - } - - cd->pn_tx_free = &cd->pn_tx_chain[0]; - cd->pn_tx_tail = cd->pn_tx_head = NULL; - - return(0); -} - - -/* - * Initialize the RX descriptors and allocate mbufs for them. Note that - * we arrange the descriptors in a closed ring, so that the last descriptor - * points back to the first. - */ -static int pn_list_rx_init(sc) - struct pn_softc *sc; -{ - struct pn_chain_data *cd; - struct pn_list_data *ld; - int i; - - cd = &sc->pn_cdata; - ld = sc->pn_ldata; - - for (i = 0; i < PN_RX_LIST_CNT; i++) { - cd->pn_rx_chain[i].pn_ptr = - (struct pn_desc *)&ld->pn_rx_list[i]; - if (pn_newbuf(sc, &cd->pn_rx_chain[i], NULL) == ENOBUFS) - return(ENOBUFS); - if (i == (PN_RX_LIST_CNT - 1)) { - cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[0]; - ld->pn_rx_list[i].pn_next = - vtophys(&ld->pn_rx_list[0]); - } else { - cd->pn_rx_chain[i].pn_nextdesc = &cd->pn_rx_chain[i + 1]; - ld->pn_rx_list[i].pn_next = - vtophys(&ld->pn_rx_list[i + 1]); - } - } - - cd->pn_rx_head = &cd->pn_rx_chain[0]; - - return(0); -} - -/* - * Initialize an RX descriptor and attach an MBUF cluster. - * Note: the length fields are only 11 bits wide, which means the - * largest size we can specify is 2047. This is important because - * MCLBYTES is 2048, so we have to subtract one otherwise we'll - * overflow the field and make a mess. - */ -static int pn_newbuf(sc, c, m) - struct pn_softc *sc; - struct pn_chain_onefrag *c; - struct mbuf *m; -{ - struct mbuf *m_new = NULL; - - if (m == NULL) { - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("pn%d: no memory for rx list " - "-- packet dropped!\n", sc->pn_unit); - return(ENOBUFS); - } - - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - printf("pn%d: no memory for rx list -- " - "packet dropped!\n", sc->pn_unit); - m_freem(m_new); - return(ENOBUFS); - } - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - } else { - m_new = m; - m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; - m_new->m_data = m_new->m_ext.ext_buf; - } - - /* - * Leave a couple of empty leading bytes; we'll need them later - * when we fix up things for proper payload alignment. - */ - m_adj(m_new, sizeof(u_int64_t)); - -#ifdef PN_RX_BUG_WAR - /* - * Zero the buffer. This is part of the workaround for the - * promiscuous mode bug in the revision 33 PNIC chips. - */ - if (sc->pn_rx_war) - bzero((char *)mtod(m_new, char *), m_new->m_len); -#endif - - c->pn_mbuf = m_new; - c->pn_ptr->pn_data = vtophys(mtod(m_new, caddr_t)); - c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; - c->pn_ptr->pn_status = PN_RXSTAT; - - return(0); -} - -#ifdef PN_RX_BUG_WAR -/* - * Grrrrr. - * The PNIC chip has a terrible bug in it that manifests itself during - * periods of heavy activity. The exact mode of failure if difficult to - * pinpoint: sometimes it only happens in promiscuous mode, sometimes it - * will happen on slow machines. The bug is that sometimes instead of - * uploading one complete frame during reception, it uploads what looks - * like the entire contents of its FIFO memory. The frame we want is at - * the end of the whole mess, but we never know exactly how much data has - * been uploaded, so salvaging the frame is hard. - * - * There is only one way to do it reliably, and it's disgusting. - * Here's what we know: - * - * - We know there will always be somewhere between one and three extra - * descriptors uploaded. - * - * - We know the desired received frame will always be at the end of the - * total data upload. - * - * - We know the size of the desired received frame because it will be - * provided in the length field of the status word in the last descriptor. - * - * Here's what we do: - * - * - When we allocate buffers for the receive ring, we bzero() them. - * This means that we know that the buffer contents should be all - * zeros, except for data uploaded by the chip. - * - * - We also force the PNIC chip to upload frames that include the - * ethernet CRC at the end. - * - * - We gather all of the bogus frame data into a single buffer. - * - * - We then position a pointer at the end of this buffer and scan - * backwards until we encounter the first non-zero byte of data. - * This is the end of the received frame. We know we will encounter - * some data at the end of the frame because the CRC will always be - * there, so even if the sender transmits a packet of all zeros, - * we won't be fooled. - * - * - We know the size of the actual received frame, so we subtract - * that value from the current pointer location. This brings us - * to the start of the actual received packet. - * - * - We copy this into an mbuf and pass it on, along with the actual - * frame length. - * - * The performance hit is tremendous, but it beats dropping frames all - * the time. - */ - -#define PN_WHOLEFRAME (PN_RXSTAT_FIRSTFRAG|PN_RXSTAT_LASTFRAG) -static void pn_rx_bug_war(sc, cur_rx) - struct pn_softc *sc; - struct pn_chain_onefrag *cur_rx; -{ - struct pn_chain_onefrag *c; - unsigned char *ptr; - int total_len; - u_int32_t rxstat = 0; - - c = sc->pn_rx_bug_save; - ptr = sc->pn_rx_buf; - bzero(ptr, sizeof(PN_RXLEN * 5)); - - /* Copy all the bytes from the bogus buffers. */ - while ((c->pn_ptr->pn_status & PN_WHOLEFRAME) != PN_WHOLEFRAME) { - rxstat = c->pn_ptr->pn_status; - m_copydata(c->pn_mbuf, 0, PN_RXLEN, ptr); - ptr += PN_RXLEN; - if (c == cur_rx) - break; - if (rxstat & PN_RXSTAT_LASTFRAG) - break; - c->pn_ptr->pn_status = PN_RXSTAT; - c->pn_ptr->pn_ctl = PN_RXCTL_RLINK | PN_RXLEN; - bzero((char *)mtod(c->pn_mbuf, char *), MCLBYTES); - c = c->pn_nextdesc; - } - - /* Find the length of the actual receive frame. */ - total_len = PN_RXBYTES(rxstat); - - /* Scan backwards until we hit a non-zero byte. */ - while(*ptr == 0x00) - ptr--; - - /* Round off. */ - if ((uintptr_t)(ptr) & 0x3) - ptr -= 1; - - /* Now find the start of the frame. */ - ptr -= total_len; - if (ptr < sc->pn_rx_buf) - ptr = sc->pn_rx_buf; - - /* - * Now copy the salvaged frame to the last mbuf and fake up - * the status word to make it look like a successful - * frame reception. - */ - m_copyback(cur_rx->pn_mbuf, 0, total_len, ptr); - cur_rx->pn_mbuf->m_len = c->pn_mbuf->m_pkthdr.len = MCLBYTES; - cur_rx->pn_ptr->pn_status |= PN_RXSTAT_FIRSTFRAG; - - return; -} -#endif - -/* - * A frame has been uploaded: pass the resulting mbuf chain up to - * the higher level protocols. - */ -static void pn_rxeof(sc) - struct pn_softc *sc; -{ - struct ether_header *eh; - struct mbuf *m; - struct ifnet *ifp; - struct pn_chain_onefrag *cur_rx; - int total_len = 0; - u_int32_t rxstat; - - ifp = &sc->arpcom.ac_if; - - while(!((rxstat = sc->pn_cdata.pn_rx_head->pn_ptr->pn_status) & - PN_RXSTAT_OWN)) { - struct mbuf *m0 = NULL; - - cur_rx = sc->pn_cdata.pn_rx_head; - sc->pn_cdata.pn_rx_head = cur_rx->pn_nextdesc; - -#ifdef PN_RX_BUG_WAR - /* - * XXX The PNIC has a nasty receiver bug that manifests - * under certain conditions (sometimes only in promiscuous - * mode, sometimes only on slow machines even when not in - * promiscuous mode). We have to keep an eye out for the - * failure condition and employ a workaround to recover - * any mangled frames. - */ - if (sc->pn_rx_war) { - if ((rxstat & PN_WHOLEFRAME) != PN_WHOLEFRAME) { - if (rxstat & PN_RXSTAT_FIRSTFRAG) - sc->pn_rx_bug_save = cur_rx; - if ((rxstat & PN_RXSTAT_LASTFRAG) == 0) - continue; - pn_rx_bug_war(sc, cur_rx); - rxstat = cur_rx->pn_ptr->pn_status; - } - } -#endif - - /* - * If an error occurs, update stats, clear the - * status word and leave the mbuf cluster in place: - * it should simply get re-used next time this descriptor - * comes up in the ring. - */ - if (rxstat & PN_RXSTAT_RXERR) { - ifp->if_ierrors++; - if (rxstat & PN_RXSTAT_COLLSEEN) - ifp->if_collisions++; - pn_newbuf(sc, cur_rx, cur_rx->pn_mbuf); - continue; - } - - /* No errors; receive the packet. */ - m = cur_rx->pn_mbuf; - total_len = PN_RXBYTES(cur_rx->pn_ptr->pn_status); - - /* Trim off the CRC. */ - total_len -= ETHER_CRC_LEN; - - m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, - total_len + ETHER_ALIGN, 0, ifp, NULL); - pn_newbuf(sc, cur_rx, m); - if (m0 == NULL) { - ifp->if_ierrors++; - continue; - } - m_adj(m0, ETHER_ALIGN); - m = m0; - ifp->if_ipackets++; - eh = mtod(m, struct ether_header *); - -#ifdef BRIDGE - if (do_bridge) { - struct ifnet *bdg_ifp; - bdg_ifp = bridge_in(m); - if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_DROP) - bdg_forward(&m, bdg_ifp); - if (((bdg_ifp != BDG_LOCAL) && (bdg_ifp != BDG_BCAST) && - (bdg_ifp != BDG_MCAST)) || bdg_ifp == BDG_DROP) { - m_freem(m); - continue; - } - } -#endif - - /* - * Handle BPF listeners. Let the BPF user see the packet, but - * don't pass it up to the ether_input() layer unless it's - * a broadcast packet, multicast packet, matches our ethernet - * address or the interface is in promiscuous mode. - */ - if (ifp->if_bpf) { - bpf_mtap(ifp, m); - if (ifp->if_flags & IFF_PROMISC && - (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - ETHER_ADDR_LEN) && - (eh->ether_dhost[0] & 1) == 0)) { - m_freem(m); - continue; - } - } - - /* Remove header from mbuf and pass it on. */ - m_adj(m, sizeof(struct ether_header)); - ether_input(ifp, eh, m); - } - - return; -} - -void pn_rxeoc(sc) - struct pn_softc *sc; -{ - - pn_rxeof(sc); - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON); - CSR_WRITE_4(sc, PN_RXADDR, vtophys(sc->pn_cdata.pn_rx_head->pn_ptr)); - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_RX_ON); - CSR_WRITE_4(sc, PN_RXSTART, 0xFFFFFFFF); - - return; -} - -/* - * A frame was downloaded to the chip. It's safe for us to clean up - * the list buffers. - */ - -static void pn_txeof(sc) - struct pn_softc *sc; -{ - struct pn_chain *cur_tx; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - /* Clear the timeout timer. */ - ifp->if_timer = 0; - - if (sc->pn_cdata.pn_tx_head == NULL) - return; - - /* - * Go through our tx list and free mbufs for those - * frames that have been transmitted. - */ - while(sc->pn_cdata.pn_tx_head->pn_mbuf != NULL) { - u_int32_t txstat; - - cur_tx = sc->pn_cdata.pn_tx_head; - txstat = PN_TXSTATUS(cur_tx); - - if (txstat & PN_TXSTAT_OWN) - break; - - if (txstat & PN_TXSTAT_ERRSUM) { - ifp->if_oerrors++; - if (txstat & PN_TXSTAT_EXCESSCOLL) - ifp->if_collisions++; - if (txstat & PN_TXSTAT_LATECOLL) - ifp->if_collisions++; - } - - ifp->if_collisions += (txstat & PN_TXSTAT_COLLCNT) >> 3; - - - ifp->if_opackets++; - m_freem(cur_tx->pn_mbuf); - cur_tx->pn_mbuf = NULL; - - if (sc->pn_cdata.pn_tx_head == sc->pn_cdata.pn_tx_tail) { - sc->pn_cdata.pn_tx_head = NULL; - sc->pn_cdata.pn_tx_tail = NULL; - break; - } - - sc->pn_cdata.pn_tx_head = cur_tx->pn_nextdesc; - } - - return; -} - -/* - * TX 'end of channel' interrupt handler. - */ -static void pn_txeoc(sc) - struct pn_softc *sc; -{ - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - ifp->if_timer = 0; - - if (sc->pn_cdata.pn_tx_head == NULL) { - ifp->if_flags &= ~IFF_OACTIVE; - sc->pn_cdata.pn_tx_tail = NULL; - if (sc->pn_want_auto) { - if (sc->pn_pinfo == NULL) - pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); - else - pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); - } - - } - - return; -} - -static void pn_intr(arg) - void *arg; -{ - struct pn_softc *sc; - struct ifnet *ifp; - u_int32_t status; - - sc = arg; - ifp = &sc->arpcom.ac_if; - - /* Supress unwanted interrupts. */ - if (!(ifp->if_flags & IFF_UP)) { - pn_stop(sc); - return; - } - - /* Disable interrupts. */ - CSR_WRITE_4(sc, PN_IMR, 0x00000000); - - for (;;) { - status = CSR_READ_4(sc, PN_ISR); - if (status) - CSR_WRITE_4(sc, PN_ISR, status); - - if ((status & PN_INTRS) == 0) - break; - - if (status & PN_ISR_RX_OK) - pn_rxeof(sc); - - if ((status & PN_ISR_RX_WATCHDOG) || (status & PN_ISR_RX_IDLE) - || (status & PN_ISR_RX_NOBUF)) - pn_rxeoc(sc); - - if (status & PN_ISR_TX_OK) - pn_txeof(sc); - - if (status & PN_ISR_TX_NOBUF) - pn_txeoc(sc); - - if (status & PN_ISR_TX_IDLE) { - pn_txeof(sc); - if (sc->pn_cdata.pn_tx_head != NULL) { - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); - CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); - } - } - - if (status & PN_ISR_TX_UNDERRUN) { - ifp->if_oerrors++; - pn_txeof(sc); - if (sc->pn_cdata.pn_tx_head != NULL) { - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON); - CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); - } - } - - if (status & PN_ISR_BUS_ERR) { - pn_reset(sc); - pn_init(sc); - } - } - - /* Re-enable interrupts. */ - CSR_WRITE_4(sc, PN_IMR, PN_INTRS); - - if (ifp->if_snd.ifq_head != NULL) { - pn_start(ifp); - } - - return; -} - -/* - * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data - * pointers to the fragment pointers. - */ -static int pn_encap(sc, c, m_head) - struct pn_softc *sc; - struct pn_chain *c; - struct mbuf *m_head; -{ - int frag = 0; - struct pn_desc *f = NULL; - int total_len; - struct mbuf *m; - - /* - * Start packing the mbufs in this chain into - * the fragment pointers. Stop when we run out - * of fragments or hit the end of the mbuf chain. - */ - m = m_head; - total_len = 0; - - for (m = m_head, frag = 0; m != NULL; m = m->m_next) { - if (m->m_len != 0) { - if (frag == PN_MAXFRAGS) - break; - total_len += m->m_len; - f = &c->pn_ptr->pn_frag[frag]; - f->pn_ctl = PN_TXCTL_TLINK | m->m_len; - if (frag == 0) { - f->pn_ctl |= PN_TXCTL_FIRSTFRAG; - f->pn_status = 0; - } else - f->pn_status = PN_TXSTAT_OWN; - f->pn_data = vtophys(mtod(m, vm_offset_t)); - f->pn_next = vtophys(&c->pn_ptr->pn_frag[frag + 1]); - frag++; - } - } - - /* - * Handle special case: we used up all 16 fragments, - * but we have more mbufs left in the chain. Copy the - * data into an mbuf cluster. Note that we don't - * bother clearing the values in the other fragment - * pointers/counters; it wouldn't gain us anything, - * and would waste cycles. - */ - if (m != NULL) { - struct mbuf *m_new = NULL; - - MGETHDR(m_new, M_DONTWAIT, MT_DATA); - if (m_new == NULL) { - printf("pn%d: no memory for tx list", sc->pn_unit); - return(1); - } - if (m_head->m_pkthdr.len > MHLEN) { - MCLGET(m_new, M_DONTWAIT); - if (!(m_new->m_flags & M_EXT)) { - m_freem(m_new); - printf("pn%d: no memory for tx list", - sc->pn_unit); - return(1); - } - } - m_copydata(m_head, 0, m_head->m_pkthdr.len, - mtod(m_new, caddr_t)); - m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; - m_freem(m_head); - m_head = m_new; - f = &c->pn_ptr->pn_frag[0]; - f->pn_data = vtophys(mtod(m_new, caddr_t)); - f->pn_ctl = total_len = m_new->m_len; - f->pn_ctl |= PN_TXCTL_TLINK|PN_TXCTL_FIRSTFRAG; - frag = 1; - } - - - c->pn_mbuf = m_head; - c->pn_lastdesc = frag - 1; - PN_TXCTL(c) |= PN_TXCTL_LASTFRAG|PN_TXCTL_FINT; - PN_TXNEXT(c) = vtophys(&c->pn_nextdesc->pn_ptr->pn_frag[0]); - - return(0); -} - -/* - * Main transmit routine. To avoid having to do mbuf copies, we put pointers - * to the mbuf data regions directly in the transmit lists. We also save a - * copy of the pointers since the transmit list fragment pointers are - * physical addresses. - */ - -static void pn_start(ifp) - struct ifnet *ifp; -{ - struct pn_softc *sc; - struct mbuf *m_head = NULL; - struct pn_chain *cur_tx = NULL, *start_tx; - - sc = ifp->if_softc; - - if (sc->pn_autoneg) { - sc->pn_tx_pend = 1; - return; - } - - /* - * Check for an available queue slot. If there are none, - * punt. - */ - if (sc->pn_cdata.pn_tx_free->pn_mbuf != NULL) { - ifp->if_flags |= IFF_OACTIVE; - return; - } - - start_tx = sc->pn_cdata.pn_tx_free; - - while(sc->pn_cdata.pn_tx_free->pn_mbuf == NULL) { - IF_DEQUEUE(&ifp->if_snd, m_head); - if (m_head == NULL) - break; - - /* Pick a descriptor off the free list. */ - cur_tx = sc->pn_cdata.pn_tx_free; - sc->pn_cdata.pn_tx_free = cur_tx->pn_nextdesc; - - /* Pack the data into the descriptor. */ - pn_encap(sc, cur_tx, m_head); - - if (cur_tx != start_tx) - PN_TXOWN(cur_tx) = PN_TXSTAT_OWN; - - /* - * If there's a BPF listener, bounce a copy of this frame - * to him. - */ - if (ifp->if_bpf) - bpf_mtap(ifp, cur_tx->pn_mbuf); - - PN_TXOWN(cur_tx) = PN_TXSTAT_OWN; - CSR_WRITE_4(sc, PN_TXSTART, 0xFFFFFFFF); - } - - /* - * If there are no packets queued, bail. - */ - if (cur_tx == NULL) - return; - - sc->pn_cdata.pn_tx_tail = cur_tx; - - if (sc->pn_cdata.pn_tx_head == NULL) - sc->pn_cdata.pn_tx_head = start_tx; - - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; - - return; -} - -static void pn_init(xsc) - void *xsc; -{ - struct pn_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - u_int16_t phy_bmcr = 0; - int s; - - if (sc->pn_autoneg) - return; - - s = splimp(); - - if (sc->pn_pinfo != NULL) - phy_bmcr = pn_phy_readreg(sc, PHY_BMCR); - - /* - * Cancel pending I/O and free all RX/TX buffers. - */ - pn_stop(sc); - pn_reset(sc); - - /* - * Set cache alignment and burst length. - */ - CSR_WRITE_4(sc, PN_BUSCTL, PN_BUSCTL_MUSTBEONE|PN_BUSCTL_ARBITRATION); - PN_SETBIT(sc, PN_BUSCTL, PN_BURSTLEN_16LONG); - switch(sc->pn_cachesize) { - case 32: - PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_32LONG); - break; - case 16: - PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_16LONG); - break; - case 8: - PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_8LONG); - break; - case 0: - default: - PN_SETBIT(sc, PN_BUSCTL, PN_CACHEALIGN_NONE); - break; - } - - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_IMMEDIATE); - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_NO_RXCRC); - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_HEARTBEAT); - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_STORENFWD); - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_BACKOFF); - - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_TX_THRESH); - PN_SETBIT(sc, PN_NETCFG, PN_TXTHRESH_72BYTES); - - if (sc->pn_pinfo == NULL) { - PN_CLRBIT(sc, PN_NETCFG, PN_NETCFG_MIIENB); - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_BACKOFF); - } else { - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_MIIENB); - PN_SETBIT(sc, PN_ENDEC, PN_ENDEC_JABBERDIS); - } - - pn_setcfg(sc, sc->ifmedia.ifm_media); - - /* Init circular RX list. */ - if (pn_list_rx_init(sc) == ENOBUFS) { - printf("pn%d: initialization failed: no " - "memory for rx buffers\n", sc->pn_unit); - pn_stop(sc); - (void)splx(s); - return; - } - - /* - * Init tx descriptors. - */ - pn_list_tx_init(sc); - - /* - * Load the address of the RX list. - */ - CSR_WRITE_4(sc, PN_RXADDR, vtophys(sc->pn_cdata.pn_rx_head->pn_ptr)); - - /* - * Load the RX/multicast filter. - */ - pn_setfilt(sc); - - /* - * Enable interrupts. - */ - CSR_WRITE_4(sc, PN_IMR, PN_INTRS); - CSR_WRITE_4(sc, PN_ISR, 0xFFFFFFFF); - - /* Enable receiver and transmitter. */ - PN_SETBIT(sc, PN_NETCFG, PN_NETCFG_TX_ON|PN_NETCFG_RX_ON); - CSR_WRITE_4(sc, PN_RXSTART, 0xFFFFFFFF); - - /* Restore state of BMCR */ - if (sc->pn_pinfo != NULL) - pn_phy_writereg(sc, PHY_BMCR, phy_bmcr); - - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; - - (void)splx(s); - - return; -} - -/* - * Set media options. - */ -static int pn_ifmedia_upd(ifp) - struct ifnet *ifp; -{ - struct pn_softc *sc; - struct ifmedia *ifm; - - sc = ifp->if_softc; - ifm = &sc->ifmedia; - - if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) - return(EINVAL); - - if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { - if (sc->pn_pinfo == NULL) - pn_autoneg(sc, PN_FLAG_SCHEDDELAY, 1); - else - pn_autoneg_mii(sc, PN_FLAG_SCHEDDELAY, 1); - } else { - if (sc->pn_pinfo == NULL) - pn_setmode(sc, ifm->ifm_media); - else - pn_setmode_mii(sc, ifm->ifm_media); - } - - return(0); -} - -/* - * Report current media status. - */ -static void pn_ifmedia_sts(ifp, ifmr) - struct ifnet *ifp; - struct ifmediareq *ifmr; -{ - struct pn_softc *sc; - u_int16_t advert = 0, ability = 0; - - sc = ifp->if_softc; - - ifmr->ifm_active = IFM_ETHER; - - if (sc->pn_pinfo == NULL) { - if (CSR_READ_4(sc, PN_NETCFG) & PN_NETCFG_SPEEDSEL) - ifmr->ifm_active = IFM_ETHER|IFM_10_T; - else - ifmr->ifm_active = IFM_ETHER|IFM_100_TX; - if (CSR_READ_4(sc, PN_NETCFG) & PN_NETCFG_FULLDUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_HDX; - return; - } - - if (!(pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { - if (pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL) - ifmr->ifm_active = IFM_ETHER|IFM_100_TX; - else - ifmr->ifm_active = IFM_ETHER|IFM_10_T; - if (pn_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX) - ifmr->ifm_active |= IFM_FDX; - else - ifmr->ifm_active |= IFM_HDX; - return; - } - - ability = pn_phy_readreg(sc, PHY_LPAR); - advert = pn_phy_readreg(sc, PHY_ANAR); - if (advert & PHY_ANAR_100BT4 && - ability & PHY_ANAR_100BT4) { - ifmr->ifm_active = IFM_ETHER|IFM_100_T4; - } else if (advert & PHY_ANAR_100BTXFULL && - ability & PHY_ANAR_100BTXFULL) { - ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_FDX; - } else if (advert & PHY_ANAR_100BTXHALF && - ability & PHY_ANAR_100BTXHALF) { - ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_HDX; - } else if (advert & PHY_ANAR_10BTFULL && - ability & PHY_ANAR_10BTFULL) { - ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_FDX; - } else if (advert & PHY_ANAR_10BTHALF && - ability & PHY_ANAR_10BTHALF) { - ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_HDX; - } - - return; -} - -static int pn_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; -{ - struct pn_softc *sc = ifp->if_softc; - struct ifreq *ifr = (struct ifreq *) data; - int s, error = 0; - - s = splimp(); - - switch(command) { - case SIOCSIFADDR: - case SIOCGIFADDR: - case SIOCSIFMTU: - error = ether_ioctl(ifp, command, data); - break; - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - pn_init(sc); - } else { - if (ifp->if_flags & IFF_RUNNING) - pn_stop(sc); - } - error = 0; - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - pn_init(sc); - error = 0; - break; - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); - break; - default: - error = EINVAL; - break; - } - - (void)splx(s); - - return(error); -} - -static void pn_watchdog(ifp) - struct ifnet *ifp; -{ - struct pn_softc *sc; - - sc = ifp->if_softc; - - if (sc->pn_autoneg) { - if (sc->pn_pinfo == NULL) - pn_autoneg(sc, PN_FLAG_DELAYTIMEO, 1); - else - pn_autoneg_mii(sc, PN_FLAG_DELAYTIMEO, 1); - if (!(ifp->if_flags & IFF_UP)) - pn_stop(sc); - return; - } - - ifp->if_oerrors++; - printf("pn%d: watchdog timeout\n", sc->pn_unit); - - if (!(pn_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) - printf("pn%d: no carrier - transceiver cable problem?\n", - sc->pn_unit); - pn_stop(sc); - pn_reset(sc); - pn_init(sc); - - if (ifp->if_snd.ifq_head != NULL) - pn_start(ifp); - - return; -} - -/* - * Stop the adapter and free any mbufs allocated to the - * RX and TX lists. - */ -static void pn_stop(sc) - struct pn_softc *sc; -{ - register int i; - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - ifp->if_timer = 0; - - PN_CLRBIT(sc, PN_NETCFG, (PN_NETCFG_RX_ON|PN_NETCFG_TX_ON)); - CSR_WRITE_4(sc, PN_IMR, 0x00000000); - CSR_WRITE_4(sc, PN_TXADDR, 0x00000000); - CSR_WRITE_4(sc, PN_RXADDR, 0x00000000); - - /* - * Free data in the RX lists. - */ - for (i = 0; i < PN_RX_LIST_CNT; i++) { - if (sc->pn_cdata.pn_rx_chain[i].pn_mbuf != NULL) { - m_freem(sc->pn_cdata.pn_rx_chain[i].pn_mbuf); - sc->pn_cdata.pn_rx_chain[i].pn_mbuf = NULL; - } - } - bzero((char *)&sc->pn_ldata->pn_rx_list, - sizeof(sc->pn_ldata->pn_rx_list)); - - /* - * Free the TX list buffers. - */ - for (i = 0; i < PN_TX_LIST_CNT; i++) { - if (sc->pn_cdata.pn_tx_chain[i].pn_mbuf != NULL) { - m_freem(sc->pn_cdata.pn_tx_chain[i].pn_mbuf); - sc->pn_cdata.pn_tx_chain[i].pn_mbuf = NULL; - } - } - - bzero((char *)&sc->pn_ldata->pn_tx_list, - sizeof(sc->pn_ldata->pn_tx_list)); - - ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); - - return; -} - -/* - * Stop all chip I/O so that the kernel's probe routines don't - * get confused by errant DMAs when rebooting. - */ -static void pn_shutdown(dev) - device_t dev; -{ - struct pn_softc *sc; - - sc = device_get_softc(dev); - - pn_stop(sc); - - return; -} diff --git a/sys/pci/if_pnreg.h b/sys/pci/if_pnreg.h deleted file mode 100644 index 0c567d0..0000000 --- a/sys/pci/if_pnreg.h +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (c) 1997, 1998 - * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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 Bill Paul. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD - * 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. - * - * $FreeBSD$ - */ - -/* - * PNIC register definitions. - */ - -#define PN_BUSCTL 0x00 /* bus control */ -#define PN_TXSTART 0x08 /* tx start demand */ -#define PN_RXSTART 0x10 /* rx start demand */ -#define PN_RXADDR 0x18 /* rx descriptor list start addr */ -#define PN_TXADDR 0x20 /* tx descriptor list start addr */ -#define PN_ISR 0x28 /* interrupt status register */ -#define PN_NETCFG 0x30 /* network config register */ -#define PN_IMR 0x38 /* interrupt mask */ -#define PN_FRAMESDISCARDED 0x40 /* # of discarded frames */ -#define PN_SIO 0x48 /* MII and ROM/EEPROM access */ -#define PN_GEN 0x60 /* general purpose register */ -#define PN_ENDEC 0x78 /* ENDEC general register */ -#define PN_SIOPWR 0x90 /* serial eeprom power up */ -#define PN_SIOCTL 0x98 /* EEPROM control register */ -#define PN_MII 0xA0 /* MII access register */ -#define PN_NWAY 0xB8 /* Internal NWAY register */ - -/* - * Bus control bits. - */ -#define PN_BUSCTL_RESET 0x00000001 -#define PN_BUSCTL_ARBITRATION 0x00000002 -#define PN_BUSCTL_SKIPLEN 0x0000007C -#define PN_BUSCTL_BUF_BIGENDIAN 0x00000080 -#define PN_BUSCTL_BURSTLEN 0x00003F00 -#define PN_BUSCTL_CACHEALIGN 0x0000C000 -#define PN_BUSCTL_TXPOLL 0x000E0000 -#define PN_BUSCTL_MUSTBEONE 0x04000000 - -#define PN_SKIPLEN_1LONG 0x00000004 -#define PN_SKIPLEN_2LONG 0x00000008 -#define PN_SKIPLEN_3LONG 0x00000010 -#define PN_SKIPLEN_4LONG 0x00000020 -#define PN_SKIPLEN_5LONG 0x00000040 - -#define PN_CACHEALIGN_NONE 0x00000000 -#define PN_CACHEALIGN_8LONG 0x00004000 -#define PN_CACHEALIGN_16LONG 0x00008000 -#define PN_CACHEALIGN_32LONG 0x0000C000 - -#define PN_BURSTLEN_USECA 0x00000000 -#define PN_BURSTLEN_1LONG 0x00000100 -#define PN_BURSTLEN_2LONG 0x00000200 -#define PN_BURSTLEN_4LONG 0x00000400 -#define PN_BURSTLEN_8LONG 0x00000800 -#define PN_BURSTLEN_16LONG 0x00001000 -#define PN_BURSTLEN_32LONG 0x00002000 - -#define PN_TXPOLL_OFF 0x00000000 -#define PN_TXPOLL_200U 0x00020000 -#define PN_TXPOLL_800U 0x00040000 -#define PN_TXPOLL_1600U 0x00060000 -#define PN_TXPOLL_12_8M 0x00080000 -#define PN_TXPOLL_25_6M 0x000A0000 -#define PN_TXPOLL_51_2M 0x000C0000 -#define PN_TXPOLL_102_4M 0x000E0000 - -#define PN_BUSCTL_CONFIG \ - (PN_CACHEALIGN_8LONG|PN_BURSTLEN_8LONG) - -/* - * Interrupt status bits. - */ -#define PN_ISR_TX_OK 0x00000001 /* packet tx ok */ -#define PN_ISR_TX_IDLE 0x00000002 /* tx stopped */ -#define PN_ISR_TX_NOBUF 0x00000004 /* no tx buffer available */ -#define PN_ISR_TX_JABTIMEO 0x00000008 /* jabber timeout */ -#define PN_ISR_LINKPASS 0x00000010 /* link test pass */ -#define PN_ISR_TX_UNDERRUN 0x00000020 /* transmit underrun */ -#define PN_ISR_RX_OK 0x00000040 /* packet rx ok */ -#define PN_ISR_RX_NOBUF 0x00000080 /* rx buffer unavailable */ -#define PN_ISR_RX_IDLE 0x00000100 /* rx stopped */ -#define PN_ISR_RX_WATCHDOG 0x00000200 /* rx watchdog timeo */ -#define PN_ISR_TX_EARLY 0x00000400 /* rx watchdog timeo */ -#define PN_ISR_LINKFAIL 0x00001000 -#define PN_ISR_BUS_ERR 0x00002000 -#define PN_ISR_ABNORMAL 0x00008000 -#define PN_ISR_NORMAL 0x00010000 -#define PN_ISR_RX_STATE 0x000E0000 -#define PN_ISR_TX_STATE 0x00700000 -#define PN_ISR_BUSERRTYPE 0x03800000 -#define PN_ISR_TXABORT 0x04000000 /* tx abort */ - -#define PN_RXSTATE_STOPPED 0x00000000 /* 000 - Stopped */ -#define PN_RXSTATE_FETCH 0x00020000 /* 001 - Fetching descriptor */ -#define PN_RXSTATE_ENDCHECK 0x00040000 /* 010 - check for rx end */ -#define PN_RXSTATE_WAIT 0x00060000 /* 011 - waiting for packet */ -#define PN_RXSTATE_SUSPEND 0x00080000 /* 100 - suspend rx */ -#define PN_RXSTATE_CLOSE 0x000A0000 /* 101 - close rx desc */ -#define PN_RXSTATE_FLUSH 0x000C0000 /* 110 - flush from FIFO */ -#define PN_RXSTATE_DEQUEUE 0x000E0000 /* 111 - dequeue from FIFO */ - -#define PN_TXSTATE_RESET 0x00000000 /* 000 - reset */ -#define PN_TXSTATE_FETCH 0x00100000 /* 001 - fetching descriptor */ -#define PN_TXSTATE_WAITEND 0x00200000 /* 010 - wait for tx end */ -#define PN_TXSTATE_READING 0x00300000 /* 011 - read and enqueue */ -#define PN_TXSTATE_RSVD 0x00400000 /* 100 - reserved */ -#define PN_TXSTATE_SETUP 0x00500000 /* 101 - setup packet */ -#define PN_TXSTATE_SUSPEND 0x00600000 /* 110 - suspend tx */ -#define PN_TXSTATE_CLOSE 0x00700000 /* 111 - close tx desc */ - -#define PN_BUSERR_PARITY 0x00000000 -#define PN_BUSERR_MASTABRT 0x00800000 -#define PN_BUSERR_TGTABRT 0x01000000 -#define PN_BUSERR_RSVD1 0x01800000 -#define PN_BUSERR_RSVD2 0x02000000 - -/* - * Network config bits. - */ -#define PN_NETCFG_HASHPERF 0x00000001 /* 0 == perf, 1 == hash */ -#define PN_NETCFG_RX_ON 0x00000002 -#define PN_NETCFG_HASHONLY 0x00000004 /* 1 == allhash */ -#define PN_NETCFG_RX_PASSERR 0x00000008 -#define PN_NETCFG_INVERSFILT 0x00000010 -#define PN_NETCFG_BACKOFF 0x00000020 -#define PN_NETCFG_RX_PROMISC 0x00000040 -#define PN_NETCFG_RX_ALLMULTI 0x00000080 -#define PN_NETCFG_FLAKYOSC 0x00000100 -#define PN_NETCFG_FULLDUPLEX 0x00000200 -#define PN_NETCFG_OPERMODE 0x00000C00 -#define PN_NETCFG_FORCECOLL 0x00001000 -#define PN_NETCFG_TX_ON 0x00002000 -#define PN_NETCFG_TX_THRESH 0x0000C000 -#define PN_NETCFG_TX_BACKOFF 0x00020000 -#define PN_NETCFG_MIIENB 0x00040000 /* 1 == MII, 0 == internal */ -#define PN_NETCFG_HEARTBEAT 0x00080000 /* 1 == disabled */ -#define PN_NETCFG_TX_IMMEDIATE 0x00100000 -#define PN_NETCFG_STORENFWD 0x00200000 -#define PN_NETCFG_SPEEDSEL 0x00400000 /* 1 == 10Mbps 0 == 100Mbps */ -#define PN_NETCFG_PCS 0x00800000 /* 1 == 100baseTX */ -#define PN_NETCFG_SCRAMBLER 0x01000000 -#define PN_NETCFG_NO_RXCRC 0x20000000 -#define PN_NETCFG_EXT_ENDEC 0x40000000 /* 1 == ext, 0 == int PHY */ - -#define PN_OPMODE_NORM 0x00000000 -#define PN_OPMODE_INTLOOP 0x00000400 -#define PN_OPMODE_EXTLOOP 0x00000800 - -#define PN_TXTHRESH_72BYTES 0x00000000 -#define PN_TXTHRESH_96BYTES 0x00004000 -#define PN_TXTHRESH_128BYTES 0x00008000 -#define PN_TXTHRESH_160BYTES 0x0000C000 - -/* - * Interrupt mask bits. - */ -#define PN_IMR_TX_OK 0x00000001 /* packet tx ok */ -#define PN_IMR_TX_IDLE 0x00000002 /* tx stopped */ -#define PN_IMR_TX_NOBUF 0x00000004 /* no tx buffer available */ -#define PN_IMR_TX_JABTIMEO 0x00000008 /* jabber timeout */ -#define PN_IMR_LINKPASS 0x00000010 /* link test pass */ -#define PN_IMR_TX_UNDERRUN 0x00000020 /* transmit underrun */ -#define PN_IMR_RX_OK 0x00000040 /* packet rx ok */ -#define PN_IMR_RX_NOBUF 0x00000080 /* rx buffer unavailable */ -#define PN_IMR_RX_IDLE 0x00000100 /* rx stopped */ -#define PN_IMR_RX_WATCHDOG 0x00000200 /* rx watchdog timeo */ -#define PN_IMR_TX_EARLY 0x00000400 /* rx watchdog timeo */ -#define PN_IMR_BUS_ERR 0x00002000 -#define PN_IMR_ABNORMAL 0x00008000 -#define PN_IMR_NORMAL 0x00010000 -#define PN_ISR_TXABORT 0x04000000 /* tx abort */ - -#define PN_INTRS \ - (PN_IMR_RX_OK|PN_IMR_TX_OK|PN_IMR_RX_NOBUF| \ - PN_IMR_TX_NOBUF|PN_IMR_TX_UNDERRUN|PN_IMR_BUS_ERR| \ - PN_IMR_ABNORMAL|PN_IMR_NORMAL) - -/* - * Serial I/O (EEPROM/ROM) bits. - */ -#define PN_SIO_DATA 0x0000003F -#define PN_SIO_OPCODE 0x00000300 -#define PN_SIO_BUSY 0x80000000 - -/* - * SIOCTL/EEPROM bits - */ -#define PN_EE_READ 0x600 - -/* - * General purpose register bits. - */ -#define PN_GEN_CTL 0x000000F0 -#define PN_GEN_100TX_LINK 0x00000008 -#define PN_GEN_BNC_ENB 0x00000004 -#define PN_GEN_100TX_LOOP 0x00000002 /* 1 == normal, 0 == loop */ -#define PN_GEN_SPEEDSEL 0x00000001 /* 1 == 100Mbps, 0 == 10Mbps */ -#define PN_GEN_MUSTBEONE 0x00000030 - -/* - * General ENDEC bits. - */ -#define PN_ENDEC_JABBERDIS 0x000000001 /* 1 == disable, 0 == enable */ - -/* - * MII bits. - */ -#define PN_MII_DATA 0x0000FFFF -#define PN_MII_REGADDR 0x007C0000 -#define PN_MII_PHYADDR 0x0F800000 -#define PN_MII_OPCODE 0x30000000 -#define PN_MII_RESERVED 0x00020000 -#define PN_MII_BUSY 0x80000000 - -#define PN_MII_READ 0x60020000 /* read PHY command */ -#define PN_MII_WRITE 0x50020000 /* write PHY command */ - -/* - * Internal PHY NWAY register bits. - */ -#define PN_NWAY_RESET 0x00000001 /* reset */ -#define PN_NWAY_PDOWN 0x00000002 /* power down */ -#define PN_NWAY_BYPASS 0x00000004 /* bypass */ -#define PN_NWAY_AUILOWCUR 0x00000008 /* AUI low current */ -#define PN_NWAY_TPEXTEND 0x00000010 /* low squelch voltage */ -#define PN_NWAY_POLARITY 0x00000020 /* 0 == on, 1 == off */ -#define PN_NWAY_TP 0x00000040 /* 1 == tp, 0 == AUI */ -#define PN_NWAY_AUIVOLT 0x00000080 /* 1 == full, 0 == half */ -#define PN_NWAY_DUPLEX 0x00000100 /* 1 == full, 0 == half */ -#define PN_NWAY_LINKTEST 0x00000200 /* 0 == on, 1 == off */ -#define PN_NWAY_AUTODETECT 0x00000400 /* 1 == off, 0 == on */ -#define PN_NWAY_SPEEDSEL 0x00000800 /* 0 == 10, 1 == 100 */ -#define PN_NWAY_NWAY_ENB 0x00001000 /* 0 == off, 1 == on */ -#define PN_NWAY_CAP10HALF 0x00002000 -#define PN_NWAY_CAP10FULL 0x00004000 -#define PN_NWAY_CAP100FULL 0x00008000 -#define PN_NWAY_CAP100HALF 0x00010000 -#define PN_NWAY_CAP100T4 0x00020000 -#define PN_NWAY_AUTONEGRSTR 0x02000000 -#define PN_NWAY_REMFAULT 0x04000000 -#define PN_NWAY_LPAR10HALF 0x08000000 -#define PN_NWAY_LPAR10FULL 0x10000000 -#define PN_NWAY_LPAR100FULL 0x20000000 -#define PN_NWAY_LPAR100HALF 0x40000000 -#define PN_NWAY_LPAR100T4 0x80000000 - -/* - * Nway register bits that must be set to turn on to initiate - * an autoneg session with all modes advertized and AUI disabled. - */ -#define PN_NWAY_AUTOENB \ - (PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY|PN_NWAY_TP \ - |PN_NWAY_NWAY_ENB|PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ - PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ - PN_NWAY_AUTONEGRSTR) - -#define PN_NWAY_MODE_10HD \ - (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ - PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ - PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ - PN_NWAY_TP) - -#define PN_NWAY_MODE_10FD \ - (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ - PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ - PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ - PN_NWAY_TP|PN_NWAY_DUPLEX) - -#define PN_NWAY_MODE_100HD \ - (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ - PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ - PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ - PN_NWAY_TP|PN_NWAY_SPEEDSEL) - -#define PN_NWAY_MODE_100FD \ - (PN_NWAY_CAP10HALF|PN_NWAY_CAP10FULL| \ - PN_NWAY_CAP100FULL|PN_NWAY_CAP100HALF|PN_NWAY_CAP100T4| \ - PN_NWAY_AUILOWCUR|PN_NWAY_TPEXTEND|PN_NWAY_POLARITY| \ - PN_NWAY_TP|PN_NWAY_SPEEDSEL|PN_NWAY_DUPLEX) - -#define PN_NWAY_MODE_100T4 PN_NWAY_MODE_100HD - -#define PN_NWAY_LPAR \ - (PN_NWAY_LPAR10HALF|PN_NWAY_LPAR10FULL|PN_NWAY_LPAR100HALF| \ - PN_NWAY_LPAR100FULL|PN_NWAY_LPAR100T4) - -/* - * Size of a setup frame. - */ -#define PN_SFRAME_LEN 192 - -/* - * PNIC TX/RX list structure. - */ - -struct pn_desc { - u_int32_t pn_status; - u_int32_t pn_ctl; - u_int32_t pn_ptr1; - u_int32_t pn_ptr2; -}; - -#define pn_data pn_ptr1 -#define pn_next pn_ptr2 - - -#define RX_RXSTAT_FIFOOFLOW 0x00000001 -#define PN_RXSTAT_CRCERR 0x00000002 -#define PN_RXSTAT_DRIBBLE 0x00000004 -#define PN_RXSTAT_WATCHDOG 0x00000010 -#define PN_RXSTAT_FRAMETYPE 0x00000020 /* 0 == IEEE 802.3 */ -#define PN_RXSTAT_COLLSEEN 0x00000040 -#define PN_RXSTAT_GIANT 0x00000080 -#define PN_RXSTAT_LASTFRAG 0x00000100 -#define PN_RXSTAT_FIRSTFRAG 0x00000200 -#define PN_RXSTAT_MULTICAST 0x00000400 -#define PN_RXSTAT_RUNT 0x00000800 -#define PN_RXSTAT_RXTYPE 0x00003000 -#define PN_RXSTAT_RXERR 0x00008000 -#define PN_RXSTAT_RXLEN 0x7FFF0000 -#define PN_RXSTAT_OWN 0x80000000 - -#define PN_RXBYTES(x) ((x & PN_RXSTAT_RXLEN) >> 16) -#define PN_RXSTAT (PN_RXSTAT_FIRSTFRAG|PN_RXSTAT_LASTFRAG|PN_RXSTAT_OWN) - -#define PN_RXCTL_BUFLEN1 0x00000FFF -#define PN_RXCTL_BUFLEN2 0x00FFF000 -#define PN_RXCTL_RLINK 0x01000000 -#define PN_RXCTL_RLAST 0x02000000 - -#define PN_TXSTAT_DEFER 0x00000001 -#define PN_TXSTAT_UNDERRUN 0x00000002 -#define PN_TXSTAT_LINKFAIL 0x00000003 -#define PN_TXSTAT_COLLCNT 0x00000078 -#define PN_TXSTAT_SQE 0x00000080 -#define PN_TXSTAT_EXCESSCOLL 0x00000100 -#define PN_TXSTAT_LATECOLL 0x00000200 -#define PN_TXSTAT_NOCARRIER 0x00000400 -#define PN_TXSTAT_CARRLOST 0x00000800 -#define PN_TXSTAT_JABTIMEO 0x00004000 -#define PN_TXSTAT_ERRSUM 0x00008000 -#define PN_TXSTAT_OWN 0x80000000 - -#define PN_TXCTL_BUFLEN1 0x000007FF -#define PN_TXCTL_BUFLEN2 0x003FF800 -#define PN_TXCTL_FILTTYPE0 0x00400000 -#define PN_TXCTL_PAD 0x00800000 -#define PN_TXCTL_TLINK 0x01000000 -#define PN_TXCTL_TLAST 0x02000000 -#define PN_TXCTL_NOCRC 0x04000000 -#define PN_TXCTL_SETUP 0x08000000 -#define PN_TXCTL_FILTTYPE1 0x10000000 -#define PN_TXCTL_FIRSTFRAG 0x20000000 -#define PN_TXCTL_LASTFRAG 0x40000000 -#define PN_TXCTL_FINT 0x80000000 - -#define PN_FILTER_PERFECT 0x00000000 -#define PN_FILTER_HASHPERF 0x00400000 -#define PN_FILTER_INVERSE 0x10000000 -#define PN_FILTER_HASHONLY 0x10400000 - -#define PN_MAXFRAGS 16 -#define PN_RX_LIST_CNT 64 -#define PN_TX_LIST_CNT 128 -#define PN_MIN_FRAMELEN 60 -#define PN_FRAMELEN 1536 -#define PN_RXLEN 1536 -#define ETHER_ALIGN 2 - -/* - * A tx 'super descriptor' is actually 16 regular descriptors - * back to back. - */ -struct pn_txdesc { - struct pn_desc pn_frag[PN_MAXFRAGS]; -}; - -#define PN_TXNEXT(x) x->pn_ptr->pn_frag[x->pn_lastdesc].pn_next -#define PN_TXSTATUS(x) x->pn_ptr->pn_frag[x->pn_lastdesc].pn_status -#define PN_TXCTL(x) x->pn_ptr->pn_frag[x->pn_lastdesc].pn_ctl -#define PN_TXDATA(x) x->pn_ptr->pn_frag[x->pn_lastdesc].pn_data - -#define PN_TXOWN(x) x->pn_ptr->pn_frag[0].pn_status - -struct pn_list_data { - struct pn_desc pn_rx_list[PN_RX_LIST_CNT]; - struct pn_txdesc pn_tx_list[PN_TX_LIST_CNT]; -}; - -struct pn_chain { - struct pn_txdesc *pn_ptr; - struct mbuf *pn_mbuf; - struct pn_chain *pn_nextdesc; - u_int8_t pn_lastdesc; -}; - -struct pn_chain_onefrag { - struct pn_desc *pn_ptr; - struct mbuf *pn_mbuf; - struct pn_chain_onefrag *pn_nextdesc; -}; - -struct pn_chain_data { - struct pn_desc pn_sframe; - u_int32_t pn_sbuf[PN_SFRAME_LEN/sizeof(u_int32_t)]; - struct pn_chain_onefrag pn_rx_chain[PN_RX_LIST_CNT]; - struct pn_chain pn_tx_chain[PN_TX_LIST_CNT]; - - struct pn_chain_onefrag *pn_rx_head; - - struct pn_chain *pn_tx_head; - struct pn_chain *pn_tx_tail; - struct pn_chain *pn_tx_free; -}; - -struct pn_type { - u_int16_t pn_vid; - u_int16_t pn_did; - char *pn_name; -}; - -struct pn_mii_frame { - u_int8_t mii_stdelim; - u_int8_t mii_opcode; - u_int8_t mii_phyaddr; - u_int8_t mii_regaddr; - u_int8_t mii_turnaround; - u_int16_t mii_data; -}; - -/* - * MII constants - */ -#define PN_MII_STARTDELIM 0x01 -#define PN_MII_READOP 0x02 -#define PN_MII_WRITEOP 0x01 -#define PN_MII_TURNAROUND 0x02 - -#define PN_FLAG_FORCEDELAY 1 -#define PN_FLAG_SCHEDDELAY 2 -#define PN_FLAG_DELAYTIMEO 3 - -struct pn_softc { - struct arpcom arpcom; /* interface info */ - struct ifmedia ifmedia; /* media info */ - bus_space_handle_t pn_bhandle; /* bus space handle */ - bus_space_tag_t pn_btag; /* bus space tag */ - void *pn_intrhand; - struct resource *pn_irq; - struct resource *pn_res; - struct pn_type *pn_info; /* PNIC adapter info */ - struct pn_type *pn_pinfo; /* phy info */ - u_int8_t pn_unit; /* interface number */ - u_int8_t pn_type; - u_int8_t pn_phy_addr; /* PHY address */ - u_int8_t pn_tx_pend; /* TX pending */ - u_int8_t pn_want_auto; - u_int8_t pn_autoneg; - u_int8_t pn_cachesize; -#ifdef PN_RX_BUG_WAR -#define PN_168_REV 16 -#define PN_169_REV 32 -#define PN_169B_REV 33 - u_int8_t pn_rx_war; - struct pn_chain_onefrag *pn_rx_bug_save; - unsigned char *pn_rx_buf; -#endif - caddr_t pn_ldata_ptr; - struct pn_list_data *pn_ldata; - struct pn_chain_data pn_cdata; -}; - -/* - * register space access macros - */ -#define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4(sc->pn_btag, sc->pn_bhandle, reg, val) -#define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2(sc->pn_btag, sc->pn_bhandle, reg, val) -#define CSR_WRITE_1(sc, reg, val) \ - bus_space_write_1(sc->pn_btag, sc->pn_bhandle, reg, val) - -#define CSR_READ_4(sc, reg) \ - bus_space_read_4(sc->pn_btag, sc->pn_bhandle, reg) -#define CSR_READ_2(sc, reg) \ - bus_space_read_2(sc->pn_btag, sc->pn_bhandle, reg) -#define CSR_READ_1(sc, reg) \ - bus_space_read_1(sc->pn_btag, sc->pn_bhandle, reg) - -#define PN_TIMEOUT 1000 - -/* - * General constants that are fun to know. - * - * Lite-On PNIC PCI vendor ID - */ -#define PN_VENDORID 0x11AD - -/* - * Lite-On PNIC PCI device ID. - */ -#define PN_DEVICEID_PNIC 0x0002 - -/* - * The 82c168 chip has the same PCI vendor/device ID as the - * 82c169, but a different revision. Assume that any revision - * between 0x10 an 0x1F is an 82c168. - */ -#define PN_REVMASK 0xF0 -#define PN_REVID_82C168 0x10 -#define PN_REVID_82C169 0x20 - -/* - * Texas Instruments PHY identifiers - */ -#define TI_PHY_VENDORID 0x4000 -#define TI_PHY_10BT 0x501F -#define TI_PHY_100VGPMI 0x502F - -/* - * These ID values are for the NS DP83840A 10/100 PHY - */ -#define NS_PHY_VENDORID 0x2000 -#define NS_PHY_83840A 0x5C0F - -/* - * Level 1 10/100 PHY - */ -#define LEVEL1_PHY_VENDORID 0x7810 -#define LEVEL1_PHY_LXT970 0x000F - -/* - * Intel 82555 10/100 PHY - */ -#define INTEL_PHY_VENDORID 0x0A28 -#define INTEL_PHY_82555 0x015F - -/* - * SEEQ 80220 10/100 PHY - */ -#define SEEQ_PHY_VENDORID 0x0016 -#define SEEQ_PHY_80220 0xF83F - - -/* - * PCI low memory base and low I/O base register, and - * other PCI registers. - */ - -#define PN_PCI_VENDOR_ID 0x00 -#define PN_PCI_DEVICE_ID 0x02 -#define PN_PCI_COMMAND 0x04 -#define PN_PCI_STATUS 0x06 -#define PN_PCI_REVISION 0x08 -#define PN_PCI_CLASSCODE 0x09 -#define PN_PCI_CACHELEN 0x0C -#define PN_PCI_LATENCY_TIMER 0x0D -#define PN_PCI_HEADER_TYPE 0x0E -#define PN_PCI_LOIO 0x10 -#define PN_PCI_LOMEM 0x14 -#define PN_PCI_BIOSROM 0x30 -#define PN_PCI_INTLINE 0x3C -#define PN_PCI_INTPIN 0x3D -#define PN_PCI_MINGNT 0x3E -#define PN_PCI_MINLAT 0x0F -#define PN_PCI_RESETOPT 0x48 -#define PN_PCI_EEPROM_DATA 0x4C - -/* power management registers */ -#define PN_PCI_CAPID 0xDC /* 8 bits */ -#define PN_PCI_NEXTPTR 0xDD /* 8 bits */ -#define PN_PCI_PWRMGMTCAP 0xDE /* 16 bits */ -#define PN_PCI_PWRMGMTCTRL 0xE0 /* 16 bits */ - -#define PN_PSTATE_MASK 0x0003 -#define PN_PSTATE_D0 0x0000 -#define PN_PSTATE_D1 0x0002 -#define PN_PSTATE_D2 0x0002 -#define PN_PSTATE_D3 0x0003 -#define PN_PME_EN 0x0010 -#define PN_PME_STATUS 0x8000 - -#define PHY_UNKNOWN 6 - -#define PN_PHYADDR_MIN 0x00 -#define PN_PHYADDR_MAX 0x1F - -#define PHY_BMCR 0x00 -#define PHY_BMSR 0x01 -#define PHY_VENID 0x02 -#define PHY_DEVID 0x03 -#define PHY_ANAR 0x04 -#define PHY_LPAR 0x05 -#define PHY_ANEXP 0x06 - -#define PHY_ANAR_NEXTPAGE 0x8000 -#define PHY_ANAR_RSVD0 0x4000 -#define PHY_ANAR_TLRFLT 0x2000 -#define PHY_ANAR_RSVD1 0x1000 -#define PHY_ANAR_RSVD2 0x0800 -#define PHY_ANAR_RSVD3 0x0400 -#define PHY_ANAR_100BT4 0x0200 -#define PHY_ANAR_100BTXFULL 0x0100 -#define PHY_ANAR_100BTXHALF 0x0080 -#define PHY_ANAR_10BTFULL 0x0040 -#define PHY_ANAR_10BTHALF 0x0020 -#define PHY_ANAR_PROTO4 0x0010 -#define PHY_ANAR_PROTO3 0x0008 -#define PHY_ANAR_PROTO2 0x0004 -#define PHY_ANAR_PROTO1 0x0002 -#define PHY_ANAR_PROTO0 0x0001 - -/* - * These are the register definitions for the PHY (physical layer - * interface chip). - */ -/* - * PHY BMCR Basic Mode Control Register - */ -#define PHY_BMCR_RESET 0x8000 -#define PHY_BMCR_LOOPBK 0x4000 -#define PHY_BMCR_SPEEDSEL 0x2000 -#define PHY_BMCR_AUTONEGENBL 0x1000 -#define PHY_BMCR_RSVD0 0x0800 /* write as zero */ -#define PHY_BMCR_ISOLATE 0x0400 -#define PHY_BMCR_AUTONEGRSTR 0x0200 -#define PHY_BMCR_DUPLEX 0x0100 -#define PHY_BMCR_COLLTEST 0x0080 -#define PHY_BMCR_RSVD1 0x0040 /* write as zero, don't care */ -#define PHY_BMCR_RSVD2 0x0020 /* write as zero, don't care */ -#define PHY_BMCR_RSVD3 0x0010 /* write as zero, don't care */ -#define PHY_BMCR_RSVD4 0x0008 /* write as zero, don't care */ -#define PHY_BMCR_RSVD5 0x0004 /* write as zero, don't care */ -#define PHY_BMCR_RSVD6 0x0002 /* write as zero, don't care */ -#define PHY_BMCR_RSVD7 0x0001 /* write as zero, don't care */ -/* - * RESET: 1 == software reset, 0 == normal operation - * Resets status and control registers to default values. - * Relatches all hardware config values. - * - * LOOPBK: 1 == loopback operation enabled, 0 == normal operation - * - * SPEEDSEL: 1 == 100Mb/s, 0 == 10Mb/s - * Link speed is selected byt his bit or if auto-negotiation if bit - * 12 (AUTONEGENBL) is set (in which case the value of this register - * is ignored). - * - * AUTONEGENBL: 1 == Autonegotiation enabled, 0 == Autonegotiation disabled - * Bits 8 and 13 are ignored when autoneg is set, otherwise bits 8 and 13 - * determine speed and mode. Should be cleared and then set if PHY configured - * for no autoneg on startup. - * - * ISOLATE: 1 == isolate PHY from MII, 0 == normal operation - * - * AUTONEGRSTR: 1 == restart autonegotiation, 0 = normal operation - * - * DUPLEX: 1 == full duplex mode, 0 == half duplex mode - * - * COLLTEST: 1 == collision test enabled, 0 == normal operation - */ - -/* - * PHY, BMSR Basic Mode Status Register - */ -#define PHY_BMSR_100BT4 0x8000 -#define PHY_BMSR_100BTXFULL 0x4000 -#define PHY_BMSR_100BTXHALF 0x2000 -#define PHY_BMSR_10BTFULL 0x1000 -#define PHY_BMSR_10BTHALF 0x0800 -#define PHY_BMSR_RSVD1 0x0400 /* write as zero, don't care */ -#define PHY_BMSR_RSVD2 0x0200 /* write as zero, don't care */ -#define PHY_BMSR_RSVD3 0x0100 /* write as zero, don't care */ -#define PHY_BMSR_RSVD4 0x0080 /* write as zero, don't care */ -#define PHY_BMSR_MFPRESUP 0x0040 -#define PHY_BMSR_AUTONEGCOMP 0x0020 -#define PHY_BMSR_REMFAULT 0x0010 -#define PHY_BMSR_CANAUTONEG 0x0008 -#define PHY_BMSR_LINKSTAT 0x0004 -#define PHY_BMSR_JABBER 0x0002 -#define PHY_BMSR_EXTENDED 0x0001 - -#ifdef __alpha__ -#undef vtophys -#define vtophys(va) alpha_XXX_dmamap((vm_offset_t)va) -#endif diff --git a/usr.sbin/sade/devices.c b/usr.sbin/sade/devices.c index 6bbe45b..e9f2694 100644 --- a/usr.sbin/sade/devices.c +++ b/usr.sbin/sade/devices.c @@ -91,14 +91,12 @@ static struct _devname { { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 2, 0, 64, 4, 'b' }, { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 1, 0, 8, 4, 'b' }, { DEVICE_TYPE_FLOPPY, "worm%d", "SCSI optical disk / CDR", 23, 0, 1, 4, 'b' }, - { DEVICE_TYPE_NETWORK, "al", "ADMtek AL981/AN985 PCI ethernet card" }, - { DEVICE_TYPE_NETWORK, "ax", "ASIX AX88140A PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" }, { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" }, { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" }, { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" }, + { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" }, { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" }, - { DEVICE_TYPE_NETWORK, "dm", "Davicom DM9100/DM9102 PCI fast ethernet card" }, { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" }, { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" }, { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" }, @@ -109,8 +107,6 @@ static struct _devname { { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" }, { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" }, { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" }, - { DEVICE_TYPE_NETWORK, "mx", "Macronix 98713/98715/98725 PCI ethernet card" }, - { DEVICE_TYPE_NETWORK, "pn", "Lite-On 82168/82169 PNIC PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" }, diff --git a/usr.sbin/sysinstall/devices.c b/usr.sbin/sysinstall/devices.c index 6bbe45b..e9f2694 100644 --- a/usr.sbin/sysinstall/devices.c +++ b/usr.sbin/sysinstall/devices.c @@ -91,14 +91,12 @@ static struct _devname { { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 2, 0, 64, 4, 'b' }, { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 1, 0, 8, 4, 'b' }, { DEVICE_TYPE_FLOPPY, "worm%d", "SCSI optical disk / CDR", 23, 0, 1, 4, 'b' }, - { DEVICE_TYPE_NETWORK, "al", "ADMtek AL981/AN985 PCI ethernet card" }, - { DEVICE_TYPE_NETWORK, "ax", "ASIX AX88140A PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" }, { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" }, { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" }, { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" }, + { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" }, { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" }, - { DEVICE_TYPE_NETWORK, "dm", "Davicom DM9100/DM9102 PCI fast ethernet card" }, { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" }, { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" }, { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" }, @@ -109,8 +107,6 @@ static struct _devname { { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" }, { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" }, { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" }, - { DEVICE_TYPE_NETWORK, "mx", "Macronix 98713/98715/98725 PCI ethernet card" }, - { DEVICE_TYPE_NETWORK, "pn", "Lite-On 82168/82169 PNIC PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" }, { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" }, |