mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
Merge branch 'remove-a-number-of-isa-and-pcmcia-ethernet-drivers'
Andrew Lunn says: ==================== Remove a number of ISA and PCMCIA Ethernet drivers These old drivers have not been much of a Maintenance burden until recently. Now there are more newbies using AI and fuzzers finding issues, resulting in more work for Maintainers. Fixing these old drivers make little sense, if it is not clear they have users. These mostly ISA and PCMCIA Ethernet devices, mostly from the last century, a couple from 2001 or 2002. It seems unlikely they are still used. However, remove them one patch at a time so they can be brought back if somebody still has the hardware, runs modern kernels and wants to take up the roll of driver Maintainer. ==================== Link: https://patch.msgid.link/20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
0c22ed0fad
|
|
@ -786,7 +786,6 @@ networking/altera_tse networking/device_drivers/ethernet/altera/altera_tse
|
|||
networking/bpf_flow_dissector bpf/prog_flow_dissector
|
||||
networking/cxacru networking/device_drivers/atm/cxacru
|
||||
networking/defza networking/device_drivers/fddi/defza
|
||||
networking/device_drivers/3com/3c509 networking/device_drivers/ethernet/3com/3c509
|
||||
networking/device_drivers/3com/vortex networking/device_drivers/ethernet/3com/vortex
|
||||
networking/device_drivers/amazon/ena networking/device_drivers/ethernet/amazon/ena
|
||||
networking/device_drivers/aquantia/atlantic networking/device_drivers/ethernet/aquantia/atlantic
|
||||
|
|
@ -821,7 +820,6 @@ networking/device_drivers/microsoft/netvsc networking/device_drivers/ethernet/mi
|
|||
networking/device_drivers/netronome/nfp networking/device_drivers/ethernet/netronome/nfp
|
||||
networking/device_drivers/pensando/ionic networking/device_drivers/ethernet/pensando/ionic
|
||||
networking/device_drivers/qualcomm/rmnet networking/device_drivers/cellular/qualcomm/rmnet
|
||||
networking/device_drivers/smsc/smc9 networking/device_drivers/ethernet/smsc/smc9
|
||||
networking/device_drivers/stmicro/stmmac networking/device_drivers/ethernet/stmicro/stmmac
|
||||
networking/device_drivers/ti/cpsw networking/device_drivers/ethernet/ti/cpsw
|
||||
networking/device_drivers/ti/cpsw_switchdev networking/device_drivers/ethernet/ti/cpsw_switchdev
|
||||
|
|
|
|||
|
|
@ -1,249 +0,0 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=============================================================================
|
||||
Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
|
||||
=============================================================================
|
||||
|
||||
This file contains the instructions and caveats for v1.18c and higher versions
|
||||
of the 3c509 driver. You should not use the driver without reading this file.
|
||||
|
||||
release 1.0
|
||||
|
||||
28 February 2002
|
||||
|
||||
Current maintainer (corrections to):
|
||||
David Ruggiero <jdr@farfalle.com>
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The following are notes and information on using the 3Com EtherLink III series
|
||||
ethercards in Linux. These cards are commonly known by the most widely-used
|
||||
card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
|
||||
be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
|
||||
(aka "Vortex" or "Boomerang") series. Kernel support for the 3c509 family is
|
||||
provided by the module 3c509.c, which has code to support all of the following
|
||||
models:
|
||||
|
||||
- 3c509 (original ISA card)
|
||||
- 3c509B (later revision of the ISA card; supports full-duplex)
|
||||
- 3c589 (PCMCIA)
|
||||
- 3c589B (later revision of the 3c589; supports full-duplex)
|
||||
- 3c579 (EISA)
|
||||
|
||||
Large portions of this documentation were heavily borrowed from the guide
|
||||
written the original author of the 3c509 driver, Donald Becker. The master
|
||||
copy of that document, which contains notes on older versions of the driver,
|
||||
currently resides on Scyld web server: http://www.scyld.com/.
|
||||
|
||||
|
||||
Special Driver Features
|
||||
=======================
|
||||
|
||||
Overriding card settings
|
||||
|
||||
The driver allows boot- or load-time overriding of the card's detected IOADDR,
|
||||
IRQ, and transceiver settings, although this capability shouldn't generally be
|
||||
needed except to enable full-duplex mode (see below). An example of the syntax
|
||||
for LILO parameters for doing this::
|
||||
|
||||
ether=10,0x310,3,0x3c509,eth0
|
||||
|
||||
This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
|
||||
transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
|
||||
with other card types when overriding the I/O address. When the driver is
|
||||
loaded as a module, only the IRQ may be overridden. For example,
|
||||
setting two cards to IRQ10 and IRQ11 is done by using the irq module
|
||||
option::
|
||||
|
||||
options 3c509 irq=10,11
|
||||
|
||||
|
||||
Full-duplex mode
|
||||
================
|
||||
|
||||
The v1.18c driver added support for the 3c509B's full-duplex capabilities.
|
||||
In order to enable and successfully use full-duplex mode, three conditions
|
||||
must be met:
|
||||
|
||||
(a) You must have a Etherlink III card model whose hardware supports full-
|
||||
duplex operations. Currently, the only members of the 3c509 family that are
|
||||
positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
|
||||
(PCMCIA) cards. Cards without the "B" model designation do *not* support
|
||||
full-duplex mode; these include the original 3c509 (no "B"), the original
|
||||
3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
|
||||
|
||||
(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
|
||||
connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
|
||||
AUI and 10base2 network cabling is physically incapable of full-duplex
|
||||
operation.
|
||||
|
||||
(c) Most importantly, your 3c509B must be connected to a link partner that is
|
||||
itself full-duplex capable. This is almost certainly one of two things: a full-
|
||||
duplex-capable Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
|
||||
another system that's connected directly to the 3c509B via a crossover cable.
|
||||
|
||||
Full-duplex mode can be enabled using 'ethtool'.
|
||||
|
||||
.. warning::
|
||||
|
||||
Extremely important caution concerning full-duplex mode
|
||||
|
||||
Understand that the 3c509B's hardware's full-duplex support is much more
|
||||
limited than that provide by more modern network interface cards. Although
|
||||
at the physical layer of the network it fully supports full-duplex operation,
|
||||
the card was designed before the current Ethernet auto-negotiation (N-way)
|
||||
spec was written. This means that the 3c509B family ***cannot and will not
|
||||
auto-negotiate a full-duplex connection with its link partner under any
|
||||
circumstances, no matter how it is initialized***. If the full-duplex mode
|
||||
of the 3c509B is enabled, its link partner will very likely need to be
|
||||
independently _forced_ into full-duplex mode as well; otherwise various nasty
|
||||
failures will occur - at the very least, you'll see massive numbers of packet
|
||||
collisions. This is one of very rare circumstances where disabling auto-
|
||||
negotiation and forcing the duplex mode of a network interface card or switch
|
||||
would ever be necessary or desirable.
|
||||
|
||||
|
||||
Available Transceiver Types
|
||||
===========================
|
||||
|
||||
For versions of the driver v1.18c and above, the available transceiver types are:
|
||||
|
||||
== =========================================================================
|
||||
0 transceiver type from EEPROM config (normally 10baseT); force half-duplex
|
||||
1 AUI (thick-net / DB15 connector)
|
||||
2 (undefined)
|
||||
3 10base2 (thin-net == coax / BNC connector)
|
||||
4 10baseT (RJ-45 connector); force half-duplex mode
|
||||
8 transceiver type and duplex mode taken from card's EEPROM config settings
|
||||
12 10baseT (RJ-45 connector); force full-duplex mode
|
||||
== =========================================================================
|
||||
|
||||
Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
|
||||
that the new transceiver codes 8 and 12 are the *only* ones that will enable
|
||||
full-duplex mode, no matter what the card's detected EEPROM settings might be.
|
||||
This insured that merely upgrading the driver from an earlier version would
|
||||
never automatically enable full-duplex mode in an existing installation;
|
||||
it must always be explicitly enabled via one of these code in order to be
|
||||
activated.
|
||||
|
||||
The transceiver type can be changed using 'ethtool'.
|
||||
|
||||
|
||||
Interpretation of error messages and common problems
|
||||
----------------------------------------------------
|
||||
|
||||
Error Messages
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
eth0: Infinite loop in interrupt, status 2011.
|
||||
These are "mostly harmless" message indicating that the driver had too much
|
||||
work during that interrupt cycle. With a status of 0x2011 you are receiving
|
||||
packets faster than they can be removed from the card. This should be rare
|
||||
or impossible in normal operation. Possible causes of this error report are:
|
||||
|
||||
- a "green" mode enabled that slows the processor down when there is no
|
||||
keyboard activity.
|
||||
|
||||
- some other device or device driver hogging the bus or disabling interrupts.
|
||||
Check /proc/interrupts for excessive interrupt counts. The timer tick
|
||||
interrupt should always be incrementing faster than the others.
|
||||
|
||||
No received packets
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
|
||||
receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
|
||||
have an interrupt line problem. Check /proc/interrupts to verify that the
|
||||
card is actually generating interrupts. If the interrupt count is not
|
||||
increasing you likely have a physical conflict with two devices trying to
|
||||
use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
|
||||
or IRQ5, and the easiest solution is to move the 3c509 to a different
|
||||
interrupt line. If the device is receiving packets but 'ping' doesn't work,
|
||||
you have a routing problem.
|
||||
|
||||
Tx Carrier Errors Reported in /proc/net/dev
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
|
||||
field in /proc/net/dev increments as quickly as the Tx packet count, you
|
||||
likely have an unterminated network or the incorrect media transceiver selected.
|
||||
|
||||
3c509B card is not detected on machines with an ISA PnP BIOS.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
While the updated driver works with most PnP BIOS programs, it does not work
|
||||
with all. This can be fixed by disabling PnP support using the 3Com-supplied
|
||||
setup program.
|
||||
|
||||
3c509 card is not detected on overclocked machines
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Increase the delay time in id_read_eeprom() from the current value, 500,
|
||||
to an absurdly high value, such as 5000.
|
||||
|
||||
|
||||
Decoding Status and Error Messages
|
||||
----------------------------------
|
||||
|
||||
|
||||
The bits in the main status register are:
|
||||
|
||||
===== ======================================
|
||||
value description
|
||||
===== ======================================
|
||||
0x01 Interrupt latch
|
||||
0x02 Tx overrun, or Rx underrun
|
||||
0x04 Tx complete
|
||||
0x08 Tx FIFO room available
|
||||
0x10 A complete Rx packet has arrived
|
||||
0x20 A Rx packet has started to arrive
|
||||
0x40 The driver has requested an interrupt
|
||||
0x80 Statistics counter nearly full
|
||||
===== ======================================
|
||||
|
||||
The bits in the transmit (Tx) status word are:
|
||||
|
||||
===== ============================================
|
||||
value description
|
||||
===== ============================================
|
||||
0x02 Out-of-window collision.
|
||||
0x04 Status stack overflow (normally impossible).
|
||||
0x08 16 collisions.
|
||||
0x10 Tx underrun (not enough PCI bus bandwidth).
|
||||
0x20 Tx jabber.
|
||||
0x40 Tx interrupt requested.
|
||||
0x80 Status is valid (this should always be set).
|
||||
===== ============================================
|
||||
|
||||
|
||||
When a transmit error occurs the driver produces a status message such as::
|
||||
|
||||
eth0: Transmit error, Tx status register 82
|
||||
|
||||
The two values typically seen here are:
|
||||
|
||||
0x82
|
||||
^^^^
|
||||
|
||||
Out of window collision. This typically occurs when some other Ethernet
|
||||
host is incorrectly set to full duplex on a half duplex network.
|
||||
|
||||
0x88
|
||||
^^^^
|
||||
|
||||
16 collisions. This typically occurs when the network is exceptionally busy
|
||||
or when another host doesn't correctly back off after a collision. If this
|
||||
error is mixed with 0x82 errors it is the result of a host incorrectly set
|
||||
to full duplex (see above).
|
||||
|
||||
Both of these errors are the result of network problems that should be
|
||||
corrected. They do not represent driver malfunction.
|
||||
|
||||
|
||||
Revision history (this file)
|
||||
============================
|
||||
|
||||
28Feb02 v1.0 DR New; major portions based on Becker original 3c509 docs
|
||||
|
||||
|
|
@ -10,7 +10,6 @@ Contents:
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
3com/3c509
|
||||
3com/vortex
|
||||
amazon/ena
|
||||
altera/altera_tse
|
||||
|
|
@ -52,7 +51,6 @@ Contents:
|
|||
pensando/ionic
|
||||
pensando/ionic_rdma
|
||||
qualcomm/ppe/ppe
|
||||
smsc/smc9
|
||||
stmicro/stmmac
|
||||
ti/cpsw
|
||||
ti/cpsw_switchdev
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
================
|
||||
SMC 9xxxx Driver
|
||||
================
|
||||
|
||||
Revision 0.12
|
||||
|
||||
3/5/96
|
||||
|
||||
Copyright 1996 Erik Stahlman
|
||||
|
||||
Released under terms of the GNU General Public License.
|
||||
|
||||
This file contains the instructions and caveats for my SMC9xxx driver. You
|
||||
should not be using the driver without reading this file.
|
||||
|
||||
Things to note about installation:
|
||||
|
||||
1. The driver should work on all kernels from 1.2.13 until 1.3.71.
|
||||
(A kernel patch is supplied for 1.3.71 )
|
||||
|
||||
2. If you include this into the kernel, you might need to change some
|
||||
options, such as for forcing IRQ.
|
||||
|
||||
|
||||
3. To compile as a module, run 'make'.
|
||||
Make will give you the appropriate options for various kernel support.
|
||||
|
||||
4. Loading the driver as a module::
|
||||
|
||||
use: insmod smc9194.o
|
||||
optional parameters:
|
||||
io=xxxx : your base address
|
||||
irq=xx : your irq
|
||||
ifport=x : 0 for whatever is default
|
||||
1 for twisted pair
|
||||
2 for AUI ( or BNC on some cards )
|
||||
|
||||
How to obtain the latest version?
|
||||
|
||||
FTP:
|
||||
ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
|
||||
ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz
|
||||
|
||||
|
||||
Contacting me:
|
||||
erik@mail.vt.edu
|
||||
|
|
@ -137,7 +137,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
|
|||
F: drivers/tty/serial/8250*
|
||||
F: include/linux/serial_8250.h
|
||||
|
||||
8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
|
||||
8390 NETWORK DRIVERS [NE2000, 3C503, etc.]
|
||||
L: netdev@vger.kernel.org
|
||||
S: Orphan / Obsolete
|
||||
F: drivers/net/ethernet/8390/
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ CONFIG_BLK_DEV_SD=m
|
|||
CONFIG_NETDEVICES=y
|
||||
CONFIG_NET_VENDOR_SMC=y
|
||||
CONFIG_PCMCIA_PCNET=y
|
||||
CONFIG_SMC9194=y
|
||||
CONFIG_SMC91X=y
|
||||
CONFIG_NET_PCMCIA=y
|
||||
# CONFIG_INPUT_MOUSE is not set
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ CONFIG_PATA_PCMCIA=y
|
|||
CONFIG_PATA_PLATFORM=y
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_NLMON=y
|
||||
CONFIG_PCMCIA_3C589=y
|
||||
CONFIG_MIPS_AU1X00_ENET=y
|
||||
CONFIG_SMC91X=y
|
||||
CONFIG_SMSC911X=y
|
||||
|
|
|
|||
|
|
@ -228,14 +228,11 @@ CONFIG_ARCNET_RIM_I=m
|
|||
CONFIG_ARCNET_COM20020=m
|
||||
CONFIG_ARCNET_COM20020_PCI=m
|
||||
CONFIG_ARCNET_COM20020_CS=m
|
||||
CONFIG_PCMCIA_3C574=m
|
||||
CONFIG_PCMCIA_3C589=m
|
||||
CONFIG_VORTEX=m
|
||||
CONFIG_TYPHOON=m
|
||||
CONFIG_ADAPTEC_STARFIRE=m
|
||||
CONFIG_AMD8111_ETH=m
|
||||
CONFIG_PCNET32=m
|
||||
CONFIG_PCMCIA_NMCLAN=m
|
||||
CONFIG_B44=m
|
||||
CONFIG_BNX2=m
|
||||
CONFIG_TIGON3=m
|
||||
|
|
@ -249,7 +246,6 @@ CONFIG_ULI526X=m
|
|||
CONFIG_PCMCIA_XIRCOM=m
|
||||
CONFIG_DL2K=m
|
||||
CONFIG_SUNDANCE=m
|
||||
CONFIG_PCMCIA_FMVJ18X=m
|
||||
CONFIG_E100=m
|
||||
CONFIG_E1000=m
|
||||
CONFIG_SKGE=m
|
||||
|
|
@ -258,7 +254,6 @@ CONFIG_MYRI10GE=m
|
|||
CONFIG_FEALNX=m
|
||||
CONFIG_NATSEMI=m
|
||||
CONFIG_NS83820=m
|
||||
CONFIG_PCMCIA_AXNET=m
|
||||
CONFIG_NE2K_PCI=m
|
||||
CONFIG_PCMCIA_PCNET=m
|
||||
CONFIG_FORCEDETH=m
|
||||
|
|
@ -270,7 +265,6 @@ CONFIG_8139TOO_8129=y
|
|||
CONFIG_R8169=m
|
||||
CONFIG_SIS900=m
|
||||
CONFIG_SIS190=m
|
||||
CONFIG_PCMCIA_SMC91C92=m
|
||||
CONFIG_EPIC100=m
|
||||
CONFIG_HAPPYMEAL=m
|
||||
CONFIG_SUNGEM=m
|
||||
|
|
|
|||
|
|
@ -395,15 +395,11 @@ CONFIG_NETCONSOLE=m
|
|||
CONFIG_TUN=m
|
||||
CONFIG_VETH=m
|
||||
CONFIG_VIRTIO_NET=m
|
||||
CONFIG_EL3=m
|
||||
CONFIG_PCMCIA_3C574=m
|
||||
CONFIG_PCMCIA_3C589=m
|
||||
CONFIG_VORTEX=m
|
||||
CONFIG_TYPHOON=m
|
||||
CONFIG_ADAPTEC_STARFIRE=m
|
||||
CONFIG_AMD8111_ETH=m
|
||||
CONFIG_PCNET32=m
|
||||
CONFIG_PCMCIA_NMCLAN=m
|
||||
CONFIG_MACE=m
|
||||
CONFIG_BMAC=m
|
||||
CONFIG_ATL1=m
|
||||
|
|
@ -426,7 +422,6 @@ CONFIG_DL2K=m
|
|||
CONFIG_SUNDANCE=m
|
||||
CONFIG_FEC_MPC52xx=m
|
||||
CONFIG_GIANFAR=m
|
||||
CONFIG_PCMCIA_FMVJ18X=m
|
||||
CONFIG_E100=m
|
||||
CONFIG_E1000=m
|
||||
CONFIG_E1000E=m
|
||||
|
|
@ -439,11 +434,9 @@ CONFIG_MYRI10GE=m
|
|||
CONFIG_FEALNX=m
|
||||
CONFIG_NATSEMI=m
|
||||
CONFIG_NS83820=m
|
||||
CONFIG_PCMCIA_AXNET=m
|
||||
CONFIG_NE2000=m
|
||||
CONFIG_NE2K_PCI=m
|
||||
CONFIG_PCMCIA_PCNET=m
|
||||
CONFIG_ULTRA=m
|
||||
CONFIG_FORCEDETH=m
|
||||
CONFIG_QLA3XXX=m
|
||||
CONFIG_NETXEN_NIC=m
|
||||
|
|
@ -457,7 +450,6 @@ CONFIG_SC92031=m
|
|||
CONFIG_SIS900=m
|
||||
CONFIG_SIS190=m
|
||||
CONFIG_SFC=m
|
||||
CONFIG_PCMCIA_SMC91C92=m
|
||||
CONFIG_EPIC100=m
|
||||
CONFIG_HAPPYMEAL=m
|
||||
CONFIG_SUNGEM=m
|
||||
|
|
|
|||
|
|
@ -200,24 +200,9 @@ static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe)
|
|||
* look for EISA/PCI cards in addition to ISA cards).
|
||||
*/
|
||||
static struct devprobe2 isa_probes[] __initdata = {
|
||||
#ifdef CONFIG_3C515
|
||||
{tc515_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_ULTRA
|
||||
{ultra_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_WD80x3
|
||||
{wd_probe, 0},
|
||||
#endif
|
||||
#if defined(CONFIG_NE2000) /* ISA (use ne2k-pci for PCI cards) */
|
||||
{ne_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */
|
||||
{lance_probe, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_SMC9194
|
||||
{smc_init, 0},
|
||||
#endif
|
||||
#ifdef CONFIG_CS89x0_ISA
|
||||
{cs89x0_probe, 0},
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,974 +0,0 @@
|
|||
/* ======================================================================
|
||||
*
|
||||
* A PCMCIA ethernet driver for the 3com 3c589 card.
|
||||
*
|
||||
* Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
|
||||
*
|
||||
* 3c589_cs.c 1.162 2001/10/13 00:08:50
|
||||
*
|
||||
* The network driver code is based on Donald Becker's 3c589 code:
|
||||
*
|
||||
* Written 1994 by Donald Becker.
|
||||
* Copyright 1993 United States Government as represented by the
|
||||
* Director, National Security Agency. This software may be used and
|
||||
* distributed according to the terms of the GNU General Public License,
|
||||
* incorporated herein by reference.
|
||||
* Donald Becker may be reached at becker@scyld.com
|
||||
*
|
||||
* Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
*
|
||||
* ======================================================================
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#define DRV_NAME "3c589_cs"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
|
||||
/* To minimize the size of the driver source I only define operating
|
||||
* constants if they are used several times. You'll need the manual
|
||||
* if you want to understand driver details.
|
||||
*/
|
||||
|
||||
/* Offsets from base I/O address. */
|
||||
#define EL3_DATA 0x00
|
||||
#define EL3_TIMER 0x0a
|
||||
#define EL3_CMD 0x0e
|
||||
#define EL3_STATUS 0x0e
|
||||
|
||||
#define EEPROM_READ 0x0080
|
||||
#define EEPROM_BUSY 0x8000
|
||||
|
||||
#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
|
||||
|
||||
/* The top five bits written to EL3_CMD are a command, the lower
|
||||
* 11 bits are the parameter, if applicable.
|
||||
*/
|
||||
|
||||
enum c509cmd {
|
||||
TotalReset = 0<<11,
|
||||
SelectWindow = 1<<11,
|
||||
StartCoax = 2<<11,
|
||||
RxDisable = 3<<11,
|
||||
RxEnable = 4<<11,
|
||||
RxReset = 5<<11,
|
||||
RxDiscard = 8<<11,
|
||||
TxEnable = 9<<11,
|
||||
TxDisable = 10<<11,
|
||||
TxReset = 11<<11,
|
||||
FakeIntr = 12<<11,
|
||||
AckIntr = 13<<11,
|
||||
SetIntrEnb = 14<<11,
|
||||
SetStatusEnb = 15<<11,
|
||||
SetRxFilter = 16<<11,
|
||||
SetRxThreshold = 17<<11,
|
||||
SetTxThreshold = 18<<11,
|
||||
SetTxStart = 19<<11,
|
||||
StatsEnable = 21<<11,
|
||||
StatsDisable = 22<<11,
|
||||
StopCoax = 23<<11
|
||||
};
|
||||
|
||||
enum c509status {
|
||||
IntLatch = 0x0001,
|
||||
AdapterFailure = 0x0002,
|
||||
TxComplete = 0x0004,
|
||||
TxAvailable = 0x0008,
|
||||
RxComplete = 0x0010,
|
||||
RxEarly = 0x0020,
|
||||
IntReq = 0x0040,
|
||||
StatsFull = 0x0080,
|
||||
CmdBusy = 0x1000
|
||||
};
|
||||
|
||||
/* The SetRxFilter command accepts the following classes: */
|
||||
enum RxFilter {
|
||||
RxStation = 1,
|
||||
RxMulticast = 2,
|
||||
RxBroadcast = 4,
|
||||
RxProm = 8
|
||||
};
|
||||
|
||||
/* Register window 1 offsets, the window used in normal operation. */
|
||||
#define TX_FIFO 0x00
|
||||
#define RX_FIFO 0x00
|
||||
#define RX_STATUS 0x08
|
||||
#define TX_STATUS 0x0B
|
||||
#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
|
||||
|
||||
#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
|
||||
#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
|
||||
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
|
||||
#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */
|
||||
|
||||
/* Time in jiffies before concluding Tx hung */
|
||||
#define TX_TIMEOUT ((400*HZ)/1000)
|
||||
|
||||
struct el3_private {
|
||||
struct pcmcia_device *p_dev;
|
||||
/* For transceiver monitoring */
|
||||
struct timer_list media;
|
||||
u16 media_status;
|
||||
u16 fast_poll;
|
||||
unsigned long last_irq;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Module parameters */
|
||||
|
||||
MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
|
||||
MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
|
||||
|
||||
/* Special hook for setting if_port when module is loaded */
|
||||
INT_MODULE_PARM(if_port, 0);
|
||||
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
static int tc589_config(struct pcmcia_device *link);
|
||||
static void tc589_release(struct pcmcia_device *link);
|
||||
|
||||
static u16 read_eeprom(unsigned int ioaddr, int index);
|
||||
static void tc589_reset(struct net_device *dev);
|
||||
static void media_check(struct timer_list *t);
|
||||
static int el3_config(struct net_device *dev, struct ifmap *map);
|
||||
static int el3_open(struct net_device *dev);
|
||||
static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
static irqreturn_t el3_interrupt(int irq, void *dev_id);
|
||||
static void update_stats(struct net_device *dev);
|
||||
static struct net_device_stats *el3_get_stats(struct net_device *dev);
|
||||
static int el3_rx(struct net_device *dev);
|
||||
static int el3_close(struct net_device *dev);
|
||||
static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
|
||||
static void set_rx_mode(struct net_device *dev);
|
||||
static void set_multicast_list(struct net_device *dev);
|
||||
static const struct ethtool_ops netdev_ethtool_ops;
|
||||
|
||||
static void tc589_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
static const struct net_device_ops el3_netdev_ops = {
|
||||
.ndo_open = el3_open,
|
||||
.ndo_stop = el3_close,
|
||||
.ndo_start_xmit = el3_start_xmit,
|
||||
.ndo_tx_timeout = el3_tx_timeout,
|
||||
.ndo_set_config = el3_config,
|
||||
.ndo_get_stats = el3_get_stats,
|
||||
.ndo_set_rx_mode = set_multicast_list,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
static int tc589_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct el3_private *lp;
|
||||
struct net_device *dev;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&link->dev, "3c589_attach()\n");
|
||||
|
||||
/* Create new ethernet device */
|
||||
dev = alloc_etherdev(sizeof(struct el3_private));
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
lp = netdev_priv(dev);
|
||||
link->priv = dev;
|
||||
lp->p_dev = link;
|
||||
|
||||
spin_lock_init(&lp->lock);
|
||||
link->resource[0]->end = 16;
|
||||
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ;
|
||||
link->config_index = 1;
|
||||
|
||||
dev->netdev_ops = &el3_netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
|
||||
dev->ethtool_ops = &netdev_ethtool_ops;
|
||||
|
||||
ret = tc589_config(link);
|
||||
if (ret)
|
||||
goto err_free_netdev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_netdev:
|
||||
free_netdev(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tc589_detach(struct pcmcia_device *link)
|
||||
{
|
||||
struct net_device *dev = link->priv;
|
||||
|
||||
dev_dbg(&link->dev, "3c589_detach\n");
|
||||
|
||||
unregister_netdev(dev);
|
||||
|
||||
tc589_release(link);
|
||||
|
||||
free_netdev(dev);
|
||||
} /* tc589_detach */
|
||||
|
||||
static int tc589_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct net_device *dev = link->priv;
|
||||
int ret, i, j, multi = 0, fifo;
|
||||
__be16 addr[ETH_ALEN / 2];
|
||||
unsigned int ioaddr;
|
||||
static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
|
||||
u8 *buf;
|
||||
size_t len;
|
||||
|
||||
dev_dbg(&link->dev, "3c589_config\n");
|
||||
|
||||
/* Is this a 3c562? */
|
||||
if (link->manf_id != MANFID_3COM)
|
||||
dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
|
||||
multi = (link->card_id == PRODID_3COM_3C562);
|
||||
|
||||
link->io_lines = 16;
|
||||
|
||||
/* For the 3c562, the base address must be xx00-xx7f */
|
||||
for (i = j = 0; j < 0x400; j += 0x10) {
|
||||
if (multi && (j & 0x80))
|
||||
continue;
|
||||
link->resource[0]->start = j ^ 0x300;
|
||||
i = pcmcia_request_io(link);
|
||||
if (i == 0)
|
||||
break;
|
||||
}
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
ret = pcmcia_request_irq(link, el3_interrupt);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
dev->irq = link->irq;
|
||||
dev->base_addr = link->resource[0]->start;
|
||||
ioaddr = dev->base_addr;
|
||||
EL3WINDOW(0);
|
||||
|
||||
/* The 3c589 has an extra EEPROM for configuration info, including
|
||||
* the hardware address. The 3c562 puts the address in the CIS.
|
||||
*/
|
||||
len = pcmcia_get_tuple(link, 0x88, &buf);
|
||||
if (buf && len >= 6) {
|
||||
for (i = 0; i < 3; i++)
|
||||
addr[i] = htons(le16_to_cpu(buf[i*2]));
|
||||
kfree(buf);
|
||||
} else {
|
||||
kfree(buf); /* 0 < len < 6 */
|
||||
for (i = 0; i < 3; i++)
|
||||
addr[i] = htons(read_eeprom(ioaddr, i));
|
||||
if (addr[0] == htons(0x6060)) {
|
||||
dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
|
||||
dev->base_addr, dev->base_addr+15);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
eth_hw_addr_set(dev, (u8 *)addr);
|
||||
|
||||
/* The address and resource configuration register aren't loaded from
|
||||
* the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
|
||||
*/
|
||||
|
||||
outw(0x3f00, ioaddr + 8);
|
||||
fifo = inl(ioaddr);
|
||||
|
||||
/* The if_port symbol can be set when the module is loaded */
|
||||
if ((if_port >= 0) && (if_port <= 3))
|
||||
dev->if_port = if_port;
|
||||
else
|
||||
dev_err(&link->dev, "invalid if_port requested\n");
|
||||
|
||||
SET_NETDEV_DEV(dev, &link->dev);
|
||||
|
||||
if (register_netdev(dev) != 0) {
|
||||
dev_err(&link->dev, "register_netdev() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
|
||||
(multi ? "562" : "589"), dev->base_addr, dev->irq,
|
||||
dev->dev_addr);
|
||||
netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n",
|
||||
(fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
|
||||
if_names[dev->if_port]);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
tc589_release(link);
|
||||
return -ENODEV;
|
||||
} /* tc589_config */
|
||||
|
||||
static void tc589_release(struct pcmcia_device *link)
|
||||
{
|
||||
pcmcia_disable_device(link);
|
||||
}
|
||||
|
||||
static int tc589_suspend(struct pcmcia_device *link)
|
||||
{
|
||||
struct net_device *dev = link->priv;
|
||||
|
||||
if (link->open)
|
||||
netif_device_detach(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc589_resume(struct pcmcia_device *link)
|
||||
{
|
||||
struct net_device *dev = link->priv;
|
||||
|
||||
if (link->open) {
|
||||
tc589_reset(dev);
|
||||
netif_device_attach(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Use this for commands that may take time to finish */
|
||||
|
||||
static void tc589_wait_for_completion(struct net_device *dev, int cmd)
|
||||
{
|
||||
int i = 100;
|
||||
outw(cmd, dev->base_addr + EL3_CMD);
|
||||
while (--i > 0)
|
||||
if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000))
|
||||
break;
|
||||
if (i == 0)
|
||||
netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
|
||||
}
|
||||
|
||||
/* Read a word from the EEPROM using the regular EEPROM access register.
|
||||
* Assume that we are in register window zero.
|
||||
*/
|
||||
|
||||
static u16 read_eeprom(unsigned int ioaddr, int index)
|
||||
{
|
||||
int i;
|
||||
outw(EEPROM_READ + index, ioaddr + 10);
|
||||
/* Reading the eeprom takes 162 us */
|
||||
for (i = 1620; i >= 0; i--)
|
||||
if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
|
||||
break;
|
||||
return inw(ioaddr + 12);
|
||||
}
|
||||
|
||||
/* Set transceiver type, perhaps to something other than what the user
|
||||
* specified in dev->if_port.
|
||||
*/
|
||||
|
||||
static void tc589_set_xcvr(struct net_device *dev, int if_port)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
|
||||
EL3WINDOW(0);
|
||||
switch (if_port) {
|
||||
case 0:
|
||||
case 1:
|
||||
outw(0, ioaddr + 6);
|
||||
break;
|
||||
case 2:
|
||||
outw(3<<14, ioaddr + 6);
|
||||
break;
|
||||
case 3:
|
||||
outw(1<<14, ioaddr + 6);
|
||||
break;
|
||||
}
|
||||
/* On PCMCIA, this just turns on the LED */
|
||||
outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
|
||||
/* 10baseT interface, enable link beat and jabber check. */
|
||||
EL3WINDOW(4);
|
||||
outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
|
||||
EL3WINDOW(1);
|
||||
if (if_port == 2)
|
||||
lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
|
||||
else
|
||||
lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
|
||||
}
|
||||
|
||||
static void dump_status(struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
EL3WINDOW(1);
|
||||
netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n",
|
||||
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
|
||||
inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
|
||||
EL3WINDOW(4);
|
||||
netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
|
||||
inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
|
||||
inw(ioaddr+0x0a));
|
||||
EL3WINDOW(1);
|
||||
}
|
||||
|
||||
/* Reset and restore all of the 3c589 registers. */
|
||||
static void tc589_reset(struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
int i;
|
||||
|
||||
EL3WINDOW(0);
|
||||
outw(0x0001, ioaddr + 4); /* Activate board. */
|
||||
outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */
|
||||
|
||||
/* Set the station address in window 2. */
|
||||
EL3WINDOW(2);
|
||||
for (i = 0; i < 6; i++)
|
||||
outb(dev->dev_addr[i], ioaddr + i);
|
||||
|
||||
tc589_set_xcvr(dev, dev->if_port);
|
||||
|
||||
/* Switch to the stats window, and clear all stats by reading. */
|
||||
outw(StatsDisable, ioaddr + EL3_CMD);
|
||||
EL3WINDOW(6);
|
||||
for (i = 0; i < 9; i++)
|
||||
inb(ioaddr+i);
|
||||
inw(ioaddr + 10);
|
||||
inw(ioaddr + 12);
|
||||
|
||||
/* Switch to register set 1 for normal use. */
|
||||
EL3WINDOW(1);
|
||||
|
||||
set_rx_mode(dev);
|
||||
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
|
||||
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
|
||||
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
|
||||
/* Allow status bits to be seen. */
|
||||
outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
|
||||
/* Ack all pending events, and set active indicator mask. */
|
||||
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
|
||||
ioaddr + EL3_CMD);
|
||||
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
|
||||
| AdapterFailure, ioaddr + EL3_CMD);
|
||||
}
|
||||
|
||||
static void netdev_get_drvinfo(struct net_device *dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
strscpy(info->driver, DRV_NAME, sizeof(info->driver));
|
||||
snprintf(info->bus_info, sizeof(info->bus_info),
|
||||
"PCMCIA 0x%lx", dev->base_addr);
|
||||
}
|
||||
|
||||
static const struct ethtool_ops netdev_ethtool_ops = {
|
||||
.get_drvinfo = netdev_get_drvinfo,
|
||||
};
|
||||
|
||||
static int el3_config(struct net_device *dev, struct ifmap *map)
|
||||
{
|
||||
if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
|
||||
if (map->port <= 3) {
|
||||
WRITE_ONCE(dev->if_port, map->port);
|
||||
netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
|
||||
tc589_set_xcvr(dev, dev->if_port);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int el3_open(struct net_device *dev)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
struct pcmcia_device *link = lp->p_dev;
|
||||
|
||||
if (!pcmcia_dev_present(link))
|
||||
return -ENODEV;
|
||||
|
||||
link->open++;
|
||||
netif_start_queue(dev);
|
||||
|
||||
tc589_reset(dev);
|
||||
timer_setup(&lp->media, media_check, 0);
|
||||
mod_timer(&lp->media, jiffies + HZ);
|
||||
|
||||
dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
|
||||
dev->name, inw(dev->base_addr + EL3_STATUS));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
|
||||
netdev_warn(dev, "Transmit timed out!\n");
|
||||
dump_status(dev);
|
||||
dev->stats.tx_errors++;
|
||||
netif_trans_update(dev); /* prevent tx timeout */
|
||||
/* Issue TX_RESET and TX_START commands. */
|
||||
tc589_wait_for_completion(dev, TxReset);
|
||||
outw(TxEnable, ioaddr + EL3_CMD);
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static void pop_tx_status(struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
int i;
|
||||
|
||||
/* Clear the Tx status stack. */
|
||||
for (i = 32; i > 0; i--) {
|
||||
u_char tx_status = inb(ioaddr + TX_STATUS);
|
||||
if (!(tx_status & 0x84))
|
||||
break;
|
||||
/* reset transmitter on jabber error or underrun */
|
||||
if (tx_status & 0x30)
|
||||
tc589_wait_for_completion(dev, TxReset);
|
||||
if (tx_status & 0x38) {
|
||||
netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
|
||||
outw(TxEnable, ioaddr + EL3_CMD);
|
||||
dev->stats.tx_aborted_errors++;
|
||||
}
|
||||
outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
|
||||
}
|
||||
}
|
||||
|
||||
static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
struct el3_private *priv = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
|
||||
netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
|
||||
(long)skb->len, inw(ioaddr + EL3_STATUS));
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
||||
/* Put out the doubleword header... */
|
||||
outw(skb->len, ioaddr + TX_FIFO);
|
||||
outw(0x00, ioaddr + TX_FIFO);
|
||||
/* ... and the packet rounded to a doubleword. */
|
||||
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
|
||||
|
||||
if (inw(ioaddr + TX_FREE) <= 1536) {
|
||||
netif_stop_queue(dev);
|
||||
/* Interrupt us when the FIFO has room for max-sized packet. */
|
||||
outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
|
||||
}
|
||||
|
||||
pop_tx_status(dev);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* The EL3 interrupt handler. */
|
||||
static irqreturn_t el3_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = (struct net_device *) dev_id;
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
unsigned int ioaddr;
|
||||
__u16 status;
|
||||
int i = 0, handled = 1;
|
||||
|
||||
if (!netif_device_present(dev))
|
||||
return IRQ_NONE;
|
||||
|
||||
ioaddr = dev->base_addr;
|
||||
|
||||
netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
while ((status = inw(ioaddr + EL3_STATUS)) &
|
||||
(IntLatch | RxComplete | StatsFull)) {
|
||||
if ((status & 0xe000) != 0x2000) {
|
||||
netdev_dbg(dev, "interrupt from dead card\n");
|
||||
handled = 0;
|
||||
break;
|
||||
}
|
||||
if (status & RxComplete)
|
||||
el3_rx(dev);
|
||||
if (status & TxAvailable) {
|
||||
netdev_dbg(dev, " TX room bit was handled.\n");
|
||||
/* There's room in the FIFO for a full-sized packet. */
|
||||
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
if (status & TxComplete)
|
||||
pop_tx_status(dev);
|
||||
if (status & (AdapterFailure | RxEarly | StatsFull)) {
|
||||
/* Handle all uncommon interrupts. */
|
||||
if (status & StatsFull) /* Empty statistics. */
|
||||
update_stats(dev);
|
||||
if (status & RxEarly) {
|
||||
/* Rx early is unused. */
|
||||
el3_rx(dev);
|
||||
outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
|
||||
}
|
||||
if (status & AdapterFailure) {
|
||||
u16 fifo_diag;
|
||||
EL3WINDOW(4);
|
||||
fifo_diag = inw(ioaddr + 4);
|
||||
EL3WINDOW(1);
|
||||
netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
|
||||
fifo_diag);
|
||||
if (fifo_diag & 0x0400) {
|
||||
/* Tx overrun */
|
||||
tc589_wait_for_completion(dev, TxReset);
|
||||
outw(TxEnable, ioaddr + EL3_CMD);
|
||||
}
|
||||
if (fifo_diag & 0x2000) {
|
||||
/* Rx underrun */
|
||||
tc589_wait_for_completion(dev, RxReset);
|
||||
set_rx_mode(dev);
|
||||
outw(RxEnable, ioaddr + EL3_CMD);
|
||||
}
|
||||
outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
|
||||
}
|
||||
}
|
||||
if (++i > 10) {
|
||||
netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
|
||||
status);
|
||||
/* Clear all interrupts */
|
||||
outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
|
||||
break;
|
||||
}
|
||||
/* Acknowledge the IRQ. */
|
||||
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
|
||||
}
|
||||
lp->last_irq = jiffies;
|
||||
spin_unlock(&lp->lock);
|
||||
netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
|
||||
inw(ioaddr + EL3_STATUS));
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
static void media_check(struct timer_list *t)
|
||||
{
|
||||
struct el3_private *lp = timer_container_of(lp, t, media);
|
||||
struct net_device *dev = lp->p_dev->priv;
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
u16 media, errs;
|
||||
unsigned long flags;
|
||||
|
||||
if (!netif_device_present(dev))
|
||||
goto reschedule;
|
||||
|
||||
/* Check for pending interrupt with expired latency timer: with
|
||||
* this, we can limp along even if the interrupt is blocked
|
||||
*/
|
||||
if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
|
||||
(inb(ioaddr + EL3_TIMER) == 0xff)) {
|
||||
if (!lp->fast_poll)
|
||||
netdev_warn(dev, "interrupt(s) dropped!\n");
|
||||
|
||||
local_irq_save(flags);
|
||||
el3_interrupt(dev->irq, dev);
|
||||
local_irq_restore(flags);
|
||||
|
||||
lp->fast_poll = HZ;
|
||||
}
|
||||
if (lp->fast_poll) {
|
||||
lp->fast_poll--;
|
||||
lp->media.expires = jiffies + HZ/100;
|
||||
add_timer(&lp->media);
|
||||
return;
|
||||
}
|
||||
|
||||
/* lp->lock guards the EL3 window. Window should always be 1 except
|
||||
* when the lock is held
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
EL3WINDOW(4);
|
||||
media = inw(ioaddr+WN4_MEDIA) & 0xc810;
|
||||
|
||||
/* Ignore collisions unless we've had no irq's recently */
|
||||
if (time_before(jiffies, lp->last_irq + HZ)) {
|
||||
media &= ~0x0010;
|
||||
} else {
|
||||
/* Try harder to detect carrier errors */
|
||||
EL3WINDOW(6);
|
||||
outw(StatsDisable, ioaddr + EL3_CMD);
|
||||
errs = inb(ioaddr + 0);
|
||||
outw(StatsEnable, ioaddr + EL3_CMD);
|
||||
dev->stats.tx_carrier_errors += errs;
|
||||
if (errs || (lp->media_status & 0x0010))
|
||||
media |= 0x0010;
|
||||
}
|
||||
|
||||
if (media != lp->media_status) {
|
||||
if ((media & lp->media_status & 0x8000) &&
|
||||
((lp->media_status ^ media) & 0x0800))
|
||||
netdev_info(dev, "%s link beat\n",
|
||||
(lp->media_status & 0x0800 ? "lost" : "found"));
|
||||
else if ((media & lp->media_status & 0x4000) &&
|
||||
((lp->media_status ^ media) & 0x0010))
|
||||
netdev_info(dev, "coax cable %s\n",
|
||||
(lp->media_status & 0x0010 ? "ok" : "problem"));
|
||||
if (dev->if_port == 0) {
|
||||
if (media & 0x8000) {
|
||||
if (media & 0x0800)
|
||||
netdev_info(dev, "flipped to 10baseT\n");
|
||||
else
|
||||
tc589_set_xcvr(dev, 2);
|
||||
} else if (media & 0x4000) {
|
||||
if (media & 0x0010)
|
||||
tc589_set_xcvr(dev, 1);
|
||||
else
|
||||
netdev_info(dev, "flipped to 10base2\n");
|
||||
}
|
||||
}
|
||||
lp->media_status = media;
|
||||
}
|
||||
|
||||
EL3WINDOW(1);
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
reschedule:
|
||||
lp->media.expires = jiffies + HZ;
|
||||
add_timer(&lp->media);
|
||||
}
|
||||
|
||||
static struct net_device_stats *el3_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
struct pcmcia_device *link = lp->p_dev;
|
||||
|
||||
if (pcmcia_dev_present(link)) {
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
update_stats(dev);
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
}
|
||||
return &dev->stats;
|
||||
}
|
||||
|
||||
/* Update statistics. We change to register window 6, so this should be run
|
||||
* single-threaded if the device is active. This is expected to be a rare
|
||||
* operation, and it's simpler for the rest of the driver to assume that
|
||||
* window 1 is always valid rather than use a special window-state variable.
|
||||
*
|
||||
* Caller must hold the lock for this
|
||||
*/
|
||||
|
||||
static void update_stats(struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
|
||||
netdev_dbg(dev, "updating the statistics.\n");
|
||||
/* Turn off statistics updates while reading. */
|
||||
outw(StatsDisable, ioaddr + EL3_CMD);
|
||||
/* Switch to the stats window, and read everything. */
|
||||
EL3WINDOW(6);
|
||||
dev->stats.tx_carrier_errors += inb(ioaddr + 0);
|
||||
dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
|
||||
/* Multiple collisions. */
|
||||
inb(ioaddr + 2);
|
||||
dev->stats.collisions += inb(ioaddr + 3);
|
||||
dev->stats.tx_window_errors += inb(ioaddr + 4);
|
||||
dev->stats.rx_fifo_errors += inb(ioaddr + 5);
|
||||
dev->stats.tx_packets += inb(ioaddr + 6);
|
||||
/* Rx packets */
|
||||
inb(ioaddr + 7);
|
||||
/* Tx deferrals */
|
||||
inb(ioaddr + 8);
|
||||
/* Rx octets */
|
||||
inw(ioaddr + 10);
|
||||
/* Tx octets */
|
||||
inw(ioaddr + 12);
|
||||
|
||||
/* Back to window 1, and turn statistics back on. */
|
||||
EL3WINDOW(1);
|
||||
outw(StatsEnable, ioaddr + EL3_CMD);
|
||||
}
|
||||
|
||||
static int el3_rx(struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
int worklimit = 32;
|
||||
short rx_status;
|
||||
|
||||
netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
|
||||
inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
|
||||
while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
|
||||
worklimit > 0) {
|
||||
worklimit--;
|
||||
if (rx_status & 0x4000) { /* Error, update stats. */
|
||||
short error = rx_status & 0x3800;
|
||||
dev->stats.rx_errors++;
|
||||
switch (error) {
|
||||
case 0x0000:
|
||||
dev->stats.rx_over_errors++;
|
||||
break;
|
||||
case 0x0800:
|
||||
dev->stats.rx_length_errors++;
|
||||
break;
|
||||
case 0x1000:
|
||||
dev->stats.rx_frame_errors++;
|
||||
break;
|
||||
case 0x1800:
|
||||
dev->stats.rx_length_errors++;
|
||||
break;
|
||||
case 0x2000:
|
||||
dev->stats.rx_frame_errors++;
|
||||
break;
|
||||
case 0x2800:
|
||||
dev->stats.rx_crc_errors++;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
short pkt_len = rx_status & 0x7ff;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = netdev_alloc_skb(dev, pkt_len + 5);
|
||||
|
||||
netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n",
|
||||
pkt_len, rx_status);
|
||||
if (skb != NULL) {
|
||||
skb_reserve(skb, 2);
|
||||
insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
|
||||
(pkt_len+3)>>2);
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
netif_rx(skb);
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += pkt_len;
|
||||
} else {
|
||||
netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
|
||||
pkt_len);
|
||||
dev->stats.rx_dropped++;
|
||||
}
|
||||
}
|
||||
/* Pop the top of the Rx FIFO */
|
||||
tc589_wait_for_completion(dev, RxDiscard);
|
||||
}
|
||||
if (worklimit == 0)
|
||||
netdev_warn(dev, "too much work in el3_rx!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_rx_mode(struct net_device *dev)
|
||||
{
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
u16 opts = SetRxFilter | RxStation | RxBroadcast;
|
||||
|
||||
if (dev->flags & IFF_PROMISC)
|
||||
opts |= RxMulticast | RxProm;
|
||||
else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
|
||||
opts |= RxMulticast;
|
||||
outw(opts, ioaddr + EL3_CMD);
|
||||
}
|
||||
|
||||
static void set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
struct el3_private *priv = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
set_rx_mode(dev);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static int el3_close(struct net_device *dev)
|
||||
{
|
||||
struct el3_private *lp = netdev_priv(dev);
|
||||
struct pcmcia_device *link = lp->p_dev;
|
||||
unsigned int ioaddr = dev->base_addr;
|
||||
|
||||
dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
|
||||
|
||||
if (pcmcia_dev_present(link)) {
|
||||
/* Turn off statistics ASAP. We update dev->stats below. */
|
||||
outw(StatsDisable, ioaddr + EL3_CMD);
|
||||
|
||||
/* Disable the receiver and transmitter. */
|
||||
outw(RxDisable, ioaddr + EL3_CMD);
|
||||
outw(TxDisable, ioaddr + EL3_CMD);
|
||||
|
||||
if (dev->if_port == 2)
|
||||
/* Turn off thinnet power. Green! */
|
||||
outw(StopCoax, ioaddr + EL3_CMD);
|
||||
else if (dev->if_port == 1) {
|
||||
/* Disable link beat and jabber */
|
||||
EL3WINDOW(4);
|
||||
outw(0, ioaddr + WN4_MEDIA);
|
||||
}
|
||||
|
||||
/* Switching back to window 0 disables the IRQ. */
|
||||
EL3WINDOW(0);
|
||||
/* But we explicitly zero the IRQ line select anyway. */
|
||||
outw(0x0f00, ioaddr + WN0_IRQ);
|
||||
|
||||
/* Check if the card still exists */
|
||||
if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
|
||||
update_stats(dev);
|
||||
}
|
||||
|
||||
link->open--;
|
||||
netif_stop_queue(dev);
|
||||
timer_delete_sync(&lp->media);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pcmcia_device_id tc589_ids[] = {
|
||||
PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562),
|
||||
PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589),
|
||||
PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202),
|
||||
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"),
|
||||
PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"),
|
||||
PCMCIA_DEVICE_NULL,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, tc589_ids);
|
||||
|
||||
static struct pcmcia_driver tc589_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "3c589_cs",
|
||||
.probe = tc589_probe,
|
||||
.remove = tc589_detach,
|
||||
.id_table = tc589_ids,
|
||||
.suspend = tc589_suspend,
|
||||
.resume = tc589_resume,
|
||||
};
|
||||
module_pcmcia_driver(tc589_driver);
|
||||
|
|
@ -17,51 +17,6 @@ config NET_VENDOR_3COM
|
|||
|
||||
if NET_VENDOR_3COM
|
||||
|
||||
config EL3
|
||||
tristate "3c509/3c579 \"EtherLink III\" support"
|
||||
depends on (ISA || EISA)
|
||||
help
|
||||
If you have a network (Ethernet) card belonging to the 3Com
|
||||
EtherLinkIII series, say Y here.
|
||||
|
||||
If your card is not working you may need to use the DOS
|
||||
setup disk to disable Plug & Play mode, and to select the default
|
||||
media type.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called 3c509.
|
||||
|
||||
config 3C515
|
||||
tristate "3c515 ISA \"Fast EtherLink\""
|
||||
depends on ISA && ISA_DMA_API && !PPC32
|
||||
select NETDEV_LEGACY_INIT
|
||||
help
|
||||
If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
|
||||
network card, say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called 3c515.
|
||||
|
||||
config PCMCIA_3C574
|
||||
tristate "3Com 3c574 PCMCIA support"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
help
|
||||
Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
|
||||
(PC-card) Fast Ethernet card to your computer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called 3c574_cs. If unsure, say N.
|
||||
|
||||
config PCMCIA_3C589
|
||||
tristate "3Com 3c589 PCMCIA support"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
help
|
||||
Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
|
||||
(PC-card) Ethernet card to your computer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called 3c589_cs. If unsure, say N.
|
||||
|
||||
config VORTEX
|
||||
tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
|
||||
depends on (PCI || EISA) && HAS_IOPORT_MAP
|
||||
|
|
|
|||
|
|
@ -3,9 +3,5 @@
|
|||
# Makefile for the 3Com Ethernet device drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_EL3) += 3c509.o
|
||||
obj-$(CONFIG_3C515) += 3c515.o
|
||||
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
|
||||
obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
|
||||
obj-$(CONFIG_VORTEX) += 3c59x.o
|
||||
obj-$(CONFIG_TYPHOON) += typhoon.o
|
||||
|
|
|
|||
|
|
@ -17,18 +17,6 @@ config NET_VENDOR_8390
|
|||
|
||||
if NET_VENDOR_8390
|
||||
|
||||
config PCMCIA_AXNET
|
||||
tristate "Asix AX88190 PCMCIA support"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
help
|
||||
Say Y here if you intend to attach an Asix AX88190-based PCMCIA
|
||||
(PC-card) Fast Ethernet card to your computer. These cards are
|
||||
nearly NE2000 compatible but need a separate driver due to a few
|
||||
misfeatures.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called axnet_cs. If unsure, say N.
|
||||
|
||||
config AX88796
|
||||
tristate "ASIX AX88796 NE2000 clone support" if !ZORRO
|
||||
depends on (ARM || MIPS || SUPERH || ZORRO || COMPILE_TEST)
|
||||
|
|
@ -167,35 +155,6 @@ config STNIC
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config ULTRA
|
||||
tristate "SMC Ultra support"
|
||||
depends on ISA
|
||||
select NETDEV_LEGACY_INIT
|
||||
select CRC32
|
||||
help
|
||||
If you have a network (Ethernet) card of this type, say Y here.
|
||||
|
||||
Important: There have been many reports that, with some motherboards
|
||||
mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible,
|
||||
such as some BusLogic models) causes corruption problems with many
|
||||
operating systems. The Linux smc-ultra driver has a work-around for
|
||||
this but keep it in mind if you have such a SCSI card and have
|
||||
problems.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called smc-ultra.
|
||||
|
||||
config WD80x3
|
||||
tristate "WD80*3 support"
|
||||
depends on ISA
|
||||
select NETDEV_LEGACY_INIT
|
||||
select CRC32
|
||||
help
|
||||
If you have a network (Ethernet) card of this type, say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called wd.
|
||||
|
||||
config ZORRO8390
|
||||
tristate "Zorro NS8390-based Ethernet support"
|
||||
depends on ZORRO
|
||||
|
|
|
|||
|
|
@ -11,10 +11,7 @@ obj-$(CONFIG_HYDRA) += hydra.o
|
|||
obj-$(CONFIG_MCF8390) += mcf8390.o
|
||||
obj-$(CONFIG_NE2000) += ne.o 8390p.o
|
||||
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
|
||||
obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o
|
||||
obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o
|
||||
obj-$(CONFIG_STNIC) += stnic.o 8390.o
|
||||
obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o
|
||||
obj-$(CONFIG_WD80x3) += wd.o 8390.o
|
||||
obj-$(CONFIG_XSURF100) += xsurf100.o
|
||||
obj-$(CONFIG_ZORRO8390) += zorro8390.o
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,630 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-1.0+
|
||||
/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
|
||||
/*
|
||||
This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
|
||||
|
||||
Written 1993-1998 by Donald Becker.
|
||||
|
||||
Copyright 1993 United States Government as represented by the
|
||||
Director, National Security Agency.
|
||||
|
||||
The author may be reached as becker@scyld.com, or C/O
|
||||
Scyld Computing Corporation
|
||||
410 Severn Ave., Suite 210
|
||||
Annapolis MD 21403
|
||||
|
||||
This driver uses the cards in the 8390-compatible mode.
|
||||
Most of the run-time complexity is handled by the generic code in
|
||||
8390.c. The code in this file is responsible for
|
||||
|
||||
ultra_probe() Detecting and initializing the card.
|
||||
ultra_probe1()
|
||||
ultra_probe_isapnp()
|
||||
|
||||
ultra_open() The card-specific details of starting, stopping
|
||||
ultra_reset_8390() and resetting the 8390 NIC core.
|
||||
ultra_close()
|
||||
|
||||
ultra_block_input() Routines for reading and writing blocks of
|
||||
ultra_block_output() packet buffer memory.
|
||||
ultra_pio_input()
|
||||
ultra_pio_output()
|
||||
|
||||
This driver enables the shared memory only when doing the actual data
|
||||
transfers to avoid a bug in early version of the card that corrupted
|
||||
data transferred by a AHA1542.
|
||||
|
||||
This driver now supports the programmed-I/O (PIO) data transfer mode of
|
||||
the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
|
||||
That support (if available) is in smc-ez.c.
|
||||
|
||||
Changelog:
|
||||
|
||||
Paul Gortmaker : multiple card support for module users.
|
||||
Donald Becker : 4/17/96 PIO support, minor potential problems avoided.
|
||||
Donald Becker : 6/6/96 correctly set auto-wrap bit.
|
||||
Alexander Sotirov : 1/20/01 Added support for ISAPnP cards
|
||||
|
||||
Note about the ISA PnP support:
|
||||
|
||||
This driver can not autoprobe for more than one SMC EtherEZ PnP card.
|
||||
You have to configure the second card manually through the /proc/isapnp
|
||||
interface and then load the module with an explicit io=0x___ option.
|
||||
*/
|
||||
|
||||
static const char version[] =
|
||||
"smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/isapnp.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <net/Space.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include "8390.h"
|
||||
|
||||
#define DRV_NAME "smc-ultra"
|
||||
|
||||
/* A zero-terminated list of I/O addresses to be probed. */
|
||||
static unsigned int ultra_portlist[] __initdata =
|
||||
{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
|
||||
|
||||
static int ultra_probe1(struct net_device *dev, int ioaddr);
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
static int ultra_probe_isapnp(struct net_device *dev);
|
||||
#endif
|
||||
|
||||
static int ultra_open(struct net_device *dev);
|
||||
static void ultra_reset_8390(struct net_device *dev);
|
||||
static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static void ultra_block_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset);
|
||||
static void ultra_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, const int start_page);
|
||||
static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static void ultra_pio_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset);
|
||||
static void ultra_pio_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, const int start_page);
|
||||
static int ultra_close_card(struct net_device *dev);
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
static struct isapnp_device_id ultra_device_ids[] __initdata = {
|
||||
{ ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
|
||||
ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416),
|
||||
(long) "SMC EtherEZ (8416)" },
|
||||
{ } /* terminate list */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(isapnp, ultra_device_ids);
|
||||
#endif
|
||||
|
||||
static u32 ultra_msg_enable;
|
||||
|
||||
#define START_PG 0x00 /* First page of TX buffer */
|
||||
|
||||
#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
|
||||
#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
|
||||
#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
|
||||
#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */
|
||||
#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */
|
||||
#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
|
||||
#define ULTRA_IO_EXTENT 32
|
||||
#define EN0_ERWCNT 0x08 /* Early receive warning count. */
|
||||
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
static void ultra_poll(struct net_device *dev)
|
||||
{
|
||||
disable_irq(dev->irq);
|
||||
ei_interrupt(dev->irq, dev);
|
||||
enable_irq(dev->irq);
|
||||
}
|
||||
#endif
|
||||
/* Probe for the Ultra. This looks like a 8013 with the station
|
||||
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
|
||||
following.
|
||||
*/
|
||||
|
||||
static int __init do_ultra_probe(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
int base_addr = dev->base_addr;
|
||||
int irq = dev->irq;
|
||||
|
||||
if (base_addr > 0x1ff) /* Check a single specified location. */
|
||||
return ultra_probe1(dev, base_addr);
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
/* Look for any installed ISAPnP cards */
|
||||
if (isapnp_present() && (ultra_probe_isapnp(dev) == 0))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
for (i = 0; ultra_portlist[i]; i++) {
|
||||
dev->irq = irq;
|
||||
if (ultra_probe1(dev, ultra_portlist[i]) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
struct net_device * __init ultra_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_ei_netdev();
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
err = do_ultra_probe(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
return dev;
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops ultra_netdev_ops = {
|
||||
.ndo_open = ultra_open,
|
||||
.ndo_stop = ultra_close_card,
|
||||
|
||||
.ndo_start_xmit = ei_start_xmit,
|
||||
.ndo_tx_timeout = ei_tx_timeout,
|
||||
.ndo_get_stats = ei_get_stats,
|
||||
.ndo_set_rx_mode = ei_set_multicast_list,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
.ndo_poll_controller = ultra_poll,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init ultra_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
int i, retval;
|
||||
int checksum = 0;
|
||||
u8 macaddr[ETH_ALEN];
|
||||
const char *model_name;
|
||||
unsigned char eeprom_irq = 0;
|
||||
static unsigned version_printed;
|
||||
/* Values from various config regs. */
|
||||
unsigned char num_pages, irqreg, addr, piomode;
|
||||
unsigned char idreg = inb(ioaddr + 7);
|
||||
unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
||||
if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME))
|
||||
return -EBUSY;
|
||||
|
||||
/* Check the ID nibble. */
|
||||
if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
|
||||
&& (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Select the station address register set. */
|
||||
outb(reg4, ioaddr + 4);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
checksum += inb(ioaddr + 8 + i);
|
||||
if ((checksum & 0xff) != 0xFF) {
|
||||
retval = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
|
||||
netdev_info(dev, version);
|
||||
|
||||
model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
macaddr[i] = inb(ioaddr + 8 + i);
|
||||
eth_hw_addr_set(dev, macaddr);
|
||||
|
||||
netdev_info(dev, "%s at %#3x, %pM", model_name,
|
||||
ioaddr, dev->dev_addr);
|
||||
|
||||
/* Switch from the station address to the alternate register set and
|
||||
read the useful registers there. */
|
||||
outb(0x80 | reg4, ioaddr + 4);
|
||||
|
||||
/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
|
||||
outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
|
||||
piomode = inb(ioaddr + 0x8);
|
||||
addr = inb(ioaddr + 0xb);
|
||||
irqreg = inb(ioaddr + 0xd);
|
||||
|
||||
/* Switch back to the station address register set so that the MS-DOS driver
|
||||
can find the card after a warm boot. */
|
||||
outb(reg4, ioaddr + 4);
|
||||
|
||||
if (dev->irq < 2) {
|
||||
unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
|
||||
int irq;
|
||||
|
||||
/* The IRQ bits are split. */
|
||||
irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
|
||||
|
||||
if (irq == 0) {
|
||||
pr_cont(", failed to detect IRQ line.\n");
|
||||
retval = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
dev->irq = irq;
|
||||
eeprom_irq = 1;
|
||||
}
|
||||
|
||||
/* The 8390 isn't at the base address, so fake the offset */
|
||||
dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
|
||||
|
||||
{
|
||||
static const int addr_tbl[4] = {
|
||||
0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000
|
||||
};
|
||||
static const short num_pages_tbl[4] = {
|
||||
0x20, 0x40, 0x80, 0xff
|
||||
};
|
||||
|
||||
dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
|
||||
num_pages = num_pages_tbl[(addr >> 4) & 3];
|
||||
}
|
||||
|
||||
ei_status.name = model_name;
|
||||
ei_status.word16 = 1;
|
||||
ei_status.tx_start_page = START_PG;
|
||||
ei_status.rx_start_page = START_PG + TX_PAGES;
|
||||
ei_status.stop_page = num_pages;
|
||||
|
||||
ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256);
|
||||
if (!ei_status.mem) {
|
||||
pr_cont(", failed to ioremap.\n");
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256;
|
||||
|
||||
if (piomode) {
|
||||
pr_cont(", %s IRQ %d programmed-I/O mode.\n",
|
||||
eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
|
||||
ei_status.block_input = &ultra_pio_input;
|
||||
ei_status.block_output = &ultra_pio_output;
|
||||
ei_status.get_8390_hdr = &ultra_pio_get_hdr;
|
||||
} else {
|
||||
pr_cont(", %s IRQ %d memory %#lx-%#lx.\n",
|
||||
eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start,
|
||||
dev->mem_end-1);
|
||||
ei_status.block_input = &ultra_block_input;
|
||||
ei_status.block_output = &ultra_block_output;
|
||||
ei_status.get_8390_hdr = &ultra_get_8390_hdr;
|
||||
}
|
||||
ei_status.reset_8390 = &ultra_reset_8390;
|
||||
|
||||
dev->netdev_ops = &ultra_netdev_ops;
|
||||
NS8390_init(dev, 0);
|
||||
ei_local->msg_enable = ultra_msg_enable;
|
||||
|
||||
retval = register_netdev(dev);
|
||||
if (retval)
|
||||
goto out;
|
||||
return 0;
|
||||
out:
|
||||
release_region(ioaddr, ULTRA_IO_EXTENT);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
static int __init ultra_probe_isapnp(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ultra_device_ids[i].vendor != 0; i++) {
|
||||
struct pnp_dev *idev = NULL;
|
||||
|
||||
while ((idev = pnp_find_dev(NULL,
|
||||
ultra_device_ids[i].vendor,
|
||||
ultra_device_ids[i].function,
|
||||
idev))) {
|
||||
/* Avoid already found cards from previous calls */
|
||||
if (pnp_device_attach(idev) < 0)
|
||||
continue;
|
||||
if (pnp_activate_dev(idev) < 0) {
|
||||
__again:
|
||||
pnp_device_detach(idev);
|
||||
continue;
|
||||
}
|
||||
/* if no io and irq, search for next */
|
||||
if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0))
|
||||
goto __again;
|
||||
/* found it */
|
||||
dev->base_addr = pnp_port_start(idev, 0);
|
||||
dev->irq = pnp_irq(idev, 0);
|
||||
netdev_info(dev,
|
||||
"smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
|
||||
(char *) ultra_device_ids[i].driver_data,
|
||||
dev->base_addr, dev->irq);
|
||||
if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
|
||||
netdev_err(dev,
|
||||
"smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n",
|
||||
dev->base_addr);
|
||||
pnp_device_detach(idev);
|
||||
return -ENXIO;
|
||||
}
|
||||
ei_status.priv = (unsigned long)idev;
|
||||
break;
|
||||
}
|
||||
if (!idev)
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
ultra_open(struct net_device *dev)
|
||||
{
|
||||
int retval;
|
||||
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
|
||||
unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40,
|
||||
0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, };
|
||||
|
||||
retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
outb(0x00, ioaddr); /* Disable shared memory for safety. */
|
||||
outb(0x80, ioaddr + 5);
|
||||
/* Set the IRQ line. */
|
||||
outb(inb(ioaddr + 4) | 0x80, ioaddr + 4);
|
||||
outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13);
|
||||
outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4);
|
||||
|
||||
if (ei_status.block_input == &ultra_pio_input) {
|
||||
outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */
|
||||
outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */
|
||||
} else
|
||||
outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
|
||||
/* Set the early receive warning level in window 0 high enough not
|
||||
to receive ERW interrupts. */
|
||||
outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
|
||||
outb(0xff, dev->base_addr + EN0_ERWCNT);
|
||||
ei_open(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ultra_reset_8390(struct net_device *dev)
|
||||
{
|
||||
int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
||||
outb(ULTRA_RESET, cmd_port);
|
||||
netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies);
|
||||
ei_status.txing = 0;
|
||||
|
||||
outb(0x00, cmd_port); /* Disable shared memory for safety. */
|
||||
outb(0x80, cmd_port + 5);
|
||||
if (ei_status.block_input == &ultra_pio_input)
|
||||
outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */
|
||||
else
|
||||
outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */
|
||||
|
||||
netif_dbg(ei_local, hw, dev, "reset done\n");
|
||||
}
|
||||
|
||||
/* Grab the 8390 specific header. Similar to the block_input routine, but
|
||||
we don't need to be concerned with ring wrap as the header will be at
|
||||
the start of a page, so we optimize accordingly. */
|
||||
|
||||
static void
|
||||
ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8);
|
||||
|
||||
outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */
|
||||
#ifdef __BIG_ENDIAN
|
||||
/* Officially this is what we are doing, but the readl() is faster */
|
||||
/* unfortunately it isn't endian aware of the struct */
|
||||
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
|
||||
hdr->count = le16_to_cpu(hdr->count);
|
||||
#else
|
||||
((unsigned int*)hdr)[0] = readl(hdr_start);
|
||||
#endif
|
||||
outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
|
||||
}
|
||||
|
||||
/* Block input and output are easy on shared memory ethercards, the only
|
||||
complication is when the ring buffer wraps. */
|
||||
|
||||
static void
|
||||
ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8);
|
||||
|
||||
/* Enable shared memory. */
|
||||
outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
|
||||
|
||||
if (ring_offset + count > ei_status.stop_page*256) {
|
||||
/* We must wrap the input move. */
|
||||
int semi_count = ei_status.stop_page*256 - ring_offset;
|
||||
memcpy_fromio(skb->data, xfer_start, semi_count);
|
||||
count -= semi_count;
|
||||
memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
|
||||
} else {
|
||||
memcpy_fromio(skb->data, xfer_start, count);
|
||||
}
|
||||
|
||||
outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
|
||||
}
|
||||
|
||||
static void
|
||||
ultra_block_output(struct net_device *dev, int count, const unsigned char *buf,
|
||||
int start_page)
|
||||
{
|
||||
void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8);
|
||||
|
||||
/* Enable shared memory. */
|
||||
outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
|
||||
|
||||
memcpy_toio(shmem, buf, count);
|
||||
|
||||
outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
|
||||
}
|
||||
|
||||
/* The identical operations for programmed I/O cards.
|
||||
The PIO model is trivial to use: the 16 bit start address is written
|
||||
byte-sequentially to IOPA, with no intervening I/O operations, and the
|
||||
data is read or written to the IOPD data port.
|
||||
The only potential complication is that the address register is shared
|
||||
and must be always be rewritten between each read/write direction change.
|
||||
This is no problem for us, as the 8390 code ensures that we are single
|
||||
threaded. */
|
||||
static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page)
|
||||
{
|
||||
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
|
||||
outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
|
||||
outb(ring_page, ioaddr + IOPA);
|
||||
insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
|
||||
}
|
||||
|
||||
static void ultra_pio_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
|
||||
char *buf = skb->data;
|
||||
|
||||
/* For now set the address again, although it should already be correct. */
|
||||
outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */
|
||||
outb(ring_offset >> 8, ioaddr + IOPA);
|
||||
/* We know skbuffs are padded to at least word alignment. */
|
||||
insw(ioaddr + IOPD, buf, (count+1)>>1);
|
||||
}
|
||||
static void ultra_pio_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, const int start_page)
|
||||
{
|
||||
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
|
||||
outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */
|
||||
outb(start_page, ioaddr + IOPA);
|
||||
/* An extra odd byte is OK here as well. */
|
||||
outsw(ioaddr + IOPD, buf, (count+1)>>1);
|
||||
}
|
||||
|
||||
static int
|
||||
ultra_close_card(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
|
||||
|
||||
outb(0x00, ioaddr + 6); /* Disable interrupts. */
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
NS8390_init(dev, 0);
|
||||
|
||||
/* We should someday disable shared memory and change to 8-bit mode
|
||||
"just in case"... */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */
|
||||
static struct net_device *dev_ultra[MAX_ULTRA_CARDS];
|
||||
static int io[MAX_ULTRA_CARDS];
|
||||
static int irq[MAX_ULTRA_CARDS];
|
||||
|
||||
module_param_hw_array(io, int, ioport, NULL, 0);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0);
|
||||
module_param_named(msg_enable, ultra_msg_enable, uint, 0444);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
|
||||
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
|
||||
MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* This is set up so that only a single autoprobe takes place per call.
|
||||
ISA device autoprobes on a running machine are not recommended. */
|
||||
static int __init ultra_init_module(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int this_dev, found = 0;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
|
||||
if (io[this_dev] == 0) {
|
||||
if (this_dev != 0) break; /* only autoprobe 1st one */
|
||||
printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
|
||||
}
|
||||
dev = alloc_ei_netdev();
|
||||
if (!dev)
|
||||
break;
|
||||
dev->irq = irq[this_dev];
|
||||
dev->base_addr = io[this_dev];
|
||||
if (do_ultra_probe(dev) == 0) {
|
||||
dev_ultra[found++] = dev;
|
||||
continue;
|
||||
}
|
||||
free_netdev(dev);
|
||||
printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
return 0;
|
||||
return -ENXIO;
|
||||
}
|
||||
module_init(ultra_init_module);
|
||||
|
||||
static void cleanup_card(struct net_device *dev)
|
||||
{
|
||||
/* NB: ultra_close_card() does free_irq */
|
||||
#ifdef __ISAPNP__
|
||||
struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
|
||||
if (idev)
|
||||
pnp_device_detach(idev);
|
||||
#endif
|
||||
release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT);
|
||||
iounmap(ei_status.mem);
|
||||
}
|
||||
|
||||
static void __exit ultra_cleanup_module(void)
|
||||
{
|
||||
int this_dev;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
|
||||
struct net_device *dev = dev_ultra[this_dev];
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
cleanup_card(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
module_exit(ultra_cleanup_module);
|
||||
#endif /* MODULE */
|
||||
|
|
@ -1,575 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-1.0+
|
||||
/* wd.c: A WD80x3 ethernet driver for linux. */
|
||||
/*
|
||||
Written 1993-94 by Donald Becker.
|
||||
|
||||
Copyright 1993 United States Government as represented by the
|
||||
Director, National Security Agency.
|
||||
|
||||
The author may be reached as becker@scyld.com, or C/O
|
||||
Scyld Computing Corporation
|
||||
410 Severn Ave., Suite 210
|
||||
Annapolis MD 21403
|
||||
|
||||
This is a driver for WD8003 and WD8013 "compatible" ethercards.
|
||||
|
||||
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
|
||||
|
||||
Changelog:
|
||||
|
||||
Paul Gortmaker : multiple card support for module users, support
|
||||
for non-standard memory sizes.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
static const char version[] =
|
||||
"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <net/Space.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "8390.h"
|
||||
|
||||
#define DRV_NAME "wd"
|
||||
|
||||
/* A zero-terminated list of I/O addresses to be probed. */
|
||||
static unsigned int wd_portlist[] __initdata =
|
||||
{0x300, 0x280, 0x380, 0x240, 0};
|
||||
|
||||
static int wd_probe1(struct net_device *dev, int ioaddr);
|
||||
|
||||
static int wd_open(struct net_device *dev);
|
||||
static void wd_reset_8390(struct net_device *dev);
|
||||
static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
|
||||
int ring_page);
|
||||
static void wd_block_input(struct net_device *dev, int count,
|
||||
struct sk_buff *skb, int ring_offset);
|
||||
static void wd_block_output(struct net_device *dev, int count,
|
||||
const unsigned char *buf, int start_page);
|
||||
static int wd_close(struct net_device *dev);
|
||||
|
||||
static u32 wd_msg_enable;
|
||||
|
||||
#define WD_START_PG 0x00 /* First page of TX buffer */
|
||||
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
|
||||
#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||
|
||||
#define WD_CMDREG 0 /* Offset to ASIC command register. */
|
||||
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
|
||||
#define WD_MEMENB 0x40 /* Enable the shared memory. */
|
||||
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
|
||||
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
|
||||
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
|
||||
#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
|
||||
#define WD_IO_EXTENT 32
|
||||
|
||||
|
||||
/* Probe for the WD8003 and WD8013. These cards have the station
|
||||
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
|
||||
following. A Soundblaster can have the same checksum as an WDethercard,
|
||||
so we have an extra exclusionary check for it.
|
||||
|
||||
The wd_probe1() routine initializes the card and fills the
|
||||
station address field. */
|
||||
|
||||
static int __init do_wd_probe(struct net_device *dev)
|
||||
{
|
||||
int i;
|
||||
struct resource *r;
|
||||
int base_addr = dev->base_addr;
|
||||
int irq = dev->irq;
|
||||
int mem_start = dev->mem_start;
|
||||
int mem_end = dev->mem_end;
|
||||
|
||||
if (base_addr > 0x1ff) { /* Check a user specified location. */
|
||||
r = request_region(base_addr, WD_IO_EXTENT, "wd-probe");
|
||||
if ( r == NULL)
|
||||
return -EBUSY;
|
||||
i = wd_probe1(dev, base_addr);
|
||||
if (i != 0)
|
||||
release_region(base_addr, WD_IO_EXTENT);
|
||||
else
|
||||
r->name = dev->name;
|
||||
return i;
|
||||
}
|
||||
else if (base_addr != 0) /* Don't probe at all. */
|
||||
return -ENXIO;
|
||||
|
||||
for (i = 0; wd_portlist[i]; i++) {
|
||||
int ioaddr = wd_portlist[i];
|
||||
r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe");
|
||||
if (r == NULL)
|
||||
continue;
|
||||
if (wd_probe1(dev, ioaddr) == 0) {
|
||||
r->name = dev->name;
|
||||
return 0;
|
||||
}
|
||||
release_region(ioaddr, WD_IO_EXTENT);
|
||||
dev->irq = irq;
|
||||
dev->mem_start = mem_start;
|
||||
dev->mem_end = mem_end;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
struct net_device * __init wd_probe(int unit)
|
||||
{
|
||||
struct net_device *dev = alloc_ei_netdev();
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sprintf(dev->name, "eth%d", unit);
|
||||
netdev_boot_setup_check(dev);
|
||||
|
||||
err = do_wd_probe(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
return dev;
|
||||
out:
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct net_device_ops wd_netdev_ops = {
|
||||
.ndo_open = wd_open,
|
||||
.ndo_stop = wd_close,
|
||||
.ndo_start_xmit = ei_start_xmit,
|
||||
.ndo_tx_timeout = ei_tx_timeout,
|
||||
.ndo_get_stats = ei_get_stats,
|
||||
.ndo_set_rx_mode = ei_set_multicast_list,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
.ndo_poll_controller = ei_poll,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init wd_probe1(struct net_device *dev, int ioaddr)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
int checksum = 0;
|
||||
int ancient = 0; /* An old card without config registers. */
|
||||
int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
|
||||
u8 addr[ETH_ALEN];
|
||||
const char *model_name;
|
||||
static unsigned version_printed;
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
checksum += inb(ioaddr + 8 + i);
|
||||
if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
|
||||
|| inb(ioaddr + 9) == 0xff
|
||||
|| (checksum & 0xff) != 0xFF)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check for semi-valid mem_start/end values if supplied. */
|
||||
if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
|
||||
netdev_warn(dev,
|
||||
"wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
|
||||
dev->mem_start = 0;
|
||||
dev->mem_end = 0;
|
||||
}
|
||||
|
||||
if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0))
|
||||
netdev_info(dev, version);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
addr[i] = inb(ioaddr + 8 + i);
|
||||
eth_hw_addr_set(dev, addr);
|
||||
|
||||
netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr);
|
||||
|
||||
/* The following PureData probe code was contributed by
|
||||
Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
|
||||
configuration differently from others so we have to check for them.
|
||||
This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
|
||||
*/
|
||||
if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
|
||||
unsigned char reg5 = inb(ioaddr+5);
|
||||
|
||||
switch (inb(ioaddr+2)) {
|
||||
case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
|
||||
case 0x05: word16 = 0; model_name = "PDUC8023"; break;
|
||||
case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
|
||||
/* Either 0x01 (dumb) or they've released a new version. */
|
||||
default: word16 = 0; model_name = "PDI8023"; break;
|
||||
}
|
||||
dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
|
||||
dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
|
||||
} else { /* End of PureData probe */
|
||||
/* This method of checking for a 16-bit board is borrowed from the
|
||||
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
|
||||
I'm comparing the two method in alpha test to make certain they
|
||||
return the same result. */
|
||||
/* Check for the old 8 bit board - it has register 0/8 aliasing.
|
||||
Do NOT check i>=6 here -- it hangs the old 8003 boards! */
|
||||
for (i = 0; i < 6; i++)
|
||||
if (inb(ioaddr+i) != inb(ioaddr+8+i))
|
||||
break;
|
||||
if (i >= 6) {
|
||||
ancient = 1;
|
||||
model_name = "WD8003-old";
|
||||
word16 = 0;
|
||||
} else {
|
||||
int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
|
||||
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
|
||||
if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
|
||||
&& (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
|
||||
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
|
||||
/* Magic to set ASIC to word-wide mode. */
|
||||
outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
|
||||
outb(tmp, ioaddr+1);
|
||||
model_name = "WD8013";
|
||||
word16 = 1; /* We have a 16bit board here! */
|
||||
} else {
|
||||
model_name = "WD8003";
|
||||
word16 = 0;
|
||||
}
|
||||
outb(tmp, ioaddr+1); /* Restore original reg1 value. */
|
||||
}
|
||||
#ifndef final_version
|
||||
if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
|
||||
pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
|
||||
word16 ? 16 : 8,
|
||||
(inb(ioaddr+1) & 0x01) ? 16 : 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
|
||||
/* Allow a compile-time override. */
|
||||
dev->mem_start = WD_SHMEM;
|
||||
#else
|
||||
if (dev->mem_start == 0) {
|
||||
/* Sanity and old 8003 check */
|
||||
int reg0 = inb(ioaddr);
|
||||
if (reg0 == 0xff || reg0 == 0) {
|
||||
/* Future plan: this could check a few likely locations first. */
|
||||
dev->mem_start = 0xd0000;
|
||||
pr_cont(" assigning address %#lx", dev->mem_start);
|
||||
} else {
|
||||
int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
|
||||
/* Some boards don't have the register 5 -- it returns 0xff. */
|
||||
if (high_addr_bits == 0x1f || word16 == 0)
|
||||
high_addr_bits = 0x01;
|
||||
dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The 8390 isn't at the base address -- the ASIC regs are there! */
|
||||
dev->base_addr = ioaddr+WD_NIC_OFFSET;
|
||||
|
||||
if (dev->irq < 2) {
|
||||
static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4};
|
||||
int reg1 = inb(ioaddr+1);
|
||||
int reg4 = inb(ioaddr+4);
|
||||
if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
|
||||
short nic_addr = ioaddr+WD_NIC_OFFSET;
|
||||
unsigned long irq_mask;
|
||||
|
||||
/* We have an old-style ethercard that doesn't report its IRQ
|
||||
line. Do autoirq to find the IRQ line. Note that this IS NOT
|
||||
a reliable way to trigger an interrupt. */
|
||||
outb_p(E8390_NODMA + E8390_STOP, nic_addr);
|
||||
outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
|
||||
|
||||
irq_mask = probe_irq_on();
|
||||
outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
|
||||
outb_p(0x00, nic_addr + EN0_RCNTLO);
|
||||
outb_p(0x00, nic_addr + EN0_RCNTHI);
|
||||
outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
|
||||
mdelay(20);
|
||||
dev->irq = probe_irq_off(irq_mask);
|
||||
|
||||
outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
|
||||
|
||||
if (wd_msg_enable & NETIF_MSG_PROBE)
|
||||
pr_cont(" autoirq is %d", dev->irq);
|
||||
if (dev->irq < 2)
|
||||
dev->irq = word16 ? 10 : 5;
|
||||
} else
|
||||
dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
|
||||
} else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
|
||||
dev->irq = 9;
|
||||
|
||||
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||
share and the board will usually be enabled. */
|
||||
i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev);
|
||||
if (i) {
|
||||
pr_cont(" unable to get IRQ %d.\n", dev->irq);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* OK, were are certain this is going to work. Setup the device. */
|
||||
ei_status.name = model_name;
|
||||
ei_status.word16 = word16;
|
||||
ei_status.tx_start_page = WD_START_PG;
|
||||
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
|
||||
|
||||
/* Don't map in the shared memory until the board is actually opened. */
|
||||
|
||||
/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
|
||||
if (dev->mem_end != 0) {
|
||||
ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
|
||||
ei_status.priv = dev->mem_end - dev->mem_start;
|
||||
} else {
|
||||
ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
|
||||
dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
|
||||
ei_status.priv = (ei_status.stop_page - WD_START_PG)*256;
|
||||
}
|
||||
|
||||
ei_status.mem = ioremap(dev->mem_start, ei_status.priv);
|
||||
if (!ei_status.mem) {
|
||||
free_irq(dev->irq, dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
|
||||
model_name, dev->irq, dev->mem_start, dev->mem_end-1);
|
||||
|
||||
ei_status.reset_8390 = wd_reset_8390;
|
||||
ei_status.block_input = wd_block_input;
|
||||
ei_status.block_output = wd_block_output;
|
||||
ei_status.get_8390_hdr = wd_get_8390_hdr;
|
||||
|
||||
dev->netdev_ops = &wd_netdev_ops;
|
||||
NS8390_init(dev, 0);
|
||||
ei_local->msg_enable = wd_msg_enable;
|
||||
|
||||
#if 1
|
||||
/* Enable interrupt generation on softconfig cards -- M.U */
|
||||
/* .. but possibly potentially unsafe - Donald */
|
||||
if (inb(ioaddr+14) & 0x20)
|
||||
outb(inb(ioaddr+4)|0x80, ioaddr+4);
|
||||
#endif
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err) {
|
||||
free_irq(dev->irq, dev);
|
||||
iounmap(ei_status.mem);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
wd_open(struct net_device *dev)
|
||||
{
|
||||
int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
|
||||
/* Map in the shared memory. Always set register 0 last to remain
|
||||
compatible with very old boards. */
|
||||
ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
|
||||
ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
|
||||
|
||||
if (ei_status.word16)
|
||||
outb(ei_status.reg5, ioaddr+WD_CMDREG5);
|
||||
outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
|
||||
|
||||
return ei_open(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
wd_reset_8390(struct net_device *dev)
|
||||
{
|
||||
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
||||
outb(WD_RESET, wd_cmd_port);
|
||||
netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n",
|
||||
jiffies);
|
||||
ei_status.txing = 0;
|
||||
|
||||
/* Set up the ASIC registers, just in case something changed them. */
|
||||
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
|
||||
if (ei_status.word16)
|
||||
outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
|
||||
|
||||
netif_dbg(ei_local, hw, dev, "reset done\n");
|
||||
}
|
||||
|
||||
/* Grab the 8390 specific header. Similar to the block_input routine, but
|
||||
we don't need to be concerned with ring wrap as the header will be at
|
||||
the start of a page, so we optimize accordingly. */
|
||||
|
||||
static void
|
||||
wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
|
||||
{
|
||||
|
||||
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8);
|
||||
|
||||
/* We'll always get a 4 byte header read followed by a packet read, so
|
||||
we enable 16 bit mode before the header, and disable after the body. */
|
||||
if (ei_status.word16)
|
||||
outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
/* Officially this is what we are doing, but the readl() is faster */
|
||||
/* unfortunately it isn't endian aware of the struct */
|
||||
memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
|
||||
hdr->count = le16_to_cpu(hdr->count);
|
||||
#else
|
||||
((unsigned int*)hdr)[0] = readl(hdr_start);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Block input and output are easy on shared memory ethercards, and trivial
|
||||
on the Western digital card where there is no choice of how to do it.
|
||||
The only complications are that the ring buffer wraps, and need to map
|
||||
switch between 8- and 16-bit modes. */
|
||||
|
||||
static void
|
||||
wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
|
||||
{
|
||||
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
unsigned long offset = ring_offset - (WD_START_PG<<8);
|
||||
void __iomem *xfer_start = ei_status.mem + offset;
|
||||
|
||||
if (offset + count > ei_status.priv) {
|
||||
/* We must wrap the input move. */
|
||||
int semi_count = ei_status.priv - offset;
|
||||
memcpy_fromio(skb->data, xfer_start, semi_count);
|
||||
count -= semi_count;
|
||||
memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count);
|
||||
} else {
|
||||
/* Packet is in one chunk -- we can copy + cksum. */
|
||||
memcpy_fromio(skb->data, xfer_start, count);
|
||||
}
|
||||
|
||||
/* Turn off 16 bit access so that reboot works. ISA brain-damage */
|
||||
if (ei_status.word16)
|
||||
outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
|
||||
}
|
||||
|
||||
static void
|
||||
wd_block_output(struct net_device *dev, int count, const unsigned char *buf,
|
||||
int start_page)
|
||||
{
|
||||
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8);
|
||||
|
||||
|
||||
if (ei_status.word16) {
|
||||
/* Turn on and off 16 bit access so that reboot works. */
|
||||
outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
|
||||
memcpy_toio(shmem, buf, count);
|
||||
outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
|
||||
} else
|
||||
memcpy_toio(shmem, buf, count);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
wd_close(struct net_device *dev)
|
||||
{
|
||||
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||
struct ei_device *ei_local = netdev_priv(dev);
|
||||
|
||||
netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n");
|
||||
ei_close(dev);
|
||||
|
||||
/* Change from 16-bit to 8-bit shared memory so reboot works. */
|
||||
if (ei_status.word16)
|
||||
outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
|
||||
|
||||
/* And disable the shared memory. */
|
||||
outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
#define MAX_WD_CARDS 4 /* Max number of wd cards per module */
|
||||
static struct net_device *dev_wd[MAX_WD_CARDS];
|
||||
static int io[MAX_WD_CARDS];
|
||||
static int irq[MAX_WD_CARDS];
|
||||
static int mem[MAX_WD_CARDS];
|
||||
static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */
|
||||
|
||||
module_param_hw_array(io, int, ioport, NULL, 0);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0);
|
||||
module_param_hw_array(mem, int, iomem, NULL, 0);
|
||||
module_param_hw_array(mem_end, int, iomem, NULL, 0);
|
||||
module_param_named(msg_enable, wd_msg_enable, uint, 0444);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)");
|
||||
MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)");
|
||||
MODULE_PARM_DESC(mem_end, "memory end address(es)");
|
||||
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");
|
||||
MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* This is set up so that only a single autoprobe takes place per call.
|
||||
ISA device autoprobes on a running machine are not recommended. */
|
||||
|
||||
static int __init wd_init_module(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int this_dev, found = 0;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
|
||||
if (io[this_dev] == 0) {
|
||||
if (this_dev != 0) break; /* only autoprobe 1st one */
|
||||
printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
|
||||
}
|
||||
dev = alloc_ei_netdev();
|
||||
if (!dev)
|
||||
break;
|
||||
dev->irq = irq[this_dev];
|
||||
dev->base_addr = io[this_dev];
|
||||
dev->mem_start = mem[this_dev];
|
||||
dev->mem_end = mem_end[this_dev];
|
||||
if (do_wd_probe(dev) == 0) {
|
||||
dev_wd[found++] = dev;
|
||||
continue;
|
||||
}
|
||||
free_netdev(dev);
|
||||
printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
return 0;
|
||||
return -ENXIO;
|
||||
}
|
||||
module_init(wd_init_module);
|
||||
|
||||
static void cleanup_card(struct net_device *dev)
|
||||
{
|
||||
free_irq(dev->irq, dev);
|
||||
release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT);
|
||||
iounmap(ei_status.mem);
|
||||
}
|
||||
|
||||
static void __exit wd_cleanup_module(void)
|
||||
{
|
||||
int this_dev;
|
||||
|
||||
for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
|
||||
struct net_device *dev = dev_wd[this_dev];
|
||||
if (dev) {
|
||||
unregister_netdev(dev);
|
||||
cleanup_card(dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
module_exit(wd_cleanup_module);
|
||||
#endif /* MODULE */
|
||||
|
|
@ -61,7 +61,6 @@ source "drivers/net/ethernet/engleder/Kconfig"
|
|||
source "drivers/net/ethernet/ezchip/Kconfig"
|
||||
source "drivers/net/ethernet/faraday/Kconfig"
|
||||
source "drivers/net/ethernet/freescale/Kconfig"
|
||||
source "drivers/net/ethernet/fujitsu/Kconfig"
|
||||
source "drivers/net/ethernet/fungible/Kconfig"
|
||||
source "drivers/net/ethernet/google/Kconfig"
|
||||
source "drivers/net/ethernet/hisilicon/Kconfig"
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ obj-$(CONFIG_NET_VENDOR_ENGLEDER) += engleder/
|
|||
obj-$(CONFIG_NET_VENDOR_EZCHIP) += ezchip/
|
||||
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
|
||||
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
|
||||
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
|
||||
obj-$(CONFIG_NET_VENDOR_FUNGIBLE) += fungible/
|
||||
obj-$(CONFIG_NET_VENDOR_GOOGLE) += google/
|
||||
obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
|
||||
|
|
|
|||
|
|
@ -43,17 +43,6 @@ config AMD8111_ETH
|
|||
To compile this driver as a module, choose M here. The module
|
||||
will be called amd8111e.
|
||||
|
||||
config LANCE
|
||||
tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
|
||||
depends on ISA && ISA_DMA_API && !ARM && !PPC32
|
||||
select NETDEV_LEGACY_INIT
|
||||
help
|
||||
If you have a network (Ethernet) card of this type, say Y here.
|
||||
Some LinkSys cards are of this type.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called lance. This is recommended.
|
||||
|
||||
config PCNET32
|
||||
tristate "AMD PCnet32 PCI support"
|
||||
depends on PCI && HAS_IOPORT
|
||||
|
|
@ -120,16 +109,6 @@ config MVME147_NET
|
|||
driver for this chip in your kernel.
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PCMCIA_NMCLAN
|
||||
tristate "New Media PCMCIA support"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
help
|
||||
Say Y here if you intend to attach a New Media Ethernet or LiveWire
|
||||
PCMCIA (PC-card) Ethernet card to your computer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called nmclan_cs. If unsure, say N.
|
||||
|
||||
config SUN3LANCE
|
||||
tristate "Sun3/Sun3x on-board LANCE support"
|
||||
depends on (SUN3 || SUN3X)
|
||||
|
|
|
|||
|
|
@ -9,10 +9,8 @@ obj-$(CONFIG_ARIADNE) += ariadne.o
|
|||
obj-$(CONFIG_ATARILANCE) += atarilance.o
|
||||
obj-$(CONFIG_DECLANCE) += declance.o
|
||||
obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
|
||||
obj-$(CONFIG_LANCE) += lance.o
|
||||
obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
|
||||
obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
|
||||
obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
|
||||
obj-$(CONFIG_PCNET32) += pcnet32.o
|
||||
obj-$(CONFIG_SUN3LANCE) += sun3lance.o
|
||||
obj-$(CONFIG_SUNLANCE) += sunlance.o
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,30 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Fujitsu Network device configuration
|
||||
#
|
||||
|
||||
config NET_VENDOR_FUJITSU
|
||||
bool "Fujitsu devices"
|
||||
default y
|
||||
depends on PCMCIA
|
||||
help
|
||||
If you have a network (Ethernet) card belonging to this class, say Y.
|
||||
|
||||
Note that the answer to this question doesn't directly affect the
|
||||
the questions about Fujitsu cards. If you say Y, you will be asked for
|
||||
your specific card in the following questions.
|
||||
|
||||
if NET_VENDOR_FUJITSU
|
||||
|
||||
config PCMCIA_FMVJ18X
|
||||
tristate "Fujitsu FMV-J18x PCMCIA support"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
select CRC32
|
||||
help
|
||||
Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
|
||||
PCMCIA (PC-card) Ethernet card to your computer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called fmvj18x_cs. If unsure, say N.
|
||||
|
||||
endif # NET_VENDOR_FUJITSU
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Makefile for the Fujitsu network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -19,21 +19,6 @@ config NET_VENDOR_SMSC
|
|||
|
||||
if NET_VENDOR_SMSC
|
||||
|
||||
config SMC9194
|
||||
tristate "SMC 9194 support"
|
||||
depends on ISA
|
||||
select CRC32
|
||||
select NETDEV_LEGACY_INIT
|
||||
help
|
||||
This is support for the SMC9xxx based Ethernet cards. Choose this
|
||||
option if you have a DELL laptop with the docking station, or
|
||||
another SMC9192/9194 based chipset. Say Y if you want it compiled
|
||||
into the kernel, and read the file
|
||||
<file:Documentation/networking/device_drivers/ethernet/smsc/smc9.rst>.
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called smc9194.
|
||||
|
||||
config SMC91X
|
||||
tristate "SMC 91C9x/91C1xxx support"
|
||||
select CRC32
|
||||
|
|
@ -52,18 +37,6 @@ config SMC91X
|
|||
The module will be called smc91x. If you want to compile it as a
|
||||
module, say M here and read <file:Documentation/kbuild/modules.rst>.
|
||||
|
||||
config PCMCIA_SMC91C92
|
||||
tristate "SMC 91Cxx PCMCIA support"
|
||||
depends on PCMCIA && HAS_IOPORT
|
||||
select CRC32
|
||||
select MII
|
||||
help
|
||||
Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
|
||||
(PC-card) Ethernet or Fast Ethernet card to your computer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called smc91c92_cs. If unsure, say N.
|
||||
|
||||
config EPIC100
|
||||
tristate "SMC EtherPower II"
|
||||
depends on PCI
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
# Makefile for the SMSC network device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SMC9194) += smc9194.o
|
||||
obj-$(CONFIG_SMC91X) += smc91x.o
|
||||
obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
|
||||
obj-$(CONFIG_EPIC100) += epic100.o
|
||||
obj-$(CONFIG_SMSC9420) += smsc9420.o
|
||||
obj-$(CONFIG_SMSC911X) += smsc911x.o
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -3,10 +3,5 @@
|
|||
* ethernet adaptor have the name "eth[0123...]".
|
||||
*/
|
||||
|
||||
struct net_device *ultra_probe(int unit);
|
||||
struct net_device *wd_probe(int unit);
|
||||
struct net_device *ne_probe(int unit);
|
||||
struct net_device *smc_init(int unit);
|
||||
struct net_device *cs89x0_probe(int unit);
|
||||
struct net_device *tc515_probe(int unit);
|
||||
struct net_device *lance_probe(int unit);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user