162306a36Sopenharmony_ci/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Written 1993-2000 by Donald Becker. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright 1994-2000 by Donald Becker. 662306a36Sopenharmony_ci Copyright 1993 United States Government as represented by the 762306a36Sopenharmony_ci Director, National Security Agency. This software may be used and 862306a36Sopenharmony_ci distributed according to the terms of the GNU General Public License, 962306a36Sopenharmony_ci incorporated herein by reference. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci This driver is for the 3Com EtherLinkIII series. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 1462306a36Sopenharmony_ci Scyld Computing Corporation 1562306a36Sopenharmony_ci 410 Severn Ave., Suite 210 1662306a36Sopenharmony_ci Annapolis MD 21403 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci Known limitations: 1962306a36Sopenharmony_ci Because of the way 3c509 ISA detection works it's difficult to predict 2062306a36Sopenharmony_ci a priori which of several ISA-mode cards will be detected first. 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci This driver does not use predictive interrupt mode, resulting in higher 2362306a36Sopenharmony_ci packet latency but lower overhead. If interrupts are disabled for an 2462306a36Sopenharmony_ci unusually long time it could also result in missed packets, but in 2562306a36Sopenharmony_ci practice this rarely happens. 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci FIXES: 2962306a36Sopenharmony_ci Alan Cox: Removed the 'Unexpected interrupt' bug. 3062306a36Sopenharmony_ci Michael Meskes: Upgraded to Donald Becker's version 1.07. 3162306a36Sopenharmony_ci Alan Cox: Increased the eeprom delay. Regardless of 3262306a36Sopenharmony_ci what the docs say some people definitely 3362306a36Sopenharmony_ci get problems with lower (but in card spec) 3462306a36Sopenharmony_ci delays 3562306a36Sopenharmony_ci v1.10 4/21/97 Fixed module code so that multiple cards may be detected, 3662306a36Sopenharmony_ci other cleanups. -djb 3762306a36Sopenharmony_ci Andrea Arcangeli: Upgraded to Donald Becker's version 1.12. 3862306a36Sopenharmony_ci Rick Payne: Fixed SMP race condition 3962306a36Sopenharmony_ci v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb 4062306a36Sopenharmony_ci v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb 4162306a36Sopenharmony_ci v1.15 1/31/98 Faster recovery for Tx errors. -djb 4262306a36Sopenharmony_ci v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb 4362306a36Sopenharmony_ci v1.18 12Mar2001 Andrew Morton 4462306a36Sopenharmony_ci - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) 4562306a36Sopenharmony_ci - Reviewed against 1.18 from scyld.com 4662306a36Sopenharmony_ci v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com> 4762306a36Sopenharmony_ci - ethtool support 4862306a36Sopenharmony_ci v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com> 4962306a36Sopenharmony_ci - Power Management support 5062306a36Sopenharmony_ci v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com> 5162306a36Sopenharmony_ci - Full duplex support 5262306a36Sopenharmony_ci v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca> 5362306a36Sopenharmony_ci - Additional ethtool features 5462306a36Sopenharmony_ci v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com> 5562306a36Sopenharmony_ci - Increase *read_eeprom udelay to workaround oops with 2 cards. 5662306a36Sopenharmony_ci v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org> 5762306a36Sopenharmony_ci - Introduce driver model for EISA cards. 5862306a36Sopenharmony_ci v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org> 5962306a36Sopenharmony_ci - convert to isa_driver and pnp_driver and some cleanups 6062306a36Sopenharmony_ci*/ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define DRV_NAME "3c509" 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* A few values that may be tweaked. */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 6762306a36Sopenharmony_ci#define TX_TIMEOUT (400*HZ/1000) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#include <linux/module.h> 7062306a36Sopenharmony_ci#include <linux/isa.h> 7162306a36Sopenharmony_ci#include <linux/pnp.h> 7262306a36Sopenharmony_ci#include <linux/string.h> 7362306a36Sopenharmony_ci#include <linux/interrupt.h> 7462306a36Sopenharmony_ci#include <linux/errno.h> 7562306a36Sopenharmony_ci#include <linux/in.h> 7662306a36Sopenharmony_ci#include <linux/ioport.h> 7762306a36Sopenharmony_ci#include <linux/init.h> 7862306a36Sopenharmony_ci#include <linux/netdevice.h> 7962306a36Sopenharmony_ci#include <linux/etherdevice.h> 8062306a36Sopenharmony_ci#include <linux/pm.h> 8162306a36Sopenharmony_ci#include <linux/skbuff.h> 8262306a36Sopenharmony_ci#include <linux/delay.h> /* for udelay() */ 8362306a36Sopenharmony_ci#include <linux/spinlock.h> 8462306a36Sopenharmony_ci#include <linux/ethtool.h> 8562306a36Sopenharmony_ci#include <linux/device.h> 8662306a36Sopenharmony_ci#include <linux/eisa.h> 8762306a36Sopenharmony_ci#include <linux/bitops.h> 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#include <linux/uaccess.h> 9062306a36Sopenharmony_ci#include <asm/io.h> 9162306a36Sopenharmony_ci#include <asm/irq.h> 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#ifdef EL3_DEBUG 9462306a36Sopenharmony_cistatic int el3_debug = EL3_DEBUG; 9562306a36Sopenharmony_ci#else 9662306a36Sopenharmony_cistatic int el3_debug = 2; 9762306a36Sopenharmony_ci#endif 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* Used to do a global count of all the cards in the system. Must be 10062306a36Sopenharmony_ci * a global variable so that the eisa probe routines can increment 10162306a36Sopenharmony_ci * it */ 10262306a36Sopenharmony_cistatic int el3_cards = 0; 10362306a36Sopenharmony_ci#define EL3_MAX_CARDS 8 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* To minimize the size of the driver source I only define operating 10662306a36Sopenharmony_ci constants if they are used several times. You'll need the manual 10762306a36Sopenharmony_ci anyway if you want to understand driver details. */ 10862306a36Sopenharmony_ci/* Offsets from base I/O address. */ 10962306a36Sopenharmony_ci#define EL3_DATA 0x00 11062306a36Sopenharmony_ci#define EL3_CMD 0x0e 11162306a36Sopenharmony_ci#define EL3_STATUS 0x0e 11262306a36Sopenharmony_ci#define EEPROM_READ 0x80 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define EL3_IO_EXTENT 16 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* The top five bits written to EL3_CMD are a command, the lower 12062306a36Sopenharmony_ci 11 bits are the parameter, if applicable. */ 12162306a36Sopenharmony_cienum c509cmd { 12262306a36Sopenharmony_ci TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, 12362306a36Sopenharmony_ci RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, 12462306a36Sopenharmony_ci TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, 12562306a36Sopenharmony_ci FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, 12662306a36Sopenharmony_ci SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, 12762306a36Sopenharmony_ci SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, 12862306a36Sopenharmony_ci StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11, 12962306a36Sopenharmony_ci PowerDown = 28<<11, PowerAuto = 29<<11}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cienum c509status { 13262306a36Sopenharmony_ci IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, 13362306a36Sopenharmony_ci TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, 13462306a36Sopenharmony_ci IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, }; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* The SetRxFilter command accepts the following classes: */ 13762306a36Sopenharmony_cienum RxFilter { 13862306a36Sopenharmony_ci RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* Register window 1 offsets, the window used in normal operation. */ 14162306a36Sopenharmony_ci#define TX_FIFO 0x00 14262306a36Sopenharmony_ci#define RX_FIFO 0x00 14362306a36Sopenharmony_ci#define RX_STATUS 0x08 14462306a36Sopenharmony_ci#define TX_STATUS 0x0B 14562306a36Sopenharmony_ci#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register */ 14862306a36Sopenharmony_ci#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */ 14962306a36Sopenharmony_ci#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ 15062306a36Sopenharmony_ci#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ 15162306a36Sopenharmony_ci#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ 15262306a36Sopenharmony_ci#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */ 15362306a36Sopenharmony_ci#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * Must be a power of two (we use a binary and in the 15762306a36Sopenharmony_ci * circular queue) 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci#define SKB_QUEUE_SIZE 64 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cienum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA }; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistruct el3_private { 16462306a36Sopenharmony_ci spinlock_t lock; 16562306a36Sopenharmony_ci /* skb send-queue */ 16662306a36Sopenharmony_ci int head, size; 16762306a36Sopenharmony_ci struct sk_buff *queue[SKB_QUEUE_SIZE]; 16862306a36Sopenharmony_ci enum el3_cardtype type; 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_cistatic int id_port; 17162306a36Sopenharmony_cistatic int current_tag; 17262306a36Sopenharmony_cistatic struct net_device *el3_devs[EL3_MAX_CARDS]; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Parameters that may be passed into the module. */ 17562306a36Sopenharmony_cistatic int debug = -1; 17662306a36Sopenharmony_cistatic int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; 17762306a36Sopenharmony_ci/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ 17862306a36Sopenharmony_cistatic int max_interrupt_work = 10; 17962306a36Sopenharmony_ci#ifdef CONFIG_PNP 18062306a36Sopenharmony_cistatic int nopnp; 18162306a36Sopenharmony_ci#endif 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int el3_common_init(struct net_device *dev); 18462306a36Sopenharmony_cistatic void el3_common_remove(struct net_device *dev); 18562306a36Sopenharmony_cistatic ushort id_read_eeprom(int index); 18662306a36Sopenharmony_cistatic ushort read_eeprom(int ioaddr, int index); 18762306a36Sopenharmony_cistatic int el3_open(struct net_device *dev); 18862306a36Sopenharmony_cistatic netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev); 18962306a36Sopenharmony_cistatic irqreturn_t el3_interrupt(int irq, void *dev_id); 19062306a36Sopenharmony_cistatic void update_stats(struct net_device *dev); 19162306a36Sopenharmony_cistatic struct net_device_stats *el3_get_stats(struct net_device *dev); 19262306a36Sopenharmony_cistatic int el3_rx(struct net_device *dev); 19362306a36Sopenharmony_cistatic int el3_close(struct net_device *dev); 19462306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev); 19562306a36Sopenharmony_cistatic void el3_tx_timeout (struct net_device *dev, unsigned int txqueue); 19662306a36Sopenharmony_cistatic void el3_down(struct net_device *dev); 19762306a36Sopenharmony_cistatic void el3_up(struct net_device *dev); 19862306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops; 19962306a36Sopenharmony_ci#ifdef CONFIG_PM 20062306a36Sopenharmony_cistatic int el3_suspend(struct device *, pm_message_t); 20162306a36Sopenharmony_cistatic int el3_resume(struct device *); 20262306a36Sopenharmony_ci#else 20362306a36Sopenharmony_ci#define el3_suspend NULL 20462306a36Sopenharmony_ci#define el3_resume NULL 20562306a36Sopenharmony_ci#endif 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* generic device remove for all device types */ 20962306a36Sopenharmony_cistatic int el3_device_remove (struct device *device); 21062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 21162306a36Sopenharmony_cistatic void el3_poll_controller(struct net_device *dev); 21262306a36Sopenharmony_ci#endif 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* Return 0 on success, 1 on error, 2 when found already detected PnP card */ 21562306a36Sopenharmony_cistatic int el3_isa_id_sequence(__be16 *phys_addr) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci short lrs_state = 0xff; 21862306a36Sopenharmony_ci int i; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* ISA boards are detected by sending the ID sequence to the 22162306a36Sopenharmony_ci ID_PORT. We find cards past the first by setting the 'current_tag' 22262306a36Sopenharmony_ci on cards as they are found. Cards with their tag set will not 22362306a36Sopenharmony_ci respond to subsequent ID sequences. */ 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci outb(0x00, id_port); 22662306a36Sopenharmony_ci outb(0x00, id_port); 22762306a36Sopenharmony_ci for (i = 0; i < 255; i++) { 22862306a36Sopenharmony_ci outb(lrs_state, id_port); 22962306a36Sopenharmony_ci lrs_state <<= 1; 23062306a36Sopenharmony_ci lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci /* For the first probe, clear all board's tag registers. */ 23362306a36Sopenharmony_ci if (current_tag == 0) 23462306a36Sopenharmony_ci outb(0xd0, id_port); 23562306a36Sopenharmony_ci else /* Otherwise kill off already-found boards. */ 23662306a36Sopenharmony_ci outb(0xd8, id_port); 23762306a36Sopenharmony_ci if (id_read_eeprom(7) != 0x6d50) 23862306a36Sopenharmony_ci return 1; 23962306a36Sopenharmony_ci /* Read in EEPROM data, which does contention-select. 24062306a36Sopenharmony_ci Only the lowest address board will stay "on-line". 24162306a36Sopenharmony_ci 3Com got the byte order backwards. */ 24262306a36Sopenharmony_ci for (i = 0; i < 3; i++) 24362306a36Sopenharmony_ci phys_addr[i] = htons(id_read_eeprom(i)); 24462306a36Sopenharmony_ci#ifdef CONFIG_PNP 24562306a36Sopenharmony_ci if (!nopnp) { 24662306a36Sopenharmony_ci /* The ISA PnP 3c509 cards respond to the ID sequence too. 24762306a36Sopenharmony_ci This check is needed in order not to register them twice. */ 24862306a36Sopenharmony_ci for (i = 0; i < el3_cards; i++) { 24962306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(el3_devs[i]); 25062306a36Sopenharmony_ci if (lp->type == EL3_PNP && 25162306a36Sopenharmony_ci ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) { 25262306a36Sopenharmony_ci if (el3_debug > 3) 25362306a36Sopenharmony_ci pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n", 25462306a36Sopenharmony_ci phys_addr[0] & 0xff, phys_addr[0] >> 8, 25562306a36Sopenharmony_ci phys_addr[1] & 0xff, phys_addr[1] >> 8, 25662306a36Sopenharmony_ci phys_addr[2] & 0xff, phys_addr[2] >> 8); 25762306a36Sopenharmony_ci /* Set the adaptor tag so that the next card can be found. */ 25862306a36Sopenharmony_ci outb(0xd0 + ++current_tag, id_port); 25962306a36Sopenharmony_ci return 2; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr, 26962306a36Sopenharmony_ci int irq, int if_port, enum el3_cardtype type) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci eth_hw_addr_set(dev, (u8 *)phys_addr); 27462306a36Sopenharmony_ci dev->base_addr = ioaddr; 27562306a36Sopenharmony_ci dev->irq = irq; 27662306a36Sopenharmony_ci dev->if_port = if_port; 27762306a36Sopenharmony_ci lp->type = type; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int el3_isa_match(struct device *pdev, unsigned int ndev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct net_device *dev; 28362306a36Sopenharmony_ci int ioaddr, isa_irq, if_port, err; 28462306a36Sopenharmony_ci unsigned int iobase; 28562306a36Sopenharmony_ci __be16 phys_addr[3]; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci while ((err = el3_isa_id_sequence(phys_addr)) == 2) 28862306a36Sopenharmony_ci ; /* Skip to next card when PnP card found */ 28962306a36Sopenharmony_ci if (err == 1) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci iobase = id_read_eeprom(8); 29362306a36Sopenharmony_ci if_port = iobase >> 14; 29462306a36Sopenharmony_ci ioaddr = 0x200 + ((iobase & 0x1f) << 4); 29562306a36Sopenharmony_ci if (irq[el3_cards] > 1 && irq[el3_cards] < 16) 29662306a36Sopenharmony_ci isa_irq = irq[el3_cards]; 29762306a36Sopenharmony_ci else 29862306a36Sopenharmony_ci isa_irq = id_read_eeprom(9) >> 12; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct el3_private)); 30162306a36Sopenharmony_ci if (!dev) 30262306a36Sopenharmony_ci return -ENOMEM; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci SET_NETDEV_DEV(dev, pdev); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) { 30762306a36Sopenharmony_ci free_netdev(dev); 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Set the adaptor tag so that the next card can be found. */ 31262306a36Sopenharmony_ci outb(0xd0 + ++current_tag, id_port); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Activate the adaptor at the EEPROM location. */ 31562306a36Sopenharmony_ci outb((ioaddr >> 4) | 0xe0, id_port); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci EL3WINDOW(0); 31862306a36Sopenharmony_ci if (inw(ioaddr) != 0x6d50) { 31962306a36Sopenharmony_ci free_netdev(dev); 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Free the interrupt so that some other card can use it. */ 32462306a36Sopenharmony_ci outw(0x0f00, ioaddr + WN0_IRQ); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA); 32762306a36Sopenharmony_ci dev_set_drvdata(pdev, dev); 32862306a36Sopenharmony_ci if (el3_common_init(dev)) { 32962306a36Sopenharmony_ci free_netdev(dev); 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci el3_devs[el3_cards++] = dev; 33462306a36Sopenharmony_ci return 1; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void el3_isa_remove(struct device *pdev, 33862306a36Sopenharmony_ci unsigned int ndev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci el3_device_remove(pdev); 34162306a36Sopenharmony_ci dev_set_drvdata(pdev, NULL); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci#ifdef CONFIG_PM 34562306a36Sopenharmony_cistatic int el3_isa_suspend(struct device *dev, unsigned int n, 34662306a36Sopenharmony_ci pm_message_t state) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci current_tag = 0; 34962306a36Sopenharmony_ci return el3_suspend(dev, state); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int el3_isa_resume(struct device *dev, unsigned int n) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 35562306a36Sopenharmony_ci int ioaddr = ndev->base_addr, err; 35662306a36Sopenharmony_ci __be16 phys_addr[3]; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci while ((err = el3_isa_id_sequence(phys_addr)) == 2) 35962306a36Sopenharmony_ci ; /* Skip to next card when PnP card found */ 36062306a36Sopenharmony_ci if (err == 1) 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci /* Set the adaptor tag so that the next card can be found. */ 36362306a36Sopenharmony_ci outb(0xd0 + ++current_tag, id_port); 36462306a36Sopenharmony_ci /* Enable the card */ 36562306a36Sopenharmony_ci outb((ioaddr >> 4) | 0xe0, id_port); 36662306a36Sopenharmony_ci EL3WINDOW(0); 36762306a36Sopenharmony_ci if (inw(ioaddr) != 0x6d50) 36862306a36Sopenharmony_ci return 1; 36962306a36Sopenharmony_ci /* Free the interrupt so that some other card can use it. */ 37062306a36Sopenharmony_ci outw(0x0f00, ioaddr + WN0_IRQ); 37162306a36Sopenharmony_ci return el3_resume(dev); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci#endif 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic struct isa_driver el3_isa_driver = { 37662306a36Sopenharmony_ci .match = el3_isa_match, 37762306a36Sopenharmony_ci .remove = el3_isa_remove, 37862306a36Sopenharmony_ci#ifdef CONFIG_PM 37962306a36Sopenharmony_ci .suspend = el3_isa_suspend, 38062306a36Sopenharmony_ci .resume = el3_isa_resume, 38162306a36Sopenharmony_ci#endif 38262306a36Sopenharmony_ci .driver = { 38362306a36Sopenharmony_ci .name = "3c509" 38462306a36Sopenharmony_ci }, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_cistatic int isa_registered; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci#ifdef CONFIG_PNP 38962306a36Sopenharmony_cistatic const struct pnp_device_id el3_pnp_ids[] = { 39062306a36Sopenharmony_ci { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */ 39162306a36Sopenharmony_ci { .id = "TCM5091" }, /* 3Com Etherlink III */ 39262306a36Sopenharmony_ci { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */ 39362306a36Sopenharmony_ci { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */ 39462306a36Sopenharmony_ci { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */ 39562306a36Sopenharmony_ci { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */ 39662306a36Sopenharmony_ci { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */ 39762306a36Sopenharmony_ci { .id = "" } 39862306a36Sopenharmony_ci}; 39962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, el3_pnp_ids); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci short i; 40462306a36Sopenharmony_ci int ioaddr, irq, if_port; 40562306a36Sopenharmony_ci __be16 phys_addr[3]; 40662306a36Sopenharmony_ci struct net_device *dev = NULL; 40762306a36Sopenharmony_ci int err; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ioaddr = pnp_port_start(pdev, 0); 41062306a36Sopenharmony_ci if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp")) 41162306a36Sopenharmony_ci return -EBUSY; 41262306a36Sopenharmony_ci irq = pnp_irq(pdev, 0); 41362306a36Sopenharmony_ci EL3WINDOW(0); 41462306a36Sopenharmony_ci for (i = 0; i < 3; i++) 41562306a36Sopenharmony_ci phys_addr[i] = htons(read_eeprom(ioaddr, i)); 41662306a36Sopenharmony_ci if_port = read_eeprom(ioaddr, 8) >> 14; 41762306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct el3_private)); 41862306a36Sopenharmony_ci if (!dev) { 41962306a36Sopenharmony_ci release_region(ioaddr, EL3_IO_EXTENT); 42062306a36Sopenharmony_ci return -ENOMEM; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP); 42562306a36Sopenharmony_ci pnp_set_drvdata(pdev, dev); 42662306a36Sopenharmony_ci err = el3_common_init(dev); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (err) { 42962306a36Sopenharmony_ci pnp_set_drvdata(pdev, NULL); 43062306a36Sopenharmony_ci free_netdev(dev); 43162306a36Sopenharmony_ci return err; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci el3_devs[el3_cards++] = dev; 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic void el3_pnp_remove(struct pnp_dev *pdev) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci el3_common_remove(pnp_get_drvdata(pdev)); 44162306a36Sopenharmony_ci pnp_set_drvdata(pdev, NULL); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci#ifdef CONFIG_PM 44562306a36Sopenharmony_cistatic int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci return el3_suspend(&pdev->dev, state); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int el3_pnp_resume(struct pnp_dev *pdev) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci return el3_resume(&pdev->dev); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci#endif 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic struct pnp_driver el3_pnp_driver = { 45762306a36Sopenharmony_ci .name = "3c509", 45862306a36Sopenharmony_ci .id_table = el3_pnp_ids, 45962306a36Sopenharmony_ci .probe = el3_pnp_probe, 46062306a36Sopenharmony_ci .remove = el3_pnp_remove, 46162306a36Sopenharmony_ci#ifdef CONFIG_PM 46262306a36Sopenharmony_ci .suspend = el3_pnp_suspend, 46362306a36Sopenharmony_ci .resume = el3_pnp_resume, 46462306a36Sopenharmony_ci#endif 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_cistatic int pnp_registered; 46762306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci#ifdef CONFIG_EISA 47062306a36Sopenharmony_cistatic const struct eisa_device_id el3_eisa_ids[] = { 47162306a36Sopenharmony_ci { "TCM5090" }, 47262306a36Sopenharmony_ci { "TCM5091" }, 47362306a36Sopenharmony_ci { "TCM5092" }, 47462306a36Sopenharmony_ci { "TCM5093" }, 47562306a36Sopenharmony_ci { "TCM5094" }, 47662306a36Sopenharmony_ci { "TCM5095" }, 47762306a36Sopenharmony_ci { "TCM5098" }, 47862306a36Sopenharmony_ci { "" } 47962306a36Sopenharmony_ci}; 48062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(eisa, el3_eisa_ids); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int el3_eisa_probe (struct device *device); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic struct eisa_driver el3_eisa_driver = { 48562306a36Sopenharmony_ci .id_table = el3_eisa_ids, 48662306a36Sopenharmony_ci .driver = { 48762306a36Sopenharmony_ci .name = "3c579", 48862306a36Sopenharmony_ci .probe = el3_eisa_probe, 48962306a36Sopenharmony_ci .remove = el3_device_remove, 49062306a36Sopenharmony_ci .suspend = el3_suspend, 49162306a36Sopenharmony_ci .resume = el3_resume, 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci}; 49462306a36Sopenharmony_cistatic int eisa_registered; 49562306a36Sopenharmony_ci#endif 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 49862306a36Sopenharmony_ci .ndo_open = el3_open, 49962306a36Sopenharmony_ci .ndo_stop = el3_close, 50062306a36Sopenharmony_ci .ndo_start_xmit = el3_start_xmit, 50162306a36Sopenharmony_ci .ndo_get_stats = el3_get_stats, 50262306a36Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 50362306a36Sopenharmony_ci .ndo_tx_timeout = el3_tx_timeout, 50462306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 50562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 50662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 50762306a36Sopenharmony_ci .ndo_poll_controller = el3_poll_controller, 50862306a36Sopenharmony_ci#endif 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int el3_common_init(struct net_device *dev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 51462306a36Sopenharmony_ci int err; 51562306a36Sopenharmony_ci static const char * const if_names[] = { 51662306a36Sopenharmony_ci "10baseT", "AUI", "undefined", "BNC" 51762306a36Sopenharmony_ci }; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci spin_lock_init(&lp->lock); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */ 52262306a36Sopenharmony_ci dev->if_port = (dev->mem_start & 0x0f); 52362306a36Sopenharmony_ci } else { /* xcvr codes 0/8 */ 52462306a36Sopenharmony_ci /* use eeprom value, but save user's full-duplex selection */ 52562306a36Sopenharmony_ci dev->if_port |= (dev->mem_start & 0x08); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* The EL3-specific entries in the device structure. */ 52962306a36Sopenharmony_ci dev->netdev_ops = &netdev_ops; 53062306a36Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 53162306a36Sopenharmony_ci dev->ethtool_ops = ðtool_ops; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci err = register_netdev(dev); 53462306a36Sopenharmony_ci if (err) { 53562306a36Sopenharmony_ci pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n", 53662306a36Sopenharmony_ci dev->base_addr, dev->irq); 53762306a36Sopenharmony_ci release_region(dev->base_addr, EL3_IO_EXTENT); 53862306a36Sopenharmony_ci return err; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n", 54262306a36Sopenharmony_ci dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)], 54362306a36Sopenharmony_ci dev->dev_addr, dev->irq); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void el3_common_remove (struct net_device *dev) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci unregister_netdev (dev); 55262306a36Sopenharmony_ci release_region(dev->base_addr, EL3_IO_EXTENT); 55362306a36Sopenharmony_ci free_netdev (dev); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci#ifdef CONFIG_EISA 55762306a36Sopenharmony_cistatic int el3_eisa_probe(struct device *device) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci short i; 56062306a36Sopenharmony_ci int ioaddr, irq, if_port; 56162306a36Sopenharmony_ci __be16 phys_addr[3]; 56262306a36Sopenharmony_ci struct net_device *dev = NULL; 56362306a36Sopenharmony_ci struct eisa_device *edev; 56462306a36Sopenharmony_ci int err; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Yeepee, The driver framework is calling us ! */ 56762306a36Sopenharmony_ci edev = to_eisa_device (device); 56862306a36Sopenharmony_ci ioaddr = edev->base_addr; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa")) 57162306a36Sopenharmony_ci return -EBUSY; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Change the register set to the configuration window 0. */ 57462306a36Sopenharmony_ci outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci irq = inw(ioaddr + WN0_IRQ) >> 12; 57762306a36Sopenharmony_ci if_port = inw(ioaddr + 6)>>14; 57862306a36Sopenharmony_ci for (i = 0; i < 3; i++) 57962306a36Sopenharmony_ci phys_addr[i] = htons(read_eeprom(ioaddr, i)); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Restore the "Product ID" to the EEPROM read register. */ 58262306a36Sopenharmony_ci read_eeprom(ioaddr, 3); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci dev = alloc_etherdev(sizeof (struct el3_private)); 58562306a36Sopenharmony_ci if (dev == NULL) { 58662306a36Sopenharmony_ci release_region(ioaddr, EL3_IO_EXTENT); 58762306a36Sopenharmony_ci return -ENOMEM; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci SET_NETDEV_DEV(dev, device); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA); 59362306a36Sopenharmony_ci eisa_set_drvdata (edev, dev); 59462306a36Sopenharmony_ci err = el3_common_init(dev); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (err) { 59762306a36Sopenharmony_ci eisa_set_drvdata (edev, NULL); 59862306a36Sopenharmony_ci free_netdev(dev); 59962306a36Sopenharmony_ci return err; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci el3_devs[el3_cards++] = dev; 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci#endif 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/* This remove works for all device types. 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * The net dev must be stored in the driver data field */ 61062306a36Sopenharmony_cistatic int el3_device_remove(struct device *device) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct net_device *dev; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci dev = dev_get_drvdata(device); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci el3_common_remove (dev); 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* Read a word from the EEPROM using the regular EEPROM access register. 62162306a36Sopenharmony_ci Assume that we are in register window zero. 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_cistatic ushort read_eeprom(int ioaddr, int index) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci outw(EEPROM_READ + index, ioaddr + 10); 62662306a36Sopenharmony_ci /* Pause for at least 162 us. for the read to take place. 62762306a36Sopenharmony_ci Some chips seem to require much longer */ 62862306a36Sopenharmony_ci mdelay(2); 62962306a36Sopenharmony_ci return inw(ioaddr + 12); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci/* Read a word from the EEPROM when in the ISA ID probe state. */ 63362306a36Sopenharmony_cistatic ushort id_read_eeprom(int index) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci int bit, word = 0; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* Issue read command, and pause for at least 162 us. for it to complete. 63862306a36Sopenharmony_ci Assume extra-fast 16Mhz bus. */ 63962306a36Sopenharmony_ci outb(EEPROM_READ + index, id_port); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Pause for at least 162 us. for the read to take place. */ 64262306a36Sopenharmony_ci /* Some chips seem to require much longer */ 64362306a36Sopenharmony_ci mdelay(4); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci for (bit = 15; bit >= 0; bit--) 64662306a36Sopenharmony_ci word = (word << 1) + (inb(id_port) & 0x01); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (el3_debug > 3) 64962306a36Sopenharmony_ci pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return word; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int 65662306a36Sopenharmony_ciel3_open(struct net_device *dev) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci int ioaddr = dev->base_addr; 65962306a36Sopenharmony_ci int i; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci outw(TxReset, ioaddr + EL3_CMD); 66262306a36Sopenharmony_ci outw(RxReset, ioaddr + EL3_CMD); 66362306a36Sopenharmony_ci outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev); 66662306a36Sopenharmony_ci if (i) 66762306a36Sopenharmony_ci return i; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci EL3WINDOW(0); 67062306a36Sopenharmony_ci if (el3_debug > 3) 67162306a36Sopenharmony_ci pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name, 67262306a36Sopenharmony_ci dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci el3_up(dev); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (el3_debug > 3) 67762306a36Sopenharmony_ci pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n", 67862306a36Sopenharmony_ci dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic void 68462306a36Sopenharmony_ciel3_tx_timeout (struct net_device *dev, unsigned int txqueue) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci int ioaddr = dev->base_addr; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* Transmitter timeout, serious problems. */ 68962306a36Sopenharmony_ci pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n", 69062306a36Sopenharmony_ci dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), 69162306a36Sopenharmony_ci inw(ioaddr + TX_FREE)); 69262306a36Sopenharmony_ci dev->stats.tx_errors++; 69362306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 69462306a36Sopenharmony_ci /* Issue TX_RESET and TX_START commands. */ 69562306a36Sopenharmony_ci outw(TxReset, ioaddr + EL3_CMD); 69662306a36Sopenharmony_ci outw(TxEnable, ioaddr + EL3_CMD); 69762306a36Sopenharmony_ci netif_wake_queue(dev); 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic netdev_tx_t 70262306a36Sopenharmony_ciel3_start_xmit(struct sk_buff *skb, struct net_device *dev) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 70562306a36Sopenharmony_ci int ioaddr = dev->base_addr; 70662306a36Sopenharmony_ci unsigned long flags; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci netif_stop_queue (dev); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (el3_debug > 4) { 71362306a36Sopenharmony_ci pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", 71462306a36Sopenharmony_ci dev->name, skb->len, inw(ioaddr + EL3_STATUS)); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci /* 71762306a36Sopenharmony_ci * We lock the driver against other processors. Note 71862306a36Sopenharmony_ci * we don't need to lock versus the IRQ as we suspended 71962306a36Sopenharmony_ci * that. This means that we lose the ability to take 72062306a36Sopenharmony_ci * an RX during a TX upload. That sucks a bit with SMP 72162306a36Sopenharmony_ci * on an original 3c509 (2K buffer) 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * Using disable_irq stops us crapping on other 72462306a36Sopenharmony_ci * time sensitive devices. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Put out the doubleword header... */ 73062306a36Sopenharmony_ci outw(skb->len, ioaddr + TX_FIFO); 73162306a36Sopenharmony_ci outw(0x00, ioaddr + TX_FIFO); 73262306a36Sopenharmony_ci /* ... and the packet rounded to a doubleword. */ 73362306a36Sopenharmony_ci outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (inw(ioaddr + TX_FREE) > 1536) 73662306a36Sopenharmony_ci netif_start_queue(dev); 73762306a36Sopenharmony_ci else 73862306a36Sopenharmony_ci /* Interrupt us when the FIFO has room for max-sized packet. */ 73962306a36Sopenharmony_ci outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci dev_consume_skb_any (skb); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Clear the Tx status stack. */ 74662306a36Sopenharmony_ci { 74762306a36Sopenharmony_ci short tx_status; 74862306a36Sopenharmony_ci int i = 4; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { 75162306a36Sopenharmony_ci if (tx_status & 0x38) dev->stats.tx_aborted_errors++; 75262306a36Sopenharmony_ci if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); 75362306a36Sopenharmony_ci if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); 75462306a36Sopenharmony_ci outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci return NETDEV_TX_OK; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci/* The EL3 interrupt handler. */ 76162306a36Sopenharmony_cistatic irqreturn_t 76262306a36Sopenharmony_ciel3_interrupt(int irq, void *dev_id) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct net_device *dev = dev_id; 76562306a36Sopenharmony_ci struct el3_private *lp; 76662306a36Sopenharmony_ci int ioaddr, status; 76762306a36Sopenharmony_ci int i = max_interrupt_work; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci lp = netdev_priv(dev); 77062306a36Sopenharmony_ci spin_lock(&lp->lock); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci ioaddr = dev->base_addr; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (el3_debug > 4) { 77562306a36Sopenharmony_ci status = inw(ioaddr + EL3_STATUS); 77662306a36Sopenharmony_ci pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci while ((status = inw(ioaddr + EL3_STATUS)) & 78062306a36Sopenharmony_ci (IntLatch | RxComplete | StatsFull)) { 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (status & RxComplete) 78362306a36Sopenharmony_ci el3_rx(dev); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (status & TxAvailable) { 78662306a36Sopenharmony_ci if (el3_debug > 5) 78762306a36Sopenharmony_ci pr_debug(" TX room bit was handled.\n"); 78862306a36Sopenharmony_ci /* There's room in the FIFO for a full-sized packet. */ 78962306a36Sopenharmony_ci outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); 79062306a36Sopenharmony_ci netif_wake_queue (dev); 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { 79362306a36Sopenharmony_ci /* Handle all uncommon interrupts. */ 79462306a36Sopenharmony_ci if (status & StatsFull) /* Empty statistics. */ 79562306a36Sopenharmony_ci update_stats(dev); 79662306a36Sopenharmony_ci if (status & RxEarly) { /* Rx early is unused. */ 79762306a36Sopenharmony_ci el3_rx(dev); 79862306a36Sopenharmony_ci outw(AckIntr | RxEarly, ioaddr + EL3_CMD); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci if (status & TxComplete) { /* Really Tx error. */ 80162306a36Sopenharmony_ci short tx_status; 80262306a36Sopenharmony_ci int i = 4; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { 80562306a36Sopenharmony_ci if (tx_status & 0x38) dev->stats.tx_aborted_errors++; 80662306a36Sopenharmony_ci if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); 80762306a36Sopenharmony_ci if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); 80862306a36Sopenharmony_ci outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci if (status & AdapterFailure) { 81262306a36Sopenharmony_ci /* Adapter failure requires Rx reset and reinit. */ 81362306a36Sopenharmony_ci outw(RxReset, ioaddr + EL3_CMD); 81462306a36Sopenharmony_ci /* Set the Rx filter to the current state. */ 81562306a36Sopenharmony_ci outw(SetRxFilter | RxStation | RxBroadcast 81662306a36Sopenharmony_ci | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) 81762306a36Sopenharmony_ci | (dev->flags & IFF_PROMISC ? RxProm : 0), 81862306a36Sopenharmony_ci ioaddr + EL3_CMD); 81962306a36Sopenharmony_ci outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ 82062306a36Sopenharmony_ci outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (--i < 0) { 82562306a36Sopenharmony_ci pr_err("%s: Infinite loop in interrupt, status %4.4x.\n", 82662306a36Sopenharmony_ci dev->name, status); 82762306a36Sopenharmony_ci /* Clear all interrupts. */ 82862306a36Sopenharmony_ci outw(AckIntr | 0xFF, ioaddr + EL3_CMD); 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci /* Acknowledge the IRQ. */ 83262306a36Sopenharmony_ci outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */ 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (el3_debug > 4) { 83662306a36Sopenharmony_ci pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, 83762306a36Sopenharmony_ci inw(ioaddr + EL3_STATUS)); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci spin_unlock(&lp->lock); 84062306a36Sopenharmony_ci return IRQ_HANDLED; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 84562306a36Sopenharmony_ci/* 84662306a36Sopenharmony_ci * Polling receive - used by netconsole and other diagnostic tools 84762306a36Sopenharmony_ci * to allow network i/o with interrupts disabled. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_cistatic void el3_poll_controller(struct net_device *dev) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci disable_irq(dev->irq); 85262306a36Sopenharmony_ci el3_interrupt(dev->irq, dev); 85362306a36Sopenharmony_ci enable_irq(dev->irq); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci#endif 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic struct net_device_stats * 85862306a36Sopenharmony_ciel3_get_stats(struct net_device *dev) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 86162306a36Sopenharmony_ci unsigned long flags; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* 86462306a36Sopenharmony_ci * This is fast enough not to bother with disable IRQ 86562306a36Sopenharmony_ci * stuff. 86662306a36Sopenharmony_ci */ 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 86962306a36Sopenharmony_ci update_stats(dev); 87062306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 87162306a36Sopenharmony_ci return &dev->stats; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci/* Update statistics. We change to register window 6, so this should be run 87562306a36Sopenharmony_ci single-threaded if the device is active. This is expected to be a rare 87662306a36Sopenharmony_ci operation, and it's simpler for the rest of the driver to assume that 87762306a36Sopenharmony_ci window 1 is always valid rather than use a special window-state variable. 87862306a36Sopenharmony_ci */ 87962306a36Sopenharmony_cistatic void update_stats(struct net_device *dev) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci int ioaddr = dev->base_addr; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (el3_debug > 5) 88462306a36Sopenharmony_ci pr_debug(" Updating the statistics.\n"); 88562306a36Sopenharmony_ci /* Turn off statistics updates while reading. */ 88662306a36Sopenharmony_ci outw(StatsDisable, ioaddr + EL3_CMD); 88762306a36Sopenharmony_ci /* Switch to the stats window, and read everything. */ 88862306a36Sopenharmony_ci EL3WINDOW(6); 88962306a36Sopenharmony_ci dev->stats.tx_carrier_errors += inb(ioaddr + 0); 89062306a36Sopenharmony_ci dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); 89162306a36Sopenharmony_ci /* Multiple collisions. */ inb(ioaddr + 2); 89262306a36Sopenharmony_ci dev->stats.collisions += inb(ioaddr + 3); 89362306a36Sopenharmony_ci dev->stats.tx_window_errors += inb(ioaddr + 4); 89462306a36Sopenharmony_ci dev->stats.rx_fifo_errors += inb(ioaddr + 5); 89562306a36Sopenharmony_ci dev->stats.tx_packets += inb(ioaddr + 6); 89662306a36Sopenharmony_ci /* Rx packets */ inb(ioaddr + 7); 89762306a36Sopenharmony_ci /* Tx deferrals */ inb(ioaddr + 8); 89862306a36Sopenharmony_ci inw(ioaddr + 10); /* Total Rx and Tx octets. */ 89962306a36Sopenharmony_ci inw(ioaddr + 12); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* Back to window 1, and turn statistics back on. */ 90262306a36Sopenharmony_ci EL3WINDOW(1); 90362306a36Sopenharmony_ci outw(StatsEnable, ioaddr + EL3_CMD); 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int 90762306a36Sopenharmony_ciel3_rx(struct net_device *dev) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci int ioaddr = dev->base_addr; 91062306a36Sopenharmony_ci short rx_status; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (el3_debug > 5) 91362306a36Sopenharmony_ci pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", 91462306a36Sopenharmony_ci inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); 91562306a36Sopenharmony_ci while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { 91662306a36Sopenharmony_ci if (rx_status & 0x4000) { /* Error, update stats. */ 91762306a36Sopenharmony_ci short error = rx_status & 0x3800; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci outw(RxDiscard, ioaddr + EL3_CMD); 92062306a36Sopenharmony_ci dev->stats.rx_errors++; 92162306a36Sopenharmony_ci switch (error) { 92262306a36Sopenharmony_ci case 0x0000: dev->stats.rx_over_errors++; break; 92362306a36Sopenharmony_ci case 0x0800: dev->stats.rx_length_errors++; break; 92462306a36Sopenharmony_ci case 0x1000: dev->stats.rx_frame_errors++; break; 92562306a36Sopenharmony_ci case 0x1800: dev->stats.rx_length_errors++; break; 92662306a36Sopenharmony_ci case 0x2000: dev->stats.rx_frame_errors++; break; 92762306a36Sopenharmony_ci case 0x2800: dev->stats.rx_crc_errors++; break; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci } else { 93062306a36Sopenharmony_ci short pkt_len = rx_status & 0x7ff; 93162306a36Sopenharmony_ci struct sk_buff *skb; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, pkt_len + 5); 93462306a36Sopenharmony_ci if (el3_debug > 4) 93562306a36Sopenharmony_ci pr_debug("Receiving packet size %d status %4.4x.\n", 93662306a36Sopenharmony_ci pkt_len, rx_status); 93762306a36Sopenharmony_ci if (skb != NULL) { 93862306a36Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte */ 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* 'skb->data' points to the start of sk_buff data area. */ 94162306a36Sopenharmony_ci insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), 94262306a36Sopenharmony_ci (pkt_len + 3) >> 2); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ 94562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb,dev); 94662306a36Sopenharmony_ci netif_rx(skb); 94762306a36Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 94862306a36Sopenharmony_ci dev->stats.rx_packets++; 94962306a36Sopenharmony_ci continue; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci outw(RxDiscard, ioaddr + EL3_CMD); 95262306a36Sopenharmony_ci dev->stats.rx_dropped++; 95362306a36Sopenharmony_ci if (el3_debug) 95462306a36Sopenharmony_ci pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", 95562306a36Sopenharmony_ci dev->name, pkt_len); 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci inw(ioaddr + EL3_STATUS); /* Delay. */ 95862306a36Sopenharmony_ci while (inw(ioaddr + EL3_STATUS) & 0x1000) 95962306a36Sopenharmony_ci pr_debug(" Waiting for 3c509 to discard packet, status %x.\n", 96062306a36Sopenharmony_ci inw(ioaddr + EL3_STATUS) ); 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci return 0; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/* 96762306a36Sopenharmony_ci * Set or clear the multicast filter for this adaptor. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_cistatic void 97062306a36Sopenharmony_ciset_multicast_list(struct net_device *dev) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci unsigned long flags; 97362306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 97462306a36Sopenharmony_ci int ioaddr = dev->base_addr; 97562306a36Sopenharmony_ci int mc_count = netdev_mc_count(dev); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (el3_debug > 1) { 97862306a36Sopenharmony_ci static int old; 97962306a36Sopenharmony_ci if (old != mc_count) { 98062306a36Sopenharmony_ci old = mc_count; 98162306a36Sopenharmony_ci pr_debug("%s: Setting Rx mode to %d addresses.\n", 98262306a36Sopenharmony_ci dev->name, mc_count); 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 98662306a36Sopenharmony_ci if (dev->flags&IFF_PROMISC) { 98762306a36Sopenharmony_ci outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, 98862306a36Sopenharmony_ci ioaddr + EL3_CMD); 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci else if (mc_count || (dev->flags&IFF_ALLMULTI)) { 99162306a36Sopenharmony_ci outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci else 99462306a36Sopenharmony_ci outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); 99562306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int 99962306a36Sopenharmony_ciel3_close(struct net_device *dev) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci int ioaddr = dev->base_addr; 100262306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (el3_debug > 2) 100562306a36Sopenharmony_ci pr_debug("%s: Shutting down ethercard.\n", dev->name); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci el3_down(dev); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci free_irq(dev->irq, dev); 101062306a36Sopenharmony_ci /* Switching back to window 0 disables the IRQ. */ 101162306a36Sopenharmony_ci EL3WINDOW(0); 101262306a36Sopenharmony_ci if (lp->type != EL3_EISA) { 101362306a36Sopenharmony_ci /* But we explicitly zero the IRQ line select anyway. Don't do 101462306a36Sopenharmony_ci * it on EISA cards, it prevents the module from getting an 101562306a36Sopenharmony_ci * IRQ after unload+reload... */ 101662306a36Sopenharmony_ci outw(0x0f00, ioaddr + WN0_IRQ); 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci return 0; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int 102362306a36Sopenharmony_ciel3_link_ok(struct net_device *dev) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci int ioaddr = dev->base_addr; 102662306a36Sopenharmony_ci u16 tmp; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci EL3WINDOW(4); 102962306a36Sopenharmony_ci tmp = inw(ioaddr + WN4_MEDIA); 103062306a36Sopenharmony_ci EL3WINDOW(1); 103162306a36Sopenharmony_ci return tmp & (1<<11); 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic void 103562306a36Sopenharmony_ciel3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci u16 tmp; 103862306a36Sopenharmony_ci int ioaddr = dev->base_addr; 103962306a36Sopenharmony_ci u32 supported; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci EL3WINDOW(0); 104262306a36Sopenharmony_ci /* obtain current transceiver via WN4_MEDIA? */ 104362306a36Sopenharmony_ci tmp = inw(ioaddr + WN0_ADDR_CONF); 104462306a36Sopenharmony_ci switch (tmp >> 14) { 104562306a36Sopenharmony_ci case 0: 104662306a36Sopenharmony_ci cmd->base.port = PORT_TP; 104762306a36Sopenharmony_ci break; 104862306a36Sopenharmony_ci case 1: 104962306a36Sopenharmony_ci cmd->base.port = PORT_AUI; 105062306a36Sopenharmony_ci break; 105162306a36Sopenharmony_ci case 3: 105262306a36Sopenharmony_ci cmd->base.port = PORT_BNC; 105362306a36Sopenharmony_ci break; 105462306a36Sopenharmony_ci default: 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 105962306a36Sopenharmony_ci supported = 0; 106062306a36Sopenharmony_ci tmp = inw(ioaddr + WN0_CONF_CTRL); 106162306a36Sopenharmony_ci if (tmp & (1<<13)) 106262306a36Sopenharmony_ci supported |= SUPPORTED_AUI; 106362306a36Sopenharmony_ci if (tmp & (1<<12)) 106462306a36Sopenharmony_ci supported |= SUPPORTED_BNC; 106562306a36Sopenharmony_ci if (tmp & (1<<9)) { 106662306a36Sopenharmony_ci supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half | 106762306a36Sopenharmony_ci SUPPORTED_10baseT_Full; /* hmm... */ 106862306a36Sopenharmony_ci EL3WINDOW(4); 106962306a36Sopenharmony_ci tmp = inw(ioaddr + WN4_NETDIAG); 107062306a36Sopenharmony_ci if (tmp & FD_ENABLE) 107162306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 107562306a36Sopenharmony_ci supported); 107662306a36Sopenharmony_ci cmd->base.speed = SPEED_10; 107762306a36Sopenharmony_ci EL3WINDOW(1); 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int 108162306a36Sopenharmony_ciel3_netdev_set_ecmd(struct net_device *dev, 108262306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci u16 tmp; 108562306a36Sopenharmony_ci int ioaddr = dev->base_addr; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (cmd->base.speed != SPEED_10) 108862306a36Sopenharmony_ci return -EINVAL; 108962306a36Sopenharmony_ci if ((cmd->base.duplex != DUPLEX_HALF) && 109062306a36Sopenharmony_ci (cmd->base.duplex != DUPLEX_FULL)) 109162306a36Sopenharmony_ci return -EINVAL; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* change XCVR type */ 109462306a36Sopenharmony_ci EL3WINDOW(0); 109562306a36Sopenharmony_ci tmp = inw(ioaddr + WN0_ADDR_CONF); 109662306a36Sopenharmony_ci switch (cmd->base.port) { 109762306a36Sopenharmony_ci case PORT_TP: 109862306a36Sopenharmony_ci tmp &= ~(3<<14); 109962306a36Sopenharmony_ci dev->if_port = 0; 110062306a36Sopenharmony_ci break; 110162306a36Sopenharmony_ci case PORT_AUI: 110262306a36Sopenharmony_ci tmp |= (1<<14); 110362306a36Sopenharmony_ci dev->if_port = 1; 110462306a36Sopenharmony_ci break; 110562306a36Sopenharmony_ci case PORT_BNC: 110662306a36Sopenharmony_ci tmp |= (3<<14); 110762306a36Sopenharmony_ci dev->if_port = 3; 110862306a36Sopenharmony_ci break; 110962306a36Sopenharmony_ci default: 111062306a36Sopenharmony_ci return -EINVAL; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci outw(tmp, ioaddr + WN0_ADDR_CONF); 111462306a36Sopenharmony_ci if (dev->if_port == 3) { 111562306a36Sopenharmony_ci /* fire up the DC-DC convertor if BNC gets enabled */ 111662306a36Sopenharmony_ci tmp = inw(ioaddr + WN0_ADDR_CONF); 111762306a36Sopenharmony_ci if (tmp & (3 << 14)) { 111862306a36Sopenharmony_ci outw(StartCoax, ioaddr + EL3_CMD); 111962306a36Sopenharmony_ci udelay(800); 112062306a36Sopenharmony_ci } else 112162306a36Sopenharmony_ci return -EIO; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci EL3WINDOW(4); 112562306a36Sopenharmony_ci tmp = inw(ioaddr + WN4_NETDIAG); 112662306a36Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) 112762306a36Sopenharmony_ci tmp |= FD_ENABLE; 112862306a36Sopenharmony_ci else 112962306a36Sopenharmony_ci tmp &= ~FD_ENABLE; 113062306a36Sopenharmony_ci outw(tmp, ioaddr + WN4_NETDIAG); 113162306a36Sopenharmony_ci EL3WINDOW(1); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci return 0; 113462306a36Sopenharmony_ci} 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cistatic void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic int el3_get_link_ksettings(struct net_device *dev, 114262306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci spin_lock_irq(&lp->lock); 114762306a36Sopenharmony_ci el3_netdev_get_ecmd(dev, cmd); 114862306a36Sopenharmony_ci spin_unlock_irq(&lp->lock); 114962306a36Sopenharmony_ci return 0; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic int el3_set_link_ksettings(struct net_device *dev, 115362306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 115662306a36Sopenharmony_ci int ret; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci spin_lock_irq(&lp->lock); 115962306a36Sopenharmony_ci ret = el3_netdev_set_ecmd(dev, cmd); 116062306a36Sopenharmony_ci spin_unlock_irq(&lp->lock); 116162306a36Sopenharmony_ci return ret; 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic u32 el3_get_link(struct net_device *dev) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct el3_private *lp = netdev_priv(dev); 116762306a36Sopenharmony_ci u32 ret; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci spin_lock_irq(&lp->lock); 117062306a36Sopenharmony_ci ret = el3_link_ok(dev); 117162306a36Sopenharmony_ci spin_unlock_irq(&lp->lock); 117262306a36Sopenharmony_ci return ret; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic u32 el3_get_msglevel(struct net_device *dev) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci return el3_debug; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_cistatic void el3_set_msglevel(struct net_device *dev, u32 v) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci el3_debug = v; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops = { 118662306a36Sopenharmony_ci .get_drvinfo = el3_get_drvinfo, 118762306a36Sopenharmony_ci .get_link = el3_get_link, 118862306a36Sopenharmony_ci .get_msglevel = el3_get_msglevel, 118962306a36Sopenharmony_ci .set_msglevel = el3_set_msglevel, 119062306a36Sopenharmony_ci .get_link_ksettings = el3_get_link_ksettings, 119162306a36Sopenharmony_ci .set_link_ksettings = el3_set_link_ksettings, 119262306a36Sopenharmony_ci}; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic void 119562306a36Sopenharmony_ciel3_down(struct net_device *dev) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci int ioaddr = dev->base_addr; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci netif_stop_queue(dev); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci /* Turn off statistics ASAP. We update lp->stats below. */ 120262306a36Sopenharmony_ci outw(StatsDisable, ioaddr + EL3_CMD); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci /* Disable the receiver and transmitter. */ 120562306a36Sopenharmony_ci outw(RxDisable, ioaddr + EL3_CMD); 120662306a36Sopenharmony_ci outw(TxDisable, ioaddr + EL3_CMD); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (dev->if_port == 3) 120962306a36Sopenharmony_ci /* Turn off thinnet power. Green! */ 121062306a36Sopenharmony_ci outw(StopCoax, ioaddr + EL3_CMD); 121162306a36Sopenharmony_ci else if (dev->if_port == 0) { 121262306a36Sopenharmony_ci /* Disable link beat and jabber, if_port may change here next open(). */ 121362306a36Sopenharmony_ci EL3WINDOW(4); 121462306a36Sopenharmony_ci outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci update_stats(dev); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic void 122362306a36Sopenharmony_ciel3_up(struct net_device *dev) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci int i, sw_info, net_diag; 122662306a36Sopenharmony_ci int ioaddr = dev->base_addr; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* Activating the board required and does no harm otherwise */ 122962306a36Sopenharmony_ci outw(0x0001, ioaddr + 4); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* Set the IRQ line. */ 123262306a36Sopenharmony_ci outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci /* Set the station address in window 2 each time opened. */ 123562306a36Sopenharmony_ci EL3WINDOW(2); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci for (i = 0; i < 6; i++) 123862306a36Sopenharmony_ci outb(dev->dev_addr[i], ioaddr + i); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if ((dev->if_port & 0x03) == 3) /* BNC interface */ 124162306a36Sopenharmony_ci /* Start the thinnet transceiver. We should really wait 50ms...*/ 124262306a36Sopenharmony_ci outw(StartCoax, ioaddr + EL3_CMD); 124362306a36Sopenharmony_ci else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */ 124462306a36Sopenharmony_ci /* Combine secondary sw_info word (the adapter level) and primary 124562306a36Sopenharmony_ci sw_info word (duplex setting plus other useless bits) */ 124662306a36Sopenharmony_ci EL3WINDOW(0); 124762306a36Sopenharmony_ci sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) | 124862306a36Sopenharmony_ci (read_eeprom(ioaddr, 0x0d) & 0xBff0); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci EL3WINDOW(4); 125162306a36Sopenharmony_ci net_diag = inw(ioaddr + WN4_NETDIAG); 125262306a36Sopenharmony_ci net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */ 125362306a36Sopenharmony_ci pr_info("%s: ", dev->name); 125462306a36Sopenharmony_ci switch (dev->if_port & 0x0c) { 125562306a36Sopenharmony_ci case 12: 125662306a36Sopenharmony_ci /* force full-duplex mode if 3c5x9b */ 125762306a36Sopenharmony_ci if (sw_info & 0x000f) { 125862306a36Sopenharmony_ci pr_cont("Forcing 3c5x9b full-duplex mode"); 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci fallthrough; 126262306a36Sopenharmony_ci case 8: 126362306a36Sopenharmony_ci /* set full-duplex mode based on eeprom config setting */ 126462306a36Sopenharmony_ci if ((sw_info & 0x000f) && (sw_info & 0x8000)) { 126562306a36Sopenharmony_ci pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)"); 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci fallthrough; 126962306a36Sopenharmony_ci default: 127062306a36Sopenharmony_ci /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */ 127162306a36Sopenharmony_ci pr_cont("Setting 3c5x9/3c5x9B half-duplex mode"); 127262306a36Sopenharmony_ci net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */ 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci outw(net_diag, ioaddr + WN4_NETDIAG); 127662306a36Sopenharmony_ci pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info); 127762306a36Sopenharmony_ci if (el3_debug > 3) 127862306a36Sopenharmony_ci pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag); 127962306a36Sopenharmony_ci /* Enable link beat and jabber check. */ 128062306a36Sopenharmony_ci outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA); 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* Switch to the stats window, and clear all stats by reading. */ 128462306a36Sopenharmony_ci outw(StatsDisable, ioaddr + EL3_CMD); 128562306a36Sopenharmony_ci EL3WINDOW(6); 128662306a36Sopenharmony_ci for (i = 0; i < 9; i++) 128762306a36Sopenharmony_ci inb(ioaddr + i); 128862306a36Sopenharmony_ci inw(ioaddr + 10); 128962306a36Sopenharmony_ci inw(ioaddr + 12); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* Switch to register set 1 for normal use. */ 129262306a36Sopenharmony_ci EL3WINDOW(1); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* Accept b-case and phys addr only. */ 129562306a36Sopenharmony_ci outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); 129662306a36Sopenharmony_ci outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ 129962306a36Sopenharmony_ci outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ 130062306a36Sopenharmony_ci /* Allow status bits to be seen. */ 130162306a36Sopenharmony_ci outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); 130262306a36Sopenharmony_ci /* Ack all pending events, and set active indicator mask. */ 130362306a36Sopenharmony_ci outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, 130462306a36Sopenharmony_ci ioaddr + EL3_CMD); 130562306a36Sopenharmony_ci outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, 130662306a36Sopenharmony_ci ioaddr + EL3_CMD); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci netif_start_queue(dev); 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci/* Power Management support functions */ 131262306a36Sopenharmony_ci#ifdef CONFIG_PM 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cistatic int 131562306a36Sopenharmony_ciel3_suspend(struct device *pdev, pm_message_t state) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci unsigned long flags; 131862306a36Sopenharmony_ci struct net_device *dev; 131962306a36Sopenharmony_ci struct el3_private *lp; 132062306a36Sopenharmony_ci int ioaddr; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci dev = dev_get_drvdata(pdev); 132362306a36Sopenharmony_ci lp = netdev_priv(dev); 132462306a36Sopenharmony_ci ioaddr = dev->base_addr; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (netif_running(dev)) 132962306a36Sopenharmony_ci netif_device_detach(dev); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci el3_down(dev); 133262306a36Sopenharmony_ci outw(PowerDown, ioaddr + EL3_CMD); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 133562306a36Sopenharmony_ci return 0; 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic int 133962306a36Sopenharmony_ciel3_resume(struct device *pdev) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci unsigned long flags; 134262306a36Sopenharmony_ci struct net_device *dev; 134362306a36Sopenharmony_ci struct el3_private *lp; 134462306a36Sopenharmony_ci int ioaddr; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci dev = dev_get_drvdata(pdev); 134762306a36Sopenharmony_ci lp = netdev_priv(dev); 134862306a36Sopenharmony_ci ioaddr = dev->base_addr; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci outw(PowerUp, ioaddr + EL3_CMD); 135362306a36Sopenharmony_ci EL3WINDOW(0); 135462306a36Sopenharmony_ci el3_up(dev); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (netif_running(dev)) 135762306a36Sopenharmony_ci netif_device_attach(dev); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci#endif /* CONFIG_PM */ 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cimodule_param(debug,int, 0); 136662306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 136762306a36Sopenharmony_cimodule_param(max_interrupt_work, int, 0); 136862306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug level (0-6)"); 136962306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); 137062306a36Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); 137162306a36Sopenharmony_ci#ifdef CONFIG_PNP 137262306a36Sopenharmony_cimodule_param(nopnp, int, 0); 137362306a36Sopenharmony_ciMODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)"); 137462306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 137562306a36Sopenharmony_ciMODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver"); 137662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic int __init el3_init_module(void) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci int ret = 0; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci if (debug >= 0) 138362306a36Sopenharmony_ci el3_debug = debug; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci#ifdef CONFIG_PNP 138662306a36Sopenharmony_ci if (!nopnp) { 138762306a36Sopenharmony_ci ret = pnp_register_driver(&el3_pnp_driver); 138862306a36Sopenharmony_ci if (!ret) 138962306a36Sopenharmony_ci pnp_registered = 1; 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci#endif 139262306a36Sopenharmony_ci /* Select an open I/O location at 0x1*0 to do ISA contention select. */ 139362306a36Sopenharmony_ci /* Start with 0x110 to avoid some sound cards.*/ 139462306a36Sopenharmony_ci for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) { 139562306a36Sopenharmony_ci if (!request_region(id_port, 1, "3c509-control")) 139662306a36Sopenharmony_ci continue; 139762306a36Sopenharmony_ci outb(0x00, id_port); 139862306a36Sopenharmony_ci outb(0xff, id_port); 139962306a36Sopenharmony_ci if (inb(id_port) & 0x01) 140062306a36Sopenharmony_ci break; 140162306a36Sopenharmony_ci else 140262306a36Sopenharmony_ci release_region(id_port, 1); 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci if (id_port >= 0x200) { 140562306a36Sopenharmony_ci id_port = 0; 140662306a36Sopenharmony_ci pr_err("No I/O port available for 3c509 activation.\n"); 140762306a36Sopenharmony_ci } else { 140862306a36Sopenharmony_ci ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS); 140962306a36Sopenharmony_ci if (!ret) 141062306a36Sopenharmony_ci isa_registered = 1; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci#ifdef CONFIG_EISA 141362306a36Sopenharmony_ci ret = eisa_driver_register(&el3_eisa_driver); 141462306a36Sopenharmony_ci if (!ret) 141562306a36Sopenharmony_ci eisa_registered = 1; 141662306a36Sopenharmony_ci#endif 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci#ifdef CONFIG_PNP 141962306a36Sopenharmony_ci if (pnp_registered) 142062306a36Sopenharmony_ci ret = 0; 142162306a36Sopenharmony_ci#endif 142262306a36Sopenharmony_ci if (isa_registered) 142362306a36Sopenharmony_ci ret = 0; 142462306a36Sopenharmony_ci#ifdef CONFIG_EISA 142562306a36Sopenharmony_ci if (eisa_registered) 142662306a36Sopenharmony_ci ret = 0; 142762306a36Sopenharmony_ci#endif 142862306a36Sopenharmony_ci return ret; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic void __exit el3_cleanup_module(void) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci#ifdef CONFIG_PNP 143462306a36Sopenharmony_ci if (pnp_registered) 143562306a36Sopenharmony_ci pnp_unregister_driver(&el3_pnp_driver); 143662306a36Sopenharmony_ci#endif 143762306a36Sopenharmony_ci if (isa_registered) 143862306a36Sopenharmony_ci isa_unregister_driver(&el3_isa_driver); 143962306a36Sopenharmony_ci if (id_port) 144062306a36Sopenharmony_ci release_region(id_port, 1); 144162306a36Sopenharmony_ci#ifdef CONFIG_EISA 144262306a36Sopenharmony_ci if (eisa_registered) 144362306a36Sopenharmony_ci eisa_driver_unregister(&el3_eisa_driver); 144462306a36Sopenharmony_ci#endif 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cimodule_init (el3_init_module); 144862306a36Sopenharmony_cimodule_exit (el3_cleanup_module); 1449