11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Written 1992-94 by Donald Becker.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Copyright 1993 United States Government as represented by the
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Director, National Security Agency.  This software may be used and
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    distributed according to the terms of the GNU General Public License,
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    incorporated herein by reference.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    The author may be reached as becker@scyld.com, or C/O
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Scyld Computing Corporation
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	410 Severn Ave., Suite 210
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Annapolis MD 21403
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    This driver should work with the 3c503 and 3c503/16.  It should be used
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    in shared memory mode for best performance, although it may also work
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    in programmed-I/O mode.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Sources:
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    EtherLink II Technical Reference Manual,
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    EtherLink II/16 Technical Reference Manual Supplement,
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    The Crynwr 3c503 packet driver.
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Changelog:
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Paul Gortmaker	: add support for the 2nd 8kB of RAM on 16 bit cards.
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Paul Gortmaker	: multiple card support for module users.
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rjohnson@analogic.com : Fix up PIO interface for efficient operation.
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Jeff Garzik		: ethtool support
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_NAME	"3c503"
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_VERSION	"1.10a"
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRV_RELDATE	"11/17/2001"
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char version[] =
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "  Donald Becker (becker@scyld.com)\n";
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
52a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan#include <linux/interrupt.h>
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ethtool.h>
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "8390.h"
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "3c503.h"
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WRD_COUNT 4
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el2_pio_probe(struct net_device *dev);
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el2_probe1(struct net_device *dev, int ioaddr);
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of I/O addresses to be probed in PIO mode. */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int netcard_portlist[] __initdata =
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL2_IO_EXTENT	16
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el2_open(struct net_device *dev);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int el2_close(struct net_device *dev);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el2_reset_8390(struct net_device *dev);
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el2_init_card(struct net_device *dev);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el2_block_output(struct net_device *dev, int count,
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			     const unsigned char *buf, int start_page);
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el2_block_input(struct net_device *dev, int count, struct sk_buff *skb,
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   int ring_offset);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 int ring_page);
827282d491ecaee9883233a0e27283c4c79486279aJeff Garzikstatic const struct ethtool_ops netdev_ethtool_ops;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
846aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This routine probes for a memory-mapped 3c503 board by looking for
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   the "location register" at the end of the jumpered boot PROM space.
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   This works even if a PROM isn't there.
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   If the ethercard isn't found there is an optional probe for
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   ethercard jumpered to programmed-I/O mode.
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   */
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init do_el2_probe(struct net_device *dev)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int base_addr = dev->base_addr;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int irq = dev->irq;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (base_addr > 0x1ff)	/* Check a single specified location. */
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return el2_probe1(dev, base_addr);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else if (base_addr != 0)		/* Don't probe at all. */
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (addr = addrs; *addr; addr++) {
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *p = ioremap(*addr, 1);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned base_bits;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!p)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		continue;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	base_bits = readb(p);
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iounmap(p);
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = ffs(base_bits) - 1;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i == -1 || base_bits != (1 << i))
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    continue;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el2_probe1(dev, netcard_portlist[i]) == 0)
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    return 0;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq = irq;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if ! defined(no_probe_nonshared_memory)
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return el2_pio_probe(dev);
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENODEV;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*  Try all of the locations that aren't obviously empty.  This touches
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    a lot of locations, and is much riskier than the code above. */
1286aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzikstatic int __init
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_pio_probe(struct net_device *dev)
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int base_addr = dev->base_addr;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int irq = dev->irq;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (base_addr > 0x1ff)	/* Check a single specified location. */
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return el2_probe1(dev, base_addr);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else if (base_addr != 0)	/* Don't probe at all. */
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0; netcard_portlist[i]; i++) {
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (el2_probe1(dev, netcard_portlist[i]) == 0)
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    return 0;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq = irq;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENODEV;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct net_device * __init el2_probe(int unit)
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
152055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox	struct net_device *dev = alloc_eip_netdev();
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENOMEM);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sprintf(dev->name, "eth%d", unit);
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netdev_boot_setup_check(dev);
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = do_el2_probe(dev);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return dev;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev(dev);
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ERR_PTR(err);
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17153cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemmingerstatic const struct net_device_ops el2_netdev_ops = {
17253cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_open		= el2_open,
17353cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_stop		= el2_close,
17453cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger
17553cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_start_xmit		= eip_start_xmit,
17653cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_tx_timeout		= eip_tx_timeout,
17753cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_get_stats		= eip_get_stats,
178afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= eip_set_multicast_list,
17953cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
180fe96aaa14f553f0eb7af0e3502563a5400c65257Stephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
18153cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
18253cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger#ifdef CONFIG_NET_POLL_CONTROLLER
18353cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger	.ndo_poll_controller 	= eip_poll,
18453cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger#endif
18553cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger};
18653cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Probe for the Etherlink II card at I/O port base IOADDR,
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   returning non-zero on success.  If found, set the station
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   address and memory parameters in DEVICE. */
1906aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzikstatic int __init
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_probe1(struct net_device *dev, int ioaddr)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i, iobase_reg, membase_reg, saved_406, wordlength, retval;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    static unsigned version_printed;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned long vendor_id;
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!request_region(ioaddr, EL2_IO_EXTENT, DRV_NAME))
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EBUSY;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!request_region(ioaddr + 0x400, 8, DRV_NAME)) {
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = -EBUSY;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Reset and/or avoid any lurking NE2000 */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (inb(ioaddr + 0x408) == 0xff) {
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    	mdelay(1);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = -ENODEV;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out1;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* We verify that it's a 3C503 board by checking the first three octets
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       of its ethernet address. */
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    iobase_reg = inb(ioaddr+0x403);
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    membase_reg = inb(ioaddr+0x404);
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* ASIC location registers should be 0 or have only a single bit set. */
2178e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches    if ((iobase_reg  & (iobase_reg - 1)) ||
2188e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches	(membase_reg & (membase_reg - 1))) {
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = -ENODEV;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out1;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    saved_406 = inb_p(ioaddr + 0x406);
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ECNTRL_THIN, ioaddr + 0x406);
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Map the station addr PROM into the lower I/O ports. We now check
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       for both the old and new 3Com prefix */
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Restore the register we frobbed. */
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(saved_406, ioaddr + 0x406);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = -ENODEV;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out1;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (ei_debug  &&  version_printed++ == 0)
237646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_debug("%s", version);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->base_addr = ioaddr;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov    pr_info("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Retrieve and print the ethernet address. */
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0; i < 6; i++)
2450795af5729b18218767fab27c44b1384f72dc9adJoe Perches	dev->dev_addr[i] = inb(ioaddr + i);
246646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov    pr_cont("%pM", dev->dev_addr);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Map the 8390 back into the window. */
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(ECNTRL_THIN, ioaddr + 0x406);
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Check for EL2/16 as described in tech. man. */
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(0, ioaddr + EN0_DCFG);
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(E8390_PAGE2, ioaddr + E8390_CMD);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Probe for, turn on and clear the board's shared memory. */
259646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov    if (ei_debug > 2)
260646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_cont(" memory jumpers %2.2x ", membase_reg);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(EGACFR_NORM, ioaddr + 0x405);	/* Enable RAM */
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* This should be probed for (or set via an ioctl()) at run-time.
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       Right now we use a sleazy hack to pass in the interface number
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       at boot-time via the low bits of the mem_end field.  That value is
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       unused, and the low bits would be discarded even if it was used. */
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if defined(EI8390_THICK) || defined(EL2_AUI)
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ei_status.interface_num = 1;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ei_status.interface_num = dev->mem_end & 0xf;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
272646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov    pr_cont(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if ((membase_reg & 0xf0) == 0) {
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->mem_start = 0;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.name = "3c503-PIO";
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.mem = NULL;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    ((membase_reg & 0xA0) ? 0x4000 : 0);
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.mem = ioremap(dev->mem_start, EL2_MEMSIZE);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef EL2MEMTEST
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* This has never found an error, but someone might care.
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Note that it only tests the 2nd 8kB on 16kB 3c503/16
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   cards between card addr. 0x2000 and 0x3fff. */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{			/* Check the card's memory. */
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    void __iomem *mem_base = ei_status.mem;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    unsigned int test_val = 0xbbadf00d;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    writel(0xba5eba5e, mem_base);
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writel(test_val, mem_base + i);
2948e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		if (readl(mem_base) != 0xba5eba5e ||
2958e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		    readl(mem_base + i) != test_val) {
296646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		    pr_warning("3c503: memory failure or memory address conflict.\n");
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    dev->mem_start = 0;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    ei_status.name = "3c503-PIO";
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    iounmap(mem_base);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    ei_status.mem = NULL;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    break;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		test_val += 0x55555555;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		writel(0, mem_base + i);
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    }
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif  /* EL2MEMTEST */
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->mem_start)
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = dev->mem_start + EL2_MEMSIZE;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (wordlength) {	/* No Tx pages to skip over to get to Rx */
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.priv = 0;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.name = "3c503/16";
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.priv = TX_PAGES * 256;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ei_status.name = "3c503";
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /*
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Divide up the memory on the card. This is the same regardless of
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	whether shared-mem or PIO is used. For 16 bit cards (16kB RAM),
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	we use the entire 8k of bank1 for an Rx ring. We only use 3k
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	of the bank0 for 2 full size Tx packet slots. For 8 bit cards,
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5kB for an Rx ring.  */
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (wordlength) {
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = EL2_MB0_START_PG;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = EL2_MB1_START_PG;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = EL2_MB1_START_PG;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Finish setting the board's parameters. */
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ei_status.stop_page = EL2_MB1_STOP_PG;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ei_status.word16 = wordlength;
340c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches    ei_status.reset_8390 = el2_reset_8390;
341c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches    ei_status.get_8390_hdr = el2_get_8390_hdr;
342c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches    ei_status.block_input = el2_block_input;
343c061b18df0f1fe3f50fe451dbbdc9ede3c19701aJoe Perches    ei_status.block_output = el2_block_output;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->irq == 2)
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq = 9;
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else if (dev->irq > 5 && dev->irq != 9) {
348646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_warning("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       dev->irq);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq = 0;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ei_status.saved_irq = dev->irq;
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35553cdd28961f979e4494c85e9ace8c3b51ae39a45Stephen Hemminger    dev->netdev_ops = &el2_netdev_ops;
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->ethtool_ops = &netdev_ethtool_ops;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
358b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>    retval = register_netdev(dev);
359b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>    if (retval)
360b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>	goto out1;
361b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->mem_start)
363646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_info("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->name, ei_status.name, (wordlength+1)<<3,
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_start, dev->mem_end-1);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    {
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.tx_start_page = EL2_MB1_START_PG;
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
371646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_info("%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n",
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       dev->name, ei_status.name, (wordlength+1)<<3);
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    release_region(ioaddr + 0x400, 8);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout1:
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    release_region(ioaddr + 0x400, 8);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    release_region(ioaddr, EL2_IO_EXTENT);
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return retval;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3826aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
383b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchingsstatic irqreturn_t el2_probe_interrupt(int irq, void *seen)
384b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings{
385b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings	*(bool *)seen = true;
386b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings	return IRQ_HANDLED;
387b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings}
388b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_open(struct net_device *dev)
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
392ab08999d6029bb2c79c16be5405d63d2bedbdfearoel kluin    int retval;
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->irq < 2) {
395b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches	static const int irqlist[] = {5, 9, 3, 4, 0};
396b6bc765067ece933cc3dc7f5e95665a89100b1d5Joe Perches	const int *irqp = irqlist;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(EGACFR_NORM, E33G_GACFR);	/* Enable RAM and interrupts. */
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
400b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		bool seen;
401b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings
402b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		retval = request_irq(*irqp, el2_probe_interrupt, 0,
403b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings				     dev->name, &seen);
404b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		if (retval == -EBUSY)
405b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings			continue;
406b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		if (retval < 0)
407b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings			goto err_disable;
408b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Twinkle the interrupt, and check if it's seen. */
410b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		seen = false;
411b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		smp_wmb();
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb_p(0x00, E33G_IDCFR);
414b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		msleep(1);
4151ffde03d2aa112750468cff07efc9e0a504517ddOndrej Zary		free_irq(*irqp, &seen);
416b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		if (!seen)
417b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings			continue;
418b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings
419b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		retval = request_irq(dev->irq = *irqp, eip_interrupt, 0,
420b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings				     dev->name, dev);
421b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		if (retval == -EBUSY)
422b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings			continue;
423b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings		if (retval < 0)
424b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings			goto err_disable;
4251ffde03d2aa112750468cff07efc9e0a504517ddOndrej Zary		break;
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (*++irqp);
427b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (*irqp == 0) {
429b0cf4dfb7cd21556efd9a6a67edcba0840b4d98dBen Hutchings	err_disable:
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
431ab08999d6029bb2c79c16be5405d63d2bedbdfearoel kluin	    return -EAGAIN;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
434055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox	if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    return retval;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    el2_init_card(dev);
440055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox    eip_open(dev);
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_close(struct net_device *dev)
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    free_irq(dev->irq, dev);
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->irq = ei_status.saved_irq;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox    eip_close(dev);
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This is called whenever we have a unrecoverable failure:
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       transmit timeout
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       Bad ring buffer packet header
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_reset_8390(struct net_device *dev)
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (ei_debug > 1) {
463646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_debug("%s: Resetting the 3c503 board...", dev->name);
464646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_cont(" %#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ei_status.txing = 0;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    el2_init_card(dev);
471646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov    if (ei_debug > 1)
472646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov	pr_cont("done\n");
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Initialize the 3c503 GA registers after a reset. */
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_init_card(struct net_device *dev)
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Unmap the station PROM and select the DIX or BNC connector. */
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Set ASIC copy of rx's first and last+1 buffer pages */
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* These must be the same as in the 8390. */
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(ei_status.rx_start_page, E33G_STARTPG);
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(ei_status.stop_page,  E33G_STOPPG);
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Point the vector pointer registers somewhere ?harmless?. */
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(0xff, E33G_VP2);	/* Point at the ROM restart location 0xffff0 */
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(0xff, E33G_VP1);
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(0x00, E33G_VP0);
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Turn off all interrupts until we're opened. */
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(0x00,  dev->base_addr + EN0_IMR);
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Enable IRQs iff started. */
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(EGACFR_NORM, E33G_GACFR);
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Set the interrupt line. */
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p((WRD_COUNT << 1), E33G_DRQCNT);	/* Set burst size to 8 */
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(0x20, E33G_DMAAH);	/* Put a valid addr in the GA DMA */
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(0x00, E33G_DMAAL);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;			/* We always succeed */
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Either use the shared memory (if enabled on the board) or put the packet
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * out through the ASIC FIFO.
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_block_output(struct net_device *dev, int count,
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 const unsigned char *buf, int start_page)
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned short int *wrd;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int boguscount;		/* timeout counter */
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned short word;	/* temporary for better machine code */
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    void __iomem *base = ei_status.mem;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (ei_status.word16)      /* Tx packets go into bank 0 on EL2/16 card */
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(EGACFR_NORM, E33G_GACFR);
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (base) {	/* Shared memory transfer */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_toio(base + ((start_page - ei_status.tx_start_page) << 8),
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buf, count);
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(EGACFR_NORM, E33G_GACFR);	/* Back to bank1 in case on bank0 */
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  No shared memory, put the packet out the other way.
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Set up then start the internal memory transfer to Tx Start Page
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    word = (unsigned short)start_page;
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(word&0xFF, E33G_DMAAH);
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(word>>8, E33G_DMAAL);
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   | ECNTRL_START, E33G_CNTRL);
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Here I am going to write data to the FIFO as quickly as possible.
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Note that E33G_FIFOH is defined incorrectly. It is really
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  E33G_FIFOL, the lowest port address for both the byte and
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  word write. Variable 'count' is NOT checked. Caller must supply a
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  valid count. Note that I may write a harmless extra byte to the
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  8390 if the byte-count was not even.
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wrd = (unsigned short int *) buf;
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    count  = (count + 1) >> 1;
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for(;;)
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    {
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        boguscount = 0x1000;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            if(!boguscount--)
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            {
558646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov                pr_notice("%s: FIFO blocked in el2_block_output.\n", dev->name);
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                el2_reset_8390(dev);
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                goto blocked;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            }
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if(count > WRD_COUNT)
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            outsw(E33G_FIFOH, wrd, WRD_COUNT);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            wrd   += WRD_COUNT;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            count -= WRD_COUNT;
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        else
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            outsw(E33G_FIFOH, wrd, count);
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            break;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    blocked:;
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read the 4 byte, page aligned 8390 specific header. */
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int boguscount;
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    void __iomem *base = ei_status.mem;
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned short word;
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (base) {       /* Use the shared memory. */
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void __iomem *hdr_start = base + ((ring_page - EL2_MB1_START_PG)<<8);
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hdr->count = le16_to_cpu(hdr->count);
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  No shared memory, use programmed I/O.
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    word = (unsigned short)ring_page;
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(word&0xFF, E33G_DMAAH);
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(word>>8, E33G_DMAAL);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   | ECNTRL_START, E33G_CNTRL);
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    boguscount = 0x1000;
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    {
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if(!boguscount--)
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
609646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov            pr_notice("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            memset(hdr, 0x00, sizeof(struct e8390_pkt_hdr));
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            el2_reset_8390(dev);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            goto blocked;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    insw(E33G_FIFOH, hdr, (sizeof(struct e8390_pkt_hdr))>> 1);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    blocked:;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsel2_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int boguscount = 0;
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    void __iomem *base = ei_status.mem;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned short int *buf;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned short word;
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Maybe enable shared memory just be to be safe... nahh.*/
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (base) {	/* Use the shared memory. */
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ring_offset -= (EL2_MB1_START_PG<<8);
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ring_offset + count > EL2_MEMSIZE) {
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    /* We must wrap the input move. */
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    int semi_count = EL2_MEMSIZE - ring_offset;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    memcpy_fromio(skb->data, base + ring_offset, semi_count);
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    count -= semi_count;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    memcpy_fromio(skb->data + semi_count, base + ei_status.priv, count);
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6394ec031166f6a466a443f462e567f7551096b1741Al Viro		memcpy_fromio(skb->data, base + ring_offset, count);
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  No shared memory, use programmed I/O.
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    word = (unsigned short) ring_offset;
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(word>>8, E33G_DMAAH);
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb(word&0xFF, E33G_DMAAL);
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   | ECNTRL_START, E33G_CNTRL);
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Here I also try to get data as fast as possible. I am betting that I
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  can read one extra byte without clobbering anything in the kernel because
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  this would only occur on an odd byte-count and allocation of skb->data
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  is word-aligned. Variable 'count' is NOT checked. Caller must check
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  for a valid count.
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  [This is currently quite safe.... but if one day the 3c503 explodes
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   you know where to come looking ;)]
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    buf =  (unsigned short int *) skb->data;
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    count =  (count + 1) >> 1;
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for(;;)
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    {
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        boguscount = 0x1000;
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            if(!boguscount--)
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            {
673646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov                pr_notice("%s: FIFO blocked in el2_block_input.\n", dev->name);
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                el2_reset_8390(dev);
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                goto blocked;
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            }
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if(count > WRD_COUNT)
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            insw(E33G_FIFOH, buf, WRD_COUNT);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            buf   += WRD_COUNT;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            count -= WRD_COUNT;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        else
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            insw(E33G_FIFOH, buf, count);
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds            break;
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    blocked:;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void netdev_get_drvinfo(struct net_device *dev,
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       struct ethtool_drvinfo *info)
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy(info->driver, DRV_NAME);
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy(info->version, DRV_VERSION);
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr);
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7037282d491ecaee9883233a0e27283c4c79486279aJeff Garzikstatic const struct ethtool_ops netdev_ethtool_ops = {
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.get_drvinfo		= netdev_get_drvinfo,
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_EL2_CARDS	4	/* Max number of EL2 cards per module */
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_el2[MAX_EL2_CARDS];
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io[MAX_EL2_CARDS];
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[MAX_EL2_CARDS];
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int xcvr[MAX_EL2_CARDS];	/* choose int. or ext. xcvr */
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0);
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(xcvr, int, NULL, 0);
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(io, "I/O base address(es)");
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(xcvr, "transceiver(s) (0=internal, 1=external)");
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("3Com ISA EtherLink II, II/16 (3c503, 3c503/16) driver");
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This is set up so that only a single autoprobe takes place per call.
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsISA device autoprobes on a running machine are not recommended. */
72596e672c79fb114ec38a868dc864e743205c24332Randy Dunlapint __init
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsinit_module(void)
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev, found = 0;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (io[this_dev] == 0)  {
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (this_dev != 0) break; /* only autoprobe 1st one */
734646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov			pr_notice("3c503.c: Presently autoprobing (not recommended) for a single card.\n");
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
736055e5110ae0c0c1176a75b78d789294f2ff2f7afAlan Cox		dev = alloc_eip_netdev();
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!dev)
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq[this_dev];
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->base_addr = io[this_dev];
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (do_el2_probe(dev) == 0) {
743b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			dev_el2[found++] = dev;
744b1fc5505e0dbcc3fd7c75bfe6bee39ec50080963<herbert@gondor.apana.org.au>			continue;
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
747646cdb32831eebe8c2f742c293d0d266326854d9Alexander Beregalov		pr_warning("3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (found)
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENXIO;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
75564916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenkostatic void cleanup_card(struct net_device *dev)
75664916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko{
75764916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	/* NB: el2_close() handles free_irq */
75864916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	release_region(dev->base_addr, EL2_IO_EXTENT);
75964916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko	if (ei_status.mem)
76064916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko		iounmap(ei_status.mem);
76164916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko}
76264916f1ebe93592153c72bcdb189a31e4d40049aDenis Vlasenko
763afc8eb46c0ea2cab8bc28713b2e0614f015a7516Al Virovoid __exit
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscleanup_module(void)
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int this_dev;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct net_device *dev = dev_el2[this_dev];
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dev) {
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			unregister_netdev(dev);
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cleanup_card(dev);
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_netdev(dev);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* MODULE */
778