* [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers
@ 2026-04-22 18:01 Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver Andrew Lunn
` (14 more replies)
0 siblings, 15 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
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.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
Changes in v2:
- Drop removal of 3c59x and xirc2ps, MVME147
- Remove some documents which belong to the drivers being removed.
- Link to v1: https://lore.kernel.org/r/20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch
---
Andrew Lunn (15):
drivers: net: 3com: 3c509: Remove this driver
drivers: net: 3com: 3c515: Remove this driver
drivers: net: 3com: 3c574: Remove this driver
drivers: net: 3com: 3c589: Remove this driver
drivers: net: amd: lance: Remove this driver
drivers: net: amd: nmclan: Remove this driver
drivers: net: smsc: smc9194: Remove this driver
drivers: net: smsc: smc91c92: Remove this driver
drivers: net: cirrus: cs89x0: Remove this driver
drivers: net: cirrus: mac89x0: Remove this driver
drivers: net: fujitsu: fmvj18x: Remove this driver
drivers: net: 8390: AX88190: Remove this driver
drivers: net: 8390: pcnet: Remove this driver
drivers: net: 8390: ultra: Remove this driver
drivers: net: 8390: wd80x3: Remove this driver
.../device_drivers/ethernet/3com/3c509.rst | 249 ---
.../device_drivers/ethernet/cirrus/cs89x0.rst | 647 ------
.../device_drivers/ethernet/smsc/smc9.rst | 48 -
drivers/net/ethernet/3com/3c509.c | 1448 --------------
drivers/net/ethernet/3com/3c515.c | 1566 ---------------
drivers/net/ethernet/3com/3c574_cs.c | 1164 -----------
drivers/net/ethernet/3com/3c589_cs.c | 974 ---------
drivers/net/ethernet/3com/Kconfig | 45 -
drivers/net/ethernet/3com/Makefile | 4 -
drivers/net/ethernet/8390/Kconfig | 52 -
drivers/net/ethernet/8390/Makefile | 4 -
drivers/net/ethernet/8390/axnet_cs.c | 1707 ----------------
drivers/net/ethernet/8390/pcnet_cs.c | 1717 ----------------
drivers/net/ethernet/8390/smc-ultra.c | 630 ------
drivers/net/ethernet/8390/wd.c | 575 ------
drivers/net/ethernet/Kconfig | 1 -
drivers/net/ethernet/amd/Kconfig | 21 -
drivers/net/ethernet/amd/Makefile | 2 -
drivers/net/ethernet/amd/lance.c | 1317 -------------
drivers/net/ethernet/amd/nmclan_cs.c | 1508 --------------
drivers/net/ethernet/cirrus/Kconfig | 40 -
drivers/net/ethernet/cirrus/Makefile | 2 -
drivers/net/ethernet/cirrus/cs89x0.c | 1915 ------------------
drivers/net/ethernet/cirrus/cs89x0.h | 461 -----
drivers/net/ethernet/cirrus/mac89x0.c | 577 ------
drivers/net/ethernet/fujitsu/Kconfig | 30 -
drivers/net/ethernet/fujitsu/Makefile | 6 -
drivers/net/ethernet/fujitsu/fmvj18x_cs.c | 1176 -----------
drivers/net/ethernet/smsc/Kconfig | 27 -
drivers/net/ethernet/smsc/Makefile | 2 -
drivers/net/ethernet/smsc/smc9194.c | 1535 ---------------
drivers/net/ethernet/smsc/smc91c92_cs.c | 2059 --------------------
32 files changed, 21509 deletions(-)
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260421-v7-0-0-net-next-driver-removal-v1-47c88c987b34
Best regards,
--
Andrew Lunn <andrew@lunn.ch>
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 02/15] drivers: net: 3com: 3c515: " Andrew Lunn
` (13 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The 3c509 was written by Donald Becker between 1993-2000. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Delete Documentation as well
---
.../device_drivers/ethernet/3com/3c509.rst | 249 ----
drivers/net/ethernet/3com/3c509.c | 1448 --------------------
drivers/net/ethernet/3com/Kconfig | 14 -
drivers/net/ethernet/3com/Makefile | 1 -
4 files changed, 1712 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst b/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
deleted file mode 100644
index 47f706bacdd9..000000000000
--- a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
+++ /dev/null
@@ -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
-
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
deleted file mode 100644
index fb68339e1511..000000000000
--- a/drivers/net/ethernet/3com/3c509.c
+++ /dev/null
@@ -1,1448 +0,0 @@
-/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
-/*
- Written 1993-2000 by Donald Becker.
-
- Copyright 1994-2000 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.
-
- This driver is for the 3Com EtherLinkIII series.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
- Known limitations:
- Because of the way 3c509 ISA detection works it's difficult to predict
- a priori which of several ISA-mode cards will be detected first.
-
- This driver does not use predictive interrupt mode, resulting in higher
- packet latency but lower overhead. If interrupts are disabled for an
- unusually long time it could also result in missed packets, but in
- practice this rarely happens.
-
-
- FIXES:
- Alan Cox: Removed the 'Unexpected interrupt' bug.
- Michael Meskes: Upgraded to Donald Becker's version 1.07.
- Alan Cox: Increased the eeprom delay. Regardless of
- what the docs say some people definitely
- get problems with lower (but in card spec)
- delays
- v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
- other cleanups. -djb
- Andrea Arcangeli: Upgraded to Donald Becker's version 1.12.
- Rick Payne: Fixed SMP race condition
- v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
- v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
- v1.15 1/31/98 Faster recovery for Tx errors. -djb
- v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
- v1.18 12Mar2001 Andrew Morton
- - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
- - Reviewed against 1.18 from scyld.com
- v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
- - ethtool support
- v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
- - Power Management support
- v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
- - Full duplex support
- v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
- - Additional ethtool features
- v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com>
- - Increase *read_eeprom udelay to workaround oops with 2 cards.
- v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
- - Introduce driver model for EISA cards.
- v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org>
- - convert to isa_driver and pnp_driver and some cleanups
-*/
-
-#define DRV_NAME "3c509"
-
-/* A few values that may be tweaked. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (400*HZ/1000)
-
-#include <linux/module.h>
-#include <linux/isa.h>
-#include <linux/pnp.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/pm.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h> /* for udelay() */
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/device.h>
-#include <linux/eisa.h>
-#include <linux/bitops.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#ifdef EL3_DEBUG
-static int el3_debug = EL3_DEBUG;
-#else
-static int el3_debug = 2;
-#endif
-
-/* Used to do a global count of all the cards in the system. Must be
- * a global variable so that the eisa probe routines can increment
- * it */
-static int el3_cards = 0;
-#define EL3_MAX_CARDS 8
-
-/* To minimize the size of the driver source I only define operating
- constants if they are used several times. You'll need the manual
- anyway if you want to understand driver details. */
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-#define EEPROM_READ 0x80
-
-#define EL3_IO_EXTENT 16
-
-#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, PowerUp = 27<<11,
- PowerDown = 28<<11, PowerAuto = 29<<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_CONF_CTRL 0x04 /* Window 0: Configuration control register */
-#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */
-#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 WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */
-#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */
-
-/*
- * Must be a power of two (we use a binary and in the
- * circular queue)
- */
-#define SKB_QUEUE_SIZE 64
-
-enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
-
-struct el3_private {
- spinlock_t lock;
- /* skb send-queue */
- int head, size;
- struct sk_buff *queue[SKB_QUEUE_SIZE];
- enum el3_cardtype type;
-};
-static int id_port;
-static int current_tag;
-static struct net_device *el3_devs[EL3_MAX_CARDS];
-
-/* Parameters that may be passed into the module. */
-static int debug = -1;
-static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 10;
-#ifdef CONFIG_PNP
-static int nopnp;
-#endif
-
-static int el3_common_init(struct net_device *dev);
-static void el3_common_remove(struct net_device *dev);
-static ushort id_read_eeprom(int index);
-static ushort read_eeprom(int ioaddr, int index);
-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 set_multicast_list(struct net_device *dev);
-static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue);
-static void el3_down(struct net_device *dev);
-static void el3_up(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-#ifdef CONFIG_PM
-static int el3_suspend(struct device *, pm_message_t);
-static int el3_resume(struct device *);
-#else
-#define el3_suspend NULL
-#define el3_resume NULL
-#endif
-
-
-/* generic device remove for all device types */
-static int el3_device_remove (struct device *device);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void el3_poll_controller(struct net_device *dev);
-#endif
-
-/* Return 0 on success, 1 on error, 2 when found already detected PnP card */
-static int el3_isa_id_sequence(__be16 *phys_addr)
-{
- short lrs_state = 0xff;
- int i;
-
- /* ISA boards are detected by sending the ID sequence to the
- ID_PORT. We find cards past the first by setting the 'current_tag'
- on cards as they are found. Cards with their tag set will not
- respond to subsequent ID sequences. */
-
- outb(0x00, id_port);
- outb(0x00, id_port);
- for (i = 0; i < 255; i++) {
- outb(lrs_state, id_port);
- lrs_state <<= 1;
- lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
- }
- /* For the first probe, clear all board's tag registers. */
- if (current_tag == 0)
- outb(0xd0, id_port);
- else /* Otherwise kill off already-found boards. */
- outb(0xd8, id_port);
- if (id_read_eeprom(7) != 0x6d50)
- return 1;
- /* Read in EEPROM data, which does contention-select.
- Only the lowest address board will stay "on-line".
- 3Com got the byte order backwards. */
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(id_read_eeprom(i));
-#ifdef CONFIG_PNP
- if (!nopnp) {
- /* The ISA PnP 3c509 cards respond to the ID sequence too.
- This check is needed in order not to register them twice. */
- for (i = 0; i < el3_cards; i++) {
- struct el3_private *lp = netdev_priv(el3_devs[i]);
- if (lp->type == EL3_PNP &&
- ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) {
- if (el3_debug > 3)
- pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
- phys_addr[0] & 0xff, phys_addr[0] >> 8,
- phys_addr[1] & 0xff, phys_addr[1] >> 8,
- phys_addr[2] & 0xff, phys_addr[2] >> 8);
- /* Set the adaptor tag so that the next card can be found. */
- outb(0xd0 + ++current_tag, id_port);
- return 2;
- }
- }
- }
-#endif /* CONFIG_PNP */
- return 0;
-
-}
-
-static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr,
- int irq, int if_port, enum el3_cardtype type)
-{
- struct el3_private *lp = netdev_priv(dev);
-
- eth_hw_addr_set(dev, (u8 *)phys_addr);
- dev->base_addr = ioaddr;
- dev->irq = irq;
- dev->if_port = if_port;
- lp->type = type;
-}
-
-static int el3_isa_match(struct device *pdev, unsigned int ndev)
-{
- struct net_device *dev;
- int ioaddr, isa_irq, if_port, err;
- unsigned int iobase;
- __be16 phys_addr[3];
-
- while ((err = el3_isa_id_sequence(phys_addr)) == 2)
- ; /* Skip to next card when PnP card found */
- if (err == 1)
- return 0;
-
- iobase = id_read_eeprom(8);
- if_port = iobase >> 14;
- ioaddr = 0x200 + ((iobase & 0x1f) << 4);
- if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
- isa_irq = irq[el3_cards];
- else
- isa_irq = id_read_eeprom(9) >> 12;
-
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev)
- return -ENOMEM;
-
- SET_NETDEV_DEV(dev, pdev);
-
- if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
- free_netdev(dev);
- return 0;
- }
-
- /* Set the adaptor tag so that the next card can be found. */
- outb(0xd0 + ++current_tag, id_port);
-
- /* Activate the adaptor at the EEPROM location. */
- outb((ioaddr >> 4) | 0xe0, id_port);
-
- EL3WINDOW(0);
- if (inw(ioaddr) != 0x6d50) {
- free_netdev(dev);
- return 0;
- }
-
- /* Free the interrupt so that some other card can use it. */
- outw(0x0f00, ioaddr + WN0_IRQ);
-
- el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
- dev_set_drvdata(pdev, dev);
- if (el3_common_init(dev)) {
- free_netdev(dev);
- return 0;
- }
-
- el3_devs[el3_cards++] = dev;
- return 1;
-}
-
-static void el3_isa_remove(struct device *pdev,
- unsigned int ndev)
-{
- el3_device_remove(pdev);
- dev_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_isa_suspend(struct device *dev, unsigned int n,
- pm_message_t state)
-{
- current_tag = 0;
- return el3_suspend(dev, state);
-}
-
-static int el3_isa_resume(struct device *dev, unsigned int n)
-{
- struct net_device *ndev = dev_get_drvdata(dev);
- int ioaddr = ndev->base_addr, err;
- __be16 phys_addr[3];
-
- while ((err = el3_isa_id_sequence(phys_addr)) == 2)
- ; /* Skip to next card when PnP card found */
- if (err == 1)
- return 0;
- /* Set the adaptor tag so that the next card can be found. */
- outb(0xd0 + ++current_tag, id_port);
- /* Enable the card */
- outb((ioaddr >> 4) | 0xe0, id_port);
- EL3WINDOW(0);
- if (inw(ioaddr) != 0x6d50)
- return 1;
- /* Free the interrupt so that some other card can use it. */
- outw(0x0f00, ioaddr + WN0_IRQ);
- return el3_resume(dev);
-}
-#endif
-
-static struct isa_driver el3_isa_driver = {
- .match = el3_isa_match,
- .remove = el3_isa_remove,
-#ifdef CONFIG_PM
- .suspend = el3_isa_suspend,
- .resume = el3_isa_resume,
-#endif
- .driver = {
- .name = "3c509"
- },
-};
-static int isa_registered;
-
-#ifdef CONFIG_PNP
-static const struct pnp_device_id el3_pnp_ids[] = {
- { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
- { .id = "TCM5091" }, /* 3Com Etherlink III */
- { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
- { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
- { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
- { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
- { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
- { .id = "" }
-};
-MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
-
-static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
-{
- short i;
- int ioaddr, irq, if_port;
- __be16 phys_addr[3];
- struct net_device *dev = NULL;
- int err;
-
- ioaddr = pnp_port_start(pdev, 0);
- if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
- return -EBUSY;
- irq = pnp_irq(pdev, 0);
- EL3WINDOW(0);
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(read_eeprom(ioaddr, i));
- if_port = read_eeprom(ioaddr, 8) >> 14;
- dev = alloc_etherdev(sizeof(struct el3_private));
- if (!dev) {
- release_region(ioaddr, EL3_IO_EXTENT);
- return -ENOMEM;
- }
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
- pnp_set_drvdata(pdev, dev);
- err = el3_common_init(dev);
-
- if (err) {
- pnp_set_drvdata(pdev, NULL);
- free_netdev(dev);
- return err;
- }
-
- el3_devs[el3_cards++] = dev;
- return 0;
-}
-
-static void el3_pnp_remove(struct pnp_dev *pdev)
-{
- el3_common_remove(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
-{
- return el3_suspend(&pdev->dev, state);
-}
-
-static int el3_pnp_resume(struct pnp_dev *pdev)
-{
- return el3_resume(&pdev->dev);
-}
-#endif
-
-static struct pnp_driver el3_pnp_driver = {
- .name = "3c509",
- .id_table = el3_pnp_ids,
- .probe = el3_pnp_probe,
- .remove = el3_pnp_remove,
-#ifdef CONFIG_PM
- .suspend = el3_pnp_suspend,
- .resume = el3_pnp_resume,
-#endif
-};
-static int pnp_registered;
-#endif /* CONFIG_PNP */
-
-#ifdef CONFIG_EISA
-static const struct eisa_device_id el3_eisa_ids[] = {
- { "TCM5090" },
- { "TCM5091" },
- { "TCM5092" },
- { "TCM5093" },
- { "TCM5094" },
- { "TCM5095" },
- { "TCM5098" },
- { "" }
-};
-MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
-
-static int el3_eisa_probe (struct device *device);
-
-static struct eisa_driver el3_eisa_driver = {
- .id_table = el3_eisa_ids,
- .driver = {
- .name = "3c579",
- .probe = el3_eisa_probe,
- .remove = el3_device_remove,
- .suspend = el3_suspend,
- .resume = el3_resume,
- }
-};
-static int eisa_registered;
-#endif
-
-static const struct net_device_ops netdev_ops = {
- .ndo_open = el3_open,
- .ndo_stop = el3_close,
- .ndo_start_xmit = el3_start_xmit,
- .ndo_get_stats = el3_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_tx_timeout = el3_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = el3_poll_controller,
-#endif
-};
-
-static int el3_common_init(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- int err;
- static const char * const if_names[] = {
- "10baseT", "AUI", "undefined", "BNC"
- };
-
- spin_lock_init(&lp->lock);
-
- if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
- dev->if_port = (dev->mem_start & 0x0f);
- } else { /* xcvr codes 0/8 */
- /* use eeprom value, but save user's full-duplex selection */
- dev->if_port |= (dev->mem_start & 0x08);
- }
-
- /* The EL3-specific entries in the device structure. */
- dev->netdev_ops = &netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
- dev->ethtool_ops = ðtool_ops;
-
- err = register_netdev(dev);
- if (err) {
- pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
- dev->base_addr, dev->irq);
- release_region(dev->base_addr, EL3_IO_EXTENT);
- return err;
- }
-
- pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
- dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
- dev->dev_addr, dev->irq);
-
- return 0;
-
-}
-
-static void el3_common_remove (struct net_device *dev)
-{
- unregister_netdev (dev);
- release_region(dev->base_addr, EL3_IO_EXTENT);
- free_netdev (dev);
-}
-
-#ifdef CONFIG_EISA
-static int el3_eisa_probe(struct device *device)
-{
- short i;
- int ioaddr, irq, if_port;
- __be16 phys_addr[3];
- struct net_device *dev = NULL;
- struct eisa_device *edev;
- int err;
-
- /* Yeepee, The driver framework is calling us ! */
- edev = to_eisa_device (device);
- ioaddr = edev->base_addr;
-
- if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
- return -EBUSY;
-
- /* Change the register set to the configuration window 0. */
- outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
-
- irq = inw(ioaddr + WN0_IRQ) >> 12;
- if_port = inw(ioaddr + 6)>>14;
- for (i = 0; i < 3; i++)
- phys_addr[i] = htons(read_eeprom(ioaddr, i));
-
- /* Restore the "Product ID" to the EEPROM read register. */
- read_eeprom(ioaddr, 3);
-
- dev = alloc_etherdev(sizeof (struct el3_private));
- if (dev == NULL) {
- release_region(ioaddr, EL3_IO_EXTENT);
- return -ENOMEM;
- }
-
- SET_NETDEV_DEV(dev, device);
-
- el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
- eisa_set_drvdata (edev, dev);
- err = el3_common_init(dev);
-
- if (err) {
- eisa_set_drvdata (edev, NULL);
- free_netdev(dev);
- return err;
- }
-
- el3_devs[el3_cards++] = dev;
- return 0;
-}
-#endif
-
-/* This remove works for all device types.
- *
- * The net dev must be stored in the driver data field */
-static int el3_device_remove(struct device *device)
-{
- struct net_device *dev;
-
- dev = dev_get_drvdata(device);
-
- el3_common_remove (dev);
- return 0;
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
- Assume that we are in register window zero.
- */
-static ushort read_eeprom(int ioaddr, int index)
-{
- outw(EEPROM_READ + index, ioaddr + 10);
- /* Pause for at least 162 us. for the read to take place.
- Some chips seem to require much longer */
- mdelay(2);
- return inw(ioaddr + 12);
-}
-
-/* Read a word from the EEPROM when in the ISA ID probe state. */
-static ushort id_read_eeprom(int index)
-{
- int bit, word = 0;
-
- /* Issue read command, and pause for at least 162 us. for it to complete.
- Assume extra-fast 16Mhz bus. */
- outb(EEPROM_READ + index, id_port);
-
- /* Pause for at least 162 us. for the read to take place. */
- /* Some chips seem to require much longer */
- mdelay(4);
-
- for (bit = 15; bit >= 0; bit--)
- word = (word << 1) + (inb(id_port) & 0x01);
-
- if (el3_debug > 3)
- pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
-
- return word;
-}
-
-
-static int
-el3_open(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- int i;
-
- outw(TxReset, ioaddr + EL3_CMD);
- outw(RxReset, ioaddr + EL3_CMD);
- outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
- i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
- if (i)
- return i;
-
- EL3WINDOW(0);
- if (el3_debug > 3)
- pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
- dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
-
- el3_up(dev);
-
- if (el3_debug > 3)
- pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n",
- dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
-
- return 0;
-}
-
-static void
-el3_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
- int ioaddr = dev->base_addr;
-
- /* Transmitter timeout, serious problems. */
- pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n",
- dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
- inw(ioaddr + TX_FREE));
- dev->stats.tx_errors++;
- netif_trans_update(dev); /* prevent tx timeout */
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_wake_queue(dev);
-}
-
-
-static netdev_tx_t
-el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
- unsigned long flags;
-
- netif_stop_queue (dev);
-
- dev->stats.tx_bytes += skb->len;
-
- if (el3_debug > 4) {
- pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
- dev->name, skb->len, inw(ioaddr + EL3_STATUS));
- }
- /*
- * We lock the driver against other processors. Note
- * we don't need to lock versus the IRQ as we suspended
- * that. This means that we lose the ability to take
- * an RX during a TX upload. That sucks a bit with SMP
- * on an original 3c509 (2K buffer)
- *
- * Using disable_irq stops us crapping on other
- * time sensitive devices.
- */
-
- spin_lock_irqsave(&lp->lock, flags);
-
- /* 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_start_queue(dev);
- else
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
-
- spin_unlock_irqrestore(&lp->lock, flags);
-
- dev_consume_skb_any (skb);
-
- /* Clear the Tx status stack. */
- {
- short tx_status;
- int i = 4;
-
- while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
- if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
- if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
- if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
- outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
- }
- }
- return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t
-el3_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct el3_private *lp;
- int ioaddr, status;
- int i = max_interrupt_work;
-
- lp = netdev_priv(dev);
- spin_lock(&lp->lock);
-
- ioaddr = dev->base_addr;
-
- if (el3_debug > 4) {
- status = inw(ioaddr + EL3_STATUS);
- pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
- }
-
- while ((status = inw(ioaddr + EL3_STATUS)) &
- (IntLatch | RxComplete | StatsFull)) {
-
- if (status & RxComplete)
- el3_rx(dev);
-
- if (status & TxAvailable) {
- if (el3_debug > 5)
- pr_debug(" 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 & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
- /* 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 & TxComplete) { /* Really Tx error. */
- short tx_status;
- int i = 4;
-
- while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
- if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
- if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
- if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
- outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
- }
- }
- if (status & AdapterFailure) {
- /* Adapter failure requires Rx reset and reinit. */
- outw(RxReset, ioaddr + EL3_CMD);
- /* Set the Rx filter to the current state. */
- outw(SetRxFilter | RxStation | RxBroadcast
- | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
- | (dev->flags & IFF_PROMISC ? RxProm : 0),
- ioaddr + EL3_CMD);
- outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
- }
-
- if (--i < 0) {
- pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
- dev->name, status);
- /* Clear all interrupts. */
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
- }
-
- if (el3_debug > 4) {
- pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
- inw(ioaddr + EL3_STATUS));
- }
- spin_unlock(&lp->lock);
- return IRQ_HANDLED;
-}
-
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void el3_poll_controller(struct net_device *dev)
-{
- disable_irq(dev->irq);
- el3_interrupt(dev->irq, dev);
- enable_irq(dev->irq);
-}
-#endif
-
-static struct net_device_stats *
-el3_get_stats(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
-
- /*
- * This is fast enough not to bother with disable IRQ
- * stuff.
- */
-
- 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.
- */
-static void update_stats(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
-
- if (el3_debug > 5)
- pr_debug(" 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);
- inw(ioaddr + 10); /* Total Rx and 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)
-{
- int ioaddr = dev->base_addr;
- short rx_status;
-
- if (el3_debug > 5)
- pr_debug(" 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)) > 0) {
- if (rx_status & 0x4000) { /* Error, update stats. */
- short error = rx_status & 0x3800;
-
- outw(RxDiscard, ioaddr + EL3_CMD);
- 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);
- if (el3_debug > 4)
- pr_debug("Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte */
-
- /* 'skb->data' points to the start of sk_buff data area. */
- insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
- (pkt_len + 3) >> 2);
-
- outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
- skb->protocol = eth_type_trans(skb,dev);
- netif_rx(skb);
- dev->stats.rx_bytes += pkt_len;
- dev->stats.rx_packets++;
- continue;
- }
- outw(RxDiscard, ioaddr + EL3_CMD);
- dev->stats.rx_dropped++;
- if (el3_debug)
- pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, pkt_len);
- }
- inw(ioaddr + EL3_STATUS); /* Delay. */
- while (inw(ioaddr + EL3_STATUS) & 0x1000)
- pr_debug(" Waiting for 3c509 to discard packet, status %x.\n",
- inw(ioaddr + EL3_STATUS) );
- }
-
- return 0;
-}
-
-/*
- * Set or clear the multicast filter for this adaptor.
- */
-static void
-set_multicast_list(struct net_device *dev)
-{
- unsigned long flags;
- struct el3_private *lp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
- int mc_count = netdev_mc_count(dev);
-
- if (el3_debug > 1) {
- static int old;
- if (old != mc_count) {
- old = mc_count;
- pr_debug("%s: Setting Rx mode to %d addresses.\n",
- dev->name, mc_count);
- }
- }
- spin_lock_irqsave(&lp->lock, flags);
- if (dev->flags&IFF_PROMISC) {
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
- ioaddr + EL3_CMD);
- }
- else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
- }
- else
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
- spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static int
-el3_close(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- struct el3_private *lp = netdev_priv(dev);
-
- if (el3_debug > 2)
- pr_debug("%s: Shutting down ethercard.\n", dev->name);
-
- el3_down(dev);
-
- free_irq(dev->irq, dev);
- /* Switching back to window 0 disables the IRQ. */
- EL3WINDOW(0);
- if (lp->type != EL3_EISA) {
- /* But we explicitly zero the IRQ line select anyway. Don't do
- * it on EISA cards, it prevents the module from getting an
- * IRQ after unload+reload... */
- outw(0x0f00, ioaddr + WN0_IRQ);
- }
-
- return 0;
-}
-
-static int
-el3_link_ok(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- u16 tmp;
-
- EL3WINDOW(4);
- tmp = inw(ioaddr + WN4_MEDIA);
- EL3WINDOW(1);
- return tmp & (1<<11);
-}
-
-static void
-el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd)
-{
- u16 tmp;
- int ioaddr = dev->base_addr;
- u32 supported;
-
- EL3WINDOW(0);
- /* obtain current transceiver via WN4_MEDIA? */
- tmp = inw(ioaddr + WN0_ADDR_CONF);
- switch (tmp >> 14) {
- case 0:
- cmd->base.port = PORT_TP;
- break;
- case 1:
- cmd->base.port = PORT_AUI;
- break;
- case 3:
- cmd->base.port = PORT_BNC;
- break;
- default:
- break;
- }
-
- cmd->base.duplex = DUPLEX_HALF;
- supported = 0;
- tmp = inw(ioaddr + WN0_CONF_CTRL);
- if (tmp & (1<<13))
- supported |= SUPPORTED_AUI;
- if (tmp & (1<<12))
- supported |= SUPPORTED_BNC;
- if (tmp & (1<<9)) {
- supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
- SUPPORTED_10baseT_Full; /* hmm... */
- EL3WINDOW(4);
- tmp = inw(ioaddr + WN4_NETDIAG);
- if (tmp & FD_ENABLE)
- cmd->base.duplex = DUPLEX_FULL;
- }
-
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
- supported);
- cmd->base.speed = SPEED_10;
- EL3WINDOW(1);
-}
-
-static int
-el3_netdev_set_ecmd(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- u16 tmp;
- int ioaddr = dev->base_addr;
-
- if (cmd->base.speed != SPEED_10)
- return -EINVAL;
- if ((cmd->base.duplex != DUPLEX_HALF) &&
- (cmd->base.duplex != DUPLEX_FULL))
- return -EINVAL;
-
- /* change XCVR type */
- EL3WINDOW(0);
- tmp = inw(ioaddr + WN0_ADDR_CONF);
- switch (cmd->base.port) {
- case PORT_TP:
- tmp &= ~(3<<14);
- dev->if_port = 0;
- break;
- case PORT_AUI:
- tmp |= (1<<14);
- dev->if_port = 1;
- break;
- case PORT_BNC:
- tmp |= (3<<14);
- dev->if_port = 3;
- break;
- default:
- return -EINVAL;
- }
-
- outw(tmp, ioaddr + WN0_ADDR_CONF);
- if (dev->if_port == 3) {
- /* fire up the DC-DC convertor if BNC gets enabled */
- tmp = inw(ioaddr + WN0_ADDR_CONF);
- if (tmp & (3 << 14)) {
- outw(StartCoax, ioaddr + EL3_CMD);
- udelay(800);
- } else
- return -EIO;
- }
-
- EL3WINDOW(4);
- tmp = inw(ioaddr + WN4_NETDIAG);
- if (cmd->base.duplex == DUPLEX_FULL)
- tmp |= FD_ENABLE;
- else
- tmp &= ~FD_ENABLE;
- outw(tmp, ioaddr + WN4_NETDIAG);
- EL3WINDOW(1);
-
- return 0;
-}
-
-static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-}
-
-static int el3_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct el3_private *lp = netdev_priv(dev);
-
- spin_lock_irq(&lp->lock);
- el3_netdev_get_ecmd(dev, cmd);
- spin_unlock_irq(&lp->lock);
- return 0;
-}
-
-static int el3_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- struct el3_private *lp = netdev_priv(dev);
- int ret;
-
- spin_lock_irq(&lp->lock);
- ret = el3_netdev_set_ecmd(dev, cmd);
- spin_unlock_irq(&lp->lock);
- return ret;
-}
-
-static u32 el3_get_link(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- u32 ret;
-
- spin_lock_irq(&lp->lock);
- ret = el3_link_ok(dev);
- spin_unlock_irq(&lp->lock);
- return ret;
-}
-
-static u32 el3_get_msglevel(struct net_device *dev)
-{
- return el3_debug;
-}
-
-static void el3_set_msglevel(struct net_device *dev, u32 v)
-{
- el3_debug = v;
-}
-
-static const struct ethtool_ops ethtool_ops = {
- .get_drvinfo = el3_get_drvinfo,
- .get_link = el3_get_link,
- .get_msglevel = el3_get_msglevel,
- .set_msglevel = el3_set_msglevel,
- .get_link_ksettings = el3_get_link_ksettings,
- .set_link_ksettings = el3_set_link_ksettings,
-};
-
-static void
-el3_down(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
-
- netif_stop_queue(dev);
-
- /* Turn off statistics ASAP. We update lp->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 == 3)
- /* Turn off thinnet power. Green! */
- outw(StopCoax, ioaddr + EL3_CMD);
- else if (dev->if_port == 0) {
- /* Disable link beat and jabber, if_port may change here next open(). */
- EL3WINDOW(4);
- outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
- }
-
- outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
- update_stats(dev);
-}
-
-static void
-el3_up(struct net_device *dev)
-{
- int i, sw_info, net_diag;
- int ioaddr = dev->base_addr;
-
- /* Activating the board required and does no harm otherwise */
- outw(0x0001, ioaddr + 4);
-
- /* Set the IRQ line. */
- outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
-
- /* Set the station address in window 2 each time opened. */
- EL3WINDOW(2);
-
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
-
- if ((dev->if_port & 0x03) == 3) /* BNC interface */
- /* Start the thinnet transceiver. We should really wait 50ms...*/
- outw(StartCoax, ioaddr + EL3_CMD);
- else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
- /* Combine secondary sw_info word (the adapter level) and primary
- sw_info word (duplex setting plus other useless bits) */
- EL3WINDOW(0);
- sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
- (read_eeprom(ioaddr, 0x0d) & 0xBff0);
-
- EL3WINDOW(4);
- net_diag = inw(ioaddr + WN4_NETDIAG);
- net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
- pr_info("%s: ", dev->name);
- switch (dev->if_port & 0x0c) {
- case 12:
- /* force full-duplex mode if 3c5x9b */
- if (sw_info & 0x000f) {
- pr_cont("Forcing 3c5x9b full-duplex mode");
- break;
- }
- fallthrough;
- case 8:
- /* set full-duplex mode based on eeprom config setting */
- if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
- pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
- break;
- }
- fallthrough;
- default:
- /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
- pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
- net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
- }
-
- outw(net_diag, ioaddr + WN4_NETDIAG);
- pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
- if (el3_debug > 3)
- pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
- /* Enable link beat and jabber check. */
- outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
- }
-
- /* 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);
-
- /* Accept b-case and phys addr only. */
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
- 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|TxComplete|RxComplete|StatsFull,
- ioaddr + EL3_CMD);
-
- netif_start_queue(dev);
-}
-
-/* Power Management support functions */
-#ifdef CONFIG_PM
-
-static int
-el3_suspend(struct device *pdev, pm_message_t state)
-{
- unsigned long flags;
- struct net_device *dev;
- struct el3_private *lp;
- int ioaddr;
-
- dev = dev_get_drvdata(pdev);
- lp = netdev_priv(dev);
- ioaddr = dev->base_addr;
-
- spin_lock_irqsave(&lp->lock, flags);
-
- if (netif_running(dev))
- netif_device_detach(dev);
-
- el3_down(dev);
- outw(PowerDown, ioaddr + EL3_CMD);
-
- spin_unlock_irqrestore(&lp->lock, flags);
- return 0;
-}
-
-static int
-el3_resume(struct device *pdev)
-{
- unsigned long flags;
- struct net_device *dev;
- struct el3_private *lp;
- int ioaddr;
-
- dev = dev_get_drvdata(pdev);
- lp = netdev_priv(dev);
- ioaddr = dev->base_addr;
-
- spin_lock_irqsave(&lp->lock, flags);
-
- outw(PowerUp, ioaddr + EL3_CMD);
- EL3WINDOW(0);
- el3_up(dev);
-
- if (netif_running(dev))
- netif_device_attach(dev);
-
- spin_unlock_irqrestore(&lp->lock, flags);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-module_param(debug,int, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "debug level (0-6)");
-MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
-MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-#ifdef CONFIG_PNP
-module_param(nopnp, int, 0);
-MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
-#endif /* CONFIG_PNP */
-MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
-MODULE_LICENSE("GPL");
-
-static int __init el3_init_module(void)
-{
- int ret = 0;
-
- if (debug >= 0)
- el3_debug = debug;
-
-#ifdef CONFIG_PNP
- if (!nopnp) {
- ret = pnp_register_driver(&el3_pnp_driver);
- if (!ret)
- pnp_registered = 1;
- }
-#endif
- /* Select an open I/O location at 0x1*0 to do ISA contention select. */
- /* Start with 0x110 to avoid some sound cards.*/
- for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) {
- if (!request_region(id_port, 1, "3c509-control"))
- continue;
- outb(0x00, id_port);
- outb(0xff, id_port);
- if (inb(id_port) & 0x01)
- break;
- else
- release_region(id_port, 1);
- }
- if (id_port >= 0x200) {
- id_port = 0;
- pr_err("No I/O port available for 3c509 activation.\n");
- } else {
- ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
- if (!ret)
- isa_registered = 1;
- }
-#ifdef CONFIG_EISA
- ret = eisa_driver_register(&el3_eisa_driver);
- if (!ret)
- eisa_registered = 1;
-#endif
-
-#ifdef CONFIG_PNP
- if (pnp_registered)
- ret = 0;
-#endif
- if (isa_registered)
- ret = 0;
-#ifdef CONFIG_EISA
- if (eisa_registered)
- ret = 0;
-#endif
- return ret;
-}
-
-static void __exit el3_cleanup_module(void)
-{
-#ifdef CONFIG_PNP
- if (pnp_registered)
- pnp_unregister_driver(&el3_pnp_driver);
-#endif
- if (isa_registered)
- isa_unregister_driver(&el3_isa_driver);
- if (id_port)
- release_region(id_port, 1);
-#ifdef CONFIG_EISA
- if (eisa_registered)
- eisa_driver_unregister(&el3_eisa_driver);
-#endif
-}
-
-module_init (el3_init_module);
-module_exit (el3_cleanup_module);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 1fbab79e2be4..c05a1b63c1c9 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,20 +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
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f8b73babc510..f7623fa2d441 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
# 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
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 02/15] drivers: net: 3com: 3c515: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 03/15] drivers: net: 3com: 3c574: " Andrew Lunn
` (12 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The 3c515 was written by Donald Becker between 1997-1998. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/3com/3c515.c | 1566 ------------------------------------
drivers/net/ethernet/3com/Kconfig | 11 -
drivers/net/ethernet/3com/Makefile | 1 -
3 files changed, 1578 deletions(-)
diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
deleted file mode 100644
index 2227c83a4862..000000000000
--- a/drivers/net/ethernet/3com/3c515.c
+++ /dev/null
@@ -1,1566 +0,0 @@
-/*
- Written 1997-1998 by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
-
- 2000/2/2- Added support for kernel-level ISAPnP
- by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
- Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
-
- 2001/11/17 - Added ethtool support (jgarzik)
-
- 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
-
-*/
-
-#define DRV_NAME "3c515"
-
-#define CORKSCREW 1
-
-/* "Knobs" that adjust features and parameters. */
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
- Setting to > 1512 effectively disables this feature. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Enable the automatic media selection code -- usually set. */
-#define AUTOMEDIA 1
-
-/* Allow the use of fragment bus master transfers instead of only
- programmed-I/O for Vortex cards. Full-bus-master transfers are always
- enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
- the feature may be turned on using 'options'. */
-#define VORTEX_BUS_MASTER
-
-/* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE 16
-#define RX_RING_SIZE 16
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
-
-#include <linux/module.h>
-#include <linux/isapnp.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/ethtool.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define NEW_MULTICAST
-#include <linux/delay.h>
-
-#define MAX_UNITS 8
-
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
-MODULE_LICENSE("GPL");
-
-/* "Knobs" for adjusting internal parameters. */
-/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define DRIVER_DEBUG 1
-/* Some values here only for performance evaluation and path-coverage
- debugging. */
-static int rx_nocopy, rx_copy, queued_packet;
-
-/* Number of times to check to see if the Tx FIFO has space, used in some
- limited cases. */
-#define WAIT_TX_AVAIL 200
-
-/* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */
-
-/* The size here is somewhat misleading: the Corkscrew also uses the ISA
- aliased registers at <base>+0x400.
- */
-#define CORKSCREW_TOTAL_SIZE 0x20
-
-#ifdef DRIVER_DEBUG
-static int corkscrew_debug = DRIVER_DEBUG;
-#else
-static int corkscrew_debug = 1;
-#endif
-
-#define CORKSCREW_ID 10
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL,
-3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout,
-it's not practical to integrate this driver with the other EtherLink drivers.
-
-II. Board-specific settings
-
-The Corkscrew has an EEPROM for configuration, but no special settings are
-needed for Linux.
-
-III. Driver operation
-
-The 3c515 series use an interface that's very similar to the 3c900 "Boomerang"
-PCI cards, with the bus master interface extensively modified to work with
-the ISA bus.
-
-The card is capable of full-bus-master transfers with separate
-lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
-DEC Tulip and Intel Speedo3.
-
-This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate
-receive buffer. This scheme allocates full-sized skbuffs as receive
-buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is
-chosen to trade-off the memory wasted by passing the full-sized skbuff to
-the queue layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the netif
-layer. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-IV. Notes
-
-Thanks to Terry Murphy of 3Com for providing documentation and a development
-board.
-
-The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com
-project names. I use these names to eliminate confusion -- 3Com product
-numbers and names are very similar and often confused.
-
-The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes!
-This driver only supports ethernet frames because of the recent MTU limit
-of 1.5K, but the changes to support 4.5K are minimal.
-*/
-
-/* Operational definitions.
- These are not used by other compilation units and thus are not
- exported in a ".h" file.
-
- First the windows. There are eight register windows, with the command
- and status registers available in each.
- */
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-/* The top five bits written to EL3_CMD are a command, the lower
- 11 bits are the parameter, if applicable.
- Note that 11 parameters bits was fine for ethernet, but the new chips
- can handle FDDI length frames (~4500 octets) and now parameters count
- 32-bit 'Dwords' rather than octets. */
-
-enum corkscrew_cmd {
- TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
- RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
- UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
- DownUnstall = (6 << 11) + 3, 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, StartDMAUp = 20 << 11,
- StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
- StatsDisable = 22 << 11, StopCoax = 23 << 11,
-};
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
- RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
-};
-
-/* Bits in the general status register. */
-enum corkscrew_status {
- IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
- TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
- IntReq = 0x0040, StatsFull = 0x0080,
- DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
- DMAInProgress = 1 << 11, /* DMA controller is still busy. */
- CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
- On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
-enum Window1 {
- TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
- RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
- TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
-};
-enum Window0 {
- Wn0IRQ = 0x08,
-#if defined(CORKSCREW)
- Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */
- Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */
-#else
- Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
- Wn0EepromData = 12, /* Window 0: EEPROM results register. */
-#endif
-};
-enum Win0_EEPROM_bits {
- EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
- EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
- EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
-};
-
-/* EEPROM locations. */
-enum eeprom_offset {
- PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3,
- EtherLink3ID = 7,
-};
-
-enum Window3 { /* Window 3: MAC/config bits. */
- Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
-};
-enum wn3_config {
- Ram_size = 7,
- Ram_width = 8,
- Ram_speed = 0x30,
- Rom_size = 0xc0,
- Ram_split_shift = 16,
- Ram_split = 3 << Ram_split_shift,
- Xcvr_shift = 20,
- Xcvr = 7 << Xcvr_shift,
- Autoselect = 0x1000000,
-};
-
-enum Window4 {
- Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */
-};
-enum Win4_Media_bits {
- Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
- Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
- Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
- Media_LnkBeat = 0x0800,
-};
-enum Window7 { /* Window 7: Bus Master control. */
- Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
-};
-
-/* Boomerang-style bus master control registers. Note ISA aliases! */
-enum MasterCtrl {
- PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
- 0x40c,
- TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
-};
-
-/* The Rx and Tx descriptor lists.
- Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
- alignment contraint on tx_ring[] and rx_ring[]. */
-struct boom_rx_desc {
- u32 next;
- s32 status;
- u32 addr;
- s32 length;
-};
-
-/* Values for the Rx status entry. */
-enum rx_desc_status {
- RxDComplete = 0x00008000, RxDError = 0x4000,
- /* See boomerang_rx() for actual error bits */
-};
-
-struct boom_tx_desc {
- u32 next;
- s32 status;
- u32 addr;
- s32 length;
-};
-
-struct corkscrew_private {
- const char *product_name;
- struct list_head list;
- struct net_device *our_dev;
- /* The Rx and Tx rings are here to keep them quad-word-aligned. */
- struct boom_rx_desc rx_ring[RX_RING_SIZE];
- struct boom_tx_desc tx_ring[TX_RING_SIZE];
- /* The addresses of transmit- and receive-in-place skbuffs. */
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
- struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
- struct timer_list timer; /* Media selection timer. */
- int capabilities ; /* Adapter capabilities word. */
- int options; /* User-settable misc. driver options. */
- int last_rx_packets; /* For media autoselection. */
- unsigned int available_media:8, /* From Wn3_Options */
- media_override:3, /* Passed-in media type. */
- default_media:3, /* Read from the EEPROM. */
- full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
- full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
- tx_full:1;
- spinlock_t lock;
- struct device *dev;
-};
-
-/* The action to take with a media selection timer tick.
- Note that we deviate from the 3Com order by checking 10base2 before AUI.
- */
-enum xcvr_types {
- XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
- XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
-};
-
-static struct media_table {
- char *name;
- unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
- mask:8, /* The transceiver-present bit in Wn3_Config. */
- next:8; /* The media type to try next. */
- short wait; /* Time before we check media status. */
-} media_tbl[] = {
- { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
- { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
- { "undefined", 0, 0x80, XCVR_10baseT, 10000},
- { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
- { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
- { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
- { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
- { "undefined", 0, 0x01, XCVR_10baseT, 10000},
- { "Default", 0, 0xFF, XCVR_10baseT, 10000},
-};
-
-#ifdef __ISAPNP__
-static struct isapnp_device_id corkscrew_isapnp_adapters[] = {
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051),
- (long) "3Com Fast EtherLink ISA" },
- { } /* terminate list */
-};
-
-MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters);
-
-static int nopnp;
-#endif /* __ISAPNP__ */
-
-static struct net_device *corkscrew_scan(int unit);
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
- struct pnp_dev *idev, int card_number);
-static int corkscrew_open(struct net_device *dev);
-static void corkscrew_timer(struct timer_list *t);
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static int corkscrew_rx(struct net_device *dev);
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue);
-static int boomerang_rx(struct net_device *dev);
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
-static int corkscrew_close(struct net_device *dev);
-static void update_stats(int addr, struct net_device *dev);
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-/*
- Unfortunately maximizing the shared code between the integrated and
- module version of the driver results in a complicated set of initialization
- procedures.
- init_module() -- modules / tc59x_init() -- built-in
- The wrappers for corkscrew_scan()
- corkscrew_scan() The common routine that scans for PCI and EISA cards
- corkscrew_found_device() Allocate a device structure when we find a card.
- Different versions exist for modules and built-in.
- corkscrew_probe1() Fill in the device structure -- this is separated
- so that the modules code can put it in dev->init.
-*/
-/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
-/* Note: this is the only limit on the number of cards supported!! */
-static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
-
-#ifdef MODULE
-static int debug = -1;
-
-module_param(debug, int, 0);
-module_param_array(options, int, NULL, 0);
-module_param(rx_copybreak, int, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
-MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
-MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
-
-/* A list of all installed Vortex devices, for removing the driver module. */
-/* we will need locking (and refcounting) if we ever use it for more */
-static LIST_HEAD(root_corkscrew_dev);
-
-static int corkscrew_init_module(void)
-{
- int found = 0;
- if (debug >= 0)
- corkscrew_debug = debug;
- while (corkscrew_scan(-1))
- found++;
- return found ? 0 : -ENODEV;
-}
-module_init(corkscrew_init_module);
-
-#else
-struct net_device *tc515_probe(int unit)
-{
- struct net_device *dev = corkscrew_scan(unit);
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- return dev;
-}
-#endif /* not MODULE */
-
-static int check_device(unsigned ioaddr)
-{
- int timer;
-
- if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))
- return 0;
- /* Check the resource configuration for a matching ioaddr. */
- if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
- release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
- return 0;
- }
- /* Verify by reading the device ID from the EEPROM. */
- outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
- /* Pause for at least 162 us. for the read to take place. */
- for (timer = 4; timer >= 0; timer--) {
- udelay(162);
- if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
- break;
- }
- if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
- release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
- return 0;
- }
- return 1;
-}
-
-static void cleanup_card(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- list_del_init(&vp->list);
- if (dev->dma)
- free_dma(dev->dma);
- outw(TotalReset, dev->base_addr + EL3_CMD);
- release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);
- if (vp->dev)
- pnp_device_detach(to_pnp_dev(vp->dev));
-}
-
-static struct net_device *corkscrew_scan(int unit)
-{
- struct net_device *dev;
- static int cards_found = 0;
- static int ioaddr;
- int err;
-#ifdef __ISAPNP__
- short i;
- static int pnp_cards;
-#endif
-
- dev = alloc_etherdev(sizeof(struct corkscrew_private));
- if (!dev)
- return ERR_PTR(-ENOMEM);
-
- if (unit >= 0) {
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
- }
-
-#ifdef __ISAPNP__
- if(nopnp == 1)
- goto no_pnp;
- for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
- struct pnp_dev *idev = NULL;
- int irq;
- while((idev = pnp_find_dev(NULL,
- corkscrew_isapnp_adapters[i].vendor,
- corkscrew_isapnp_adapters[i].function,
- idev))) {
-
- if (pnp_device_attach(idev) < 0)
- continue;
- if (pnp_activate_dev(idev) < 0) {
- pr_warn("pnp activate failed (out of resources?)\n");
- pnp_device_detach(idev);
- continue;
- }
- if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
- pnp_device_detach(idev);
- continue;
- }
- ioaddr = pnp_port_start(idev, 0);
- irq = pnp_irq(idev, 0);
- if (!check_device(ioaddr)) {
- pnp_device_detach(idev);
- continue;
- }
- if(corkscrew_debug)
- pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n",
- (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);
- pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
- inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
- /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
- SET_NETDEV_DEV(dev, &idev->dev);
- pnp_cards++;
- err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
- if (!err)
- return dev;
- cleanup_card(dev);
- }
- }
-no_pnp:
-#endif /* __ISAPNP__ */
-
- /* Check all locations on the ISA bus -- evil! */
- for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
- if (!check_device(ioaddr))
- continue;
-
- pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
- inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
- err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
- if (!err)
- return dev;
- cleanup_card(dev);
- }
- free_netdev(dev);
- return NULL;
-}
-
-
-static const struct net_device_ops netdev_ops = {
- .ndo_open = corkscrew_open,
- .ndo_stop = corkscrew_close,
- .ndo_start_xmit = corkscrew_start_xmit,
- .ndo_tx_timeout = corkscrew_timeout,
- .ndo_get_stats = corkscrew_get_stats,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
- struct pnp_dev *idev, int card_number)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
- __be16 addr[ETH_ALEN / 2];
- int i;
- int irq;
-
-#ifdef __ISAPNP__
- if (idev) {
- irq = pnp_irq(idev, 0);
- vp->dev = &idev->dev;
- } else {
- irq = inw(ioaddr + 0x2002) & 15;
- }
-#else
- irq = inw(ioaddr + 0x2002) & 15;
-#endif
-
- dev->base_addr = ioaddr;
- dev->irq = irq;
- dev->dma = inw(ioaddr + 0x2000) & 7;
- vp->product_name = "3c515";
- vp->options = dev->mem_start;
- vp->our_dev = dev;
-
- if (!vp->options) {
- if (card_number >= MAX_UNITS)
- vp->options = -1;
- else
- vp->options = options[card_number];
- }
-
- if (vp->options >= 0) {
- vp->media_override = vp->options & 7;
- if (vp->media_override == 2)
- vp->media_override = 0;
- vp->full_duplex = (vp->options & 8) ? 1 : 0;
- vp->bus_master = (vp->options & 16) ? 1 : 0;
- } else {
- vp->media_override = 7;
- vp->full_duplex = 0;
- vp->bus_master = 0;
- }
-#ifdef MODULE
- list_add(&vp->list, &root_corkscrew_dev);
-#endif
-
- pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
-
- spin_lock_init(&vp->lock);
-
- timer_setup(&vp->timer, corkscrew_timer, 0);
-
- /* Read the station address from the EEPROM. */
- EL3WINDOW(0);
- for (i = 0; i < 0x18; i++) {
- int timer;
- outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
- /* Pause for at least 162 us. for the read to take place. */
- for (timer = 4; timer >= 0; timer--) {
- udelay(162);
- if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
- break;
- }
- eeprom[i] = inw(ioaddr + Wn0EepromData);
- checksum ^= eeprom[i];
- if (i < 3)
- addr[i] = htons(eeprom[i]);
- }
- eth_hw_addr_set(dev, (u8 *)addr);
- checksum = (checksum ^ (checksum >> 8)) & 0xff;
- if (checksum != 0x00)
- pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
- pr_cont(" %pM", dev->dev_addr);
- if (eeprom[16] == 0x11c7) { /* Corkscrew */
- if (request_dma(dev->dma, "3c515")) {
- pr_cont(", DMA %d allocation failed", dev->dma);
- dev->dma = 0;
- } else
- pr_cont(", DMA %d", dev->dma);
- }
- pr_cont(", IRQ %d\n", dev->irq);
- /* Tell them about an invalid IRQ. */
- if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))
- pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n");
-
- {
- static const char * const ram_split[] = {
- "5:3", "3:1", "1:1", "3:5"
- };
- __u32 config;
- EL3WINDOW(3);
- vp->available_media = inw(ioaddr + Wn3_Options);
- config = inl(ioaddr + Wn3_Config);
- if (corkscrew_debug > 1)
- pr_info(" Internal config register is %4.4x, transceivers %#x.\n",
- config, inw(ioaddr + Wn3_Options));
- pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
- 8 << config & Ram_size,
- config & Ram_width ? "word" : "byte",
- ram_split[(config & Ram_split) >> Ram_split_shift],
- config & Autoselect ? "autoselect/" : "",
- media_tbl[(config & Xcvr) >> Xcvr_shift].name);
- vp->default_media = (config & Xcvr) >> Xcvr_shift;
- vp->autoselect = config & Autoselect ? 1 : 0;
- dev->if_port = vp->default_media;
- }
- if (vp->media_override != 7) {
- pr_info(" Media override to transceiver type %d (%s).\n",
- vp->media_override,
- media_tbl[vp->media_override].name);
- dev->if_port = vp->media_override;
- }
-
- vp->capabilities = eeprom[16];
- vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
- /* Rx is broken at 10mbps, so we always disable it. */
- /* vp->full_bus_master_rx = 0; */
- vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
-
- /* The 3c51x-specific entries in the device structure. */
- dev->netdev_ops = &netdev_ops;
- dev->watchdog_timeo = (400 * HZ) / 1000;
- dev->ethtool_ops = &netdev_ethtool_ops;
-
- return register_netdev(dev);
-}
-
-
-static int corkscrew_open(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- struct corkscrew_private *vp = netdev_priv(dev);
- bool armtimer = false;
- __u32 config;
- int i;
-
- /* Before initializing select the active media port. */
- EL3WINDOW(3);
- if (vp->full_duplex)
- outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
- config = inl(ioaddr + Wn3_Config);
-
- if (vp->media_override != 7) {
- if (corkscrew_debug > 1)
- pr_info("%s: Media override to transceiver %d (%s).\n",
- dev->name, vp->media_override,
- media_tbl[vp->media_override].name);
- dev->if_port = vp->media_override;
- } else if (vp->autoselect) {
- /* Find first available media type, starting with 100baseTx. */
- dev->if_port = 4;
- while (!(vp->available_media & media_tbl[dev->if_port].mask))
- dev->if_port = media_tbl[dev->if_port].next;
-
- if (corkscrew_debug > 1)
- pr_debug("%s: Initial media type %s.\n",
- dev->name, media_tbl[dev->if_port].name);
- armtimer = true;
- } else
- dev->if_port = vp->default_media;
-
- config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
- outl(config, ioaddr + Wn3_Config);
-
- if (corkscrew_debug > 1) {
- pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n",
- dev->name, config);
- }
-
- outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
-
- outw(RxReset, ioaddr + EL3_CMD);
- /* Wait a few ticks for the RxReset command to complete. */
- for (i = 20; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
-
- outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
- /* Use the now-standard shared IRQ implementation. */
- if (vp->capabilities == 0x11c7) {
- /* Corkscrew: Cannot share ISA resources. */
- if (dev->irq == 0 ||
- dev->dma == 0 ||
- request_irq(dev->irq, corkscrew_interrupt, 0,
- vp->product_name, dev))
- return -EAGAIN;
- enable_dma(dev->dma);
- set_dma_mode(dev->dma, DMA_MODE_CASCADE);
- } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
- vp->product_name, dev)) {
- return -EAGAIN;
- }
-
- if (armtimer)
- mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait);
-
- if (corkscrew_debug > 1) {
- EL3WINDOW(4);
- pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
- dev->name, dev->irq, inw(ioaddr + Wn4_Media));
- }
-
- /* Set the station address and mask in window 2 each time opened. */
- EL3WINDOW(2);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
- for (; i < 12; i += 2)
- outw(0, ioaddr + i);
-
- if (dev->if_port == 3)
- /* Start the thinnet transceiver. We should really wait 50ms... */
- outw(StartCoax, ioaddr + EL3_CMD);
- EL3WINDOW(4);
- outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
- media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 10; i++)
- inb(ioaddr + i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
- /* New: On the Vortex we must also clear the BadSSD counter. */
- EL3WINDOW(4);
- inb(ioaddr + 12);
- /* ..and on the Boomerang we enable the extra statistics bits. */
- outw(0x0040, ioaddr + Wn4_NetDiag);
-
- /* Switch to register set 7 for normal use. */
- EL3WINDOW(7);
-
- if (vp->full_bus_master_rx) { /* Boomerang bus master. */
- vp->cur_rx = vp->dirty_rx = 0;
- if (corkscrew_debug > 2)
- pr_debug("%s: Filling in the Rx ring.\n", dev->name);
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- if (i < (RX_RING_SIZE - 1))
- vp->rx_ring[i].next =
- isa_virt_to_bus(&vp->rx_ring[i + 1]);
- else
- vp->rx_ring[i].next = 0;
- vp->rx_ring[i].status = 0; /* Clear complete bit. */
- vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
- skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
- vp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break; /* Bad news! */
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
- }
- if (i != 0)
- vp->rx_ring[i - 1].next =
- isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */
- outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
- }
- if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
- vp->cur_tx = vp->dirty_tx = 0;
- outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */
- /* Clear the Tx ring. */
- for (i = 0; i < TX_RING_SIZE; i++)
- vp->tx_skbuff[i] = NULL;
- outl(0, ioaddr + DownListPtr);
- }
- /* Set receiver mode: presumably accept b-case and phys addr only. */
- set_rx_mode(dev);
- outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-
- netif_start_queue(dev);
-
- outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
- outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
- /* Allow status bits to be seen. */
- outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
- (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
- (vp->full_bus_master_rx ? UpComplete : RxComplete) |
- (vp->bus_master ? DMADone : 0), 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
- | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
- ioaddr + EL3_CMD);
-
- return 0;
-}
-
-static void corkscrew_timer(struct timer_list *t)
-{
-#ifdef AUTOMEDIA
- struct corkscrew_private *vp = timer_container_of(vp, t, timer);
- struct net_device *dev = vp->our_dev;
- int ioaddr = dev->base_addr;
- unsigned long flags;
- int ok = 0;
-
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection timer tick happened, %s.\n",
- dev->name, media_tbl[dev->if_port].name);
-
- spin_lock_irqsave(&vp->lock, flags);
-
- {
- int old_window = inw(ioaddr + EL3_CMD) >> 13;
- int media_status;
- EL3WINDOW(4);
- media_status = inw(ioaddr + Wn4_Media);
- switch (dev->if_port) {
- case 0:
- case 4:
- case 5: /* 10baseT, 100baseTX, 100baseFX */
- if (media_status & Media_LnkBeat) {
- ok = 1;
- if (corkscrew_debug > 1)
- pr_debug("%s: Media %s has link beat, %x.\n",
- dev->name,
- media_tbl[dev->if_port].name,
- media_status);
- } else if (corkscrew_debug > 1)
- pr_debug("%s: Media %s is has no link beat, %x.\n",
- dev->name,
- media_tbl[dev->if_port].name,
- media_status);
-
- break;
- default: /* Other media types handled by Tx timeouts. */
- if (corkscrew_debug > 1)
- pr_debug("%s: Media %s is has no indication, %x.\n",
- dev->name,
- media_tbl[dev->if_port].name,
- media_status);
- ok = 1;
- }
- if (!ok) {
- __u32 config;
-
- do {
- dev->if_port =
- media_tbl[dev->if_port].next;
- }
- while (!(vp->available_media & media_tbl[dev->if_port].mask));
-
- if (dev->if_port == 8) { /* Go back to default. */
- dev->if_port = vp->default_media;
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection failing, using default %s port.\n",
- dev->name,
- media_tbl[dev->if_port].name);
- } else {
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection failed, now trying %s port.\n",
- dev->name,
- media_tbl[dev->if_port].name);
- vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
- add_timer(&vp->timer);
- }
- outw((media_status & ~(Media_10TP | Media_SQE)) |
- media_tbl[dev->if_port].media_bits,
- ioaddr + Wn4_Media);
-
- EL3WINDOW(3);
- config = inl(ioaddr + Wn3_Config);
- config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
- outl(config, ioaddr + Wn3_Config);
-
- outw(dev->if_port == 3 ? StartCoax : StopCoax,
- ioaddr + EL3_CMD);
- }
- EL3WINDOW(old_window);
- }
-
- spin_unlock_irqrestore(&vp->lock, flags);
- if (corkscrew_debug > 1)
- pr_debug("%s: Media selection timer finished, %s.\n",
- dev->name, media_tbl[dev->if_port].name);
-
-#endif /* AUTOMEDIA */
-}
-
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue)
-{
- int i;
- struct corkscrew_private *vp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
-
- pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n",
- dev->name, inb(ioaddr + TxStatus),
- inw(ioaddr + EL3_STATUS));
- /* Slight code bloat to be user friendly. */
- if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
- pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n",
- dev->name);
-#ifndef final_version
- pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n",
- vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx,
- vp->cur_tx);
- pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
- &vp->tx_ring[0]);
- for (i = 0; i < TX_RING_SIZE; i++) {
- pr_debug(" %d: %p length %8.8x status %8.8x\n", i,
- &vp->tx_ring[i],
- vp->tx_ring[i].length, vp->tx_ring[i].status);
- }
-#endif
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- outw(TxEnable, ioaddr + EL3_CMD);
- netif_trans_update(dev); /* prevent tx timeout */
- dev->stats.tx_errors++;
- dev->stats.tx_dropped++;
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
-
- /* Block a timer-based transmit from overlapping. */
-
- netif_stop_queue(dev);
-
- if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */
- /* Calculate the next Tx descriptor entry. */
- int entry = vp->cur_tx % TX_RING_SIZE;
- struct boom_tx_desc *prev_entry;
- unsigned long flags;
- int i;
-
- if (vp->tx_full) /* No room to transmit with */
- return NETDEV_TX_BUSY;
- if (vp->cur_tx != 0)
- prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
- else
- prev_entry = NULL;
- if (corkscrew_debug > 3)
- pr_debug("%s: Trying to send a packet, Tx index %d.\n",
- dev->name, vp->cur_tx);
- /* vp->tx_full = 1; */
- vp->tx_skbuff[entry] = skb;
- vp->tx_ring[entry].next = 0;
- vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data);
- vp->tx_ring[entry].length = skb->len | 0x80000000;
- vp->tx_ring[entry].status = skb->len | 0x80000000;
-
- spin_lock_irqsave(&vp->lock, flags);
- outw(DownStall, ioaddr + EL3_CMD);
- /* Wait for the stall to complete. */
- for (i = 20; i >= 0; i--)
- if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
- break;
- if (prev_entry)
- prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
- if (inl(ioaddr + DownListPtr) == 0) {
- outl(isa_virt_to_bus(&vp->tx_ring[entry]),
- ioaddr + DownListPtr);
- queued_packet++;
- }
- outw(DownUnstall, ioaddr + EL3_CMD);
- spin_unlock_irqrestore(&vp->lock, flags);
-
- vp->cur_tx++;
- if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
- vp->tx_full = 1;
- else { /* Clear previous interrupt enable. */
- if (prev_entry)
- prev_entry->status &= ~0x80000000;
- netif_wake_queue(dev);
- }
- return NETDEV_TX_OK;
- }
- /* Put out the doubleword header... */
- outl(skb->len, ioaddr + TX_FIFO);
- dev->stats.tx_bytes += skb->len;
-#ifdef VORTEX_BUS_MASTER
- if (vp->bus_master) {
- /* Set the bus-master controller to transfer the packet. */
- outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr);
- outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
- vp->tx_skb = skb;
- outw(StartDMADown, ioaddr + EL3_CMD);
- /* queue will be woken at the DMADone interrupt. */
- } else {
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb(skb);
- if (inw(ioaddr + TxFree) > 1536) {
- netif_wake_queue(dev);
- } else
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + (1536 >> 2),
- ioaddr + EL3_CMD);
- }
-#else
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb(skb);
- if (inw(ioaddr + TxFree) > 1536) {
- netif_wake_queue(dev);
- } else
- /* Interrupt us when the FIFO has room for max-sized packet. */
- outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD);
-#endif /* bus master */
-
-
- /* Clear the Tx status stack. */
- {
- short tx_status;
- int i = 4;
-
- while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
- if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */
- if (corkscrew_debug > 2)
- pr_debug("%s: Tx error, status %2.2x.\n",
- dev->name, tx_status);
- if (tx_status & 0x04)
- dev->stats.tx_fifo_errors++;
- if (tx_status & 0x38)
- dev->stats.tx_aborted_errors++;
- if (tx_status & 0x30) {
- int j;
- outw(TxReset, ioaddr + EL3_CMD);
- for (j = 20; j >= 0; j--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- }
- outw(TxEnable, ioaddr + EL3_CMD);
- }
- outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
- }
- }
- return NETDEV_TX_OK;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
-{
- /* Use the now-standard shared IRQ implementation. */
- struct net_device *dev = dev_id;
- struct corkscrew_private *lp = netdev_priv(dev);
- int ioaddr, status;
- int latency;
- int i = max_interrupt_work;
-
- ioaddr = dev->base_addr;
- latency = inb(ioaddr + Timer);
-
- spin_lock(&lp->lock);
-
- status = inw(ioaddr + EL3_STATUS);
-
- if (corkscrew_debug > 4)
- pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
- dev->name, status, latency);
- if ((status & 0xE000) != 0xE000) {
- static int donedidthis;
- /* Some interrupt controllers store a bogus interrupt from boot-time.
- Ignore a single early interrupt, but don't hang the machine for
- other interrupt problems. */
- if (donedidthis++ > 100) {
- pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
- dev->name, status, netif_running(dev));
- free_irq(dev->irq, dev);
- dev->irq = -1;
- }
- }
-
- do {
- if (corkscrew_debug > 5)
- pr_debug("%s: In interrupt loop, status %4.4x.\n",
- dev->name, status);
- if (status & RxComplete)
- corkscrew_rx(dev);
-
- if (status & TxAvailable) {
- if (corkscrew_debug > 5)
- pr_debug(" 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 & DownComplete) {
- unsigned int dirty_tx = lp->dirty_tx;
-
- while (lp->cur_tx - dirty_tx > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
- if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
- break; /* It still hasn't been processed. */
- if (lp->tx_skbuff[entry]) {
- dev_consume_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = NULL;
- }
- dirty_tx++;
- }
- lp->dirty_tx = dirty_tx;
- outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
- if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
- lp->tx_full = 0;
- netif_wake_queue(dev);
- }
- }
-#ifdef VORTEX_BUS_MASTER
- if (status & DMADone) {
- outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
- dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */
- netif_wake_queue(dev);
- }
-#endif
- if (status & UpComplete) {
- boomerang_rx(dev);
- outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
- }
- if (status & (AdapterFailure | RxEarly | StatsFull)) {
- /* Handle all uncommon interrupts at once. */
- if (status & RxEarly) { /* Rx early is unused. */
- corkscrew_rx(dev);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & StatsFull) { /* Empty statistics. */
- static int DoneDidThat;
- if (corkscrew_debug > 4)
- pr_debug("%s: Updating stats.\n", dev->name);
- update_stats(ioaddr, dev);
- /* DEBUG HACK: Disable statistics as an interrupt source. */
- /* This occurs when we have the wrong media type! */
- if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
- int win, reg;
- pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
- dev->name);
- for (win = 0; win < 8; win++) {
- EL3WINDOW(win);
- pr_notice("Vortex window %d:", win);
- for (reg = 0; reg < 16; reg++)
- pr_cont(" %2.2x", inb(ioaddr + reg));
- pr_cont("\n");
- }
- EL3WINDOW(7);
- outw(SetIntrEnb | TxAvailable |
- RxComplete | AdapterFailure |
- UpComplete | DownComplete |
- TxComplete, ioaddr + EL3_CMD);
- DoneDidThat++;
- }
- }
- if (status & AdapterFailure) {
- /* Adapter failure requires Rx reset and reinit. */
- outw(RxReset, ioaddr + EL3_CMD);
- /* Set the Rx filter to the current state. */
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
- outw(AckIntr | AdapterFailure,
- ioaddr + EL3_CMD);
- }
- }
-
- if (--i < 0) {
- pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
- dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
- /* Disable all pending interrupts. */
- outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
- outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
-
- } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
-
- spin_unlock(&lp->lock);
-
- if (corkscrew_debug > 4)
- pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
- return IRQ_HANDLED;
-}
-
-static int corkscrew_rx(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- int i;
- short rx_status;
-
- if (corkscrew_debug > 5)
- pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
- inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
- while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
- if (rx_status & 0x4000) { /* Error, update stats. */
- unsigned char rx_error = inb(ioaddr + RxErrors);
- if (corkscrew_debug > 2)
- pr_debug(" Rx error: status %2.2x.\n",
- rx_error);
- dev->stats.rx_errors++;
- if (rx_error & 0x01)
- dev->stats.rx_over_errors++;
- if (rx_error & 0x02)
- dev->stats.rx_length_errors++;
- if (rx_error & 0x04)
- dev->stats.rx_frame_errors++;
- if (rx_error & 0x08)
- dev->stats.rx_crc_errors++;
- if (rx_error & 0x10)
- dev->stats.rx_length_errors++;
- } else {
- /* The packet length: up to 4.5K!. */
- short pkt_len = rx_status & 0x1fff;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 5 + 2);
- if (corkscrew_debug > 4)
- pr_debug("Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
- if (skb != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- /* 'skb_put()' points to the start of sk_buff data area. */
- insl(ioaddr + RX_FIFO,
- skb_put(skb, pkt_len),
- (pkt_len + 3) >> 2);
- outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- /* Wait a limited time to go to next packet. */
- for (i = 200; i >= 0; i--)
- if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- continue;
- } else if (corkscrew_debug)
- pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
- }
- outw(RxDiscard, ioaddr + EL3_CMD);
- dev->stats.rx_dropped++;
- /* Wait a limited time to skip this packet. */
- for (i = 200; i >= 0; i--)
- if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- }
- return 0;
-}
-
-static int boomerang_rx(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- int entry = vp->cur_rx % RX_RING_SIZE;
- int ioaddr = dev->base_addr;
- int rx_status;
-
- if (corkscrew_debug > 5)
- pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
- inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
- while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
- if (rx_status & RxDError) { /* Error, update stats. */
- unsigned char rx_error = rx_status >> 16;
- if (corkscrew_debug > 2)
- pr_debug(" Rx error: status %2.2x.\n",
- rx_error);
- dev->stats.rx_errors++;
- if (rx_error & 0x01)
- dev->stats.rx_over_errors++;
- if (rx_error & 0x02)
- dev->stats.rx_length_errors++;
- if (rx_error & 0x04)
- dev->stats.rx_frame_errors++;
- if (rx_error & 0x08)
- dev->stats.rx_crc_errors++;
- if (rx_error & 0x10)
- dev->stats.rx_length_errors++;
- } else {
- /* The packet length: up to 4.5K!. */
- short pkt_len = rx_status & 0x1fff;
- struct sk_buff *skb;
-
- dev->stats.rx_bytes += pkt_len;
- if (corkscrew_debug > 4)
- pr_debug("Receiving packet size %d status %4.4x.\n",
- pkt_len, rx_status);
-
- /* Check if the packet is long enough to just accept without
- copying to a properly sized skbuff. */
- if (pkt_len < rx_copybreak &&
- (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- /* 'skb_put()' points to the start of sk_buff data area. */
- skb_put_data(skb,
- isa_bus_to_virt(vp->rx_ring[entry].addr),
- pkt_len);
- rx_copy++;
- } else {
- void *temp;
- /* Pass up the skbuff already on the Rx ring. */
- skb = vp->rx_skbuff[entry];
- vp->rx_skbuff[entry] = NULL;
- temp = skb_put(skb, pkt_len);
- /* Remove this checking code for final release. */
- if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
- pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n",
- dev->name,
- isa_bus_to_virt(vp->rx_ring[entry].addr),
- skb->head, temp);
- rx_nocopy++;
- }
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- }
- entry = (++vp->cur_rx) % RX_RING_SIZE;
- }
- /* Refill the Rx ring buffers. */
- for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
- struct sk_buff *skb;
- entry = vp->dirty_rx % RX_RING_SIZE;
- if (vp->rx_skbuff[entry] == NULL) {
- skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
- if (skb == NULL)
- break; /* Bad news! */
- skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
- vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
- vp->rx_skbuff[entry] = skb;
- }
- vp->rx_ring[entry].status = 0; /* Clear complete bit. */
- }
- return 0;
-}
-
-static int corkscrew_close(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- int ioaddr = dev->base_addr;
- int i;
-
- netif_stop_queue(dev);
-
- if (corkscrew_debug > 1) {
- pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
- dev->name, inw(ioaddr + EL3_STATUS),
- inb(ioaddr + TxStatus));
- pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
- dev->name, rx_nocopy, rx_copy, queued_packet);
- }
-
- timer_delete_sync(&vp->timer);
-
- /* Turn off statistics ASAP. We update lp->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 == XCVR_10base2)
- /* Turn off thinnet power. Green! */
- outw(StopCoax, ioaddr + EL3_CMD);
-
- free_irq(dev->irq, dev);
-
- outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
- update_stats(ioaddr, dev);
- if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */
- outl(0, ioaddr + UpListPtr);
- for (i = 0; i < RX_RING_SIZE; i++)
- if (vp->rx_skbuff[i]) {
- dev_kfree_skb(vp->rx_skbuff[i]);
- vp->rx_skbuff[i] = NULL;
- }
- }
- if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
- outl(0, ioaddr + DownListPtr);
- for (i = 0; i < TX_RING_SIZE; i++)
- if (vp->tx_skbuff[i]) {
- dev_kfree_skb(vp->tx_skbuff[i]);
- vp->tx_skbuff[i] = NULL;
- }
- }
-
- return 0;
-}
-
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
-{
- struct corkscrew_private *vp = netdev_priv(dev);
- unsigned long flags;
-
- if (netif_running(dev)) {
- spin_lock_irqsave(&vp->lock, flags);
- update_stats(dev->base_addr, dev);
- spin_unlock_irqrestore(&vp->lock, flags);
- }
- return &dev->stats;
-}
-
-/* Update statistics.
- Unlike with the EL3 we need not worry about interrupts changing
- the window setting from underneath us, but we must still guard
- against a race condition with a StatsUpdate interrupt updating the
- table. This is done by checking that the ASM (!) code generated uses
- atomic updates with '+='.
- */
-static void update_stats(int ioaddr, struct net_device *dev)
-{
- /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
- /* 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);
- dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
- /* Rx packets */ inb(ioaddr + 7);
- /* Must read to clear */
- /* Tx deferrals */ inb(ioaddr + 8);
- /* Don't bother with register 9, an extension of registers 6&7.
- If we do use the 6&7 values the atomic update assumption above
- is invalid. */
- inw(ioaddr + 10); /* Total Rx and Tx octets. */
- inw(ioaddr + 12);
- /* New: On the Vortex we must also clear the BadSSD counter. */
- EL3WINDOW(4);
- inb(ioaddr + 12);
-
- /* We change back to window 7 (not 1) with the Vortex. */
- EL3WINDOW(7);
-}
-
-/* This new version of set_rx_mode() supports v1.4 kernels.
- The Vortex chip has no documented multicast filter, so the only
- multicast setting is to receive all multicast frames. At least
- the chip has a very clean way to set the mode, unlike many others. */
-static void set_rx_mode(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- unsigned short new_mode;
-
- if (dev->flags & IFF_PROMISC) {
- if (corkscrew_debug > 3)
- pr_debug("%s: Setting promiscuous mode.\n",
- dev->name);
- new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;
- } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
- new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;
- } else
- new_mode = SetRxFilter | RxStation | RxBroadcast;
-
- outw(new_mode, 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), "ISA 0x%lx",
- dev->base_addr);
-}
-
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
- return corkscrew_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
- corkscrew_debug = level;
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
- .get_drvinfo = netdev_get_drvinfo,
- .get_msglevel = netdev_get_msglevel,
- .set_msglevel = netdev_set_msglevel,
-};
-
-#ifdef MODULE
-static void __exit corkscrew_exit_module(void)
-{
- while (!list_empty(&root_corkscrew_dev)) {
- struct net_device *dev;
- struct corkscrew_private *vp;
-
- vp = list_entry(root_corkscrew_dev.next,
- struct corkscrew_private, list);
- dev = vp->our_dev;
- unregister_netdev(dev);
- cleanup_card(dev);
- free_netdev(dev);
- }
-}
-module_exit(corkscrew_exit_module);
-#endif /* MODULE */
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index c05a1b63c1c9..3fd3202d9776 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,17 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-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
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f7623fa2d441..babfd93d5d53 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
# Makefile for the 3Com Ethernet device drivers
#
-obj-$(CONFIG_3C515) += 3c515.o
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 03/15] drivers: net: 3com: 3c574: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 02/15] drivers: net: 3com: 3c515: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-23 2:14 ` Wei Fang
2026-04-22 18:01 ` [PATCH net v2 04/15] drivers: net: 3com: 3c589: " Andrew Lunn
` (11 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The 3c574 was written by Donald Becker between 19973-1998. It is an
PCMCIA device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/3com/3c574_cs.c | 1164 ----------------------------------
drivers/net/ethernet/3com/Kconfig | 10 -
drivers/net/ethernet/3com/Makefile | 1 -
3 files changed, 1175 deletions(-)
diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c
deleted file mode 100644
index 1f2070497a75..000000000000
--- a/drivers/net/ethernet/3com/3c574_cs.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
-
- Written 1993-1998 by
- Donald Becker, becker@scyld.com, (driver core) and
- David Hinds, dahinds@users.sourceforge.net (from his PC card code).
- Locking fixes (C) Copyright 2003 Red Hat Inc
-
- This software may be used and distributed according to the terms of
- the GNU General Public License, incorporated herein by reference.
-
- This driver derives from Donald Becker's 3c509 core, which has the
- following copyright:
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
-
-
-*/
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
-Adapter.
-
-II. Board-specific settings
-
-None -- PC cards are autoconfigured.
-
-III. Driver operation
-
-The 3c574 uses a Boomerang-style interface, without the bus-master capability.
-See the Boomerang driver and documentation for most details.
-
-IV. Notes and chip documentation.
-
-Two added registers are used to enhance PIO performance, RunnerRdCtrl and
-RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the
-count of word (16 bits) reads or writes the driver is about to do to the Rx
-or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card
-translation latency by buffering the I/O operations with an 8 word FIFO.
-Note: No other chip accesses are permitted when this buffer is used.
-
-A second enhancement is that both attribute and common memory space
-0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster
-with *some* PCcard bridges) may be used instead of I/O operations.
-This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
-
-Some slow PC card bridges work better if they never see a WAIT signal.
-This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
-Only do this after testing that it is reliable and improves performance.
-
-The upper five bits of RunnerRdCtrl are used to window into PCcard
-configuration space registers. Window 0 is the regular Boomerang/Odie
-register set, 1-5 are various PC card control registers, and 16-31 are
-the (reversed!) CIS table.
-
-A final note: writing the InternalConfig register in window 3 with an
-invalid ramWidth is Very Bad.
-
-V. References
-
-http://www.scyld.com/expert/NWay.html
-http://www.national.com/opf/DP/DP83840A.html
-
-Thanks to Terry Murphy of 3Com for providing development information for
-earlier 3Com products.
-
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.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/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/mii.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-INT_MODULE_PARM(max_interrupt_work, 32);
-
-/* Force full duplex modes? */
-INT_MODULE_PARM(full_duplex, 0);
-
-/* Autodetect link polarity reversal? */
-INT_MODULE_PARM(auto_polarity, 1);
-
-
-/*====================================================================*/
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT ((800*HZ)/1000)
-
-/* To minimize the size of the driver source and make the driver more
- readable not all constants are symbolically defined.
- You'll need the manual if you want to understand driver details anyway. */
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-#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 el3_cmds {
- 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 elxl_status {
- 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
-};
-
-enum Window0 {
- Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
- IntrStatus=0x0E, /* Valid in all windows. */
-};
-/* These assumes the larger EEPROM. */
-enum Win0_EEPROM_cmds {
- EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
- EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
- EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
- On the "Odie" this window is always mapped at offsets 0x10-0x1f.
- Except for TxFree, which is overlapped by RunnerWrCtrl. */
-enum Window1 {
- TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
- RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
- TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
- RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
-};
-
-enum Window3 { /* Window 3: MAC/config bits. */
- Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
-};
-enum wn3_config {
- Ram_size = 7,
- Ram_width = 8,
- Ram_speed = 0x30,
- Rom_size = 0xc0,
- Ram_split_shift = 16,
- Ram_split = 3 << Ram_split_shift,
- Xcvr_shift = 20,
- Xcvr = 7 << Xcvr_shift,
- Autoselect = 0x1000000,
-};
-
-enum Window4 { /* Window 4: Xcvr/media bits. */
- Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
-};
-
-#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
-
-struct el3_private {
- struct pcmcia_device *p_dev;
- u16 advertising, partner; /* NWay media advertisement */
- unsigned char phys; /* MII device address */
- unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */
- /* for transceiver monitoring */
- struct timer_list media;
- unsigned short media_status;
- unsigned short fast_poll;
- unsigned long last_irq;
- spinlock_t window_lock; /* Guards the Window selection */
-};
-
-/* Set iff a MII transceiver on any interface requires mdio preamble.
- This only set with the original DP83840 on older 3c905 boards, so the extra
- code size of a per-interface flag is not worthwhile. */
-static char mii_preamble_required = 0;
-
-/* Index of functions. */
-
-static int tc574_config(struct pcmcia_device *link);
-static void tc574_release(struct pcmcia_device *link);
-
-static void mdio_sync(unsigned int ioaddr, int bits);
-static int mdio_read(unsigned int ioaddr, int phy_id, int location);
-static void mdio_write(unsigned int ioaddr, int phy_id, int location,
- int value);
-static unsigned short read_eeprom(unsigned int ioaddr, int index);
-static void tc574_wait_for_completion(struct net_device *dev, int cmd);
-
-static void tc574_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-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, int worklimit);
-static int el3_close(struct net_device *dev);
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void set_rx_mode(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-
-static void tc574_detach(struct pcmcia_device *p_dev);
-
-/*
- tc574_attach() creates an "instance" of the driver, allocating
- local data structures for one device. The device is registered
- with Card Services.
-*/
-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_get_stats = el3_get_stats,
- .ndo_eth_ioctl = el3_ioctl,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int tc574_probe(struct pcmcia_device *link)
-{
- struct el3_private *lp;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "3c574_attach()\n");
-
- /* Create the PC card device object. */
- 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->window_lock);
- link->resource[0]->end = 32;
- 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;
-
- return tc574_config(link);
-}
-
-static void tc574_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "3c574_detach()\n");
-
- unregister_netdev(dev);
-
- tc574_release(link);
-
- free_netdev(dev);
-} /* tc574_detach */
-
-static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
-
-static int tc574_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct el3_private *lp = netdev_priv(dev);
- int ret, i, j;
- __be16 addr[ETH_ALEN / 2];
- unsigned int ioaddr;
- char *cardname;
- __u32 config;
- u8 *buf;
- size_t len;
-
- dev_dbg(&link->dev, "3c574_config()\n");
-
- link->io_lines = 16;
-
- for (i = j = 0; j < 0x400; j += 0x20) {
- 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;
-
- /* The 3c574 normally uses an EEPROM for configuration info, including
- the hardware address. The future products may include a modem chip
- and put 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 */
- EL3WINDOW(0);
- for (i = 0; i < 3; i++)
- addr[i] = htons(read_eeprom(ioaddr, i + 10));
- if (addr[0] == htons(0x6060)) {
- pr_notice("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);
- if (link->prod_id[1])
- cardname = link->prod_id[1];
- else
- cardname = "3Com 3c574";
-
- {
- u_char mcr;
- outw(2<<11, ioaddr + RunnerRdCtrl);
- mcr = inb(ioaddr + 2);
- outw(0<<11, ioaddr + RunnerRdCtrl);
- pr_info(" ASIC rev %d,", mcr>>3);
- EL3WINDOW(3);
- config = inl(ioaddr + Wn3_Config);
- lp->default_media = (config & Xcvr) >> Xcvr_shift;
- lp->autoselect = config & Autoselect ? 1 : 0;
- }
-
- timer_setup(&lp->media, media_check, 0);
-
- {
- int phy;
-
- /* Roadrunner only: Turn on the MII transceiver */
- outw(0x8040, ioaddr + Wn3_Options);
- mdelay(1);
- outw(0xc040, ioaddr + Wn3_Options);
- tc574_wait_for_completion(dev, TxReset);
- tc574_wait_for_completion(dev, RxReset);
- mdelay(1);
- outw(0x8040, ioaddr + Wn3_Options);
-
- EL3WINDOW(4);
- for (phy = 1; phy <= 32; phy++) {
- int mii_status;
- mdio_sync(ioaddr, 32);
- mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
- if (mii_status != 0xffff) {
- lp->phys = phy & 0x1f;
- dev_dbg(&link->dev, " MII transceiver at "
- "index %d, status %x.\n",
- phy, mii_status);
- if ((mii_status & 0x0040) == 0)
- mii_preamble_required = 1;
- break;
- }
- }
- if (phy > 32) {
- pr_notice(" No MII transceivers found!\n");
- goto failed;
- }
- i = mdio_read(ioaddr, lp->phys, 16) | 0x40;
- mdio_write(ioaddr, lp->phys, 16, i);
- lp->advertising = mdio_read(ioaddr, lp->phys, 4);
- if (full_duplex) {
- /* Only advertise the FD media types. */
- lp->advertising &= ~0x02a0;
- mdio_write(ioaddr, lp->phys, 4, lp->advertising);
- }
- }
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n",
- cardname, dev->base_addr, dev->irq, dev->dev_addr);
- netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n",
- 8 << (config & Ram_size),
- ram_split[(config & Ram_split) >> Ram_split_shift],
- config & Autoselect ? "autoselect " : "");
-
- return 0;
-
-failed:
- tc574_release(link);
- return -ENODEV;
-
-} /* tc574_config */
-
-static void tc574_release(struct pcmcia_device *link)
-{
- pcmcia_disable_device(link);
-}
-
-static int tc574_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int tc574_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- tc574_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-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+RxStatus), inb(ioaddr+TxStatus),
- inw(ioaddr+TxFree));
- 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);
-}
-
-/*
- Use this for commands that may take time to finish
-*/
-static void tc574_wait_for_completion(struct net_device *dev, int cmd)
-{
- int i = 1500;
- outw(cmd, dev->base_addr + EL3_CMD);
- while (--i > 0)
- if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
- if (i == 0)
- netdev_notice(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 unsigned short read_eeprom(unsigned int ioaddr, int index)
-{
- int timer;
- outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
- /* Pause for at least 162 usec for the read to take place. */
- for (timer = 1620; timer >= 0; timer--) {
- if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
- break;
- }
- return inw(ioaddr + Wn0EepromData);
-}
-
-/* MII transceiver control section.
- Read and write the MII registers using software-generated serial
- MDIO protocol. See the MII specifications or DP83840A data sheet
- for details.
- The maxium data clock rate is 2.5 Mhz. The timing is easily met by the
- slow PC card interface. */
-
-#define MDIO_SHIFT_CLK 0x01
-#define MDIO_DIR_WRITE 0x04
-#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
-#define MDIO_DATA_READ 0x02
-#define MDIO_ENB_IN 0x00
-
-/* Generate the preamble required for initial synchronization and
- a few older transceivers. */
-static void mdio_sync(unsigned int ioaddr, int bits)
-{
- unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
- /* Establish sync by sending at least 32 logic ones. */
- while (-- bits >= 0) {
- outw(MDIO_DATA_WRITE1, mdio_addr);
- outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- }
-}
-
-static int mdio_read(unsigned int ioaddr, int phy_id, int location)
-{
- int i;
- int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- unsigned int retval = 0;
- unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
- if (mii_preamble_required)
- mdio_sync(ioaddr, 32);
-
- /* Shift the read command bits out. */
- for (i = 14; i >= 0; i--) {
- int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outw(dataval, mdio_addr);
- outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
- }
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- outw(MDIO_ENB_IN, mdio_addr);
- retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value)
-{
- int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
- unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
- int i;
-
- if (mii_preamble_required)
- mdio_sync(ioaddr, 32);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outw(dataval, mdio_addr);
- outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
- }
- /* Leave the interface idle. */
- for (i = 1; i >= 0; i--) {
- outw(MDIO_ENB_IN, mdio_addr);
- outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- }
-}
-
-/* Reset and restore all of the 3c574 registers. */
-static void tc574_reset(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- int i;
- unsigned int ioaddr = dev->base_addr;
- unsigned long flags;
-
- tc574_wait_for_completion(dev, TotalReset|0x10);
-
- spin_lock_irqsave(&lp->window_lock, flags);
- /* Clear any transactions in progress. */
- outw(0, ioaddr + RunnerWrCtrl);
- outw(0, ioaddr + RunnerRdCtrl);
-
- /* Set the station address and mask. */
- EL3WINDOW(2);
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + i);
- for (; i < 12; i+=2)
- outw(0, ioaddr + i);
-
- /* Reset config options */
- EL3WINDOW(3);
- outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
- outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
- ioaddr + Wn3_Config);
- /* Roadrunner only: Turn on the MII transceiver. */
- outw(0x8040, ioaddr + Wn3_Options);
- mdelay(1);
- outw(0xc040, ioaddr + Wn3_Options);
- EL3WINDOW(1);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
- tc574_wait_for_completion(dev, TxReset);
- tc574_wait_for_completion(dev, RxReset);
- mdelay(1);
- spin_lock_irqsave(&lp->window_lock, flags);
- EL3WINDOW(3);
- outw(0x8040, ioaddr + Wn3_Options);
-
- /* Switch to the stats window, and clear all stats by reading. */
- outw(StatsDisable, ioaddr + EL3_CMD);
- EL3WINDOW(6);
- for (i = 0; i < 10; i++)
- inb(ioaddr + i);
- inw(ioaddr + 10);
- inw(ioaddr + 12);
- EL3WINDOW(4);
- inb(ioaddr + 12);
- inb(ioaddr + 13);
-
- /* .. enable any extra statistics bits.. */
- outw(0x0040, ioaddr + Wn4_NetDiag);
-
- EL3WINDOW(1);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
- /* .. re-sync MII and re-fill what NWay is advertising. */
- mdio_sync(ioaddr, 32);
- mdio_write(ioaddr, lp->phys, 4, lp->advertising);
- if (!auto_polarity) {
- /* works for TDK 78Q2120 series MII's */
- i = mdio_read(ioaddr, lp->phys, 16) | 0x20;
- mdio_write(ioaddr, lp->phys, 16, i);
- }
-
- spin_lock_irqsave(&lp->window_lock, flags);
- /* Switch to register set 1 for normal use, just for TxFree. */
- set_rx_mode(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- 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 | RxEarly, ioaddr + EL3_CMD);
-}
-
-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);
-
- tc574_reset(dev);
- lp->media.expires = jiffies + HZ;
- add_timer(&lp->media);
-
- 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_notice(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. */
- tc574_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 + TxStatus);
- if (!(tx_status & 0x84))
- break;
- /* reset transmitter on jabber error or underrun */
- if (tx_status & 0x30)
- tc574_wait_for_completion(dev, TxReset);
- if (tx_status & 0x38) {
- pr_debug("%s: transmit error: status 0x%02x\n",
- dev->name, tx_status);
- outw(TxEnable, ioaddr + EL3_CMD);
- dev->stats.tx_aborted_errors++;
- }
- outb(0x00, ioaddr + TxStatus); /* 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 *lp = netdev_priv(dev);
- unsigned long flags;
-
- pr_debug("%s: el3_start_xmit(length = %ld) called, "
- "status %4.4x.\n", dev->name, (long)skb->len,
- inw(ioaddr + EL3_STATUS));
-
- spin_lock_irqsave(&lp->window_lock, flags);
-
- dev->stats.tx_bytes += skb->len;
-
- /* Put out the doubleword header... */
- outw(skb->len, ioaddr + TX_FIFO);
- outw(0, ioaddr + TX_FIFO);
- /* ... and the packet rounded to a doubleword. */
- outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
-
- /* TxFree appears only in Window 1, not offset 0x1c. */
- if (inw(ioaddr + TxFree) <= 1536) {
- netif_stop_queue(dev);
- /* Interrupt us when the FIFO has room for max-sized packet.
- The threshold is in units of dwords. */
- outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
- }
-
- pop_tx_status(dev);
- spin_unlock_irqrestore(&lp->window_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;
- unsigned status;
- int work_budget = max_interrupt_work;
- int handled = 0;
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
- ioaddr = dev->base_addr;
-
- pr_debug("%s: interrupt, status %4.4x.\n",
- dev->name, inw(ioaddr + EL3_STATUS));
-
- spin_lock(&lp->window_lock);
-
- while ((status = inw(ioaddr + EL3_STATUS)) &
- (IntLatch | RxComplete | RxEarly | StatsFull)) {
- if (!netif_device_present(dev) ||
- ((status & 0xe000) != 0x2000)) {
- pr_debug("%s: Interrupt from dead card\n", dev->name);
- break;
- }
-
- handled = 1;
-
- if (status & RxComplete)
- work_budget = el3_rx(dev, work_budget);
-
- if (status & TxAvailable) {
- pr_debug(" 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)
- update_stats(dev);
- if (status & RxEarly) {
- work_budget = el3_rx(dev, work_budget);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & AdapterFailure) {
- u16 fifo_diag;
- EL3WINDOW(4);
- fifo_diag = inw(ioaddr + Wn4_FIFODiag);
- EL3WINDOW(1);
- netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n",
- fifo_diag);
- if (fifo_diag & 0x0400) {
- /* Tx overrun */
- tc574_wait_for_completion(dev, TxReset);
- outw(TxEnable, ioaddr + EL3_CMD);
- }
- if (fifo_diag & 0x2000) {
- /* Rx underrun */
- tc574_wait_for_completion(dev, RxReset);
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD);
- }
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
- }
-
- if (--work_budget < 0) {
- pr_debug("%s: Too much work in interrupt, "
- "status %4.4x.\n", dev->name, status);
- /* Clear all interrupts */
- outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
- break;
- }
- /* Acknowledge the IRQ. */
- outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
- }
-
- pr_debug("%s: exiting interrupt, status %4.4x.\n",
- dev->name, inw(ioaddr + EL3_STATUS));
-
- spin_unlock(&lp->window_lock);
- return IRQ_RETVAL(handled);
-}
-
-/*
- This timer serves two purposes: to check for missed interrupts
- (and as a last resort, poll the NIC for events), and to monitor
- the MII, reporting changes in cable status.
-*/
-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;
- unsigned long flags;
- unsigned short /* cable, */ media, partner;
-
- 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 + Timer) == 0xff)) {
- if (!lp->fast_poll)
- netdev_info(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 + 2*HZ/100;
- add_timer(&lp->media);
- return;
- }
-
- spin_lock_irqsave(&lp->window_lock, flags);
- EL3WINDOW(4);
- media = mdio_read(ioaddr, lp->phys, 1);
- partner = mdio_read(ioaddr, lp->phys, 5);
- EL3WINDOW(1);
-
- if (media != lp->media_status) {
- if ((media ^ lp->media_status) & 0x0004)
- netdev_info(dev, "%s link beat\n",
- (lp->media_status & 0x0004) ? "lost" : "found");
- if ((media ^ lp->media_status) & 0x0020) {
- lp->partner = 0;
- if (lp->media_status & 0x0020) {
- netdev_info(dev, "autonegotiation restarted\n");
- } else if (partner) {
- partner &= lp->advertising;
- lp->partner = partner;
- netdev_info(dev, "autonegotiation complete: "
- "%dbaseT-%cD selected\n",
- (partner & 0x0180) ? 100 : 10,
- (partner & 0x0140) ? 'F' : 'H');
- } else {
- netdev_info(dev, "link partner did not autonegotiate\n");
- }
-
- EL3WINDOW(3);
- outb((partner & 0x0140 ? 0x20 : 0) |
- (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
- EL3WINDOW(1);
-
- }
- if (media & 0x0010)
- netdev_info(dev, "remote fault detected\n");
- if (media & 0x0002)
- netdev_info(dev, "jabber detected\n");
- lp->media_status = media;
- }
- spin_unlock_irqrestore(&lp->window_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);
-
- if (netif_device_present(dev)) {
- unsigned long flags;
- spin_lock_irqsave(&lp->window_lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- }
- return &dev->stats;
-}
-
-/* Update statistics.
- Surprisingly this need not be run single-threaded, but it effectively is.
- The counters clear when read, so the adds must merely be atomic.
- */
-static void update_stats(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u8 up;
-
- pr_debug("%s: updating the statistics.\n", dev->name);
-
- if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
- return;
-
- /* Unlike the 3c509 we need not turn off stats updates while reading. */
- /* 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);
- up = inb(ioaddr + 9);
- dev->stats.tx_packets += (up&0x30) << 4;
- /* Rx packets */ inb(ioaddr + 7);
- /* Tx deferrals */ inb(ioaddr + 8);
- /* rx */ inw(ioaddr + 10);
- /* tx */ inw(ioaddr + 12);
-
- EL3WINDOW(4);
- /* BadSSD */ inb(ioaddr + 12);
- up = inb(ioaddr + 13);
-
- EL3WINDOW(1);
-}
-
-static int el3_rx(struct net_device *dev, int worklimit)
-{
- unsigned int ioaddr = dev->base_addr;
- short rx_status;
-
- pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
- dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
- while (!((rx_status = inw(ioaddr + RxStatus)) & 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);
-
- pr_debug(" 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 {
- pr_debug("%s: couldn't allocate a sk_buff of"
- " size %d.\n", dev->name, pkt_len);
- dev->stats.rx_dropped++;
- }
- }
- tc574_wait_for_completion(dev, RxDiscard);
- }
-
- return worklimit;
-}
-
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- struct mii_ioctl_data *data = if_mii(rq);
- int phy = lp->phys & 0x1f;
-
- pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
- dev->name, rq->ifr_ifrn.ifrn_name, cmd,
- data->phy_id, data->reg_num, data->val_in, data->val_out);
-
- switch(cmd) {
- case SIOCGMIIPHY: /* Get the address of the PHY in use. */
- data->phy_id = phy;
- fallthrough;
- case SIOCGMIIREG: /* Read the specified MII register. */
- {
- int saved_window;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->window_lock, flags);
- saved_window = inw(ioaddr + EL3_CMD) >> 13;
- EL3WINDOW(4);
- data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f,
- data->reg_num & 0x1f);
- EL3WINDOW(saved_window);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- return 0;
- }
- case SIOCSMIIREG: /* Write the specified MII register */
- {
- int saved_window;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->window_lock, flags);
- saved_window = inw(ioaddr + EL3_CMD) >> 13;
- EL3WINDOW(4);
- mdio_write(ioaddr, data->phy_id & 0x1f,
- data->reg_num & 0x1f, data->val_in);
- EL3WINDOW(saved_window);
- spin_unlock_irqrestore(&lp->window_lock, flags);
- return 0;
- }
- default:
- return -EOPNOTSUPP;
- }
-}
-
-/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
- documented. Until it is we revert to receiving all multicast frames when
- any multicast reception is desired.
- Note: My other drivers emit a log message whenever promiscuous mode is
- entered to help detect password sniffers. This is less desirable on
- typical PC card machines, so we omit the message.
- */
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
-
- if (dev->flags & IFF_PROMISC)
- outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
- ioaddr + EL3_CMD);
- else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
- outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
- else
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- struct el3_private *lp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&lp->window_lock, flags);
- set_rx_mode(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-}
-
-static int el3_close(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
- if (pcmcia_dev_present(link)) {
- unsigned long flags;
-
- /* Turn off statistics ASAP. We update lp->stats below. */
- outw(StatsDisable, ioaddr + EL3_CMD);
-
- /* Disable the receiver and transmitter. */
- outw(RxDisable, ioaddr + EL3_CMD);
- outw(TxDisable, ioaddr + EL3_CMD);
-
- /* Note: Switching to window 0 may disable the IRQ. */
- EL3WINDOW(0);
- spin_lock_irqsave(&lp->window_lock, flags);
- update_stats(dev);
- spin_unlock_irqrestore(&lp->window_lock, flags);
-
- /* force interrupts off */
- outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
- }
-
- link->open--;
- netif_stop_queue(dev);
- timer_delete_sync(&lp->media);
-
- return 0;
-}
-
-static const struct pcmcia_device_id tc574_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574),
- PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, tc574_ids);
-
-static struct pcmcia_driver tc574_driver = {
- .owner = THIS_MODULE,
- .name = "3c574_cs",
- .probe = tc574_probe,
- .remove = tc574_detach,
- .id_table = tc574_ids,
- .suspend = tc574_suspend,
- .resume = tc574_resume,
-};
-module_pcmcia_driver(tc574_driver);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 3fd3202d9776..294403ad7141 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,16 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-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
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index babfd93d5d53..45fb9af9b5c7 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -4,6 +4,5 @@
#
obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
-obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 04/15] drivers: net: 3com: 3c589: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (2 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 03/15] drivers: net: 3com: 3c574: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 05/15] drivers: net: amd: lance: " Andrew Lunn
` (10 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The 3c589 was written by David A. Hinds 2001. It is an PCMCIA device,
so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/3com/3c589_cs.c | 974 -----------------------------------
drivers/net/ethernet/3com/Kconfig | 10 -
drivers/net/ethernet/3com/Makefile | 1 -
3 files changed, 985 deletions(-)
diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
deleted file mode 100644
index ea49be43b8c3..000000000000
--- a/drivers/net/ethernet/3com/3c589_cs.c
+++ /dev/null
@@ -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);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 294403ad7141..399cb6c56198 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,16 +17,6 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
-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
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index 45fb9af9b5c7..5c4d07f1d456 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,6 +3,5 @@
# Makefile for the 3Com Ethernet device drivers
#
-obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 05/15] drivers: net: amd: lance: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (3 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 04/15] drivers: net: 3com: 3c589: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 06/15] drivers: net: amd: nmclan: " Andrew Lunn
` (9 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The lance was written by Donald Becker between 1993-1998. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/amd/Kconfig | 11 -
drivers/net/ethernet/amd/Makefile | 1 -
drivers/net/ethernet/amd/lance.c | 1317 -------------------------------------
3 files changed, 1329 deletions(-)
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 45e8d698781c..c5abb81977dd 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -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
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
index 2dcfb84731e1..f261501f7324 100644
--- a/drivers/net/ethernet/amd/Makefile
+++ b/drivers/net/ethernet/amd/Makefile
@@ -9,7 +9,6 @@ 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
diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c
deleted file mode 100644
index 98afd8cb0efb..000000000000
--- a/drivers/net/ethernet/amd/lance.c
+++ /dev/null
@@ -1,1317 +0,0 @@
-/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */
-/*
- Written/copyright 1993-1998 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.
-
- This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
- with most other LANCE-based bus-master (NE2100/NE2500) ethercards.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
- Andrey V. Savochkin:
- - alignment problem with 1.3.* kernel and some minor changes.
- Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
- - added support for Linux/Alpha, but removed most of it, because
- it worked only for the PCI chip.
- - added hook for the 32bit lance driver
- - added PCnetPCI II (79C970A) to chip table
- Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
- - hopefully fix above so Linux/Alpha can use ISA cards too.
- 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
- v1.12 10/27/97 Module support -djb
- v1.14 2/3/98 Module support modified, made PCI support optional -djb
- v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed
- before unregister_netdev() which caused NULL pointer
- reference later in the chain (in rtnetlink_fill_ifinfo())
- -- Mika Kuoppala <miku@iki.fi>
-
- Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
- the 2.1 version of the old driver - Alan Cox
-
- Get rid of check_region, check kmalloc return in lance_probe1
- Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
-
- Reworked detection, added support for Racal InterLan EtherBlaster cards
- Vesselin Kostadinov <vesok at yahoo dot com > - 22/4/2004
-*/
-
-static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/mm.h>
-#include <linux/bitops.h>
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
-static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options);
-static int __init do_lance_probe(struct net_device *dev);
-
-
-static struct card {
- char id_offset14;
- char id_offset15;
-} cards[] = {
- { //"normal"
- .id_offset14 = 0x57,
- .id_offset15 = 0x57,
- },
- { //NI6510EB
- .id_offset14 = 0x52,
- .id_offset15 = 0x44,
- },
- { //Racal InterLan EtherBlaster
- .id_offset14 = 0x52,
- .id_offset15 = 0x49,
- },
-};
-#define NUM_CARDS 3
-
-#ifdef LANCE_DEBUG
-static int lance_debug = LANCE_DEBUG;
-#else
-static int lance_debug = 1;
-#endif
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the AMD 79C960, the "PCnet-ISA
-single-chip ethernet controller for ISA". This chip is used in a wide
-variety of boards from vendors such as Allied Telesis, HP, Kingston,
-and Boca. This driver is also intended to work with older AMD 7990
-designs, such as the NE1500 and NE2100, and newer 79C961. For convenience,
-I use the name LANCE to refer to all of the AMD chips, even though it properly
-refers only to the original 7990.
-
-II. Board-specific settings
-
-The driver is designed to work the boards that use the faster
-bus-master mode, rather than in shared memory mode. (Only older designs
-have on-board buffer memory needed to support the slower shared memory mode.)
-
-Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
-channel. This driver probes the likely base addresses:
-{0x300, 0x320, 0x340, 0x360}.
-After the board is found it generates a DMA-timeout interrupt and uses
-autoIRQ to find the IRQ line. The DMA channel can be set with the low bits
-of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is
-probed for by enabling each free DMA channel in turn and checking if
-initialization succeeds.
-
-The HP-J2405A board is an exception: with this board it is easy to read the
-EEPROM-set values for the base, IRQ, and DMA. (Of course you must already
-_know_ the base address -- that field is for writing the EEPROM.)
-
-III. Driver operation
-
-IIIa. Ring buffers
-The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes
-the base and length of the data buffer, along with status bits. The length
-of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
-the buffer length (rather than being directly the buffer length) for
-implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to
-ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries
-needlessly uses extra space and reduces the chance that an upper layer will
-be able to reorder queued Tx packets based on priority. Decreasing the number
-of entries makes it more difficult to achieve back-to-back packet transmission
-and increases the chance that Rx ring will overflow. (Consider the worst case
-of receiving back-to-back minimum-sized packets.)
-
-The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
-statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
-avoid the administrative overhead. For the Rx side this avoids dynamically
-allocating full-sized buffers "just in case", at the expense of a
-memory-to-memory data copy for each packet received. For most systems this
-is a good tradeoff: the Rx buffer will always be in low memory, the copy
-is inexpensive, and it primes the cache for later packet processing. For Tx
-the buffers are only used when needed as low-memory bounce buffers.
-
-IIIB. 16M memory limitations.
-For the ISA bus master mode all structures used directly by the LANCE,
-the initialization block, Rx and Tx rings, and data buffers, must be
-accessible from the ISA bus, i.e. in the lower 16M of real memory.
-This is a problem for current Linux kernels on >16M machines. The network
-devices are initialized after memory initialization, and the kernel doles out
-memory from the top of memory downward. The current solution is to have a
-special network initialization routine that's called before memory
-initialization; this will eventually be generalized for all network devices.
-As mentioned before, low-memory "bounce-buffers" are used when needed.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'lp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-*/
-
-/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
- Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
- That translates to 4 and 4 (16 == 2^^4).
- This is a compile-time option for efficiency.
- */
-#ifndef LANCE_LOG_TX_BUFFERS
-#define LANCE_LOG_TX_BUFFERS 4
-#define LANCE_LOG_RX_BUFFERS 4
-#endif
-
-#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
-#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
-#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
-
-#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
-#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
-#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
-
-#define PKT_BUF_SZ 1544
-
-/* Offsets from base I/O address. */
-#define LANCE_DATA 0x10
-#define LANCE_ADDR 0x12
-#define LANCE_RESET 0x14
-#define LANCE_BUS_IF 0x16
-#define LANCE_TOTAL_SIZE 0x18
-
-#define TX_TIMEOUT (HZ/5)
-
-/* The LANCE Rx and Tx ring descriptors. */
-struct lance_rx_head {
- s32 base;
- s16 buf_length; /* This length is 2s complement (negative)! */
- s16 msg_length; /* This length is "normal". */
-};
-
-struct lance_tx_head {
- s32 base;
- s16 length; /* Length is 2s complement (negative)! */
- s16 misc;
-};
-
-/* The LANCE initialization block, described in databook. */
-struct lance_init_block {
- u16 mode; /* Pre-set mode (reg. 15) */
- u8 phys_addr[6]; /* Physical ethernet address */
- u32 filter[2]; /* Multicast filter (unused). */
- /* Receive and transmit ring base, along with extra bits. */
- u32 rx_ring; /* Tx and Rx ring base pointers */
- u32 tx_ring;
-};
-
-struct lance_private {
- /* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */
- struct lance_rx_head rx_ring[RX_RING_SIZE];
- struct lance_tx_head tx_ring[TX_RING_SIZE];
- struct lance_init_block init_block;
- const char *name;
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
- /* The addresses of receive-in-place skbuffs. */
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
- unsigned long rx_buffs; /* Address of Rx and Tx buffers. */
- /* Tx low-memory "bounce buffer" address. */
- char (*tx_bounce_buffs)[PKT_BUF_SZ];
- int cur_rx, cur_tx; /* The next free ring entry */
- int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- int dma;
- unsigned char chip_version; /* See lance_chip_type. */
- spinlock_t devlock;
-};
-
-#define LANCE_MUST_PAD 0x00000001
-#define LANCE_ENABLE_AUTOSELECT 0x00000002
-#define LANCE_MUST_REINIT_RING 0x00000004
-#define LANCE_MUST_UNRESET 0x00000008
-#define LANCE_HAS_MISSED_FRAME 0x00000010
-
-/* A mapping from the chip ID number to the part number and features.
- These are from the datasheets -- in real life the '970 version
- reportedly has the same ID as the '965. */
-static struct lance_chip_type {
- int id_number;
- const char *name;
- int flags;
-} chip_table[] = {
- {0x0000, "LANCE 7990", /* Ancient lance chip. */
- LANCE_MUST_PAD + LANCE_MUST_UNRESET},
- {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
- it the PCnet32. */
- {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
- {0x0, "PCnet (unknown)",
- LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
- LANCE_HAS_MISSED_FRAME},
-};
-
-enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6};
-
-
-/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers.
- Assume yes until we know the memory size. */
-static unsigned char lance_need_isa_bounce_buffers = 1;
-
-static int lance_open(struct net_device *dev);
-static void lance_init_ring(struct net_device *dev, gfp_t mode);
-static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static int lance_rx(struct net_device *dev);
-static irqreturn_t lance_interrupt(int irq, void *dev_id);
-static int lance_close(struct net_device *dev);
-static struct net_device_stats *lance_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue);
-
-
-
-#ifdef MODULE
-#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */
-
-static struct net_device *dev_lance[MAX_CARDS];
-static int io[MAX_CARDS];
-static int dma[MAX_CARDS];
-static int irq[MAX_CARDS];
-
-module_param_hw_array(io, int, ioport, NULL, 0);
-module_param_hw_array(dma, int, dma, NULL, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(lance_debug, int, 0);
-MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required");
-MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)");
-MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)");
-MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)");
-
-static int __init lance_init_module(void)
-{
- struct net_device *dev;
- int this_dev, found = 0;
-
- for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
- if (io[this_dev] == 0) {
- if (this_dev != 0) /* only complain once */
- break;
- printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
- return -EPERM;
- }
- dev = alloc_etherdev(0);
- if (!dev)
- break;
- dev->irq = irq[this_dev];
- dev->base_addr = io[this_dev];
- dev->dma = dma[this_dev];
- if (do_lance_probe(dev) == 0) {
- dev_lance[found++] = dev;
- continue;
- }
- free_netdev(dev);
- break;
- }
- if (found != 0)
- return 0;
- return -ENXIO;
-}
-module_init(lance_init_module);
-
-static void cleanup_card(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- if (dev->dma != 4)
- free_dma(dev->dma);
- release_region(dev->base_addr, LANCE_TOTAL_SIZE);
- kfree(lp->tx_bounce_buffs);
- kfree((void*)lp->rx_buffs);
- kfree(lp);
-}
-
-static void __exit lance_cleanup_module(void)
-{
- int this_dev;
-
- for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
- struct net_device *dev = dev_lance[this_dev];
- if (dev) {
- unregister_netdev(dev);
- cleanup_card(dev);
- free_netdev(dev);
- }
- }
-}
-module_exit(lance_cleanup_module);
-#endif /* MODULE */
-MODULE_DESCRIPTION("AMD LANCE/PCnet Ethernet driver");
-MODULE_LICENSE("GPL");
-
-
-/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other
- board probes now that kmalloc() can allocate ISA DMA-able regions.
- This also allows the LANCE driver to be used as a module.
- */
-static int __init do_lance_probe(struct net_device *dev)
-{
- unsigned int *port;
- int result;
-
- if (high_memory <= phys_to_virt(16*1024*1024))
- lance_need_isa_bounce_buffers = 0;
-
- for (port = lance_portlist; *port; port++) {
- int ioaddr = *port;
- struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE,
- "lance-probe");
-
- if (r) {
- /* Detect the card with minimal I/O reads */
- char offset14 = inb(ioaddr + 14);
- int card;
- for (card = 0; card < NUM_CARDS; ++card)
- if (cards[card].id_offset14 == offset14)
- break;
- if (card < NUM_CARDS) {/*yes, the first byte matches*/
- char offset15 = inb(ioaddr + 15);
- for (card = 0; card < NUM_CARDS; ++card)
- if ((cards[card].id_offset14 == offset14) &&
- (cards[card].id_offset15 == offset15))
- break;
- }
- if (card < NUM_CARDS) { /*Signature OK*/
- result = lance_probe1(dev, ioaddr, 0, 0);
- if (!result) {
- struct lance_private *lp = dev->ml_priv;
- int ver = lp->chip_version;
-
- r->name = chip_table[ver].name;
- return 0;
- }
- }
- release_region(ioaddr, LANCE_TOTAL_SIZE);
- }
- }
- return -ENODEV;
-}
-
-#ifndef MODULE
-struct net_device * __init lance_probe(int unit)
-{
- struct net_device *dev = alloc_etherdev(0);
- int err;
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
-
- err = do_lance_probe(dev);
- if (err)
- goto out;
- return dev;
-out:
- free_netdev(dev);
- return ERR_PTR(err);
-}
-#endif
-
-static const struct net_device_ops lance_netdev_ops = {
- .ndo_open = lance_open,
- .ndo_start_xmit = lance_start_xmit,
- .ndo_stop = lance_close,
- .ndo_get_stats = lance_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_tx_timeout = lance_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
-{
- struct lance_private *lp;
- unsigned long dma_channels; /* Mark spuriously-busy DMA channels */
- int i, reset_val, lance_version;
- const char *chipname;
- /* Flags for specific chips or boards. */
- unsigned char hpJ2405A = 0; /* HP ISA adaptor */
- int hp_builtin = 0; /* HP on-board ethernet. */
- static int did_version; /* Already printed version info. */
- unsigned long flags;
- int err = -ENOMEM;
- void __iomem *bios;
- u8 addr[ETH_ALEN];
-
- /* First we look for special cases.
- Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
- There are two HP versions, check the BIOS for the configuration port.
- This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com.
- */
- bios = ioremap(0xf00f0, 0x14);
- if (!bios)
- return -ENOMEM;
- if (readw(bios + 0x12) == 0x5048) {
- static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
- int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99;
- /* We can have boards other than the built-in! Verify this is on-board. */
- if ((inb(hp_port) & 0xc0) == 0x80 &&
- ioaddr_table[inb(hp_port) & 3] == ioaddr)
- hp_builtin = hp_port;
- }
- iounmap(bios);
- /* We also recognize the HP Vectra on-board here, but check below. */
- hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 &&
- inb(ioaddr+2) == 0x09);
-
- /* Reset the LANCE. */
- reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
-
- /* The Un-Reset needed is only needed for the real NE2100, and will
- confuse the HP board. */
- if (!hpJ2405A)
- outw(reset_val, ioaddr+LANCE_RESET);
-
- outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
- if (inw(ioaddr+LANCE_DATA) != 0x0004)
- return -ENODEV;
-
- /* Get the version of the chip. */
- outw(88, ioaddr+LANCE_ADDR);
- if (inw(ioaddr+LANCE_ADDR) != 88) {
- lance_version = 0;
- } else { /* Good, it's a newer chip. */
- int chip_version = inw(ioaddr+LANCE_DATA);
- outw(89, ioaddr+LANCE_ADDR);
- chip_version |= inw(ioaddr+LANCE_DATA) << 16;
- if (lance_debug > 2)
- printk(" LANCE chip version is %#x.\n", chip_version);
- if ((chip_version & 0xfff) != 0x003)
- return -ENODEV;
- chip_version = (chip_version >> 12) & 0xffff;
- for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
- if (chip_table[lance_version].id_number == chip_version)
- break;
- }
- }
-
- /* We can't allocate private data from alloc_etherdev() because it must
- a ISA DMA-able region. */
- chipname = chip_table[lance_version].name;
- printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr);
-
- /* There is a 16 byte station address PROM at the base address.
- The first six bytes are the station address. */
- for (i = 0; i < 6; i++)
- addr[i] = inb(ioaddr + i);
- eth_hw_addr_set(dev, addr);
- printk("%pM", dev->dev_addr);
-
- dev->base_addr = ioaddr;
- /* Make certain the data structures used by the LANCE are aligned and DMAble. */
-
- lp = kzalloc_obj(*lp, GFP_DMA | GFP_KERNEL);
- if (!lp)
- return -ENOMEM;
- if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
- dev->ml_priv = lp;
- lp->name = chipname;
- lp->rx_buffs = (unsigned long)kmalloc_array(RX_RING_SIZE, PKT_BUF_SZ,
- GFP_DMA | GFP_KERNEL);
- if (!lp->rx_buffs)
- goto out_lp;
- if (lance_need_isa_bounce_buffers) {
- lp->tx_bounce_buffs = kmalloc_array(TX_RING_SIZE, PKT_BUF_SZ,
- GFP_DMA | GFP_KERNEL);
- if (!lp->tx_bounce_buffs)
- goto out_rx;
- } else
- lp->tx_bounce_buffs = NULL;
-
- lp->chip_version = lance_version;
- spin_lock_init(&lp->devlock);
-
- lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
- for (i = 0; i < 6; i++)
- lp->init_block.phys_addr[i] = dev->dev_addr[i];
- lp->init_block.filter[0] = 0x00000000;
- lp->init_block.filter[1] = 0x00000000;
- lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
- lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
-
- outw(0x0001, ioaddr+LANCE_ADDR);
- inw(ioaddr+LANCE_ADDR);
- outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
- outw(0x0002, ioaddr+LANCE_ADDR);
- inw(ioaddr+LANCE_ADDR);
- outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
- outw(0x0000, ioaddr+LANCE_ADDR);
- inw(ioaddr+LANCE_ADDR);
-
- if (irq) { /* Set iff PCI card. */
- dev->dma = 4; /* Native bus-master, no DMA channel needed. */
- dev->irq = irq;
- } else if (hp_builtin) {
- static const char dma_tbl[4] = {3, 5, 6, 0};
- static const char irq_tbl[4] = {3, 4, 5, 9};
- unsigned char port_val = inb(hp_builtin);
- dev->dma = dma_tbl[(port_val >> 4) & 3];
- dev->irq = irq_tbl[(port_val >> 2) & 3];
- printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma);
- } else if (hpJ2405A) {
- static const char dma_tbl[4] = {3, 5, 6, 7};
- static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15};
- short reset_val = inw(ioaddr+LANCE_RESET);
- dev->dma = dma_tbl[(reset_val >> 2) & 3];
- dev->irq = irq_tbl[(reset_val >> 4) & 7];
- printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
- } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */
- short bus_info;
- outw(8, ioaddr+LANCE_ADDR);
- bus_info = inw(ioaddr+LANCE_BUS_IF);
- dev->dma = bus_info & 0x07;
- dev->irq = (bus_info >> 4) & 0x0F;
- } else {
- /* The DMA channel may be passed in PARAM1. */
- if (dev->mem_start & 0x07)
- dev->dma = dev->mem_start & 0x07;
- }
-
- if (dev->dma == 0) {
- /* Read the DMA channel status register, so that we can avoid
- stuck DMA channels in the DMA detection below. */
- dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
- (inb(DMA2_STAT_REG) & 0xf0);
- }
- err = -ENODEV;
- if (dev->irq >= 2)
- printk(" assigned IRQ %d", dev->irq);
- else if (lance_version != 0) { /* 7990 boards need DMA detection first. */
- unsigned long irq_mask;
-
- /* To auto-IRQ we enable the initialization-done and DMA error
- interrupts. For ISA boards we get a DMA error, but VLB and PCI
- boards will work. */
- irq_mask = probe_irq_on();
-
- /* Trigger an initialization just for the interrupt. */
- outw(0x0041, ioaddr+LANCE_DATA);
-
- mdelay(20);
- dev->irq = probe_irq_off(irq_mask);
- if (dev->irq)
- printk(", probed IRQ %d", dev->irq);
- else {
- printk(", failed to detect IRQ line.\n");
- goto out_tx;
- }
-
- /* Check for the initialization done bit, 0x0100, which means
- that we don't need a DMA channel. */
- if (inw(ioaddr+LANCE_DATA) & 0x0100)
- dev->dma = 4;
- }
-
- if (dev->dma == 4) {
- printk(", no DMA needed.\n");
- } else if (dev->dma) {
- if (request_dma(dev->dma, chipname)) {
- printk("DMA %d allocation failed.\n", dev->dma);
- goto out_tx;
- } else
- printk(", assigned DMA %d.\n", dev->dma);
- } else { /* OK, we have to auto-DMA. */
- for (i = 0; i < 4; i++) {
- static const char dmas[] = { 5, 6, 7, 3 };
- int dma = dmas[i];
- int boguscnt;
-
- /* Don't enable a permanently busy DMA channel, or the machine
- will hang. */
- if (test_bit(dma, &dma_channels))
- continue;
- outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
- if (request_dma(dma, chipname))
- continue;
-
- flags=claim_dma_lock();
- set_dma_mode(dma, DMA_MODE_CASCADE);
- enable_dma(dma);
- release_dma_lock(flags);
-
- /* Trigger an initialization. */
- outw(0x0001, ioaddr+LANCE_DATA);
- for (boguscnt = 100; boguscnt > 0; --boguscnt)
- if (inw(ioaddr+LANCE_DATA) & 0x0900)
- break;
- if (inw(ioaddr+LANCE_DATA) & 0x0100) {
- dev->dma = dma;
- printk(", DMA %d.\n", dev->dma);
- break;
- } else {
- flags=claim_dma_lock();
- disable_dma(dma);
- release_dma_lock(flags);
- free_dma(dma);
- }
- }
- if (i == 4) { /* Failure: bail. */
- printk("DMA detection failed.\n");
- goto out_tx;
- }
- }
-
- if (lance_version == 0 && dev->irq == 0) {
- /* We may auto-IRQ now that we have a DMA channel. */
- /* Trigger an initialization just for the interrupt. */
- unsigned long irq_mask;
-
- irq_mask = probe_irq_on();
- outw(0x0041, ioaddr+LANCE_DATA);
-
- mdelay(40);
- dev->irq = probe_irq_off(irq_mask);
- if (dev->irq == 0) {
- printk(" Failed to detect the 7990 IRQ line.\n");
- goto out_dma;
- }
- printk(" Auto-IRQ detected IRQ%d.\n", dev->irq);
- }
-
- if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
- /* Turn on auto-select of media (10baseT or BNC) so that the user
- can watch the LEDs even if the board isn't opened. */
- outw(0x0002, ioaddr+LANCE_ADDR);
- /* Don't touch 10base2 power bit. */
- outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
- }
-
- if (lance_debug > 0 && did_version++ == 0)
- printk(version);
-
- /* The LANCE-specific entries in the device structure. */
- dev->netdev_ops = &lance_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- err = register_netdev(dev);
- if (err)
- goto out_dma;
- return 0;
-out_dma:
- if (dev->dma != 4)
- free_dma(dev->dma);
-out_tx:
- kfree(lp->tx_bounce_buffs);
-out_rx:
- kfree((void*)lp->rx_buffs);
-out_lp:
- kfree(lp);
- return err;
-}
-
-
-static int
-lance_open(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int ioaddr = dev->base_addr;
- int i;
-
- if (dev->irq == 0 ||
- request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) {
- return -EAGAIN;
- }
-
- /* We used to allocate DMA here, but that was silly.
- DMA lines can't be shared! We now permanently allocate them. */
-
- /* Reset the LANCE */
- inw(ioaddr+LANCE_RESET);
-
- /* The DMA controller is used as a no-operation slave, "cascade mode". */
- if (dev->dma != 4) {
- unsigned long flags=claim_dma_lock();
- enable_dma(dev->dma);
- set_dma_mode(dev->dma, DMA_MODE_CASCADE);
- release_dma_lock(flags);
- }
-
- /* Un-Reset the LANCE, needed only for the NE2100. */
- if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
- outw(0, ioaddr+LANCE_RESET);
-
- if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
- /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
- outw(0x0002, ioaddr+LANCE_ADDR);
- /* Only touch autoselect bit. */
- outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
- }
-
- if (lance_debug > 1)
- printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
- dev->name, dev->irq, dev->dma,
- (u32) isa_virt_to_bus(lp->tx_ring),
- (u32) isa_virt_to_bus(lp->rx_ring),
- (u32) isa_virt_to_bus(&lp->init_block));
-
- lance_init_ring(dev, GFP_KERNEL);
- /* Re-initialize the LANCE, and start it when done. */
- outw(0x0001, ioaddr+LANCE_ADDR);
- outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
- outw(0x0002, ioaddr+LANCE_ADDR);
- outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
-
- outw(0x0004, ioaddr+LANCE_ADDR);
- outw(0x0915, ioaddr+LANCE_DATA);
-
- outw(0x0000, ioaddr+LANCE_ADDR);
- outw(0x0001, ioaddr+LANCE_DATA);
-
- netif_start_queue (dev);
-
- i = 0;
- while (i++ < 100)
- if (inw(ioaddr+LANCE_DATA) & 0x0100)
- break;
- /*
- * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
- * reports that doing so triggers a bug in the '974.
- */
- outw(0x0042, ioaddr+LANCE_DATA);
-
- if (lance_debug > 2)
- printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
- dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
-
- return 0; /* Always succeed */
-}
-
-/* The LANCE has been halted for one reason or another (busmaster memory
- arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
- etc.). Modern LANCE variants always reload their ring-buffer
- configuration when restarted, so we must reinitialize our ring
- context before restarting. As part of this reinitialization,
- find all packets still on the Tx ring and pretend that they had been
- sent (in effect, drop the packets on the floor) - the higher-level
- protocols will time out and retransmit. It'd be better to shuffle
- these skbs to a temp list and then actually re-Tx them after
- restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
-*/
-
-static void
-lance_purge_ring(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int i;
-
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = lp->rx_skbuff[i];
- lp->rx_skbuff[i] = NULL;
- lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */
- if (skb)
- dev_kfree_skb_any(skb);
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- if (lp->tx_skbuff[i]) {
- dev_kfree_skb_any(lp->tx_skbuff[i]);
- lp->tx_skbuff[i] = NULL;
- }
- }
-}
-
-
-/* Initialize the LANCE Rx and Tx rings. */
-static void
-lance_init_ring(struct net_device *dev, gfp_t gfp)
-{
- struct lance_private *lp = dev->ml_priv;
- int i;
-
- lp->cur_rx = lp->cur_tx = 0;
- lp->dirty_rx = lp->dirty_tx = 0;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- void *rx_buff;
-
- skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
- lp->rx_skbuff[i] = skb;
- if (skb)
- rx_buff = skb->data;
- else
- rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
- if (!rx_buff)
- lp->rx_ring[i].base = 0;
- else
- lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000;
- lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
- }
- /* The Tx buffer address is filled in as needed, but we do need to clear
- the upper ownership bit. */
- for (i = 0; i < TX_RING_SIZE; i++) {
- lp->tx_skbuff[i] = NULL;
- lp->tx_ring[i].base = 0;
- }
-
- lp->init_block.mode = 0x0000;
- for (i = 0; i < 6; i++)
- lp->init_block.phys_addr[i] = dev->dev_addr[i];
- lp->init_block.filter[0] = 0x00000000;
- lp->init_block.filter[1] = 0x00000000;
- lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
- lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
-}
-
-static void
-lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
-{
- struct lance_private *lp = dev->ml_priv;
-
- if (must_reinit ||
- (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
- lance_purge_ring(dev);
- lance_init_ring(dev, GFP_ATOMIC);
- }
- outw(0x0000, dev->base_addr + LANCE_ADDR);
- outw(csr0_bits, dev->base_addr + LANCE_DATA);
-}
-
-
-static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
- struct lance_private *lp = (struct lance_private *) dev->ml_priv;
- int ioaddr = dev->base_addr;
-
- outw (0, ioaddr + LANCE_ADDR);
- printk ("%s: transmit timed out, status %4.4x, resetting.\n",
- dev->name, inw (ioaddr + LANCE_DATA));
- outw (0x0004, ioaddr + LANCE_DATA);
- dev->stats.tx_errors++;
-#ifndef final_version
- if (lance_debug > 3) {
- int i;
- printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
- lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "",
- lp->cur_rx);
- for (i = 0; i < RX_RING_SIZE; i++)
- printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
- lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
- lp->rx_ring[i].msg_length);
- for (i = 0; i < TX_RING_SIZE; i++)
- printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
- lp->tx_ring[i].base, -lp->tx_ring[i].length,
- lp->tx_ring[i].misc);
- printk ("\n");
- }
-#endif
- lance_restart (dev, 0x0043, 1);
-
- netif_trans_update(dev); /* prevent tx timeout */
- netif_wake_queue (dev);
-}
-
-
-static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int ioaddr = dev->base_addr;
- int entry;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->devlock, flags);
-
- if (lance_debug > 3) {
- outw(0x0000, ioaddr+LANCE_ADDR);
- printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
- inw(ioaddr+LANCE_DATA));
- outw(0x0000, ioaddr+LANCE_DATA);
- }
-
- /* Fill in a Tx ring entry */
-
- /* Mask to ring buffer boundary. */
- entry = lp->cur_tx & TX_RING_MOD_MASK;
-
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
-
- /* The old LANCE chips doesn't automatically pad buffers to min. size. */
- if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
- if (skb->len < ETH_ZLEN) {
- if (skb_padto(skb, ETH_ZLEN))
- goto out;
- lp->tx_ring[entry].length = -ETH_ZLEN;
- }
- else
- lp->tx_ring[entry].length = -skb->len;
- } else
- lp->tx_ring[entry].length = -skb->len;
-
- lp->tx_ring[entry].misc = 0x0000;
-
- dev->stats.tx_bytes += skb->len;
-
- /* If any part of this buffer is >16M we must copy it to a low-memory
- buffer. */
- if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) {
- if (lance_debug > 5)
- printk("%s: bouncing a high-memory packet (%#x).\n",
- dev->name, (u32)isa_virt_to_bus(skb->data));
- skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len);
- lp->tx_ring[entry].base =
- ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
- dev_consume_skb_irq(skb);
- } else {
- lp->tx_skbuff[entry] = skb;
- lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
- }
- lp->cur_tx++;
-
- /* Trigger an immediate send poll. */
- outw(0x0000, ioaddr+LANCE_ADDR);
- outw(0x0048, ioaddr+LANCE_DATA);
-
- if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE)
- netif_stop_queue(dev);
-
-out:
- spin_unlock_irqrestore(&lp->devlock, flags);
- return NETDEV_TX_OK;
-}
-
-/* The LANCE interrupt handler. */
-static irqreturn_t lance_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct lance_private *lp;
- int csr0, ioaddr, boguscnt=10;
- int must_restart;
-
- ioaddr = dev->base_addr;
- lp = dev->ml_priv;
-
- spin_lock (&lp->devlock);
-
- outw(0x00, dev->base_addr + LANCE_ADDR);
- while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 &&
- --boguscnt >= 0) {
- /* Acknowledge all of the current interrupt sources ASAP. */
- outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
-
- must_restart = 0;
-
- if (lance_debug > 5)
- printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
- dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
-
- if (csr0 & 0x0400) /* Rx interrupt */
- lance_rx(dev);
-
- if (csr0 & 0x0200) { /* Tx-done interrupt */
- int dirty_tx = lp->dirty_tx;
-
- while (dirty_tx < lp->cur_tx) {
- int entry = dirty_tx & TX_RING_MOD_MASK;
- int status = lp->tx_ring[entry].base;
-
- if (status < 0)
- break; /* It still hasn't been Txed */
-
- lp->tx_ring[entry].base = 0;
-
- if (status & 0x40000000) {
- /* There was an major error, log it. */
- int err_status = lp->tx_ring[entry].misc;
- dev->stats.tx_errors++;
- if (err_status & 0x0400)
- dev->stats.tx_aborted_errors++;
- if (err_status & 0x0800)
- dev->stats.tx_carrier_errors++;
- if (err_status & 0x1000)
- dev->stats.tx_window_errors++;
- if (err_status & 0x4000) {
- /* Ackk! On FIFO errors the Tx unit is turned off! */
- dev->stats.tx_fifo_errors++;
- /* Remove this verbosity later! */
- printk("%s: Tx FIFO error! Status %4.4x.\n",
- dev->name, csr0);
- /* Restart the chip. */
- must_restart = 1;
- }
- } else {
- if (status & 0x18000000)
- dev->stats.collisions++;
- dev->stats.tx_packets++;
- }
-
- /* We must free the original skb if it's not a data-only copy
- in the bounce buffer. */
- if (lp->tx_skbuff[entry]) {
- dev_consume_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = NULL;
- }
- dirty_tx++;
- }
-
-#ifndef final_version
- if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
- printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n",
- dirty_tx, lp->cur_tx,
- netif_queue_stopped(dev) ? "yes" : "no");
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- /* if the ring is no longer full, accept more packets */
- if (netif_queue_stopped(dev) &&
- dirty_tx > lp->cur_tx - TX_RING_SIZE + 2)
- netif_wake_queue (dev);
-
- lp->dirty_tx = dirty_tx;
- }
-
- /* Log misc errors. */
- if (csr0 & 0x4000)
- dev->stats.tx_errors++; /* Tx babble. */
- if (csr0 & 0x1000)
- dev->stats.rx_errors++; /* Missed a Rx frame. */
- if (csr0 & 0x0800) {
- printk("%s: Bus master arbitration failure, status %4.4x.\n",
- dev->name, csr0);
- /* Restart the chip. */
- must_restart = 1;
- }
-
- if (must_restart) {
- /* stop the chip to clear the error condition, then restart */
- outw(0x0000, dev->base_addr + LANCE_ADDR);
- outw(0x0004, dev->base_addr + LANCE_DATA);
- lance_restart(dev, 0x0002, 0);
- }
- }
-
- /* Clear any other interrupt, and set interrupt enable. */
- outw(0x0000, dev->base_addr + LANCE_ADDR);
- outw(0x7940, dev->base_addr + LANCE_DATA);
-
- if (lance_debug > 4)
- printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
- dev->name, inw(ioaddr + LANCE_ADDR),
- inw(dev->base_addr + LANCE_DATA));
-
- spin_unlock (&lp->devlock);
- return IRQ_HANDLED;
-}
-
-static int
-lance_rx(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
- int entry = lp->cur_rx & RX_RING_MOD_MASK;
- int i;
-
- /* If we own the next entry, it's a new packet. Send it up. */
- while (lp->rx_ring[entry].base >= 0) {
- int status = lp->rx_ring[entry].base >> 24;
-
- if (status != 0x03) { /* There was an error. */
- /* There is a tricky error noted by John Murphy,
- <murf@perftech.com> to Russ Nelson: Even with full-sized
- buffers it's possible for a jabber packet to use two
- buffers, with only the last correctly noting the error. */
- if (status & 0x01) /* Only count a general error at the */
- dev->stats.rx_errors++; /* end of a packet.*/
- if (status & 0x20)
- dev->stats.rx_frame_errors++;
- if (status & 0x10)
- dev->stats.rx_over_errors++;
- if (status & 0x08)
- dev->stats.rx_crc_errors++;
- if (status & 0x04)
- dev->stats.rx_fifo_errors++;
- lp->rx_ring[entry].base &= 0x03ffffff;
- }
- else
- {
- /* Malloc up new buffer, compatible with net3. */
- short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
- struct sk_buff *skb;
-
- if(pkt_len<60)
- {
- printk("%s: Runt packet!\n",dev->name);
- dev->stats.rx_errors++;
- }
- else
- {
- skb = dev_alloc_skb(pkt_len+2);
- if (!skb)
- {
- printk("%s: Memory squeeze, deferring packet.\n", dev->name);
- for (i=0; i < RX_RING_SIZE; i++)
- if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
- break;
-
- if (i > RX_RING_SIZE -2)
- {
- dev->stats.rx_dropped++;
- lp->rx_ring[entry].base |= 0x80000000;
- lp->cur_rx++;
- }
- break;
- }
- skb_reserve(skb,2); /* 16 byte align */
- skb_put(skb,pkt_len); /* Make room */
- skb_copy_to_linear_data(skb,
- (unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)),
- pkt_len);
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- }
- }
- /* The docs say that the buffer length isn't touched, but Andrew Boyd
- of QNX reports that some revs of the 79C965 clear it. */
- lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
- lp->rx_ring[entry].base |= 0x80000000;
- entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
- }
-
- /* We should check that at least two ring entries are free. If not,
- we should free one and mark stats->rx_dropped++. */
-
- return 0;
-}
-
-static int
-lance_close(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- struct lance_private *lp = dev->ml_priv;
-
- netif_stop_queue (dev);
-
- if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
- outw(112, ioaddr+LANCE_ADDR);
- dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
- }
- outw(0, ioaddr+LANCE_ADDR);
-
- if (lance_debug > 1)
- printk("%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inw(ioaddr+LANCE_DATA));
-
- /* We stop the LANCE here -- it occasionally polls
- memory if we don't. */
- outw(0x0004, ioaddr+LANCE_DATA);
-
- if (dev->dma != 4)
- {
- unsigned long flags=claim_dma_lock();
- disable_dma(dev->dma);
- release_dma_lock(flags);
- }
- free_irq(dev->irq, dev);
-
- lance_purge_ring(dev);
-
- return 0;
-}
-
-static struct net_device_stats *lance_get_stats(struct net_device *dev)
-{
- struct lance_private *lp = dev->ml_priv;
-
- if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
- short ioaddr = dev->base_addr;
- short saved_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->devlock, flags);
- saved_addr = inw(ioaddr+LANCE_ADDR);
- outw(112, ioaddr+LANCE_ADDR);
- dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
- outw(saved_addr, ioaddr+LANCE_ADDR);
- spin_unlock_irqrestore(&lp->devlock, flags);
- }
-
- return &dev->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- */
-
-static void set_multicast_list(struct net_device *dev)
-{
- short ioaddr = dev->base_addr;
-
- outw(0, ioaddr+LANCE_ADDR);
- outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
-
- if (dev->flags&IFF_PROMISC) {
- outw(15, ioaddr+LANCE_ADDR);
- outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
- } else {
- short multicast_table[4];
- int i;
- int num_addrs=netdev_mc_count(dev);
- if(dev->flags&IFF_ALLMULTI)
- num_addrs=1;
- /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
- memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
- for (i = 0; i < 4; i++) {
- outw(8 + i, ioaddr+LANCE_ADDR);
- outw(multicast_table[i], ioaddr+LANCE_DATA);
- }
- outw(15, ioaddr+LANCE_ADDR);
- outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
- }
-
- lance_restart(dev, 0x0142, 0); /* Resume normal operation */
-
-}
-
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 06/15] drivers: net: amd: nmclan: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (4 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 05/15] drivers: net: amd: lance: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 07/15] drivers: net: smsc: smc9194: " Andrew Lunn
` (8 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The nmclan was written by Roger C Pao in 1995. It is an PCMCIA device,
so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/amd/Kconfig | 10 -
drivers/net/ethernet/amd/Makefile | 1 -
drivers/net/ethernet/amd/nmclan_cs.c | 1508 ----------------------------------
3 files changed, 1519 deletions(-)
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index c5abb81977dd..e35991141a1a 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -109,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)
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
index f261501f7324..e485fae235a7 100644
--- a/drivers/net/ethernet/amd/Makefile
+++ b/drivers/net/ethernet/amd/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_DECLANCE) += declance.o
obj-$(CONFIG_HPLANCE) += hplance.o 7990.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
diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
deleted file mode 100644
index 37054a670407..000000000000
--- a/drivers/net/ethernet/amd/nmclan_cs.c
+++ /dev/null
@@ -1,1508 +0,0 @@
-/* ----------------------------------------------------------------------------
-Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
- nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
-
- The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
- Access Controller for Ethernet (MACE). It is essentially the Am2150
- PCMCIA Ethernet card contained in the Am2150 Demo Kit.
-
-Written by Roger C. Pao <rpao@paonet.org>
- Copyright 1995 Roger C. Pao
- Linux 2.5 cleanups Copyright Red Hat 2003
-
- This software may be used and distributed according to the terms of
- the GNU General Public License.
-
-Ported to Linux 1.3.* network driver environment by
- Matti Aarnio <mea@utu.fi>
-
-References
-
- Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
- Am79C940 (MACE) Data Sheet, 1994
- Am79C90 (C-LANCE) Data Sheet, 1994
- Linux PCMCIA Programmer's Guide v1.17
- /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
-
- Eric Mears, New Media Corporation
- Tom Pollard, New Media Corporation
- Dean Siasoyco, New Media Corporation
- Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
- Donald Becker <becker@scyld.com>
- David Hinds <dahinds@users.sourceforge.net>
-
- The Linux client driver is based on the 3c589_cs.c client driver by
- David Hinds.
-
- The Linux network driver outline is based on the 3c589_cs.c driver,
- the 8390.c driver, and the example skeleton.c kernel code, which are
- by Donald Becker.
-
- The Am2150 network driver hardware interface code is based on the
- OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
-
- Special thanks for testing and help in debugging this driver goes
- to Ken Lesniak.
-
--------------------------------------------------------------------------------
-Driver Notes and Issues
--------------------------------------------------------------------------------
-
-1. Developed on a Dell 320SLi
- PCMCIA Card Services 2.6.2
- Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
-
-2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
- 'insmod pcmcia_core.o io_speed=300'.
- This will avoid problems with fast systems which causes rx_framecnt
- to return random values.
-
-3. If hot extraction does not work for you, use 'ifconfig eth0 down'
- before extraction.
-
-4. There is a bad slow-down problem in this driver.
-
-5. Future: Multicast processing. In the meantime, do _not_ compile your
- kernel with multicast ip enabled.
-
--------------------------------------------------------------------------------
-History
--------------------------------------------------------------------------------
-Log: nmclan_cs.c,v
- * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk>
- * Fixed hang on card eject as we probe it
- * Cleaned up to use new style locking.
- *
- * Revision 0.16 1995/07/01 06:42:17 rpao
- * Bug fix: nmclan_reset() called CardServices incorrectly.
- *
- * Revision 0.15 1995/05/24 08:09:47 rpao
- * Re-implement MULTI_TX dev->tbusy handling.
- *
- * Revision 0.14 1995/05/23 03:19:30 rpao
- * Added, in nmclan_config(), "tuple.Attributes = 0;".
- * Modified MACE ID check to ignore chip revision level.
- * Avoid tx_free_frames race condition between _start_xmit and _interrupt.
- *
- * Revision 0.13 1995/05/18 05:56:34 rpao
- * Statistics changes.
- * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
- * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup.
- *
- * Revision 0.12 1995/05/14 00:12:23 rpao
- * Statistics overhaul.
- *
-
-95/05/13 rpao V0.10a
- Bug fix: MACE statistics counters used wrong I/O ports.
- Bug fix: mace_interrupt() needed to allow statistics to be
- processed without RX or TX interrupts pending.
-95/05/11 rpao V0.10
- Multiple transmit request processing.
- Modified statistics to use MACE counters where possible.
-95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
- *Released
-95/05/10 rpao V0.08
- Bug fix: Make all non-exported functions private by using
- static keyword.
- Bug fix: Test IntrCnt _before_ reading MACE_IR.
-95/05/10 rpao V0.07 Statistics.
-95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
-
----------------------------------------------------------------------------- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME "nmclan_cs"
-
-/* ----------------------------------------------------------------------------
-Conditional Compilation Options
----------------------------------------------------------------------------- */
-
-#define MULTI_TX 0
-#define RESET_ON_TIMEOUT 1
-#define TX_INTERRUPTABLE 1
-#define RESET_XILINX 0
-
-/* ----------------------------------------------------------------------------
-Include Files
----------------------------------------------------------------------------- */
-
-#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 <pcmcia/cisreg.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/* ----------------------------------------------------------------------------
-Defines
----------------------------------------------------------------------------- */
-
-#define MACE_LADRF_LEN 8
- /* 8 bytes in Logical Address Filter */
-
-/* Loop Control Defines */
-#define MACE_MAX_IR_ITERATIONS 10
-#define MACE_MAX_RX_ITERATIONS 12
- /*
- TBD: Dean brought this up, and I assumed the hardware would
- handle it:
-
- If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
- non-zero when the isr exits. We may not get another interrupt
- to process the remaining packets for some time.
- */
-
-/*
-The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
-which manages the interface between the MACE and the PCMCIA bus. It
-also includes buffer management for the 32K x 8 SRAM to control up to
-four transmit and 12 receive frames at a time.
-*/
-#define AM2150_MAX_TX_FRAMES 4
-#define AM2150_MAX_RX_FRAMES 12
-
-/* Am2150 Ethernet Card I/O Mapping */
-#define AM2150_RCV 0x00
-#define AM2150_XMT 0x04
-#define AM2150_XMT_SKIP 0x09
-#define AM2150_RCV_NEXT 0x0A
-#define AM2150_RCV_FRAME_COUNT 0x0B
-#define AM2150_MACE_BANK 0x0C
-#define AM2150_MACE_BASE 0x10
-
-/* MACE Registers */
-#define MACE_RCVFIFO 0
-#define MACE_XMTFIFO 1
-#define MACE_XMTFC 2
-#define MACE_XMTFS 3
-#define MACE_XMTRC 4
-#define MACE_RCVFC 5
-#define MACE_RCVFS 6
-#define MACE_FIFOFC 7
-#define MACE_IR 8
-#define MACE_IMR 9
-#define MACE_PR 10
-#define MACE_BIUCC 11
-#define MACE_FIFOCC 12
-#define MACE_MACCC 13
-#define MACE_PLSCC 14
-#define MACE_PHYCC 15
-#define MACE_CHIPIDL 16
-#define MACE_CHIPIDH 17
-#define MACE_IAC 18
-/* Reserved */
-#define MACE_LADRF 20
-#define MACE_PADR 21
-/* Reserved */
-/* Reserved */
-#define MACE_MPC 24
-/* Reserved */
-#define MACE_RNTPC 26
-#define MACE_RCVCC 27
-/* Reserved */
-#define MACE_UTR 29
-#define MACE_RTR1 30
-#define MACE_RTR2 31
-
-/* MACE Bit Masks */
-#define MACE_XMTRC_EXDEF 0x80
-#define MACE_XMTRC_XMTRC 0x0F
-
-#define MACE_XMTFS_XMTSV 0x80
-#define MACE_XMTFS_UFLO 0x40
-#define MACE_XMTFS_LCOL 0x20
-#define MACE_XMTFS_MORE 0x10
-#define MACE_XMTFS_ONE 0x08
-#define MACE_XMTFS_DEFER 0x04
-#define MACE_XMTFS_LCAR 0x02
-#define MACE_XMTFS_RTRY 0x01
-
-#define MACE_RCVFS_RCVSTS 0xF000
-#define MACE_RCVFS_OFLO 0x8000
-#define MACE_RCVFS_CLSN 0x4000
-#define MACE_RCVFS_FRAM 0x2000
-#define MACE_RCVFS_FCS 0x1000
-
-#define MACE_FIFOFC_RCVFC 0xF0
-#define MACE_FIFOFC_XMTFC 0x0F
-
-#define MACE_IR_JAB 0x80
-#define MACE_IR_BABL 0x40
-#define MACE_IR_CERR 0x20
-#define MACE_IR_RCVCCO 0x10
-#define MACE_IR_RNTPCO 0x08
-#define MACE_IR_MPCO 0x04
-#define MACE_IR_RCVINT 0x02
-#define MACE_IR_XMTINT 0x01
-
-#define MACE_MACCC_PROM 0x80
-#define MACE_MACCC_DXMT2PD 0x40
-#define MACE_MACCC_EMBA 0x20
-#define MACE_MACCC_RESERVED 0x10
-#define MACE_MACCC_DRCVPA 0x08
-#define MACE_MACCC_DRCVBC 0x04
-#define MACE_MACCC_ENXMT 0x02
-#define MACE_MACCC_ENRCV 0x01
-
-#define MACE_PHYCC_LNKFL 0x80
-#define MACE_PHYCC_DLNKTST 0x40
-#define MACE_PHYCC_REVPOL 0x20
-#define MACE_PHYCC_DAPC 0x10
-#define MACE_PHYCC_LRT 0x08
-#define MACE_PHYCC_ASEL 0x04
-#define MACE_PHYCC_RWAKE 0x02
-#define MACE_PHYCC_AWAKE 0x01
-
-#define MACE_IAC_ADDRCHG 0x80
-#define MACE_IAC_PHYADDR 0x04
-#define MACE_IAC_LOGADDR 0x02
-
-#define MACE_UTR_RTRE 0x80
-#define MACE_UTR_RTRD 0x40
-#define MACE_UTR_RPA 0x20
-#define MACE_UTR_FCOLL 0x10
-#define MACE_UTR_RCVFCSE 0x08
-#define MACE_UTR_LOOP_INCL_MENDEC 0x06
-#define MACE_UTR_LOOP_NO_MENDEC 0x04
-#define MACE_UTR_LOOP_EXTERNAL 0x02
-#define MACE_UTR_LOOP_NONE 0x00
-#define MACE_UTR_RESERVED 0x01
-
-/* Switch MACE register bank (only 0 and 1 are valid) */
-#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
-
-#define MACE_IMR_DEFAULT \
- (0xFF - \
- ( \
- MACE_IR_CERR | \
- MACE_IR_RCVCCO | \
- MACE_IR_RNTPCO | \
- MACE_IR_MPCO | \
- MACE_IR_RCVINT | \
- MACE_IR_XMTINT \
- ) \
- )
-#undef MACE_IMR_DEFAULT
-#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
-
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-/* ----------------------------------------------------------------------------
-Type Definitions
----------------------------------------------------------------------------- */
-
-typedef struct _mace_statistics {
- /* MACE_XMTFS */
- int xmtsv;
- int uflo;
- int lcol;
- int more;
- int one;
- int defer;
- int lcar;
- int rtry;
-
- /* MACE_XMTRC */
- int exdef;
- int xmtrc;
-
- /* RFS1--Receive Status (RCVSTS) */
- int oflo;
- int clsn;
- int fram;
- int fcs;
-
- /* RFS2--Runt Packet Count (RNTPC) */
- int rfs_rntpc;
-
- /* RFS3--Receive Collision Count (RCVCC) */
- int rfs_rcvcc;
-
- /* MACE_IR */
- int jab;
- int babl;
- int cerr;
- int rcvcco;
- int rntpco;
- int mpco;
-
- /* MACE_MPC */
- int mpc;
-
- /* MACE_RNTPC */
- int rntpc;
-
- /* MACE_RCVCC */
- int rcvcc;
-} mace_statistics;
-
-typedef struct _mace_private {
- struct pcmcia_device *p_dev;
- mace_statistics mace_stats; /* MACE chip statistics counters */
-
- /* restore_multicast_list() state variables */
- int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
- int multicast_num_addrs;
-
- char tx_free_frames; /* Number of free transmit frame buffers */
- char tx_irq_disabled; /* MACE TX interrupt disabled */
-
- spinlock_t bank_lock; /* Must be held if you step off bank 0 */
-} mace_private;
-
-/* ----------------------------------------------------------------------------
-Private Global Variables
----------------------------------------------------------------------------- */
-
-static const char *if_names[]={
- "Auto", "10baseT", "BNC",
-};
-
-/* ----------------------------------------------------------------------------
-Parameters
- These are the parameters that can be set during loading with
- 'insmod'.
----------------------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("New Media PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */
-INT_MODULE_PARM(if_port, 0);
-
-
-/* ----------------------------------------------------------------------------
-Function Prototypes
----------------------------------------------------------------------------- */
-
-static int nmclan_config(struct pcmcia_device *link);
-static void nmclan_release(struct pcmcia_device *link);
-
-static void nmclan_reset(struct net_device *dev);
-static int mace_config(struct net_device *dev, struct ifmap *map);
-static int mace_open(struct net_device *dev);
-static int mace_close(struct net_device *dev);
-static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static irqreturn_t mace_interrupt(int irq, void *dev_id);
-static struct net_device_stats *mace_get_stats(struct net_device *dev);
-static int mace_rx(struct net_device *dev, unsigned char RxCnt);
-static void restore_multicast_list(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-static void nmclan_detach(struct pcmcia_device *p_dev);
-
-static const struct net_device_ops mace_netdev_ops = {
- .ndo_open = mace_open,
- .ndo_stop = mace_close,
- .ndo_start_xmit = mace_start_xmit,
- .ndo_tx_timeout = mace_tx_timeout,
- .ndo_set_config = mace_config,
- .ndo_get_stats = mace_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int nmclan_probe(struct pcmcia_device *link)
-{
- mace_private *lp;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "nmclan_attach()\n");
-
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(mace_private));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- lp->p_dev = link;
- link->priv = dev;
-
- spin_lock_init(&lp->bank_lock);
- link->resource[0]->end = 32;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- link->config_flags |= CONF_ENABLE_IRQ;
- link->config_index = 1;
- link->config_regs = PRESENT_OPTION;
-
- lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
- dev->netdev_ops = &mace_netdev_ops;
- dev->ethtool_ops = &netdev_ethtool_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- return nmclan_config(link);
-} /* nmclan_attach */
-
-static void nmclan_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "nmclan_detach\n");
-
- unregister_netdev(dev);
-
- nmclan_release(link);
-
- free_netdev(dev);
-} /* nmclan_detach */
-
-/* ----------------------------------------------------------------------------
-mace_read
- Reads a MACE register. This is bank independent; however, the
- caller must ensure that this call is not interruptable. We are
- assuming that during normal operation, the MACE is always in
- bank 0.
----------------------------------------------------------------------------- */
-static int mace_read(mace_private *lp, unsigned int ioaddr, int reg)
-{
- int data = 0xFF;
- unsigned long flags;
-
- switch (reg >> 4) {
- case 0: /* register 0-15 */
- data = inb(ioaddr + AM2150_MACE_BASE + reg);
- break;
- case 1: /* register 16-31 */
- spin_lock_irqsave(&lp->bank_lock, flags);
- MACEBANK(1);
- data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
- MACEBANK(0);
- spin_unlock_irqrestore(&lp->bank_lock, flags);
- break;
- }
- return data & 0xFF;
-} /* mace_read */
-
-/* ----------------------------------------------------------------------------
-mace_write
- Writes to a MACE register. This is bank independent; however,
- the caller must ensure that this call is not interruptable. We
- are assuming that during normal operation, the MACE is always in
- bank 0.
----------------------------------------------------------------------------- */
-static void mace_write(mace_private *lp, unsigned int ioaddr, int reg,
- int data)
-{
- unsigned long flags;
-
- switch (reg >> 4) {
- case 0: /* register 0-15 */
- outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
- break;
- case 1: /* register 16-31 */
- spin_lock_irqsave(&lp->bank_lock, flags);
- MACEBANK(1);
- outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
- MACEBANK(0);
- spin_unlock_irqrestore(&lp->bank_lock, flags);
- break;
- }
-} /* mace_write */
-
-/* ----------------------------------------------------------------------------
-mace_init
- Resets the MACE chip.
----------------------------------------------------------------------------- */
-static int mace_init(mace_private *lp, unsigned int ioaddr,
- const char *enet_addr)
-{
- int i;
- int ct = 0;
-
- /* MACE Software reset */
- mace_write(lp, ioaddr, MACE_BIUCC, 1);
- while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
- /* Wait for reset bit to be cleared automatically after <= 200ns */;
- if(++ct > 500)
- {
- pr_err("reset failed, card removed?\n");
- return -1;
- }
- udelay(1);
- }
- mace_write(lp, ioaddr, MACE_BIUCC, 0);
-
- /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
- mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
-
- mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
- mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
-
- /*
- * Bit 2-1 PORTSEL[1-0] Port Select.
- * 00 AUI/10Base-2
- * 01 10Base-T
- * 10 DAI Port (reserved in Am2150)
- * 11 GPSI
- * For this card, only the first two are valid.
- * So, PLSCC should be set to
- * 0x00 for 10Base-2
- * 0x02 for 10Base-T
- * Or just set ASEL in PHYCC below!
- */
- switch (if_port) {
- case 1:
- mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
- break;
- case 2:
- mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
- break;
- default:
- mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
- /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden,
- and the MACE device will automatically select the operating media
- interface port. */
- break;
- }
-
- mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
- /* Poll ADDRCHG bit */
- ct = 0;
- while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
- {
- if(++ ct > 500)
- {
- pr_err("ADDRCHG timeout, card removed?\n");
- return -1;
- }
- }
- /* Set PADR register */
- for (i = 0; i < ETH_ALEN; i++)
- mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
-
- /* MAC Configuration Control Register should be written last */
- /* Let set_multicast_list set this. */
- /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
- mace_write(lp, ioaddr, MACE_MACCC, 0x00);
- return 0;
-} /* mace_init */
-
-static int nmclan_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- mace_private *lp = netdev_priv(dev);
- u8 *buf;
- size_t len;
- int i, ret;
- unsigned int ioaddr;
-
- dev_dbg(&link->dev, "nmclan_config\n");
-
- link->io_lines = 5;
- ret = pcmcia_request_io(link);
- if (ret)
- goto failed;
- ret = pcmcia_request_irq(link, mace_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;
-
- /* Read the ethernet address from the CIS. */
- len = pcmcia_get_tuple(link, 0x80, &buf);
- if (!buf || len < ETH_ALEN) {
- kfree(buf);
- goto failed;
- }
- eth_hw_addr_set(dev, buf);
- kfree(buf);
-
- /* Verify configuration by reading the MACE ID. */
- {
- char sig[2];
-
- sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
- sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
- if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
- dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n",
- sig[0], sig[1]);
- } else {
- pr_notice("mace id not found: %x %x should be 0x40 0x?9\n",
- sig[0], sig[1]);
- goto failed;
- }
- }
-
- if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
- goto failed;
-
- /* The if_port symbol can be set when the module is loaded */
- if (if_port <= 2)
- dev->if_port = if_port;
- else
- pr_notice("invalid if_port requested\n");
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- i = register_netdev(dev);
- if (i != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n",
- dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr);
- return 0;
-
-failed:
- nmclan_release(link);
- return -ENODEV;
-} /* nmclan_config */
-
-static void nmclan_release(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "nmclan_release\n");
- pcmcia_disable_device(link);
-}
-
-static int nmclan_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int nmclan_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- nmclan_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/* ----------------------------------------------------------------------------
-nmclan_reset
- Reset and restore all of the Xilinx and MACE registers.
----------------------------------------------------------------------------- */
-static void nmclan_reset(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
-#if RESET_XILINX
- struct pcmcia_device *link = &lp->link;
- u8 OrigCorValue;
-
- /* Save original COR value */
- pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue);
-
- /* Reset Xilinx */
- dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n",
- OrigCorValue);
- pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET);
- /* Need to wait for 20 ms for PCMCIA to finish reset. */
-
- /* Restore original COR configuration index */
- pcmcia_write_config_byte(link, CISREG_COR,
- (COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK)));
- /* Xilinx is now completely reset along with the MACE chip. */
- lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
-#endif /* #if RESET_XILINX */
-
- /* Xilinx is now completely reset along with the MACE chip. */
- lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
- /* Reinitialize the MACE chip for operation. */
- mace_init(lp, dev->base_addr, dev->dev_addr);
- mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
-
- /* Restore the multicast list and enable TX and RX. */
- restore_multicast_list(dev);
-} /* nmclan_reset */
-
-/* ----------------------------------------------------------------------------
-mace_config
- [Someone tell me what this is supposed to do? Is if_port a defined
- standard? If so, there should be defines to indicate 1=10Base-T,
- 2=10Base-2, etc. including limited automatic detection.]
----------------------------------------------------------------------------- */
-static int mace_config(struct net_device *dev, struct ifmap *map)
-{
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (map->port <= 2) {
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- } else
- return -EINVAL;
- }
- return 0;
-} /* mace_config */
-
-/* ----------------------------------------------------------------------------
-mace_open
- Open device driver.
----------------------------------------------------------------------------- */
-static int mace_open(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- mace_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- link->open++;
-
- MACEBANK(0);
-
- netif_start_queue(dev);
- nmclan_reset(dev);
-
- return 0; /* Always succeed */
-} /* mace_open */
-
-/* ----------------------------------------------------------------------------
-mace_close
- Closes device driver.
----------------------------------------------------------------------------- */
-static int mace_close(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- mace_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
- /* Mask off all interrupts from the MACE chip. */
- outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
-
- link->open--;
- netif_stop_queue(dev);
-
- return 0;
-} /* mace_close */
-
-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,
-};
-
-/* ----------------------------------------------------------------------------
-mace_start_xmit
- This routine begins the packet transmit function. When completed,
- it will generate a transmit interrupt.
-
- According to /usr/src/linux/net/inet/dev.c, if _start_xmit
- returns 0, the "packet is now solely the responsibility of the
- driver." If _start_xmit returns non-zero, the "transmission
- failed, put skb back into a list."
----------------------------------------------------------------------------- */
-
-static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- mace_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- netdev_notice(dev, "transmit timed out -- ");
-#if RESET_ON_TIMEOUT
- pr_cont("resetting card\n");
- pcmcia_reset_card(link->socket);
-#else /* #if RESET_ON_TIMEOUT */
- pr_cont("NOT resetting card\n");
-#endif /* #if RESET_ON_TIMEOUT */
- netif_trans_update(dev); /* prevent tx timeout */
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netif_stop_queue(dev);
-
- pr_debug("%s: mace_start_xmit(length = %ld) called.\n",
- dev->name, (long)skb->len);
-
-#if (!TX_INTERRUPTABLE)
- /* Disable MACE TX interrupts. */
- outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
- ioaddr + AM2150_MACE_BASE + MACE_IMR);
- lp->tx_irq_disabled=1;
-#endif /* #if (!TX_INTERRUPTABLE) */
-
- {
- /* This block must not be interrupted by another transmit request!
- mace_tx_timeout will take care of timer-based retransmissions from
- the upper layers. The interrupt handler is guaranteed never to
- service a transmit interrupt while we are in here.
- */
-
- dev->stats.tx_bytes += skb->len;
- lp->tx_free_frames--;
-
- /* WARNING: Write the _exact_ number of bytes written in the header! */
- /* Put out the word header [must be an outw()] . . . */
- outw(skb->len, ioaddr + AM2150_XMT);
- /* . . . and the packet [may be any combination of outw() and outb()] */
- outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
- if (skb->len & 1) {
- /* Odd byte transfer */
- outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
- }
-
-#if MULTI_TX
- if (lp->tx_free_frames > 0)
- netif_start_queue(dev);
-#endif /* #if MULTI_TX */
- }
-
-#if (!TX_INTERRUPTABLE)
- /* Re-enable MACE TX interrupts. */
- lp->tx_irq_disabled=0;
- outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
-#endif /* #if (!TX_INTERRUPTABLE) */
-
- dev_kfree_skb(skb);
-
- return NETDEV_TX_OK;
-} /* mace_start_xmit */
-
-/* ----------------------------------------------------------------------------
-mace_interrupt
- The interrupt handler.
----------------------------------------------------------------------------- */
-static irqreturn_t mace_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = (struct net_device *) dev_id;
- mace_private *lp = netdev_priv(dev);
- unsigned int ioaddr;
- int status;
- int IntrCnt = MACE_MAX_IR_ITERATIONS;
-
- if (!dev) {
- pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n",
- irq);
- return IRQ_NONE;
- }
-
- ioaddr = dev->base_addr;
-
- if (lp->tx_irq_disabled) {
- const char *msg;
- if (lp->tx_irq_disabled)
- msg = "Interrupt with tx_irq_disabled";
- else
- msg = "Re-entering the interrupt handler";
- netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n",
- msg,
- inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
- inb(ioaddr + AM2150_MACE_BASE + MACE_IMR));
- /* WARNING: MACE_IR has been read! */
- return IRQ_NONE;
- }
-
- if (!netif_device_present(dev)) {
- netdev_dbg(dev, "interrupt from dead card\n");
- return IRQ_NONE;
- }
-
- do {
- /* WARNING: MACE_IR is a READ/CLEAR port! */
- status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
- if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS)
- return IRQ_NONE;
-
- pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
-
- if (status & MACE_IR_RCVINT) {
- mace_rx(dev, MACE_MAX_RX_ITERATIONS);
- }
-
- if (status & MACE_IR_XMTINT) {
- unsigned char fifofc;
- unsigned char xmtrc;
- unsigned char xmtfs;
-
- fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
- if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
- dev->stats.tx_errors++;
- outb(0xFF, ioaddr + AM2150_XMT_SKIP);
- }
-
- /* Transmit Retry Count (XMTRC, reg 4) */
- xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
- if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
- lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
-
- if (
- (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
- MACE_XMTFS_XMTSV /* Transmit Status Valid */
- ) {
- lp->mace_stats.xmtsv++;
-
- if (xmtfs & ~MACE_XMTFS_XMTSV) {
- if (xmtfs & MACE_XMTFS_UFLO) {
- /* Underflow. Indicates that the Transmit FIFO emptied before
- the end of frame was reached. */
- lp->mace_stats.uflo++;
- }
- if (xmtfs & MACE_XMTFS_LCOL) {
- /* Late Collision */
- lp->mace_stats.lcol++;
- }
- if (xmtfs & MACE_XMTFS_MORE) {
- /* MORE than one retry was needed */
- lp->mace_stats.more++;
- }
- if (xmtfs & MACE_XMTFS_ONE) {
- /* Exactly ONE retry occurred */
- lp->mace_stats.one++;
- }
- if (xmtfs & MACE_XMTFS_DEFER) {
- /* Transmission was defered */
- lp->mace_stats.defer++;
- }
- if (xmtfs & MACE_XMTFS_LCAR) {
- /* Loss of carrier */
- lp->mace_stats.lcar++;
- }
- if (xmtfs & MACE_XMTFS_RTRY) {
- /* Retry error: transmit aborted after 16 attempts */
- lp->mace_stats.rtry++;
- }
- } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
-
- } /* if (xmtfs & MACE_XMTFS_XMTSV) */
-
- dev->stats.tx_packets++;
- lp->tx_free_frames++;
- netif_wake_queue(dev);
- } /* if (status & MACE_IR_XMTINT) */
-
- if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
- if (status & MACE_IR_JAB) {
- /* Jabber Error. Excessive transmit duration (20-150ms). */
- lp->mace_stats.jab++;
- }
- if (status & MACE_IR_BABL) {
- /* Babble Error. >1518 bytes transmitted. */
- lp->mace_stats.babl++;
- }
- if (status & MACE_IR_CERR) {
- /* Collision Error. CERR indicates the absence of the
- Signal Quality Error Test message after a packet
- transmission. */
- lp->mace_stats.cerr++;
- }
- if (status & MACE_IR_RCVCCO) {
- /* Receive Collision Count Overflow; */
- lp->mace_stats.rcvcco++;
- }
- if (status & MACE_IR_RNTPCO) {
- /* Runt Packet Count Overflow */
- lp->mace_stats.rntpco++;
- }
- if (status & MACE_IR_MPCO) {
- /* Missed Packet Count Overflow */
- lp->mace_stats.mpco++;
- }
- } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
-
- } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
-
- return IRQ_HANDLED;
-} /* mace_interrupt */
-
-/* ----------------------------------------------------------------------------
-mace_rx
- Receives packets.
----------------------------------------------------------------------------- */
-static int mace_rx(struct net_device *dev, unsigned char RxCnt)
-{
- mace_private *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- unsigned char rx_framecnt;
- unsigned short rx_status;
-
- while (
- ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
- (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
- (RxCnt--)
- ) {
- rx_status = inw(ioaddr + AM2150_RCV);
-
- pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status"
- " 0x%X.\n", dev->name, rx_framecnt, rx_status);
-
- if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
- dev->stats.rx_errors++;
- if (rx_status & MACE_RCVFS_OFLO) {
- lp->mace_stats.oflo++;
- }
- if (rx_status & MACE_RCVFS_CLSN) {
- lp->mace_stats.clsn++;
- }
- if (rx_status & MACE_RCVFS_FRAM) {
- lp->mace_stats.fram++;
- }
- if (rx_status & MACE_RCVFS_FCS) {
- lp->mace_stats.fcs++;
- }
- } else {
- short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
- /* Auto Strip is off, always subtract 4 */
- struct sk_buff *skb;
-
- lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
- /* runt packet count */
- lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
- /* rcv collision count */
-
- pr_debug(" receiving packet size 0x%X rx_status"
- " 0x%X.\n", pkt_len, rx_status);
-
- skb = netdev_alloc_skb(dev, pkt_len + 2);
-
- if (skb) {
- skb_reserve(skb, 2);
- insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
- if (pkt_len & 1)
- *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV);
- skb->protocol = eth_type_trans(skb, dev);
-
- netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
-
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
- continue;
- } else {
- pr_debug("%s: couldn't allocate a sk_buff of size"
- " %d.\n", dev->name, pkt_len);
- dev->stats.rx_dropped++;
- }
- }
- outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
- } /* while */
-
- return 0;
-} /* mace_rx */
-
-/* ----------------------------------------------------------------------------
-pr_linux_stats
----------------------------------------------------------------------------- */
-static void pr_linux_stats(struct net_device_stats *pstats)
-{
- pr_debug("pr_linux_stats\n");
- pr_debug(" rx_packets=%-7ld tx_packets=%ld\n",
- (long)pstats->rx_packets, (long)pstats->tx_packets);
- pr_debug(" rx_errors=%-7ld tx_errors=%ld\n",
- (long)pstats->rx_errors, (long)pstats->tx_errors);
- pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n",
- (long)pstats->rx_dropped, (long)pstats->tx_dropped);
- pr_debug(" multicast=%-7ld collisions=%ld\n",
- (long)pstats->multicast, (long)pstats->collisions);
-
- pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n",
- (long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
- pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n",
- (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
- pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n",
- (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
-
- pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
- (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
- pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n",
- (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
- pr_debug(" tx_window_errors=%ld\n",
- (long)pstats->tx_window_errors);
-} /* pr_linux_stats */
-
-/* ----------------------------------------------------------------------------
-pr_mace_stats
----------------------------------------------------------------------------- */
-static void pr_mace_stats(mace_statistics *pstats)
-{
- pr_debug("pr_mace_stats\n");
-
- pr_debug(" xmtsv=%-7d uflo=%d\n",
- pstats->xmtsv, pstats->uflo);
- pr_debug(" lcol=%-7d more=%d\n",
- pstats->lcol, pstats->more);
- pr_debug(" one=%-7d defer=%d\n",
- pstats->one, pstats->defer);
- pr_debug(" lcar=%-7d rtry=%d\n",
- pstats->lcar, pstats->rtry);
-
- /* MACE_XMTRC */
- pr_debug(" exdef=%-7d xmtrc=%d\n",
- pstats->exdef, pstats->xmtrc);
-
- /* RFS1--Receive Status (RCVSTS) */
- pr_debug(" oflo=%-7d clsn=%d\n",
- pstats->oflo, pstats->clsn);
- pr_debug(" fram=%-7d fcs=%d\n",
- pstats->fram, pstats->fcs);
-
- /* RFS2--Runt Packet Count (RNTPC) */
- /* RFS3--Receive Collision Count (RCVCC) */
- pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n",
- pstats->rfs_rntpc, pstats->rfs_rcvcc);
-
- /* MACE_IR */
- pr_debug(" jab=%-7d babl=%d\n",
- pstats->jab, pstats->babl);
- pr_debug(" cerr=%-7d rcvcco=%d\n",
- pstats->cerr, pstats->rcvcco);
- pr_debug(" rntpco=%-7d mpco=%d\n",
- pstats->rntpco, pstats->mpco);
-
- /* MACE_MPC */
- pr_debug(" mpc=%d\n", pstats->mpc);
-
- /* MACE_RNTPC */
- pr_debug(" rntpc=%d\n", pstats->rntpc);
-
- /* MACE_RCVCC */
- pr_debug(" rcvcc=%d\n", pstats->rcvcc);
-
-} /* pr_mace_stats */
-
-/* ----------------------------------------------------------------------------
-update_stats
- Update statistics. We change to register window 1, 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 0 is always valid rather
- than use a special window-state variable.
-
- oflo & uflo should _never_ occur since it would mean the Xilinx
- was not able to transfer data between the MACE FIFO and the
- card's SRAM fast enough. If this happens, something is
- seriously wrong with the hardware.
----------------------------------------------------------------------------- */
-static void update_stats(unsigned int ioaddr, struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
- lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
- lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
- lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
- /* At this point, mace_stats is fully updated for this call.
- We may now update the netdev stats. */
-
- /* The MACE has no equivalent for netdev stats field which are commented
- out. */
-
- /* dev->stats.multicast; */
- dev->stats.collisions =
- lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
- /* Collision: The MACE may retry sending a packet 15 times
- before giving up. The retry count is in XMTRC.
- Does each retry constitute a collision?
- If so, why doesn't the RCVCC record these collisions? */
-
- /* detailed rx_errors: */
- dev->stats.rx_length_errors =
- lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
- /* dev->stats.rx_over_errors */
- dev->stats.rx_crc_errors = lp->mace_stats.fcs;
- dev->stats.rx_frame_errors = lp->mace_stats.fram;
- dev->stats.rx_fifo_errors = lp->mace_stats.oflo;
- dev->stats.rx_missed_errors =
- lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
-
- /* detailed tx_errors */
- dev->stats.tx_aborted_errors = lp->mace_stats.rtry;
- dev->stats.tx_carrier_errors = lp->mace_stats.lcar;
- /* LCAR usually results from bad cabling. */
- dev->stats.tx_fifo_errors = lp->mace_stats.uflo;
- dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr;
- /* dev->stats.tx_window_errors; */
-} /* update_stats */
-
-/* ----------------------------------------------------------------------------
-mace_get_stats
- Gathers ethernet statistics from the MACE chip.
----------------------------------------------------------------------------- */
-static struct net_device_stats *mace_get_stats(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
- update_stats(dev->base_addr, dev);
-
- pr_debug("%s: updating the statistics.\n", dev->name);
- pr_linux_stats(&dev->stats);
- pr_mace_stats(&lp->mace_stats);
-
- return &dev->stats;
-} /* net_device_stats */
-
-/* ----------------------------------------------------------------------------
-updateCRC
- Modified from Am79C90 data sheet.
----------------------------------------------------------------------------- */
-
-#ifdef BROKEN_MULTICAST
-
-static void updateCRC(int *CRC, int bit)
-{
- static const int poly[]={
- 1,1,1,0, 1,1,0,1,
- 1,0,1,1, 1,0,0,0,
- 1,0,0,0, 0,0,1,1,
- 0,0,1,0, 0,0,0,0
- }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the
- CRC generator polynomial. */
-
- int j;
-
- /* shift CRC and control bit (CRC[32]) */
- for (j = 32; j > 0; j--)
- CRC[j] = CRC[j-1];
- CRC[0] = 0;
-
- /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
- if (bit ^ CRC[32])
- for (j = 0; j < 32; j++)
- CRC[j] ^= poly[j];
-} /* updateCRC */
-
-/* ----------------------------------------------------------------------------
-BuildLAF
- Build logical address filter.
- Modified from Am79C90 data sheet.
-
-Input
- ladrf: logical address filter (contents initialized to 0)
- adr: ethernet address
----------------------------------------------------------------------------- */
-static void BuildLAF(int *ladrf, int *adr)
-{
- int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
-
- int i, byte; /* temporary array indices */
- int hashcode; /* the output object */
-
- CRC[32]=0;
-
- for (byte = 0; byte < 6; byte++)
- for (i = 0; i < 8; i++)
- updateCRC(CRC, (adr[byte] >> i) & 1);
-
- hashcode = 0;
- for (i = 0; i < 6; i++)
- hashcode = (hashcode << 1) + CRC[i];
-
- byte = hashcode >> 3;
- ladrf[byte] |= (1 << (hashcode & 7));
-
-#ifdef PCMCIA_DEBUG
- if (0)
- printk(KERN_DEBUG " adr =%pM\n", adr);
- printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode);
- for (i = 0; i < 8; i++)
- pr_cont(" %02X", ladrf[i]);
- pr_cont("\n");
-#endif
-} /* BuildLAF */
-
-/* ----------------------------------------------------------------------------
-restore_multicast_list
- Restores the multicast filter for MACE chip to the last
- set_multicast_list() call.
-
-Input
- multicast_num_addrs
- multicast_ladrf[]
----------------------------------------------------------------------------- */
-static void restore_multicast_list(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
- int num_addrs = lp->multicast_num_addrs;
- int *ladrf = lp->multicast_ladrf;
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- pr_debug("%s: restoring Rx mode to %d addresses.\n",
- dev->name, num_addrs);
-
- if (num_addrs > 0) {
-
- pr_debug("Attempt to restore multicast list detected.\n");
-
- mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
- /* Poll ADDRCHG bit */
- while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
- ;
- /* Set LADRF register */
- for (i = 0; i < MACE_LADRF_LEN; i++)
- mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
-
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-
- } else if (num_addrs < 0) {
-
- /* Promiscuous mode: receive all packets */
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC,
- MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
- );
-
- } else {
-
- /* Normal mode */
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-
- }
-} /* restore_multicast_list */
-
-/* ----------------------------------------------------------------------------
-set_multicast_list
- Set or clear the multicast filter for this adaptor.
-
-Input
- num_addrs == -1 Promiscuous mode, receive all packets
- num_addrs == 0 Normal mode, clear multicast list
- num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- best-effort filtering.
-Output
- multicast_num_addrs
- multicast_ladrf[]
----------------------------------------------------------------------------- */
-
-static void set_multicast_list(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
- int adr[ETH_ALEN] = {0}; /* Ethernet address */
- struct netdev_hw_addr *ha;
-
-#ifdef PCMCIA_DEBUG
- {
- static int old;
- if (netdev_mc_count(dev) != old) {
- old = netdev_mc_count(dev);
- pr_debug("%s: setting Rx mode to %d addresses.\n",
- dev->name, old);
- }
- }
-#endif
-
- /* Set multicast_num_addrs. */
- lp->multicast_num_addrs = netdev_mc_count(dev);
-
- /* Set multicast_ladrf. */
- if (num_addrs > 0) {
- /* Calculate multicast logical address filter */
- memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
- netdev_for_each_mc_addr(ha, dev) {
- memcpy(adr, ha->addr, ETH_ALEN);
- BuildLAF(lp->multicast_ladrf, adr);
- }
- }
-
- restore_multicast_list(dev);
-
-} /* set_multicast_list */
-
-#endif /* BROKEN_MULTICAST */
-
-static void restore_multicast_list(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- mace_private *lp = netdev_priv(dev);
-
- pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name,
- lp->multicast_num_addrs);
-
- if (dev->flags & IFF_PROMISC) {
- /* Promiscuous mode: receive all packets */
- mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC,
- MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
- );
- } else {
- /* Normal mode */
- mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
- mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
- }
-} /* restore_multicast_list */
-
-static void set_multicast_list(struct net_device *dev)
-{
- mace_private *lp = netdev_priv(dev);
-
-#ifdef PCMCIA_DEBUG
- {
- static int old;
- if (netdev_mc_count(dev) != old) {
- old = netdev_mc_count(dev);
- pr_debug("%s: setting Rx mode to %d addresses.\n",
- dev->name, old);
- }
- }
-#endif
-
- lp->multicast_num_addrs = netdev_mc_count(dev);
- restore_multicast_list(dev);
-
-} /* set_multicast_list */
-
-static const struct pcmcia_device_id nmclan_ids[] = {
- PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, nmclan_ids);
-
-static struct pcmcia_driver nmclan_cs_driver = {
- .owner = THIS_MODULE,
- .name = "nmclan_cs",
- .probe = nmclan_probe,
- .remove = nmclan_detach,
- .id_table = nmclan_ids,
- .suspend = nmclan_suspend,
- .resume = nmclan_resume,
-};
-module_pcmcia_driver(nmclan_cs_driver);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 07/15] drivers: net: smsc: smc9194: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (5 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 06/15] drivers: net: amd: nmclan: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 08/15] drivers: net: smsc: smc91c92: " Andrew Lunn
` (7 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The smc9194 was written by Erik Stahlman in 1996. It is an ISA device,
so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/smsc/Kconfig | 15 -
drivers/net/ethernet/smsc/Makefile | 1 -
drivers/net/ethernet/smsc/smc9194.c | 1535 -----------------------------------
3 files changed, 1551 deletions(-)
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index 13ce9086a9ca..d25bbcc98854 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -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
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
index 1501fa364c13..afea0b94c2a4 100644
--- a/drivers/net/ethernet/smsc/Makefile
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -3,7 +3,6 @@
# 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
diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c
deleted file mode 100644
index e2e7b1c68563..000000000000
--- a/drivers/net/ethernet/smsc/smc9194.c
+++ /dev/null
@@ -1,1535 +0,0 @@
-/*------------------------------------------------------------------------
- . smc9194.c
- . This is a driver for SMC's 9000 series of Ethernet cards.
- .
- . Copyright (C) 1996 by Erik Stahlman
- . This software may be used and distributed according to the terms
- . of the GNU General Public License, incorporated herein by reference.
- .
- . "Features" of the SMC chip:
- . 4608 byte packet memory. ( for the 91C92. Others have more )
- . EEPROM for configuration
- . AUI/TP selection ( mine has 10Base2/10BaseT select )
- .
- . Arguments:
- . io = for the base address
- . irq = for the IRQ
- . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )
- .
- . author:
- . Erik Stahlman ( erik@vt.edu )
- . contributors:
- . Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- .
- . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
- .
- . Sources:
- . o SMC databook
- . o skeleton.c by Donald Becker ( becker@scyld.com )
- . o ( a LOT of advice from Becker as well )
- .
- . History:
- . 12/07/95 Erik Stahlman written, got receive/xmit handled
- . 01/03/96 Erik Stahlman worked out some bugs, actually usable!!! :-)
- . 01/06/96 Erik Stahlman cleaned up some, better testing, etc
- . 01/29/96 Erik Stahlman fixed autoirq, added multicast
- . 02/01/96 Erik Stahlman 1. disabled all interrupts in smc_reset
- . 2. got rid of post-decrementing bug -- UGH.
- . 02/13/96 Erik Stahlman Tried to fix autoirq failure. Added more
- . descriptive error messages.
- . 02/15/96 Erik Stahlman Fixed typo that caused detection failure
- . 02/23/96 Erik Stahlman Modified it to fit into kernel tree
- . Added support to change hardware address
- . Cleared stats on opens
- . 02/26/96 Erik Stahlman Trial support for Kernel 1.2.13
- . Kludge for automatic IRQ detection
- . 03/04/96 Erik Stahlman Fixed kernel 1.3.70 +
- . Fixed bug reported by Gardner Buchanan in
- . smc_enable, with outw instead of outb
- . 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert
- . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory
- . allocation
- . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet
- . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ"
- . 11/08/01 Matt Domsch Use common crc32 function
- ----------------------------------------------------------------------------*/
-
-static const char version[] =
- "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)";
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/crc32.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/bitops.h>
-
-#include <asm/io.h>
-
-#include "smc9194.h"
-
-#define DRV_NAME "smc9194"
-
-/*------------------------------------------------------------------------
- .
- . Configuration options, for the experienced user to change.
- .
- -------------------------------------------------------------------------*/
-
-/*
- . Do you want to use 32 bit xfers? This should work on all chips, as
- . the chipset is designed to accommodate them.
-*/
-#ifdef __sh__
-#undef USE_32_BIT
-#else
-#define USE_32_BIT 1
-#endif
-
-/*
- .the SMC9194 can be at any of the following port addresses. To change,
- .for a slightly different card, you can add it to the array. Keep in
- .mind that the array must end in zero.
-*/
-
-struct devlist {
- unsigned int port;
- unsigned int irq;
-};
-
-static struct devlist smc_devlist[] __initdata = {
- {.port = 0x200, .irq = 0},
- {.port = 0x220, .irq = 0},
- {.port = 0x240, .irq = 0},
- {.port = 0x260, .irq = 0},
- {.port = 0x280, .irq = 0},
- {.port = 0x2A0, .irq = 0},
- {.port = 0x2C0, .irq = 0},
- {.port = 0x2E0, .irq = 0},
- {.port = 0x300, .irq = 0},
- {.port = 0x320, .irq = 0},
- {.port = 0x340, .irq = 0},
- {.port = 0x360, .irq = 0},
- {.port = 0x380, .irq = 0},
- {.port = 0x3A0, .irq = 0},
- {.port = 0x3C0, .irq = 0},
- {.port = 0x3E0, .irq = 0},
- {.port = 0, .irq = 0},
-};
-/*
- . Wait time for memory to be free. This probably shouldn't be
- . tuned that much, as waiting for this means nothing else happens
- . in the system
-*/
-#define MEMORY_WAIT_TIME 16
-
-/*
- . DEBUGGING LEVELS
- .
- . 0 for normal operation
- . 1 for slightly more details
- . >2 for various levels of increasingly useless information
- . 2 for interrupt tracking, status flags
- . 3 for packet dumps, etc.
-*/
-#define SMC_DEBUG 0
-
-#if (SMC_DEBUG > 2 )
-#define PRINTK3(x) printk x
-#else
-#define PRINTK3(x)
-#endif
-
-#if SMC_DEBUG > 1
-#define PRINTK2(x) printk x
-#else
-#define PRINTK2(x)
-#endif
-
-#ifdef SMC_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x)
-#endif
-
-
-/*------------------------------------------------------------------------
- .
- . The internal workings of the driver. If you are changing anything
- . here with the SMC stuff, you should have the datasheet and known
- . what you are doing.
- .
- -------------------------------------------------------------------------*/
-#define CARDNAME "SMC9194"
-
-
-/* store this information for the driver.. */
-struct smc_local {
- /*
- If I have to wait until memory is available to send
- a packet, I will store the skbuff here, until I get the
- desired memory. Then, I'll send it out and free it.
- */
- struct sk_buff * saved_skb;
-
- /*
- . This keeps track of how many packets that I have
- . sent out. When an TX_EMPTY interrupt comes, I know
- . that all of these have been sent.
- */
- int packets_waiting;
-};
-
-
-/*-----------------------------------------------------------------
- .
- . The driver can be entered at any of the following entry points.
- .
- .------------------------------------------------------------------ */
-
-/*
- . This is called by register_netdev(). It is responsible for
- . checking the portlist for the SMC9000 series chipset. If it finds
- . one, then it will initialize the device, find the hardware information,
- . and sets up the appropriate device parameters.
- . NOTE: Interrupts are *OFF* when this procedure is called.
- .
- . NB:This shouldn't be static since it is referred to externally.
-*/
-struct net_device *smc_init(int unit);
-
-/*
- . The kernel calls this function when someone wants to use the device,
- . typically 'ifconfig ethX up'.
-*/
-static int smc_open(struct net_device *dev);
-
-/*
- . Our watchdog timed out. Called by the networking layer
-*/
-static void smc_timeout(struct net_device *dev, unsigned int txqueue);
-
-/*
- . This is called by the kernel in response to 'ifconfig ethX down'. It
- . is responsible for cleaning up everything that the open routine
- . does, and maybe putting the card into a powerdown state.
-*/
-static int smc_close(struct net_device *dev);
-
-/*
- . Finally, a call to set promiscuous mode ( for TCPDUMP and related
- . programs ) and multicast modes.
-*/
-static void smc_set_multicast_list(struct net_device *dev);
-
-
-/*---------------------------------------------------------------
- .
- . Interrupt level calls..
- .
- ----------------------------------------------------------------*/
-
-/*
- . Handles the actual interrupt
-*/
-static irqreturn_t smc_interrupt(int irq, void *);
-/*
- . This is a separate procedure to handle the receipt of a packet, to
- . leave the interrupt code looking slightly cleaner
-*/
-static inline void smc_rcv( struct net_device *dev );
-/*
- . This handles a TX interrupt, which is only called when an error
- . relating to a packet is sent.
-*/
-static inline void smc_tx( struct net_device * dev );
-
-/*
- ------------------------------------------------------------
- .
- . Internal routines
- .
- ------------------------------------------------------------
-*/
-
-/*
- . Test if a given location contains a chip, trying to cause as
- . little damage as possible if it's not a SMC chip.
-*/
-static int smc_probe(struct net_device *dev, int ioaddr);
-
-/*
- . A rather simple routine to print out a packet for debugging purposes.
-*/
-#if SMC_DEBUG > 2
-static void print_packet( byte *, int );
-#endif
-
-#define tx_done(dev) 1
-
-/* this is called to actually send the packet to the chip */
-static void smc_hardware_send_packet( struct net_device * dev );
-
-/* Since I am not sure if I will have enough room in the chip's ram
- . to store the packet, I call this routine, which either sends it
- . now, or generates an interrupt when the card is ready for the
- . packet */
-static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb,
- struct net_device *dev );
-
-/* this does a soft reset on the device */
-static void smc_reset( int ioaddr );
-
-/* Enable Interrupts, Receive, and Transmit */
-static void smc_enable( int ioaddr );
-
-/* this puts the device in an inactive state */
-static void smc_shutdown( int ioaddr );
-
-/* This routine will find the IRQ of the driver if one is not
- . specified in the input to the device. */
-static int smc_findirq( int ioaddr );
-
-/*
- . Function: smc_reset( int ioaddr )
- . Purpose:
- . This sets the SMC91xx chip to its normal state, hopefully from whatever
- . mess that any other DOS driver has put it in.
- .
- . Maybe I should reset more registers to defaults in here? SOFTRESET should
- . do that for me.
- .
- . Method:
- . 1. send a SOFT RESET
- . 2. wait for it to finish
- . 3. enable autorelease mode
- . 4. reset the memory management unit
- . 5. clear all interrupts
- .
-*/
-static void smc_reset( int ioaddr )
-{
- /* This resets the registers mostly to defaults, but doesn't
- affect EEPROM. That seems unnecessary */
- SMC_SELECT_BANK( 0 );
- outw( RCR_SOFTRESET, ioaddr + RCR );
-
- /* this should pause enough for the chip to be happy */
- SMC_DELAY( );
-
- /* Set the transmit and receive configuration registers to
- default values */
- outw( RCR_CLEAR, ioaddr + RCR );
- outw( TCR_CLEAR, ioaddr + TCR );
-
- /* set the control register to automatically
- release successfully transmitted packets, to make the best
- use out of our limited memory */
- SMC_SELECT_BANK( 1 );
- outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
-
- /* Reset the MMU */
- SMC_SELECT_BANK( 2 );
- outw( MC_RESET, ioaddr + MMU_CMD );
-
- /* Note: It doesn't seem that waiting for the MMU busy is needed here,
- but this is a place where future chipsets _COULD_ break. Be wary
- of issuing another MMU command right after this */
-
- outb( 0, ioaddr + INT_MASK );
-}
-
-/*
- . Function: smc_enable
- . Purpose: let the chip talk to the outside work
- . Method:
- . 1. Enable the transmitter
- . 2. Enable the receiver
- . 3. Enable interrupts
-*/
-static void smc_enable( int ioaddr )
-{
- SMC_SELECT_BANK( 0 );
- /* see the header file for options in TCR/RCR NORMAL*/
- outw( TCR_NORMAL, ioaddr + TCR );
- outw( RCR_NORMAL, ioaddr + RCR );
-
- /* now, enable interrupts */
- SMC_SELECT_BANK( 2 );
- outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK );
-}
-
-/*
- . Function: smc_shutdown
- . Purpose: closes down the SMC91xxx chip.
- . Method:
- . 1. zero the interrupt mask
- . 2. clear the enable receive flag
- . 3. clear the enable xmit flags
- .
- . TODO:
- . (1) maybe utilize power down mode.
- . Why not yet? Because while the chip will go into power down mode,
- . the manual says that it will wake up in response to any I/O requests
- . in the register space. Empirical results do not show this working.
-*/
-static void smc_shutdown( int ioaddr )
-{
- /* no more interrupts for me */
- SMC_SELECT_BANK( 2 );
- outb( 0, ioaddr + INT_MASK );
-
- /* and tell the card to stay away from that nasty outside world */
- SMC_SELECT_BANK( 0 );
- outb( RCR_CLEAR, ioaddr + RCR );
- outb( TCR_CLEAR, ioaddr + TCR );
-#if 0
- /* finally, shut the chip down */
- SMC_SELECT_BANK( 1 );
- outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL );
-#endif
-}
-
-
-/*
- . Function: smc_setmulticast( int ioaddr, struct net_device *dev )
- . Purpose:
- . This sets the internal hardware table to filter out unwanted multicast
- . packets before they take up memory.
- .
- . The SMC chip uses a hash table where the high 6 bits of the CRC of
- . address are the offset into the table. If that bit is 1, then the
- . multicast packet is accepted. Otherwise, it's dropped silently.
- .
- . To use the 6 bits as an offset into the table, the high 3 bits are the
- . number of the 8 bit register, while the low 3 bits are the bit within
- . that register.
- .
- . This routine is based very heavily on the one provided by Peter Cammaert.
-*/
-
-
-static void smc_setmulticast(int ioaddr, struct net_device *dev)
-{
- int i;
- unsigned char multicast_table[ 8 ];
- struct netdev_hw_addr *ha;
- /* table for flipping the order of 3 bits */
- unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
-
- /* start with a table of all zeros: reject all */
- memset( multicast_table, 0, sizeof( multicast_table ) );
-
- netdev_for_each_mc_addr(ha, dev) {
- int position;
-
- /* only use the low order bits */
- position = ether_crc_le(6, ha->addr) & 0x3f;
-
- /* do some messy swapping to put the bit in the right spot */
- multicast_table[invert3[position&7]] |=
- (1<<invert3[(position>>3)&7]);
-
- }
- /* now, the table can be loaded into the chipset */
- SMC_SELECT_BANK( 3 );
-
- for ( i = 0; i < 8 ; i++ ) {
- outb( multicast_table[i], ioaddr + MULTICAST1 + i );
- }
-}
-
-/*
- . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * )
- . Purpose:
- . Attempt to allocate memory for a packet, if chip-memory is not
- . available, then tell the card to generate an interrupt when it
- . is available.
- .
- . Algorithm:
- .
- . o if the saved_skb is not currently null, then drop this packet
- . on the floor. This should never happen, because of TBUSY.
- . o if the saved_skb is null, then replace it with the current packet,
- . o See if I can sending it now.
- . o (NO): Enable interrupts and let the interrupt handler deal with it.
- . o (YES):Send it now.
-*/
-static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smc_local *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- word length;
- unsigned short numPages;
- word time_out;
-
- netif_stop_queue(dev);
- /* Well, I want to send the packet.. but I don't know
- if I can send it right now... */
-
- if ( lp->saved_skb) {
- /* THIS SHOULD NEVER HAPPEN. */
- dev->stats.tx_aborted_errors++;
- printk(CARDNAME": Bad Craziness - sent packet while busy.\n" );
- return NETDEV_TX_BUSY;
- }
- lp->saved_skb = skb;
-
- length = skb->len;
-
- if (length < ETH_ZLEN) {
- if (skb_padto(skb, ETH_ZLEN)) {
- netif_wake_queue(dev);
- return NETDEV_TX_OK;
- }
- length = ETH_ZLEN;
- }
-
- /*
- ** The MMU wants the number of pages to be the number of 256 bytes
- ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
- **
- ** Pkt size for allocating is data length +6 (for additional status words,
- ** length and ctl!) If odd size last byte is included in this header.
- */
- numPages = ((length & 0xfffe) + 6) / 256;
-
- if (numPages > 7 ) {
- printk(CARDNAME": Far too big packet error.\n");
- /* freeing the packet is a good thing here... but should
- . any packets of this size get down here? */
- dev_kfree_skb (skb);
- lp->saved_skb = NULL;
- /* this IS an error, but, i don't want the skb saved */
- netif_wake_queue(dev);
- return NETDEV_TX_OK;
- }
- /* either way, a packet is waiting now */
- lp->packets_waiting++;
-
- /* now, try to allocate the memory */
- SMC_SELECT_BANK( 2 );
- outw( MC_ALLOC | numPages, ioaddr + MMU_CMD );
- /*
- . Performance Hack
- .
- . wait a short amount of time.. if I can send a packet now, I send
- . it now. Otherwise, I enable an interrupt and wait for one to be
- . available.
- .
- . I could have handled this a slightly different way, by checking to
- . see if any memory was available in the FREE MEMORY register. However,
- . either way, I need to generate an allocation, and the allocation works
- . no matter what, so I saw no point in checking free memory.
- */
- time_out = MEMORY_WAIT_TIME;
- do {
- word status;
-
- status = inb( ioaddr + INTERRUPT );
- if ( status & IM_ALLOC_INT ) {
- /* acknowledge the interrupt */
- outb( IM_ALLOC_INT, ioaddr + INTERRUPT );
- break;
- }
- } while ( -- time_out );
-
- if ( !time_out ) {
- /* oh well, wait until the chip finds memory later */
- SMC_ENABLE_INT( IM_ALLOC_INT );
- PRINTK2((CARDNAME": memory allocation deferred.\n"));
- /* it's deferred, but I'll handle it later */
- return NETDEV_TX_OK;
- }
- /* or YES! I can send the packet now.. */
- smc_hardware_send_packet(dev);
- netif_wake_queue(dev);
- return NETDEV_TX_OK;
-}
-
-/*
- . Function: smc_hardware_send_packet(struct net_device * )
- . Purpose:
- . This sends the actual packet to the SMC9xxx chip.
- .
- . Algorithm:
- . First, see if a saved_skb is available.
- . ( this should NOT be called if there is no 'saved_skb'
- . Now, find the packet number that the chip allocated
- . Point the data pointers at it in memory
- . Set the length word in the chip's memory
- . Dump the packet to chip memory
- . Check if a last byte is needed ( odd length packet )
- . if so, set the control flag right
- . Tell the card to send it
- . Enable the transmit interrupt, so I know if it failed
- . Free the kernel data if I actually sent it.
-*/
-static void smc_hardware_send_packet( struct net_device * dev )
-{
- struct smc_local *lp = netdev_priv(dev);
- byte packet_no;
- struct sk_buff * skb = lp->saved_skb;
- word length;
- unsigned int ioaddr;
- byte * buf;
-
- ioaddr = dev->base_addr;
-
- if ( !skb ) {
- PRINTK((CARDNAME": In XMIT with no packet to send\n"));
- return;
- }
- length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- buf = skb->data;
-
- /* If I get here, I _know_ there is a packet slot waiting for me */
- packet_no = inb( ioaddr + PNR_ARR + 1 );
- if ( packet_no & 0x80 ) {
- /* or isn't there? BAD CHIP! */
- netdev_dbg(dev, CARDNAME": Memory allocation failed.\n");
- dev_kfree_skb_any(skb);
- lp->saved_skb = NULL;
- netif_wake_queue(dev);
- return;
- }
-
- /* we have a packet address, so tell the card to use it */
- outb( packet_no, ioaddr + PNR_ARR );
-
- /* point to the beginning of the packet */
- outw( PTR_AUTOINC , ioaddr + POINTER );
-
- PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length));
-#if SMC_DEBUG > 2
- print_packet( buf, length );
-#endif
-
- /* send the packet length ( +6 for status, length and ctl byte )
- and the status word ( set to zeros ) */
-#ifdef USE_32_BIT
- outl( (length +6 ) << 16 , ioaddr + DATA_1 );
-#else
- outw( 0, ioaddr + DATA_1 );
- /* send the packet length ( +6 for status words, length, and ctl*/
- outb( (length+6) & 0xFF,ioaddr + DATA_1 );
- outb( (length+6) >> 8 , ioaddr + DATA_1 );
-#endif
-
- /* send the actual data
- . I _think_ it's faster to send the longs first, and then
- . mop up by sending the last word. It depends heavily
- . on alignment, at least on the 486. Maybe it would be
- . a good idea to check which is optimal? But that could take
- . almost as much time as is saved?
- */
-#ifdef USE_32_BIT
- if ( length & 0x2 ) {
- outsl(ioaddr + DATA_1, buf, length >> 2 );
- outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
- }
- else
- outsl(ioaddr + DATA_1, buf, length >> 2 );
-#else
- outsw(ioaddr + DATA_1 , buf, (length ) >> 1);
-#endif
- /* Send the last byte, if there is one. */
-
- if ( (length & 1) == 0 ) {
- outw( 0, ioaddr + DATA_1 );
- } else {
- outb( buf[length -1 ], ioaddr + DATA_1 );
- outb( 0x20, ioaddr + DATA_1);
- }
-
- /* enable the interrupts */
- SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
-
- /* and let the chipset deal with it */
- outw( MC_ENQUEUE , ioaddr + MMU_CMD );
-
- PRINTK2((CARDNAME": Sent packet of length %d\n", length));
-
- lp->saved_skb = NULL;
- dev_kfree_skb_any (skb);
-
- netif_trans_update(dev);
-
- /* we can send another packet */
- netif_wake_queue(dev);
-}
-
-/*-------------------------------------------------------------------------
- |
- | smc_init(int unit)
- | Input parameters:
- | dev->base_addr == 0, try to find all possible locations
- | dev->base_addr == 1, return failure code
- | dev->base_addr == 2, always allocate space, and return success
- | dev->base_addr == <anything else> this is the address to check
- |
- | Output:
- | pointer to net_device or ERR_PTR(error)
- |
- ---------------------------------------------------------------------------
-*/
-static int io;
-static int irq;
-static int ifport;
-
-struct net_device * __init smc_init(int unit)
-{
- struct net_device *dev = alloc_etherdev(sizeof(struct smc_local));
- struct devlist *smcdev = smc_devlist;
- int err = 0;
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- if (unit >= 0) {
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
- io = dev->base_addr;
- irq = dev->irq;
- }
-
- if (io > 0x1ff) { /* Check a single specified location. */
- err = smc_probe(dev, io);
- } else if (io != 0) { /* Don't probe at all. */
- err = -ENXIO;
- } else {
- for (;smcdev->port; smcdev++) {
- if (smc_probe(dev, smcdev->port) == 0)
- break;
- }
- if (!smcdev->port)
- err = -ENODEV;
- }
- if (err)
- goto out;
- err = register_netdev(dev);
- if (err)
- goto out1;
- return dev;
-out1:
- free_irq(dev->irq, dev);
- release_region(dev->base_addr, SMC_IO_EXTENT);
-out:
- free_netdev(dev);
- return ERR_PTR(err);
-}
-
-/*----------------------------------------------------------------------
- . smc_findirq
- .
- . This routine has a simple purpose -- make the SMC chip generate an
- . interrupt, so an auto-detect routine can detect it, and find the IRQ,
- ------------------------------------------------------------------------
-*/
-static int __init smc_findirq(int ioaddr)
-{
-#ifndef NO_AUTOPROBE
- int timeout = 20;
- unsigned long cookie;
-
-
- cookie = probe_irq_on();
-
- /*
- * What I try to do here is trigger an ALLOC_INT. This is done
- * by allocating a small chunk of memory, which will give an interrupt
- * when done.
- */
-
-
- SMC_SELECT_BANK(2);
- /* enable ALLOCation interrupts ONLY */
- outb( IM_ALLOC_INT, ioaddr + INT_MASK );
-
- /*
- . Allocate 512 bytes of memory. Note that the chip was just
- . reset so all the memory is available
- */
- outw( MC_ALLOC | 1, ioaddr + MMU_CMD );
-
- /*
- . Wait until positive that the interrupt has been generated
- */
- while ( timeout ) {
- byte int_status;
-
- int_status = inb( ioaddr + INTERRUPT );
-
- if ( int_status & IM_ALLOC_INT )
- break; /* got the interrupt */
- timeout--;
- }
- /* there is really nothing that I can do here if timeout fails,
- as probe_irq_off will return a 0 anyway, which is what I
- want in this case. Plus, the clean up is needed in both
- cases. */
-
- /* DELAY HERE!
- On a fast machine, the status might change before the interrupt
- is given to the processor. This means that the interrupt was
- never detected, and probe_irq_off fails to report anything.
- This should fix probe_irq_* problems.
- */
- SMC_DELAY();
- SMC_DELAY();
-
- /* and disable all interrupts again */
- outb( 0, ioaddr + INT_MASK );
-
- /* and return what I found */
- return probe_irq_off(cookie);
-#else /* NO_AUTOPROBE */
- struct devlist *smcdev;
- for (smcdev = smc_devlist; smcdev->port; smcdev++) {
- if (smcdev->port == ioaddr)
- return smcdev->irq;
- }
- return 0;
-#endif
-}
-
-static const struct net_device_ops smc_netdev_ops = {
- .ndo_open = smc_open,
- .ndo_stop = smc_close,
- .ndo_start_xmit = smc_wait_to_send_packet,
- .ndo_tx_timeout = smc_timeout,
- .ndo_set_rx_mode = smc_set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-/*----------------------------------------------------------------------
- . Function: smc_probe( int ioaddr )
- .
- . Purpose:
- . Tests to see if a given ioaddr points to an SMC9xxx chip.
- . Returns a 0 on success
- .
- . Algorithm:
- . (1) see if the high byte of BANK_SELECT is 0x33
- . (2) compare the ioaddr with the base register's address
- . (3) see if I recognize the chip ID in the appropriate register
- .
- .---------------------------------------------------------------------
- */
-
-/*---------------------------------------------------------------
- . Here I do typical initialization tasks.
- .
- . o Initialize the structure if needed
- . o print out my vanity message if not done so already
- . o print out what type of hardware is detected
- . o print out the ethernet address
- . o find the IRQ
- . o set up my private data
- . o configure the dev structure with my subroutines
- . o actually GRAB the irq.
- . o GRAB the region
- .-----------------------------------------------------------------
-*/
-static int __init smc_probe(struct net_device *dev, int ioaddr)
-{
- int i, memory, retval;
- unsigned int bank;
-
- const char *version_string;
- const char *if_string;
-
- /* registers */
- word revision_register;
- word base_address_register;
- word configuration_register;
- word memory_info_register;
- word memory_cfg_register;
- u8 addr[ETH_ALEN];
-
- /* Grab the region so that no one else tries to probe our ioports. */
- if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME))
- return -EBUSY;
-
- dev->irq = irq;
- dev->if_port = ifport;
-
- /* First, see if the high byte is 0x33 */
- bank = inw( ioaddr + BANK_SELECT );
- if ( (bank & 0xFF00) != 0x3300 ) {
- retval = -ENODEV;
- goto err_out;
- }
- /* The above MIGHT indicate a device, but I need to write to further
- test this. */
- outw( 0x0, ioaddr + BANK_SELECT );
- bank = inw( ioaddr + BANK_SELECT );
- if ( (bank & 0xFF00 ) != 0x3300 ) {
- retval = -ENODEV;
- goto err_out;
- }
- /* well, we've already written once, so hopefully another time won't
- hurt. This time, I need to switch the bank register to bank 1,
- so I can access the base address register */
- SMC_SELECT_BANK(1);
- base_address_register = inw( ioaddr + BASE );
- if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) {
- printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). "
- "Probably not a SMC chip\n",
- ioaddr, base_address_register >> 3 & 0x3E0 );
- /* well, the base address register didn't match. Must not have
- been a SMC chip after all. */
- retval = -ENODEV;
- goto err_out;
- }
-
- /* check if the revision register is something that I recognize.
- These might need to be added to later, as future revisions
- could be added. */
- SMC_SELECT_BANK(3);
- revision_register = inw( ioaddr + REVISION );
- if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) {
- /* I don't recognize this chip, so... */
- printk(CARDNAME ": IO %x: Unrecognized revision register:"
- " %x, Contact author.\n", ioaddr, revision_register);
-
- retval = -ENODEV;
- goto err_out;
- }
-
- /* at this point I'll assume that the chip is an SMC9xxx.
- It might be prudent to check a listing of MAC addresses
- against the hardware address, or do some other tests. */
-
- pr_info_once("%s\n", version);
-
- /* fill in some of the fields */
- dev->base_addr = ioaddr;
-
- /*
- . Get the MAC address ( bank 1, regs 4 - 9 )
- */
- SMC_SELECT_BANK( 1 );
- for ( i = 0; i < 6; i += 2 ) {
- word address;
-
- address = inw( ioaddr + ADDR0 + i );
- addr[i + 1] = address >> 8;
- addr[i] = address & 0xFF;
- }
- eth_hw_addr_set(dev, addr);
-
- /* get the memory information */
-
- SMC_SELECT_BANK( 0 );
- memory_info_register = inw( ioaddr + MIR );
- memory_cfg_register = inw( ioaddr + MCR );
- memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */
- memory *= 256 * ( memory_info_register & 0xFF );
-
- /*
- Now, I want to find out more about the chip. This is sort of
- redundant, but it's cleaner to have it in both, rather than having
- one VERY long probe procedure.
- */
- SMC_SELECT_BANK(3);
- revision_register = inw( ioaddr + REVISION );
- version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ];
- if ( !version_string ) {
- /* I shouldn't get here because this call was done before.... */
- retval = -ENODEV;
- goto err_out;
- }
-
- /* is it using AUI or 10BaseT ? */
- if ( dev->if_port == 0 ) {
- SMC_SELECT_BANK(1);
- configuration_register = inw( ioaddr + CONFIG );
- if ( configuration_register & CFG_AUI_SELECT )
- dev->if_port = 2;
- else
- dev->if_port = 1;
- }
- if_string = interfaces[ dev->if_port - 1 ];
-
- /* now, reset the chip, and put it into a known state */
- smc_reset( ioaddr );
-
- /*
- . If dev->irq is 0, then the device has to be banged on to see
- . what the IRQ is.
- .
- . This banging doesn't always detect the IRQ, for unknown reasons.
- . a workaround is to reset the chip and try again.
- .
- . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
- . be what is requested on the command line. I don't do that, mostly
- . because the card that I have uses a non-standard method of accessing
- . the IRQs, and because this _should_ work in most configurations.
- .
- . Specifying an IRQ is done with the assumption that the user knows
- . what (s)he is doing. No checking is done!!!!
- .
- */
- if ( dev->irq < 2 ) {
- int trials;
-
- trials = 3;
- while ( trials-- ) {
- dev->irq = smc_findirq( ioaddr );
- if ( dev->irq )
- break;
- /* kick the card and try again */
- smc_reset( ioaddr );
- }
- }
- if (dev->irq == 0 ) {
- printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
- retval = -ENODEV;
- goto err_out;
- }
-
- /* now, print out the card info, in a short format.. */
-
- netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ",
- version_string, revision_register & 0xF, ioaddr, dev->irq,
- if_string, memory);
- /*
- . Print the Ethernet address
- */
- netdev_info(dev, "ADDR: %pM\n", dev->dev_addr);
-
- /* Grab the IRQ */
- retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
- if (retval) {
- netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n",
- DRV_NAME, dev->irq, retval);
- goto err_out;
- }
-
- dev->netdev_ops = &smc_netdev_ops;
- dev->watchdog_timeo = HZ/20;
-
- return 0;
-
-err_out:
- release_region(ioaddr, SMC_IO_EXTENT);
- return retval;
-}
-
-#if SMC_DEBUG > 2
-static void print_packet( byte * buf, int length )
-{
-#if 0
- print_hex_dump_debug(DRV_NAME, DUMP_PREFIX_OFFSET, 16, 1,
- buf, length, true);
-#endif
-}
-#endif
-
-
-/*
- * Open and Initialize the board
- *
- * Set up everything, reset the card, etc ..
- *
- */
-static int smc_open(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
-
- int i; /* used to set hw ethernet address */
-
- /* clear out all the junk that was put here before... */
- memset(netdev_priv(dev), 0, sizeof(struct smc_local));
-
- /* reset the hardware */
-
- smc_reset( ioaddr );
- smc_enable( ioaddr );
-
- /* Select which interface to use */
-
- SMC_SELECT_BANK( 1 );
- if ( dev->if_port == 1 ) {
- outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
- ioaddr + CONFIG );
- }
- else if ( dev->if_port == 2 ) {
- outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,
- ioaddr + CONFIG );
- }
-
- /*
- According to Becker, I have to set the hardware address
- at this point, because the (l)user can set it with an
- ioctl. Easily done...
- */
- SMC_SELECT_BANK( 1 );
- for ( i = 0; i < 6; i += 2 ) {
- word address;
-
- address = dev->dev_addr[ i + 1 ] << 8 ;
- address |= dev->dev_addr[ i ];
- outw( address, ioaddr + ADDR0 + i );
- }
-
- netif_start_queue(dev);
- return 0;
-}
-
-/*--------------------------------------------------------
- . Called by the kernel to send a packet out into the void
- . of the net. This routine is largely based on
- . skeleton.c, from Becker.
- .--------------------------------------------------------
-*/
-
-static void smc_timeout(struct net_device *dev, unsigned int txqueue)
-{
- /* If we get here, some higher level has decided we are broken.
- There should really be a "kick me" function call instead. */
- netdev_warn(dev, CARDNAME": transmit timed out, %s?\n",
- tx_done(dev) ? "IRQ conflict" : "network cable problem");
- /* "kick" the adaptor */
- smc_reset( dev->base_addr );
- smc_enable( dev->base_addr );
- netif_trans_update(dev); /* prevent tx timeout */
- /* clear anything saved */
- ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
- netif_wake_queue(dev);
-}
-
-/*-------------------------------------------------------------
- .
- . smc_rcv - receive a packet from the card
- .
- . There is ( at least ) a packet waiting to be read from
- . chip-memory.
- .
- . o Read the status
- . o If an error, record it
- . o otherwise, read in the packet
- --------------------------------------------------------------
-*/
-static void smc_rcv(struct net_device *dev)
-{
- int ioaddr = dev->base_addr;
- int packet_number;
- word status;
- word packet_length;
-
- /* assume bank 2 */
-
- packet_number = inw( ioaddr + FIFO_PORTS );
-
- if ( packet_number & FP_RXEMPTY ) {
- /* we got called , but nothing was on the FIFO */
- PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n"));
- /* don't need to restore anything */
- return;
- }
-
- /* start reading from the start of the packet */
- outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
-
- /* First two words are status and packet_length */
- status = inw( ioaddr + DATA_1 );
- packet_length = inw( ioaddr + DATA_1 );
-
- packet_length &= 0x07ff; /* mask off top bits */
-
- PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));
- /*
- . the packet length contains 3 extra words :
- . status, length, and an extra word with an odd byte .
- */
- packet_length -= 6;
-
- if ( !(status & RS_ERRORS ) ){
- /* do stuff to make a new packet */
- struct sk_buff * skb;
- byte * data;
-
- /* read one extra byte */
- if ( status & RS_ODDFRAME )
- packet_length++;
-
- /* set multicast stats */
- if ( status & RS_MULTICAST )
- dev->stats.multicast++;
-
- skb = netdev_alloc_skb(dev, packet_length + 5);
- if ( skb == NULL ) {
- dev->stats.rx_dropped++;
- goto done;
- }
-
- /*
- ! This should work without alignment, but it could be
- ! in the worse case
- */
-
- skb_reserve( skb, 2 ); /* 16 bit alignment */
-
- data = skb_put( skb, packet_length);
-
-#ifdef USE_32_BIT
- /* QUESTION: Like in the TX routine, do I want
- to send the DWORDs or the bytes first, or some
- mixture. A mixture might improve already slow PIO
- performance */
- PRINTK3((" Reading %d dwords (and %d bytes)\n",
- packet_length >> 2, packet_length & 3 ));
- insl(ioaddr + DATA_1 , data, packet_length >> 2 );
- /* read the left over bytes */
- insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),
- packet_length & 0x3 );
-#else
- PRINTK3((" Reading %d words and %d byte(s)\n",
- (packet_length >> 1 ), packet_length & 1 ));
- insw(ioaddr + DATA_1 , data, packet_length >> 1);
- if ( packet_length & 1 ) {
- data += packet_length & ~1;
- *(data++) = inb( ioaddr + DATA_1 );
- }
-#endif
-#if SMC_DEBUG > 2
- print_packet( data, packet_length );
-#endif
-
- skb->protocol = eth_type_trans(skb, dev );
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += packet_length;
- } else {
- /* error ... */
- dev->stats.rx_errors++;
-
- if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++;
- if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
- dev->stats.rx_length_errors++;
- if ( status & RS_BADCRC) dev->stats.rx_crc_errors++;
- }
-
-done:
- /* error or good, tell the card to get rid of this packet */
- outw( MC_RELEASE, ioaddr + MMU_CMD );
-}
-
-
-/*************************************************************************
- . smc_tx
- .
- . Purpose: Handle a transmit error message. This will only be called
- . when an error, because of the AUTO_RELEASE mode.
- .
- . Algorithm:
- . Save pointer and packet no
- . Get the packet no from the top of the queue
- . check if it's valid ( if not, is this an error??? )
- . read the status word
- . record the error
- . ( resend? Not really, since we don't want old packets around )
- . Restore saved values
- ************************************************************************/
-static void smc_tx( struct net_device * dev )
-{
- int ioaddr = dev->base_addr;
- struct smc_local *lp = netdev_priv(dev);
- byte saved_packet;
- byte packet_no;
- word tx_status;
-
-
- /* assume bank 2 */
-
- saved_packet = inb( ioaddr + PNR_ARR );
- packet_no = inw( ioaddr + FIFO_PORTS );
- packet_no &= 0x7F;
-
- /* select this as the packet to read from */
- outb( packet_no, ioaddr + PNR_ARR );
-
- /* read the first word from this packet */
- outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );
-
- tx_status = inw( ioaddr + DATA_1 );
- PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status));
-
- dev->stats.tx_errors++;
- if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++;
- if ( tx_status & TS_LATCOL ) {
- netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n");
- dev->stats.tx_window_errors++;
- }
-#if 0
- if ( tx_status & TS_16COL ) { ... }
-#endif
-
- if ( tx_status & TS_SUCCESS ) {
- netdev_info(dev, CARDNAME": Successful packet caused interrupt\n");
- }
- /* re-enable transmit */
- SMC_SELECT_BANK( 0 );
- outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );
-
- /* kill the packet */
- SMC_SELECT_BANK( 2 );
- outw( MC_FREEPKT, ioaddr + MMU_CMD );
-
- /* one less packet waiting for me */
- lp->packets_waiting--;
-
- outb( saved_packet, ioaddr + PNR_ARR );
-}
-
-/*--------------------------------------------------------------------
- .
- . This is the main routine of the driver, to handle the device when
- . it needs some attention.
- .
- . So:
- . first, save state of the chipset
- . branch off into routines to handle each case, and acknowledge
- . each to the interrupt register
- . and finally restore state.
- .
- ---------------------------------------------------------------------*/
-
-static irqreturn_t smc_interrupt(int irq, void * dev_id)
-{
- struct net_device *dev = dev_id;
- int ioaddr = dev->base_addr;
- struct smc_local *lp = netdev_priv(dev);
-
- byte status;
- word card_stats;
- byte mask;
- int timeout;
- /* state registers */
- word saved_bank;
- word saved_pointer;
- int handled = 0;
-
-
- PRINTK3((CARDNAME": SMC interrupt started\n"));
-
- saved_bank = inw( ioaddr + BANK_SELECT );
-
- SMC_SELECT_BANK(2);
- saved_pointer = inw( ioaddr + POINTER );
-
- mask = inb( ioaddr + INT_MASK );
- /* clear all interrupts */
- outb( 0, ioaddr + INT_MASK );
-
-
- /* set a timeout value, so I don't stay here forever */
- timeout = 4;
-
- PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask));
- do {
- /* read the status flag, and mask it */
- status = inb( ioaddr + INTERRUPT ) & mask;
- if (!status )
- break;
-
- handled = 1;
-
- PRINTK3((KERN_WARNING CARDNAME
- ": Handling interrupt status %x\n", status));
-
- if (status & IM_RCV_INT) {
- /* Got a packet(s). */
- PRINTK2((KERN_WARNING CARDNAME
- ": Receive Interrupt\n"));
- smc_rcv(dev);
- } else if (status & IM_TX_INT ) {
- PRINTK2((KERN_WARNING CARDNAME
- ": TX ERROR handled\n"));
- smc_tx(dev);
- outb(IM_TX_INT, ioaddr + INTERRUPT );
- } else if (status & IM_TX_EMPTY_INT ) {
- /* update stats */
- SMC_SELECT_BANK( 0 );
- card_stats = inw( ioaddr + COUNTER );
- /* single collisions */
- dev->stats.collisions += card_stats & 0xF;
- card_stats >>= 4;
- /* multiple collisions */
- dev->stats.collisions += card_stats & 0xF;
-
- /* these are for when linux supports these statistics */
-
- SMC_SELECT_BANK( 2 );
- PRINTK2((KERN_WARNING CARDNAME
- ": TX_BUFFER_EMPTY handled\n"));
- outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );
- mask &= ~IM_TX_EMPTY_INT;
- dev->stats.tx_packets += lp->packets_waiting;
- lp->packets_waiting = 0;
-
- } else if (status & IM_ALLOC_INT ) {
- PRINTK2((KERN_DEBUG CARDNAME
- ": Allocation interrupt\n"));
- /* clear this interrupt so it doesn't happen again */
- mask &= ~IM_ALLOC_INT;
-
- smc_hardware_send_packet( dev );
-
- /* enable xmit interrupts based on this */
- mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
-
- /* and let the card send more packets to me */
- netif_wake_queue(dev);
-
- PRINTK2((CARDNAME": Handoff done successfully.\n"));
- } else if (status & IM_RX_OVRN_INT ) {
- dev->stats.rx_errors++;
- dev->stats.rx_fifo_errors++;
- outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );
- } else if (status & IM_EPH_INT ) {
- PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n"));
- } else if (status & IM_ERCV_INT ) {
- PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n"));
- outb( IM_ERCV_INT, ioaddr + INTERRUPT );
- }
- } while ( timeout -- );
-
-
- /* restore state register */
- SMC_SELECT_BANK( 2 );
- outb( mask, ioaddr + INT_MASK );
-
- PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask));
- outw( saved_pointer, ioaddr + POINTER );
-
- SMC_SELECT_BANK( saved_bank );
-
- PRINTK3((CARDNAME ": Interrupt done\n"));
- return IRQ_RETVAL(handled);
-}
-
-
-/*----------------------------------------------------
- . smc_close
- .
- . this makes the board clean up everything that it can
- . and not talk to the outside world. Caused by
- . an 'ifconfig ethX down'
- .
- -----------------------------------------------------*/
-static int smc_close(struct net_device *dev)
-{
- netif_stop_queue(dev);
- /* clear everything */
- smc_shutdown( dev->base_addr );
-
- /* Update the statistics here. */
- return 0;
-}
-
-/*-----------------------------------------------------------
- . smc_set_multicast_list
- .
- . This routine will, depending on the values passed to it,
- . either make it accept multicast packets, go into
- . promiscuous mode ( for TCPDUMP and cousins ) or accept
- . a select set of multicast packets
-*/
-static void smc_set_multicast_list(struct net_device *dev)
-{
- short ioaddr = dev->base_addr;
-
- SMC_SELECT_BANK(0);
- if ( dev->flags & IFF_PROMISC )
- outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR );
-
-/* BUG? I never disable promiscuous mode if multicasting was turned on.
- Now, I turn off promiscuous mode, but I don't do anything to multicasting
- when promiscuous mode is turned on.
-*/
-
- /* Here, I am setting this to accept all multicast packets.
- I don't need to zero the multicast table, because the flag is
- checked before the table is
- */
- else if (dev->flags & IFF_ALLMULTI)
- outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
-
- /* We just get all multicast packets even if we only want them
- . from one source. This will be changed at some future
- . point. */
- else if (!netdev_mc_empty(dev)) {
- /* support hardware multicasting */
-
- /* be sure I get rid of flags I might have set */
- outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
- ioaddr + RCR );
- /* NOTE: this has to set the bank, so make sure it is the
- last thing called. The bank is set to zero at the top */
- smc_setmulticast(ioaddr, dev);
- }
- else {
- outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
- ioaddr + RCR );
-
- /*
- since I'm disabling all multicast entirely, I need to
- clear the multicast list
- */
- SMC_SELECT_BANK( 3 );
- outw( 0, ioaddr + MULTICAST1 );
- outw( 0, ioaddr + MULTICAST2 );
- outw( 0, ioaddr + MULTICAST3 );
- outw( 0, ioaddr + MULTICAST4 );
- }
-}
-
-#ifdef MODULE
-
-static struct net_device *devSMC9194;
-MODULE_DESCRIPTION("SMC 9194 Ethernet driver");
-MODULE_LICENSE("GPL");
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param(ifport, int, 0);
-MODULE_PARM_DESC(io, "SMC 99194 I/O base address");
-MODULE_PARM_DESC(irq, "SMC 99194 IRQ number");
-MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)");
-
-static int __init smc_init_module(void)
-{
- if (io == 0)
- printk(KERN_WARNING
- CARDNAME": You shouldn't use auto-probing with insmod!\n" );
-
- /* copy the parameters from insmod into the device structure */
- devSMC9194 = smc_init(-1);
- return PTR_ERR_OR_ZERO(devSMC9194);
-}
-module_init(smc_init_module);
-
-static void __exit smc_cleanup_module(void)
-{
- unregister_netdev(devSMC9194);
- free_irq(devSMC9194->irq, devSMC9194);
- release_region(devSMC9194->base_addr, SMC_IO_EXTENT);
- free_netdev(devSMC9194);
-}
-module_exit(smc_cleanup_module);
-
-#endif /* MODULE */
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 08/15] drivers: net: smsc: smc91c92: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (6 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 07/15] drivers: net: smsc: smc9194: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 09/15] drivers: net: cirrus: cs89x0: " Andrew Lunn
` (6 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The smc91c92 was written by David A Hinds in 1999. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Remove the Documentation as well, since it refers to kernel versions
1.2.13 until 1.3.71 and FTP sites which no longer exist.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Remove document as well
---
.../device_drivers/ethernet/smsc/smc9.rst | 48 -
drivers/net/ethernet/smsc/Kconfig | 12 -
drivers/net/ethernet/smsc/Makefile | 1 -
drivers/net/ethernet/smsc/smc91c92_cs.c | 2059 --------------------
4 files changed, 2120 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst b/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst
deleted file mode 100644
index e5eac896a631..000000000000
--- a/Documentation/networking/device_drivers/ethernet/smsc/smc9.rst
+++ /dev/null
@@ -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
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index d25bbcc98854..66bca803b19c 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -37,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
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
index afea0b94c2a4..ab6f03f7ba17 100644
--- a/drivers/net/ethernet/smsc/Makefile
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -4,7 +4,6 @@
#
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
diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
deleted file mode 100644
index cc0c75694351..000000000000
--- a/drivers/net/ethernet/smsc/smc91c92_cs.c
+++ /dev/null
@@ -1,2059 +0,0 @@
-/*======================================================================
-
- A PCMCIA ethernet driver for SMC91c92-based cards.
-
- This driver supports Megahertz PCMCIA ethernet cards; and
- Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
- multifunction cards.
-
- Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
-
- smc91c92_cs.c 1.122 2002/10/25 06:26:39
-
- This driver contains code written by Donald Becker
- (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
- David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
- (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of
- Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've
- incorporated some parts of his driver here. I (Dave) wrote most
- of the PCMCIA glue code, and the Ositech support code. Kelly
- Stephens (kstephen@holli.com) added support for the Motorola
- Mariner, with help from Allen Brost.
-
- This software may be used and distributed according to the terms of
- the GNU General Public License, incorporated herein by reference.
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/crc32.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/ethtool.h>
-#include <linux/mii.h>
-#include <linux/jiffies.h>
-#include <linux/firmware.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/ss.h>
-
-#include <asm/io.h>
-#include <linux/uaccess.h>
-
-/*====================================================================*/
-
-static const char *if_names[] = { "auto", "10baseT", "10base2"};
-
-/* Firmware name */
-#define FIRMWARE_NAME "ositech/Xilinx7OD.bin"
-
-/* Module parameters */
-
-MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(FIRMWARE_NAME);
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/*
- Transceiver/media type.
- 0 = auto
- 1 = 10baseT (and autoselect if #define AUTOSELECT),
- 2 = AUI/10base2,
-*/
-INT_MODULE_PARM(if_port, 0);
-
-
-#define DRV_NAME "smc91c92_cs"
-#define DRV_VERSION "1.123"
-
-/*====================================================================*/
-
-/* Operational parameter that usually are not changed. */
-
-/* Time in jiffies before concluding Tx hung */
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-#define INTR_WORK 4
-
-/* Times to check the check the chip before concluding that it doesn't
- currently have room for another Tx packet. */
-#define MEMORY_WAIT_TIME 8
-
-struct smc_private {
- struct pcmcia_device *p_dev;
- spinlock_t lock;
- u_short manfid;
- u_short cardid;
-
- struct sk_buff *saved_skb;
- int packets_waiting;
- void __iomem *base;
- u_short cfg;
- struct timer_list media;
- int watchdog, tx_err;
- u_short media_status;
- u_short fast_poll;
- u_short link_status;
- struct mii_if_info mii_if;
- int duplex;
- int rx_ovrn;
- unsigned long last_rx;
-};
-
-/* Special definitions for Megahertz multifunction cards */
-#define MEGAHERTZ_ISR 0x0380
-
-/* Special function registers for Motorola Mariner */
-#define MOT_LAN 0x0000
-#define MOT_UART 0x0020
-#define MOT_EEPROM 0x20
-
-#define MOT_NORMAL \
-(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
-
-/* Special function registers for Ositech cards */
-#define OSITECH_AUI_CTL 0x0c
-#define OSITECH_PWRDOWN 0x0d
-#define OSITECH_RESET 0x0e
-#define OSITECH_ISR 0x0f
-#define OSITECH_AUI_PWR 0x0c
-#define OSITECH_RESET_ISR 0x0e
-
-#define OSI_AUI_PWR 0x40
-#define OSI_LAN_PWRDOWN 0x02
-#define OSI_MODEM_PWRDOWN 0x01
-#define OSI_LAN_RESET 0x02
-#define OSI_MODEM_RESET 0x01
-
-/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
-#define BANK_SELECT 14 /* Window select register. */
-#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); }
-
-/* Bank 0 registers. */
-#define TCR 0 /* transmit control register */
-#define TCR_CLEAR 0 /* do NOTHING */
-#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
-#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */
-#define TCR_MONCSN 0x0400 /* Monitor Carrier. */
-#define TCR_FDUPLX 0x0800 /* Full duplex mode. */
-#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
-
-#define EPH 2 /* Ethernet Protocol Handler report. */
-#define EPH_TX_SUC 0x0001
-#define EPH_SNGLCOL 0x0002
-#define EPH_MULCOL 0x0004
-#define EPH_LTX_MULT 0x0008
-#define EPH_16COL 0x0010
-#define EPH_SQET 0x0020
-#define EPH_LTX_BRD 0x0040
-#define EPH_TX_DEFR 0x0080
-#define EPH_LAT_COL 0x0200
-#define EPH_LOST_CAR 0x0400
-#define EPH_EXC_DEF 0x0800
-#define EPH_CTR_ROL 0x1000
-#define EPH_RX_OVRN 0x2000
-#define EPH_LINK_OK 0x4000
-#define EPH_TX_UNRN 0x8000
-#define MEMINFO 8 /* Memory Information Register */
-#define MEMCFG 10 /* Memory Configuration Register */
-
-/* Bank 1 registers. */
-#define CONFIG 0
-#define CFG_MII_SELECT 0x8000 /* 91C100 only */
-#define CFG_NO_WAIT 0x1000
-#define CFG_FULL_STEP 0x0400
-#define CFG_SET_SQLCH 0x0200
-#define CFG_AUI_SELECT 0x0100
-#define CFG_16BIT 0x0080
-#define CFG_DIS_LINK 0x0040
-#define CFG_STATIC 0x0030
-#define CFG_IRQ_SEL_1 0x0004
-#define CFG_IRQ_SEL_0 0x0002
-#define BASE_ADDR 2
-#define ADDR0 4
-#define GENERAL 10
-#define CONTROL 12
-#define CTL_STORE 0x0001
-#define CTL_RELOAD 0x0002
-#define CTL_EE_SELECT 0x0004
-#define CTL_TE_ENABLE 0x0020
-#define CTL_CR_ENABLE 0x0040
-#define CTL_LE_ENABLE 0x0080
-#define CTL_AUTO_RELEASE 0x0800
-#define CTL_POWERDOWN 0x2000
-
-/* Bank 2 registers. */
-#define MMU_CMD 0
-#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
-#define MC_RESET 0x40
-#define MC_RELEASE 0x80 /* remove and release the current rx packet */
-#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
-#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
-#define PNR_ARR 2
-#define FIFO_PORTS 4
-#define FP_RXEMPTY 0x8000
-#define POINTER 6
-#define PTR_AUTO_INC 0x0040
-#define PTR_READ 0x2000
-#define PTR_AUTOINC 0x4000
-#define PTR_RCV 0x8000
-#define DATA_1 8
-#define INTERRUPT 12
-#define IM_RCV_INT 0x1
-#define IM_TX_INT 0x2
-#define IM_TX_EMPTY_INT 0x4
-#define IM_ALLOC_INT 0x8
-#define IM_RX_OVRN_INT 0x10
-#define IM_EPH_INT 0x20
-
-#define RCR 4
-enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
- RxEnable = 0x0100, RxStripCRC = 0x0200};
-#define RCR_SOFTRESET 0x8000 /* resets the chip */
-#define RCR_STRIP_CRC 0x200 /* strips CRC */
-#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
-#define RCR_ALMUL 0x4 /* receive all multicast packets */
-#define RCR_PROMISC 0x2 /* enable promiscuous mode */
-
-/* the normal settings for the RCR register : */
-#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
-#define RCR_CLEAR 0x0 /* set it to a base state */
-#define COUNTER 6
-
-/* BANK 3 -- not the same values as in smc9194! */
-#define MULTICAST0 0
-#define MULTICAST2 2
-#define MULTICAST4 4
-#define MULTICAST6 6
-#define MGMT 8
-#define REVISION 0x0a
-
-/* Transmit status bits. */
-#define TS_SUCCESS 0x0001
-#define TS_16COL 0x0010
-#define TS_LATCOL 0x0200
-#define TS_LOSTCAR 0x0400
-
-/* Receive status bits. */
-#define RS_ALGNERR 0x8000
-#define RS_BADCRC 0x2000
-#define RS_ODDFRAME 0x1000
-#define RS_TOOLONG 0x0800
-#define RS_TOOSHORT 0x0400
-#define RS_MULTICAST 0x0001
-#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
-
-#define set_bits(v, p) outw(inw(p)|(v), (p))
-#define mask_bits(v, p) outw(inw(p)&(v), (p))
-
-/*====================================================================*/
-
-static void smc91c92_detach(struct pcmcia_device *p_dev);
-static int smc91c92_config(struct pcmcia_device *link);
-static void smc91c92_release(struct pcmcia_device *link);
-
-static int smc_open(struct net_device *dev);
-static int smc_close(struct net_device *dev);
-static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t smc_interrupt(int irq, void *dev_id);
-static void smc_rx(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static int s9k_config(struct net_device *dev, struct ifmap *map);
-static void smc_set_xcvr(struct net_device *dev, int if_port);
-static void smc_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static void mdio_sync(unsigned int addr);
-static int mdio_read(struct net_device *dev, int phy_id, int loc);
-static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
-static int smc_link_ok(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-
-static const struct net_device_ops smc_netdev_ops = {
- .ndo_open = smc_open,
- .ndo_stop = smc_close,
- .ndo_start_xmit = smc_start_xmit,
- .ndo_tx_timeout = smc_tx_timeout,
- .ndo_set_config = s9k_config,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_eth_ioctl = smc_ioctl,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int smc91c92_probe(struct pcmcia_device *link)
-{
- struct smc_private *smc;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "smc91c92_attach()\n");
-
- /* Create new ethernet device */
- dev = alloc_etherdev(sizeof(struct smc_private));
- if (!dev)
- return -ENOMEM;
- smc = netdev_priv(dev);
- smc->p_dev = link;
- link->priv = dev;
-
- spin_lock_init(&smc->lock);
-
- /* The SMC91c92-specific entries in the device structure. */
- dev->netdev_ops = &smc_netdev_ops;
- dev->ethtool_ops = ðtool_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- smc->mii_if.dev = dev;
- smc->mii_if.mdio_read = mdio_read;
- smc->mii_if.mdio_write = mdio_write;
- smc->mii_if.phy_id_mask = 0x1f;
- smc->mii_if.reg_num_mask = 0x1f;
-
- return smc91c92_config(link);
-} /* smc91c92_attach */
-
-static void smc91c92_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "smc91c92_detach\n");
-
- unregister_netdev(dev);
-
- smc91c92_release(link);
-
- free_netdev(dev);
-} /* smc91c92_detach */
-
-/*====================================================================*/
-
-static int cvt_ascii_address(struct net_device *dev, char *s)
-{
- u8 mac[ETH_ALEN];
- int i, j, da, c;
-
- if (strlen(s) != 12)
- return -1;
- for (i = 0; i < 6; i++) {
- da = 0;
- for (j = 0; j < 2; j++) {
- c = *s++;
- da <<= 4;
- da += ((c >= '0') && (c <= '9')) ?
- (c - '0') : ((c & 0x0f) + 9);
- }
- mac[i] = da;
- }
- eth_hw_addr_set(dev, mac);
- return 0;
-}
-
-/*====================================================================
-
- Configuration stuff for Megahertz cards
-
- mhz_3288_power() is used to power up a 3288's ethernet chip.
- mhz_mfc_config() handles socket setup for multifunction (1144
- and 3288) cards. mhz_setup() gets a card's hardware ethernet
- address.
-
-======================================================================*/
-
-static int mhz_3288_power(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- u_char tmp;
-
- /* Read the ISR twice... */
- readb(smc->base+MEGAHERTZ_ISR);
- udelay(5);
- readb(smc->base+MEGAHERTZ_ISR);
-
- /* Pause 200ms... */
- mdelay(200);
-
- /* Now read and write the COR... */
- tmp = readb(smc->base + link->config_base + CISREG_COR);
- udelay(5);
- writeb(tmp, smc->base + link->config_base + CISREG_COR);
-
- return 0;
-}
-
-static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data)
-{
- int k;
- p_dev->io_lines = 16;
- p_dev->resource[1]->start = p_dev->resource[0]->start;
- p_dev->resource[1]->end = 8;
- p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- p_dev->resource[0]->end = 16;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- for (k = 0; k < 0x400; k += 0x10) {
- if (k & 0x80)
- continue;
- p_dev->resource[0]->start = k ^ 0x300;
- if (!pcmcia_request_io(p_dev))
- return 0;
- }
- return -ENODEV;
-}
-
-static int mhz_mfc_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int offset;
- int i;
-
- link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ |
- CONF_AUTO_SET_IO;
-
- /* The Megahertz combo cards have modem-like CIS entries, so
- we have to explicitly try a bunch of port combinations. */
- if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
- return -ENODEV;
-
- dev->base_addr = link->resource[0]->start;
-
- /* Allocate a memory window, for accessing the ISR */
- link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return -ENODEV;
-
- smc->base = ioremap(link->resource[2]->start,
- resource_size(link->resource[2]));
- offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0;
- i = pcmcia_map_mem_page(link, link->resource[2], offset);
- if ((i == 0) &&
- (smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
-
- return 0;
-}
-
-static int pcmcia_get_versmac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
-{
- struct net_device *dev = priv;
- cisparse_t parse;
- u8 *buf;
-
- if (pcmcia_parse_tuple(tuple, &parse))
- return -EINVAL;
-
- buf = parse.version_1.str + parse.version_1.ofs[3];
-
- if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0))
- return 0;
-
- return -EINVAL;
-};
-
-static int mhz_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- size_t len;
- u8 *buf;
- int rc;
-
- /* Read the station address from the CIS. It is stored as the last
- (fourth) string in the Version 1 Version/ID tuple. */
- if ((link->prod_id[3]) &&
- (cvt_ascii_address(dev, link->prod_id[3]) == 0))
- return 0;
-
- /* Workarounds for broken cards start here. */
- /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
- if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
- return 0;
-
- /* Another possibility: for the EM3288, in a special tuple */
- rc = -1;
- len = pcmcia_get_tuple(link, 0x81, &buf);
- if (buf && len >= 13) {
- buf[12] = '\0';
- if (cvt_ascii_address(dev, buf) == 0)
- rc = 0;
- }
- kfree(buf);
-
- return rc;
-};
-
-/*======================================================================
-
- Configuration stuff for the Motorola Mariner
-
- mot_config() writes directly to the Mariner configuration
- registers because the CIS is just bogus.
-
-======================================================================*/
-
-static void mot_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- unsigned int iouart = link->resource[1]->start;
-
- /* Set UART base address and force map with COR bit 1 */
- writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0);
- writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
- writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR);
-
- /* Set SMC base address and force map with COR bit 1 */
- writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0);
- writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
- writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR);
-
- /* Wait for things to settle down */
- mdelay(100);
-}
-
-static int mot_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int i, wait, loop;
- u8 mac[ETH_ALEN];
- u_int addr;
-
- /* Read Ethernet address from Serial EEPROM */
-
- for (i = 0; i < 3; i++) {
- SMC_SELECT_BANK(2);
- outw(MOT_EEPROM + i, ioaddr + POINTER);
- SMC_SELECT_BANK(1);
- outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
-
- for (loop = wait = 0; loop < 200; loop++) {
- udelay(10);
- wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
- if (wait == 0) break;
- }
-
- if (wait)
- return -1;
-
- addr = inw(ioaddr + GENERAL);
- mac[2*i] = addr & 0xff;
- mac[2*i+1] = (addr >> 8) & 0xff;
- }
- eth_hw_addr_set(dev, mac);
-
- return 0;
-}
-
-/*====================================================================*/
-
-static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
- p_dev->resource[0]->end = 16;
- p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
-
- return pcmcia_request_io(p_dev);
-}
-
-static int smc_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- int i;
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- i = pcmcia_loop_config(link, smc_configcheck, NULL);
- if (!i)
- dev->base_addr = link->resource[0]->start;
-
- return i;
-}
-
-
-static int smc_setup(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- /* Check for a LAN function extension tuple */
- if (!pcmcia_get_mac_from_cis(link, dev))
- return 0;
-
- /* Try the third string in the Version 1 Version/ID tuple. */
- if (link->prod_id[2]) {
- if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
- return 0;
- }
- return -1;
-}
-
-/*====================================================================*/
-
-static int osi_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
- int i, j;
-
- link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ;
- link->resource[0]->end = 64;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->end = 8;
-
- /* Enable Hard Decode, LAN, Modem */
- link->io_lines = 16;
- link->config_index = 0x23;
-
- for (i = j = 0; j < 4; j++) {
- link->resource[1]->start = com[j];
- i = pcmcia_request_io(link);
- if (i == 0)
- break;
- }
- if (i != 0) {
- /* Fallback: turn off hard decode */
- link->config_index = 0x03;
- link->resource[1]->end = 0;
- i = pcmcia_request_io(link);
- }
- dev->base_addr = link->resource[0]->start + 0x10;
- return i;
-}
-
-static int osi_load_firmware(struct pcmcia_device *link)
-{
- const struct firmware *fw;
- int i, err;
-
- err = request_firmware(&fw, FIRMWARE_NAME, &link->dev);
- if (err) {
- pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME);
- return err;
- }
-
- /* Download the Seven of Diamonds firmware */
- for (i = 0; i < fw->size; i++) {
- outb(fw->data[i], link->resource[0]->start + 2);
- udelay(50);
- }
- release_firmware(fw);
- return err;
-}
-
-static int pcmcia_osi_mac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
-{
- struct net_device *dev = priv;
-
- if (tuple->TupleDataLen < 8)
- return -EINVAL;
- if (tuple->TupleData[0] != 0x04)
- return -EINVAL;
-
- eth_hw_addr_set(dev, &tuple->TupleData[2]);
- return 0;
-};
-
-
-static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
-{
- struct net_device *dev = link->priv;
- int rc;
-
- /* Read the station address from tuple 0x90, subtuple 0x04 */
- if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
- return -1;
-
- if (((manfid == MANFID_OSITECH) &&
- (cardid == PRODID_OSITECH_SEVEN)) ||
- ((manfid == MANFID_PSION) &&
- (cardid == PRODID_PSION_NET100))) {
- rc = osi_load_firmware(link);
- if (rc)
- return rc;
- } else if (manfid == MANFID_OSITECH) {
- /* Make sure both functions are powered up */
- set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR);
- /* Now, turn on the interrupt for both card functions */
- set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR);
- dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
- inw(link->resource[0]->start + OSITECH_AUI_PWR),
- inw(link->resource[0]->start + OSITECH_RESET_ISR));
- }
- return 0;
-}
-
-static int smc91c92_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int smc91c92_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- int i;
-
- if ((smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288))
- mhz_3288_power(link);
- if (smc->manfid == MANFID_MOTOROLA)
- mot_config(link);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- /* Power up the card and enable interrupts */
- set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
- set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
- }
- if (((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid == PRODID_OSITECH_SEVEN)) ||
- ((smc->manfid == MANFID_PSION) &&
- (smc->cardid == PRODID_PSION_NET100))) {
- i = osi_load_firmware(link);
- if (i) {
- netdev_err(dev, "Failed to load firmware\n");
- return i;
- }
- }
- if (link->open) {
- smc_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/*======================================================================
-
- This verifies that the chip is some SMC91cXX variant, and returns
- the revision code if successful. Otherwise, it returns -ENODEV.
-
-======================================================================*/
-
-static int check_sig(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int width;
- u_short s;
-
- SMC_SELECT_BANK(1);
- if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
- /* Try powering up the chip */
- outw(0, ioaddr + CONTROL);
- mdelay(55);
- }
-
- /* Try setting bus width */
- width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO);
- s = inb(ioaddr + CONFIG);
- if (width)
- s |= CFG_16BIT;
- else
- s &= ~CFG_16BIT;
- outb(s, ioaddr + CONFIG);
-
- /* Check Base Address Register to make sure bus width is OK */
- s = inw(ioaddr + BASE_ADDR);
- if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
- ((s >> 8) != (s & 0xff))) {
- SMC_SELECT_BANK(3);
- s = inw(ioaddr + REVISION);
- return s & 0xff;
- }
-
- if (width) {
- netdev_info(dev, "using 8-bit IO window\n");
-
- smc91c92_suspend(link);
- pcmcia_fixup_iowidth(link);
- smc91c92_resume(link);
- return check_sig(link);
- }
- return -ENODEV;
-}
-
-static int smc91c92_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- char *name;
- int i, rev, j = 0;
- unsigned int ioaddr;
- u_long mir;
-
- dev_dbg(&link->dev, "smc91c92_config\n");
-
- smc->manfid = link->manf_id;
- smc->cardid = link->card_id;
-
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- i = osi_config(link);
- } else if ((smc->manfid == MANFID_MOTOROLA) ||
- ((smc->manfid == MANFID_MEGAHERTZ) &&
- ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
- (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
- i = mhz_mfc_config(link);
- } else {
- i = smc_config(link);
- }
- if (i)
- goto config_failed;
-
- i = pcmcia_request_irq(link, smc_interrupt);
- if (i)
- goto config_failed;
- i = pcmcia_enable_device(link);
- if (i)
- goto config_failed;
-
- if (smc->manfid == MANFID_MOTOROLA)
- mot_config(link);
-
- dev->irq = link->irq;
-
- if ((if_port >= 0) && (if_port <= 2))
- dev->if_port = if_port;
- else
- dev_notice(&link->dev, "invalid if_port requested\n");
-
- switch (smc->manfid) {
- case MANFID_OSITECH:
- case MANFID_PSION:
- i = osi_setup(link, smc->manfid, smc->cardid); break;
- case MANFID_SMC:
- case MANFID_NEW_MEDIA:
- i = smc_setup(link); break;
- case 0x128: /* For broken Megahertz cards */
- case MANFID_MEGAHERTZ:
- i = mhz_setup(link); break;
- case MANFID_MOTOROLA:
- default: /* get the hw address from EEPROM */
- i = mot_setup(link); break;
- }
-
- if (i != 0) {
- dev_notice(&link->dev, "Unable to find hardware address.\n");
- goto config_failed;
- }
-
- smc->duplex = 0;
- smc->rx_ovrn = 0;
-
- rev = check_sig(link);
- name = "???";
- if (rev > 0)
- switch (rev >> 4) {
- case 3: name = "92"; break;
- case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
- case 5: name = "95"; break;
- case 7: name = "100"; break;
- case 8: name = "100-FD"; break;
- case 9: name = "110"; break;
- }
-
- ioaddr = dev->base_addr;
- if (rev > 0) {
- u_long mcr;
- SMC_SELECT_BANK(0);
- mir = inw(ioaddr + MEMINFO) & 0xff;
- if (mir == 0xff) mir++;
- /* Get scale factor for memory size */
- mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
- mir *= 128 * (1<<((mcr >> 9) & 7));
- SMC_SELECT_BANK(1);
- smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
- smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
- if (smc->manfid == MANFID_OSITECH)
- smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
- if ((rev >> 4) >= 7)
- smc->cfg |= CFG_MII_SELECT;
- } else
- mir = 0;
-
- if (smc->cfg & CFG_MII_SELECT) {
- SMC_SELECT_BANK(3);
-
- for (i = 0; i < 32; i++) {
- j = mdio_read(dev, i, 1);
- if ((j != 0) && (j != 0xffff)) break;
- }
- smc->mii_if.phy_id = (i < 32) ? i : -1;
-
- SMC_SELECT_BANK(0);
- }
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- dev_err(&link->dev, "register_netdev() failed\n");
- goto config_undo;
- }
-
- netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n",
- name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr);
-
- if (rev > 0) {
- if (mir & 0x3ff)
- netdev_info(dev, " %lu byte", mir);
- else
- netdev_info(dev, " %lu kb", mir>>10);
- pr_cont(" buffer, %s xcvr\n",
- (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]);
- }
-
- if (smc->cfg & CFG_MII_SELECT) {
- if (smc->mii_if.phy_id != -1) {
- netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
- smc->mii_if.phy_id, j);
- } else {
- netdev_notice(dev, " No MII transceivers found!\n");
- }
- }
- return 0;
-
-config_undo:
- unregister_netdev(dev);
-config_failed:
- smc91c92_release(link);
- free_netdev(dev);
- return -ENODEV;
-} /* smc91c92_config */
-
-static void smc91c92_release(struct pcmcia_device *link)
-{
- dev_dbg(&link->dev, "smc91c92_release\n");
- if (link->resource[2]->end) {
- struct net_device *dev = link->priv;
- struct smc_private *smc = netdev_priv(dev);
- iounmap(smc->base);
- }
- pcmcia_disable_device(link);
-}
-
-/*======================================================================
-
- MII interface support for SMC91cXX based cards
-======================================================================*/
-
-#define MDIO_SHIFT_CLK 0x04
-#define MDIO_DATA_OUT 0x01
-#define MDIO_DIR_WRITE 0x08
-#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
-#define MDIO_DATA_READ 0x02
-
-static void mdio_sync(unsigned int addr)
-{
- int bits;
- for (bits = 0; bits < 32; bits++) {
- outb(MDIO_DATA_WRITE1, addr);
- outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
- }
-}
-
-static int mdio_read(struct net_device *dev, int phy_id, int loc)
-{
- unsigned int addr = dev->base_addr + MGMT;
- u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
- int i, retval = 0;
-
- mdio_sync(addr);
- for (i = 13; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(dat, addr);
- outb(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 19; i > 0; i--) {
- outb(0, addr);
- retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
- outb(MDIO_SHIFT_CLK, addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
-{
- unsigned int addr = dev->base_addr + MGMT;
- u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
- int i;
-
- mdio_sync(addr);
- for (i = 31; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(dat, addr);
- outb(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 1; i >= 0; i--) {
- outb(0, addr);
- outb(MDIO_SHIFT_CLK, addr);
- }
-}
-
-/*======================================================================
-
- The driver core code, most of which should be common with a
- non-PCMCIA implementation.
-
-======================================================================*/
-
-#ifdef PCMCIA_DEBUG
-static void smc_dump(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u_short i, w, save;
- save = inw(ioaddr + BANK_SELECT);
- for (w = 0; w < 4; w++) {
- SMC_SELECT_BANK(w);
- netdev_dbg(dev, "bank %d: ", w);
- for (i = 0; i < 14; i += 2)
- pr_cont(" %04x", inw(ioaddr + i));
- pr_cont("\n");
- }
- outw(save, ioaddr + BANK_SELECT);
-}
-#endif
-
-static int smc_open(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct pcmcia_device *link = smc->p_dev;
-
- dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
- dev->name, dev, inw(dev->base_addr + BANK_SELECT));
-#ifdef PCMCIA_DEBUG
- smc_dump(dev);
-#endif
-
- /* Check that the PCMCIA card is still here. */
- if (!pcmcia_dev_present(link))
- return -ENODEV;
- /* Physical device present signature. */
- if (check_sig(link) < 0) {
- netdev_info(dev, "Yikes! Bad chip signature!\n");
- return -ENODEV;
- }
- link->open++;
-
- netif_start_queue(dev);
- smc->saved_skb = NULL;
- smc->packets_waiting = 0;
-
- smc_reset(dev);
- timer_setup(&smc->media, media_check, 0);
- mod_timer(&smc->media, jiffies + HZ);
-
- return 0;
-} /* smc_open */
-
-/*====================================================================*/
-
-static int smc_close(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct pcmcia_device *link = smc->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
- dev->name, inw(ioaddr + BANK_SELECT));
-
- netif_stop_queue(dev);
-
- /* Shut off all interrupts, and turn off the Tx and Rx sections.
- Don't bother to check for chip present. */
- SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */
- outw(0, ioaddr + INTERRUPT);
- SMC_SELECT_BANK(0);
- mask_bits(0xff00, ioaddr + RCR);
- mask_bits(0xff00, ioaddr + TCR);
-
- /* Put the chip into power-down mode. */
- SMC_SELECT_BANK(1);
- outw(CTL_POWERDOWN, ioaddr + CONTROL );
-
- link->open--;
- timer_delete_sync(&smc->media);
-
- return 0;
-} /* smc_close */
-
-/*======================================================================
-
- Transfer a packet to the hardware and trigger the packet send.
- This may be called at either from either the Tx queue code
- or the interrupt handler.
-
-======================================================================*/
-
-static void smc_hardware_send_packet(struct net_device * dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct sk_buff *skb = smc->saved_skb;
- unsigned int ioaddr = dev->base_addr;
- u_char packet_no;
-
- if (!skb) {
- netdev_err(dev, "In XMIT with no packet to send\n");
- return;
- }
-
- /* There should be a packet slot waiting. */
- packet_no = inw(ioaddr + PNR_ARR) >> 8;
- if (packet_no & 0x80) {
- /* If not, there is a hardware problem! Likely an ejected card. */
- netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n",
- packet_no);
- dev_kfree_skb_irq(skb);
- smc->saved_skb = NULL;
- netif_start_queue(dev);
- return;
- }
-
- dev->stats.tx_bytes += skb->len;
- /* The card should use the just-allocated buffer. */
- outw(packet_no, ioaddr + PNR_ARR);
- /* point to the beginning of the packet */
- outw(PTR_AUTOINC , ioaddr + POINTER);
-
- /* Send the packet length (+6 for status, length and ctl byte)
- and the status word (set to zeros). */
- {
- u_char *buf = skb->data;
- u_int length = skb->len; /* The chip will pad to ethernet min. */
-
- netdev_dbg(dev, "Trying to xmit packet of length %d\n", length);
-
- /* send the packet length: +6 for status word, length, and ctl */
- outw(0, ioaddr + DATA_1);
- outw(length + 6, ioaddr + DATA_1);
- outsw(ioaddr + DATA_1, buf, length >> 1);
-
- /* The odd last byte, if there is one, goes in the control word. */
- outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
- }
-
- /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
- outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
- (inw(ioaddr + INTERRUPT) & 0xff00),
- ioaddr + INTERRUPT);
-
- /* The chip does the rest of the work. */
- outw(MC_ENQUEUE , ioaddr + MMU_CMD);
-
- smc->saved_skb = NULL;
- dev_kfree_skb_irq(skb);
- netif_trans_update(dev);
- netif_start_queue(dev);
-}
-
-/*====================================================================*/
-
-static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n",
- inw(ioaddr)&0xff, inw(ioaddr + 2));
- dev->stats.tx_errors++;
- smc_reset(dev);
- netif_trans_update(dev); /* prevent tx timeout */
- smc->saved_skb = NULL;
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short num_pages;
- short time_out, ir;
- unsigned long flags;
-
- netif_stop_queue(dev);
-
- netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n",
- skb->len, inw(ioaddr + 2));
-
- if (smc->saved_skb) {
- /* THIS SHOULD NEVER HAPPEN. */
- dev->stats.tx_aborted_errors++;
- netdev_dbg(dev, "Internal error -- sent packet while busy\n");
- return NETDEV_TX_BUSY;
- }
- smc->saved_skb = skb;
-
- num_pages = skb->len >> 8;
-
- if (num_pages > 7) {
- netdev_err(dev, "Far too big packet error: %d pages\n", num_pages);
- dev_kfree_skb (skb);
- smc->saved_skb = NULL;
- dev->stats.tx_dropped++;
- return NETDEV_TX_OK; /* Do not re-queue this packet. */
- }
- /* A packet is now waiting. */
- smc->packets_waiting++;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */
-
- /* need MC_RESET to keep the memory consistent. errata? */
- if (smc->rx_ovrn) {
- outw(MC_RESET, ioaddr + MMU_CMD);
- smc->rx_ovrn = 0;
- }
-
- /* Allocate the memory; send the packet now if we win. */
- outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
- for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
- ir = inw(ioaddr+INTERRUPT);
- if (ir & IM_ALLOC_INT) {
- /* Acknowledge the interrupt, send the packet. */
- outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
- smc_hardware_send_packet(dev); /* Send the packet now.. */
- spin_unlock_irqrestore(&smc->lock, flags);
- return NETDEV_TX_OK;
- }
- }
-
- /* Otherwise defer until the Tx-space-allocated interrupt. */
- netdev_dbg(dev, "memory allocation deferred.\n");
- outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
- spin_unlock_irqrestore(&smc->lock, flags);
-
- return NETDEV_TX_OK;
-}
-
-/*======================================================================
-
- Handle a Tx anomalous event. Entered while in Window 2.
-
-======================================================================*/
-
-static void smc_tx_err(struct net_device * dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
- int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
- int tx_status;
-
- /* select this as the packet to read from */
- outw(packet_no, ioaddr + PNR_ARR);
-
- /* read the first word from this packet */
- outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
-
- tx_status = inw(ioaddr + DATA_1);
-
- dev->stats.tx_errors++;
- if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++;
- if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++;
- if (tx_status & TS_16COL) {
- dev->stats.tx_aborted_errors++;
- smc->tx_err++;
- }
-
- if (tx_status & TS_SUCCESS) {
- netdev_notice(dev, "Successful packet caused error interrupt?\n");
- }
- /* re-enable transmit */
- SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
- SMC_SELECT_BANK(2);
-
- outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */
-
- /* one less packet waiting for me */
- smc->packets_waiting--;
-
- outw(saved_packet, ioaddr + PNR_ARR);
-}
-
-/*====================================================================*/
-
-static void smc_eph_irq(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short card_stats, ephs;
-
- SMC_SELECT_BANK(0);
- ephs = inw(ioaddr + EPH);
- netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n",
- ephs);
- /* Could be a counter roll-over warning: update stats. */
- card_stats = inw(ioaddr + COUNTER);
- /* single collisions */
- dev->stats.collisions += card_stats & 0xF;
- card_stats >>= 4;
- /* multiple collisions */
- dev->stats.collisions += card_stats & 0xF;
-#if 0 /* These are for when linux supports these statistics */
- card_stats >>= 4; /* deferred */
- card_stats >>= 4; /* excess deferred */
-#endif
- /* If we had a transmit error we must re-enable the transmitter. */
- outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
-
- /* Clear a link error interrupt. */
- SMC_SELECT_BANK(1);
- outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
- outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
- ioaddr + CONTROL);
- SMC_SELECT_BANK(2);
-}
-
-/*====================================================================*/
-
-static irqreturn_t smc_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr;
- u_short saved_bank, saved_pointer, mask, status;
- unsigned int handled = 1;
- char bogus_cnt = INTR_WORK; /* Work we are willing to do. */
-
- if (!netif_device_present(dev))
- return IRQ_NONE;
-
- ioaddr = dev->base_addr;
-
- netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n",
- irq, ioaddr);
-
- spin_lock(&smc->lock);
- smc->watchdog = 0;
- saved_bank = inw(ioaddr + BANK_SELECT);
- if ((saved_bank & 0xff00) != 0x3300) {
- /* The device does not exist -- the card could be off-line, or
- maybe it has been ejected. */
- netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n",
- irq);
- handled = 0;
- goto irq_done;
- }
-
- SMC_SELECT_BANK(2);
- saved_pointer = inw(ioaddr + POINTER);
- mask = inw(ioaddr + INTERRUPT) >> 8;
- /* clear all interrupts */
- outw(0, ioaddr + INTERRUPT);
-
- do { /* read the status flag, and mask it */
- status = inw(ioaddr + INTERRUPT) & 0xff;
- netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n",
- status, mask);
- if ((status & mask) == 0) {
- if (bogus_cnt == INTR_WORK)
- handled = 0;
- break;
- }
- if (status & IM_RCV_INT) {
- /* Got a packet(s). */
- smc_rx(dev);
- }
- if (status & IM_TX_INT) {
- smc_tx_err(dev);
- outw(IM_TX_INT, ioaddr + INTERRUPT);
- }
- status &= mask;
- if (status & IM_TX_EMPTY_INT) {
- outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
- mask &= ~IM_TX_EMPTY_INT;
- dev->stats.tx_packets += smc->packets_waiting;
- smc->packets_waiting = 0;
- }
- if (status & IM_ALLOC_INT) {
- /* Clear this interrupt so it doesn't happen again */
- mask &= ~IM_ALLOC_INT;
-
- smc_hardware_send_packet(dev);
-
- /* enable xmit interrupts based on this */
- mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
-
- /* and let the card send more packets to me */
- netif_wake_queue(dev);
- }
- if (status & IM_RX_OVRN_INT) {
- dev->stats.rx_errors++;
- dev->stats.rx_fifo_errors++;
- if (smc->duplex)
- smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
- outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
- }
- if (status & IM_EPH_INT)
- smc_eph_irq(dev);
- } while (--bogus_cnt);
-
- netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n",
- mask, saved_bank, saved_pointer);
-
- /* restore state register */
- outw((mask<<8), ioaddr + INTERRUPT);
- outw(saved_pointer, ioaddr + POINTER);
- SMC_SELECT_BANK(saved_bank);
-
- netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq);
-
-irq_done:
-
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN)) {
- /* Retrigger interrupt if needed */
- mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
- set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
- }
- if (smc->manfid == MANFID_MOTOROLA) {
- u_char cor;
- cor = readb(smc->base + MOT_UART + CISREG_COR);
- writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
- writeb(cor, smc->base + MOT_UART + CISREG_COR);
- cor = readb(smc->base + MOT_LAN + CISREG_COR);
- writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
- writeb(cor, smc->base + MOT_LAN + CISREG_COR);
- }
-
- if ((smc->base != NULL) && /* Megahertz MFC's */
- (smc->manfid == MANFID_MEGAHERTZ) &&
- (smc->cardid == PRODID_MEGAHERTZ_EM3288)) {
-
- u_char tmp;
- tmp = readb(smc->base+MEGAHERTZ_ISR);
- tmp = readb(smc->base+MEGAHERTZ_ISR);
-
- /* Retrigger interrupt if needed */
- writeb(tmp, smc->base + MEGAHERTZ_ISR);
- writeb(tmp, smc->base + MEGAHERTZ_ISR);
- }
-
- spin_unlock(&smc->lock);
- return IRQ_RETVAL(handled);
-}
-
-/*====================================================================*/
-
-static void smc_rx(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int rx_status;
- int packet_length; /* Caution: not frame length, rather words
- to transfer from the chip. */
-
- /* Assertion: we are in Window 2. */
-
- if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
- netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n");
- return;
- }
-
- /* Reset the read pointer, and read the status and packet length. */
- outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
- rx_status = inw(ioaddr + DATA_1);
- packet_length = inw(ioaddr + DATA_1) & 0x07ff;
-
- netdev_dbg(dev, "Receive status %4.4x length %d.\n",
- rx_status, packet_length);
-
- if (!(rx_status & RS_ERRORS)) {
- /* do stuff to make a new packet */
- struct sk_buff *skb;
- struct smc_private *smc = netdev_priv(dev);
-
- /* Note: packet_length adds 5 or 6 extra bytes here! */
- skb = netdev_alloc_skb(dev, packet_length+2);
-
- if (skb == NULL) {
- netdev_dbg(dev, "Low memory, packet dropped.\n");
- dev->stats.rx_dropped++;
- outw(MC_RELEASE, ioaddr + MMU_CMD);
- return;
- }
-
- packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
- skb_reserve(skb, 2);
- insw(ioaddr+DATA_1, skb_put(skb, packet_length),
- (packet_length+1)>>1);
- skb->protocol = eth_type_trans(skb, dev);
-
- netif_rx(skb);
- smc->last_rx = jiffies;
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += packet_length;
- if (rx_status & RS_MULTICAST)
- dev->stats.multicast++;
- } else {
- /* error ... */
- dev->stats.rx_errors++;
-
- if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++;
- if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
- dev->stats.rx_length_errors++;
- if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++;
- }
- /* Let the MMU free the memory of this packet. */
- outw(MC_RELEASE, ioaddr + MMU_CMD);
-}
-
-/*======================================================================
-
- Set the receive mode.
-
- This routine is used by both the protocol level to notify us of
- promiscuous/multicast mode changes, and by the open/reset code to
- initialize the Rx registers. We always set the multicast list and
- leave the receiver running.
-
-======================================================================*/
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
- unsigned char multicast_table[8];
- unsigned long flags;
- u_short rx_cfg_setting;
- int i;
-
- memset(multicast_table, 0, sizeof(multicast_table));
-
- if (dev->flags & IFF_PROMISC) {
- rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
- } else if (dev->flags & IFF_ALLMULTI)
- rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
- else {
- if (!netdev_mc_empty(dev)) {
- struct netdev_hw_addr *ha;
-
- netdev_for_each_mc_addr(ha, dev) {
- u_int position = ether_crc(6, ha->addr);
- multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
- }
- }
- rx_cfg_setting = RxStripCRC | RxEnable;
- }
-
- /* Load MC table and Rx setting into the chip without interrupts. */
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- for (i = 0; i < 8; i++)
- outb(multicast_table[i], ioaddr + MULTICAST0 + i);
- SMC_SELECT_BANK(0);
- outw(rx_cfg_setting, ioaddr + RCR);
- SMC_SELECT_BANK(2);
- spin_unlock_irqrestore(&smc->lock, flags);
-}
-
-/*======================================================================
-
- Senses when a card's config changes. Here, it's coax or TP.
-
-======================================================================*/
-
-static int s9k_config(struct net_device *dev, struct ifmap *map)
-{
- struct smc_private *smc = netdev_priv(dev);
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (smc->cfg & CFG_MII_SELECT)
- return -EOPNOTSUPP;
- else if (map->port > 2)
- return -EINVAL;
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- smc_reset(dev);
- }
- return 0;
-}
-
-/*======================================================================
-
- Reset the chip, reloading every register that might be corrupted.
-
-======================================================================*/
-
-/*
- Set transceiver type, perhaps to something other than what the user
- specified in dev->if_port.
-*/
-static void smc_set_xcvr(struct net_device *dev, int if_port)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u_short saved_bank;
-
- saved_bank = inw(ioaddr + BANK_SELECT);
- SMC_SELECT_BANK(1);
- if (if_port == 2) {
- outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
- smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
- } else {
- outw(smc->cfg, ioaddr + CONFIG);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
- smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
- }
- SMC_SELECT_BANK(saved_bank);
-}
-
-static void smc_reset(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
- int i;
-
- netdev_dbg(dev, "smc91c92 reset called.\n");
-
- /* The first interaction must be a write to bring the chip out
- of sleep mode. */
- SMC_SELECT_BANK(0);
- /* Reset the chip. */
- outw(RCR_SOFTRESET, ioaddr + RCR);
- udelay(10);
-
- /* Clear the transmit and receive configuration registers. */
- outw(RCR_CLEAR, ioaddr + RCR);
- outw(TCR_CLEAR, ioaddr + TCR);
-
- /* Set the Window 1 control, configuration and station addr registers.
- No point in writing the I/O base register ;-> */
- SMC_SELECT_BANK(1);
- /* Automatically release successfully transmitted packets,
- Accept link errors, counter and Tx error interrupts. */
- outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
- ioaddr + CONTROL);
- smc_set_xcvr(dev, dev->if_port);
- if ((smc->manfid == MANFID_OSITECH) &&
- (smc->cardid != PRODID_OSITECH_SEVEN))
- outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
- (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
- ioaddr - 0x10 + OSITECH_AUI_PWR);
-
- /* Fill in the physical address. The databook is wrong about the order! */
- for (i = 0; i < 6; i += 2)
- outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
- ioaddr + ADDR0 + i);
-
- /* Reset the MMU */
- SMC_SELECT_BANK(2);
- outw(MC_RESET, ioaddr + MMU_CMD);
- outw(0, ioaddr + INTERRUPT);
-
- /* Re-enable the chip. */
- SMC_SELECT_BANK(0);
- outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
- TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
- set_rx_mode(dev);
-
- if (smc->cfg & CFG_MII_SELECT) {
- SMC_SELECT_BANK(3);
-
- /* Reset MII */
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
-
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
-
- /* Restart MII autonegotiation */
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
- mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
- }
-
- /* Enable interrupts. */
- SMC_SELECT_BANK(2);
- outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
- ioaddr + INTERRUPT);
-}
-
-/*======================================================================
-
- Media selection timer routine
-
-======================================================================*/
-
-static void media_check(struct timer_list *t)
-{
- struct smc_private *smc = timer_container_of(smc, t, media);
- struct net_device *dev = smc->mii_if.dev;
- unsigned int ioaddr = dev->base_addr;
- u_short i, media, saved_bank;
- u_short link;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
-
- saved_bank = inw(ioaddr + BANK_SELECT);
-
- if (!netif_device_present(dev))
- goto reschedule;
-
- SMC_SELECT_BANK(2);
-
- /* need MC_RESET to keep the memory consistent. errata? */
- if (smc->rx_ovrn) {
- outw(MC_RESET, ioaddr + MMU_CMD);
- smc->rx_ovrn = 0;
- }
- i = inw(ioaddr + INTERRUPT);
- SMC_SELECT_BANK(0);
- media = inw(ioaddr + EPH) & EPH_LINK_OK;
- SMC_SELECT_BANK(1);
- media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
-
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
-
- /* Check for pending interrupt with watchdog flag set: with
- this, we can limp along even if the interrupt is blocked */
- if (smc->watchdog++ && ((i>>8) & i)) {
- if (!smc->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
- local_irq_save(flags);
- smc_interrupt(dev->irq, dev);
- local_irq_restore(flags);
- smc->fast_poll = HZ;
- }
- if (smc->fast_poll) {
- smc->fast_poll--;
- smc->media.expires = jiffies + HZ/100;
- add_timer(&smc->media);
- return;
- }
-
- spin_lock_irqsave(&smc->lock, flags);
-
- saved_bank = inw(ioaddr + BANK_SELECT);
-
- if (smc->cfg & CFG_MII_SELECT) {
- if (smc->mii_if.phy_id < 0)
- goto reschedule;
-
- SMC_SELECT_BANK(3);
- link = mdio_read(dev, smc->mii_if.phy_id, 1);
- if (!link || (link == 0xffff)) {
- netdev_info(dev, "MII is missing!\n");
- smc->mii_if.phy_id = -1;
- goto reschedule;
- }
-
- link &= 0x0004;
- if (link != smc->link_status) {
- u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
- netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
- smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
- ? TCR_FDUPLX : 0);
- if (link) {
- netdev_info(dev, "autonegotiation complete: "
- "%dbaseT-%cD selected\n",
- (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H');
- }
- SMC_SELECT_BANK(0);
- outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
- smc->link_status = link;
- }
- goto reschedule;
- }
-
- /* Ignore collisions unless we've had no rx's recently */
- if (time_after(jiffies, smc->last_rx + HZ)) {
- if (smc->tx_err || (smc->media_status & EPH_16COL))
- media |= EPH_16COL;
- }
- smc->tx_err = 0;
-
- if (media != smc->media_status) {
- if ((media & smc->media_status & 1) &&
- ((smc->media_status ^ media) & EPH_LINK_OK))
- netdev_info(dev, "%s link beat\n",
- smc->media_status & EPH_LINK_OK ? "lost" : "found");
- else if ((media & smc->media_status & 2) &&
- ((smc->media_status ^ media) & EPH_16COL))
- netdev_info(dev, "coax cable %s\n",
- media & EPH_16COL ? "problem" : "ok");
- if (dev->if_port == 0) {
- if (media & 1) {
- if (media & EPH_LINK_OK)
- netdev_info(dev, "flipped to 10baseT\n");
- else
- smc_set_xcvr(dev, 2);
- } else {
- if (media & EPH_16COL)
- smc_set_xcvr(dev, 1);
- else
- netdev_info(dev, "flipped to 10base2\n");
- }
- }
- smc->media_status = media;
- }
-
-reschedule:
- smc->media.expires = jiffies + HZ;
- add_timer(&smc->media);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
-}
-
-static int smc_link_ok(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- struct smc_private *smc = netdev_priv(dev);
-
- if (smc->cfg & CFG_MII_SELECT) {
- return mii_link_ok(&smc->mii_if);
- } else {
- SMC_SELECT_BANK(0);
- return inw(ioaddr + EPH) & EPH_LINK_OK;
- }
-}
-
-static void smc_netdev_get_ecmd(struct net_device *dev,
- struct ethtool_link_ksettings *ecmd)
-{
- u16 tmp;
- unsigned int ioaddr = dev->base_addr;
- u32 supported;
-
- supported = (SUPPORTED_TP | SUPPORTED_AUI |
- SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
-
- SMC_SELECT_BANK(1);
- tmp = inw(ioaddr + CONFIG);
- ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
- ecmd->base.speed = SPEED_10;
- ecmd->base.phy_address = ioaddr + MGMT;
-
- SMC_SELECT_BANK(0);
- tmp = inw(ioaddr + TCR);
- ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
-
- ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported,
- supported);
-}
-
-static int smc_netdev_set_ecmd(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- u16 tmp;
- unsigned int ioaddr = dev->base_addr;
-
- if (ecmd->base.speed != SPEED_10)
- return -EINVAL;
- if (ecmd->base.duplex != DUPLEX_HALF &&
- ecmd->base.duplex != DUPLEX_FULL)
- return -EINVAL;
- if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI)
- return -EINVAL;
-
- if (ecmd->base.port == PORT_AUI)
- smc_set_xcvr(dev, 1);
- else
- smc_set_xcvr(dev, 0);
-
- SMC_SELECT_BANK(0);
- tmp = inw(ioaddr + TCR);
- if (ecmd->base.duplex == DUPLEX_FULL)
- tmp |= TCR_FDUPLX;
- else
- tmp &= ~TCR_FDUPLX;
- outw(tmp, ioaddr + TCR);
-
- return 0;
-}
-
-static int check_if_running(struct net_device *dev)
-{
- if (!netif_running(dev))
- return -EINVAL;
- return 0;
-}
-
-static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- strscpy(info->version, DRV_VERSION, sizeof(info->version));
-}
-
-static int smc_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *ecmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- if (smc->cfg & CFG_MII_SELECT)
- mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd);
- else
- smc_netdev_get_ecmd(dev, ecmd);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return 0;
-}
-
-static int smc_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *ecmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- if (smc->cfg & CFG_MII_SELECT)
- ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd);
- else
- ret = smc_netdev_set_ecmd(dev, ecmd);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return ret;
-}
-
-static u32 smc_get_link(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- u32 ret;
- unsigned long flags;
-
- spin_lock_irqsave(&smc->lock, flags);
- SMC_SELECT_BANK(3);
- ret = smc_link_ok(dev);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return ret;
-}
-
-static int smc_nway_reset(struct net_device *dev)
-{
- struct smc_private *smc = netdev_priv(dev);
- if (smc->cfg & CFG_MII_SELECT) {
- unsigned int ioaddr = dev->base_addr;
- u16 saved_bank = inw(ioaddr + BANK_SELECT);
- int res;
-
- SMC_SELECT_BANK(3);
- res = mii_nway_restart(&smc->mii_if);
- SMC_SELECT_BANK(saved_bank);
-
- return res;
- } else
- return -EOPNOTSUPP;
-}
-
-static const struct ethtool_ops ethtool_ops = {
- .begin = check_if_running,
- .get_drvinfo = smc_get_drvinfo,
- .get_link = smc_get_link,
- .nway_reset = smc_nway_reset,
- .get_link_ksettings = smc_get_link_ksettings,
- .set_link_ksettings = smc_set_link_ksettings,
-};
-
-static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct smc_private *smc = netdev_priv(dev);
- struct mii_ioctl_data *mii = if_mii(rq);
- int rc = 0;
- u16 saved_bank;
- unsigned int ioaddr = dev->base_addr;
- unsigned long flags;
-
- if (!netif_running(dev))
- return -EINVAL;
-
- spin_lock_irqsave(&smc->lock, flags);
- saved_bank = inw(ioaddr + BANK_SELECT);
- SMC_SELECT_BANK(3);
- rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
- SMC_SELECT_BANK(saved_bank);
- spin_unlock_irqrestore(&smc->lock, flags);
- return rc;
-}
-
-static const struct pcmcia_device_id smc91c92_ids[] = {
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
- PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
- PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020),
- PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023),
- PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb),
- PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc),
- PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5),
- PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9),
- PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953),
- PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a),
- PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314),
- PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc),
- PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9),
- PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d),
- /* These conflict with other cards! */
- /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */
- /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids);
-
-static struct pcmcia_driver smc91c92_cs_driver = {
- .owner = THIS_MODULE,
- .name = "smc91c92_cs",
- .probe = smc91c92_probe,
- .remove = smc91c92_detach,
- .id_table = smc91c92_ids,
- .suspend = smc91c92_suspend,
- .resume = smc91c92_resume,
-};
-module_pcmcia_driver(smc91c92_cs_driver);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 09/15] drivers: net: cirrus: cs89x0: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (7 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 08/15] drivers: net: smsc: smc91c92: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: " Andrew Lunn
` (5 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The cs89x0 was written by Bonald Becker 1993 to 1994. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Remove documentation as well
---
.../device_drivers/ethernet/cirrus/cs89x0.rst | 647 -------
drivers/net/ethernet/cirrus/Kconfig | 30 -
drivers/net/ethernet/cirrus/Makefile | 1 -
drivers/net/ethernet/cirrus/cs89x0.c | 1915 --------------------
4 files changed, 2593 deletions(-)
diff --git a/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst b/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst
deleted file mode 100644
index e5c283940ac5..000000000000
--- a/Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst
+++ /dev/null
@@ -1,647 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-================================================
-Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
-================================================
-
-.. note::
-
- This document was contributed by Cirrus Logic for kernel 2.2.5. This version
- has been updated for 2.3.48 by Andrew Morton.
-
- Still, this is too outdated! A major cleanup is needed here.
-
-Cirrus make a copy of this driver available at their website, as
-described below. In general, you should use the driver version which
-comes with your Linux distribution.
-
-
-Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>
-
-
-.. TABLE OF CONTENTS
-
- 1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
- 1.1 Product Overview
- 1.2 Driver Description
- 1.2.1 Driver Name
- 1.2.2 File in the Driver Package
- 1.3 System Requirements
- 1.4 Licensing Information
-
- 2.0 ADAPTER INSTALLATION and CONFIGURATION
- 2.1 CS8900-based Adapter Configuration
- 2.2 CS8920-based Adapter Configuration
-
- 3.0 LOADING THE DRIVER AS A MODULE
-
- 4.0 COMPILING THE DRIVER
- 4.1 Compiling the Driver as a Loadable Module
- 4.2 Compiling the driver to support memory mode
- 4.3 Compiling the driver to support Rx DMA
-
- 5.0 TESTING AND TROUBLESHOOTING
- 5.1 Known Defects and Limitations
- 5.2 Testing the Adapter
- 5.2.1 Diagnostic Self-Test
- 5.2.2 Diagnostic Network Test
- 5.3 Using the Adapter's LEDs
- 5.4 Resolving I/O Conflicts
-
- 6.0 TECHNICAL SUPPORT
- 6.1 Contacting Cirrus Logic's Technical Support
- 6.2 Information Required Before Contacting Technical Support
- 6.3 Obtaining the Latest Driver Version
- 6.4 Current maintainer
- 6.5 Kernel boot parameters
-
-
-1. Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
-===================================================
-
-
-1.1. Product Overview
-=====================
-
-The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow
-IEEE 802.3 standards and support half or full-duplex operation in ISA bus
-computers on 10 Mbps Ethernet networks. The adapters are designed for operation
-in 16-bit ISA or EISA bus expansion slots and are available in
-10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5
-or fiber networks).
-
-CS8920-based adapters are similar to the CS8900-based adapter with additional
-features for Plug and Play (PnP) support and Wakeup Frame recognition. As
-such, the configuration procedures differ somewhat between the two types of
-adapters. Refer to the "Adapter Configuration" section for details on
-configuring both types of adapters.
-
-
-1.2. Driver Description
-=======================
-
-The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux
-v2.3.48 or greater kernel. It can be compiled directly into the kernel
-or loaded at run-time as a device driver module.
-
-1.2.1 Driver Name: cs89x0
-
-1.2.2 Files in the Driver Archive:
-
-The files in the driver at Cirrus' website include:
-
- =================== ====================================================
- readme.txt this file
- build batch file to compile cs89x0.c.
- cs89x0.c driver C code
- cs89x0.h driver header file
- cs89x0.o pre-compiled module (for v2.2.5 kernel)
- config/Config.in sample file to include cs89x0 driver in the kernel.
- config/Makefile sample file to include cs89x0 driver in the kernel.
- config/Space.c sample file to include cs89x0 driver in the kernel.
- =================== ====================================================
-
-
-
-1.3. System Requirements
-------------------------
-
-The following hardware is required:
-
- * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter
-
- * IBM or IBM-compatible PC with:
- * An 80386 or higher processor
- * 16 bytes of contiguous IO space available between 210h - 370h
- * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
-
- * Appropriate cable (and connector for AUI, 10BASE-2) for your network
- topology.
-
-The following software is required:
-
-* LINUX kernel version 2.3.48 or higher
-
- * CS8900/20 Setup Utility (DOS-based)
-
- * LINUX kernel sources for your kernel (if compiling into kernel)
-
- * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel
- or a module)
-
-
-
-1.4. Licensing Information
---------------------------
-
-This program is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, version 1.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-more details.
-
-For a full copy of the GNU General Public License, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
-
-2. Adapter Installation and Configuration
-=========================================
-
-Both the CS8900 and CS8920-based adapters can be configured using parameters
-stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup
-Utility if you want to change the adapter's configuration in EEPROM.
-
-When loading the driver as a module, you can specify many of the adapter's
-configuration parameters on the command-line to override the EEPROM's settings
-or for interface configuration when an EEPROM is not used. (CS8920-based
-adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
-
-Since the CS8900/20 Setup Utility is a DOS-based application, you must install
-and configure the adapter in a DOS-based system using the CS8900/20 Setup
-Utility before installation in the target LINUX system. (Not required if
-installing a CS8900-based adapter and the default configuration is acceptable.)
-
-
-2.1. CS8900-based Adapter Configuration
----------------------------------------
-
-CS8900-based adapters shipped from Cirrus Logic have been configured
-with the following "default" settings::
-
- Operation Mode: Memory Mode
- IRQ: 10
- Base I/O Address: 300
- Memory Base Address: D0000
- Optimization: DOS Client
- Transmission Mode: Half-duplex
- BootProm: None
- Media Type: Autodetect (3-media cards) or
- 10BASE-T (10BASE-T only adapter)
-
-You should only change the default configuration settings if conflicts with
-another adapter exists. To change the adapter's configuration, run the
-CS8900/20 Setup Utility.
-
-
-2.2. CS8920-based Adapter Configuration
----------------------------------------
-
-CS8920-based adapters are shipped from Cirrus Logic configured as Plug
-and Play (PnP) enabled. However, since the cs89x0 driver does NOT
-support PnP, you must install the CS8920 adapter in a DOS-based PC and
-run the CS8900/20 Setup Utility to disable PnP and configure the
-adapter before installation in the target Linux system. Failure to do
-this will leave the adapter inactive and the driver will be unable to
-communicate with the adapter.
-
-::
-
- ****************************************************************
- * CS8920-BASED ADAPTERS: *
- * *
- * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT. *
- * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST *
- * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND *
- * TO ACTIVATE THE ADAPTER. *
- ****************************************************************
-
-
-
-
-3. Loading the Driver as a Module
-=================================
-
-If the driver is compiled as a loadable module, you can load the driver module
-with the 'modprobe' command. Many of the adapter's configuration parameters can
-be specified as command-line arguments to the load command. This facility
-provides a means to override the EEPROM's settings or for interface
-configuration when an EEPROM is not used.
-
-Example::
-
- insmod cs89x0.o io=0x200 irq=0xA media=aui
-
-This example loads the module and configures the adapter to use an IO port base
-address of 200h, interrupt 10, and use the AUI media connection. The following
-configuration options are available on the command line::
-
- io=### - specify IO address (200h-360h)
- irq=## - specify interrupt level
- use_dma=1 - Enable DMA
- dma=# - specify dma channel (Driver is compiled to support
- Rx DMA only)
- dmasize=# (16 or 64) - DMA size 16K or 64K. Default value is set to 16.
- media=rj45 - specify media type
- or media=bnc
- or media=aui
- or media=auto
- duplex=full - specify forced half/full/autonegotiate duplex
- or duplex=half
- or duplex=auto
- debug=# - debug level (only available if the driver was compiled
- for debugging)
-
-**Notes:**
-
-a) If an EEPROM is present, any specified command-line parameter
- will override the corresponding configuration value stored in
- EEPROM.
-
-b) The "io" parameter must be specified on the command-line.
-
-c) The driver's hardware probe routine is designed to avoid
- writing to I/O space until it knows that there is a cs89x0
- card at the written addresses. This could cause problems
- with device probing. To avoid this behaviour, add one
- to the ``io=`` module parameter. This doesn't actually change
- the I/O address, but it is a flag to tell the driver
- to partially initialise the hardware before trying to
- identify the card. This could be dangerous if you are
- not sure that there is a cs89x0 card at the provided address.
-
- For example, to scan for an adapter located at IO base 0x300,
- specify an IO address of 0x301.
-
-d) The "duplex=auto" parameter is only supported for the CS8920.
-
-e) The minimum command-line configuration required if an EEPROM is
- not present is:
-
- io
- irq
- media type (no autodetect)
-
-f) The following additional parameters are CS89XX defaults (values
- used with no EEPROM or command-line argument).
-
- * DMA Burst = enabled
- * IOCHRDY Enabled = enabled
- * UseSA = enabled
- * CS8900 defaults to half-duplex if not specified on command-line
- * CS8920 defaults to autoneg if not specified on command-line
- * Use reset defaults for other config parameters
- * dma_mode = 0
-
-g) You can use ifconfig to set the adapter's Ethernet address.
-
-h) Many Linux distributions use the 'modprobe' command to load
- modules. This program uses the '/etc/conf.modules' file to
- determine configuration information which is passed to a driver
- module when it is loaded. All the configuration options which are
- described above may be placed within /etc/conf.modules.
-
- For example::
-
- > cat /etc/conf.modules
- ...
- alias eth0 cs89x0
- options cs89x0 io=0x0200 dma=5 use_dma=1
- ...
-
- In this example we are telling the module system that the
- ethernet driver for this machine should use the cs89x0 driver. We
- are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'
- arguments to the driver when it is loaded.
-
-i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or
- 7. You will probably find that other DMA channels will not work.
-
-j) The cs89x0 supports DMA for receiving only. DMA mode is
- significantly more efficient. Flooding a 400 MHz Celeron machine
- with large ping packets consumes 82% of its CPU capacity in non-DMA
- mode. With DMA this is reduced to 45%.
-
-k) If your Linux kernel was compiled with inbuilt plug-and-play
- support you will be able to find information about the cs89x0 card
- with the command::
-
- cat /proc/isapnp
-
-l) If during DMA operation you find erratic behavior or network data
- corruption you should use your PC's BIOS to slow the EISA bus clock.
-
-m) If the cs89x0 driver is compiled directly into the kernel
- (non-modular) then its I/O address is automatically determined by
- ISA bus probing. The IRQ number, media options, etc are determined
- from the card's EEPROM.
-
-n) If the cs89x0 driver is compiled directly into the kernel, DMA
- mode may be selected by providing the kernel with a boot option
- 'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7).
-
- Kernel boot options may be provided on the LILO command line::
-
- LILO boot: linux cs89x0_dma=5
-
- or they may be placed in /etc/lilo.conf::
-
- image=/boot/bzImage-2.3.48
- append="cs89x0_dma=5"
- label=linux
- root=/dev/hda5
- read-only
-
- The DMA Rx buffer size is hardwired to 16 kbytes in this mode.
- (64k mode is not available).
-
-
-4. Compiling the Driver
-=======================
-
-The cs89x0 driver can be compiled directly into the kernel or compiled into
-a loadable device driver module.
-
-Just use the standard way to configure the driver and compile the Kernel.
-
-
-4.1. Compiling the Driver to Support Rx DMA
--------------------------------------------
-
-The compile-time optionality for DMA was removed in the 2.3 kernel
-series. DMA support is now unconditionally part of the driver. It is
-enabled by the 'use_dma=1' module option.
-
-
-5. Testing and Troubleshooting
-==============================
-
-5.1. Known Defects and Limitations
-----------------------------------
-
-Refer to the RELEASE.TXT file distributed as part of this archive for a list of
-known defects, driver limitations, and work arounds.
-
-
-5.2. Testing the Adapter
-------------------------
-
-Once the adapter has been installed and configured, the diagnostic option of
-the CS8900/20 Setup Utility can be used to test the functionality of the
-adapter and its network connection. Use the diagnostics 'Self Test' option to
-test the functionality of the adapter with the hardware configuration you have
-assigned. You can use the diagnostics 'Network Test' to test the ability of the
-adapter to communicate across the Ethernet with another PC equipped with a
-CS8900/20-based adapter card (it must also be running the CS8900/20 Setup
-Utility).
-
-.. note::
-
- The Setup Utility's diagnostics are designed to run in a
- DOS-only operating system environment. DO NOT run the diagnostics
- from a DOS or command prompt session under Windows 95, Windows NT,
- OS/2, or other operating system.
-
-To run the diagnostics tests on the CS8900/20 adapter:
-
- 1. Boot DOS on the PC and start the CS8900/20 Setup Utility.
-
- 2. The adapter's current configuration is displayed. Hit the ENTER key to
- get to the main menu.
-
- 4. Select 'Diagnostics' (ALT-G) from the main menu.
- * Select 'Self-Test' to test the adapter's basic functionality.
- * Select 'Network Test' to test the network connection and cabling.
-
-
-5.2.1. Diagnostic Self-test
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The diagnostic self-test checks the adapter's basic functionality as well as
-its ability to communicate across the ISA bus based on the system resources
-assigned during hardware configuration. The following tests are performed:
-
- * IO Register Read/Write Test
-
- The IO Register Read/Write test insures that the CS8900/20 can be
- accessed in IO mode, and that the IO base address is correct.
-
- * Shared Memory Test
-
- The Shared Memory test insures the CS8900/20 can be accessed in memory
- mode and that the range of memory addresses assigned does not conflict
- with other devices in the system.
-
- * Interrupt Test
-
- The Interrupt test insures there are no conflicts with the assigned IRQ
- signal.
-
- * EEPROM Test
-
- The EEPROM test insures the EEPROM can be read.
-
- * Chip RAM Test
-
- The Chip RAM test insures the 4K of memory internal to the CS8900/20 is
- working properly.
-
- * Internal Loop-back Test
-
- The Internal Loop Back test insures the adapter's transmitter and
- receiver are operating properly. If this test fails, make sure the
- adapter's cable is connected to the network (check for LED activity for
- example).
-
- * Boot PROM Test
-
- The Boot PROM test insures the Boot PROM is present, and can be read.
- Failure indicates the Boot PROM was not successfully read due to a
- hardware problem or due to a conflicts on the Boot PROM address
- assignment. (Test only applies if the adapter is configured to use the
- Boot PROM option.)
-
-Failure of a test item indicates a possible system resource conflict with
-another device on the ISA bus. In this case, you should use the Manual Setup
-option to reconfigure the adapter by selecting a different value for the system
-resource that failed.
-
-
-5.2.2. Diagnostic Network Test
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The Diagnostic Network Test verifies a working network connection by
-transferring data between two CS8900/20 adapters installed in different PCs
-on the same network. (Note: the diagnostic network test should not be run
-between two nodes across a router.)
-
-This test requires that each of the two PCs have a CS8900/20-based adapter
-installed and have the CS8900/20 Setup Utility running. The first PC is
-configured as a Responder and the other PC is configured as an Initiator.
-Once the Initiator is started, it sends data frames to the Responder which
-returns the frames to the Initiator.
-
-The total number of frames received and transmitted are displayed on the
-Initiator's display, along with a count of the number of frames received and
-transmitted OK or in error. The test can be terminated anytime by the user at
-either PC.
-
-To setup the Diagnostic Network Test:
-
- 1. Select a PC with a CS8900/20-based adapter and a known working network
- connection to act as the Responder. Run the CS8900/20 Setup Utility
- and select 'Diagnostics -> Network Test -> Responder' from the main
- menu. Hit ENTER to start the Responder.
-
- 2. Return to the PC with the CS8900/20-based adapter you want to test and
- start the CS8900/20 Setup Utility.
-
- 3. From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
- Hit ENTER to start the test.
-
-You may stop the test on the Initiator at any time while allowing the Responder
-to continue running. In this manner, you can move to additional PCs and test
-them by starting the Initiator on another PC without having to stop/start the
-Responder.
-
-
-
-5.3. Using the Adapter's LEDs
------------------------------
-
-The 2 and 3-media adapters have two LEDs visible on the back end of the board
-located near the 10Base-T connector.
-
-Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T
-connection. (Only applies to 10Base-T. The green LED has no significance for
-a 10Base-2 or AUI connection.)
-
-TX/RX LED: The yellow LED lights briefly each time the adapter transmits or
-receives data. (The yellow LED will appear to "flicker" on a typical network.)
-
-
-5.4. Resolving I/O Conflicts
-----------------------------
-
-An IO conflict occurs when two or more adapter use the same ISA resource (IO
-address, memory address or IRQ). You can usually detect an IO conflict in one
-of four ways after installing and or configuring the CS8900/20-based adapter:
-
- 1. The system does not boot properly (or at all).
-
- 2. The driver cannot communicate with the adapter, reporting an "Adapter
- not found" error message.
-
- 3. You cannot connect to the network or the driver will not load.
-
- 4. If you have configured the adapter to run in memory mode but the driver
- reports it is using IO mode when loading, this is an indication of a
- memory address conflict.
-
-If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a
-diagnostic self-test. Normally, the ISA resource in conflict will fail the
-self-test. If so, reconfigure the adapter selecting another choice for the
-resource in conflict. Run the diagnostics again to check for further IO
-conflicts.
-
-In some cases, such as when the PC will not boot, it may be necessary to remove
-the adapter and reconfigure it by installing it in another PC to run the
-CS8900/20 Setup Utility. Once reinstalled in the target system, run the
-diagnostics self-test to ensure the new configuration is free of conflicts
-before loading the driver again.
-
-When manually configuring the adapter, keep in mind the typical ISA system
-resource usage as indicated in the tables below.
-
-::
-
- I/O Address Device IRQ Device
- ----------- -------- --- --------
- 200-20F Game I/O adapter 3 COM2, Bus Mouse
- 230-23F Bus Mouse 4 COM1
- 270-27F LPT3: third parallel port 5 LPT2
- 2F0-2FF COM2: second serial port 6 Floppy Disk controller
- 320-32F Fixed disk controller 7 LPT1
- 8 Real-time Clock
- 9 EGA/VGA display adapter
- 12 Mouse (PS/2)
- Memory Address Device 13 Math Coprocessor
- -------------- --------------------- 14 Hard Disk controller
- A000-BFFF EGA Graphics Adapter
- A000-C7FF VGA Graphics Adapter
- B000-BFFF Mono Graphics Adapter
- B800-BFFF Color Graphics Adapter
- E000-FFFF AT BIOS
-
-
-
-
-6. Technical Support
-====================
-
-6.1. Contacting Cirrus Logic's Technical Support
-------------------------------------------------
-
-Cirrus Logic's CS89XX Technical Application Support can be reached at::
-
- Telephone :(800) 888-5016 (from inside U.S. and Canada)
- :(512) 442-7555 (from outside the U.S. and Canada)
- Fax :(512) 912-3871
- Email :ethernet@crystal.cirrus.com
- WWW :http://www.cirrus.com
-
-
-6.2. Information Required before Contacting Technical Support
--------------------------------------------------------------
-
-Before contacting Cirrus Logic for technical support, be prepared to provide as
-Much of the following information as possible.
-
-1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
-
-2.) Adapter configuration
-
- * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
- * Plug and Play enabled/disabled (CS8920-based adapters only)
- * Configured for media auto-detect or specific media type (which type).
-
-3.) PC System's Configuration
-
- * Plug and Play system (yes/no)
- * BIOS (make and version)
- * System make and model
- * CPU (type and speed)
- * System RAM
- * SCSI Adapter
-
-4.) Software
-
- * CS89XX driver and version
- * Your network operating system and version
- * Your system's OS version
- * Version of all protocol support files
-
-5.) Any Error Message displayed.
-
-
-
-6.3 Obtaining the Latest Driver Version
----------------------------------------
-
-You can obtain the latest CS89XX drivers and support software from Cirrus Logic's
-Web site. You can also contact Cirrus Logic's Technical Support (email:
-ethernet@crystal.cirrus.com) and request that you be registered for automatic
-software-update notification.
-
-Cirrus Logic maintains a web page at http://www.cirrus.com with the
-latest drivers and technical publications.
-
-
-6.4. Current maintainer
------------------------
-
-In February 2000 the maintenance of this driver was assumed by Andrew
-Morton.
-
-6.5 Kernel module parameters
-----------------------------
-
-For use in embedded environments with no cs89x0 EEPROM, the kernel boot
-parameter ``cs89x0_media=`` has been implemented. Usage is::
-
- cs89x0_media=rj45 or
- cs89x0_media=aui or
- cs89x0_media=bnc
diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
index 5bdf731d9503..1a0c7b3bfcd6 100644
--- a/drivers/net/ethernet/cirrus/Kconfig
+++ b/drivers/net/ethernet/cirrus/Kconfig
@@ -17,36 +17,6 @@ config NET_VENDOR_CIRRUS
if NET_VENDOR_CIRRUS
-config CS89x0
- tristate
-
-config CS89x0_ISA
- tristate "CS89x0 ISA driver support"
- depends on HAS_IOPORT_MAP
- depends on ISA
- depends on !PPC32
- depends on CS89x0_PLATFORM=n
- select NETDEV_LEGACY_INIT
- select CS89x0
- help
- Support for CS89x0 chipset based Ethernet cards. If you have a
- network (Ethernet) card of this type, say Y and read the file
- <file:Documentation/networking/device_drivers/ethernet/cirrus/cs89x0.rst>.
-
- To compile this driver as a module, choose M here. The module
- will be called cs89x0.
-
-config CS89x0_PLATFORM
- tristate "CS89x0 platform driver support"
- depends on ARM || (COMPILE_TEST && !PPC)
- select CS89x0
- help
- Say Y to compile the cs89x0 platform driver. This makes this driver
- suitable for use on certain evaluation boards such as the iMX21ADS.
-
- To compile this driver as a module, choose M here. The module
- will be called cs89x0.
-
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on (ARM && ARCH_EP93XX) || COMPILE_TEST
diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile
index 84865e593788..cb740939d976 100644
--- a/drivers/net/ethernet/cirrus/Makefile
+++ b/drivers/net/ethernet/cirrus/Makefile
@@ -3,6 +3,5 @@
# Makefile for the Cirrus network device drivers.
#
-obj-$(CONFIG_CS89x0) += cs89x0.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_MAC89x0) += mac89x0.o
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
deleted file mode 100644
index fa5857923db4..000000000000
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ /dev/null
@@ -1,1915 +0,0 @@
-/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0
- * driver for linux.
- * Written 1996 by Russell Nelson, with reference to skeleton.c
- * written 1993-1994 by Donald Becker.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * The author may be reached at nelson@crynwr.com, Crynwr
- * Software, 521 Pleasant Valley Rd., Potsdam, NY 13676
- *
- * Other contributors:
- * Mike Cruse : mcruse@cti-ltd.com
- * Russ Nelson
- * Melody Lee : ethernet@crystal.cirrus.com
- * Alan Cox
- * Andrew Morton
- * Oskar Schirmer : oskar@scara.com
- * Deepak Saxena : dsaxena@plexity.net
- * Dmitry Pervushin : dpervushin@ru.mvista.com
- * Deepak Saxena : dsaxena@plexity.net
- * Domenico Andreoli : cavokz@gmail.com
- */
-
-
-/*
- * Set this to zero to disable DMA code
- *
- * Note that even if DMA is turned off we still support the 'dma' and 'use_dma'
- * module options so we don't break any startup scripts.
- */
-#ifndef CONFIG_ISA_DMA_API
-#define ALLOW_DMA 0
-#else
-#define ALLOW_DMA 1
-#endif
-
-/*
- * Set this to zero to remove all the debug statements via
- * dead code elimination
- */
-#define DEBUGGING 1
-
-/* Sources:
- * Crynwr packet driver epktisa.
- * Crystal Semiconductor data sheets.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/printk.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/jiffies.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <linux/io.h>
-
-#include <net/Space.h>
-
-#include <asm/irq.h>
-#include <linux/atomic.h>
-#if ALLOW_DMA
-#include <asm/dma.h>
-#endif
-
-#include "cs89x0.h"
-
-#define cs89_dbg(val, level, fmt, ...) \
-do { \
- if (val <= net_debug) \
- pr_##level(fmt, ##__VA_ARGS__); \
-} while (0)
-
-static char version[] __initdata =
- "v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton";
-
-#define DRV_NAME "cs89x0"
-
-/* First, a few definitions that the brave might change.
- * A zero-terminated list of I/O addresses to be probed. Some special flags..
- * Addr & 1 = Read back the address port, look for signature and reset
- * the page window before probing
- * Addr & 3 = Reset the page window and probe
- * The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space,
- * but it is possible that a Cirrus board could be plugged into the ISA
- * slots.
- */
-/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps
- * them to system IRQ numbers. This mapping is card specific and is set to
- * the configuration of the Cirrus Eval board for this chip.
- */
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
-static unsigned int netcard_portlist[] __used __initdata = {
- 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240,
- 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0
-};
-static unsigned int cs8900_irq_map[] = {
- 10, 11, 12, 5
-};
-#endif
-
-#if DEBUGGING
-static unsigned int net_debug = DEBUGGING;
-#else
-#define net_debug 0 /* gcc will remove all the debug code for us */
-#endif
-
-/* The number of low I/O ports used by the ethercard. */
-#define NETCARD_IO_EXTENT 16
-
-/* we allow the user to override various values normally set in the EEPROM */
-#define FORCE_RJ45 0x0001 /* pick one of these three */
-#define FORCE_AUI 0x0002
-#define FORCE_BNC 0x0004
-
-#define FORCE_AUTO 0x0010 /* pick one of these three */
-#define FORCE_HALF 0x0020
-#define FORCE_FULL 0x0030
-
-/* Information that need to be kept for each board. */
-struct net_local {
- int chip_type; /* one of: CS8900, CS8920, CS8920M */
- char chip_revision; /* revision letter of the chip ('A'...) */
- int send_cmd; /* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
- int auto_neg_cnf; /* auto-negotiation word from EEPROM */
- int adapter_cnf; /* adapter configuration from EEPROM */
- int isa_config; /* ISA configuration from EEPROM */
- int irq_map; /* IRQ map from EEPROM */
- int rx_mode; /* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
- int curr_rx_cfg; /* a copy of PP_RxCFG */
- int linectl; /* either 0 or LOW_RX_SQUELCH, depending on configuration. */
- int send_underrun; /* keep track of how many underruns in a row we get */
- int force; /* force various values; see FORCE* above. */
- spinlock_t lock;
- void __iomem *virt_addr;/* CS89x0 virtual address. */
-#if ALLOW_DMA
- int use_dma; /* Flag: we're using dma */
- int dma; /* DMA channel */
- int dmasize; /* 16 or 64 */
- unsigned char *dma_buff; /* points to the beginning of the buffer */
- unsigned char *end_dma_buff; /* points to the end of the buffer */
- unsigned char *rx_dma_ptr; /* points to the next packet */
-#endif
-};
-
-/* Example routines you must write ;->. */
-#define tx_done(dev) 1
-
-/*
- * Permit 'cs89x0_dma=N' in the kernel boot environment
- */
-#if !defined(MODULE)
-#if ALLOW_DMA
-static int g_cs89x0_dma;
-
-static int __init dma_fn(char *str)
-{
- g_cs89x0_dma = simple_strtol(str, NULL, 0);
- return 1;
-}
-
-__setup("cs89x0_dma=", dma_fn);
-#endif /* ALLOW_DMA */
-
-static int g_cs89x0_media__force;
-
-static int __init media_fn(char *str)
-{
- if (!strcmp(str, "rj45"))
- g_cs89x0_media__force = FORCE_RJ45;
- else if (!strcmp(str, "aui"))
- g_cs89x0_media__force = FORCE_AUI;
- else if (!strcmp(str, "bnc"))
- g_cs89x0_media__force = FORCE_BNC;
-
- return 1;
-}
-
-__setup("cs89x0_media=", media_fn);
-#endif
-
-static void readwords(struct net_local *lp, int portno, void *buf, int length)
-{
- u8 *buf8 = (u8 *)buf;
-
- do {
- u16 tmp16;
-
- tmp16 = ioread16(lp->virt_addr + portno);
- *buf8++ = (u8)tmp16;
- *buf8++ = (u8)(tmp16 >> 8);
- } while (--length);
-}
-
-static void writewords(struct net_local *lp, int portno, void *buf, int length)
-{
- u8 *buf8 = (u8 *)buf;
-
- do {
- u16 tmp16;
-
- tmp16 = *buf8++;
- tmp16 |= (*buf8++) << 8;
- iowrite16(tmp16, lp->virt_addr + portno);
- } while (--length);
-}
-
-static u16
-readreg(struct net_device *dev, u16 regno)
-{
- struct net_local *lp = netdev_priv(dev);
-
- iowrite16(regno, lp->virt_addr + ADD_PORT);
- return ioread16(lp->virt_addr + DATA_PORT);
-}
-
-static void
-writereg(struct net_device *dev, u16 regno, u16 value)
-{
- struct net_local *lp = netdev_priv(dev);
-
- iowrite16(regno, lp->virt_addr + ADD_PORT);
- iowrite16(value, lp->virt_addr + DATA_PORT);
-}
-
-static int __init
-wait_eeprom_ready(struct net_device *dev)
-{
- unsigned long timeout = jiffies;
- /* check to see if the EEPROM is ready,
- * a timeout is used just in case EEPROM is ready when
- * SI_BUSY in the PP_SelfST is clear
- */
- while (readreg(dev, PP_SelfST) & SI_BUSY)
- if (time_after_eq(jiffies, timeout + 40))
- return -1;
- return 0;
-}
-
-static int __init
-get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
-{
- int i;
-
- cs89_dbg(3, info, "EEPROM data from %x for %x:", off, len);
- for (i = 0; i < len; i++) {
- if (wait_eeprom_ready(dev) < 0)
- return -1;
- /* Now send the EEPROM read command and EEPROM location to read */
- writereg(dev, PP_EECMD, (off + i) | EEPROM_READ_CMD);
- if (wait_eeprom_ready(dev) < 0)
- return -1;
- buffer[i] = readreg(dev, PP_EEData);
- cs89_dbg(3, cont, " %04x", buffer[i]);
- }
- cs89_dbg(3, cont, "\n");
- return 0;
-}
-
-static int __init
-get_eeprom_cksum(int off, int len, int *buffer)
-{
- int i, cksum;
-
- cksum = 0;
- for (i = 0; i < len; i++)
- cksum += buffer[i];
- cksum &= 0xffff;
- if (cksum == 0)
- return 0;
- return -1;
-}
-
-static void
-write_irq(struct net_device *dev, int chip_type, int irq)
-{
- int i;
-
- if (chip_type == CS8900) {
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
- /* Search the mapping table for the corresponding IRQ pin. */
- for (i = 0; i != ARRAY_SIZE(cs8900_irq_map); i++)
- if (cs8900_irq_map[i] == irq)
- break;
- /* Not found */
- if (i == ARRAY_SIZE(cs8900_irq_map))
- i = 3;
-#else
- /* INTRQ0 pin is used for interrupt generation. */
- i = 0;
-#endif
- writereg(dev, PP_CS8900_ISAINT, i);
- } else {
- writereg(dev, PP_CS8920_ISAINT, irq);
- }
-}
-
-static void
-count_rx_errors(int status, struct net_device *dev)
-{
- dev->stats.rx_errors++;
- if (status & RX_RUNT)
- dev->stats.rx_length_errors++;
- if (status & RX_EXTRA_DATA)
- dev->stats.rx_length_errors++;
- if ((status & RX_CRC_ERROR) && !(status & (RX_EXTRA_DATA | RX_RUNT)))
- /* per str 172 */
- dev->stats.rx_crc_errors++;
- if (status & RX_DRIBBLE)
- dev->stats.rx_frame_errors++;
-}
-
-/*********************************
- * This page contains DMA routines
- *********************************/
-
-#if ALLOW_DMA
-
-#define dma_page_eq(ptr1, ptr2) ((long)(ptr1) >> 17 == (long)(ptr2) >> 17)
-
-static void
-get_dma_channel(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- if (lp->dma) {
- dev->dma = lp->dma;
- lp->isa_config |= ISA_RxDMA;
- } else {
- if ((lp->isa_config & ANY_ISA_DMA) == 0)
- return;
- dev->dma = lp->isa_config & DMA_NO_MASK;
- if (lp->chip_type == CS8900)
- dev->dma += 5;
- if (dev->dma < 5 || dev->dma > 7) {
- lp->isa_config &= ~ANY_ISA_DMA;
- return;
- }
- }
-}
-
-static void
-write_dma(struct net_device *dev, int chip_type, int dma)
-{
- struct net_local *lp = netdev_priv(dev);
- if ((lp->isa_config & ANY_ISA_DMA) == 0)
- return;
- if (chip_type == CS8900)
- writereg(dev, PP_CS8900_ISADMA, dma - 5);
- else
- writereg(dev, PP_CS8920_ISADMA, dma);
-}
-
-static void
-set_dma_cfg(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- if (lp->use_dma) {
- if ((lp->isa_config & ANY_ISA_DMA) == 0) {
- cs89_dbg(3, err, "set_dma_cfg(): no DMA\n");
- return;
- }
- if (lp->isa_config & ISA_RxDMA) {
- lp->curr_rx_cfg |= RX_DMA_ONLY;
- cs89_dbg(3, info, "set_dma_cfg(): RX_DMA_ONLY\n");
- } else {
- lp->curr_rx_cfg |= AUTO_RX_DMA; /* not that we support it... */
- cs89_dbg(3, info, "set_dma_cfg(): AUTO_RX_DMA\n");
- }
- }
-}
-
-static int
-dma_bufcfg(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- if (lp->use_dma)
- return (lp->isa_config & ANY_ISA_DMA) ? RX_DMA_ENBL : 0;
- else
- return 0;
-}
-
-static int
-dma_busctl(struct net_device *dev)
-{
- int retval = 0;
- struct net_local *lp = netdev_priv(dev);
- if (lp->use_dma) {
- if (lp->isa_config & ANY_ISA_DMA)
- retval |= RESET_RX_DMA; /* Reset the DMA pointer */
- if (lp->isa_config & DMA_BURST)
- retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
- if (lp->dmasize == 64)
- retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
- retval |= MEMORY_ON; /* we need memory enabled to use DMA. */
- }
- return retval;
-}
-
-static void
-dma_rx(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- struct sk_buff *skb;
- int status, length;
- unsigned char *bp = lp->rx_dma_ptr;
-
- status = bp[0] + (bp[1] << 8);
- length = bp[2] + (bp[3] << 8);
- bp += 4;
-
- cs89_dbg(5, debug, "%s: receiving DMA packet at %lx, status %x, length %x\n",
- dev->name, (unsigned long)bp, status, length);
-
- if ((status & RX_OK) == 0) {
- count_rx_errors(status, dev);
- goto skip_this_frame;
- }
-
- /* Malloc up new buffer. */
- skb = netdev_alloc_skb(dev, length + 2);
- if (skb == NULL) {
- dev->stats.rx_dropped++;
-
- /* AKPM: advance bp to the next frame */
-skip_this_frame:
- bp += (length + 3) & ~3;
- if (bp >= lp->end_dma_buff)
- bp -= lp->dmasize * 1024;
- lp->rx_dma_ptr = bp;
- return;
- }
- skb_reserve(skb, 2); /* longword align L3 header */
-
- if (bp + length > lp->end_dma_buff) {
- int semi_cnt = lp->end_dma_buff - bp;
- skb_put_data(skb, bp, semi_cnt);
- skb_put_data(skb, lp->dma_buff, length - semi_cnt);
- } else {
- skb_put_data(skb, bp, length);
- }
- bp += (length + 3) & ~3;
- if (bp >= lp->end_dma_buff)
- bp -= lp->dmasize*1024;
- lp->rx_dma_ptr = bp;
-
- cs89_dbg(3, info, "%s: received %d byte DMA packet of type %x\n",
- dev->name, length,
- ((skb->data[ETH_ALEN + ETH_ALEN] << 8) |
- skb->data[ETH_ALEN + ETH_ALEN + 1]));
-
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += length;
-}
-
-static void release_dma_buff(struct net_local *lp)
-{
- if (lp->dma_buff) {
- free_pages((unsigned long)(lp->dma_buff),
- get_order(lp->dmasize * 1024));
- lp->dma_buff = NULL;
- }
-}
-
-#endif /* ALLOW_DMA */
-
-static void
-control_dc_dc(struct net_device *dev, int on_not_off)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned int selfcontrol;
- unsigned long timenow = jiffies;
- /* control the DC to DC convertor in the SelfControl register.
- * Note: This is hooked up to a general purpose pin, might not
- * always be a DC to DC convertor.
- */
-
- selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
- if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
- selfcontrol |= HCB1;
- else
- selfcontrol &= ~HCB1;
- writereg(dev, PP_SelfCTL, selfcontrol);
-
- /* Wait for the DC/DC converter to power up - 500ms */
- while (time_before(jiffies, timenow + HZ))
- ;
-}
-
-/* send a test packet - return true if carrier bits are ok */
-static int
-send_test_pkt(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- char test_packet[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 46, /* A 46 in network order */
- 0, 0, /* DSAP=0 & SSAP=0 fields */
- 0xf3, 0 /* Control (Test Req + P bit set) */
- };
- unsigned long timenow = jiffies;
-
- writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_TX_ON);
-
- memcpy(test_packet, dev->dev_addr, ETH_ALEN);
- memcpy(test_packet + ETH_ALEN, dev->dev_addr, ETH_ALEN);
-
- iowrite16(TX_AFTER_ALL, lp->virt_addr + TX_CMD_PORT);
- iowrite16(ETH_ZLEN, lp->virt_addr + TX_LEN_PORT);
-
- /* Test to see if the chip has allocated memory for the packet */
- while (time_before(jiffies, timenow + 5))
- if (readreg(dev, PP_BusST) & READY_FOR_TX_NOW)
- break;
- if (time_after_eq(jiffies, timenow + 5))
- return 0; /* this shouldn't happen */
-
- /* Write the contents of the packet */
- writewords(lp, TX_FRAME_PORT, test_packet, (ETH_ZLEN + 1) >> 1);
-
- cs89_dbg(1, debug, "Sending test packet ");
- /* wait a couple of jiffies for packet to be received */
- for (timenow = jiffies; time_before(jiffies, timenow + 3);)
- ;
- if ((readreg(dev, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
- cs89_dbg(1, cont, "succeeded\n");
- return 1;
- }
- cs89_dbg(1, cont, "failed\n");
- return 0;
-}
-
-#define DETECTED_NONE 0
-#define DETECTED_RJ45H 1
-#define DETECTED_RJ45F 2
-#define DETECTED_AUI 3
-#define DETECTED_BNC 4
-
-static int
-detect_tp(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long timenow = jiffies;
- int fdx;
-
- cs89_dbg(1, debug, "%s: Attempting TP\n", dev->name);
-
- /* If connected to another full duplex capable 10-Base-T card
- * the link pulses seem to be lost when the auto detect bit in
- * the LineCTL is set. To overcome this the auto detect bit will
- * be cleared whilst testing the 10-Base-T interface. This would
- * not be necessary for the sparrow chip but is simpler to do it
- * anyway.
- */
- writereg(dev, PP_LineCTL, lp->linectl & ~AUI_ONLY);
- control_dc_dc(dev, 0);
-
- /* Delay for the hardware to work out if the TP cable is present
- * - 150ms
- */
- for (timenow = jiffies; time_before(jiffies, timenow + 15);)
- ;
- if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
- return DETECTED_NONE;
-
- if (lp->chip_type == CS8900) {
- switch (lp->force & 0xf0) {
-#if 0
- case FORCE_AUTO:
- pr_info("%s: cs8900 doesn't autonegotiate\n",
- dev->name);
- return DETECTED_NONE;
-#endif
- /* CS8900 doesn't support AUTO, change to HALF*/
- case FORCE_AUTO:
- lp->force &= ~FORCE_AUTO;
- lp->force |= FORCE_HALF;
- break;
- case FORCE_HALF:
- break;
- case FORCE_FULL:
- writereg(dev, PP_TestCTL,
- readreg(dev, PP_TestCTL) | FDX_8900);
- break;
- }
- fdx = readreg(dev, PP_TestCTL) & FDX_8900;
- } else {
- switch (lp->force & 0xf0) {
- case FORCE_AUTO:
- lp->auto_neg_cnf = AUTO_NEG_ENABLE;
- break;
- case FORCE_HALF:
- lp->auto_neg_cnf = 0;
- break;
- case FORCE_FULL:
- lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX;
- break;
- }
-
- writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);
-
- if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
- pr_info("%s: negotiating duplex...\n", dev->name);
- while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
- if (time_after(jiffies, timenow + 4000)) {
- pr_err("**** Full / half duplex auto-negotiation timed out ****\n");
- break;
- }
- }
- }
- fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
- }
- if (fdx)
- return DETECTED_RJ45F;
- else
- return DETECTED_RJ45H;
-}
-
-static int
-detect_bnc(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- cs89_dbg(1, debug, "%s: Attempting BNC\n", dev->name);
- control_dc_dc(dev, 1);
-
- writereg(dev, PP_LineCTL, (lp->linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
-
- if (send_test_pkt(dev))
- return DETECTED_BNC;
- else
- return DETECTED_NONE;
-}
-
-static int
-detect_aui(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- cs89_dbg(1, debug, "%s: Attempting AUI\n", dev->name);
- control_dc_dc(dev, 0);
-
- writereg(dev, PP_LineCTL, (lp->linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
-
- if (send_test_pkt(dev))
- return DETECTED_AUI;
- else
- return DETECTED_NONE;
-}
-
-/* We have a good packet(s), get it/them out of the buffers. */
-static void
-net_rx(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- struct sk_buff *skb;
- int status, length;
-
- status = ioread16(lp->virt_addr + RX_FRAME_PORT);
- length = ioread16(lp->virt_addr + RX_FRAME_PORT);
-
- if ((status & RX_OK) == 0) {
- count_rx_errors(status, dev);
- return;
- }
-
- /* Malloc up new buffer. */
- skb = netdev_alloc_skb(dev, length + 2);
- if (skb == NULL) {
- dev->stats.rx_dropped++;
- return;
- }
- skb_reserve(skb, 2); /* longword align L3 header */
-
- readwords(lp, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
- if (length & 1)
- skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT);
-
- cs89_dbg(3, debug, "%s: received %d byte packet of type %x\n",
- dev->name, length,
- (skb->data[ETH_ALEN + ETH_ALEN] << 8) |
- skb->data[ETH_ALEN + ETH_ALEN + 1]);
-
- skb->protocol = eth_type_trans(skb, dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += length;
-}
-
-/* The typical workload of the driver:
- * Handle the network interface interrupts.
- */
-
-static irqreturn_t net_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct net_local *lp;
- int status;
- int handled = 0;
-
- lp = netdev_priv(dev);
-
- /* we MUST read all the events out of the ISQ, otherwise we'll never
- * get interrupted again. As a consequence, we can't have any limit
- * on the number of times we loop in the interrupt handler. The
- * hardware guarantees that eventually we'll run out of events. Of
- * course, if you're on a slow machine, and packets are arriving
- * faster than you can read them off, you're screwed. Hasta la
- * vista, baby!
- */
- while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
- cs89_dbg(4, debug, "%s: event=%04x\n", dev->name, status);
- handled = 1;
- switch (status & ISQ_EVENT_MASK) {
- case ISQ_RECEIVER_EVENT:
- /* Got a packet(s). */
- net_rx(dev);
- break;
- case ISQ_TRANSMITTER_EVENT:
- dev->stats.tx_packets++;
- netif_wake_queue(dev); /* Inform upper layers. */
- if ((status & (TX_OK |
- TX_LOST_CRS |
- TX_SQE_ERROR |
- TX_LATE_COL |
- TX_16_COL)) != TX_OK) {
- if ((status & TX_OK) == 0)
- dev->stats.tx_errors++;
- if (status & TX_LOST_CRS)
- dev->stats.tx_carrier_errors++;
- if (status & TX_SQE_ERROR)
- dev->stats.tx_heartbeat_errors++;
- if (status & TX_LATE_COL)
- dev->stats.tx_window_errors++;
- if (status & TX_16_COL)
- dev->stats.tx_aborted_errors++;
- }
- break;
- case ISQ_BUFFER_EVENT:
- if (status & READY_FOR_TX) {
- /* we tried to transmit a packet earlier,
- * but inexplicably ran out of buffers.
- * That shouldn't happen since we only ever
- * load one packet. Shrug. Do the right
- * thing anyway.
- */
- netif_wake_queue(dev); /* Inform upper layers. */
- }
- if (status & TX_UNDERRUN) {
- cs89_dbg(0, err, "%s: transmit underrun\n",
- dev->name);
- lp->send_underrun++;
- if (lp->send_underrun == 3)
- lp->send_cmd = TX_AFTER_381;
- else if (lp->send_underrun == 6)
- lp->send_cmd = TX_AFTER_ALL;
- /* transmit cycle is done, although
- * frame wasn't transmitted - this
- * avoids having to wait for the upper
- * layers to timeout on us, in the
- * event of a tx underrun
- */
- netif_wake_queue(dev); /* Inform upper layers. */
- }
-#if ALLOW_DMA
- if (lp->use_dma && (status & RX_DMA)) {
- int count = readreg(dev, PP_DmaFrameCnt);
- while (count) {
- cs89_dbg(5, debug,
- "%s: receiving %d DMA frames\n",
- dev->name, count);
- if (count > 1)
- cs89_dbg(2, debug,
- "%s: receiving %d DMA frames\n",
- dev->name, count);
- dma_rx(dev);
- if (--count == 0)
- count = readreg(dev, PP_DmaFrameCnt);
- if (count > 0)
- cs89_dbg(2, debug,
- "%s: continuing with %d DMA frames\n",
- dev->name, count);
- }
- }
-#endif
- break;
- case ISQ_RX_MISS_EVENT:
- dev->stats.rx_missed_errors += (status >> 6);
- break;
- case ISQ_TX_COL_EVENT:
- dev->stats.collisions += (status >> 6);
- break;
- }
- }
- return IRQ_RETVAL(handled);
-}
-
-/* Open/initialize the board. This is called (in the current kernel)
- sometime after booting when the 'ifconfig' program is run.
-
- This routine should set everything up anew at each open, even
- registers that "should" only need to be set once at boot, so that
- there is non-reboot way to recover if something goes wrong.
-*/
-
-/* AKPM: do we need to do any locking here? */
-
-static int
-net_open(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- int result = 0;
- int i;
- int ret;
-
- if (dev->irq < 2) {
- /* Allow interrupts to be generated by the chip */
-/* Cirrus' release had this: */
-#if 0
- writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
-#endif
-/* And 2.3.47 had this: */
- writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
-
- for (i = 2; i < CS8920_NO_INTS; i++) {
- if ((1 << i) & lp->irq_map) {
- if (request_irq(i, net_interrupt, 0, dev->name,
- dev) == 0) {
- dev->irq = i;
- write_irq(dev, lp->chip_type, i);
- /* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */
- break;
- }
- }
- }
-
- if (i >= CS8920_NO_INTS) {
- writereg(dev, PP_BusCTL, 0); /* disable interrupts. */
- pr_err("can't get an interrupt\n");
- ret = -EAGAIN;
- goto bad_out;
- }
- } else {
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
- if (((1 << dev->irq) & lp->irq_map) == 0) {
- pr_err("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
- dev->name, dev->irq, lp->irq_map);
- ret = -EAGAIN;
- goto bad_out;
- }
-#endif
-/* FIXME: Cirrus' release had this: */
- writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ);
-/* And 2.3.47 had this: */
-#if 0
- writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
-#endif
- write_irq(dev, lp->chip_type, dev->irq);
- ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev);
- if (ret) {
- pr_err("request_irq(%d) failed\n", dev->irq);
- goto bad_out;
- }
- }
-
-#if ALLOW_DMA
- if (lp->use_dma && (lp->isa_config & ANY_ISA_DMA)) {
- unsigned long flags;
- lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
- get_order(lp->dmasize * 1024));
- if (!lp->dma_buff) {
- pr_err("%s: cannot get %dK memory for DMA\n",
- dev->name, lp->dmasize);
- goto release_irq;
- }
- cs89_dbg(1, debug, "%s: dma %lx %lx\n",
- dev->name,
- (unsigned long)lp->dma_buff,
- (unsigned long)isa_virt_to_bus(lp->dma_buff));
- if ((unsigned long)lp->dma_buff >= MAX_DMA_ADDRESS ||
- !dma_page_eq(lp->dma_buff,
- lp->dma_buff + lp->dmasize * 1024 - 1)) {
- pr_err("%s: not usable as DMA buffer\n", dev->name);
- goto release_irq;
- }
- memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */
- if (request_dma(dev->dma, dev->name)) {
- pr_err("%s: cannot get dma channel %d\n",
- dev->name, dev->dma);
- goto release_irq;
- }
- write_dma(dev, lp->chip_type, dev->dma);
- lp->rx_dma_ptr = lp->dma_buff;
- lp->end_dma_buff = lp->dma_buff + lp->dmasize * 1024;
- spin_lock_irqsave(&lp->lock, flags);
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- set_dma_mode(dev->dma, DMA_RX_MODE); /* auto_init as well */
- set_dma_addr(dev->dma, isa_virt_to_bus(lp->dma_buff));
- set_dma_count(dev->dma, lp->dmasize * 1024);
- enable_dma(dev->dma);
- spin_unlock_irqrestore(&lp->lock, flags);
- }
-#endif /* ALLOW_DMA */
-
- /* set the Ethernet address */
- for (i = 0; i < ETH_ALEN / 2; i++)
- writereg(dev, PP_IA + i * 2,
- (dev->dev_addr[i * 2] |
- (dev->dev_addr[i * 2 + 1] << 8)));
-
- /* while we're testing the interface, leave interrupts disabled */
- writereg(dev, PP_BusCTL, MEMORY_ON);
-
- /* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
- if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) &&
- (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
- lp->linectl = LOW_RX_SQUELCH;
- else
- lp->linectl = 0;
-
- /* check to make sure that they have the "right" hardware available */
- switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
- case A_CNF_MEDIA_10B_T:
- result = lp->adapter_cnf & A_CNF_10B_T;
- break;
- case A_CNF_MEDIA_AUI:
- result = lp->adapter_cnf & A_CNF_AUI;
- break;
- case A_CNF_MEDIA_10B_2:
- result = lp->adapter_cnf & A_CNF_10B_2;
- break;
- default:
- result = lp->adapter_cnf & (A_CNF_10B_T |
- A_CNF_AUI |
- A_CNF_10B_2);
- }
- if (!result) {
- pr_err("%s: EEPROM is configured for unavailable media\n",
- dev->name);
-release_dma:
-#if ALLOW_DMA
- free_dma(dev->dma);
-release_irq:
- release_dma_buff(lp);
-#endif
- writereg(dev, PP_LineCTL,
- readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
- free_irq(dev->irq, dev);
- ret = -EAGAIN;
- goto bad_out;
- }
-
- /* set the hardware to the configured choice */
- switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
- case A_CNF_MEDIA_10B_T:
- result = detect_tp(dev);
- if (result == DETECTED_NONE) {
- pr_warn("%s: 10Base-T (RJ-45) has no cable\n",
- dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
- }
- break;
- case A_CNF_MEDIA_AUI:
- result = detect_aui(dev);
- if (result == DETECTED_NONE) {
- pr_warn("%s: 10Base-5 (AUI) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = DETECTED_AUI; /* Yes! I don't care if I see a carrier */
- }
- break;
- case A_CNF_MEDIA_10B_2:
- result = detect_bnc(dev);
- if (result == DETECTED_NONE) {
- pr_warn("%s: 10Base-2 (BNC) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
- }
- break;
- case A_CNF_MEDIA_AUTO:
- writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
- if (lp->adapter_cnf & A_CNF_10B_T) {
- result = detect_tp(dev);
- if (result != DETECTED_NONE)
- break;
- }
- if (lp->adapter_cnf & A_CNF_AUI) {
- result = detect_aui(dev);
- if (result != DETECTED_NONE)
- break;
- }
- if (lp->adapter_cnf & A_CNF_10B_2) {
- result = detect_bnc(dev);
- if (result != DETECTED_NONE)
- break;
- }
- pr_err("%s: no media detected\n", dev->name);
- goto release_dma;
- }
- switch (result) {
- case DETECTED_NONE:
- pr_err("%s: no network cable attached to configured media\n",
- dev->name);
- goto release_dma;
- case DETECTED_RJ45H:
- pr_info("%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
- break;
- case DETECTED_RJ45F:
- pr_info("%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
- break;
- case DETECTED_AUI:
- pr_info("%s: using 10Base-5 (AUI)\n", dev->name);
- break;
- case DETECTED_BNC:
- pr_info("%s: using 10Base-2 (BNC)\n", dev->name);
- break;
- }
-
- /* Turn on both receive and transmit operations */
- writereg(dev, PP_LineCTL,
- readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
-
- /* Receive only error free packets addressed to this card */
- lp->rx_mode = 0;
- writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
-
- lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
-
- if (lp->isa_config & STREAM_TRANSFER)
- lp->curr_rx_cfg |= RX_STREAM_ENBL;
-#if ALLOW_DMA
- set_dma_cfg(dev);
-#endif
- writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
-
- writereg(dev, PP_TxCFG, (TX_LOST_CRS_ENBL |
- TX_SQE_ERROR_ENBL |
- TX_OK_ENBL |
- TX_LATE_COL_ENBL |
- TX_JBR_ENBL |
- TX_ANY_COL_ENBL |
- TX_16_COL_ENBL));
-
- writereg(dev, PP_BufCFG, (READY_FOR_TX_ENBL |
- RX_MISS_COUNT_OVRFLOW_ENBL |
-#if ALLOW_DMA
- dma_bufcfg(dev) |
-#endif
- TX_COL_COUNT_OVRFLOW_ENBL |
- TX_UNDERRUN_ENBL));
-
- /* now that we've got our act together, enable everything */
- writereg(dev, PP_BusCTL, (ENABLE_IRQ
- | (dev->mem_start ? MEMORY_ON : 0) /* turn memory on */
-#if ALLOW_DMA
- | dma_busctl(dev)
-#endif
- ));
- netif_start_queue(dev);
- cs89_dbg(1, debug, "net_open() succeeded\n");
- return 0;
-bad_out:
- return ret;
-}
-
-/* The inverse routine to net_open(). */
-static int
-net_close(struct net_device *dev)
-{
-#if ALLOW_DMA
- struct net_local *lp = netdev_priv(dev);
-#endif
-
- netif_stop_queue(dev);
-
- writereg(dev, PP_RxCFG, 0);
- writereg(dev, PP_TxCFG, 0);
- writereg(dev, PP_BufCFG, 0);
- writereg(dev, PP_BusCTL, 0);
-
- free_irq(dev->irq, dev);
-
-#if ALLOW_DMA
- if (lp->use_dma && lp->dma) {
- free_dma(dev->dma);
- release_dma_buff(lp);
- }
-#endif
-
- /* Update the statistics here. */
- return 0;
-}
-
-/* Get the current statistics.
- * This may be called with the card open or closed.
- */
-static struct net_device_stats *
-net_get_stats(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&lp->lock, flags);
- /* Update the statistics from the device registers. */
- dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
- dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
- spin_unlock_irqrestore(&lp->lock, flags);
-
- return &dev->stats;
-}
-
-static void net_timeout(struct net_device *dev, unsigned int txqueue)
-{
- /* If we get here, some higher level has decided we are broken.
- There should really be a "kick me" function call instead. */
- cs89_dbg(0, err, "%s: transmit timed out, %s?\n",
- dev->name,
- tx_done(dev) ? "IRQ conflict" : "network cable problem");
- /* Try to restart the adaptor. */
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
-
- cs89_dbg(3, debug, "%s: sent %d byte packet of type %x\n",
- dev->name, skb->len,
- ((skb->data[ETH_ALEN + ETH_ALEN] << 8) |
- skb->data[ETH_ALEN + ETH_ALEN + 1]));
-
- /* keep the upload from being interrupted, since we
- * ask the chip to start transmitting before the
- * whole packet has been completely uploaded.
- */
-
- spin_lock_irqsave(&lp->lock, flags);
- netif_stop_queue(dev);
-
- /* initiate a transmit sequence */
- iowrite16(lp->send_cmd, lp->virt_addr + TX_CMD_PORT);
- iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);
-
- /* Test to see if the chip has allocated memory for the packet */
- if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
- /* Gasp! It hasn't. But that shouldn't happen since
- * we're waiting for TxOk, so return 1 and requeue this packet.
- */
-
- spin_unlock_irqrestore(&lp->lock, flags);
- cs89_dbg(0, err, "Tx buffer not free!\n");
- return NETDEV_TX_BUSY;
- }
- /* Write the contents of the packet */
- writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);
- spin_unlock_irqrestore(&lp->lock, flags);
- dev->stats.tx_bytes += skb->len;
- dev_consume_skb_any(skb);
-
- /* We DO NOT call netif_wake_queue() here.
- * We also DO NOT call netif_start_queue().
- *
- * Either of these would cause another bottom half run through
- * net_send_packet() before this packet has fully gone out.
- * That causes us to hit the "Gasp!" above and the send is rescheduled.
- * it runs like a dog. We just return and wait for the Tx completion
- * interrupt handler to restart the netdevice layer
- */
-
- return NETDEV_TX_OK;
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
- u16 cfg;
-
- spin_lock_irqsave(&lp->lock, flags);
- if (dev->flags & IFF_PROMISC)
- lp->rx_mode = RX_ALL_ACCEPT;
- else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
- /* The multicast-accept list is initialized to accept-all,
- * and we rely on higher-level filtering for now.
- */
- lp->rx_mode = RX_MULTCAST_ACCEPT;
- else
- lp->rx_mode = 0;
-
- writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
-
- /* in promiscuous mode, we accept errored packets,
- * so we have to enable interrupts on them also
- */
- cfg = lp->curr_rx_cfg;
- if (lp->rx_mode == RX_ALL_ACCEPT)
- cfg |= RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL;
- writereg(dev, PP_RxCFG, cfg);
- spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static int set_mac_address(struct net_device *dev, void *p)
-{
- int i;
- struct sockaddr *addr = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- eth_hw_addr_set(dev, addr->sa_data);
-
- cs89_dbg(0, debug, "%s: Setting MAC address to %pM\n",
- dev->name, dev->dev_addr);
-
- /* set the Ethernet address */
- for (i = 0; i < ETH_ALEN / 2; i++)
- writereg(dev, PP_IA + i * 2,
- (dev->dev_addr[i * 2] |
- (dev->dev_addr[i * 2 + 1] << 8)));
-
- return 0;
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void net_poll_controller(struct net_device *dev)
-{
- disable_irq(dev->irq);
- net_interrupt(dev->irq, dev);
- enable_irq(dev->irq);
-}
-#endif
-
-static const struct net_device_ops net_ops = {
- .ndo_open = net_open,
- .ndo_stop = net_close,
- .ndo_tx_timeout = net_timeout,
- .ndo_start_xmit = net_send_packet,
- .ndo_get_stats = net_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = set_mac_address,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = net_poll_controller,
-#endif
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static void __init reset_chip(struct net_device *dev)
-{
-#if !defined(CONFIG_MACH_MX31ADS)
- struct net_local *lp = netdev_priv(dev);
- unsigned long reset_start_time;
-
- writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
-
- /* wait 30 ms */
- msleep(30);
-
- if (lp->chip_type != CS8900) {
- /* Hardware problem requires PNP registers to be reconfigured after a reset */
- iowrite16(PP_CS8920_ISAINT, lp->virt_addr + ADD_PORT);
- iowrite8(dev->irq, lp->virt_addr + DATA_PORT);
- iowrite8(0, lp->virt_addr + DATA_PORT + 1);
-
- iowrite16(PP_CS8920_ISAMemB, lp->virt_addr + ADD_PORT);
- iowrite8((dev->mem_start >> 16) & 0xff,
- lp->virt_addr + DATA_PORT);
- iowrite8((dev->mem_start >> 8) & 0xff,
- lp->virt_addr + DATA_PORT + 1);
- }
-
- /* Wait until the chip is reset */
- reset_start_time = jiffies;
- while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 &&
- time_before(jiffies, reset_start_time + 2))
- ;
-#endif /* !CONFIG_MACH_MX31ADS */
-}
-
-/* This is the real probe routine.
- * Linux has a history of friendly device probes on the ISA bus.
- * A good device probes avoids doing writes, and
- * verifies that the correct device exists and functions.
- * Return 0 on success.
- */
-static int __init
-cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
-{
- struct net_local *lp = netdev_priv(dev);
- int i;
- int tmp;
- unsigned rev_type = 0;
- int eeprom_buff[CHKSUM_LEN];
- u8 addr[ETH_ALEN];
- int retval;
-
- /* Initialize the device structure. */
- if (!modular) {
- memset(lp, 0, sizeof(*lp));
- spin_lock_init(&lp->lock);
-#ifndef MODULE
-#if ALLOW_DMA
- if (g_cs89x0_dma) {
- lp->use_dma = 1;
- lp->dma = g_cs89x0_dma;
- lp->dmasize = 16; /* Could make this an option... */
- }
-#endif
- lp->force = g_cs89x0_media__force;
-#endif
- }
-
- pr_debug("PP_addr at %p[%x]: 0x%x\n",
- ioaddr, ADD_PORT, ioread16(ioaddr + ADD_PORT));
- iowrite16(PP_ChipID, ioaddr + ADD_PORT);
-
- tmp = ioread16(ioaddr + DATA_PORT);
- if (tmp != CHIP_EISA_ID_SIG) {
- pr_debug("%s: incorrect signature at %p[%x]: 0x%x!="
- CHIP_EISA_ID_SIG_STR "\n",
- dev->name, ioaddr, DATA_PORT, tmp);
- retval = -ENODEV;
- goto out1;
- }
-
- lp->virt_addr = ioaddr;
-
- /* get the chip type */
- rev_type = readreg(dev, PRODUCT_ID_ADD);
- lp->chip_type = rev_type & ~REVISON_BITS;
- lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
-
- /* Check the chip type and revision in order to set the correct
- * send command. CS8920 revision C and CS8900 revision F can use
- * the faster send.
- */
- lp->send_cmd = TX_AFTER_381;
- if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
- lp->send_cmd = TX_NOW;
- if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
- lp->send_cmd = TX_NOW;
-
- pr_info_once("%s\n", version);
-
- pr_info("%s: cs89%c0%s rev %c found at %p ",
- dev->name,
- lp->chip_type == CS8900 ? '0' : '2',
- lp->chip_type == CS8920M ? "M" : "",
- lp->chip_revision,
- lp->virt_addr);
-
- reset_chip(dev);
-
- /* Here we read the current configuration of the chip.
- * If there is no Extended EEPROM then the idea is to not disturb
- * the chip configuration, it should have been correctly setup by
- * automatic EEPROM read on reset. So, if the chip says it read
- * the EEPROM the driver will always do *something* instead of
- * complain that adapter_cnf is 0.
- */
-
- if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) ==
- (EEPROM_OK | EEPROM_PRESENT)) {
- /* Load the MAC. */
- for (i = 0; i < ETH_ALEN / 2; i++) {
- unsigned int Addr;
- Addr = readreg(dev, PP_IA + i * 2);
- addr[i * 2] = Addr & 0xFF;
- addr[i * 2 + 1] = Addr >> 8;
- }
- eth_hw_addr_set(dev, addr);
-
- /* Load the Adapter Configuration.
- * Note: Barring any more specific information from some
- * other source (ie EEPROM+Schematics), we would not know
- * how to operate a 10Base2 interface on the AUI port.
- * However, since we do read the status of HCB1 and use
- * settings that always result in calls to control_dc_dc(dev,0)
- * a BNC interface should work if the enable pin
- * (dc/dc converter) is on HCB1.
- * It will be called AUI however.
- */
-
- lp->adapter_cnf = 0;
- i = readreg(dev, PP_LineCTL);
- /* Preserve the setting of the HCB1 pin. */
- if ((i & (HCB1 | HCB1_ENBL)) == (HCB1 | HCB1_ENBL))
- lp->adapter_cnf |= A_CNF_DC_DC_POLARITY;
- /* Save the sqelch bit */
- if ((i & LOW_RX_SQUELCH) == LOW_RX_SQUELCH)
- lp->adapter_cnf |= A_CNF_EXTND_10B_2 | A_CNF_LOW_RX_SQUELCH;
- /* Check if the card is in 10Base-t only mode */
- if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == 0)
- lp->adapter_cnf |= A_CNF_10B_T | A_CNF_MEDIA_10B_T;
- /* Check if the card is in AUI only mode */
- if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUI_ONLY)
- lp->adapter_cnf |= A_CNF_AUI | A_CNF_MEDIA_AUI;
- /* Check if the card is in Auto mode. */
- if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUTO_AUI_10BASET)
- lp->adapter_cnf |= A_CNF_AUI | A_CNF_10B_T |
- A_CNF_MEDIA_AUI | A_CNF_MEDIA_10B_T | A_CNF_MEDIA_AUTO;
-
- cs89_dbg(1, info, "%s: PP_LineCTL=0x%x, adapter_cnf=0x%x\n",
- dev->name, i, lp->adapter_cnf);
-
- /* IRQ. Other chips already probe, see below. */
- if (lp->chip_type == CS8900)
- lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK;
-
- pr_cont("[Cirrus EEPROM] ");
- }
-
- pr_cont("\n");
-
- /* First check to see if an EEPROM is attached. */
-
- if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
- pr_warn("No EEPROM, relying on command line....\n");
- else if (get_eeprom_data(dev, START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
- pr_warn("EEPROM read failed, relying on command line\n");
- } else if (get_eeprom_cksum(START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
- /* Check if the chip was able to read its own configuration starting
- at 0 in the EEPROM*/
- if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) !=
- (EEPROM_OK | EEPROM_PRESENT))
- pr_warn("Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n");
-
- } else {
- /* This reads an extended EEPROM that is not documented
- * in the CS8900 datasheet.
- */
-
- /* get transmission control word but keep the autonegotiation bits */
- if (!lp->auto_neg_cnf)
- lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET / 2];
- /* Store adapter configuration */
- if (!lp->adapter_cnf)
- lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET / 2];
- /* Store ISA configuration */
- lp->isa_config = eeprom_buff[ISA_CNF_OFFSET / 2];
- dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET / 2] << 8;
-
- /* eeprom_buff has 32-bit ints, so we can't just memcpy it */
- /* store the initial memory base address */
- for (i = 0; i < ETH_ALEN / 2; i++) {
- addr[i * 2] = eeprom_buff[i];
- addr[i * 2 + 1] = eeprom_buff[i] >> 8;
- }
- eth_hw_addr_set(dev, addr);
- cs89_dbg(1, debug, "%s: new adapter_cnf: 0x%x\n",
- dev->name, lp->adapter_cnf);
- }
-
- /* allow them to force multiple transceivers. If they force multiple, autosense */
- {
- int count = 0;
- if (lp->force & FORCE_RJ45) {
- lp->adapter_cnf |= A_CNF_10B_T;
- count++;
- }
- if (lp->force & FORCE_AUI) {
- lp->adapter_cnf |= A_CNF_AUI;
- count++;
- }
- if (lp->force & FORCE_BNC) {
- lp->adapter_cnf |= A_CNF_10B_2;
- count++;
- }
- if (count > 1)
- lp->adapter_cnf |= A_CNF_MEDIA_AUTO;
- else if (lp->force & FORCE_RJ45)
- lp->adapter_cnf |= A_CNF_MEDIA_10B_T;
- else if (lp->force & FORCE_AUI)
- lp->adapter_cnf |= A_CNF_MEDIA_AUI;
- else if (lp->force & FORCE_BNC)
- lp->adapter_cnf |= A_CNF_MEDIA_10B_2;
- }
-
- cs89_dbg(1, debug, "%s: after force 0x%x, adapter_cnf=0x%x\n",
- dev->name, lp->force, lp->adapter_cnf);
-
- /* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */
-
- /* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */
-
- /* FIXME: we don't set the Ethernet address on the command line. Use
- * ifconfig IFACE hw ether AABBCCDDEEFF
- */
-
- pr_info("media %s%s%s",
- (lp->adapter_cnf & A_CNF_10B_T) ? "RJ-45," : "",
- (lp->adapter_cnf & A_CNF_AUI) ? "AUI," : "",
- (lp->adapter_cnf & A_CNF_10B_2) ? "BNC," : "");
-
- lp->irq_map = 0xffff;
-
- /* If this is a CS8900 then no pnp soft */
- if (lp->chip_type != CS8900 &&
- /* Check if the ISA IRQ has been set */
- (i = readreg(dev, PP_CS8920_ISAINT) & 0xff,
- (i != 0 && i < CS8920_NO_INTS))) {
- if (!dev->irq)
- dev->irq = i;
- } else {
- i = lp->isa_config & INT_NO_MASK;
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
- if (lp->chip_type == CS8900) {
- /* Translate the IRQ using the IRQ mapping table. */
- if (i >= ARRAY_SIZE(cs8900_irq_map))
- pr_err("invalid ISA interrupt number %d\n", i);
- else
- i = cs8900_irq_map[i];
-
- lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */
- } else {
- int irq_map_buff[IRQ_MAP_LEN/2];
-
- if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA,
- IRQ_MAP_LEN / 2,
- irq_map_buff) >= 0) {
- if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT)
- lp->irq_map = ((irq_map_buff[0] >> 8) |
- (irq_map_buff[1] << 8));
- }
- }
-#endif
- if (!dev->irq)
- dev->irq = i;
- }
-
- pr_cont(" IRQ %d", dev->irq);
-
-#if ALLOW_DMA
- if (lp->use_dma) {
- get_dma_channel(dev);
- pr_cont(", DMA %d", dev->dma);
- } else
-#endif
- pr_cont(", programmed I/O");
-
- /* print the ethernet address. */
- pr_cont(", MAC %pM\n", dev->dev_addr);
-
- dev->netdev_ops = &net_ops;
- dev->watchdog_timeo = HZ;
-
- cs89_dbg(0, info, "cs89x0_probe1() successful\n");
-
- retval = register_netdev(dev);
- if (retval)
- goto out2;
- return 0;
-out2:
- iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
-out1:
- return retval;
-}
-
-#if IS_ENABLED(CONFIG_CS89x0_ISA)
-/*
- * This function converts the I/O port address used by the cs89x0_probe() and
- * init_module() functions to the I/O memory address used by the
- * cs89x0_probe1() function.
- */
-static int __init
-cs89x0_ioport_probe(struct net_device *dev, unsigned long ioport, int modular)
-{
- struct net_local *lp = netdev_priv(dev);
- int ret;
- void __iomem *io_mem;
-
- if (!lp)
- return -ENOMEM;
-
- dev->base_addr = ioport;
-
- if (!request_region(ioport, NETCARD_IO_EXTENT, DRV_NAME)) {
- ret = -EBUSY;
- goto out;
- }
-
- io_mem = ioport_map(ioport & ~3, NETCARD_IO_EXTENT);
- if (!io_mem) {
- ret = -ENOMEM;
- goto release;
- }
-
- /* if they give us an odd I/O address, then do ONE write to
- * the address port, to get it back to address zero, where we
- * expect to find the EISA signature word. An IO with a base of 0x3
- * will skip the test for the ADD_PORT.
- */
- if (ioport & 1) {
- cs89_dbg(1, info, "%s: odd ioaddr 0x%lx\n", dev->name, ioport);
- if ((ioport & 2) != 2) {
- if ((ioread16(io_mem + ADD_PORT) & ADD_MASK) !=
- ADD_SIG) {
- pr_err("%s: bad signature 0x%x\n",
- dev->name, ioread16(io_mem + ADD_PORT));
- ret = -ENODEV;
- goto unmap;
- }
- }
- }
-
- ret = cs89x0_probe1(dev, io_mem, modular);
- if (!ret)
- goto out;
-unmap:
- ioport_unmap(io_mem);
-release:
- release_region(ioport, NETCARD_IO_EXTENT);
-out:
- return ret;
-}
-
-#ifndef MODULE
-/* Check for a network adaptor of this type, and return '0' iff one exists.
- * If dev->base_addr == 0, probe all likely locations.
- * If dev->base_addr == 1, always return failure.
- * If dev->base_addr == 2, allocate space for the device and return success
- * (detachable devices only).
- * Return 0 on success.
- */
-
-struct net_device * __init cs89x0_probe(int unit)
-{
- struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
- unsigned *port;
- int err = 0;
- int irq;
- int io;
-
- if (!dev)
- return ERR_PTR(-ENODEV);
-
- sprintf(dev->name, "eth%d", unit);
- netdev_boot_setup_check(dev);
- io = dev->base_addr;
- irq = dev->irq;
-
- cs89_dbg(0, info, "cs89x0_probe(0x%x)\n", io);
-
- if (io > 0x1ff) { /* Check a single specified location. */
- err = cs89x0_ioport_probe(dev, io, 0);
- } else if (io != 0) { /* Don't probe at all. */
- err = -ENXIO;
- } else {
- for (port = netcard_portlist; *port; port++) {
- if (cs89x0_ioport_probe(dev, *port, 0) == 0)
- break;
- dev->irq = irq;
- }
- if (!*port)
- err = -ENODEV;
- }
- if (err)
- goto out;
- return dev;
-out:
- free_netdev(dev);
- pr_warn("no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
- return ERR_PTR(err);
-}
-#else
-static struct net_device *dev_cs89x0;
-
-/* Support the 'debug' module parm even if we're compiled for non-debug to
- * avoid breaking someone's startup scripts
- */
-
-static int io;
-static int irq;
-static int debug;
-static char media[8];
-static int duplex = -1;
-
-static int use_dma; /* These generate unused var warnings if ALLOW_DMA = 0 */
-static int dma;
-static int dmasize = 16; /* or 64 */
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param(debug, int, 0);
-module_param_string(media, media, sizeof(media), 0);
-module_param(duplex, int, 0);
-module_param_hw(dma , int, dma, 0);
-module_param(dmasize , int, 0);
-module_param(use_dma , int, 0);
-MODULE_PARM_DESC(io, "cs89x0 I/O base address");
-MODULE_PARM_DESC(irq, "cs89x0 IRQ number");
-#if DEBUGGING
-MODULE_PARM_DESC(debug, "cs89x0 debug level (0-6)");
-#else
-MODULE_PARM_DESC(debug, "(ignored)");
-#endif
-MODULE_PARM_DESC(media, "Set cs89x0 adapter(s) media type(s) (rj45,bnc,aui)");
-/* No other value than -1 for duplex seems to be currently interpreted */
-MODULE_PARM_DESC(duplex, "(ignored)");
-#if ALLOW_DMA
-MODULE_PARM_DESC(dma , "cs89x0 ISA DMA channel; ignored if use_dma=0");
-MODULE_PARM_DESC(dmasize , "cs89x0 DMA size in kB (16,64); ignored if use_dma=0");
-MODULE_PARM_DESC(use_dma , "cs89x0 using DMA (0-1)");
-#else
-MODULE_PARM_DESC(dma , "(ignored)");
-MODULE_PARM_DESC(dmasize , "(ignored)");
-MODULE_PARM_DESC(use_dma , "(ignored)");
-#endif
-
-MODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton");
-MODULE_LICENSE("GPL");
-
-/*
- * media=t - specify media type
- * or media=2
- * or media=aui
- * or medai=auto
- * duplex=0 - specify forced half/full/autonegotiate duplex
- * debug=# - debug level
- *
- * Default Chip Configuration:
- * DMA Burst = enabled
- * IOCHRDY Enabled = enabled
- * UseSA = enabled
- * CS8900 defaults to half-duplex if not specified on command-line
- * CS8920 defaults to autoneg if not specified on command-line
- * Use reset defaults for other config parameters
- *
- * Assumptions:
- * media type specified is supported (circuitry is present)
- * if memory address is > 1MB, then required mem decode hw is present
- * if 10B-2, then agent other than driver will enable DC/DC converter
- * (hw or software util)
- */
-
-static int __init cs89x0_isa_init_module(void)
-{
- struct net_device *dev;
- struct net_local *lp;
- int ret = 0;
-
-#if DEBUGGING
- net_debug = debug;
-#else
- debug = 0;
-#endif
- dev = alloc_etherdev(sizeof(struct net_local));
- if (!dev)
- return -ENOMEM;
-
- dev->irq = irq;
- dev->base_addr = io;
- lp = netdev_priv(dev);
-
-#if ALLOW_DMA
- if (use_dma) {
- lp->use_dma = use_dma;
- lp->dma = dma;
- lp->dmasize = dmasize;
- }
-#endif
-
- spin_lock_init(&lp->lock);
-
- /* boy, they'd better get these right */
- if (!strcmp(media, "rj45"))
- lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
- else if (!strcmp(media, "aui"))
- lp->adapter_cnf = A_CNF_MEDIA_AUI | A_CNF_AUI;
- else if (!strcmp(media, "bnc"))
- lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2;
- else
- lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
-
- if (duplex == -1)
- lp->auto_neg_cnf = AUTO_NEG_ENABLE;
-
- if (io == 0) {
- pr_err("Module autoprobing not allowed\n");
- pr_err("Append io=0xNNN\n");
- ret = -EPERM;
- goto out;
- } else if (io <= 0x1ff) {
- ret = -ENXIO;
- goto out;
- }
-
-#if ALLOW_DMA
- if (use_dma && dmasize != 16 && dmasize != 64) {
- pr_err("dma size must be either 16K or 64K, not %dK\n",
- dmasize);
- ret = -EPERM;
- goto out;
- }
-#endif
- ret = cs89x0_ioport_probe(dev, io, 1);
- if (ret)
- goto out;
-
- dev_cs89x0 = dev;
- return 0;
-out:
- free_netdev(dev);
- return ret;
-}
-module_init(cs89x0_isa_init_module);
-
-static void __exit cs89x0_isa_cleanup_module(void)
-{
- struct net_local *lp = netdev_priv(dev_cs89x0);
-
- unregister_netdev(dev_cs89x0);
- iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
- ioport_unmap(lp->virt_addr);
- release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT);
- free_netdev(dev_cs89x0);
-}
-module_exit(cs89x0_isa_cleanup_module);
-#endif /* MODULE */
-#endif /* CONFIG_CS89x0_ISA */
-
-#if IS_ENABLED(CONFIG_CS89x0_PLATFORM)
-static int __init cs89x0_platform_probe(struct platform_device *pdev)
-{
- struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
- void __iomem *virt_addr;
- int err;
-
- if (!dev)
- return -ENOMEM;
-
- dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq < 0) {
- err = dev->irq;
- goto free;
- }
-
- virt_addr = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(virt_addr)) {
- err = PTR_ERR(virt_addr);
- goto free;
- }
-
- err = cs89x0_probe1(dev, virt_addr, 0);
- if (err) {
- dev_warn(&dev->dev, "no cs8900 or cs8920 detected\n");
- goto free;
- }
-
- platform_set_drvdata(pdev, dev);
- return 0;
-
-free:
- free_netdev(dev);
- return err;
-}
-
-static void cs89x0_platform_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
-
- /* This platform_get_resource() call will not return NULL, because
- * the same call in cs89x0_platform_probe() has returned a non NULL
- * value.
- */
- unregister_netdev(dev);
- free_netdev(dev);
-}
-
-static const struct of_device_id __maybe_unused cs89x0_match[] = {
- { .compatible = "cirrus,cs8900", },
- { .compatible = "cirrus,cs8920", },
- { },
-};
-MODULE_DEVICE_TABLE(of, cs89x0_match);
-
-static struct platform_driver cs89x0_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(cs89x0_match),
- },
- .remove = cs89x0_platform_remove,
-};
-
-module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
-
-#endif /* CONFIG_CS89x0_PLATFORM */
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Crystal Semiconductor (Now Cirrus Logic) CS89[02]0 network driver");
-MODULE_AUTHOR("Russell Nelson <nelson@crynwr.com>");
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (8 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 09/15] drivers: net: cirrus: cs89x0: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-23 7:07 ` Geert Uytterhoeven
2026-04-22 18:01 ` [PATCH net v2 11/15] drivers: net: fujitsu: fmvj18x: " Andrew Lunn
` (4 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The mac89x0 was written by Russell Nelson in 1996. It is an MAC
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/cirrus/Kconfig | 10 -
drivers/net/ethernet/cirrus/Makefile | 1 -
drivers/net/ethernet/cirrus/cs89x0.h | 461 ---------------------------
drivers/net/ethernet/cirrus/mac89x0.c | 577 ----------------------------------
4 files changed, 1049 deletions(-)
diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
index 1a0c7b3bfcd6..786d379e79fe 100644
--- a/drivers/net/ethernet/cirrus/Kconfig
+++ b/drivers/net/ethernet/cirrus/Kconfig
@@ -25,14 +25,4 @@ config EP93XX_ETH
This is a driver for the ethernet hardware included in EP93xx CPUs.
Say Y if you are building a kernel for EP93xx based devices.
-config MAC89x0
- tristate "Macintosh CS89x0 based ethernet cards"
- depends on MAC
- help
- Support for CS89x0 chipset based Ethernet cards. If you have a
- Nubus or LC-PDS network (Ethernet) card of this type, say Y here.
-
- To compile this driver as a module, choose M here. This module will
- be called mac89x0.
-
endif # NET_VENDOR_CIRRUS
diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile
index cb740939d976..03800af0f0e1 100644
--- a/drivers/net/ethernet/cirrus/Makefile
+++ b/drivers/net/ethernet/cirrus/Makefile
@@ -4,4 +4,3 @@
#
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
-obj-$(CONFIG_MAC89x0) += mac89x0.o
diff --git a/drivers/net/ethernet/cirrus/cs89x0.h b/drivers/net/ethernet/cirrus/cs89x0.h
deleted file mode 100644
index 210f9ec9af4b..000000000000
--- a/drivers/net/ethernet/cirrus/cs89x0.h
+++ /dev/null
@@ -1,461 +0,0 @@
-/* Copyright, 1988-1992, Russell Nelson, Crynwr Software
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, version 1.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */
- /* offset 2h -> Model/Product Number */
- /* offset 3h -> Chip Revision Number */
-
-#define PP_ISAIOB 0x0020 /* IO base address */
-#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */
-#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */
-#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */
-#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */
-#define PP_ISASOF 0x0026 /* ISA DMA offset */
-#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */
-#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */
-#define PP_CS8900_ISAMemB 0x002C /* Memory base */
-#define PP_CS8920_ISAMemB 0x0348 /* */
-
-#define PP_ISABootBase 0x0030 /* Boot Prom base */
-#define PP_ISABootMask 0x0034 /* Boot Prom Mask */
-
-/* EEPROM data and command registers */
-#define PP_EECMD 0x0040 /* NVR Interface Command register */
-#define PP_EEData 0x0042 /* NVR Interface Data Register */
-#define PP_DebugReg 0x0044 /* Debug Register */
-
-#define PP_RxCFG 0x0102 /* Rx Bus config */
-#define PP_RxCTL 0x0104 /* Receive Control Register */
-#define PP_TxCFG 0x0106 /* Transmit Config Register */
-#define PP_TxCMD 0x0108 /* Transmit Command Register */
-#define PP_BufCFG 0x010A /* Bus configuration Register */
-#define PP_LineCTL 0x0112 /* Line Config Register */
-#define PP_SelfCTL 0x0114 /* Self Command Register */
-#define PP_BusCTL 0x0116 /* ISA bus control Register */
-#define PP_TestCTL 0x0118 /* Test Register */
-#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */
-
-#define PP_ISQ 0x0120 /* Interrupt Status */
-#define PP_RxEvent 0x0124 /* Rx Event Register */
-#define PP_TxEvent 0x0128 /* Tx Event Register */
-#define PP_BufEvent 0x012C /* Bus Event Register */
-#define PP_RxMiss 0x0130 /* Receive Miss Count */
-#define PP_TxCol 0x0132 /* Transmit Collision Count */
-#define PP_LineST 0x0134 /* Line State Register */
-#define PP_SelfST 0x0136 /* Self State register */
-#define PP_BusST 0x0138 /* Bus Status */
-#define PP_TDR 0x013C /* Time Domain Reflectometry */
-#define PP_AutoNegST 0x013E /* Auto Neg Status */
-#define PP_TxCommand 0x0144 /* Tx Command */
-#define PP_TxLength 0x0146 /* Tx Length */
-#define PP_LAF 0x0150 /* Hash Table */
-#define PP_IA 0x0158 /* Physical Address Register */
-
-#define PP_RxStatus 0x0400 /* Receive start of frame */
-#define PP_RxLength 0x0402 /* Receive Length of frame */
-#define PP_RxFrame 0x0404 /* Receive frame pointer */
-#define PP_TxFrame 0x0A00 /* Transmit frame pointer */
-
-/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */
-/* can be used as the default I/O base to access the PacketPage Area. */
-#define DEFAULTIOBASE 0x0300
-#define FIRST_IO 0x020C /* First I/O port to check */
-#define LAST_IO 0x037C /* Last I/O port to check (+10h) */
-#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */
-#define ADD_SIG 0x3000 /* Expected ID signature */
-
-/* On Macs, we only need use the ISA I/O stuff until we do MEMORY_ON */
-#ifdef CONFIG_MAC
-#define LCSLOTBASE 0xfee00000
-#define MMIOBASE 0x40000
-#endif
-
-#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */
-#define CHIP_EISA_ID_SIG_STR "0x630E"
-
-#ifdef IBMEIPKT
-#define EISA_ID_SIG 0x4D24 /* IBM */
-#define PART_NO_SIG 0x1010 /* IBM */
-#define MONGOOSE_BIT 0x0000 /* IBM */
-#else
-#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */
-#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */
-#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */
-#endif
-
-#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */
-
-/* Mask to find out the types of registers */
-#define REG_TYPE_MASK 0x001F
-
-/* Eeprom Commands */
-#define ERSE_WR_ENBL 0x00F0
-#define ERSE_WR_DISABLE 0x0000
-
-/* Defines Control/Config register quintuplet numbers */
-#define RX_BUF_CFG 0x0003
-#define RX_CONTROL 0x0005
-#define TX_CFG 0x0007
-#define TX_COMMAND 0x0009
-#define BUF_CFG 0x000B
-#define LINE_CONTROL 0x0013
-#define SELF_CONTROL 0x0015
-#define BUS_CONTROL 0x0017
-#define TEST_CONTROL 0x0019
-
-/* Defines Status/Count registers quintuplet numbers */
-#define RX_EVENT 0x0004
-#define TX_EVENT 0x0008
-#define BUF_EVENT 0x000C
-#define RX_MISS_COUNT 0x0010
-#define TX_COL_COUNT 0x0012
-#define LINE_STATUS 0x0014
-#define SELF_STATUS 0x0016
-#define BUS_STATUS 0x0018
-#define TDR 0x001C
-
-/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */
-#define SKIP_1 0x0040
-#define RX_STREAM_ENBL 0x0080
-#define RX_OK_ENBL 0x0100
-#define RX_DMA_ONLY 0x0200
-#define AUTO_RX_DMA 0x0400
-#define BUFFER_CRC 0x0800
-#define RX_CRC_ERROR_ENBL 0x1000
-#define RX_RUNT_ENBL 0x2000
-#define RX_EXTRA_DATA_ENBL 0x4000
-
-/* PP_RxCTL - Receive Control bit definition - Read/write */
-#define RX_IA_HASH_ACCEPT 0x0040
-#define RX_PROM_ACCEPT 0x0080
-#define RX_OK_ACCEPT 0x0100
-#define RX_MULTCAST_ACCEPT 0x0200
-#define RX_IA_ACCEPT 0x0400
-#define RX_BROADCAST_ACCEPT 0x0800
-#define RX_BAD_CRC_ACCEPT 0x1000
-#define RX_RUNT_ACCEPT 0x2000
-#define RX_EXTRA_DATA_ACCEPT 0x4000
-#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT)
-/* Default receive mode - individually addressed, broadcast, and error free */
-#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT)
-
-/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */
-#define TX_LOST_CRS_ENBL 0x0040
-#define TX_SQE_ERROR_ENBL 0x0080
-#define TX_OK_ENBL 0x0100
-#define TX_LATE_COL_ENBL 0x0200
-#define TX_JBR_ENBL 0x0400
-#define TX_ANY_COL_ENBL 0x0800
-#define TX_16_COL_ENBL 0x8000
-
-/* PP_TxCMD - Transmit Command bit definition - Read-only */
-#define TX_START_4_BYTES 0x0000
-#define TX_START_64_BYTES 0x0040
-#define TX_START_128_BYTES 0x0080
-#define TX_START_ALL_BYTES 0x00C0
-#define TX_FORCE 0x0100
-#define TX_ONE_COL 0x0200
-#define TX_TWO_PART_DEFF_DISABLE 0x0400
-#define TX_NO_CRC 0x1000
-#define TX_RUNT 0x2000
-
-/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */
-#define GENERATE_SW_INTERRUPT 0x0040
-#define RX_DMA_ENBL 0x0080
-#define READY_FOR_TX_ENBL 0x0100
-#define TX_UNDERRUN_ENBL 0x0200
-#define RX_MISS_ENBL 0x0400
-#define RX_128_BYTE_ENBL 0x0800
-#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
-#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
-#define RX_DEST_MATCH_ENBL 0x8000
-
-/* PP_LineCTL - Line Control bit definition - Read/write */
-#define SERIAL_RX_ON 0x0040
-#define SERIAL_TX_ON 0x0080
-#define AUI_ONLY 0x0100
-#define AUTO_AUI_10BASET 0x0200
-#define MODIFIED_BACKOFF 0x0800
-#define NO_AUTO_POLARITY 0x1000
-#define TWO_PART_DEFDIS 0x2000
-#define LOW_RX_SQUELCH 0x4000
-
-/* PP_SelfCTL - Software Self Control bit definition - Read/write */
-#define POWER_ON_RESET 0x0040
-#define SW_STOP 0x0100
-#define SLEEP_ON 0x0200
-#define AUTO_WAKEUP 0x0400
-#define HCB0_ENBL 0x1000
-#define HCB1_ENBL 0x2000
-#define HCB0 0x4000
-#define HCB1 0x8000
-
-/* PP_BusCTL - ISA Bus Control bit definition - Read/write */
-#define RESET_RX_DMA 0x0040
-#define MEMORY_ON 0x0400
-#define DMA_BURST_MODE 0x0800
-#define IO_CHANNEL_READY_ON 0x1000
-#define RX_DMA_SIZE_64K 0x2000
-#define ENABLE_IRQ 0x8000
-
-/* PP_TestCTL - Test Control bit definition - Read/write */
-#define LINK_OFF 0x0080
-#define ENDEC_LOOPBACK 0x0200
-#define AUI_LOOPBACK 0x0400
-#define BACKOFF_OFF 0x0800
-#define FDX_8900 0x4000
-#define FAST_TEST 0x8000
-
-/* PP_RxEvent - Receive Event Bit definition - Read-only */
-#define RX_IA_HASHED 0x0040
-#define RX_DRIBBLE 0x0080
-#define RX_OK 0x0100
-#define RX_HASHED 0x0200
-#define RX_IA 0x0400
-#define RX_BROADCAST 0x0800
-#define RX_CRC_ERROR 0x1000
-#define RX_RUNT 0x2000
-#define RX_EXTRA_DATA 0x4000
-
-#define HASH_INDEX_MASK 0x0FC00
-
-/* PP_TxEvent - Transmit Event Bit definition - Read-only */
-#define TX_LOST_CRS 0x0040
-#define TX_SQE_ERROR 0x0080
-#define TX_OK 0x0100
-#define TX_LATE_COL 0x0200
-#define TX_JBR 0x0400
-#define TX_16_COL 0x8000
-#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS)
-#define TX_COL_COUNT_MASK 0x7800
-
-/* PP_BufEvent - Buffer Event Bit definition - Read-only */
-#define SW_INTERRUPT 0x0040
-#define RX_DMA 0x0080
-#define READY_FOR_TX 0x0100
-#define TX_UNDERRUN 0x0200
-#define RX_MISS 0x0400
-#define RX_128_BYTE 0x0800
-#define TX_COL_OVRFLW 0x1000
-#define RX_MISS_OVRFLW 0x2000
-#define RX_DEST_MATCH 0x8000
-
-/* PP_LineST - Ethernet Line Status bit definition - Read-only */
-#define LINK_OK 0x0080
-#define AUI_ON 0x0100
-#define TENBASET_ON 0x0200
-#define POLARITY_OK 0x1000
-#define CRS_OK 0x4000
-
-/* PP_SelfST - Chip Software Status bit definition */
-#define ACTIVE_33V 0x0040
-#define INIT_DONE 0x0080
-#define SI_BUSY 0x0100
-#define EEPROM_PRESENT 0x0200
-#define EEPROM_OK 0x0400
-#define EL_PRESENT 0x0800
-#define EE_SIZE_64 0x1000
-
-/* PP_BusST - ISA Bus Status bit definition */
-#define TX_BID_ERROR 0x0080
-#define READY_FOR_TX_NOW 0x0100
-
-/* PP_AutoNegCTL - Auto Negotiation Control bit definition */
-#define RE_NEG_NOW 0x0040
-#define ALLOW_FDX 0x0080
-#define AUTO_NEG_ENABLE 0x0100
-#define NLP_ENABLE 0x0200
-#define FORCE_FDX 0x8000
-#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE)
-#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW)
-
-/* PP_AutoNegST - Auto Negotiation Status bit definition */
-#define AUTO_NEG_BUSY 0x0080
-#define FLP_LINK 0x0100
-#define FLP_LINK_GOOD 0x0800
-#define LINK_FAULT 0x1000
-#define HDX_ACTIVE 0x4000
-#define FDX_ACTIVE 0x8000
-
-/* The following block defines the ISQ event types */
-#define ISQ_RECEIVER_EVENT 0x04
-#define ISQ_TRANSMITTER_EVENT 0x08
-#define ISQ_BUFFER_EVENT 0x0c
-#define ISQ_RX_MISS_EVENT 0x10
-#define ISQ_TX_COL_EVENT 0x12
-
-#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */
-#define ISQ_HIST 16 /* small history buffer */
-#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */
-
-#define TXRXBUFSIZE 0x0600
-#define RXDMABUFSIZE 0x8000
-#define RXDMASIZE 0x4000
-#define TXRX_LENGTH_MASK 0x07FF
-
-/* rx options bits */
-#define RCV_WITH_RXON 1 /* Set SerRx ON */
-#define RCV_COUNTS 2 /* Use Framecnt1 */
-#define RCV_PONG 4 /* Pong respondent */
-#define RCV_DONG 8 /* Dong operation */
-#define RCV_POLLING 0x10 /* Poll RxEvent */
-#define RCV_ISQ 0x20 /* Use ISQ, int */
-#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */
-#define RCV_DMA 0x200 /* Set RxDMA only */
-#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */
-#define RCV_FIXED_DATA 0x800 /* Every frame same */
-#define RCV_IO 0x1000 /* Use ISA IO only */
-#define RCV_MEMORY 0x2000 /* Use ISA Memory */
-
-#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */
-#define PKT_START PP_TxFrame /* Start of packet RAM */
-
-#define RX_FRAME_PORT 0x0000
-#define TX_FRAME_PORT RX_FRAME_PORT
-#define TX_CMD_PORT 0x0004
-#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */
-#define TX_AFTER_381 0x0040 /* Tx packet after 381 bytes copied */
-#define TX_AFTER_ALL 0x00c0 /* Tx packet after all bytes copied */
-#define TX_LEN_PORT 0x0006
-#define ISQ_PORT 0x0008
-#define ADD_PORT 0x000A
-#define DATA_PORT 0x000C
-
-#define EEPROM_WRITE_EN 0x00F0
-#define EEPROM_WRITE_DIS 0x0000
-#define EEPROM_WRITE_CMD 0x0100
-#define EEPROM_READ_CMD 0x0200
-
-/* Receive Header */
-/* Description of header of each packet in receive area of memory */
-#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */
-#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */
-#define RBUF_LEN_LOW 2 /* Length of received data - low byte */
-#define RBUF_LEN_HI 3 /* Length of received data - high byte */
-#define RBUF_HEAD_LEN 4 /* Length of this header */
-
-#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */
-#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */
-
-/* for bios scan */
-/* */
-#ifdef CSDEBUG
-/* use these values for debugging bios scan */
-#define BIOS_START_SEG 0x00000
-#define BIOS_OFFSET_INC 0x0010
-#else
-#define BIOS_START_SEG 0x0c000
-#define BIOS_OFFSET_INC 0x0200
-#endif
-
-#define BIOS_LAST_OFFSET 0x0fc00
-
-/* Byte offsets into the EEPROM configuration buffer */
-#define ISA_CNF_OFFSET 0x6
-#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */
-#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */
-
- /* the assumption here is that the bits in the eeprom are generally */
- /* in the same position as those in the autonegctl register. */
- /* Of course the IMM bit is not in that register so it must be */
- /* masked out */
-#define EE_FORCE_FDX 0x8000
-#define EE_NLP_ENABLE 0x0200
-#define EE_AUTO_NEG_ENABLE 0x0100
-#define EE_ALLOW_FDX 0x0080
-#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX)
-
-#define IMM_BIT 0x0040 /* ignore missing media */
-
-#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2)
-#define A_CNF_10B_T 0x0001
-#define A_CNF_AUI 0x0002
-#define A_CNF_10B_2 0x0004
-#define A_CNF_MEDIA_TYPE 0x0070
-#define A_CNF_MEDIA_AUTO 0x0070
-#define A_CNF_MEDIA_10B_T 0x0020
-#define A_CNF_MEDIA_AUI 0x0040
-#define A_CNF_MEDIA_10B_2 0x0010
-#define A_CNF_DC_DC_POLARITY 0x0080
-#define A_CNF_NO_AUTO_POLARITY 0x2000
-#define A_CNF_LOW_RX_SQUELCH 0x4000
-#define A_CNF_EXTND_10B_2 0x8000
-
-#define PACKET_PAGE_OFFSET 0x8
-
-/* Bit definitions for the ISA configuration word from the EEPROM */
-#define INT_NO_MASK 0x000F
-#define DMA_NO_MASK 0x0070
-#define ISA_DMA_SIZE 0x0200
-#define ISA_AUTO_RxDMA 0x0400
-#define ISA_RxDMA 0x0800
-#define DMA_BURST 0x1000
-#define STREAM_TRANSFER 0x2000
-#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA)
-
-/* DMA controller registers */
-#define DMA_BASE 0x00 /* DMA controller base */
-#define DMA_BASE_2 0x0C0 /* DMA controller base */
-
-#define DMA_STAT 0x0D0 /* DMA controller status register */
-#define DMA_MASK 0x0D4 /* DMA controller mask register */
-#define DMA_MODE 0x0D6 /* DMA controller mode register */
-#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */
-
-/* DMA data */
-#define DMA_DISABLE 0x04 /* Disable channel n */
-#define DMA_ENABLE 0x00 /* Enable channel n */
-/* Demand transfers, incr. address, auto init, writes, ch. n */
-#define DMA_RX_MODE 0x14
-/* Demand transfers, incr. address, auto init, reads, ch. n */
-#define DMA_TX_MODE 0x18
-
-#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */
-
-#define CS8900 0x0000
-#define CS8920 0x4000
-#define CS8920M 0x6000
-#define REVISON_BITS 0x1F00
-#define EEVER_NUMBER 0x12
-#define CHKSUM_LEN 0x14
-#define CHKSUM_VAL 0x0000
-#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */
-#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */
-#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */
-#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */
-#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */
-
-#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */
-
-#define PNP_ADD_PORT 0x0279
-#define PNP_WRITE_PORT 0x0A79
-
-#define GET_PNP_ISA_STRUCT 0x40
-#define PNP_ISA_STRUCT_LEN 0x06
-#define PNP_CSN_CNT_OFF 0x01
-#define PNP_RD_PORT_OFF 0x02
-#define PNP_FUNCTION_OK 0x00
-#define PNP_WAKE 0x03
-#define PNP_RSRC_DATA 0x04
-#define PNP_RSRC_READY 0x01
-#define PNP_STATUS 0x05
-#define PNP_ACTIVATE 0x30
-#define PNP_CNF_IO_H 0x60
-#define PNP_CNF_IO_L 0x61
-#define PNP_CNF_INT 0x70
-#define PNP_CNF_DMA 0x74
-#define PNP_CNF_MEM 0x48
diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
deleted file mode 100644
index 6723df9b65d9..000000000000
--- a/drivers/net/ethernet/cirrus/mac89x0.c
+++ /dev/null
@@ -1,577 +0,0 @@
-/* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
-/*
- Written 1996 by Russell Nelson, with reference to skeleton.c
- written 1993-1994 by Donald Becker.
-
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- The author may be reached at nelson@crynwr.com, Crynwr
- Software, 11 Grant St., Potsdam, NY 13676
-
- Changelog:
-
- Mike Cruse : mcruse@cti-ltd.com
- : Changes for Linux 2.0 compatibility.
- : Added dev_id parameter in net_interrupt(),
- : request_irq() and free_irq(). Just NULL for now.
-
- Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
- : in net_open() and net_close() so kerneld would know
- : that the module is in use and wouldn't eject the
- : driver prematurely.
-
- Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c
- : as an example. Disabled autoprobing in init_module(),
- : not a good thing to do to other devices while Linux
- : is running from all accounts.
-
- Alan Cox : Removed 1.2 support, added 2.1 extra counters.
-
- David Huggins-Daines <dhd@debian.org>
-
- Split this off into mac89x0.c, and gutted it of all parts which are
- not relevant to the existing CS8900 cards on the Macintosh
- (i.e. basically the Daynaport CS and LC cards). To be precise:
-
- * Removed all the media-detection stuff, because these cards are
- TP-only.
-
- * Lobotomized the ISA interrupt bogosity, because these cards use
- a hardwired NuBus interrupt and a magic ISAIRQ value in the card.
-
- * Basically eliminated everything not relevant to getting the
- cards minimally functioning on the Macintosh.
-
- I might add that these cards are badly designed even from the Mac
- standpoint, in that Dayna, in their infinite wisdom, used NuBus slot
- I/O space and NuBus interrupts for these cards, but neglected to
- provide anything even remotely resembling a NuBus ROM. Therefore we
- have to probe for them in a brain-damaged ISA-like fashion.
-
- Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
- check kmalloc and release the allocated memory on failure in
- mac89x0_probe and in init_module
- use local_irq_{save,restore}(flags) in net_get_stat, not just
- local_irq_{dis,en}able()
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-static const char version[] =
-"cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
-
-#include <linux/module.h>
-
-/*
- Sources:
-
- Crynwr packet driver epktisa.
-
- Crystal Semiconductor data sheets.
-
-*/
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/nubus.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/platform_device.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-#include <linux/gfp.h>
-
-#include <asm/io.h>
-#include <asm/hwtest.h>
-#include <asm/macints.h>
-
-#include "cs89x0.h"
-
-static int debug = -1;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "debug message level");
-
-/* Information that need to be kept for each board. */
-struct net_local {
- int msg_enable;
- int chip_type; /* one of: CS8900, CS8920, CS8920M */
- char chip_revision; /* revision letter of the chip ('A'...) */
- int send_cmd; /* the propercommand used to send a packet. */
- int rx_mode;
- int curr_rx_cfg;
- int send_underrun; /* keep track of how many underruns in a row we get */
-};
-
-/* Index to functions, as function prototypes. */
-static int net_open(struct net_device *dev);
-static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t net_interrupt(int irq, void *dev_id);
-static void set_multicast_list(struct net_device *dev);
-static void net_rx(struct net_device *dev);
-static int net_close(struct net_device *dev);
-static struct net_device_stats *net_get_stats(struct net_device *dev);
-static int set_mac_address(struct net_device *dev, void *addr);
-
-/* For reading/writing registers ISA-style */
-static inline int
-readreg_io(struct net_device *dev, int portno)
-{
- nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
- return swab16(nubus_readw(dev->base_addr + DATA_PORT));
-}
-
-static inline void
-writereg_io(struct net_device *dev, int portno, int value)
-{
- nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
- nubus_writew(swab16(value), dev->base_addr + DATA_PORT);
-}
-
-/* These are for reading/writing registers in shared memory */
-static inline int
-readreg(struct net_device *dev, int portno)
-{
- return swab16(nubus_readw(dev->mem_start + portno));
-}
-
-static inline void
-writereg(struct net_device *dev, int portno, int value)
-{
- nubus_writew(swab16(value), dev->mem_start + portno);
-}
-
-static const struct net_device_ops mac89x0_netdev_ops = {
- .ndo_open = net_open,
- .ndo_stop = net_close,
- .ndo_start_xmit = net_send_packet,
- .ndo_get_stats = net_get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = set_mac_address,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-/* Probe for the CS8900 card in slot E. We won't bother looking
- anywhere else until we have a really good reason to do so. */
-static int mac89x0_device_probe(struct platform_device *pdev)
-{
- struct net_device *dev;
- struct net_local *lp;
- int i, slot;
- unsigned rev_type = 0;
- unsigned long ioaddr;
- unsigned short sig;
- int err = -ENODEV;
- struct nubus_rsrc *fres;
-
- dev = alloc_etherdev(sizeof(struct net_local));
- if (!dev)
- return -ENOMEM;
-
- /* We might have to parameterize this later */
- slot = 0xE;
- /* Get out now if there's a real NuBus card in slot E */
- for_each_func_rsrc(fres)
- if (fres->board->slot == slot)
- goto out;
-
- /* The pseudo-ISA bits always live at offset 0x300 (gee,
- wonder why...) */
- ioaddr = (unsigned long)
- nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE);
- {
- int card_present;
-
- card_present = (hwreg_present((void *)ioaddr + 4) &&
- hwreg_present((void *)ioaddr + DATA_PORT));
- if (!card_present)
- goto out;
- }
-
- nubus_writew(0, ioaddr + ADD_PORT);
- sig = nubus_readw(ioaddr + DATA_PORT);
- if (sig != swab16(CHIP_EISA_ID_SIG))
- goto out;
-
- SET_NETDEV_DEV(dev, &pdev->dev);
-
- /* Initialize the net_device structure. */
- lp = netdev_priv(dev);
-
- lp->msg_enable = netif_msg_init(debug, 0);
-
- /* Fill in the 'dev' fields. */
- dev->base_addr = ioaddr;
- dev->mem_start = (unsigned long)
- nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE);
- dev->mem_end = dev->mem_start + 0x1000;
-
- /* Turn on shared memory */
- writereg_io(dev, PP_BusCTL, MEMORY_ON);
-
- /* get the chip type */
- rev_type = readreg(dev, PRODUCT_ID_ADD);
- lp->chip_type = rev_type &~ REVISON_BITS;
- lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
-
- /* Check the chip type and revision in order to set the correct send command
- CS8920 revision C and CS8900 revision F can use the faster send. */
- lp->send_cmd = TX_AFTER_381;
- if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
- lp->send_cmd = TX_NOW;
- if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
- lp->send_cmd = TX_NOW;
-
- netif_dbg(lp, drv, dev, "%s", version);
-
- pr_info("cs89%c0%s rev %c found at %#8lx\n",
- lp->chip_type == CS8900 ? '0' : '2',
- lp->chip_type == CS8920M ? "M" : "",
- lp->chip_revision, dev->base_addr);
-
- /* Try to read the MAC address */
- if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
- pr_info("No EEPROM, giving up now.\n");
- goto out1;
- } else {
- u8 addr[ETH_ALEN];
-
- for (i = 0; i < ETH_ALEN; i += 2) {
- /* Big-endian (why??!) */
- unsigned short s = readreg(dev, PP_IA + i);
- addr[i] = s >> 8;
- addr[i+1] = s & 0xff;
- }
- eth_hw_addr_set(dev, addr);
- }
-
- dev->irq = SLOT2IRQ(slot);
-
- /* print the IRQ and ethernet address. */
-
- pr_info("MAC %pM, IRQ %d\n", dev->dev_addr, dev->irq);
-
- dev->netdev_ops = &mac89x0_netdev_ops;
-
- err = register_netdev(dev);
- if (err)
- goto out1;
-
- platform_set_drvdata(pdev, dev);
- return 0;
-out1:
- nubus_writew(0, dev->base_addr + ADD_PORT);
-out:
- free_netdev(dev);
- return err;
-}
-
-/* Open/initialize the board. This is called (in the current kernel)
- sometime after booting when the 'ifconfig' program is run.
-
- This routine should set everything up anew at each open, even
- registers that "should" only need to be set once at boot, so that
- there is non-reboot way to recover if something goes wrong.
- */
-static int
-net_open(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- int i;
-
- /* Disable the interrupt for now */
- writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
-
- /* Grab the interrupt */
- if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev))
- return -EAGAIN;
-
- /* Set up the IRQ - Apparently magic */
- if (lp->chip_type == CS8900)
- writereg(dev, PP_CS8900_ISAINT, 0);
- else
- writereg(dev, PP_CS8920_ISAINT, 0);
-
- /* set the Ethernet address */
- for (i=0; i < ETH_ALEN/2; i++)
- writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
-
- /* Turn on both receive and transmit operations */
- writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
-
- /* Receive only error free packets addressed to this card */
- lp->rx_mode = 0;
- writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
-
- lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
-
- writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
-
- writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
- TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
-
- writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
- TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
-
- /* now that we've got our act together, enable everything */
- writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
- netif_start_queue(dev);
- return 0;
-}
-
-static netdev_tx_t
-net_send_packet(struct sk_buff *skb, struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- unsigned long flags;
-
- netif_dbg(lp, tx_queued, dev, "sent %d byte packet of type %x\n",
- skb->len, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
- skb->data[ETH_ALEN + ETH_ALEN + 1]);
-
- /* keep the upload from being interrupted, since we
- ask the chip to start transmitting before the
- whole packet has been completely uploaded. */
- local_irq_save(flags);
- netif_stop_queue(dev);
-
- /* initiate a transmit sequence */
- writereg(dev, PP_TxCMD, lp->send_cmd);
- writereg(dev, PP_TxLength, skb->len);
-
- /* Test to see if the chip has allocated memory for the packet */
- if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
- /* Gasp! It hasn't. But that shouldn't happen since
- we're waiting for TxOk, so return 1 and requeue this packet. */
- local_irq_restore(flags);
- return NETDEV_TX_BUSY;
- }
-
- /* Write the contents of the packet */
- skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame),
- skb->len+1);
-
- local_irq_restore(flags);
- dev_kfree_skb (skb);
-
- return NETDEV_TX_OK;
-}
-
-/* The typical workload of the driver:
- Handle the network interface interrupts. */
-static irqreturn_t net_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct net_local *lp;
- int ioaddr, status;
-
- ioaddr = dev->base_addr;
- lp = netdev_priv(dev);
-
- /* we MUST read all the events out of the ISQ, otherwise we'll never
- get interrupted again. As a consequence, we can't have any limit
- on the number of times we loop in the interrupt handler. The
- hardware guarantees that eventually we'll run out of events. Of
- course, if you're on a slow machine, and packets are arriving
- faster than you can read them off, you're screwed. Hasta la
- vista, baby! */
- while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) {
- netif_dbg(lp, intr, dev, "status=%04x\n", status);
- switch(status & ISQ_EVENT_MASK) {
- case ISQ_RECEIVER_EVENT:
- /* Got a packet(s). */
- net_rx(dev);
- break;
- case ISQ_TRANSMITTER_EVENT:
- dev->stats.tx_packets++;
- netif_wake_queue(dev);
- if ((status & TX_OK) == 0)
- dev->stats.tx_errors++;
- if (status & TX_LOST_CRS)
- dev->stats.tx_carrier_errors++;
- if (status & TX_SQE_ERROR)
- dev->stats.tx_heartbeat_errors++;
- if (status & TX_LATE_COL)
- dev->stats.tx_window_errors++;
- if (status & TX_16_COL)
- dev->stats.tx_aborted_errors++;
- break;
- case ISQ_BUFFER_EVENT:
- if (status & READY_FOR_TX) {
- /* we tried to transmit a packet earlier,
- but inexplicably ran out of buffers.
- That shouldn't happen since we only ever
- load one packet. Shrug. Do the right
- thing anyway. */
- netif_wake_queue(dev);
- }
- if (status & TX_UNDERRUN) {
- netif_dbg(lp, tx_err, dev, "transmit underrun\n");
- lp->send_underrun++;
- if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
- else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
- }
- break;
- case ISQ_RX_MISS_EVENT:
- dev->stats.rx_missed_errors += (status >> 6);
- break;
- case ISQ_TX_COL_EVENT:
- dev->stats.collisions += (status >> 6);
- break;
- }
- }
- return IRQ_HANDLED;
-}
-
-/* We have a good packet(s), get it/them out of the buffers. */
-static void
-net_rx(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
- struct sk_buff *skb;
- int status, length;
-
- status = readreg(dev, PP_RxStatus);
- if ((status & RX_OK) == 0) {
- dev->stats.rx_errors++;
- if (status & RX_RUNT)
- dev->stats.rx_length_errors++;
- if (status & RX_EXTRA_DATA)
- dev->stats.rx_length_errors++;
- if ((status & RX_CRC_ERROR) &&
- !(status & (RX_EXTRA_DATA|RX_RUNT)))
- /* per str 172 */
- dev->stats.rx_crc_errors++;
- if (status & RX_DRIBBLE)
- dev->stats.rx_frame_errors++;
- return;
- }
-
- length = readreg(dev, PP_RxLength);
- /* Malloc up new buffer. */
- skb = alloc_skb(length, GFP_ATOMIC);
- if (skb == NULL) {
- dev->stats.rx_dropped++;
- return;
- }
- skb_put(skb, length);
-
- skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame),
- length);
-
- netif_dbg(lp, rx_status, dev, "received %d byte packet of type %x\n",
- length, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
- skb->data[ETH_ALEN + ETH_ALEN + 1]);
-
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += length;
-}
-
-/* The inverse routine to net_open(). */
-static int
-net_close(struct net_device *dev)
-{
-
- writereg(dev, PP_RxCFG, 0);
- writereg(dev, PP_TxCFG, 0);
- writereg(dev, PP_BufCFG, 0);
- writereg(dev, PP_BusCTL, 0);
-
- netif_stop_queue(dev);
-
- free_irq(dev->irq, dev);
-
- /* Update the statistics here. */
-
- return 0;
-
-}
-
-/* Get the current statistics. This may be called with the card open or
- closed. */
-static struct net_device_stats *
-net_get_stats(struct net_device *dev)
-{
- unsigned long flags;
-
- local_irq_save(flags);
- /* Update the statistics from the device registers. */
- dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
- dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
- local_irq_restore(flags);
-
- return &dev->stats;
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
- struct net_local *lp = netdev_priv(dev);
-
- if(dev->flags&IFF_PROMISC)
- {
- lp->rx_mode = RX_ALL_ACCEPT;
- } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
- /* The multicast-accept list is initialized to accept-all, and we
- rely on higher-level filtering for now. */
- lp->rx_mode = RX_MULTCAST_ACCEPT;
- }
- else
- lp->rx_mode = 0;
-
- writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
-
- /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
- writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
- (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
-}
-
-
-static int set_mac_address(struct net_device *dev, void *addr)
-{
- struct sockaddr *saddr = addr;
- int i;
-
- if (!is_valid_ether_addr(saddr->sa_data))
- return -EADDRNOTAVAIL;
-
- eth_hw_addr_set(dev, saddr->sa_data);
- netdev_info(dev, "Setting MAC address to %pM\n", dev->dev_addr);
-
- /* set the Ethernet address */
- for (i=0; i < ETH_ALEN/2; i++)
- writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
-
- return 0;
-}
-
-MODULE_DESCRIPTION("Macintosh CS89x0-based Ethernet driver");
-MODULE_LICENSE("GPL");
-
-static void mac89x0_device_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
-
- unregister_netdev(dev);
- nubus_writew(0, dev->base_addr + ADD_PORT);
- free_netdev(dev);
-}
-
-static struct platform_driver mac89x0_platform_driver = {
- .probe = mac89x0_device_probe,
- .remove = mac89x0_device_remove,
- .driver = {
- .name = "mac89x0",
- },
-};
-
-module_platform_driver(mac89x0_platform_driver);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 11/15] drivers: net: fujitsu: fmvj18x: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (9 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 12/15] drivers: net: 8390: AX88190: " Andrew Lunn
` (3 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The fmvj18x was written by Shingo Fujimoto in 2002. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/Kconfig | 1 -
drivers/net/ethernet/fujitsu/Kconfig | 30 -
drivers/net/ethernet/fujitsu/Makefile | 6 -
drivers/net/ethernet/fujitsu/fmvj18x_cs.c | 1176 -----------------------------
4 files changed, 1213 deletions(-)
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index bdc29d143160..c94e8f27af94 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -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"
diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig
deleted file mode 100644
index 06a28bce5d27..000000000000
--- a/drivers/net/ethernet/fujitsu/Kconfig
+++ /dev/null
@@ -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
diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile
deleted file mode 100644
index 74feebbf4572..000000000000
--- a/drivers/net/ethernet/fujitsu/Makefile
+++ /dev/null
@@ -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
diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
deleted file mode 100644
index 4859493471db..000000000000
--- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c
+++ /dev/null
@@ -1,1176 +0,0 @@
-/*======================================================================
- fmvj18x_cs.c 2.8 2002/03/23
-
- A fmvj18x (and its compatibles) PCMCIA client driver
-
- Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp
-
- TDK LAK-CD021 and CONTEC C-NET(PC)C support added by
- Nobuhiro Katayama, kata-n@po.iijnet.or.jp
-
- The PCMCIA client code is based on code written by David Hinds.
- Network code is based on the "FMV-18x driver" by Yutaka TAMIYA
- but is actually largely Donald Becker's AT1700 driver, which
- carries the following attribution:
-
- Written 1993-94 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.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME "fmvj18x_cs"
-#define DRV_VERSION "2.9"
-
-#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/crc32.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_DESCRIPTION("fmvj18x and compatible PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* SRAM configuration */
-/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */
-INT_MODULE_PARM(sram_config, 0);
-
-
-/*====================================================================*/
-/*
- PCMCIA event handlers
- */
-static int fmvj18x_config(struct pcmcia_device *link);
-static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id);
-static int fmvj18x_setup_mfc(struct pcmcia_device *link);
-static void fmvj18x_release(struct pcmcia_device *link);
-static void fmvj18x_detach(struct pcmcia_device *p_dev);
-
-/*
- LAN controller(MBH86960A) specific routines
- */
-static int fjn_config(struct net_device *dev, struct ifmap *map);
-static int fjn_open(struct net_device *dev);
-static int fjn_close(struct net_device *dev);
-static netdev_tx_t fjn_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static irqreturn_t fjn_interrupt(int irq, void *dev_id);
-static void fjn_rx(struct net_device *dev);
-static void fjn_reset(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-/*
- card type
- */
-enum cardtype { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN,
- XXX10304, NEC, KME
-};
-
-/*
- driver specific data structure
-*/
-struct local_info {
- struct pcmcia_device *p_dev;
- long open_time;
- uint tx_started:1;
- uint tx_queue;
- u_short tx_queue_len;
- enum cardtype cardtype;
- u_short sent;
- u_char __iomem *base;
-};
-
-#define MC_FILTERBREAK 64
-
-/*====================================================================*/
-/*
- ioport offset from the base address
- */
-#define TX_STATUS 0 /* transmit status register */
-#define RX_STATUS 1 /* receive status register */
-#define TX_INTR 2 /* transmit interrupt mask register */
-#define RX_INTR 3 /* receive interrupt mask register */
-#define TX_MODE 4 /* transmit mode register */
-#define RX_MODE 5 /* receive mode register */
-#define CONFIG_0 6 /* configuration register 0 */
-#define CONFIG_1 7 /* configuration register 1 */
-
-#define NODE_ID 8 /* node ID register (bank 0) */
-#define MAR_ADR 8 /* multicast address registers (bank 1) */
-
-#define DATAPORT 8 /* buffer mem port registers (bank 2) */
-#define TX_START 10 /* transmit start register */
-#define COL_CTRL 11 /* 16 collision control register */
-#define BMPR12 12 /* reserved */
-#define BMPR13 13 /* reserved */
-#define RX_SKIP 14 /* skip received packet register */
-
-#define LAN_CTRL 16 /* LAN card control register */
-
-#define MAC_ID 0x1a /* hardware address */
-#define UNGERMANN_MAC_ID 0x18 /* UNGERMANN-BASS hardware address */
-
-/*
- control bits
- */
-#define ENA_TMT_OK 0x80
-#define ENA_TMT_REC 0x20
-#define ENA_COL 0x04
-#define ENA_16_COL 0x02
-#define ENA_TBUS_ERR 0x01
-
-#define ENA_PKT_RDY 0x80
-#define ENA_BUS_ERR 0x40
-#define ENA_LEN_ERR 0x08
-#define ENA_ALG_ERR 0x04
-#define ENA_CRC_ERR 0x02
-#define ENA_OVR_FLO 0x01
-
-/* flags */
-#define F_TMT_RDY 0x80 /* can accept new packet */
-#define F_NET_BSY 0x40 /* carrier is detected */
-#define F_TMT_OK 0x20 /* send packet successfully */
-#define F_SRT_PKT 0x10 /* short packet error */
-#define F_COL_ERR 0x04 /* collision error */
-#define F_16_COL 0x02 /* 16 collision error */
-#define F_TBUS_ERR 0x01 /* bus read error */
-
-#define F_PKT_RDY 0x80 /* packet(s) in buffer */
-#define F_BUS_ERR 0x40 /* bus read error */
-#define F_LEN_ERR 0x08 /* short packet */
-#define F_ALG_ERR 0x04 /* frame error */
-#define F_CRC_ERR 0x02 /* CRC error */
-#define F_OVR_FLO 0x01 /* overflow error */
-
-#define F_BUF_EMP 0x40 /* receive buffer is empty */
-
-#define F_SKP_PKT 0x05 /* drop packet in buffer */
-
-/* default bitmaps */
-#define D_TX_INTR ( ENA_TMT_OK )
-#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \
- | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO )
-#define TX_STAT_M ( F_TMT_RDY )
-#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \
- | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO )
-
-/* commands */
-#define D_TX_MODE 0x06 /* no tests, detect carrier */
-#define ID_MATCHED 0x02 /* (RX_MODE) */
-#define RECV_ALL 0x03 /* (RX_MODE) */
-#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */
-#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */
-#define CONFIG0_RST 0xda /* Data Link Controller off (CONFIG_0) */
-#define CONFIG0_RST_1 0xde /* Data Link Controller off (CONFIG_0) */
-#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */
-#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */
-#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */
-#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */
-#define DO_TX 0x80 /* do transmit packet */
-#define SEND_PKT 0x81 /* send a packet */
-#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */
-#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */
-#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */
-#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */
-#define INTR_OFF 0x0d /* LAN controller ignores interrupts */
-#define INTR_ON 0x1d /* LAN controller will catch interrupts */
-
-#define TX_TIMEOUT ((400*HZ)/1000)
-
-#define BANK_0U 0x20 /* bank 0 (CONFIG_1) */
-#define BANK_1U 0x24 /* bank 1 (CONFIG_1) */
-#define BANK_2U 0x28 /* bank 2 (CONFIG_1) */
-
-static const struct net_device_ops fjn_netdev_ops = {
- .ndo_open = fjn_open,
- .ndo_stop = fjn_close,
- .ndo_start_xmit = fjn_start_xmit,
- .ndo_tx_timeout = fjn_tx_timeout,
- .ndo_set_config = fjn_config,
- .ndo_set_rx_mode = set_rx_mode,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int fmvj18x_probe(struct pcmcia_device *link)
-{
- struct local_info *lp;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "fmvj18x_attach()\n");
-
- /* Make up a FMVJ18x specific data structure */
- dev = alloc_etherdev(sizeof(struct local_info));
- if (!dev)
- return -ENOMEM;
- lp = netdev_priv(dev);
- link->priv = dev;
- lp->p_dev = link;
- lp->base = NULL;
-
- /* The io structure describes IO port mapping */
- link->resource[0]->end = 32;
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
-
- /* General socket configuration */
- link->config_flags |= CONF_ENABLE_IRQ;
-
- dev->netdev_ops = &fjn_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
-
- dev->ethtool_ops = &netdev_ethtool_ops;
-
- return fmvj18x_config(link);
-} /* fmvj18x_attach */
-
-/*====================================================================*/
-
-static void fmvj18x_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "fmvj18x_detach\n");
-
- unregister_netdev(dev);
-
- fmvj18x_release(link);
-
- free_netdev(dev);
-} /* fmvj18x_detach */
-
-/*====================================================================*/
-
-static int mfc_try_io_port(struct pcmcia_device *link)
-{
- int i, ret;
- static const unsigned int serial_base[5] =
- { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
-
- for (i = 0; i < 5; i++) {
- link->resource[1]->start = serial_base[i];
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- if (link->resource[1]->start == 0) {
- link->resource[1]->end = 0;
- pr_notice("out of resource for serial\n");
- }
- ret = pcmcia_request_io(link);
- if (ret == 0)
- return ret;
- }
- return ret;
-}
-
-static int ungermann_try_io_port(struct pcmcia_device *link)
-{
- int ret;
- unsigned int ioaddr;
- /*
- Ungermann-Bass Access/CARD accepts 0x300,0x320,0x340,0x360
- 0x380,0x3c0 only for ioport.
- */
- for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) {
- link->resource[0]->start = ioaddr;
- ret = pcmcia_request_io(link);
- if (ret == 0) {
- /* calculate ConfigIndex value */
- link->config_index =
- ((link->resource[0]->start & 0x0f0) >> 3) | 0x22;
- return ret;
- }
- }
- return ret; /* RequestIO failed */
-}
-
-static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
-{
- return 0; /* strange, but that's what the code did already before... */
-}
-
-static int fmvj18x_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct local_info *lp = netdev_priv(dev);
- int i, ret;
- unsigned int ioaddr;
- enum cardtype cardtype;
- char *card_name = "unknown";
- u8 *buf;
- size_t len;
- u_char buggybuf[32];
- u8 addr[ETH_ALEN];
-
- dev_dbg(&link->dev, "fmvj18x_config\n");
-
- link->io_lines = 5;
-
- len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf);
- kfree(buf);
-
- if (len) {
- /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
- ret = pcmcia_loop_config(link, fmvj18x_ioprobe, NULL);
- if (ret != 0)
- goto failed;
-
- switch (link->manf_id) {
- case MANFID_TDK:
- cardtype = TDK;
- if (link->card_id == PRODID_TDK_GN3410 ||
- link->card_id == PRODID_TDK_NP9610 ||
- link->card_id == PRODID_TDK_MN3200) {
- /* MultiFunction Card */
- link->config_base = 0x800;
- link->config_index = 0x47;
- link->resource[1]->end = 8;
- }
- break;
- case MANFID_NEC:
- cardtype = NEC; /* MultiFunction Card */
- link->config_base = 0x800;
- link->config_index = 0x47;
- link->resource[1]->end = 8;
- break;
- case MANFID_KME:
- cardtype = KME; /* MultiFunction Card */
- link->config_base = 0x800;
- link->config_index = 0x47;
- link->resource[1]->end = 8;
- break;
- case MANFID_CONTEC:
- cardtype = CONTEC;
- break;
- case MANFID_FUJITSU:
- if (link->config_base == 0x0fe0)
- cardtype = MBH10302;
- else if (link->card_id == PRODID_FUJITSU_MBH10302)
- /* RATOC REX-5588/9822/4886's PRODID are 0004(=MBH10302),
- but these are MBH10304 based card. */
- cardtype = MBH10304;
- else if (link->card_id == PRODID_FUJITSU_MBH10304)
- cardtype = MBH10304;
- else
- cardtype = LA501;
- break;
- default:
- cardtype = MBH10304;
- }
- } else {
- /* old type card */
- switch (link->manf_id) {
- case MANFID_FUJITSU:
- if (link->card_id == PRODID_FUJITSU_MBH10304) {
- cardtype = XXX10304; /* MBH10304 with buggy CIS */
- link->config_index = 0x20;
- } else {
- cardtype = MBH10302; /* NextCom NC5310, etc. */
- link->config_index = 1;
- }
- break;
- case MANFID_UNGERMANN:
- cardtype = UNGERMANN;
- break;
- default:
- cardtype = MBH10302;
- link->config_index = 1;
- }
- }
-
- if (link->resource[1]->end != 0) {
- ret = mfc_try_io_port(link);
- if (ret != 0) goto failed;
- } else if (cardtype == UNGERMANN) {
- ret = ungermann_try_io_port(link);
- if (ret != 0) goto failed;
- } else {
- ret = pcmcia_request_io(link);
- if (ret)
- goto failed;
- }
- ret = pcmcia_request_irq(link, fjn_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;
-
- if (resource_size(link->resource[1]) != 0) {
- ret = fmvj18x_setup_mfc(link);
- if (ret != 0) goto failed;
- }
-
- ioaddr = dev->base_addr;
-
- /* Reset controller */
- if (sram_config == 0)
- outb(CONFIG0_RST, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
-
- /* Power On chip and select bank 0 */
- if (cardtype == MBH10302)
- outb(BANK_0, ioaddr + CONFIG_1);
- else
- outb(BANK_0U, ioaddr + CONFIG_1);
-
- /* Set hardware address */
- switch (cardtype) {
- case MBH10304:
- case TDK:
- case LA501:
- case CONTEC:
- case NEC:
- case KME:
- if (cardtype == MBH10304) {
- card_name = "FMV-J182";
-
- len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf);
- if (len < 11) {
- kfree(buf);
- goto failed;
- }
- /* Read MACID from CIS */
- eth_hw_addr_set(dev, &buf[5]);
- kfree(buf);
- } else {
- if (pcmcia_get_mac_from_cis(link, dev))
- goto failed;
- if( cardtype == TDK ) {
- card_name = "TDK LAK-CD021";
- } else if( cardtype == LA501 ) {
- card_name = "LA501";
- } else if( cardtype == NEC ) {
- card_name = "PK-UG-J001";
- } else if( cardtype == KME ) {
- card_name = "Panasonic";
- } else {
- card_name = "C-NET(PC)C";
- }
- }
- break;
- case UNGERMANN:
- /* Read MACID from register */
- for (i = 0; i < 6; i++)
- addr[i] = inb(ioaddr + UNGERMANN_MAC_ID + i);
- eth_hw_addr_set(dev, addr);
- card_name = "Access/CARD";
- break;
- case XXX10304:
- /* Read MACID from Buggy CIS */
- if (fmvj18x_get_hwinfo(link, buggybuf) == -1) {
- pr_notice("unable to read hardware net address\n");
- goto failed;
- }
- eth_hw_addr_set(dev, buggybuf);
- card_name = "FMV-J182";
- break;
- case MBH10302:
- default:
- /* Read MACID from register */
- for (i = 0; i < 6; i++)
- addr[i] = inb(ioaddr + MAC_ID + i);
- eth_hw_addr_set(dev, addr);
- card_name = "FMV-J181";
- break;
- }
-
- lp->cardtype = cardtype;
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- /* print current configuration */
- netdev_info(dev, "%s, sram %s, port %#3lx, irq %d, hw_addr %pM\n",
- card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2",
- dev->base_addr, dev->irq, dev->dev_addr);
-
- return 0;
-
-failed:
- fmvj18x_release(link);
- return -ENODEV;
-} /* fmvj18x_config */
-/*====================================================================*/
-
-static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id)
-{
- u_char __iomem *base;
- int i, j;
-
- /* Allocate a small memory window */
- link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = 0; link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return -1;
-
- base = ioremap(link->resource[2]->start, resource_size(link->resource[2]));
- if (!base) {
- pcmcia_release_window(link, link->resource[2]);
- return -1;
- }
-
- pcmcia_map_mem_page(link, link->resource[2], 0);
-
- /*
- * MBH10304 CISTPL_FUNCE_LAN_NODE_ID format
- * 22 0d xx xx xx 04 06 yy yy yy yy yy yy ff
- * 'xx' is garbage.
- * 'yy' is MAC address.
- */
- for (i = 0; i < 0x200; i++) {
- if (readb(base+i*2) == 0x22) {
- if (readb(base+(i-1)*2) == 0xff &&
- readb(base+(i+5)*2) == 0x04 &&
- readb(base+(i+6)*2) == 0x06 &&
- readb(base+(i+13)*2) == 0xff)
- break;
- }
- }
-
- if (i != 0x200) {
- for (j = 0 ; j < 6; j++,i++) {
- node_id[j] = readb(base+(i+7)*2);
- }
- }
-
- iounmap(base);
- j = pcmcia_release_window(link, link->resource[2]);
- return (i != 0x200) ? 0 : -1;
-
-} /* fmvj18x_get_hwinfo */
-/*====================================================================*/
-
-static int fmvj18x_setup_mfc(struct pcmcia_device *link)
-{
- int i;
- struct net_device *dev = link->priv;
- unsigned int ioaddr;
- struct local_info *lp = netdev_priv(dev);
-
- /* Allocate a small memory window */
- link->resource[3]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[3]->start = link->resource[3]->end = 0;
- i = pcmcia_request_window(link, link->resource[3], 0);
- if (i != 0)
- return -1;
-
- lp->base = ioremap(link->resource[3]->start,
- resource_size(link->resource[3]));
- if (lp->base == NULL) {
- netdev_notice(dev, "ioremap failed\n");
- return -1;
- }
-
- i = pcmcia_map_mem_page(link, link->resource[3], 0);
- if (i != 0) {
- iounmap(lp->base);
- lp->base = NULL;
- return -1;
- }
-
- ioaddr = dev->base_addr;
- writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */
- writeb(0x0, lp->base+0x802); /* Config and Status Register */
-
- writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */
- writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */
-
- writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */
- writeb(0x8, lp->base+0x822); /* Config and Status Register */
-
- return 0;
-
-}
-/*====================================================================*/
-
-static void fmvj18x_release(struct pcmcia_device *link)
-{
-
- struct net_device *dev = link->priv;
- struct local_info *lp = netdev_priv(dev);
- u_char __iomem *tmp;
-
- dev_dbg(&link->dev, "fmvj18x_release\n");
-
- if (lp->base != NULL) {
- tmp = lp->base;
- lp->base = NULL; /* set NULL before iounmap */
- iounmap(tmp);
- }
-
- pcmcia_disable_device(link);
-
-}
-
-static int fmvj18x_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int fmvj18x_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- fjn_reset(dev);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-/*====================================================================*/
-
-static const struct pcmcia_device_id fmvj18x_ids[] = {
- PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004),
- PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59),
- PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922),
- PCMCIA_DEVICE_PROD_ID12("Eiger labs,Inc.", "EPX-10BT PC Card Ethernet 10BT", 0xf47e6c66, 0x877f9922),
- PCMCIA_DEVICE_PROD_ID12("FUJITSU", "LAN Card(FMV-J182)", 0x6ee5a3d8, 0x5baf31db),
- PCMCIA_DEVICE_PROD_ID12("FUJITSU", "MBH10308", 0x6ee5a3d8, 0x3f04875e),
- PCMCIA_DEVICE_PROD_ID12("FUJITSU TOWA", "LA501", 0xb8451188, 0x12939ba2),
- PCMCIA_DEVICE_PROD_ID12("HITACHI", "HT-4840-11", 0xf4f43949, 0x773910f4),
- PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310B Ver1.0 ", 0x8cef4d3a, 0x075fc7b6),
- PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310 Ver1.0 ", 0x8cef4d3a, 0xbccf43e6),
- PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "10BASE_T CARD R280", 0x85c10e17, 0xd9413666),
- PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CD02x", 0x1eae9475, 0x8fa0ee70),
- PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CF010", 0x1eae9475, 0x7683bc9a),
- PCMCIA_DEVICE_PROD_ID1("CONTEC Co.,Ltd.", 0x58d8fee2),
- PCMCIA_DEVICE_PROD_ID1("PCMCIA LAN MBH10304 ES", 0x2599f454),
- PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da),
- PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0e01),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0a05),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0b05),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x1101),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids);
-
-static struct pcmcia_driver fmvj18x_cs_driver = {
- .owner = THIS_MODULE,
- .name = "fmvj18x_cs",
- .probe = fmvj18x_probe,
- .remove = fmvj18x_detach,
- .id_table = fmvj18x_ids,
- .suspend = fmvj18x_suspend,
- .resume = fmvj18x_resume,
-};
-module_pcmcia_driver(fmvj18x_cs_driver);
-
-/*====================================================================*/
-
-static irqreturn_t fjn_interrupt(int dummy, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr;
- unsigned short tx_stat, rx_stat;
-
- ioaddr = dev->base_addr;
-
- /* avoid multiple interrupts */
- outw(0x0000, ioaddr + TX_INTR);
-
- /* wait for a while */
- udelay(1);
-
- /* get status */
- tx_stat = inb(ioaddr + TX_STATUS);
- rx_stat = inb(ioaddr + RX_STATUS);
-
- /* clear status */
- outb(tx_stat, ioaddr + TX_STATUS);
- outb(rx_stat, ioaddr + RX_STATUS);
-
- pr_debug("%s: interrupt, rx_status %02x.\n", dev->name, rx_stat);
- pr_debug(" tx_status %02x.\n", tx_stat);
-
- if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
- /* there is packet(s) in rx buffer */
- fjn_rx(dev);
- }
- if (tx_stat & F_TMT_RDY) {
- dev->stats.tx_packets += lp->sent ;
- lp->sent = 0 ;
- if (lp->tx_queue) {
- outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
- lp->sent = lp->tx_queue ;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- netif_trans_update(dev);
- } else {
- lp->tx_started = 0;
- }
- netif_wake_queue(dev);
- }
- pr_debug("%s: exiting interrupt,\n", dev->name);
- pr_debug(" tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat);
-
- outb(D_TX_INTR, ioaddr + TX_INTR);
- outb(D_RX_INTR, ioaddr + RX_INTR);
-
- if (lp->base != NULL) {
- /* Ack interrupt for multifunction card */
- writeb(0x01, lp->base+0x802);
- writeb(0x09, lp->base+0x822);
- }
-
- return IRQ_HANDLED;
-
-} /* fjn_interrupt */
-
-/*====================================================================*/
-
-static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
-
- netdev_notice(dev, "transmit timed out with status %04x, %s?\n",
- htons(inw(ioaddr + TX_STATUS)),
- inb(ioaddr + TX_STATUS) & F_TMT_RDY
- ? "IRQ conflict" : "network cable problem");
- netdev_notice(dev, "timeout registers: %04x %04x %04x "
- "%04x %04x %04x %04x %04x.\n",
- htons(inw(ioaddr + 0)), htons(inw(ioaddr + 2)),
- htons(inw(ioaddr + 4)), htons(inw(ioaddr + 6)),
- htons(inw(ioaddr + 8)), htons(inw(ioaddr + 10)),
- htons(inw(ioaddr + 12)), htons(inw(ioaddr + 14)));
- dev->stats.tx_errors++;
- /* ToDo: We should try to restart the adaptor... */
- local_irq_disable();
- fjn_reset(dev);
-
- lp->tx_started = 0;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- lp->sent = 0;
- lp->open_time = jiffies;
- local_irq_enable();
- netif_wake_queue(dev);
-}
-
-static netdev_tx_t fjn_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- short length = skb->len;
-
- if (length < ETH_ZLEN)
- {
- if (skb_padto(skb, ETH_ZLEN))
- return NETDEV_TX_OK;
- length = ETH_ZLEN;
- }
-
- netif_stop_queue(dev);
-
- {
- unsigned char *buf = skb->data;
-
- if (length > ETH_FRAME_LEN) {
- netdev_notice(dev, "Attempting to send a large packet (%d bytes)\n",
- length);
- return NETDEV_TX_BUSY;
- }
-
- netdev_dbg(dev, "Transmitting a packet of length %lu\n",
- (unsigned long)skb->len);
- dev->stats.tx_bytes += skb->len;
-
- /* Disable both interrupts. */
- outw(0x0000, ioaddr + TX_INTR);
-
- /* wait for a while */
- udelay(1);
-
- outw(length, ioaddr + DATAPORT);
- outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
-
- lp->tx_queue++;
- lp->tx_queue_len += ((length+3) & ~1);
-
- if (lp->tx_started == 0) {
- /* If the Tx is idle, always trigger a transmit. */
- outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
- lp->sent = lp->tx_queue ;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- lp->tx_started = 1;
- netif_start_queue(dev);
- } else {
- if( sram_config == 0 ) {
- if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) )
- /* Yes, there is room for one more packet. */
- netif_start_queue(dev);
- } else {
- if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) &&
- lp->tx_queue < 127 )
- /* Yes, there is room for one more packet. */
- netif_start_queue(dev);
- }
- }
-
- /* Re-enable interrupts */
- outb(D_TX_INTR, ioaddr + TX_INTR);
- outb(D_RX_INTR, ioaddr + RX_INTR);
- }
- dev_kfree_skb (skb);
-
- return NETDEV_TX_OK;
-} /* fjn_start_xmit */
-
-/*====================================================================*/
-
-static void fjn_reset(struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- unsigned int ioaddr = dev->base_addr;
- int i;
-
- netdev_dbg(dev, "fjn_reset() called\n");
-
- /* Reset controller */
- if( sram_config == 0 )
- outb(CONFIG0_RST, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
-
- /* Power On chip and select bank 0 */
- if (lp->cardtype == MBH10302)
- outb(BANK_0, ioaddr + CONFIG_1);
- else
- outb(BANK_0U, ioaddr + CONFIG_1);
-
- /* Set Tx modes */
- outb(D_TX_MODE, ioaddr + TX_MODE);
- /* set Rx modes */
- outb(ID_MATCHED, ioaddr + RX_MODE);
-
- /* Set hardware address */
- for (i = 0; i < 6; i++)
- outb(dev->dev_addr[i], ioaddr + NODE_ID + i);
-
- /* (re)initialize the multicast table */
- set_rx_mode(dev);
-
- /* Switch to bank 2 (runtime mode) */
- if (lp->cardtype == MBH10302)
- outb(BANK_2, ioaddr + CONFIG_1);
- else
- outb(BANK_2U, ioaddr + CONFIG_1);
-
- /* set 16col ctrl bits */
- if( lp->cardtype == TDK || lp->cardtype == CONTEC)
- outb(TDK_AUTO_MODE, ioaddr + COL_CTRL);
- else
- outb(AUTO_MODE, ioaddr + COL_CTRL);
-
- /* clear Reserved Regs */
- outb(0x00, ioaddr + BMPR12);
- outb(0x00, ioaddr + BMPR13);
-
- /* reset Skip packet reg. */
- outb(0x01, ioaddr + RX_SKIP);
-
- /* Enable Tx and Rx */
- if( sram_config == 0 )
- outb(CONFIG0_DFL, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_DFL_1, ioaddr + CONFIG_0);
-
- /* Init receive pointer ? */
- inw(ioaddr + DATAPORT);
- inw(ioaddr + DATAPORT);
-
- /* Clear all status */
- outb(0xff, ioaddr + TX_STATUS);
- outb(0xff, ioaddr + RX_STATUS);
-
- if (lp->cardtype == MBH10302)
- outb(INTR_OFF, ioaddr + LAN_CTRL);
-
- /* Turn on Rx interrupts */
- outb(D_TX_INTR, ioaddr + TX_INTR);
- outb(D_RX_INTR, ioaddr + RX_INTR);
-
- /* Turn on interrupts from LAN card controller */
- if (lp->cardtype == MBH10302)
- outb(INTR_ON, ioaddr + LAN_CTRL);
-} /* fjn_reset */
-
-/*====================================================================*/
-
-static void fjn_rx(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- int boguscount = 10; /* 5 -> 10: by agy 19940922 */
-
- pr_debug("%s: in rx_packet(), rx_status %02x.\n",
- dev->name, inb(ioaddr + RX_STATUS));
-
- while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
- u_short status = inw(ioaddr + DATAPORT);
-
- netdev_dbg(dev, "Rxing packet mode %02x status %04x.\n",
- inb(ioaddr + RX_MODE), status);
-#ifndef final_version
- if (status == 0) {
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- break;
- }
-#endif
- if ((status & 0xF0) != 0x20) { /* There was an error. */
- dev->stats.rx_errors++;
- if (status & F_LEN_ERR) dev->stats.rx_length_errors++;
- if (status & F_ALG_ERR) dev->stats.rx_frame_errors++;
- if (status & F_CRC_ERR) dev->stats.rx_crc_errors++;
- if (status & F_OVR_FLO) dev->stats.rx_over_errors++;
- } else {
- u_short pkt_len = inw(ioaddr + DATAPORT);
- /* Malloc up new buffer. */
- struct sk_buff *skb;
-
- if (pkt_len > 1550) {
- netdev_notice(dev, "The FMV-18x claimed a very large packet, size %d\n",
- pkt_len);
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- dev->stats.rx_errors++;
- break;
- }
- skb = netdev_alloc_skb(dev, pkt_len + 2);
- if (skb == NULL) {
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- dev->stats.rx_dropped++;
- break;
- }
-
- skb_reserve(skb, 2);
- insw(ioaddr + DATAPORT, skb_put(skb, pkt_len),
- (pkt_len + 1) >> 1);
- skb->protocol = eth_type_trans(skb, dev);
-
- {
- int i;
- pr_debug("%s: Rxed packet of length %d: ",
- dev->name, pkt_len);
- for (i = 0; i < 14; i++)
- pr_debug(" %02x", skb->data[i]);
- pr_debug(".\n");
- }
-
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- }
- if (--boguscount <= 0)
- break;
- }
-
- /* If any worth-while packets have been received, dev_rint()
- has done a netif_wake_queue() for us and will work on them
- when we get to the bottom-half routine. */
-/*
- if (lp->cardtype != TDK) {
- int i;
- for (i = 0; i < 20; i++) {
- if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP)
- break;
- (void)inw(ioaddr + DATAPORT); /+ dummy status read +/
- outb(F_SKP_PKT, ioaddr + RX_SKIP);
- }
-
- if (i > 0)
- pr_debug("%s: Exint Rx packet with mode %02x after "
- "%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
- }
-*/
-} /* fjn_rx */
-
-/*====================================================================*/
-
-static void netdev_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- strscpy(info->version, DRV_VERSION, sizeof(info->version));
- 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 fjn_config(struct net_device *dev, struct ifmap *map){
- return 0;
-}
-
-static int fjn_open(struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
-
- pr_debug("fjn_open('%s').\n", dev->name);
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- link->open++;
-
- fjn_reset(dev);
-
- lp->tx_started = 0;
- lp->tx_queue = 0;
- lp->tx_queue_len = 0;
- lp->open_time = jiffies;
- netif_start_queue(dev);
-
- return 0;
-} /* fjn_open */
-
-/*====================================================================*/
-
-static int fjn_close(struct net_device *dev)
-{
- struct local_info *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
- unsigned int ioaddr = dev->base_addr;
-
- pr_debug("fjn_close('%s').\n", dev->name);
-
- lp->open_time = 0;
- netif_stop_queue(dev);
-
- /* Set configuration register 0 to disable Tx and Rx. */
- if( sram_config == 0 )
- outb(CONFIG0_RST ,ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0);
-
- /* Update the statistics -- ToDo. */
-
- /* Power-down the chip. Green, green, green! */
- outb(CHIP_OFF ,ioaddr + CONFIG_1);
-
- /* Set the ethernet adaptor disable IRQ */
- if (lp->cardtype == MBH10302)
- outb(INTR_OFF, ioaddr + LAN_CTRL);
-
- link->open--;
-
- return 0;
-} /* fjn_close */
-
-/*====================================================================*/
-
-/*
- Set the multicast/promiscuous mode for this adaptor.
-*/
-
-static void set_rx_mode(struct net_device *dev)
-{
- unsigned int ioaddr = dev->base_addr;
- u_char mc_filter[8]; /* Multicast hash filter */
- u_long flags;
- int i;
-
- int saved_bank;
- int saved_config_0 = inb(ioaddr + CONFIG_0);
-
- local_irq_save(flags);
-
- /* Disable Tx and Rx */
- if (sram_config == 0)
- outb(CONFIG0_RST, ioaddr + CONFIG_0);
- else
- outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
-
- if (dev->flags & IFF_PROMISC) {
- memset(mc_filter, 0xff, sizeof(mc_filter));
- outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
- } else if (netdev_mc_count(dev) > MC_FILTERBREAK ||
- (dev->flags & IFF_ALLMULTI)) {
- /* Too many to filter perfectly -- accept all multicasts. */
- memset(mc_filter, 0xff, sizeof(mc_filter));
- outb(2, ioaddr + RX_MODE); /* Use normal mode. */
- } else if (netdev_mc_empty(dev)) {
- memset(mc_filter, 0x00, sizeof(mc_filter));
- outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */
- } else {
- struct netdev_hw_addr *ha;
-
- memset(mc_filter, 0, sizeof(mc_filter));
- netdev_for_each_mc_addr(ha, dev) {
- unsigned int bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26;
- mc_filter[bit >> 3] |= (1 << (bit & 7));
- }
- outb(2, ioaddr + RX_MODE); /* Use normal mode. */
- }
-
- /* Switch to bank 1 and set the multicast table. */
- saved_bank = inb(ioaddr + CONFIG_1);
- outb(0xe4, ioaddr + CONFIG_1);
-
- for (i = 0; i < 8; i++)
- outb(mc_filter[i], ioaddr + MAR_ADR + i);
- outb(saved_bank, ioaddr + CONFIG_1);
-
- outb(saved_config_0, ioaddr + CONFIG_0);
-
- local_irq_restore(flags);
-}
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 12/15] drivers: net: 8390: AX88190: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (10 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 11/15] drivers: net: fujitsu: fmvj18x: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-23 9:06 ` Bjørn Mork
2026-04-22 18:01 ` [PATCH net v2 13/15] drivers: net: 8390: pcnet: " Andrew Lunn
` (2 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The ax88190 was written by David A. Hindsh in 2001. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/8390/Kconfig | 12 -
drivers/net/ethernet/8390/Makefile | 1 -
drivers/net/ethernet/8390/axnet_cs.c | 1707 ----------------------------------
3 files changed, 1720 deletions(-)
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index 345f250781c6..3dea042cc2eb 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -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)
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index 85c83c566ec6..60220484b382 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -11,7 +11,6 @@ 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
diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c
deleted file mode 100644
index 7c8213011b5c..000000000000
--- a/drivers/net/ethernet/8390/axnet_cs.c
+++ /dev/null
@@ -1,1707 +0,0 @@
-// SPDX-License-Identifier: GPL-1.0+
-
-/*======================================================================
-
- A PCMCIA ethernet driver for Asix AX88190-based cards
-
- The Asix AX88190 is a NS8390-derived chipset with a few nasty
- idiosyncracies that make it very inconvenient to support with a
- standard 8390 driver. This driver is based on pcnet_cs, with the
- tweaked 8390 code grafted on the end. Much of what I did was to
- clean up and update a similar driver supplied by Asix, which was
- adapted by William Lee, william@asix.com.tw.
-
- Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
-
- axnet_cs.c 1.28 2002/06/29 06:27:37
-
- The network driver code is based on Donald Becker's NE2000 code:
-
- Written 1992,1993 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
- Donald Becker may be reached at becker@scyld.com
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/crc32.h>
-#include <linux/mii.h>
-#include "8390.h"
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#define AXNET_CMD 0x00
-#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
-#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
-#define AXNET_MII_EEP 0x14 /* Offset of MII access port */
-#define AXNET_TEST 0x15 /* Offset of TEST Register port */
-#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */
-
-#define AXNET_START_PG 0x40 /* First page of TX buffer */
-#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */
-
-#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */
-
-#define IS_AX88190 0x0001
-#define IS_AX88790 0x0002
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-
-/*====================================================================*/
-
-static int axnet_config(struct pcmcia_device *link);
-static void axnet_release(struct pcmcia_device *link);
-static int axnet_open(struct net_device *dev);
-static int axnet_close(struct net_device *dev);
-static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
-static struct net_device_stats *get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
-static void ei_watchdog(struct timer_list *t);
-static void axnet_reset_8390(struct net_device *dev);
-
-static int mdio_read(unsigned int addr, int phy_id, int loc);
-static void mdio_write(unsigned int addr, int phy_id, int loc, int value);
-
-static void get_8390_hdr(struct net_device *,
- struct e8390_pkt_hdr *, int);
-static void block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset);
-static void block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page);
-
-static void axnet_detach(struct pcmcia_device *p_dev);
-
-static void AX88190_init(struct net_device *dev, int startp);
-static int ax_open(struct net_device *dev);
-static int ax_close(struct net_device *dev);
-static irqreturn_t ax_interrupt(int irq, void *dev_id);
-
-/*====================================================================*/
-
-struct axnet_dev {
- struct pcmcia_device *p_dev;
- caddr_t base;
- struct timer_list watchdog;
- int stale, fast_poll;
- u_short link_status;
- u_char duplex_flag;
- int phy_id;
- int flags;
- int active_low;
-};
-
-static inline struct axnet_dev *PRIV(struct net_device *dev)
-{
- void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device);
- return p;
-}
-
-static const struct net_device_ops axnet_netdev_ops = {
- .ndo_open = axnet_open,
- .ndo_stop = axnet_close,
- .ndo_eth_ioctl = axnet_ioctl,
- .ndo_start_xmit = axnet_start_xmit,
- .ndo_tx_timeout = axnet_tx_timeout,
- .ndo_get_stats = get_stats,
- .ndo_set_rx_mode = set_multicast_list,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-};
-
-static int axnet_probe(struct pcmcia_device *link)
-{
- struct axnet_dev *info;
- struct net_device *dev;
- struct ei_device *ei_local;
-
- dev_dbg(&link->dev, "axnet_attach()\n");
-
- dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(struct axnet_dev));
- if (!dev)
- return -ENOMEM;
-
- ei_local = netdev_priv(dev);
- spin_lock_init(&ei_local->page_lock);
-
- info = PRIV(dev);
- info->p_dev = link;
- link->priv = dev;
- link->config_flags |= CONF_ENABLE_IRQ;
-
- dev->netdev_ops = &axnet_netdev_ops;
-
- dev->watchdog_timeo = TX_TIMEOUT;
-
- return axnet_config(link);
-} /* axnet_attach */
-
-static void axnet_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link);
-
- unregister_netdev(dev);
-
- axnet_release(link);
-
- free_netdev(dev);
-} /* axnet_detach */
-
-/*======================================================================
-
- This probes for a card's hardware address by reading the PROM.
-
-======================================================================*/
-
-static int get_prom(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- u8 addr[ETH_ALEN];
- int i, j;
-
- /* This is based on drivers/net/ethernet/8390/ne.c */
- struct {
- u_char value, offset;
- } program_seq[] = {
- {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
- {0x01, EN0_DCFG}, /* Set word-wide access. */
- {0x00, EN0_RCNTLO}, /* Clear the count regs. */
- {0x00, EN0_RCNTHI},
- {0x00, EN0_IMR}, /* Mask completion irq. */
- {0xFF, EN0_ISR},
- {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */
- {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
- {0x10, EN0_RCNTLO},
- {0x00, EN0_RCNTHI},
- {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */
- {0x04, EN0_RSARHI},
- {E8390_RREAD+E8390_START, E8390_CMD},
- };
-
- /* Not much of a test, but the alternatives are messy */
- if (link->config_base != 0x03c0)
- return 0;
-
- axnet_reset_8390(dev);
- mdelay(10);
-
- for (i = 0; i < ARRAY_SIZE(program_seq); i++)
- outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
-
- for (i = 0; i < 6; i += 2) {
- j = inw(ioaddr + AXNET_DATAPORT);
- addr[i] = j & 0xff;
- addr[i+1] = j >> 8;
- }
- eth_hw_addr_set(dev, addr);
-
- return 1;
-} /* get_prom */
-
-static int try_io_port(struct pcmcia_device *link)
-{
- int j, ret;
- link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- if (link->resource[0]->end == 32) {
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- /* for master/slave multifunction cards */
- if (link->resource[1]->end > 0)
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- } else {
- /* This should be two 16-port windows */
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
- }
- if (link->resource[0]->start == 0) {
- for (j = 0; j < 0x400; j += 0x20) {
- link->resource[0]->start = j ^ 0x300;
- link->resource[1]->start = (j ^ 0x300) + 0x10;
- link->io_lines = 16;
- ret = pcmcia_request_io(link);
- if (ret == 0)
- return ret;
- }
- return ret;
- } else {
- return pcmcia_request_io(link);
- }
-}
-
-static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
- if (p_dev->config_index == 0)
- return -EINVAL;
-
- p_dev->config_index = 0x05;
- if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
- return -ENODEV;
-
- return try_io_port(p_dev);
-}
-
-static int axnet_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct axnet_dev *info = PRIV(dev);
- int i, j, j2, ret;
-
- dev_dbg(&link->dev, "axnet_config(0x%p)\n", link);
-
- /* don't trust the CIS on this; Linksys got it wrong */
- link->config_regs = 0x63;
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
- ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
- if (ret != 0)
- goto failed;
-
- if (!link->irq)
- goto failed;
-
- if (resource_size(link->resource[1]) == 8)
- link->config_flags |= CONF_ENABLE_SPKR;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- goto failed;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
-
- if (!get_prom(link)) {
- pr_notice("this is not an AX88190 card!\n");
- pr_notice("use pcnet_cs instead.\n");
- goto failed;
- }
-
- ei_status.name = "AX88190";
- ei_status.word16 = 1;
- ei_status.tx_start_page = AXNET_START_PG;
- ei_status.rx_start_page = AXNET_START_PG + TX_PAGES;
- ei_status.stop_page = AXNET_STOP_PG;
- ei_status.reset_8390 = axnet_reset_8390;
- ei_status.get_8390_hdr = get_8390_hdr;
- ei_status.block_input = block_input;
- ei_status.block_output = block_output;
-
- if (inb(dev->base_addr + AXNET_TEST) != 0)
- info->flags |= IS_AX88790;
- else
- info->flags |= IS_AX88190;
-
- if (info->flags & IS_AX88790)
- outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */
-
- info->active_low = 0;
-
- for (i = 0; i < 32; i++) {
- j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
- j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
- if (j == j2) continue;
- if ((j != 0) && (j != 0xffff)) break;
- }
-
- if (i == 32) {
- /* Maybe PHY is in power down mode. (PPD_SET = 1)
- Bit 2 of CCSR is active low. */
- pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
- for (i = 0; i < 32; i++) {
- j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
- j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2);
- if (j == j2) continue;
- if ((j != 0) && (j != 0xffff)) {
- info->active_low = 1;
- break;
- }
- }
- }
-
- info->phy_id = (i < 32) ? i : -1;
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n",
- ((info->flags & IS_AX88790) ? 7 : 1),
- dev->base_addr, dev->irq, dev->dev_addr);
- if (info->phy_id != -1) {
- netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
- info->phy_id, j);
- } else {
- netdev_notice(dev, " No MII transceivers found!\n");
- }
- return 0;
-
-failed:
- axnet_release(link);
- return -ENODEV;
-} /* axnet_config */
-
-static void axnet_release(struct pcmcia_device *link)
-{
- pcmcia_disable_device(link);
-}
-
-static int axnet_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int axnet_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct axnet_dev *info = PRIV(dev);
-
- if (link->open) {
- if (info->active_low == 1)
- pcmcia_write_config_byte(link, CISREG_CCSR, 0x04);
-
- axnet_reset_8390(dev);
- AX88190_init(dev, 1);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/*======================================================================
-
- MII interface support
-
-======================================================================*/
-
-#define MDIO_SHIFT_CLK 0x01
-#define MDIO_DATA_WRITE0 0x00
-#define MDIO_DATA_WRITE1 0x08
-#define MDIO_DATA_READ 0x04
-#define MDIO_MASK 0x0f
-#define MDIO_ENB_IN 0x02
-
-static void mdio_sync(unsigned int addr)
-{
- int bits;
- for (bits = 0; bits < 32; bits++) {
- outb_p(MDIO_DATA_WRITE1, addr);
- outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
- }
-}
-
-static int mdio_read(unsigned int addr, int phy_id, int loc)
-{
- u_int cmd = (0xf6<<10)|(phy_id<<5)|loc;
- int i, retval = 0;
-
- mdio_sync(addr);
- for (i = 14; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb_p(dat, addr);
- outb_p(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 19; i > 0; i--) {
- outb_p(MDIO_ENB_IN, addr);
- retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0);
- outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
-{
- u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
- int i;
-
- mdio_sync(addr);
- for (i = 31; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb_p(dat, addr);
- outb_p(dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 1; i >= 0; i--) {
- outb_p(MDIO_ENB_IN, addr);
- outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
- }
-}
-
-/*====================================================================*/
-
-static int axnet_open(struct net_device *dev)
-{
- int ret;
- struct axnet_dev *info = PRIV(dev);
- struct pcmcia_device *link = info->p_dev;
- unsigned int nic_base = dev->base_addr;
-
- dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name);
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
- ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev);
- if (ret)
- return ret;
-
- link->open++;
-
- info->link_status = 0x00;
- timer_setup(&info->watchdog, ei_watchdog, 0);
- mod_timer(&info->watchdog, jiffies + HZ);
-
- return ax_open(dev);
-} /* axnet_open */
-
-/*====================================================================*/
-
-static int axnet_close(struct net_device *dev)
-{
- struct axnet_dev *info = PRIV(dev);
- struct pcmcia_device *link = info->p_dev;
-
- dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name);
-
- ax_close(dev);
- free_irq(dev->irq, dev);
-
- link->open--;
- netif_stop_queue(dev);
- timer_delete_sync(&info->watchdog);
-
- return 0;
-} /* axnet_close */
-
-/*======================================================================
-
- Hard reset the card. This used to pause for the same period that
- a 8390 reset command required, but that shouldn't be necessary.
-
-======================================================================*/
-
-static void axnet_reset_8390(struct net_device *dev)
-{
- unsigned int nic_base = dev->base_addr;
- int i;
-
- ei_status.txing = ei_status.dmaing = 0;
-
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
-
- outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET);
-
- for (i = 0; i < 100; i++) {
- if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
- break;
- udelay(100);
- }
- outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
-
- if (i == 100)
- netdev_err(dev, "axnet_reset_8390() did not complete\n");
-
-} /* axnet_reset_8390 */
-
-/*====================================================================*/
-
-static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- PRIV(dev)->stale = 0;
- return ax_interrupt(irq, dev_id);
-}
-
-static void ei_watchdog(struct timer_list *t)
-{
- struct axnet_dev *info = timer_container_of(info, t, watchdog);
- struct net_device *dev = info->p_dev->priv;
- unsigned int nic_base = dev->base_addr;
- unsigned int mii_addr = nic_base + AXNET_MII_EEP;
- u_short link;
-
- 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 (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
- if (!info->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
- ei_irq_wrapper(dev->irq, dev);
- info->fast_poll = HZ;
- }
- if (info->fast_poll) {
- info->fast_poll--;
- info->watchdog.expires = jiffies + 1;
- add_timer(&info->watchdog);
- return;
- }
-
- if (info->phy_id < 0)
- goto reschedule;
- link = mdio_read(mii_addr, info->phy_id, 1);
- if (!link || (link == 0xffff)) {
- netdev_info(dev, "MII is missing!\n");
- info->phy_id = -1;
- goto reschedule;
- }
-
- link &= 0x0004;
- if (link != info->link_status) {
- u_short p = mdio_read(mii_addr, info->phy_id, 5);
- netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
- if (link) {
- info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00;
- if (p)
- netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n",
- (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H');
- else
- netdev_info(dev, "link partner did not autonegotiate\n");
- AX88190_init(dev, 1);
- }
- info->link_status = link;
- }
-
-reschedule:
- info->watchdog.expires = jiffies + HZ;
- add_timer(&info->watchdog);
-}
-
-/*====================================================================*/
-
-static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct axnet_dev *info = PRIV(dev);
- struct mii_ioctl_data *data = if_mii(rq);
- unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP;
- switch (cmd) {
- case SIOCGMIIPHY:
- data->phy_id = info->phy_id;
- fallthrough;
- case SIOCGMIIREG: /* Read MII PHY register. */
- data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
- return 0;
- case SIOCSMIIREG: /* Write MII PHY register. */
- mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
- return 0;
- }
- return -EOPNOTSUPP;
-}
-
-/*====================================================================*/
-
-static void get_8390_hdr(struct net_device *dev,
- struct e8390_pkt_hdr *hdr,
- int ring_page)
-{
- unsigned int nic_base = dev->base_addr;
-
- outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
- outb_p(ring_page, nic_base + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
-
- insw(nic_base + AXNET_DATAPORT, hdr,
- sizeof(struct e8390_pkt_hdr)>>1);
- /* Fix for big endian systems */
- hdr->count = le16_to_cpu(hdr->count);
-
-}
-
-/*====================================================================*/
-
-static void block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset)
-{
- unsigned int nic_base = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- char *buf = skb->data;
-
- if ((netif_msg_rx_status(ei_local)) && (count != 4))
- netdev_dbg(dev, "[bi=%d]\n", count+4);
- outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
- outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
-
- insw(nic_base + AXNET_DATAPORT,buf,count>>1);
- if (count & 0x01) {
- buf[count-1] = inb(nic_base + AXNET_DATAPORT);
- }
-}
-
-/*====================================================================*/
-
-static void block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page)
-{
- unsigned int nic_base = dev->base_addr;
-
- pr_debug("%s: [bo=%d]\n", dev->name, count);
-
- /* Round the count up for word writes. Do we need to do this?
- What effect will an odd byte count have on the 8390?
- I should check someday. */
- if (count & 0x01)
- count++;
-
- outb_p(0x00, nic_base + EN0_RSARLO);
- outb_p(start_page, nic_base + EN0_RSARHI);
- outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD);
- outsw(nic_base + AXNET_DATAPORT, buf, count>>1);
-}
-
-static const struct pcmcia_device_id axnet_ids[] = {
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081),
- PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301),
- PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106),
- PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab),
- PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202),
- PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc),
- PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef),
- PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef),
- PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1),
- PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc),
- PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061),
- PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90),
- PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609),
- PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058),
- PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef),
- PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, axnet_ids);
-
-static struct pcmcia_driver axnet_cs_driver = {
- .owner = THIS_MODULE,
- .name = "axnet_cs",
- .probe = axnet_probe,
- .remove = axnet_detach,
- .id_table = axnet_ids,
- .suspend = axnet_suspend,
- .resume = axnet_resume,
-};
-module_pcmcia_driver(axnet_cs_driver);
-
-/*====================================================================*/
-
-/* 8390.c: A general NS8390 ethernet driver core for linux. */
-/*
- Written 1992-94 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.
-
- 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 the chip-specific code for many 8390-based ethernet adaptors.
- This is not a complete driver, it must be combined with board-specific
- code such as ne.c, wd.c, 3c503.c, etc.
-
- Seeing how at least eight drivers use this code, (not counting the
- PCMCIA ones either) it is easy to break some card by what seems like
- a simple innocent change. Please contact me or Donald if you think
- you have found something that needs changing. -- PG
-
- Changelog:
-
- Paul Gortmaker : remove set_bit lock, other cleanups.
- Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
- ei_block_input() for eth_io_copy_and_sum().
- Paul Gortmaker : exchange static int ei_pingpong for a #define,
- also add better Tx error handling.
- Paul Gortmaker : rewrite Rx overrun handling as per NS specs.
- Alexey Kuznetsov : use the 8390's six bit hash multicast filter.
- Paul Gortmaker : tweak ANK's above multicast changes a bit.
- Paul Gortmaker : update packet statistics for v2.1.x
- Alan Cox : support arbitrary stupid port mappings on the
- 68K Macintosh. Support >16bit I/O spaces
- Paul Gortmaker : add kmod support for auto-loading of the 8390
- module by all drivers that require it.
- Alan Cox : Spinlocking work, added 'BUG_83C690'
- Paul Gortmaker : Separate out Tx timeout code from Tx path.
-
- Sources:
- The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
-
- */
-
-#include <linux/bitops.h>
-#include <asm/irq.h>
-#include <linux/fcntl.h>
-#include <linux/in.h>
-#include <linux/interrupt.h>
-
-#define BUG_83C690
-
-/* These are the operational function interfaces to board-specific
- routines.
- void reset_8390(struct net_device *dev)
- Resets the board associated with DEV, including a hardware reset of
- the 8390. This is only called when there is a transmit timeout, and
- it is always followed by 8390_init().
- void block_output(struct net_device *dev, int count, const unsigned char *buf,
- int start_page)
- Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
- "page" value uses the 8390's 256-byte pages.
- void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
- Read the 4 byte, page aligned 8390 header. *If* there is a
- subsequent read, it will be of the rest of the packet.
- void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
- Read COUNT bytes from the packet buffer into the skb data area. Start
- reading from RING_OFFSET, the address as the 8390 sees it. This will always
- follow the read of the 8390 header.
-*/
-#define ei_reset_8390 (ei_local->reset_8390)
-#define ei_block_output (ei_local->block_output)
-#define ei_block_input (ei_local->block_input)
-#define ei_get_8390_hdr (ei_local->get_8390_hdr)
-
-/* Index to functions. */
-static void ei_tx_intr(struct net_device *dev);
-static void ei_tx_err(struct net_device *dev);
-static void ei_receive(struct net_device *dev);
-static void ei_rx_overrun(struct net_device *dev);
-
-/* Routines generic to NS8390-based boards. */
-static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
- int start_page);
-static void do_set_multicast_list(struct net_device *dev);
-
-/*
- * SMP and the 8390 setup.
- *
- * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is
- * a page register that controls bank and packet buffer access. We guard
- * this with ei_local->page_lock. Nobody should assume or set the page other
- * than zero when the lock is not held. Lock holders must restore page 0
- * before unlocking. Even pure readers must take the lock to protect in
- * page 0.
- *
- * To make life difficult the chip can also be very slow. We therefore can't
- * just use spinlocks. For the longer lockups we disable the irq the device
- * sits on and hold the lock. We must hold the lock because there is a dual
- * processor case other than interrupts (get stats/set multicast list in
- * parallel with each other and transmit).
- *
- * Note: in theory we can just disable the irq on the card _but_ there is
- * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
- * enter lock, take the queued irq. So we waddle instead of flying.
- *
- * Finally by special arrangement for the purpose of being generally
- * annoying the transmit function is called bh atomic. That places
- * restrictions on the user context callers as disable_irq won't save
- * them.
- */
-
-/**
- * ax_open - Open/initialize the board.
- * @dev: network device to initialize
- *
- * This routine goes all-out, setting everything
- * up anew at each open, even though many of these registers should only
- * need to be set once at boot.
- */
-static int ax_open(struct net_device *dev)
-{
- unsigned long flags;
- struct ei_device *ei_local = netdev_priv(dev);
-
- /*
- * Grab the page lock so we own the register set, then call
- * the init function.
- */
-
- spin_lock_irqsave(&ei_local->page_lock, flags);
- AX88190_init(dev, 1);
- /* Set the flag before we drop the lock, That way the IRQ arrives
- after its set and we get no silly warnings */
- netif_start_queue(dev);
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
- ei_local->irqlock = 0;
- return 0;
-}
-
-#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock)
-
-/**
- * ax_close - shut down network device
- * @dev: network device to close
- *
- * Opposite of ax_open(). Only used when "ifconfig <devname> down" is done.
- */
-static int ax_close(struct net_device *dev)
-{
- unsigned long flags;
-
- /*
- * Hold the page lock during close
- */
-
- spin_lock_irqsave(&dev_lock(dev), flags);
- AX88190_init(dev, 0);
- spin_unlock_irqrestore(&dev_lock(dev), flags);
- netif_stop_queue(dev);
- return 0;
-}
-
-/**
- * axnet_tx_timeout - handle transmit time out condition
- * @dev: network device which has apparently fallen asleep
- * @txqueue: unused
- *
- * Called by kernel when device never acknowledges a transmit has
- * completed (or failed) - i.e. never posted a Tx related interrupt.
- */
-
-static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
- long e8390_base = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- int txsr, isr, tickssofar = jiffies - dev_trans_start(dev);
- unsigned long flags;
-
- dev->stats.tx_errors++;
-
- spin_lock_irqsave(&ei_local->page_lock, flags);
- txsr = inb(e8390_base+EN0_TSR);
- isr = inb(e8390_base+EN0_ISR);
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
-
- netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
- (txsr & ENTSR_ABT) ? "excess collisions." :
- (isr) ? "lost interrupt?" : "cable problem?",
- txsr, isr, tickssofar);
-
- if (!isr && !dev->stats.tx_packets)
- {
- /* The 8390 probably hasn't gotten on the cable yet. */
- ei_local->interface_num ^= 1; /* Try a different xcvr. */
- }
-
- /* Ugly but a reset can be slow, yet must be protected */
-
- spin_lock_irqsave(&ei_local->page_lock, flags);
-
- /* Try to restart the card. Perhaps the user has fixed something. */
- ei_reset_8390(dev);
- AX88190_init(dev, 1);
-
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
- netif_wake_queue(dev);
-}
-
-/**
- * axnet_start_xmit - begin packet transmission
- * @skb: packet to be sent
- * @dev: network device to which packet is sent
- *
- * Sends a packet to an 8390 network device.
- */
-
-static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- long e8390_base = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- int length, send_length, output_page;
- unsigned long flags;
- u8 packet[ETH_ZLEN];
-
- netif_stop_queue(dev);
-
- length = skb->len;
-
- /* Mask interrupts from the ethercard.
- SMP: We have to grab the lock here otherwise the IRQ handler
- on another CPU can flip window and race the IRQ mask set. We end
- up trashing the mcast filter not disabling irqs if we don't lock */
-
- spin_lock_irqsave(&ei_local->page_lock, flags);
- outb_p(0x00, e8390_base + EN0_IMR);
-
- /*
- * Slow phase with lock held.
- */
-
- ei_local->irqlock = 1;
-
- send_length = max(length, ETH_ZLEN);
-
- /*
- * We have two Tx slots available for use. Find the first free
- * slot, and then perform some sanity checks. With two Tx bufs,
- * you get very close to transmitting back-to-back packets. With
- * only one Tx buf, the transmitter sits idle while you reload the
- * card, leaving a substantial gap between each transmitted packet.
- */
-
- if (ei_local->tx1 == 0)
- {
- output_page = ei_local->tx_start_page;
- ei_local->tx1 = send_length;
- if ((netif_msg_tx_queued(ei_local)) &&
- ei_local->tx2 > 0)
- netdev_dbg(dev,
- "idle transmitter tx2=%d, lasttx=%d, txing=%d\n",
- ei_local->tx2, ei_local->lasttx,
- ei_local->txing);
- }
- else if (ei_local->tx2 == 0)
- {
- output_page = ei_local->tx_start_page + TX_PAGES/2;
- ei_local->tx2 = send_length;
- if ((netif_msg_tx_queued(ei_local)) &&
- ei_local->tx1 > 0)
- netdev_dbg(dev,
- "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n",
- ei_local->tx1, ei_local->lasttx,
- ei_local->txing);
- }
- else
- { /* We should never get here. */
- netif_dbg(ei_local, tx_err, dev,
- "No Tx buffers free! tx1=%d tx2=%d last=%d\n",
- ei_local->tx1, ei_local->tx2,
- ei_local->lasttx);
- ei_local->irqlock = 0;
- netif_stop_queue(dev);
- outb_p(ENISR_ALL, e8390_base + EN0_IMR);
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
- dev->stats.tx_errors++;
- return NETDEV_TX_BUSY;
- }
-
- /*
- * Okay, now upload the packet and trigger a send if the transmitter
- * isn't already sending. If it is busy, the interrupt handler will
- * trigger the send later, upon receiving a Tx done interrupt.
- */
-
- if (length == skb->len)
- ei_block_output(dev, length, skb->data, output_page);
- else {
- memset(packet, 0, ETH_ZLEN);
- skb_copy_from_linear_data(skb, packet, skb->len);
- ei_block_output(dev, length, packet, output_page);
- }
-
- if (! ei_local->txing)
- {
- ei_local->txing = 1;
- NS8390_trigger_send(dev, send_length, output_page);
- netif_trans_update(dev);
- if (output_page == ei_local->tx_start_page)
- {
- ei_local->tx1 = -1;
- ei_local->lasttx = -1;
- }
- else
- {
- ei_local->tx2 = -1;
- ei_local->lasttx = -2;
- }
- }
- else ei_local->txqueue++;
-
- if (ei_local->tx1 && ei_local->tx2)
- netif_stop_queue(dev);
- else
- netif_start_queue(dev);
-
- /* Turn 8390 interrupts back on. */
- ei_local->irqlock = 0;
- outb_p(ENISR_ALL, e8390_base + EN0_IMR);
-
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
-
- dev_kfree_skb (skb);
- dev->stats.tx_bytes += send_length;
-
- return NETDEV_TX_OK;
-}
-
-/**
- * ax_interrupt - handle the interrupts from an 8390
- * @irq: interrupt number
- * @dev_id: a pointer to the net_device
- *
- * Handle the ether interface interrupts. We pull packets from
- * the 8390 via the card specific functions and fire them at the networking
- * stack. We also handle transmit completions and wake the transmit path if
- * necessary. We also update the counters and do other housekeeping as
- * needed.
- */
-
-static irqreturn_t ax_interrupt(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- long e8390_base;
- int interrupts, nr_serviced = 0, i;
- struct ei_device *ei_local;
- int handled = 0;
- unsigned long flags;
-
- e8390_base = dev->base_addr;
- ei_local = netdev_priv(dev);
-
- /*
- * Protect the irq test too.
- */
-
- spin_lock_irqsave(&ei_local->page_lock, flags);
-
- if (ei_local->irqlock) {
-#if 1 /* This might just be an interrupt for a PCI device sharing this line */
- const char *msg;
- /* The "irqlock" check is only for testing. */
- if (ei_local->irqlock)
- msg = "Interrupted while interrupts are masked!";
- else
- msg = "Reentering the interrupt handler!";
- netdev_info(dev, "%s, isr=%#2x imr=%#2x\n",
- msg,
- inb_p(e8390_base + EN0_ISR),
- inb_p(e8390_base + EN0_IMR));
-#endif
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
- return IRQ_NONE;
- }
-
- netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n",
- inb_p(e8390_base + EN0_ISR));
-
- outb_p(0x00, e8390_base + EN0_ISR);
- ei_local->irqlock = 1;
-
- /* !!Assumption!! -- we stay in page 0. Don't break this. */
- while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 &&
- ++nr_serviced < MAX_SERVICE)
- {
- if (!netif_running(dev) || (interrupts == 0xff)) {
- netif_warn(ei_local, intr, dev,
- "interrupt from stopped card\n");
- outb_p(interrupts, e8390_base + EN0_ISR);
- interrupts = 0;
- break;
- }
- handled = 1;
-
- /* AX88190 bug fix. */
- outb_p(interrupts, e8390_base + EN0_ISR);
- for (i = 0; i < 10; i++) {
- if (!(inb(e8390_base + EN0_ISR) & interrupts))
- break;
- outb_p(0, e8390_base + EN0_ISR);
- outb_p(interrupts, e8390_base + EN0_ISR);
- }
- if (interrupts & ENISR_OVER)
- ei_rx_overrun(dev);
- else if (interrupts & (ENISR_RX+ENISR_RX_ERR))
- {
- /* Got a good (?) packet. */
- ei_receive(dev);
- }
- /* Push the next to-transmit packet through. */
- if (interrupts & ENISR_TX)
- ei_tx_intr(dev);
- else if (interrupts & ENISR_TX_ERR)
- ei_tx_err(dev);
-
- if (interrupts & ENISR_COUNTERS)
- {
- dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
- dev->stats.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
- dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
- }
- }
-
- if (interrupts && (netif_msg_intr(ei_local)))
- {
- handled = 1;
- if (nr_serviced >= MAX_SERVICE)
- {
- /* 0xFF is valid for a card removal */
- if (interrupts != 0xFF)
- netdev_warn(dev,
- "Too much work at interrupt, status %#2.2x\n",
- interrupts);
- outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
- } else {
- netdev_warn(dev, "unknown interrupt %#2x\n",
- interrupts);
- outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
- }
- }
-
- /* Turn 8390 interrupts back on. */
- ei_local->irqlock = 0;
- outb_p(ENISR_ALL, e8390_base + EN0_IMR);
-
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
- return IRQ_RETVAL(handled);
-}
-
-/**
- * ei_tx_err - handle transmitter error
- * @dev: network device which threw the exception
- *
- * A transmitter error has happened. Most likely excess collisions (which
- * is a fairly normal condition). If the error is one where the Tx will
- * have been aborted, we try and send another one right away, instead of
- * letting the failed packet sit and collect dust in the Tx buffer. This
- * is a much better solution as it avoids kernel based Tx timeouts, and
- * an unnecessary card reset.
- *
- * Called with lock held.
- */
-
-static void ei_tx_err(struct net_device *dev)
-{
- long e8390_base = dev->base_addr;
- unsigned char txsr = inb_p(e8390_base+EN0_TSR);
- unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
-
-#ifdef VERBOSE_ERROR_DUMP
- netdev_dbg(dev, "transmitter error (%#2x):", txsr);
- if (txsr & ENTSR_ABT)
- pr_cont(" excess-collisions");
- if (txsr & ENTSR_ND)
- pr_cont(" non-deferral");
- if (txsr & ENTSR_CRS)
- pr_cont(" lost-carrier");
- if (txsr & ENTSR_FU)
- pr_cont(" FIFO-underrun");
- if (txsr & ENTSR_CDH)
- pr_cont(" lost-heartbeat");
- pr_cont("\n");
-#endif
-
- if (tx_was_aborted)
- ei_tx_intr(dev);
- else
- {
- dev->stats.tx_errors++;
- if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++;
- if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++;
- if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++;
- }
-}
-
-/**
- * ei_tx_intr - transmit interrupt handler
- * @dev: network device for which tx intr is handled
- *
- * We have finished a transmit: check for errors and then trigger the next
- * packet to be sent. Called with lock held.
- */
-
-static void ei_tx_intr(struct net_device *dev)
-{
- long e8390_base = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- int status = inb(e8390_base + EN0_TSR);
-
- /*
- * There are two Tx buffers, see which one finished, and trigger
- * the send of another one if it exists.
- */
- ei_local->txqueue--;
-
- if (ei_local->tx1 < 0)
- {
- if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
- netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n",
- ei_local->name, ei_local->lasttx,
- ei_local->tx1);
- ei_local->tx1 = 0;
- if (ei_local->tx2 > 0)
- {
- ei_local->txing = 1;
- NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
- netif_trans_update(dev);
- ei_local->tx2 = -1;
- ei_local->lasttx = 2;
- } else {
- ei_local->lasttx = 20;
- ei_local->txing = 0;
- }
- }
- else if (ei_local->tx2 < 0)
- {
- if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
- netdev_err(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n",
- ei_local->name, ei_local->lasttx,
- ei_local->tx2);
- ei_local->tx2 = 0;
- if (ei_local->tx1 > 0)
- {
- ei_local->txing = 1;
- NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
- netif_trans_update(dev);
- ei_local->tx1 = -1;
- ei_local->lasttx = 1;
- } else {
- ei_local->lasttx = 10;
- ei_local->txing = 0;
- }
- }
-// else
-// netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n",
-// ei_local->lasttx);
-
- /* Minimize Tx latency: update the statistics after we restart TXing. */
- if (status & ENTSR_COL)
- dev->stats.collisions++;
- if (status & ENTSR_PTX)
- dev->stats.tx_packets++;
- else
- {
- dev->stats.tx_errors++;
- if (status & ENTSR_ABT)
- {
- dev->stats.tx_aborted_errors++;
- dev->stats.collisions += 16;
- }
- if (status & ENTSR_CRS)
- dev->stats.tx_carrier_errors++;
- if (status & ENTSR_FU)
- dev->stats.tx_fifo_errors++;
- if (status & ENTSR_CDH)
- dev->stats.tx_heartbeat_errors++;
- if (status & ENTSR_OWC)
- dev->stats.tx_window_errors++;
- }
- netif_wake_queue(dev);
-}
-
-/**
- * ei_receive - receive some packets
- * @dev: network device with which receive will be run
- *
- * We have a good packet(s), get it/them out of the buffers.
- * Called with lock held.
- */
-
-static void ei_receive(struct net_device *dev)
-{
- long e8390_base = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- unsigned char rxing_page, this_frame, next_frame;
- unsigned short current_offset;
- int rx_pkt_count = 0;
- struct e8390_pkt_hdr rx_frame;
-
- while (++rx_pkt_count < 10)
- {
- int pkt_len, pkt_stat;
-
- /* Get the rx page (incoming packet pointer). */
- rxing_page = inb_p(e8390_base + EN1_CURPAG -1);
-
- /* Remove one frame from the ring. Boundary is always a page behind. */
- this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
- if (this_frame >= ei_local->stop_page)
- this_frame = ei_local->rx_start_page;
-
- /* Someday we'll omit the previous, iff we never get this message.
- (There is at least one clone claimed to have a problem.)
-
- Keep quiet if it looks like a card removal. One problem here
- is that some clones crash in roughly the same way.
- */
- if ((netif_msg_rx_err(ei_local)) &&
- this_frame != ei_local->current_page &&
- (this_frame != 0x0 || rxing_page != 0xFF))
- netdev_err(dev, "mismatched read page pointers %2x vs %2x\n",
- this_frame, ei_local->current_page);
-
- if (this_frame == rxing_page) /* Read all the frames? */
- break; /* Done for now */
-
- current_offset = this_frame << 8;
- ei_get_8390_hdr(dev, &rx_frame, this_frame);
-
- pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
- pkt_stat = rx_frame.status;
-
- next_frame = this_frame + 1 + ((pkt_len+4)>>8);
-
- if (pkt_len < 60 || pkt_len > 1518)
- {
- netif_err(ei_local, rx_err, dev,
- "bogus packet size: %d, status=%#2x nxpg=%#2x\n",
- rx_frame.count, rx_frame.status,
- rx_frame.next);
- dev->stats.rx_errors++;
- dev->stats.rx_length_errors++;
- }
- else if ((pkt_stat & 0x0F) == ENRSR_RXOK)
- {
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb(dev, pkt_len + 2);
- if (skb == NULL)
- {
- netif_err(ei_local, rx_err, dev,
- "Couldn't allocate a sk_buff of size %d\n",
- pkt_len);
- dev->stats.rx_dropped++;
- break;
- }
- else
- {
- skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
- skb_put(skb, pkt_len); /* Make room */
- ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
- skb->protocol=eth_type_trans(skb,dev);
- netif_rx(skb);
- dev->stats.rx_packets++;
- dev->stats.rx_bytes += pkt_len;
- if (pkt_stat & ENRSR_PHY)
- dev->stats.multicast++;
- }
- }
- else
- {
- netif_err(ei_local, rx_err, dev,
- "bogus packet: status=%#2x nxpg=%#2x size=%d\n",
- rx_frame.status, rx_frame.next,
- rx_frame.count);
- dev->stats.rx_errors++;
- /* NB: The NIC counts CRC, frame and missed errors. */
- if (pkt_stat & ENRSR_FO)
- dev->stats.rx_fifo_errors++;
- }
- next_frame = rx_frame.next;
-
- /* This _should_ never happen: it's here for avoiding bad clones. */
- if (next_frame >= ei_local->stop_page) {
- netdev_info(dev, "next frame inconsistency, %#2x\n",
- next_frame);
- next_frame = ei_local->rx_start_page;
- }
- ei_local->current_page = next_frame;
- outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
- }
-}
-
-/**
- * ei_rx_overrun - handle receiver overrun
- * @dev: network device which threw exception
- *
- * We have a receiver overrun: we have to kick the 8390 to get it started
- * again. Problem is that you have to kick it exactly as NS prescribes in
- * the updated datasheets, or "the NIC may act in an unpredictable manner."
- * This includes causing "the NIC to defer indefinitely when it is stopped
- * on a busy network." Ugh.
- * Called with lock held. Don't call this with the interrupts off or your
- * computer will hate you - it takes 10ms or so.
- */
-
-static void ei_rx_overrun(struct net_device *dev)
-{
- struct axnet_dev *info = PRIV(dev);
- long e8390_base = dev->base_addr;
- unsigned char was_txing, must_resend = 0;
- struct ei_device *ei_local = netdev_priv(dev);
-
- /*
- * Record whether a Tx was in progress and then issue the
- * stop command.
- */
- was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
-
- netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n");
- dev->stats.rx_over_errors++;
-
- /*
- * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
- * We wait at least 2ms.
- */
-
- mdelay(2);
-
- /*
- * Reset RBCR[01] back to zero as per magic incantation.
- */
- outb_p(0x00, e8390_base+EN0_RCNTLO);
- outb_p(0x00, e8390_base+EN0_RCNTHI);
-
- /*
- * See if any Tx was interrupted or not. According to NS, this
- * step is vital, and skipping it will cause no end of havoc.
- */
-
- if (was_txing)
- {
- unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
- if (!tx_completed)
- must_resend = 1;
- }
-
- /*
- * Have to enter loopback mode and then restart the NIC before
- * you are allowed to slurp packets up off the ring.
- */
- outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
- outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
-
- /*
- * Clear the Rx ring of all the debris, and ack the interrupt.
- */
- ei_receive(dev);
-
- /*
- * Leave loopback mode, and resend any packet that got stopped.
- */
- outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR);
- if (must_resend)
- outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
-}
-
-/*
- * Collect the stats. This is called unlocked and from several contexts.
- */
-
-static struct net_device_stats *get_stats(struct net_device *dev)
-{
- long ioaddr = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- unsigned long flags;
-
- /* If the card is stopped, just return the present stats. */
- if (!netif_running(dev))
- return &dev->stats;
-
- spin_lock_irqsave(&ei_local->page_lock,flags);
- /* Read the counter registers, assuming we are in page 0. */
- dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
- dev->stats.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
- dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
- spin_unlock_irqrestore(&ei_local->page_lock, flags);
-
- return &dev->stats;
-}
-
-/*
- * Form the 64 bit 8390 multicast table from the linked list of addresses
- * associated with this dev structure.
- */
-
-static inline void make_mc_bits(u8 *bits, struct net_device *dev)
-{
- struct netdev_hw_addr *ha;
- u32 crc;
-
- netdev_for_each_mc_addr(ha, dev) {
- crc = ether_crc(ETH_ALEN, ha->addr);
- /*
- * The 8390 uses the 6 most significant bits of the
- * CRC to index the multicast table.
- */
- bits[crc>>29] |= (1<<((crc>>26)&7));
- }
-}
-
-/**
- * do_set_multicast_list - set/clear multicast filter
- * @dev: net device for which multicast filter is adjusted
- *
- * Set or clear the multicast filter for this adaptor.
- * Must be called with lock held.
- */
-
-static void do_set_multicast_list(struct net_device *dev)
-{
- long e8390_base = dev->base_addr;
- int i;
- struct ei_device *ei_local = netdev_priv(dev);
-
- if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
- memset(ei_local->mcfilter, 0, 8);
- if (!netdev_mc_empty(dev))
- make_mc_bits(ei_local->mcfilter, dev);
- } else {
- /* set to accept-all */
- memset(ei_local->mcfilter, 0xFF, 8);
- }
-
- outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
- for(i = 0; i < 8; i++)
- {
- outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
- }
- outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
-
- if(dev->flags&IFF_PROMISC)
- outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
- else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev))
- outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR);
- else
- outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR);
-
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
-}
-
-/*
- * Called without lock held. This is invoked from user context and may
- * be parallel to just about everything else. Its also fairly quick and
- * not called too often. Must protect against both bh and irq users
- */
-
-static void set_multicast_list(struct net_device *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev_lock(dev), flags);
- do_set_multicast_list(dev);
- spin_unlock_irqrestore(&dev_lock(dev), flags);
-}
-
-/* This page of functions should be 8390 generic */
-/* Follow National Semi's recommendations for initializing the "NIC". */
-
-/**
- * AX88190_init - initialize 8390 hardware
- * @dev: network device to initialize
- * @startp: boolean. non-zero value to initiate chip processing
- *
- * Must be called with lock held.
- */
-
-static void AX88190_init(struct net_device *dev, int startp)
-{
- struct axnet_dev *info = PRIV(dev);
- long e8390_base = dev->base_addr;
- struct ei_device *ei_local = netdev_priv(dev);
- int i;
- int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
-
- if(sizeof(struct e8390_pkt_hdr)!=4)
- panic("8390.c: header struct mispacked\n");
- /* Follow National Semi's recommendations for initing the DP83902. */
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
- outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
- /* Clear the remote byte count registers. */
- outb_p(0x00, e8390_base + EN0_RCNTLO);
- outb_p(0x00, e8390_base + EN0_RCNTHI);
- /* Set to monitor and loopback mode -- this is vital!. */
- outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */
- outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
- /* Set the transmit page and receive ring. */
- outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
- ei_local->tx1 = ei_local->tx2 = 0;
- outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
- outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
- ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
- outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
- /* Clear the pending interrupts and mask. */
- outb_p(0xFF, e8390_base + EN0_ISR);
- outb_p(0x00, e8390_base + EN0_IMR);
-
- /* Copy the station address into the DS8390 registers. */
-
- outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
- for(i = 0; i < 6; i++)
- {
- outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
- if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
- netdev_err(dev, "Hw. address read/write mismap %d\n", i);
- }
-
- outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
-
- netif_start_queue(dev);
- ei_local->tx1 = ei_local->tx2 = 0;
- ei_local->txing = 0;
-
- if (info->flags & IS_AX88790) /* select Internal PHY */
- outb(0x10, e8390_base + AXNET_GPIO);
-
- if (startp)
- {
- outb_p(0xff, e8390_base + EN0_ISR);
- outb_p(ENISR_ALL, e8390_base + EN0_IMR);
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
- outb_p(E8390_TXCONFIG | info->duplex_flag,
- e8390_base + EN0_TXCR); /* xmit on. */
- /* 3c503 TechMan says rxconfig only after the NIC is started. */
- outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */
- do_set_multicast_list(dev); /* (re)load the mcast table */
- }
-}
-
-/* Trigger a transmit start, assuming the length is valid.
- Always called with the page lock held */
-
-static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
- int start_page)
-{
- long e8390_base = dev->base_addr;
- struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev);
-
- if (inb_p(e8390_base) & E8390_TRANS)
- {
- netdev_warn(dev, "trigger_send() called with the transmitter busy\n");
- return;
- }
- outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
- outb_p(length >> 8, e8390_base + EN0_TCNTHI);
- outb_p(start_page, e8390_base + EN0_TPSR);
- outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
-}
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 13/15] drivers: net: 8390: pcnet: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (11 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 12/15] drivers: net: 8390: AX88190: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-23 5:49 ` Dominik Brodowski
2026-04-23 9:09 ` Bjørn Mork
2026-04-22 18:01 ` [PATCH net v2 14/15] drivers: net: 8390: ultra: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 15/15] drivers: net: 8390: wd80x3: " Andrew Lunn
14 siblings, 2 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The pcnet was written by David A. Hindsh in 1999. It is an PCMCIA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/8390/Kconfig | 11 -
drivers/net/ethernet/8390/Makefile | 1 -
drivers/net/ethernet/8390/pcnet_cs.c | 1717 ----------------------------------
3 files changed, 1729 deletions(-)
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index 3dea042cc2eb..3e56806471a3 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -132,17 +132,6 @@ config APNE
To compile this driver as a module, choose M here: the module
will be called apne.
-config PCMCIA_PCNET
- tristate "NE2000 compatible PCMCIA support"
- depends on PCMCIA && HAS_IOPORT
- select CRC32
- help
- Say Y here if you intend to attach an NE2000 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 pcnet_cs. If unsure, say N.
-
config STNIC
tristate "National DP83902AV support"
depends on SUPERH
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index 60220484b382..b215136a603b 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -11,7 +11,6 @@ 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_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
diff --git a/drivers/net/ethernet/8390/pcnet_cs.c b/drivers/net/ethernet/8390/pcnet_cs.c
deleted file mode 100644
index 19f9c5db3f3b..000000000000
--- a/drivers/net/ethernet/8390/pcnet_cs.c
+++ /dev/null
@@ -1,1717 +0,0 @@
-// SPDX-License-Identifier: GPL-1.0+
-/*======================================================================
-
- A PCMCIA ethernet driver for NS8390-based cards
-
- This driver supports the D-Link DE-650 and Linksys EthernetCard
- cards, the newer D-Link and Linksys combo cards, Accton EN2212
- cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory
- mode, and the IBM Credit Card Adapter, the NE4100, the Thomas
- Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
- mode. It will also handle the Socket EA card in either mode.
-
- Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
-
- pcnet_cs.c 1.153 2003/11/09 18:53:09
-
- The network driver code is based on Donald Becker's NE2000 code:
-
- Written 1992,1993 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
- Donald Becker may be reached at becker@scyld.com
-
- Based also on Keith Moore's changes to Don Becker's code, for IBM
- CCAE support. Drivers merged back together, and shared-memory
- Socket EA support added, by Ken Raeburn, September 1995.
-
-======================================================================*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/log2.h>
-#include <linux/etherdevice.h>
-#include <linux/mii.h>
-#include "8390.h"
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#include <asm/io.h>
-#include <asm/byteorder.h>
-#include <linux/uaccess.h>
-
-#define PCNET_CMD 0x00
-#define PCNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
-#define PCNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
-#define PCNET_MISC 0x18 /* For IBM CCAE and Socket EA cards */
-
-#define PCNET_START_PG 0x40 /* First page of TX buffer */
-#define PCNET_STOP_PG 0x80 /* Last page +1 of RX ring */
-
-/* Socket EA cards have a larger packet buffer */
-#define SOCKET_START_PG 0x01
-#define SOCKET_STOP_PG 0xff
-
-#define PCNET_RDC_TIMEOUT (2*HZ/100) /* Max wait in jiffies for Tx RDC */
-
-static const char *if_names[] = { "auto", "10baseT", "10base2"};
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-INT_MODULE_PARM(if_port, 1); /* Transceiver type */
-INT_MODULE_PARM(use_big_buf, 1); /* use 64K packet buffer? */
-INT_MODULE_PARM(mem_speed, 0); /* shared mem speed, in ns */
-INT_MODULE_PARM(delay_output, 0); /* pause after xmit? */
-INT_MODULE_PARM(delay_time, 4); /* in usec */
-INT_MODULE_PARM(use_shmem, -1); /* use shared memory? */
-INT_MODULE_PARM(full_duplex, 0); /* full duplex? */
-
-/* Ugh! Let the user hardwire the hardware address for queer cards */
-static int hw_addr[6] = { 0, /* ... */ };
-module_param_array(hw_addr, int, NULL, 0);
-
-/*====================================================================*/
-
-static void mii_phy_probe(struct net_device *dev);
-static int pcnet_config(struct pcmcia_device *link);
-static void pcnet_release(struct pcmcia_device *link);
-static int pcnet_open(struct net_device *dev);
-static int pcnet_close(struct net_device *dev);
-static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
-static void ei_watchdog(struct timer_list *t);
-static void pcnet_reset_8390(struct net_device *dev);
-static int set_config(struct net_device *dev, struct ifmap *map);
-static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
- int stop_pg, int cm_offset);
-static int setup_dma_config(struct pcmcia_device *link, int start_pg,
- int stop_pg);
-
-static void pcnet_detach(struct pcmcia_device *p_dev);
-
-/*====================================================================*/
-
-struct hw_info {
- u_int offset;
- u_char a0, a1, a2;
- u_int flags;
-};
-
-#define DELAY_OUTPUT 0x01
-#define HAS_MISC_REG 0x02
-#define USE_BIG_BUF 0x04
-#define HAS_IBM_MISC 0x08
-#define IS_DL10019 0x10
-#define IS_DL10022 0x20
-#define HAS_MII 0x40
-#define USE_SHMEM 0x80 /* autodetected */
-
-#define AM79C9XX_HOME_PHY 0x00006B90 /* HomePNA PHY */
-#define AM79C9XX_ETH_PHY 0x00006B70 /* 10baseT PHY */
-#define MII_PHYID_REV_MASK 0xfffffff0
-#define MII_PHYID_REG1 0x02
-#define MII_PHYID_REG2 0x03
-
-static struct hw_info hw_info[] = {
- { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT },
- { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
- { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 },
- { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94,
- DELAY_OUTPUT | HAS_IBM_MISC },
- { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 },
- { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 },
- { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 },
- { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 },
- { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 },
- { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 },
- { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 },
- { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 },
- { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 },
- { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 },
- { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 },
- { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 },
- { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 },
- { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 },
- { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45,
- HAS_MISC_REG | HAS_IBM_MISC },
- { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 },
- { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 },
- { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
- { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
- DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
- { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
- { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
- { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
- { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
- { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
-};
-
-#define NR_INFO ARRAY_SIZE(hw_info)
-
-static struct hw_info default_info = { 0, 0, 0, 0, 0 };
-static struct hw_info dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
-static struct hw_info dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
-
-struct pcnet_dev {
- struct pcmcia_device *p_dev;
- u_int flags;
- void __iomem *base;
- struct timer_list watchdog;
- int stale, fast_poll;
- u_char phy_id;
- u_char eth_phy, pna_phy;
- u_short link_status;
- u_long mii_reset;
-};
-
-static inline struct pcnet_dev *PRIV(struct net_device *dev)
-{
- char *p = netdev_priv(dev);
- return (struct pcnet_dev *)(p + sizeof(struct ei_device));
-}
-
-static const struct net_device_ops pcnet_netdev_ops = {
- .ndo_open = pcnet_open,
- .ndo_stop = pcnet_close,
- .ndo_set_config = set_config,
- .ndo_start_xmit = ei_start_xmit,
- .ndo_get_stats = ei_get_stats,
- .ndo_eth_ioctl = ei_ioctl,
- .ndo_set_rx_mode = ei_set_multicast_list,
- .ndo_tx_timeout = ei_tx_timeout,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = ei_poll,
-#endif
-};
-
-static int pcnet_probe(struct pcmcia_device *link)
-{
- struct pcnet_dev *info;
- struct net_device *dev;
-
- dev_dbg(&link->dev, "pcnet_attach()\n");
-
- /* Create new ethernet device */
- dev = __alloc_ei_netdev(sizeof(struct pcnet_dev));
- if (!dev) return -ENOMEM;
- info = PRIV(dev);
- info->p_dev = link;
- link->priv = dev;
-
- link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
-
- dev->netdev_ops = &pcnet_netdev_ops;
-
- return pcnet_config(link);
-} /* pcnet_attach */
-
-static void pcnet_detach(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- dev_dbg(&link->dev, "pcnet_detach\n");
-
- unregister_netdev(dev);
-
- pcnet_release(link);
-
- free_netdev(dev);
-} /* pcnet_detach */
-
-/*======================================================================
-
- This probes for a card's hardware address, for card types that
- encode this information in their CIS.
-
-======================================================================*/
-
-static struct hw_info *get_hwinfo(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- u_char __iomem *base, *virt;
- u8 addr[ETH_ALEN];
- int i, j;
-
- /* Allocate a small memory window */
- link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- link->resource[2]->start = 0; link->resource[2]->end = 0;
- i = pcmcia_request_window(link, link->resource[2], 0);
- if (i != 0)
- return NULL;
-
- virt = ioremap(link->resource[2]->start,
- resource_size(link->resource[2]));
- if (unlikely(!virt)) {
- pcmcia_release_window(link, link->resource[2]);
- return NULL;
- }
-
- for (i = 0; i < NR_INFO; i++) {
- pcmcia_map_mem_page(link, link->resource[2],
- hw_info[i].offset & ~(resource_size(link->resource[2])-1));
- base = &virt[hw_info[i].offset & (resource_size(link->resource[2])-1)];
- if ((readb(base+0) == hw_info[i].a0) &&
- (readb(base+2) == hw_info[i].a1) &&
- (readb(base+4) == hw_info[i].a2)) {
- for (j = 0; j < 6; j++)
- addr[j] = readb(base + (j<<1));
- eth_hw_addr_set(dev, addr);
- break;
- }
- }
-
- iounmap(virt);
- j = pcmcia_release_window(link, link->resource[2]);
- return (i < NR_INFO) ? hw_info+i : NULL;
-} /* get_hwinfo */
-
-/*======================================================================
-
- This probes for a card's hardware address by reading the PROM.
- It checks the address against a list of known types, then falls
- back to a simple NE2000 clone signature check.
-
-======================================================================*/
-
-static struct hw_info *get_prom(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- u8 addr[ETH_ALEN];
- u_char prom[32];
- int i, j;
-
- /* This is lifted straight from drivers/net/ethernet/8390/ne.c */
- struct {
- u_char value, offset;
- } program_seq[] = {
- {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
- {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
- {0x00, EN0_RCNTLO}, /* Clear the count regs. */
- {0x00, EN0_RCNTHI},
- {0x00, EN0_IMR}, /* Mask completion irq. */
- {0xFF, EN0_ISR},
- {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
- {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
- {32, EN0_RCNTLO},
- {0x00, EN0_RCNTHI},
- {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
- {0x00, EN0_RSARHI},
- {E8390_RREAD+E8390_START, E8390_CMD},
- };
-
- pcnet_reset_8390(dev);
- mdelay(10);
-
- for (i = 0; i < ARRAY_SIZE(program_seq); i++)
- outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
-
- for (i = 0; i < 32; i++)
- prom[i] = inb(ioaddr + PCNET_DATAPORT);
- for (i = 0; i < NR_INFO; i++) {
- if ((prom[0] == hw_info[i].a0) &&
- (prom[2] == hw_info[i].a1) &&
- (prom[4] == hw_info[i].a2))
- break;
- }
- if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) {
- for (j = 0; j < 6; j++)
- addr[j] = prom[j<<1];
- eth_hw_addr_set(dev, addr);
- return (i < NR_INFO) ? hw_info+i : &default_info;
- }
- return NULL;
-} /* get_prom */
-
-/*======================================================================
-
- For DL10019 based cards, like the Linksys EtherFast
-
-======================================================================*/
-
-static struct hw_info *get_dl10019(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- u8 addr[ETH_ALEN];
- int i;
- u_char sum;
-
- for (sum = 0, i = 0x14; i < 0x1c; i++)
- sum += inb_p(dev->base_addr + i);
- if (sum != 0xff)
- return NULL;
- for (i = 0; i < 6; i++)
- addr[i] = inb_p(dev->base_addr + 0x14 + i);
- eth_hw_addr_set(dev, addr);
- i = inb(dev->base_addr + 0x1f);
- return ((i == 0x91)||(i == 0x99)) ? &dl10022_info : &dl10019_info;
-}
-
-/*======================================================================
-
- For Asix AX88190 based cards
-
-======================================================================*/
-
-static struct hw_info *get_ax88190(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- u8 addr[ETH_ALEN];
- int i, j;
-
- /* Not much of a test, but the alternatives are messy */
- if (link->config_base != 0x03c0)
- return NULL;
-
- outb_p(0x01, ioaddr + EN0_DCFG); /* Set word-wide access. */
- outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0400. */
- outb_p(0x04, ioaddr + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, ioaddr + E8390_CMD);
-
- for (i = 0; i < 6; i += 2) {
- j = inw(ioaddr + PCNET_DATAPORT);
- addr[i] = j & 0xff;
- addr[i+1] = j >> 8;
- }
- eth_hw_addr_set(dev, addr);
- return NULL;
-}
-
-/*======================================================================
-
- This should be totally unnecessary... but when we can't figure
- out the hardware address any other way, we'll let the user hard
- wire it when the module is initialized.
-
-======================================================================*/
-
-static struct hw_info *get_hwired(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- u8 addr[ETH_ALEN];
- int i;
-
- for (i = 0; i < 6; i++)
- if (hw_addr[i] != 0) break;
- if (i == 6)
- return NULL;
-
- for (i = 0; i < 6; i++)
- addr[i] = hw_addr[i];
- eth_hw_addr_set(dev, addr);
-
- return &default_info;
-} /* get_hwired */
-
-static int try_io_port(struct pcmcia_device *link)
-{
- int j, ret;
- link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- if (link->resource[0]->end == 32) {
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- if (link->resource[1]->end > 0) {
- /* for master/slave multifunction cards */
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- }
- } else {
- /* This should be two 16-port windows */
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
- }
- if (link->resource[0]->start == 0) {
- for (j = 0; j < 0x400; j += 0x20) {
- link->resource[0]->start = j ^ 0x300;
- link->resource[1]->start = (j ^ 0x300) + 0x10;
- link->io_lines = 16;
- ret = pcmcia_request_io(link);
- if (ret == 0)
- return ret;
- }
- return ret;
- } else {
- return pcmcia_request_io(link);
- }
-}
-
-static int pcnet_confcheck(struct pcmcia_device *p_dev, void *priv_data)
-{
- int *priv = priv_data;
- int try = (*priv & 0x1);
-
- *priv &= (p_dev->resource[2]->end >= 0x4000) ? 0x10 : ~0x10;
-
- if (p_dev->config_index == 0)
- return -EINVAL;
-
- if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32)
- return -EINVAL;
-
- if (try)
- p_dev->io_lines = 16;
- return try_io_port(p_dev);
-}
-
-static struct hw_info *pcnet_try_config(struct pcmcia_device *link,
- int *has_shmem, int try)
-{
- struct net_device *dev = link->priv;
- struct hw_info *local_hw_info;
- struct pcnet_dev *info = PRIV(dev);
- int priv = try;
- int ret;
-
- ret = pcmcia_loop_config(link, pcnet_confcheck, &priv);
- if (ret) {
- dev_warn(&link->dev, "no useable port range found\n");
- return NULL;
- }
- *has_shmem = (priv & 0x10);
-
- if (!link->irq)
- return NULL;
-
- if (resource_size(link->resource[1]) == 8)
- link->config_flags |= CONF_ENABLE_SPKR;
-
- if ((link->manf_id == MANFID_IBM) &&
- (link->card_id == PRODID_IBM_HOME_AND_AWAY))
- link->config_index |= 0x10;
-
- ret = pcmcia_enable_device(link);
- if (ret)
- return NULL;
-
- dev->irq = link->irq;
- dev->base_addr = link->resource[0]->start;
-
- if (info->flags & HAS_MISC_REG) {
- if ((if_port == 1) || (if_port == 2))
- dev->if_port = if_port;
- else
- dev_notice(&link->dev, "invalid if_port requested\n");
- } else
- dev->if_port = 0;
-
- if ((link->config_base == 0x03c0) &&
- (link->manf_id == 0x149) && (link->card_id == 0xc1ab)) {
- dev_info(&link->dev,
- "this is an AX88190 card - use axnet_cs instead.\n");
- return NULL;
- }
-
- local_hw_info = get_hwinfo(link);
- if (!local_hw_info)
- local_hw_info = get_prom(link);
- if (!local_hw_info)
- local_hw_info = get_dl10019(link);
- if (!local_hw_info)
- local_hw_info = get_ax88190(link);
- if (!local_hw_info)
- local_hw_info = get_hwired(link);
-
- return local_hw_info;
-}
-
-static int pcnet_config(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
- struct pcnet_dev *info = PRIV(dev);
- int start_pg, stop_pg, cm_offset;
- int has_shmem = 0;
- struct hw_info *local_hw_info;
-
- dev_dbg(&link->dev, "pcnet_config\n");
-
- local_hw_info = pcnet_try_config(link, &has_shmem, 0);
- if (!local_hw_info) {
- /* check whether forcing io_lines to 16 helps... */
- pcmcia_disable_device(link);
- local_hw_info = pcnet_try_config(link, &has_shmem, 1);
- if (local_hw_info == NULL) {
- dev_notice(&link->dev, "unable to read hardware net"
- " address for io base %#3lx\n", dev->base_addr);
- goto failed;
- }
- }
-
- info->flags = local_hw_info->flags;
- /* Check for user overrides */
- info->flags |= (delay_output) ? DELAY_OUTPUT : 0;
- if ((link->manf_id == MANFID_SOCKET) &&
- ((link->card_id == PRODID_SOCKET_LPE) ||
- (link->card_id == PRODID_SOCKET_LPE_CF) ||
- (link->card_id == PRODID_SOCKET_EIO)))
- info->flags &= ~USE_BIG_BUF;
- if (!use_big_buf)
- info->flags &= ~USE_BIG_BUF;
-
- if (info->flags & USE_BIG_BUF) {
- start_pg = SOCKET_START_PG;
- stop_pg = SOCKET_STOP_PG;
- cm_offset = 0x10000;
- } else {
- start_pg = PCNET_START_PG;
- stop_pg = PCNET_STOP_PG;
- cm_offset = 0;
- }
-
- /* has_shmem is ignored if use_shmem != -1 */
- if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) ||
- (setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0))
- setup_dma_config(link, start_pg, stop_pg);
-
- ei_status.name = "NE2000";
- ei_status.word16 = 1;
- ei_status.reset_8390 = pcnet_reset_8390;
-
- if (info->flags & (IS_DL10019|IS_DL10022))
- mii_phy_probe(dev);
-
- SET_NETDEV_DEV(dev, &link->dev);
-
- if (register_netdev(dev) != 0) {
- pr_notice("register_netdev() failed\n");
- goto failed;
- }
-
- if (info->flags & (IS_DL10019|IS_DL10022)) {
- u_char id = inb(dev->base_addr + 0x1a);
- netdev_info(dev, "NE2000 (DL100%d rev %02x): ",
- (info->flags & IS_DL10022) ? 22 : 19, id);
- if (info->pna_phy)
- pr_cont("PNA, ");
- } else {
- netdev_info(dev, "NE2000 Compatible: ");
- }
- pr_cont("io %#3lx, irq %d,", dev->base_addr, dev->irq);
- if (info->flags & USE_SHMEM)
- pr_cont(" mem %#5lx,", dev->mem_start);
- if (info->flags & HAS_MISC_REG)
- pr_cont(" %s xcvr,", if_names[dev->if_port]);
- pr_cont(" hw_addr %pM\n", dev->dev_addr);
- return 0;
-
-failed:
- pcnet_release(link);
- return -ENODEV;
-} /* pcnet_config */
-
-static void pcnet_release(struct pcmcia_device *link)
-{
- struct pcnet_dev *info = PRIV(link->priv);
-
- dev_dbg(&link->dev, "pcnet_release\n");
-
- if (info->flags & USE_SHMEM)
- iounmap(info->base);
-
- pcmcia_disable_device(link);
-}
-
-static int pcnet_suspend(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open)
- netif_device_detach(dev);
-
- return 0;
-}
-
-static int pcnet_resume(struct pcmcia_device *link)
-{
- struct net_device *dev = link->priv;
-
- if (link->open) {
- pcnet_reset_8390(dev);
- NS8390_init(dev, 1);
- netif_device_attach(dev);
- }
-
- return 0;
-}
-
-
-/*======================================================================
-
- MII interface support for DL10019 and DL10022 based cards
-
- On the DL10019, the MII IO direction bit is 0x10; on the DL10022
- it is 0x20. Setting both bits seems to work on both card types.
-
-======================================================================*/
-
-#define DLINK_GPIO 0x1c
-#define DLINK_DIAG 0x1d
-#define DLINK_EEPROM 0x1e
-
-#define MDIO_SHIFT_CLK 0x80
-#define MDIO_DATA_OUT 0x40
-#define MDIO_DIR_WRITE 0x30
-#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
-#define MDIO_DATA_READ 0x10
-#define MDIO_MASK 0x0f
-
-static void mdio_sync(unsigned int addr)
-{
- int bits, mask = inb(addr) & MDIO_MASK;
- for (bits = 0; bits < 32; bits++) {
- outb(mask | MDIO_DATA_WRITE1, addr);
- outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
- }
-}
-
-static int mdio_read(unsigned int addr, int phy_id, int loc)
-{
- u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
- int i, retval = 0, mask = inb(addr) & MDIO_MASK;
-
- mdio_sync(addr);
- for (i = 13; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(mask | dat, addr);
- outb(mask | dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 19; i > 0; i--) {
- outb(mask, addr);
- retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
- outb(mask | MDIO_SHIFT_CLK, addr);
- }
- return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(unsigned int addr, int phy_id, int loc, int value)
-{
- u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
- int i, mask = inb(addr) & MDIO_MASK;
-
- mdio_sync(addr);
- for (i = 31; i >= 0; i--) {
- int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outb(mask | dat, addr);
- outb(mask | dat | MDIO_SHIFT_CLK, addr);
- }
- for (i = 1; i >= 0; i--) {
- outb(mask, addr);
- outb(mask | MDIO_SHIFT_CLK, addr);
- }
-}
-
-/*======================================================================
-
- EEPROM access routines for DL10019 and DL10022 based cards
-
-======================================================================*/
-
-#define EE_EEP 0x40
-#define EE_ASIC 0x10
-#define EE_CS 0x08
-#define EE_CK 0x04
-#define EE_DO 0x02
-#define EE_DI 0x01
-#define EE_ADOT 0x01 /* DataOut for ASIC */
-#define EE_READ_CMD 0x06
-
-#define DL19FDUPLX 0x0400 /* DL10019 Full duplex mode */
-
-static int read_eeprom(unsigned int ioaddr, int location)
-{
- int i, retval = 0;
- unsigned int ee_addr = ioaddr + DLINK_EEPROM;
- int read_cmd = location | (EE_READ_CMD << 8);
-
- outb(0, ee_addr);
- outb(EE_EEP|EE_CS, ee_addr);
-
- /* Shift the read command bits out. */
- for (i = 10; i >= 0; i--) {
- short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
- outb_p(EE_EEP|EE_CS|dataval, ee_addr);
- outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
- }
- outb(EE_EEP|EE_CS, ee_addr);
-
- for (i = 16; i > 0; i--) {
- outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
- retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
- outb_p(EE_EEP|EE_CS, ee_addr);
- }
-
- /* Terminate the EEPROM access. */
- outb(0, ee_addr);
- return retval;
-}
-
-/*
- The internal ASIC registers can be changed by EEPROM READ access
- with EE_ASIC bit set.
- In ASIC mode, EE_ADOT is used to output the data to the ASIC.
-*/
-
-static void write_asic(unsigned int ioaddr, int location, short asic_data)
-{
- int i;
- unsigned int ee_addr = ioaddr + DLINK_EEPROM;
- short dataval;
- int read_cmd = location | (EE_READ_CMD << 8);
-
- asic_data |= read_eeprom(ioaddr, location);
-
- outb(0, ee_addr);
- outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
-
- read_cmd = read_cmd >> 1;
-
- /* Shift the read command bits out. */
- for (i = 9; i >= 0; i--) {
- dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
- outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
- outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
- outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
- }
- // sync
- outb(EE_ASIC|EE_CS, ee_addr);
- outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
- outb(EE_ASIC|EE_CS, ee_addr);
-
- for (i = 15; i >= 0; i--) {
- dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
- outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
- outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
- outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
- }
-
- /* Terminate the ASIC access. */
- outb(EE_ASIC|EE_DI, ee_addr);
- outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
- outb(EE_ASIC|EE_DI, ee_addr);
-
- outb(0, ee_addr);
-}
-
-/*====================================================================*/
-
-static void set_misc_reg(struct net_device *dev)
-{
- unsigned int nic_base = dev->base_addr;
- struct pcnet_dev *info = PRIV(dev);
- u_char tmp;
-
- if (info->flags & HAS_MISC_REG) {
- tmp = inb_p(nic_base + PCNET_MISC) & ~3;
- if (dev->if_port == 2)
- tmp |= 1;
- if (info->flags & USE_BIG_BUF)
- tmp |= 2;
- if (info->flags & HAS_IBM_MISC)
- tmp |= 8;
- outb_p(tmp, nic_base + PCNET_MISC);
- }
- if (info->flags & IS_DL10022) {
- if (info->flags & HAS_MII) {
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
- /* Restart MII autonegotiation */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
- info->mii_reset = jiffies;
- } else {
- outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
- }
- } else if (info->flags & IS_DL10019) {
- /* Advertise 100F, 100H, 10F, 10H */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 4, 0x01e1);
- /* Restart MII autonegotiation */
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
- mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
- }
-}
-
-/*====================================================================*/
-
-static void mii_phy_probe(struct net_device *dev)
-{
- struct pcnet_dev *info = PRIV(dev);
- unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
- int i;
- u_int tmp, phyid;
-
- for (i = 31; i >= 0; i--) {
- tmp = mdio_read(mii_addr, i, 1);
- if ((tmp == 0) || (tmp == 0xffff))
- continue;
- tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
- phyid = tmp << 16;
- phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
- phyid &= MII_PHYID_REV_MASK;
- netdev_dbg(dev, "MII at %d is 0x%08x\n", i, phyid);
- if (phyid == AM79C9XX_HOME_PHY) {
- info->pna_phy = i;
- } else if (phyid != AM79C9XX_ETH_PHY) {
- info->eth_phy = i;
- }
- }
-}
-
-static int pcnet_open(struct net_device *dev)
-{
- int ret;
- struct pcnet_dev *info = PRIV(dev);
- struct pcmcia_device *link = info->p_dev;
- unsigned int nic_base = dev->base_addr;
-
- dev_dbg(&link->dev, "pcnet_open('%s')\n", dev->name);
-
- if (!pcmcia_dev_present(link))
- return -ENODEV;
-
- set_misc_reg(dev);
-
- outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */
- ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, dev->name, dev);
- if (ret)
- return ret;
-
- link->open++;
-
- info->phy_id = info->eth_phy;
- info->link_status = 0x00;
- timer_setup(&info->watchdog, ei_watchdog, 0);
- mod_timer(&info->watchdog, jiffies + HZ);
-
- return ei_open(dev);
-} /* pcnet_open */
-
-/*====================================================================*/
-
-static int pcnet_close(struct net_device *dev)
-{
- struct pcnet_dev *info = PRIV(dev);
- struct pcmcia_device *link = info->p_dev;
-
- dev_dbg(&link->dev, "pcnet_close('%s')\n", dev->name);
-
- ei_close(dev);
- free_irq(dev->irq, dev);
-
- link->open--;
- netif_stop_queue(dev);
- timer_delete_sync(&info->watchdog);
-
- return 0;
-} /* pcnet_close */
-
-/*======================================================================
-
- Hard reset the card. This used to pause for the same period that
- a 8390 reset command required, but that shouldn't be necessary.
-
-======================================================================*/
-
-static void pcnet_reset_8390(struct net_device *dev)
-{
- unsigned int nic_base = dev->base_addr;
- int i;
-
- ei_status.txing = ei_status.dmaing = 0;
-
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
-
- outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
-
- for (i = 0; i < 100; i++) {
- if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
- break;
- udelay(100);
- }
- outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
-
- if (i == 100)
- netdev_err(dev, "pcnet_reset_8390() did not complete.\n");
-
- set_misc_reg(dev);
-
-} /* pcnet_reset_8390 */
-
-/*====================================================================*/
-
-static int set_config(struct net_device *dev, struct ifmap *map)
-{
- struct pcnet_dev *info = PRIV(dev);
- if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
- if (!(info->flags & HAS_MISC_REG))
- return -EOPNOTSUPP;
- else if ((map->port < 1) || (map->port > 2))
- return -EINVAL;
- WRITE_ONCE(dev->if_port, map->port);
- netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
- NS8390_init(dev, 1);
- }
- return 0;
-}
-
-/*====================================================================*/
-
-static irqreturn_t ei_irq_wrapper(int irq, void *dev_id)
-{
- struct net_device *dev = dev_id;
- struct pcnet_dev *info;
- irqreturn_t ret = ei_interrupt(irq, dev_id);
-
- if (ret == IRQ_HANDLED) {
- info = PRIV(dev);
- info->stale = 0;
- }
- return ret;
-}
-
-static void ei_watchdog(struct timer_list *t)
-{
- struct pcnet_dev *info = timer_container_of(info, t, watchdog);
- struct net_device *dev = info->p_dev->priv;
- unsigned int nic_base = dev->base_addr;
- unsigned int mii_addr = nic_base + DLINK_GPIO;
- u_short link;
-
- 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 (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
- if (!info->fast_poll)
- netdev_info(dev, "interrupt(s) dropped!\n");
- ei_irq_wrapper(dev->irq, dev);
- info->fast_poll = HZ;
- }
- if (info->fast_poll) {
- info->fast_poll--;
- info->watchdog.expires = jiffies + 1;
- add_timer(&info->watchdog);
- return;
- }
-
- if (!(info->flags & HAS_MII))
- goto reschedule;
-
- mdio_read(mii_addr, info->phy_id, 1);
- link = mdio_read(mii_addr, info->phy_id, 1);
- if (!link || (link == 0xffff)) {
- if (info->eth_phy) {
- info->phy_id = info->eth_phy = 0;
- } else {
- netdev_info(dev, "MII is missing!\n");
- info->flags &= ~HAS_MII;
- }
- goto reschedule;
- }
-
- link &= 0x0004;
- if (link != info->link_status) {
- u_short p = mdio_read(mii_addr, info->phy_id, 5);
- netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
- if (link && (info->flags & IS_DL10022)) {
- /* Disable collision detection on full duplex links */
- outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
- } else if (link && (info->flags & IS_DL10019)) {
- /* Disable collision detection on full duplex links */
- write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
- }
- if (link) {
- if (info->phy_id == info->eth_phy) {
- if (p)
- netdev_info(dev, "autonegotiation complete: "
- "%sbaseT-%cD selected\n",
- ((p & 0x0180) ? "100" : "10"),
- ((p & 0x0140) ? 'F' : 'H'));
- else
- netdev_info(dev, "link partner did not autonegotiate\n");
- }
- NS8390_init(dev, 1);
- }
- info->link_status = link;
- }
- if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) {
- link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
- if (((info->phy_id == info->pna_phy) && link) ||
- ((info->phy_id != info->pna_phy) && !link)) {
- /* isolate this MII and try flipping to the other one */
- mdio_write(mii_addr, info->phy_id, 0, 0x0400);
- info->phy_id ^= info->pna_phy ^ info->eth_phy;
- netdev_info(dev, "switched to %s transceiver\n",
- (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
- mdio_write(mii_addr, info->phy_id, 0,
- (info->phy_id == info->eth_phy) ? 0x1000 : 0);
- info->link_status = 0;
- info->mii_reset = jiffies;
- }
- }
-
-reschedule:
- info->watchdog.expires = jiffies + HZ;
- add_timer(&info->watchdog);
-}
-
-/*====================================================================*/
-
-
-static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
- struct pcnet_dev *info = PRIV(dev);
- struct mii_ioctl_data *data = if_mii(rq);
- unsigned int mii_addr = dev->base_addr + DLINK_GPIO;
-
- if (!(info->flags & (IS_DL10019|IS_DL10022)))
- return -EINVAL;
-
- switch (cmd) {
- case SIOCGMIIPHY:
- data->phy_id = info->phy_id;
- fallthrough;
- case SIOCGMIIREG: /* Read MII PHY register. */
- data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
- return 0;
- case SIOCSMIIREG: /* Write MII PHY register. */
- mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
- return 0;
- }
- return -EOPNOTSUPP;
-}
-
-/*====================================================================*/
-
-static void dma_get_8390_hdr(struct net_device *dev,
- struct e8390_pkt_hdr *hdr,
- int ring_page)
-{
- unsigned int nic_base = dev->base_addr;
-
- if (ei_status.dmaing) {
- netdev_err(dev, "DMAing conflict in dma_block_input."
- "[DMAstat:%1x][irqlock:%1x]\n",
- ei_status.dmaing, ei_status.irqlock);
- return;
- }
-
- ei_status.dmaing |= 0x01;
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
- outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
- outb_p(0, nic_base + EN0_RCNTHI);
- outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
- outb_p(ring_page, nic_base + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
-
- insw(nic_base + PCNET_DATAPORT, hdr,
- sizeof(struct e8390_pkt_hdr)>>1);
- /* Fix for big endian systems */
- hdr->count = le16_to_cpu(hdr->count);
-
- outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
- ei_status.dmaing &= ~0x01;
-}
-
-/*====================================================================*/
-
-static void dma_block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset)
-{
- unsigned int nic_base = dev->base_addr;
- int xfer_count = count;
- char *buf = skb->data;
- struct ei_device *ei_local = netdev_priv(dev);
-
- if ((netif_msg_rx_status(ei_local)) && (count != 4))
- netdev_dbg(dev, "[bi=%d]\n", count+4);
- if (ei_status.dmaing) {
- netdev_err(dev, "DMAing conflict in dma_block_input."
- "[DMAstat:%1x][irqlock:%1x]\n",
- ei_status.dmaing, ei_status.irqlock);
- return;
- }
- ei_status.dmaing |= 0x01;
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
- outb_p(count & 0xff, nic_base + EN0_RCNTLO);
- outb_p(count >> 8, nic_base + EN0_RCNTHI);
- outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
- outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
- outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
-
- insw(nic_base + PCNET_DATAPORT,buf,count>>1);
- if (count & 0x01) {
- buf[count-1] = inb(nic_base + PCNET_DATAPORT);
- xfer_count++;
- }
-
- /* This was for the ALPHA version only, but enough people have been
- encountering problems that it is still here. */
-#ifdef PCMCIA_DEBUG
- /* DMA termination address check... */
- if (netif_msg_rx_status(ei_local)) {
- int addr, tries = 20;
- do {
- /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
- -- it's broken for Rx on some cards! */
- int high = inb_p(nic_base + EN0_RSARHI);
- int low = inb_p(nic_base + EN0_RSARLO);
- addr = (high << 8) + low;
- if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
- break;
- } while (--tries > 0);
- if (tries <= 0)
- netdev_notice(dev, "RX transfer address mismatch,"
- "%#4.4x (expected) vs. %#4.4x (actual).\n",
- ring_offset + xfer_count, addr);
- }
-#endif
- outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
- ei_status.dmaing &= ~0x01;
-} /* dma_block_input */
-
-/*====================================================================*/
-
-static void dma_block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page)
-{
- unsigned int nic_base = dev->base_addr;
- struct pcnet_dev *info = PRIV(dev);
-#ifdef PCMCIA_DEBUG
- int retries = 0;
- struct ei_device *ei_local = netdev_priv(dev);
-#endif
- u_long dma_start;
-
-#ifdef PCMCIA_DEBUG
- netif_dbg(ei_local, tx_queued, dev, "[bo=%d]\n", count);
-#endif
-
- /* Round the count up for word writes. Do we need to do this?
- What effect will an odd byte count have on the 8390?
- I should check someday. */
- if (count & 0x01)
- count++;
- if (ei_status.dmaing) {
- netdev_err(dev, "DMAing conflict in dma_block_output."
- "[DMAstat:%1x][irqlock:%1x]\n",
- ei_status.dmaing, ei_status.irqlock);
- return;
- }
- ei_status.dmaing |= 0x01;
- /* We should already be in page 0, but to be safe... */
- outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);
-
-#ifdef PCMCIA_DEBUG
- retry:
-#endif
-
- outb_p(ENISR_RDC, nic_base + EN0_ISR);
-
- /* Now the normal output. */
- outb_p(count & 0xff, nic_base + EN0_RCNTLO);
- outb_p(count >> 8, nic_base + EN0_RCNTHI);
- outb_p(0x00, nic_base + EN0_RSARLO);
- outb_p(start_page, nic_base + EN0_RSARHI);
-
- outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD);
- outsw(nic_base + PCNET_DATAPORT, buf, count>>1);
-
- dma_start = jiffies;
-
-#ifdef PCMCIA_DEBUG
- /* This was for the ALPHA version only, but enough people have been
- encountering problems that it is still here. */
- /* DMA termination address check... */
- if (netif_msg_tx_queued(ei_local)) {
- int addr, tries = 20;
- do {
- int high = inb_p(nic_base + EN0_RSARHI);
- int low = inb_p(nic_base + EN0_RSARLO);
- addr = (high << 8) + low;
- if ((start_page << 8) + count == addr)
- break;
- } while (--tries > 0);
- if (tries <= 0) {
- netdev_notice(dev, "Tx packet transfer address mismatch,"
- "%#4.4x (expected) vs. %#4.4x (actual).\n",
- (start_page << 8) + count, addr);
- if (retries++ == 0)
- goto retry;
- }
- }
-#endif
-
- while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
- if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) {
- netdev_warn(dev, "timeout waiting for Tx RDC.\n");
- pcnet_reset_8390(dev);
- NS8390_init(dev, 1);
- break;
- }
-
- outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
- if (info->flags & DELAY_OUTPUT)
- udelay((long)delay_time);
- ei_status.dmaing &= ~0x01;
-}
-
-/*====================================================================*/
-
-static int setup_dma_config(struct pcmcia_device *link, int start_pg,
- int stop_pg)
-{
- struct net_device *dev = link->priv;
-
- ei_status.tx_start_page = start_pg;
- ei_status.rx_start_page = start_pg + TX_PAGES;
- ei_status.stop_page = stop_pg;
-
- /* set up block i/o functions */
- ei_status.get_8390_hdr = dma_get_8390_hdr;
- ei_status.block_input = dma_block_input;
- ei_status.block_output = dma_block_output;
-
- return 0;
-}
-
-/*====================================================================*/
-
-static void copyin(void *dest, void __iomem *src, int c)
-{
- u_short *d = dest;
- u_short __iomem *s = src;
- int odd;
-
- if (c <= 0)
- return;
- odd = (c & 1); c >>= 1;
-
- if (c) {
- do { *d++ = __raw_readw(s++); } while (--c);
- }
- /* get last byte by fetching a word and masking */
- if (odd)
- *((u_char *)d) = readw(s) & 0xff;
-}
-
-static void copyout(void __iomem *dest, const void *src, int c)
-{
- u_short __iomem *d = dest;
- const u_short *s = src;
- int odd;
-
- if (c <= 0)
- return;
- odd = (c & 1); c >>= 1;
-
- if (c) {
- do { __raw_writew(*s++, d++); } while (--c);
- }
- /* copy last byte doing a read-modify-write */
- if (odd)
- writew((readw(d) & 0xff00) | *(u_char *)s, d);
-}
-
-/*====================================================================*/
-
-static void shmem_get_8390_hdr(struct net_device *dev,
- struct e8390_pkt_hdr *hdr,
- int ring_page)
-{
- void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
- + (ring_page << 8)
- - (ei_status.rx_start_page << 8);
-
- copyin(hdr, xfer_start, sizeof(struct e8390_pkt_hdr));
- /* Fix for big endian systems */
- hdr->count = le16_to_cpu(hdr->count);
-}
-
-/*====================================================================*/
-
-static void shmem_block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset)
-{
- void __iomem *base = ei_status.mem;
- unsigned long offset = (TX_PAGES<<8) + ring_offset
- - (ei_status.rx_start_page << 8);
- char *buf = skb->data;
-
- if (offset + count > ei_status.priv) {
- /* We must wrap the input move. */
- int semi_count = ei_status.priv - offset;
- copyin(buf, base + offset, semi_count);
- buf += semi_count;
- offset = TX_PAGES<<8;
- count -= semi_count;
- }
- copyin(buf, base + offset, count);
-}
-
-/*====================================================================*/
-
-static void shmem_block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page)
-{
- void __iomem *shmem = ei_status.mem + (start_page << 8);
- shmem -= ei_status.tx_start_page << 8;
- copyout(shmem, buf, count);
-}
-
-/*====================================================================*/
-
-static int setup_shmem_window(struct pcmcia_device *link, int start_pg,
- int stop_pg, int cm_offset)
-{
- struct net_device *dev = link->priv;
- struct pcnet_dev *info = PRIV(dev);
- int i, window_size, offset, ret;
-
- window_size = (stop_pg - start_pg) << 8;
- if (window_size > 32 * 1024)
- window_size = 32 * 1024;
-
- /* Make sure it's a power of two. */
- window_size = roundup_pow_of_two(window_size);
-
- /* Allocate a memory window */
- link->resource[3]->flags |= WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
- link->resource[3]->flags |= WIN_USE_WAIT;
- link->resource[3]->start = 0; link->resource[3]->end = window_size;
- ret = pcmcia_request_window(link, link->resource[3], mem_speed);
- if (ret)
- goto failed;
-
- offset = (start_pg << 8) + cm_offset;
- offset -= offset % window_size;
- ret = pcmcia_map_mem_page(link, link->resource[3], offset);
- if (ret)
- goto failed;
-
- /* Try scribbling on the buffer */
- info->base = ioremap(link->resource[3]->start,
- resource_size(link->resource[3]));
- if (unlikely(!info->base)) {
- ret = -ENOMEM;
- goto failed;
- }
-
- for (i = 0; i < (TX_PAGES<<8); i += 2)
- __raw_writew((i>>1), info->base+offset+i);
- udelay(100);
- for (i = 0; i < (TX_PAGES<<8); i += 2)
- if (__raw_readw(info->base+offset+i) != (i>>1)) break;
- pcnet_reset_8390(dev);
- if (i != (TX_PAGES<<8)) {
- iounmap(info->base);
- pcmcia_release_window(link, link->resource[3]);
- info->base = NULL;
- goto failed;
- }
-
- ei_status.mem = info->base + offset;
- ei_status.priv = resource_size(link->resource[3]);
- dev->mem_start = (u_long)ei_status.mem;
- dev->mem_end = dev->mem_start + resource_size(link->resource[3]);
-
- ei_status.tx_start_page = start_pg;
- ei_status.rx_start_page = start_pg + TX_PAGES;
- ei_status.stop_page = start_pg + (
- (resource_size(link->resource[3]) - offset) >> 8);
-
- /* set up block i/o functions */
- ei_status.get_8390_hdr = shmem_get_8390_hdr;
- ei_status.block_input = shmem_block_input;
- ei_status.block_output = shmem_block_output;
-
- info->flags |= USE_SHMEM;
- return 0;
-
-failed:
- return 1;
-}
-
-/*====================================================================*/
-
-static const struct pcmcia_device_id pcnet_ids[] = {
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0x3341),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0xc0ab),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x021b, 0x0101),
- PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x08a1, 0xc0ab),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
- PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
- PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
- PCMCIA_MFC_DEVICE_PROD_ID123(0, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
- PCMCIA_MFC_DEVICE_PROD_ID2(0, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
- PCMCIA_DEVICE_MANF_CARD(0x0057, 0x1004),
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x000d),
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0075),
- PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0145),
- PCMCIA_DEVICE_MANF_CARD(0x0149, 0x0230),
- PCMCIA_DEVICE_MANF_CARD(0x0149, 0x4530),
- PCMCIA_DEVICE_MANF_CARD(0x0149, 0xc1ab),
- PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0110),
- PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x8041),
- PCMCIA_DEVICE_MANF_CARD(0x0213, 0x2452),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0300),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0307),
- PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030a),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1103),
- PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1121),
- PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0009),
- PCMCIA_DEVICE_PROD_ID12("2408LAN", "Ethernet", 0x352fff7f, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID1234("Socket", "CF 10/100 Ethernet Card", "Revision B", "05/11/06", 0xb38bcc2e, 0x4de88352, 0xeaca6c8d, 0x7e57c22e),
- PCMCIA_DEVICE_PROD_ID123("Cardwell", "PCMCIA", "ETHERNET", 0x9533672e, 0x281f1c5d, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("CNet ", "CN30BC", "ETHERNET", 0x9fe55d3d, 0x85601198, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("Digital", "Ethernet", "Adapter", 0x9999ab35, 0x00b2e941, 0x4b0d829e),
- PCMCIA_DEVICE_PROD_ID123("Edimax Technology Inc.", "PCMCIA", "Ethernet Card", 0x738a0019, 0x281f1c5d, 0x5e9d92c0),
- PCMCIA_DEVICE_PROD_ID123("EFA ", "EFA207", "ETHERNET", 0x3d294be4, 0xeb9aab6c, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("I-O DATA", "PCLA", "ETHERNET", 0x1d55d7ec, 0xe4c64d34, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCLATE", "ETHERNET", 0x547e66dc, 0x6b260753, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID123("KingMax Technology Inc.", "EN10-T2", "PCMCIA Ethernet Card", 0x932b7189, 0x699e4436, 0x6f6652e0),
- PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2216", 0x281f1c5d, 0xd4cd2f20, 0xb87add82),
- PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2620", 0x281f1c5d, 0xd4cd2f20, 0x7d3d83a8),
- PCMCIA_DEVICE_PROD_ID1("2412LAN", 0x67f236ab),
- PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2212", 0xdfc6b5b2, 0xcb112a11),
- PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2216-PCMCIA-ETHERNET", 0xdfc6b5b2, 0x5542bfff),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA100-PCM-T V2 100/10M LAN PC Card", 0xbb7fbdd7, 0xcd91cc68),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA100-PCM V2", 0x36634a66, 0xc6d05997),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM_V2", 0xbb7fBdd7, 0x28e299f8),
- PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA-PCM V3", 0x36634a66, 0x62241d96),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8010", 0x5070a7f9, 0x82f96e96),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8610", 0x5070a7f9, 0x86741224),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002", 0x93b15570, 0x75ec3efb),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002T", 0x93b15570, 0x461c5247),
- PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8010", 0x93b15570, 0x82f96e96),
- PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet", 0x578ba6e7, 0x0a9888c1),
- PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet 10/100", 0x578ba6e7, 0x939fedbd),
- PCMCIA_DEVICE_PROD_ID12("AROWANA", "PCMCIA Ethernet LAN Card", 0x313adbc8, 0x08d9f190),
- PCMCIA_DEVICE_PROD_ID12("ASANTE", "FriendlyNet PC Card", 0x3a7ade0f, 0x41c64504),
- PCMCIA_DEVICE_PROD_ID12("Billionton", "LNT-10TB", 0x552ab682, 0xeeb1ba6a),
- PCMCIA_DEVICE_PROD_ID12("CF", "10Base-Ethernet", 0x44ebf863, 0x93ae4d79),
- PCMCIA_DEVICE_PROD_ID12("CNet", "CN40BC Ethernet", 0xbc477dde, 0xfba775a7),
- PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "BASEline PCMCIA 10 MBit Ethernetadapter", 0xfa2e424d, 0xe9190d8a),
- PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "FASTline PCMCIA 10/100 Fast-Ethernet", 0xfa2e424d, 0x3953d9b9),
- PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722),
- PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2),
- PCMCIA_DEVICE_PROD_ID12("corega", "Ether CF-TD", 0x0a21501a, 0x6589340a),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether CF-TD LAN Card", 0x5261440f, 0x8797663b),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-TD", 0x5261440f, 0xc49bd73d),
- PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-TD", 0x5261440f, 0x47d5ca83),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9),
- PCMCIA_DEVICE_PROD_ID12("Corega,K.K.", "Ethernet LAN Card", 0x110d26d9, 0x9fd2f0a2),
- PCMCIA_DEVICE_PROD_ID12("corega,K.K.", "Ethernet LAN Card", 0x9791a90e, 0x9fd2f0a2),
- PCMCIA_DEVICE_PROD_ID12("corega K.K.", "(CG-LAPCCTXD)", 0x5261440f, 0x73ec0d88),
- PCMCIA_DEVICE_PROD_ID12("CouplerlessPCMCIA", "100BASE", 0xee5af0ad, 0x7c2add04),
- PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-010", 0x77008979, 0x9d8d445d),
- PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-110E 10/100M LAN Card", 0x77008979, 0xfd184814),
- PCMCIA_DEVICE_PROD_ID12("DataTrek.", "NetCard ", 0x5cd66d9d, 0x84697ce0),
- PCMCIA_DEVICE_PROD_ID12("Dayna Communications, Inc.", "CommuniCard E", 0x0c629325, 0xb4e7dbaf),
- PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100", 0x697403d8, 0xe160b995),
- PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100 Dongless", 0x697403d8, 0xa6d3b233),
- PCMCIA_DEVICE_PROD_ID12("DIGITAL", "DEPCM-XX", 0x69616cb3, 0xe600e76e),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-650", 0x1a424a1c, 0xf28c8398),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660", 0x1a424a1c, 0xd9a1d05b),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660+", 0x1a424a1c, 0x50dcd0ec),
- PCMCIA_DEVICE_PROD_ID12("D-Link", "DFE-650", 0x1a424a1c, 0x0f0073f9),
- PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 PC Card", 0x725b842d, 0xf1efee84),
- PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 Port Attached PC Card", 0x725b842d, 0x2db1f8e9),
- PCMCIA_DEVICE_PROD_ID12("Dynalink", "L10BC", 0x55632fd5, 0xdc65f2b1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10BC", 0x6a26d1cf, 0xdc65f2b1),
- PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10C", 0x6a26d1cf, 0xc4f84efb),
- PCMCIA_DEVICE_PROD_ID12("E-CARD", "E-CARD", 0x6701da11, 0x6701da11),
- PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet 10BaseT card", 0x53c864c6, 0xedd059f6),
- PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet Combo card", 0x53c864c6, 0x929c486c),
- PCMCIA_DEVICE_PROD_ID12("Ethernet", "Adapter", 0x00b2e941, 0x4b0d829e),
- PCMCIA_DEVICE_PROD_ID12("Ethernet Adapter", "E2000 PCMCIA Ethernet", 0x96767301, 0x71fbbc61),
- PCMCIA_DEVICE_PROD_ID12("Ethernet PCMCIA adapter", "EP-210", 0x8dd86181, 0xf2b52517),
- PCMCIA_DEVICE_PROD_ID12("Fast Ethernet", "Adapter", 0xb4be14e3, 0x4b0d829e),
- PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2000", 0x2a151fac, 0xf00555cb),
- PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2220", 0x2a151fac, 0xc1b7e327),
- PCMCIA_DEVICE_PROD_ID12("GVC", "NIC-2000p", 0x76e171bd, 0x6eb1c947),
- PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "Ethernet", 0xe3736c88, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("IC-CARD", "IC-CARD", 0x60cb09a6, 0x60cb09a6),
- PCMCIA_DEVICE_PROD_ID12("IC-CARD+", "IC-CARD+", 0x93693494, 0x93693494),
- PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCETTX", 0x547e66dc, 0x6fc5459b),
- PCMCIA_DEVICE_PROD_ID12("iPort", "10/100 Ethernet Card", 0x56c538d2, 0x11b0ffc0),
- PCMCIA_DEVICE_PROD_ID12("KANSAI ELECTRIC CO.,LTD", "KLA-PCM/T", 0xb18dc3b4, 0xcc51a956),
- PCMCIA_DEVICE_PROD_ID12("KENTRONICS", "KEP-230", 0xaf8144c9, 0x868f6616),
- PCMCIA_DEVICE_PROD_ID12("KCI", "PE520 PCMCIA Ethernet Adapter", 0xa89b87d3, 0x1eb88e64),
- PCMCIA_DEVICE_PROD_ID12("KINGMAX", "EN10T2T", 0x7bcb459a, 0xa5c81fa5),
- PCMCIA_DEVICE_PROD_ID12("Kingston", "KNE-PC2", 0x1128e633, 0xce2a89b3),
- PCMCIA_DEVICE_PROD_ID12("Kingston Technology Corp.", "EtheRx PC Card Ethernet Adapter", 0x313c7be3, 0x0afb54a2),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-10/100CD", 0x1b7827b2, 0xcda71d1c),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDF", 0x1b7827b2, 0xfec71e40),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDL/T", 0x1b7827b2, 0x79fba4f7),
- PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDS", 0x1b7827b2, 0x931afaab),
- PCMCIA_DEVICE_PROD_ID12("LEMEL", "LM-N89TX PRO", 0xbbefb52f, 0xd2897a97),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "Combo PCMCIA EthernetCard (EC2T)", 0x0733cc81, 0x32ee8c78),
- PCMCIA_DEVICE_PROD_ID12("LINKSYS", "E-CARD", 0xf7cb0b07, 0x6701da11),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 Integrated PC Card (PCM100)", 0x0733cc81, 0x453c3f9d),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100)", 0x0733cc81, 0x66c5a389),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V2)", 0x0733cc81, 0x3a3b28e9),
- PCMCIA_DEVICE_PROD_ID12("Linksys", "HomeLink Phoneline + 10/100 Network PC Card (PCM100H1)", 0x733cc81, 0x7a3e5c3a),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TX", 0x88fcdeda, 0x6d772737),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TE", 0x88fcdeda, 0x0e714bee),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN20T", 0x88fcdeda, 0x81090922),
- PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN10TE", 0x88fcdeda, 0xc1e2521c),
- PCMCIA_DEVICE_PROD_ID12("LONGSHINE", "PCMCIA Ethernet Card", 0xf866b0b0, 0x6f6652e0),
- PCMCIA_DEVICE_PROD_ID12("MACNICA", "ME1-JEIDA", 0x20841b68, 0xaf8a3578),
- PCMCIA_DEVICE_PROD_ID12("Macsense", "MPC-10", 0xd830297f, 0xd265c307),
- PCMCIA_DEVICE_PROD_ID12("Matsushita Electric Industrial Co.,LTD.", "CF-VEL211", 0x44445376, 0x8ded41d4),
- PCMCIA_DEVICE_PROD_ID12("MAXTECH", "PCN2000", 0x78d64bc0, 0xca0ca4b8),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-T", 0x481e0094, 0xa2eb0cf3),
- PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-TX", 0x481e0094, 0x41a6916c),
- PCMCIA_DEVICE_PROD_ID12("Microcom C.E.", "Travel Card LAN 10/100", 0x4b91cec7, 0xe70220d6),
- PCMCIA_DEVICE_PROD_ID12("Microdyne", "NE4200", 0x2e6da59b, 0x0478e472),
- PCMCIA_DEVICE_PROD_ID12("MIDORI ELEC.", "LT-PCMT", 0x648d55c1, 0xbde526c7),
- PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover 4100", 0x36e1191f, 0x60c229b9),
- PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8),
- PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76),
- PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e),
- PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f),
- PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet", 0x281f1c5d, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET", 0x281f1c5d, 0x3ff7175b),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet 10BaseT Card", 0x281f1c5d, 0x4de2f6c8),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Card", 0x281f1c5d, 0x5e9d92c0),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Combo card", 0x281f1c5d, 0x929c486c),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET V1.0", 0x281f1c5d, 0x4d8817c8),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEthernet", 0x281f1c5d, 0xfe871eeb),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast-Ethernet", 0x281f1c5d, 0x45f1f3b4),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FAST ETHERNET CARD", 0x281f1c5d, 0xec5dbca7),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA LAN", "Ethernet", 0x7500e246, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "LNT-10TN", 0x281f1c5d, 0xe707f641),
- PCMCIA_DEVICE_PROD_ID12("PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", "UE2212", 0x281f1c5d, 0xbf17199b),
- PCMCIA_DEVICE_PROD_ID12("PCMCIA", " Ethernet NE2000 Compatible", 0x281f1c5d, 0x42d5d7e1),
- PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10baseT 3.3V", 0xebf91155, 0x30074c80),
- PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10BaseT 3.3V", 0xebf91155, 0x7f5a4f50),
- PCMCIA_DEVICE_PROD_ID12("Psion Dacom", "Gold Card Ethernet", 0xf5f025c2, 0x3a30e110),
- PCMCIA_DEVICE_PROD_ID12("=RELIA==", "Ethernet", 0xcdd0644a, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("RIOS Systems Co.", "PC CARD3 ETHERNET", 0x7dd33481, 0x10b41826),
- PCMCIA_DEVICE_PROD_ID12("RP", "1625B Ethernet NE2000 Compatible", 0xe3e66e22, 0xb96150df),
- PCMCIA_DEVICE_PROD_ID12("RPTI", "EP400 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4a7e2ae0),
- PCMCIA_DEVICE_PROD_ID12("RPTI", "EP401 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4bcbd7fd),
- PCMCIA_DEVICE_PROD_ID12("RPTI LTD.", "EP400", 0xc53ac515, 0x81e39388),
- PCMCIA_DEVICE_PROD_ID12("SCM", "Ethernet Combo card", 0xbdc3b102, 0x929c486c),
- PCMCIA_DEVICE_PROD_ID12("Seiko Epson Corp.", "Ethernet", 0x09928730, 0x00b2e941),
- PCMCIA_DEVICE_PROD_ID12("SMC", "EZCard-10-PCMCIA", 0xc4f8b18b, 0xfb21d265),
- PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision D", 0xc70a4760, 0x2ade483e),
- PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision E", 0xc70a4760, 0x5dd978a8),
- PCMCIA_DEVICE_PROD_ID12("TDK", "LAK-CD031 for PCMCIA", 0x1eae9475, 0x0ed386fa),
- PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE450T", 0x466b05f0, 0x8b74bc4f),
- PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE550T", 0x466b05f0, 0x33c8db2a),
- PCMCIA_DEVICE_PROD_ID13("Hypertec", "EP401", 0x8787bec7, 0xf6e4a31e),
- PCMCIA_DEVICE_PROD_ID13("KingMax Technology Inc.", "Ethernet Card", 0x932b7189, 0x5e9d92c0),
- PCMCIA_DEVICE_PROD_ID13("LONGSHINE", "EP401", 0xf866b0b0, 0xf6e4a31e),
- PCMCIA_DEVICE_PROD_ID13("Xircom", "CFE-10", 0x2e3ee845, 0x22a49f89),
- PCMCIA_DEVICE_PROD_ID1("CyQ've 10 Base-T LAN CARD", 0x94faf360),
- PCMCIA_DEVICE_PROD_ID1("EP-210 PCMCIA LAN CARD.", 0x8850b4de),
- PCMCIA_DEVICE_PROD_ID1("ETHER-C16", 0x06a8514f),
- PCMCIA_DEVICE_PROD_ID1("NE2000 Compatible", 0x75b8ad5a),
- PCMCIA_DEVICE_PROD_ID2("EN-6200P2", 0xa996d078),
- /* too generic! */
- /* PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100 Ethernet Card", 0x281f1c5d, 0x11b0ffc0), */
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"),
- PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"),
- PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"),
- PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"),
- PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "cis/DP83903.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("Allied Telesis,K.K", "Ethernet LAN Card", 0x2ad62f3c, 0x9fd2f0a2, "cis/LA-PCM.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "cis/PE520.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "cis/NE2K.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "cis/PE-200.cis"),
- PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "cis/tamarack.cis"),
- PCMCIA_DEVICE_PROD_ID12("Ethernet", "CF Size PC Card", 0x00b2e941, 0x43ac239b),
- PCMCIA_DEVICE_PROD_ID123("Fast Ethernet", "CF Size PC Card", "1.0",
- 0xb4be14e3, 0x43ac239b, 0x0877b627),
- PCMCIA_DEVICE_NULL
-};
-MODULE_DEVICE_TABLE(pcmcia, pcnet_ids);
-MODULE_FIRMWARE("cis/PCMLM28.cis");
-MODULE_FIRMWARE("cis/DP83903.cis");
-MODULE_FIRMWARE("cis/LA-PCM.cis");
-MODULE_FIRMWARE("cis/PE520.cis");
-MODULE_FIRMWARE("cis/NE2K.cis");
-MODULE_FIRMWARE("cis/PE-200.cis");
-MODULE_FIRMWARE("cis/tamarack.cis");
-
-static struct pcmcia_driver pcnet_driver = {
- .name = "pcnet_cs",
- .probe = pcnet_probe,
- .remove = pcnet_detach,
- .owner = THIS_MODULE,
- .id_table = pcnet_ids,
- .suspend = pcnet_suspend,
- .resume = pcnet_resume,
-};
-module_pcmcia_driver(pcnet_driver);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 14/15] drivers: net: 8390: ultra: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (12 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 13/15] drivers: net: 8390: pcnet: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 15/15] drivers: net: 8390: wd80x3: " Andrew Lunn
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The ultra was written by Donald Becker 1993 to 1998. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/8390/Kconfig | 18 -
drivers/net/ethernet/8390/Makefile | 1 -
drivers/net/ethernet/8390/smc-ultra.c | 630 ----------------------------------
3 files changed, 649 deletions(-)
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index 3e56806471a3..dd066b0a39d5 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -144,24 +144,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
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index b215136a603b..0afdccddda58 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -12,7 +12,6 @@ obj-$(CONFIG_MCF8390) += mcf8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.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
diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c
deleted file mode 100644
index 22ca804b2e95..000000000000
--- a/drivers/net/ethernet/8390/smc-ultra.c
+++ /dev/null
@@ -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 */
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH net v2 15/15] drivers: net: 8390: wd80x3: Remove this driver
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
` (13 preceding siblings ...)
2026-04-22 18:01 ` [PATCH net v2 14/15] drivers: net: 8390: ultra: " Andrew Lunn
@ 2026-04-22 18:01 ` Andrew Lunn
14 siblings, 0 replies; 25+ messages in thread
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
The wd80x3 was written by Donald Becker 1993 to 1994. It is an ISA
device, so unlikely to be used with modern kernels.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/ethernet/8390/Kconfig | 11 -
drivers/net/ethernet/8390/Makefile | 1 -
drivers/net/ethernet/8390/wd.c | 575 -------------------------------------
3 files changed, 587 deletions(-)
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig
index dd066b0a39d5..d2116d70ab5d 100644
--- a/drivers/net/ethernet/8390/Kconfig
+++ b/drivers/net/ethernet/8390/Kconfig
@@ -144,17 +144,6 @@ config STNIC
If unsure, say N.
-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
diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile
index 0afdccddda58..ad5145c30c85 100644
--- a/drivers/net/ethernet/8390/Makefile
+++ b/drivers/net/ethernet/8390/Makefile
@@ -12,6 +12,5 @@ obj-$(CONFIG_MCF8390) += mcf8390.o
obj-$(CONFIG_NE2000) += ne.o 8390p.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_STNIC) += stnic.o 8390.o
-obj-$(CONFIG_WD80x3) += wd.o 8390.o
obj-$(CONFIG_XSURF100) += xsurf100.o
obj-$(CONFIG_ZORRO8390) += zorro8390.o
diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c
deleted file mode 100644
index ffd639477dfc..000000000000
--- a/drivers/net/ethernet/8390/wd.c
+++ /dev/null
@@ -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 */
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* RE: [PATCH net v2 03/15] drivers: net: 3com: 3c574: Remove this driver
2026-04-22 18:01 ` [PATCH net v2 03/15] drivers: net: 3com: 3c574: " Andrew Lunn
@ 2026-04-23 2:14 ` Wei Fang
2026-04-23 2:21 ` Jakub Kicinski
0 siblings, 1 reply; 25+ messages in thread
From: Wei Fang @ 2026-04-23 2:14 UTC (permalink / raw)
To: Andrew Lunn, Andrew Lunn
Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, linux-doc@vger.kernel.org,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan
> The 3c574 was written by Donald Becker between 19973-1998. It is an
Nit: 19973 --> 1993
> PCMCIA device, so unlikely to be used with modern kernels.
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 03/15] drivers: net: 3com: 3c574: Remove this driver
2026-04-23 2:14 ` Wei Fang
@ 2026-04-23 2:21 ` Jakub Kicinski
0 siblings, 0 replies; 25+ messages in thread
From: Jakub Kicinski @ 2026-04-23 2:21 UTC (permalink / raw)
To: Wei Fang
Cc: Andrew Lunn, Andrew Lunn, Geert Uytterhoeven, Michael Fritscher,
Byron Stanoszek, Daniel Palmer, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, linux-doc@vger.kernel.org,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Jonathan Corbet, Shuah Khan
On Thu, 23 Apr 2026 02:14:22 +0000 Wei Fang wrote:
> > The 3c574 was written by Donald Becker between 19973-1998. It is an
>
> Nit: 19973 --> 1993
Will fix when applying FWIW
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 13/15] drivers: net: 8390: pcnet: Remove this driver
2026-04-22 18:01 ` [PATCH net v2 13/15] drivers: net: 8390: pcnet: " Andrew Lunn
@ 2026-04-23 5:49 ` Dominik Brodowski
2026-04-23 9:09 ` Bjørn Mork
1 sibling, 0 replies; 25+ messages in thread
From: Dominik Brodowski @ 2026-04-23 5:49 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc
On Wed, Apr 22, 2026 at 01:01:56PM -0500, Andrew Lunn wrote:
> The pcnet was written by David A. Hindsh in 1999. It is an PCMCIA
> device, so unlikely to be used with modern kernels.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
> drivers/net/ethernet/8390/Kconfig | 11 -
> drivers/net/ethernet/8390/Makefile | 1 -
> drivers/net/ethernet/8390/pcnet_cs.c | 1717 ----------------------------------
NACK for this one. This is the most common PCMCIA networking device, so
should be removed only once the rest of the PCMCIA subsystem is removed.
For the other removals of PCMCIA device drivers in this series:
Acked-by: Dominik Brodowski <linux@dominikbrodowski.net>
Thanks,
Dominik
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: Remove this driver
2026-04-22 18:01 ` [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: " Andrew Lunn
@ 2026-04-23 7:07 ` Geert Uytterhoeven
2026-04-23 7:10 ` John Paul Adrian Glaubitz
0 siblings, 1 reply; 25+ messages in thread
From: Geert Uytterhoeven @ 2026-04-23 7:07 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Michael Fritscher, Byron Stanoszek, Daniel Palmer, linux-kernel,
netdev, linux-doc, linux-m68k
CC linux-m68k
On Thu, 23 Apr 2026 at 02:29, Andrew Lunn <andrew@lunn.ch> wrote:
> The mac89x0 was written by Russell Nelson in 1996. It is an MAC
It is based on the ISA cs89x0 driver, written by Russell Nelson.
> device, so unlikely to be used with modern kernels.
Macs do run modern kernels.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
> drivers/net/ethernet/cirrus/Kconfig | 10 -
> drivers/net/ethernet/cirrus/Makefile | 1 -
> drivers/net/ethernet/cirrus/cs89x0.h | 461 ---------------------------
> drivers/net/ethernet/cirrus/mac89x0.c | 577 ----------------------------------
> 4 files changed, 1049 deletions(-)
>
> diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
> index 1a0c7b3bfcd6..786d379e79fe 100644
> --- a/drivers/net/ethernet/cirrus/Kconfig
> +++ b/drivers/net/ethernet/cirrus/Kconfig
> @@ -25,14 +25,4 @@ config EP93XX_ETH
> This is a driver for the ethernet hardware included in EP93xx CPUs.
> Say Y if you are building a kernel for EP93xx based devices.
>
> -config MAC89x0
> - tristate "Macintosh CS89x0 based ethernet cards"
> - depends on MAC
> - help
> - Support for CS89x0 chipset based Ethernet cards. If you have a
> - Nubus or LC-PDS network (Ethernet) card of this type, say Y here.
> -
> - To compile this driver as a module, choose M here. This module will
> - be called mac89x0.
> -
> endif # NET_VENDOR_CIRRUS
> diff --git a/drivers/net/ethernet/cirrus/Makefile b/drivers/net/ethernet/cirrus/Makefile
> index cb740939d976..03800af0f0e1 100644
> --- a/drivers/net/ethernet/cirrus/Makefile
> +++ b/drivers/net/ethernet/cirrus/Makefile
> @@ -4,4 +4,3 @@
> #
>
> obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
> -obj-$(CONFIG_MAC89x0) += mac89x0.o
> diff --git a/drivers/net/ethernet/cirrus/cs89x0.h b/drivers/net/ethernet/cirrus/cs89x0.h
> deleted file mode 100644
> index 210f9ec9af4b..000000000000
> --- a/drivers/net/ethernet/cirrus/cs89x0.h
> +++ /dev/null
> @@ -1,461 +0,0 @@
> -/* Copyright, 1988-1992, Russell Nelson, Crynwr Software
> -
> - This program is free software; you can redistribute it and/or modify
> - it under the terms of the GNU General Public License as published by
> - the Free Software Foundation, version 1.
> -
> - This program is distributed in the hope that it will be useful,
> - but WITHOUT ANY WARRANTY; without even the implied warranty of
> - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - GNU General Public License for more details.
> -
> - You should have received a copy of the GNU General Public License
> - along with this program; if not, write to the Free Software
> - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> - */
> -
> -
> -#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */
> - /* offset 2h -> Model/Product Number */
> - /* offset 3h -> Chip Revision Number */
> -
> -#define PP_ISAIOB 0x0020 /* IO base address */
> -#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */
> -#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */
> -#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */
> -#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */
> -#define PP_ISASOF 0x0026 /* ISA DMA offset */
> -#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */
> -#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */
> -#define PP_CS8900_ISAMemB 0x002C /* Memory base */
> -#define PP_CS8920_ISAMemB 0x0348 /* */
> -
> -#define PP_ISABootBase 0x0030 /* Boot Prom base */
> -#define PP_ISABootMask 0x0034 /* Boot Prom Mask */
> -
> -/* EEPROM data and command registers */
> -#define PP_EECMD 0x0040 /* NVR Interface Command register */
> -#define PP_EEData 0x0042 /* NVR Interface Data Register */
> -#define PP_DebugReg 0x0044 /* Debug Register */
> -
> -#define PP_RxCFG 0x0102 /* Rx Bus config */
> -#define PP_RxCTL 0x0104 /* Receive Control Register */
> -#define PP_TxCFG 0x0106 /* Transmit Config Register */
> -#define PP_TxCMD 0x0108 /* Transmit Command Register */
> -#define PP_BufCFG 0x010A /* Bus configuration Register */
> -#define PP_LineCTL 0x0112 /* Line Config Register */
> -#define PP_SelfCTL 0x0114 /* Self Command Register */
> -#define PP_BusCTL 0x0116 /* ISA bus control Register */
> -#define PP_TestCTL 0x0118 /* Test Register */
> -#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */
> -
> -#define PP_ISQ 0x0120 /* Interrupt Status */
> -#define PP_RxEvent 0x0124 /* Rx Event Register */
> -#define PP_TxEvent 0x0128 /* Tx Event Register */
> -#define PP_BufEvent 0x012C /* Bus Event Register */
> -#define PP_RxMiss 0x0130 /* Receive Miss Count */
> -#define PP_TxCol 0x0132 /* Transmit Collision Count */
> -#define PP_LineST 0x0134 /* Line State Register */
> -#define PP_SelfST 0x0136 /* Self State register */
> -#define PP_BusST 0x0138 /* Bus Status */
> -#define PP_TDR 0x013C /* Time Domain Reflectometry */
> -#define PP_AutoNegST 0x013E /* Auto Neg Status */
> -#define PP_TxCommand 0x0144 /* Tx Command */
> -#define PP_TxLength 0x0146 /* Tx Length */
> -#define PP_LAF 0x0150 /* Hash Table */
> -#define PP_IA 0x0158 /* Physical Address Register */
> -
> -#define PP_RxStatus 0x0400 /* Receive start of frame */
> -#define PP_RxLength 0x0402 /* Receive Length of frame */
> -#define PP_RxFrame 0x0404 /* Receive frame pointer */
> -#define PP_TxFrame 0x0A00 /* Transmit frame pointer */
> -
> -/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */
> -/* can be used as the default I/O base to access the PacketPage Area. */
> -#define DEFAULTIOBASE 0x0300
> -#define FIRST_IO 0x020C /* First I/O port to check */
> -#define LAST_IO 0x037C /* Last I/O port to check (+10h) */
> -#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */
> -#define ADD_SIG 0x3000 /* Expected ID signature */
> -
> -/* On Macs, we only need use the ISA I/O stuff until we do MEMORY_ON */
> -#ifdef CONFIG_MAC
> -#define LCSLOTBASE 0xfee00000
> -#define MMIOBASE 0x40000
> -#endif
> -
> -#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */
> -#define CHIP_EISA_ID_SIG_STR "0x630E"
> -
> -#ifdef IBMEIPKT
> -#define EISA_ID_SIG 0x4D24 /* IBM */
> -#define PART_NO_SIG 0x1010 /* IBM */
> -#define MONGOOSE_BIT 0x0000 /* IBM */
> -#else
> -#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */
> -#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */
> -#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */
> -#endif
> -
> -#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */
> -
> -/* Mask to find out the types of registers */
> -#define REG_TYPE_MASK 0x001F
> -
> -/* Eeprom Commands */
> -#define ERSE_WR_ENBL 0x00F0
> -#define ERSE_WR_DISABLE 0x0000
> -
> -/* Defines Control/Config register quintuplet numbers */
> -#define RX_BUF_CFG 0x0003
> -#define RX_CONTROL 0x0005
> -#define TX_CFG 0x0007
> -#define TX_COMMAND 0x0009
> -#define BUF_CFG 0x000B
> -#define LINE_CONTROL 0x0013
> -#define SELF_CONTROL 0x0015
> -#define BUS_CONTROL 0x0017
> -#define TEST_CONTROL 0x0019
> -
> -/* Defines Status/Count registers quintuplet numbers */
> -#define RX_EVENT 0x0004
> -#define TX_EVENT 0x0008
> -#define BUF_EVENT 0x000C
> -#define RX_MISS_COUNT 0x0010
> -#define TX_COL_COUNT 0x0012
> -#define LINE_STATUS 0x0014
> -#define SELF_STATUS 0x0016
> -#define BUS_STATUS 0x0018
> -#define TDR 0x001C
> -
> -/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */
> -#define SKIP_1 0x0040
> -#define RX_STREAM_ENBL 0x0080
> -#define RX_OK_ENBL 0x0100
> -#define RX_DMA_ONLY 0x0200
> -#define AUTO_RX_DMA 0x0400
> -#define BUFFER_CRC 0x0800
> -#define RX_CRC_ERROR_ENBL 0x1000
> -#define RX_RUNT_ENBL 0x2000
> -#define RX_EXTRA_DATA_ENBL 0x4000
> -
> -/* PP_RxCTL - Receive Control bit definition - Read/write */
> -#define RX_IA_HASH_ACCEPT 0x0040
> -#define RX_PROM_ACCEPT 0x0080
> -#define RX_OK_ACCEPT 0x0100
> -#define RX_MULTCAST_ACCEPT 0x0200
> -#define RX_IA_ACCEPT 0x0400
> -#define RX_BROADCAST_ACCEPT 0x0800
> -#define RX_BAD_CRC_ACCEPT 0x1000
> -#define RX_RUNT_ACCEPT 0x2000
> -#define RX_EXTRA_DATA_ACCEPT 0x4000
> -#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT)
> -/* Default receive mode - individually addressed, broadcast, and error free */
> -#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT)
> -
> -/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */
> -#define TX_LOST_CRS_ENBL 0x0040
> -#define TX_SQE_ERROR_ENBL 0x0080
> -#define TX_OK_ENBL 0x0100
> -#define TX_LATE_COL_ENBL 0x0200
> -#define TX_JBR_ENBL 0x0400
> -#define TX_ANY_COL_ENBL 0x0800
> -#define TX_16_COL_ENBL 0x8000
> -
> -/* PP_TxCMD - Transmit Command bit definition - Read-only */
> -#define TX_START_4_BYTES 0x0000
> -#define TX_START_64_BYTES 0x0040
> -#define TX_START_128_BYTES 0x0080
> -#define TX_START_ALL_BYTES 0x00C0
> -#define TX_FORCE 0x0100
> -#define TX_ONE_COL 0x0200
> -#define TX_TWO_PART_DEFF_DISABLE 0x0400
> -#define TX_NO_CRC 0x1000
> -#define TX_RUNT 0x2000
> -
> -/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */
> -#define GENERATE_SW_INTERRUPT 0x0040
> -#define RX_DMA_ENBL 0x0080
> -#define READY_FOR_TX_ENBL 0x0100
> -#define TX_UNDERRUN_ENBL 0x0200
> -#define RX_MISS_ENBL 0x0400
> -#define RX_128_BYTE_ENBL 0x0800
> -#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
> -#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
> -#define RX_DEST_MATCH_ENBL 0x8000
> -
> -/* PP_LineCTL - Line Control bit definition - Read/write */
> -#define SERIAL_RX_ON 0x0040
> -#define SERIAL_TX_ON 0x0080
> -#define AUI_ONLY 0x0100
> -#define AUTO_AUI_10BASET 0x0200
> -#define MODIFIED_BACKOFF 0x0800
> -#define NO_AUTO_POLARITY 0x1000
> -#define TWO_PART_DEFDIS 0x2000
> -#define LOW_RX_SQUELCH 0x4000
> -
> -/* PP_SelfCTL - Software Self Control bit definition - Read/write */
> -#define POWER_ON_RESET 0x0040
> -#define SW_STOP 0x0100
> -#define SLEEP_ON 0x0200
> -#define AUTO_WAKEUP 0x0400
> -#define HCB0_ENBL 0x1000
> -#define HCB1_ENBL 0x2000
> -#define HCB0 0x4000
> -#define HCB1 0x8000
> -
> -/* PP_BusCTL - ISA Bus Control bit definition - Read/write */
> -#define RESET_RX_DMA 0x0040
> -#define MEMORY_ON 0x0400
> -#define DMA_BURST_MODE 0x0800
> -#define IO_CHANNEL_READY_ON 0x1000
> -#define RX_DMA_SIZE_64K 0x2000
> -#define ENABLE_IRQ 0x8000
> -
> -/* PP_TestCTL - Test Control bit definition - Read/write */
> -#define LINK_OFF 0x0080
> -#define ENDEC_LOOPBACK 0x0200
> -#define AUI_LOOPBACK 0x0400
> -#define BACKOFF_OFF 0x0800
> -#define FDX_8900 0x4000
> -#define FAST_TEST 0x8000
> -
> -/* PP_RxEvent - Receive Event Bit definition - Read-only */
> -#define RX_IA_HASHED 0x0040
> -#define RX_DRIBBLE 0x0080
> -#define RX_OK 0x0100
> -#define RX_HASHED 0x0200
> -#define RX_IA 0x0400
> -#define RX_BROADCAST 0x0800
> -#define RX_CRC_ERROR 0x1000
> -#define RX_RUNT 0x2000
> -#define RX_EXTRA_DATA 0x4000
> -
> -#define HASH_INDEX_MASK 0x0FC00
> -
> -/* PP_TxEvent - Transmit Event Bit definition - Read-only */
> -#define TX_LOST_CRS 0x0040
> -#define TX_SQE_ERROR 0x0080
> -#define TX_OK 0x0100
> -#define TX_LATE_COL 0x0200
> -#define TX_JBR 0x0400
> -#define TX_16_COL 0x8000
> -#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS)
> -#define TX_COL_COUNT_MASK 0x7800
> -
> -/* PP_BufEvent - Buffer Event Bit definition - Read-only */
> -#define SW_INTERRUPT 0x0040
> -#define RX_DMA 0x0080
> -#define READY_FOR_TX 0x0100
> -#define TX_UNDERRUN 0x0200
> -#define RX_MISS 0x0400
> -#define RX_128_BYTE 0x0800
> -#define TX_COL_OVRFLW 0x1000
> -#define RX_MISS_OVRFLW 0x2000
> -#define RX_DEST_MATCH 0x8000
> -
> -/* PP_LineST - Ethernet Line Status bit definition - Read-only */
> -#define LINK_OK 0x0080
> -#define AUI_ON 0x0100
> -#define TENBASET_ON 0x0200
> -#define POLARITY_OK 0x1000
> -#define CRS_OK 0x4000
> -
> -/* PP_SelfST - Chip Software Status bit definition */
> -#define ACTIVE_33V 0x0040
> -#define INIT_DONE 0x0080
> -#define SI_BUSY 0x0100
> -#define EEPROM_PRESENT 0x0200
> -#define EEPROM_OK 0x0400
> -#define EL_PRESENT 0x0800
> -#define EE_SIZE_64 0x1000
> -
> -/* PP_BusST - ISA Bus Status bit definition */
> -#define TX_BID_ERROR 0x0080
> -#define READY_FOR_TX_NOW 0x0100
> -
> -/* PP_AutoNegCTL - Auto Negotiation Control bit definition */
> -#define RE_NEG_NOW 0x0040
> -#define ALLOW_FDX 0x0080
> -#define AUTO_NEG_ENABLE 0x0100
> -#define NLP_ENABLE 0x0200
> -#define FORCE_FDX 0x8000
> -#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE)
> -#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW)
> -
> -/* PP_AutoNegST - Auto Negotiation Status bit definition */
> -#define AUTO_NEG_BUSY 0x0080
> -#define FLP_LINK 0x0100
> -#define FLP_LINK_GOOD 0x0800
> -#define LINK_FAULT 0x1000
> -#define HDX_ACTIVE 0x4000
> -#define FDX_ACTIVE 0x8000
> -
> -/* The following block defines the ISQ event types */
> -#define ISQ_RECEIVER_EVENT 0x04
> -#define ISQ_TRANSMITTER_EVENT 0x08
> -#define ISQ_BUFFER_EVENT 0x0c
> -#define ISQ_RX_MISS_EVENT 0x10
> -#define ISQ_TX_COL_EVENT 0x12
> -
> -#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */
> -#define ISQ_HIST 16 /* small history buffer */
> -#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */
> -
> -#define TXRXBUFSIZE 0x0600
> -#define RXDMABUFSIZE 0x8000
> -#define RXDMASIZE 0x4000
> -#define TXRX_LENGTH_MASK 0x07FF
> -
> -/* rx options bits */
> -#define RCV_WITH_RXON 1 /* Set SerRx ON */
> -#define RCV_COUNTS 2 /* Use Framecnt1 */
> -#define RCV_PONG 4 /* Pong respondent */
> -#define RCV_DONG 8 /* Dong operation */
> -#define RCV_POLLING 0x10 /* Poll RxEvent */
> -#define RCV_ISQ 0x20 /* Use ISQ, int */
> -#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */
> -#define RCV_DMA 0x200 /* Set RxDMA only */
> -#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */
> -#define RCV_FIXED_DATA 0x800 /* Every frame same */
> -#define RCV_IO 0x1000 /* Use ISA IO only */
> -#define RCV_MEMORY 0x2000 /* Use ISA Memory */
> -
> -#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */
> -#define PKT_START PP_TxFrame /* Start of packet RAM */
> -
> -#define RX_FRAME_PORT 0x0000
> -#define TX_FRAME_PORT RX_FRAME_PORT
> -#define TX_CMD_PORT 0x0004
> -#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */
> -#define TX_AFTER_381 0x0040 /* Tx packet after 381 bytes copied */
> -#define TX_AFTER_ALL 0x00c0 /* Tx packet after all bytes copied */
> -#define TX_LEN_PORT 0x0006
> -#define ISQ_PORT 0x0008
> -#define ADD_PORT 0x000A
> -#define DATA_PORT 0x000C
> -
> -#define EEPROM_WRITE_EN 0x00F0
> -#define EEPROM_WRITE_DIS 0x0000
> -#define EEPROM_WRITE_CMD 0x0100
> -#define EEPROM_READ_CMD 0x0200
> -
> -/* Receive Header */
> -/* Description of header of each packet in receive area of memory */
> -#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */
> -#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */
> -#define RBUF_LEN_LOW 2 /* Length of received data - low byte */
> -#define RBUF_LEN_HI 3 /* Length of received data - high byte */
> -#define RBUF_HEAD_LEN 4 /* Length of this header */
> -
> -#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */
> -#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */
> -
> -/* for bios scan */
> -/* */
> -#ifdef CSDEBUG
> -/* use these values for debugging bios scan */
> -#define BIOS_START_SEG 0x00000
> -#define BIOS_OFFSET_INC 0x0010
> -#else
> -#define BIOS_START_SEG 0x0c000
> -#define BIOS_OFFSET_INC 0x0200
> -#endif
> -
> -#define BIOS_LAST_OFFSET 0x0fc00
> -
> -/* Byte offsets into the EEPROM configuration buffer */
> -#define ISA_CNF_OFFSET 0x6
> -#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */
> -#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */
> -
> - /* the assumption here is that the bits in the eeprom are generally */
> - /* in the same position as those in the autonegctl register. */
> - /* Of course the IMM bit is not in that register so it must be */
> - /* masked out */
> -#define EE_FORCE_FDX 0x8000
> -#define EE_NLP_ENABLE 0x0200
> -#define EE_AUTO_NEG_ENABLE 0x0100
> -#define EE_ALLOW_FDX 0x0080
> -#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX)
> -
> -#define IMM_BIT 0x0040 /* ignore missing media */
> -
> -#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2)
> -#define A_CNF_10B_T 0x0001
> -#define A_CNF_AUI 0x0002
> -#define A_CNF_10B_2 0x0004
> -#define A_CNF_MEDIA_TYPE 0x0070
> -#define A_CNF_MEDIA_AUTO 0x0070
> -#define A_CNF_MEDIA_10B_T 0x0020
> -#define A_CNF_MEDIA_AUI 0x0040
> -#define A_CNF_MEDIA_10B_2 0x0010
> -#define A_CNF_DC_DC_POLARITY 0x0080
> -#define A_CNF_NO_AUTO_POLARITY 0x2000
> -#define A_CNF_LOW_RX_SQUELCH 0x4000
> -#define A_CNF_EXTND_10B_2 0x8000
> -
> -#define PACKET_PAGE_OFFSET 0x8
> -
> -/* Bit definitions for the ISA configuration word from the EEPROM */
> -#define INT_NO_MASK 0x000F
> -#define DMA_NO_MASK 0x0070
> -#define ISA_DMA_SIZE 0x0200
> -#define ISA_AUTO_RxDMA 0x0400
> -#define ISA_RxDMA 0x0800
> -#define DMA_BURST 0x1000
> -#define STREAM_TRANSFER 0x2000
> -#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA)
> -
> -/* DMA controller registers */
> -#define DMA_BASE 0x00 /* DMA controller base */
> -#define DMA_BASE_2 0x0C0 /* DMA controller base */
> -
> -#define DMA_STAT 0x0D0 /* DMA controller status register */
> -#define DMA_MASK 0x0D4 /* DMA controller mask register */
> -#define DMA_MODE 0x0D6 /* DMA controller mode register */
> -#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */
> -
> -/* DMA data */
> -#define DMA_DISABLE 0x04 /* Disable channel n */
> -#define DMA_ENABLE 0x00 /* Enable channel n */
> -/* Demand transfers, incr. address, auto init, writes, ch. n */
> -#define DMA_RX_MODE 0x14
> -/* Demand transfers, incr. address, auto init, reads, ch. n */
> -#define DMA_TX_MODE 0x18
> -
> -#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */
> -
> -#define CS8900 0x0000
> -#define CS8920 0x4000
> -#define CS8920M 0x6000
> -#define REVISON_BITS 0x1F00
> -#define EEVER_NUMBER 0x12
> -#define CHKSUM_LEN 0x14
> -#define CHKSUM_VAL 0x0000
> -#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */
> -#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */
> -#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */
> -#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */
> -#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */
> -
> -#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */
> -
> -#define PNP_ADD_PORT 0x0279
> -#define PNP_WRITE_PORT 0x0A79
> -
> -#define GET_PNP_ISA_STRUCT 0x40
> -#define PNP_ISA_STRUCT_LEN 0x06
> -#define PNP_CSN_CNT_OFF 0x01
> -#define PNP_RD_PORT_OFF 0x02
> -#define PNP_FUNCTION_OK 0x00
> -#define PNP_WAKE 0x03
> -#define PNP_RSRC_DATA 0x04
> -#define PNP_RSRC_READY 0x01
> -#define PNP_STATUS 0x05
> -#define PNP_ACTIVATE 0x30
> -#define PNP_CNF_IO_H 0x60
> -#define PNP_CNF_IO_L 0x61
> -#define PNP_CNF_INT 0x70
> -#define PNP_CNF_DMA 0x74
> -#define PNP_CNF_MEM 0x48
> diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
> deleted file mode 100644
> index 6723df9b65d9..000000000000
> --- a/drivers/net/ethernet/cirrus/mac89x0.c
> +++ /dev/null
> @@ -1,577 +0,0 @@
> -/* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
> -/*
> - Written 1996 by Russell Nelson, with reference to skeleton.c
> - written 1993-1994 by Donald Becker.
> -
> - This software may be used and distributed according to the terms
> - of the GNU General Public License, incorporated herein by reference.
> -
> - The author may be reached at nelson@crynwr.com, Crynwr
> - Software, 11 Grant St., Potsdam, NY 13676
> -
> - Changelog:
> -
> - Mike Cruse : mcruse@cti-ltd.com
> - : Changes for Linux 2.0 compatibility.
> - : Added dev_id parameter in net_interrupt(),
> - : request_irq() and free_irq(). Just NULL for now.
> -
> - Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
> - : in net_open() and net_close() so kerneld would know
> - : that the module is in use and wouldn't eject the
> - : driver prematurely.
> -
> - Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c
> - : as an example. Disabled autoprobing in init_module(),
> - : not a good thing to do to other devices while Linux
> - : is running from all accounts.
> -
> - Alan Cox : Removed 1.2 support, added 2.1 extra counters.
> -
> - David Huggins-Daines <dhd@debian.org>
> -
> - Split this off into mac89x0.c, and gutted it of all parts which are
> - not relevant to the existing CS8900 cards on the Macintosh
> - (i.e. basically the Daynaport CS and LC cards). To be precise:
> -
> - * Removed all the media-detection stuff, because these cards are
> - TP-only.
> -
> - * Lobotomized the ISA interrupt bogosity, because these cards use
> - a hardwired NuBus interrupt and a magic ISAIRQ value in the card.
> -
> - * Basically eliminated everything not relevant to getting the
> - cards minimally functioning on the Macintosh.
> -
> - I might add that these cards are badly designed even from the Mac
> - standpoint, in that Dayna, in their infinite wisdom, used NuBus slot
> - I/O space and NuBus interrupts for these cards, but neglected to
> - provide anything even remotely resembling a NuBus ROM. Therefore we
> - have to probe for them in a brain-damaged ISA-like fashion.
> -
> - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
> - check kmalloc and release the allocated memory on failure in
> - mac89x0_probe and in init_module
> - use local_irq_{save,restore}(flags) in net_get_stat, not just
> - local_irq_{dis,en}able()
> -*/
> -
> -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> -
> -static const char version[] =
> -"cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
> -
> -#include <linux/module.h>
> -
> -/*
> - Sources:
> -
> - Crynwr packet driver epktisa.
> -
> - Crystal Semiconductor data sheets.
> -
> -*/
> -
> -#include <linux/kernel.h>
> -#include <linux/types.h>
> -#include <linux/fcntl.h>
> -#include <linux/interrupt.h>
> -#include <linux/ioport.h>
> -#include <linux/in.h>
> -#include <linux/string.h>
> -#include <linux/nubus.h>
> -#include <linux/errno.h>
> -#include <linux/init.h>
> -#include <linux/netdevice.h>
> -#include <linux/platform_device.h>
> -#include <linux/etherdevice.h>
> -#include <linux/skbuff.h>
> -#include <linux/delay.h>
> -#include <linux/bitops.h>
> -#include <linux/gfp.h>
> -
> -#include <asm/io.h>
> -#include <asm/hwtest.h>
> -#include <asm/macints.h>
> -
> -#include "cs89x0.h"
> -
> -static int debug = -1;
> -module_param(debug, int, 0);
> -MODULE_PARM_DESC(debug, "debug message level");
> -
> -/* Information that need to be kept for each board. */
> -struct net_local {
> - int msg_enable;
> - int chip_type; /* one of: CS8900, CS8920, CS8920M */
> - char chip_revision; /* revision letter of the chip ('A'...) */
> - int send_cmd; /* the propercommand used to send a packet. */
> - int rx_mode;
> - int curr_rx_cfg;
> - int send_underrun; /* keep track of how many underruns in a row we get */
> -};
> -
> -/* Index to functions, as function prototypes. */
> -static int net_open(struct net_device *dev);
> -static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
> -static irqreturn_t net_interrupt(int irq, void *dev_id);
> -static void set_multicast_list(struct net_device *dev);
> -static void net_rx(struct net_device *dev);
> -static int net_close(struct net_device *dev);
> -static struct net_device_stats *net_get_stats(struct net_device *dev);
> -static int set_mac_address(struct net_device *dev, void *addr);
> -
> -/* For reading/writing registers ISA-style */
> -static inline int
> -readreg_io(struct net_device *dev, int portno)
> -{
> - nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
> - return swab16(nubus_readw(dev->base_addr + DATA_PORT));
> -}
> -
> -static inline void
> -writereg_io(struct net_device *dev, int portno, int value)
> -{
> - nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
> - nubus_writew(swab16(value), dev->base_addr + DATA_PORT);
> -}
> -
> -/* These are for reading/writing registers in shared memory */
> -static inline int
> -readreg(struct net_device *dev, int portno)
> -{
> - return swab16(nubus_readw(dev->mem_start + portno));
> -}
> -
> -static inline void
> -writereg(struct net_device *dev, int portno, int value)
> -{
> - nubus_writew(swab16(value), dev->mem_start + portno);
> -}
> -
> -static const struct net_device_ops mac89x0_netdev_ops = {
> - .ndo_open = net_open,
> - .ndo_stop = net_close,
> - .ndo_start_xmit = net_send_packet,
> - .ndo_get_stats = net_get_stats,
> - .ndo_set_rx_mode = set_multicast_list,
> - .ndo_set_mac_address = set_mac_address,
> - .ndo_validate_addr = eth_validate_addr,
> -};
> -
> -/* Probe for the CS8900 card in slot E. We won't bother looking
> - anywhere else until we have a really good reason to do so. */
> -static int mac89x0_device_probe(struct platform_device *pdev)
> -{
> - struct net_device *dev;
> - struct net_local *lp;
> - int i, slot;
> - unsigned rev_type = 0;
> - unsigned long ioaddr;
> - unsigned short sig;
> - int err = -ENODEV;
> - struct nubus_rsrc *fres;
> -
> - dev = alloc_etherdev(sizeof(struct net_local));
> - if (!dev)
> - return -ENOMEM;
> -
> - /* We might have to parameterize this later */
> - slot = 0xE;
> - /* Get out now if there's a real NuBus card in slot E */
> - for_each_func_rsrc(fres)
> - if (fres->board->slot == slot)
> - goto out;
> -
> - /* The pseudo-ISA bits always live at offset 0x300 (gee,
> - wonder why...) */
> - ioaddr = (unsigned long)
> - nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE);
> - {
> - int card_present;
> -
> - card_present = (hwreg_present((void *)ioaddr + 4) &&
> - hwreg_present((void *)ioaddr + DATA_PORT));
> - if (!card_present)
> - goto out;
> - }
> -
> - nubus_writew(0, ioaddr + ADD_PORT);
> - sig = nubus_readw(ioaddr + DATA_PORT);
> - if (sig != swab16(CHIP_EISA_ID_SIG))
> - goto out;
> -
> - SET_NETDEV_DEV(dev, &pdev->dev);
> -
> - /* Initialize the net_device structure. */
> - lp = netdev_priv(dev);
> -
> - lp->msg_enable = netif_msg_init(debug, 0);
> -
> - /* Fill in the 'dev' fields. */
> - dev->base_addr = ioaddr;
> - dev->mem_start = (unsigned long)
> - nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE);
> - dev->mem_end = dev->mem_start + 0x1000;
> -
> - /* Turn on shared memory */
> - writereg_io(dev, PP_BusCTL, MEMORY_ON);
> -
> - /* get the chip type */
> - rev_type = readreg(dev, PRODUCT_ID_ADD);
> - lp->chip_type = rev_type &~ REVISON_BITS;
> - lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
> -
> - /* Check the chip type and revision in order to set the correct send command
> - CS8920 revision C and CS8900 revision F can use the faster send. */
> - lp->send_cmd = TX_AFTER_381;
> - if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
> - lp->send_cmd = TX_NOW;
> - if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
> - lp->send_cmd = TX_NOW;
> -
> - netif_dbg(lp, drv, dev, "%s", version);
> -
> - pr_info("cs89%c0%s rev %c found at %#8lx\n",
> - lp->chip_type == CS8900 ? '0' : '2',
> - lp->chip_type == CS8920M ? "M" : "",
> - lp->chip_revision, dev->base_addr);
> -
> - /* Try to read the MAC address */
> - if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
> - pr_info("No EEPROM, giving up now.\n");
> - goto out1;
> - } else {
> - u8 addr[ETH_ALEN];
> -
> - for (i = 0; i < ETH_ALEN; i += 2) {
> - /* Big-endian (why??!) */
> - unsigned short s = readreg(dev, PP_IA + i);
> - addr[i] = s >> 8;
> - addr[i+1] = s & 0xff;
> - }
> - eth_hw_addr_set(dev, addr);
> - }
> -
> - dev->irq = SLOT2IRQ(slot);
> -
> - /* print the IRQ and ethernet address. */
> -
> - pr_info("MAC %pM, IRQ %d\n", dev->dev_addr, dev->irq);
> -
> - dev->netdev_ops = &mac89x0_netdev_ops;
> -
> - err = register_netdev(dev);
> - if (err)
> - goto out1;
> -
> - platform_set_drvdata(pdev, dev);
> - return 0;
> -out1:
> - nubus_writew(0, dev->base_addr + ADD_PORT);
> -out:
> - free_netdev(dev);
> - return err;
> -}
> -
> -/* Open/initialize the board. This is called (in the current kernel)
> - sometime after booting when the 'ifconfig' program is run.
> -
> - This routine should set everything up anew at each open, even
> - registers that "should" only need to be set once at boot, so that
> - there is non-reboot way to recover if something goes wrong.
> - */
> -static int
> -net_open(struct net_device *dev)
> -{
> - struct net_local *lp = netdev_priv(dev);
> - int i;
> -
> - /* Disable the interrupt for now */
> - writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
> -
> - /* Grab the interrupt */
> - if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev))
> - return -EAGAIN;
> -
> - /* Set up the IRQ - Apparently magic */
> - if (lp->chip_type == CS8900)
> - writereg(dev, PP_CS8900_ISAINT, 0);
> - else
> - writereg(dev, PP_CS8920_ISAINT, 0);
> -
> - /* set the Ethernet address */
> - for (i=0; i < ETH_ALEN/2; i++)
> - writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
> -
> - /* Turn on both receive and transmit operations */
> - writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
> -
> - /* Receive only error free packets addressed to this card */
> - lp->rx_mode = 0;
> - writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
> -
> - lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
> -
> - writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
> -
> - writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
> - TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
> -
> - writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
> - TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
> -
> - /* now that we've got our act together, enable everything */
> - writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
> - netif_start_queue(dev);
> - return 0;
> -}
> -
> -static netdev_tx_t
> -net_send_packet(struct sk_buff *skb, struct net_device *dev)
> -{
> - struct net_local *lp = netdev_priv(dev);
> - unsigned long flags;
> -
> - netif_dbg(lp, tx_queued, dev, "sent %d byte packet of type %x\n",
> - skb->len, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
> - skb->data[ETH_ALEN + ETH_ALEN + 1]);
> -
> - /* keep the upload from being interrupted, since we
> - ask the chip to start transmitting before the
> - whole packet has been completely uploaded. */
> - local_irq_save(flags);
> - netif_stop_queue(dev);
> -
> - /* initiate a transmit sequence */
> - writereg(dev, PP_TxCMD, lp->send_cmd);
> - writereg(dev, PP_TxLength, skb->len);
> -
> - /* Test to see if the chip has allocated memory for the packet */
> - if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
> - /* Gasp! It hasn't. But that shouldn't happen since
> - we're waiting for TxOk, so return 1 and requeue this packet. */
> - local_irq_restore(flags);
> - return NETDEV_TX_BUSY;
> - }
> -
> - /* Write the contents of the packet */
> - skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame),
> - skb->len+1);
> -
> - local_irq_restore(flags);
> - dev_kfree_skb (skb);
> -
> - return NETDEV_TX_OK;
> -}
> -
> -/* The typical workload of the driver:
> - Handle the network interface interrupts. */
> -static irqreturn_t net_interrupt(int irq, void *dev_id)
> -{
> - struct net_device *dev = dev_id;
> - struct net_local *lp;
> - int ioaddr, status;
> -
> - ioaddr = dev->base_addr;
> - lp = netdev_priv(dev);
> -
> - /* we MUST read all the events out of the ISQ, otherwise we'll never
> - get interrupted again. As a consequence, we can't have any limit
> - on the number of times we loop in the interrupt handler. The
> - hardware guarantees that eventually we'll run out of events. Of
> - course, if you're on a slow machine, and packets are arriving
> - faster than you can read them off, you're screwed. Hasta la
> - vista, baby! */
> - while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) {
> - netif_dbg(lp, intr, dev, "status=%04x\n", status);
> - switch(status & ISQ_EVENT_MASK) {
> - case ISQ_RECEIVER_EVENT:
> - /* Got a packet(s). */
> - net_rx(dev);
> - break;
> - case ISQ_TRANSMITTER_EVENT:
> - dev->stats.tx_packets++;
> - netif_wake_queue(dev);
> - if ((status & TX_OK) == 0)
> - dev->stats.tx_errors++;
> - if (status & TX_LOST_CRS)
> - dev->stats.tx_carrier_errors++;
> - if (status & TX_SQE_ERROR)
> - dev->stats.tx_heartbeat_errors++;
> - if (status & TX_LATE_COL)
> - dev->stats.tx_window_errors++;
> - if (status & TX_16_COL)
> - dev->stats.tx_aborted_errors++;
> - break;
> - case ISQ_BUFFER_EVENT:
> - if (status & READY_FOR_TX) {
> - /* we tried to transmit a packet earlier,
> - but inexplicably ran out of buffers.
> - That shouldn't happen since we only ever
> - load one packet. Shrug. Do the right
> - thing anyway. */
> - netif_wake_queue(dev);
> - }
> - if (status & TX_UNDERRUN) {
> - netif_dbg(lp, tx_err, dev, "transmit underrun\n");
> - lp->send_underrun++;
> - if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
> - else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
> - }
> - break;
> - case ISQ_RX_MISS_EVENT:
> - dev->stats.rx_missed_errors += (status >> 6);
> - break;
> - case ISQ_TX_COL_EVENT:
> - dev->stats.collisions += (status >> 6);
> - break;
> - }
> - }
> - return IRQ_HANDLED;
> -}
> -
> -/* We have a good packet(s), get it/them out of the buffers. */
> -static void
> -net_rx(struct net_device *dev)
> -{
> - struct net_local *lp = netdev_priv(dev);
> - struct sk_buff *skb;
> - int status, length;
> -
> - status = readreg(dev, PP_RxStatus);
> - if ((status & RX_OK) == 0) {
> - dev->stats.rx_errors++;
> - if (status & RX_RUNT)
> - dev->stats.rx_length_errors++;
> - if (status & RX_EXTRA_DATA)
> - dev->stats.rx_length_errors++;
> - if ((status & RX_CRC_ERROR) &&
> - !(status & (RX_EXTRA_DATA|RX_RUNT)))
> - /* per str 172 */
> - dev->stats.rx_crc_errors++;
> - if (status & RX_DRIBBLE)
> - dev->stats.rx_frame_errors++;
> - return;
> - }
> -
> - length = readreg(dev, PP_RxLength);
> - /* Malloc up new buffer. */
> - skb = alloc_skb(length, GFP_ATOMIC);
> - if (skb == NULL) {
> - dev->stats.rx_dropped++;
> - return;
> - }
> - skb_put(skb, length);
> -
> - skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame),
> - length);
> -
> - netif_dbg(lp, rx_status, dev, "received %d byte packet of type %x\n",
> - length, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
> - skb->data[ETH_ALEN + ETH_ALEN + 1]);
> -
> - skb->protocol=eth_type_trans(skb,dev);
> - netif_rx(skb);
> - dev->stats.rx_packets++;
> - dev->stats.rx_bytes += length;
> -}
> -
> -/* The inverse routine to net_open(). */
> -static int
> -net_close(struct net_device *dev)
> -{
> -
> - writereg(dev, PP_RxCFG, 0);
> - writereg(dev, PP_TxCFG, 0);
> - writereg(dev, PP_BufCFG, 0);
> - writereg(dev, PP_BusCTL, 0);
> -
> - netif_stop_queue(dev);
> -
> - free_irq(dev->irq, dev);
> -
> - /* Update the statistics here. */
> -
> - return 0;
> -
> -}
> -
> -/* Get the current statistics. This may be called with the card open or
> - closed. */
> -static struct net_device_stats *
> -net_get_stats(struct net_device *dev)
> -{
> - unsigned long flags;
> -
> - local_irq_save(flags);
> - /* Update the statistics from the device registers. */
> - dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
> - dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
> - local_irq_restore(flags);
> -
> - return &dev->stats;
> -}
> -
> -static void set_multicast_list(struct net_device *dev)
> -{
> - struct net_local *lp = netdev_priv(dev);
> -
> - if(dev->flags&IFF_PROMISC)
> - {
> - lp->rx_mode = RX_ALL_ACCEPT;
> - } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
> - /* The multicast-accept list is initialized to accept-all, and we
> - rely on higher-level filtering for now. */
> - lp->rx_mode = RX_MULTCAST_ACCEPT;
> - }
> - else
> - lp->rx_mode = 0;
> -
> - writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
> -
> - /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
> - writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
> - (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
> -}
> -
> -
> -static int set_mac_address(struct net_device *dev, void *addr)
> -{
> - struct sockaddr *saddr = addr;
> - int i;
> -
> - if (!is_valid_ether_addr(saddr->sa_data))
> - return -EADDRNOTAVAIL;
> -
> - eth_hw_addr_set(dev, saddr->sa_data);
> - netdev_info(dev, "Setting MAC address to %pM\n", dev->dev_addr);
> -
> - /* set the Ethernet address */
> - for (i=0; i < ETH_ALEN/2; i++)
> - writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
> -
> - return 0;
> -}
> -
> -MODULE_DESCRIPTION("Macintosh CS89x0-based Ethernet driver");
> -MODULE_LICENSE("GPL");
> -
> -static void mac89x0_device_remove(struct platform_device *pdev)
> -{
> - struct net_device *dev = platform_get_drvdata(pdev);
> -
> - unregister_netdev(dev);
> - nubus_writew(0, dev->base_addr + ADD_PORT);
> - free_netdev(dev);
> -}
> -
> -static struct platform_driver mac89x0_platform_driver = {
> - .probe = mac89x0_device_probe,
> - .remove = mac89x0_device_remove,
> - .driver = {
> - .name = "mac89x0",
> - },
> -};
> -
> -module_platform_driver(mac89x0_platform_driver);
>
> --
> 2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: Remove this driver
2026-04-23 7:07 ` Geert Uytterhoeven
@ 2026-04-23 7:10 ` John Paul Adrian Glaubitz
2026-04-23 8:52 ` Daniel Palmer
0 siblings, 1 reply; 25+ messages in thread
From: John Paul Adrian Glaubitz @ 2026-04-23 7:10 UTC (permalink / raw)
To: Geert Uytterhoeven, Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Michael Fritscher, Byron Stanoszek, Daniel Palmer, linux-kernel,
netdev, linux-doc, linux-m68k
Hi Geert,
On Thu, 2026-04-23 at 09:07 +0200, Geert Uytterhoeven wrote:
> CC linux-m68k
>
> On Thu, 23 Apr 2026 at 02:29, Andrew Lunn <andrew@lunn.ch> wrote:
> > The mac89x0 was written by Russell Nelson in 1996. It is an MAC
>
> It is based on the ISA cs89x0 driver, written by Russell Nelson.
>
> > device, so unlikely to be used with modern kernels.
>
> Macs do run modern kernels.
Retrocomputing still is not well regarded by some maintainers, it seems :-(.
Adrian
--
.''`. John Paul Adrian Glaubitz
: :' : Debian Developer
`. `' Physicist
`- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: Remove this driver
2026-04-23 7:10 ` John Paul Adrian Glaubitz
@ 2026-04-23 8:52 ` Daniel Palmer
2026-04-23 9:28 ` John Paul Adrian Glaubitz
0 siblings, 1 reply; 25+ messages in thread
From: Daniel Palmer @ 2026-04-23 8:52 UTC (permalink / raw)
To: John Paul Adrian Glaubitz
Cc: Geert Uytterhoeven, Andrew Lunn, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Jonathan Corbet, Shuah Khan, Michael Fritscher, Byron Stanoszek,
linux-kernel, netdev, linux-doc, linux-m68k
Hi Adrian,
On Thu, 23 Apr 2026 at 16:10, John Paul Adrian Glaubitz
<glaubitz@physik.fu-berlin.de> wrote:
> > Macs do run modern kernels.
>
> Retrocomputing still is not well regarded by some maintainers, it seems :-(.
I've found bugs in drivers by plugging those things into exotic
hardware like my Amiga 4000 and Ultra5 [0].
So, it's not totally pointless. And having a shader capable Amiga[1]
is pretty cool.
Sad to see fun stuff getting pushed out by basically spam bots. :(
0 - https://lists.freedesktop.org/archives/amd-gfx/2025-October/132283.html
1 - https://gist.github.com/fifteenhex/0e5ce8c1614bcec20ed242045c11d1d9
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 12/15] drivers: net: 8390: AX88190: Remove this driver
2026-04-22 18:01 ` [PATCH net v2 12/15] drivers: net: 8390: AX88190: " Andrew Lunn
@ 2026-04-23 9:06 ` Bjørn Mork
0 siblings, 0 replies; 25+ messages in thread
From: Bjørn Mork @ 2026-04-23 9:06 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc
Andrew Lunn <andrew@lunn.ch> writes:
> The ax88190 was written by David A. Hindsh
Hinds
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 13/15] drivers: net: 8390: pcnet: Remove this driver
2026-04-22 18:01 ` [PATCH net v2 13/15] drivers: net: 8390: pcnet: " Andrew Lunn
2026-04-23 5:49 ` Dominik Brodowski
@ 2026-04-23 9:09 ` Bjørn Mork
1 sibling, 0 replies; 25+ messages in thread
From: Bjørn Mork @ 2026-04-23 9:09 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
Daniel Palmer, linux-kernel, netdev, linux-doc
Andrew Lunn <andrew@lunn.ch> writes:
> The pcnet was written by David A. Hindsh
Hinds again
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: Remove this driver
2026-04-23 8:52 ` Daniel Palmer
@ 2026-04-23 9:28 ` John Paul Adrian Glaubitz
0 siblings, 0 replies; 25+ messages in thread
From: John Paul Adrian Glaubitz @ 2026-04-23 9:28 UTC (permalink / raw)
To: Daniel Palmer
Cc: Geert Uytterhoeven, Andrew Lunn, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Jonathan Corbet, Shuah Khan, Michael Fritscher, Byron Stanoszek,
linux-kernel, netdev, linux-doc, linux-m68k
Hi Daniel,
On Thu, 2026-04-23 at 17:52 +0900, Daniel Palmer wrote:
> Hi Adrian,
>
> On Thu, 23 Apr 2026 at 16:10, John Paul Adrian Glaubitz
> <glaubitz@physik.fu-berlin.de> wrote:
> > > Macs do run modern kernels.
> >
> > Retrocomputing still is not well regarded by some maintainers, it seems :-(.
>
> I've found bugs in drivers by plugging those things into exotic
> hardware like my Amiga 4000 and Ultra5 [0].
> So, it's not totally pointless. And having a shader capable Amiga[1]
> is pretty cool.
Not only that. Those exotic architectures also help iron out generic kernel
bugs and give new maintainers an possibility to get their feet wet.
> Sad to see fun stuff getting pushed out by basically spam bots. :(
It's not so much pushed out by spam bots but by the commercialization of the
Linux kernel. Many developers think that the sole purpose of the Linux kernel
is to run on modern commodity hardware.
Adrian
--
.''`. John Paul Adrian Glaubitz
: :' : Debian Developer
`. `' Physicist
`- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2026-04-23 9:28 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-22 18:01 [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 02/15] drivers: net: 3com: 3c515: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 03/15] drivers: net: 3com: 3c574: " Andrew Lunn
2026-04-23 2:14 ` Wei Fang
2026-04-23 2:21 ` Jakub Kicinski
2026-04-22 18:01 ` [PATCH net v2 04/15] drivers: net: 3com: 3c589: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 05/15] drivers: net: amd: lance: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 06/15] drivers: net: amd: nmclan: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 07/15] drivers: net: smsc: smc9194: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 08/15] drivers: net: smsc: smc91c92: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 09/15] drivers: net: cirrus: cs89x0: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 10/15] drivers: net: cirrus: mac89x0: " Andrew Lunn
2026-04-23 7:07 ` Geert Uytterhoeven
2026-04-23 7:10 ` John Paul Adrian Glaubitz
2026-04-23 8:52 ` Daniel Palmer
2026-04-23 9:28 ` John Paul Adrian Glaubitz
2026-04-22 18:01 ` [PATCH net v2 11/15] drivers: net: fujitsu: fmvj18x: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 12/15] drivers: net: 8390: AX88190: " Andrew Lunn
2026-04-23 9:06 ` Bjørn Mork
2026-04-22 18:01 ` [PATCH net v2 13/15] drivers: net: 8390: pcnet: " Andrew Lunn
2026-04-23 5:49 ` Dominik Brodowski
2026-04-23 9:09 ` Bjørn Mork
2026-04-22 18:01 ` [PATCH net v2 14/15] drivers: net: 8390: ultra: " Andrew Lunn
2026-04-22 18:01 ` [PATCH net v2 15/15] drivers: net: 8390: wd80x3: " Andrew Lunn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox