git diff 64edfa65062dc4509ba75978116b2f6d392346f5 64edfa65062dc4509ba75978116b2f6d392346f5^ drivers/net/ethernet diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c new file mode 100644 index 000000000000..2227c83a4862 --- /dev/null +++ b/drivers/net/ethernet/3com/3c515.c @@ -0,0 +1,1566 @@ +/* + Written 1997-1998 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + + 2000/2/2- Added support for kernel-level ISAPnP + by Stephen Frost and Alessandro Zummo + Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox. + + 2001/11/17 - Added ethtool support (jgarzik) + + 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk) + +*/ + +#define DRV_NAME "3c515" + +#define CORKSCREW 1 + +/* "Knobs" that adjust features and parameters. */ +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1512 effectively disables this feature. */ +static int rx_copybreak = 200; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Enable the automatic media selection code -- usually set. */ +#define AUTOMEDIA 1 + +/* Allow the use of fragment bus master transfers instead of only + programmed-I/O for Vortex cards. Full-bus-master transfers are always + enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, + the feature may be turned on using 'options'. */ +#define VORTEX_BUS_MASTER + +/* A few values that may be tweaked. */ +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 16 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define NEW_MULTICAST +#include + +#define MAX_UNITS 8 + +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver"); +MODULE_LICENSE("GPL"); + +/* "Knobs" for adjusting internal parameters. */ +/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ +#define DRIVER_DEBUG 1 +/* Some values here only for performance evaluation and path-coverage + debugging. */ +static int rx_nocopy, rx_copy, queued_packet; + +/* Number of times to check to see if the Tx FIFO has space, used in some + limited cases. */ +#define WAIT_TX_AVAIL 200 + +/* Operational parameter that usually are not changed. */ +#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */ + +/* The size here is somewhat misleading: the Corkscrew also uses the ISA + aliased registers at +0x400. + */ +#define CORKSCREW_TOTAL_SIZE 0x20 + +#ifdef DRIVER_DEBUG +static int corkscrew_debug = DRIVER_DEBUG; +#else +static int corkscrew_debug = 1; +#endif + +#define CORKSCREW_ID 10 + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, +3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout, +it's not practical to integrate this driver with the other EtherLink drivers. + +II. Board-specific settings + +The Corkscrew has an EEPROM for configuration, but no special settings are +needed for Linux. + +III. Driver operation + +The 3c515 series use an interface that's very similar to the 3c900 "Boomerang" +PCI cards, with the bus master interface extensively modified to work with +the ISA bus. + +The card is capable of full-bus-master transfers with separate +lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, +DEC Tulip and Intel Speedo3. + +This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate +receive buffer. This scheme allocates full-sized skbuffs as receive +buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is +chosen to trade-off the memory wasted by passing the full-sized skbuff to +the queue layer for all frames vs. the copying cost of copying a frame to a +correctly-sized skbuff. + + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the netif +layer. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +IV. Notes + +Thanks to Terry Murphy of 3Com for providing documentation and a development +board. + +The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com +project names. I use these names to eliminate confusion -- 3Com product +numbers and names are very similar and often confused. + +The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! +This driver only supports ethernet frames because of the recent MTU limit +of 1.5K, but the changes to support 4.5K are minimal. +*/ + +/* Operational definitions. + These are not used by other compilation units and thus are not + exported in a ".h" file. + + First the windows. There are eight register windows, with the command + and status registers available in each. + */ +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. + Note that 11 parameters bits was fine for ethernet, but the new chips + can handle FDDI length frames (~4500 octets) and now parameters count + 32-bit 'Dwords' rather than octets. */ + +enum corkscrew_cmd { + TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11, + RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11, + UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2, + DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11, + TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11, + AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11, + SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11, + SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11, + StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11, + StatsDisable = 22 << 11, StopCoax = 23 << 11, +}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 +}; + +/* Bits in the general status register. */ +enum corkscrew_status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, + DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10, + DMAInProgress = 1 << 11, /* DMA controller is still busy. */ + CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */ +}; + +/* Register window 1 offsets, the window used in normal operation. + On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ +enum Window1 { + TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, + RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B, + TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ +}; +enum Window0 { + Wn0IRQ = 0x08, +#if defined(CORKSCREW) + Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */ + Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */ +#else + Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ + Wn0EepromData = 12, /* Window 0: EEPROM results register. */ +#endif +}; +enum Win0_EEPROM_bits { + EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, + EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ + EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ +}; + +/* EEPROM locations. */ +enum eeprom_offset { + PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3, + EtherLink3ID = 7, +}; + +enum Window3 { /* Window 3: MAC/config bits. */ + Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8, +}; +enum wn3_config { + Ram_size = 7, + Ram_width = 8, + Ram_speed = 0x30, + Rom_size = 0xc0, + Ram_split_shift = 16, + Ram_split = 3 << Ram_split_shift, + Xcvr_shift = 20, + Xcvr = 7 << Xcvr_shift, + Autoselect = 0x1000000, +}; + +enum Window4 { + Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ +}; +enum Win4_Media_bits { + Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ + Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ + Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */ + Media_LnkBeat = 0x0800, +}; +enum Window7 { /* Window 7: Bus Master control. */ + Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, +}; + +/* Boomerang-style bus master control registers. Note ISA aliases! */ +enum MasterCtrl { + PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = + 0x40c, + TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, +}; + +/* The Rx and Tx descriptor lists. + Caution Alpha hackers: these types are 32 bits! Note also the 8 byte + alignment contraint on tx_ring[] and rx_ring[]. */ +struct boom_rx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; + +/* Values for the Rx status entry. */ +enum rx_desc_status { + RxDComplete = 0x00008000, RxDError = 0x4000, + /* See boomerang_rx() for actual error bits */ +}; + +struct boom_tx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; + +struct corkscrew_private { + const char *product_name; + struct list_head list; + struct net_device *our_dev; + /* The Rx and Tx rings are here to keep them quad-word-aligned. */ + struct boom_rx_desc rx_ring[RX_RING_SIZE]; + struct boom_tx_desc tx_ring[TX_RING_SIZE]; + /* The addresses of transmit- and receive-in-place skbuffs. */ + struct sk_buff *rx_skbuff[RX_RING_SIZE]; + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */ + struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ + struct timer_list timer; /* Media selection timer. */ + int capabilities ; /* Adapter capabilities word. */ + int options; /* User-settable misc. driver options. */ + int last_rx_packets; /* For media autoselection. */ + unsigned int available_media:8, /* From Wn3_Options */ + media_override:3, /* Passed-in media type. */ + default_media:3, /* Read from the EEPROM. */ + full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */ + full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */ + tx_full:1; + spinlock_t lock; + struct device *dev; +}; + +/* The action to take with a media selection timer tick. + Note that we deviate from the 3Com order by checking 10base2 before AUI. + */ +enum xcvr_types { + XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, + XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8, +}; + +static struct media_table { + char *name; + unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ + mask:8, /* The transceiver-present bit in Wn3_Config. */ + next:8; /* The media type to try next. */ + short wait; /* Time before we check media status. */ +} media_tbl[] = { + { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 }, + { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}, + { "undefined", 0, 0x80, XCVR_10baseT, 10000}, + { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}, + { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10}, + { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}, + { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ}, + { "undefined", 0, 0x01, XCVR_10baseT, 10000}, + { "Default", 0, 0xFF, XCVR_10baseT, 10000}, +}; + +#ifdef __ISAPNP__ +static struct isapnp_device_id corkscrew_isapnp_adapters[] = { + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051), + (long) "3Com Fast EtherLink ISA" }, + { } /* terminate list */ +}; + +MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters); + +static int nopnp; +#endif /* __ISAPNP__ */ + +static struct net_device *corkscrew_scan(int unit); +static int corkscrew_setup(struct net_device *dev, int ioaddr, + struct pnp_dev *idev, int card_number); +static int corkscrew_open(struct net_device *dev); +static void corkscrew_timer(struct timer_list *t); +static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static int corkscrew_rx(struct net_device *dev); +static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue); +static int boomerang_rx(struct net_device *dev); +static irqreturn_t corkscrew_interrupt(int irq, void *dev_id); +static int corkscrew_close(struct net_device *dev); +static void update_stats(int addr, struct net_device *dev); +static struct net_device_stats *corkscrew_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static const struct ethtool_ops netdev_ethtool_ops; + + +/* + Unfortunately maximizing the shared code between the integrated and + module version of the driver results in a complicated set of initialization + procedures. + init_module() -- modules / tc59x_init() -- built-in + The wrappers for corkscrew_scan() + corkscrew_scan() The common routine that scans for PCI and EISA cards + corkscrew_found_device() Allocate a device structure when we find a card. + Different versions exist for modules and built-in. + corkscrew_probe1() Fill in the device structure -- this is separated + so that the modules code can put it in dev->init. +*/ +/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ +/* Note: this is the only limit on the number of cards supported!! */ +static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, }; + +#ifdef MODULE +static int debug = -1; + +module_param(debug, int, 0); +module_param_array(options, int, NULL, 0); +module_param(rx_copybreak, int, 0); +module_param(max_interrupt_work, int, 0); +MODULE_PARM_DESC(debug, "3c515 debug level (0-6)"); +MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering"); +MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt"); + +/* A list of all installed Vortex devices, for removing the driver module. */ +/* we will need locking (and refcounting) if we ever use it for more */ +static LIST_HEAD(root_corkscrew_dev); + +static int corkscrew_init_module(void) +{ + int found = 0; + if (debug >= 0) + corkscrew_debug = debug; + while (corkscrew_scan(-1)) + found++; + return found ? 0 : -ENODEV; +} +module_init(corkscrew_init_module); + +#else +struct net_device *tc515_probe(int unit) +{ + struct net_device *dev = corkscrew_scan(unit); + + if (!dev) + return ERR_PTR(-ENODEV); + + return dev; +} +#endif /* not MODULE */ + +static int check_device(unsigned ioaddr) +{ + int timer; + + if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515")) + return 0; + /* Check the resource configuration for a matching ioaddr. */ + if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) { + release_region(ioaddr, CORKSCREW_TOTAL_SIZE); + return 0; + } + /* Verify by reading the device ID from the EEPROM. */ + outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 4; timer >= 0; timer--) { + udelay(162); + if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) + break; + } + if (inw(ioaddr + Wn0EepromData) != 0x6d50) { + release_region(ioaddr, CORKSCREW_TOTAL_SIZE); + return 0; + } + return 1; +} + +static void cleanup_card(struct net_device *dev) +{ + struct corkscrew_private *vp = netdev_priv(dev); + list_del_init(&vp->list); + if (dev->dma) + free_dma(dev->dma); + outw(TotalReset, dev->base_addr + EL3_CMD); + release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE); + if (vp->dev) + pnp_device_detach(to_pnp_dev(vp->dev)); +} + +static struct net_device *corkscrew_scan(int unit) +{ + struct net_device *dev; + static int cards_found = 0; + static int ioaddr; + int err; +#ifdef __ISAPNP__ + short i; + static int pnp_cards; +#endif + + dev = alloc_etherdev(sizeof(struct corkscrew_private)); + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + } + +#ifdef __ISAPNP__ + if(nopnp == 1) + goto no_pnp; + for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) { + struct pnp_dev *idev = NULL; + int irq; + while((idev = pnp_find_dev(NULL, + corkscrew_isapnp_adapters[i].vendor, + corkscrew_isapnp_adapters[i].function, + idev))) { + + if (pnp_device_attach(idev) < 0) + continue; + if (pnp_activate_dev(idev) < 0) { + pr_warn("pnp activate failed (out of resources?)\n"); + pnp_device_detach(idev); + continue; + } + if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) { + pnp_device_detach(idev); + continue; + } + ioaddr = pnp_port_start(idev, 0); + irq = pnp_irq(idev, 0); + if (!check_device(ioaddr)) { + pnp_device_detach(idev); + continue; + } + if(corkscrew_debug) + pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n", + (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq); + pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", + inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); + /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */ + SET_NETDEV_DEV(dev, &idev->dev); + pnp_cards++; + err = corkscrew_setup(dev, ioaddr, idev, cards_found++); + if (!err) + return dev; + cleanup_card(dev); + } + } +no_pnp: +#endif /* __ISAPNP__ */ + + /* Check all locations on the ISA bus -- evil! */ + for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { + if (!check_device(ioaddr)) + continue; + + pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", + inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); + err = corkscrew_setup(dev, ioaddr, NULL, cards_found++); + if (!err) + return dev; + cleanup_card(dev); + } + free_netdev(dev); + return NULL; +} + + +static const struct net_device_ops netdev_ops = { + .ndo_open = corkscrew_open, + .ndo_stop = corkscrew_close, + .ndo_start_xmit = corkscrew_start_xmit, + .ndo_tx_timeout = corkscrew_timeout, + .ndo_get_stats = corkscrew_get_stats, + .ndo_set_rx_mode = set_rx_mode, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + + +static int corkscrew_setup(struct net_device *dev, int ioaddr, + struct pnp_dev *idev, int card_number) +{ + struct corkscrew_private *vp = netdev_priv(dev); + unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ + __be16 addr[ETH_ALEN / 2]; + int i; + int irq; + +#ifdef __ISAPNP__ + if (idev) { + irq = pnp_irq(idev, 0); + vp->dev = &idev->dev; + } else { + irq = inw(ioaddr + 0x2002) & 15; + } +#else + irq = inw(ioaddr + 0x2002) & 15; +#endif + + dev->base_addr = ioaddr; + dev->irq = irq; + dev->dma = inw(ioaddr + 0x2000) & 7; + vp->product_name = "3c515"; + vp->options = dev->mem_start; + vp->our_dev = dev; + + if (!vp->options) { + if (card_number >= MAX_UNITS) + vp->options = -1; + else + vp->options = options[card_number]; + } + + if (vp->options >= 0) { + vp->media_override = vp->options & 7; + if (vp->media_override == 2) + vp->media_override = 0; + vp->full_duplex = (vp->options & 8) ? 1 : 0; + vp->bus_master = (vp->options & 16) ? 1 : 0; + } else { + vp->media_override = 7; + vp->full_duplex = 0; + vp->bus_master = 0; + } +#ifdef MODULE + list_add(&vp->list, &root_corkscrew_dev); +#endif + + pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr); + + spin_lock_init(&vp->lock); + + timer_setup(&vp->timer, corkscrew_timer, 0); + + /* Read the station address from the EEPROM. */ + EL3WINDOW(0); + for (i = 0; i < 0x18; i++) { + int timer; + outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 4; timer >= 0; timer--) { + udelay(162); + if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) + break; + } + eeprom[i] = inw(ioaddr + Wn0EepromData); + checksum ^= eeprom[i]; + if (i < 3) + addr[i] = htons(eeprom[i]); + } + eth_hw_addr_set(dev, (u8 *)addr); + checksum = (checksum ^ (checksum >> 8)) & 0xff; + if (checksum != 0x00) + pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum); + pr_cont(" %pM", dev->dev_addr); + if (eeprom[16] == 0x11c7) { /* Corkscrew */ + if (request_dma(dev->dma, "3c515")) { + pr_cont(", DMA %d allocation failed", dev->dma); + dev->dma = 0; + } else + pr_cont(", DMA %d", dev->dma); + } + pr_cont(", IRQ %d\n", dev->irq); + /* Tell them about an invalid IRQ. */ + if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15)) + pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n"); + + { + static const char * const ram_split[] = { + "5:3", "3:1", "1:1", "3:5" + }; + __u32 config; + EL3WINDOW(3); + vp->available_media = inw(ioaddr + Wn3_Options); + config = inl(ioaddr + Wn3_Config); + if (corkscrew_debug > 1) + pr_info(" Internal config register is %4.4x, transceivers %#x.\n", + config, inw(ioaddr + Wn3_Options)); + pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", + 8 << config & Ram_size, + config & Ram_width ? "word" : "byte", + ram_split[(config & Ram_split) >> Ram_split_shift], + config & Autoselect ? "autoselect/" : "", + media_tbl[(config & Xcvr) >> Xcvr_shift].name); + vp->default_media = (config & Xcvr) >> Xcvr_shift; + vp->autoselect = config & Autoselect ? 1 : 0; + dev->if_port = vp->default_media; + } + if (vp->media_override != 7) { + pr_info(" Media override to transceiver type %d (%s).\n", + vp->media_override, + media_tbl[vp->media_override].name); + dev->if_port = vp->media_override; + } + + vp->capabilities = eeprom[16]; + vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; + /* Rx is broken at 10mbps, so we always disable it. */ + /* vp->full_bus_master_rx = 0; */ + vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; + + /* The 3c51x-specific entries in the device structure. */ + dev->netdev_ops = &netdev_ops; + dev->watchdog_timeo = (400 * HZ) / 1000; + dev->ethtool_ops = &netdev_ethtool_ops; + + return register_netdev(dev); +} + + +static int corkscrew_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct corkscrew_private *vp = netdev_priv(dev); + bool armtimer = false; + __u32 config; + int i; + + /* Before initializing select the active media port. */ + EL3WINDOW(3); + if (vp->full_duplex) + outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ + config = inl(ioaddr + Wn3_Config); + + if (vp->media_override != 7) { + if (corkscrew_debug > 1) + pr_info("%s: Media override to transceiver %d (%s).\n", + dev->name, vp->media_override, + media_tbl[vp->media_override].name); + dev->if_port = vp->media_override; + } else if (vp->autoselect) { + /* Find first available media type, starting with 100baseTx. */ + dev->if_port = 4; + while (!(vp->available_media & media_tbl[dev->if_port].mask)) + dev->if_port = media_tbl[dev->if_port].next; + + if (corkscrew_debug > 1) + pr_debug("%s: Initial media type %s.\n", + dev->name, media_tbl[dev->if_port].name); + armtimer = true; + } else + dev->if_port = vp->default_media; + + config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); + outl(config, ioaddr + Wn3_Config); + + if (corkscrew_debug > 1) { + pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n", + dev->name, config); + } + + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0; i--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(RxReset, ioaddr + EL3_CMD); + /* Wait a few ticks for the RxReset command to complete. */ + for (i = 20; i >= 0; i--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + + /* Use the now-standard shared IRQ implementation. */ + if (vp->capabilities == 0x11c7) { + /* Corkscrew: Cannot share ISA resources. */ + if (dev->irq == 0 || + dev->dma == 0 || + request_irq(dev->irq, corkscrew_interrupt, 0, + vp->product_name, dev)) + return -EAGAIN; + enable_dma(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_CASCADE); + } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED, + vp->product_name, dev)) { + return -EAGAIN; + } + + if (armtimer) + mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait); + + if (corkscrew_debug > 1) { + EL3WINDOW(4); + pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n", + dev->name, dev->irq, inw(ioaddr + Wn4_Media)); + } + + /* Set the station address and mask in window 2 each time opened. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + for (; i < 12; i += 2) + outw(0, ioaddr + i); + + if (dev->if_port == 3) + /* Start the thinnet transceiver. We should really wait 50ms... */ + outw(StartCoax, ioaddr + EL3_CMD); + EL3WINDOW(4); + outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | + media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(ioaddr + i); + inw(ioaddr + 10); + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(ioaddr + 12); + /* ..and on the Boomerang we enable the extra statistics bits. */ + outw(0x0040, ioaddr + Wn4_NetDiag); + + /* Switch to register set 7 for normal use. */ + EL3WINDOW(7); + + if (vp->full_bus_master_rx) { /* Boomerang bus master. */ + vp->cur_rx = vp->dirty_rx = 0; + if (corkscrew_debug > 2) + pr_debug("%s: Filling in the Rx ring.\n", dev->name); + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + if (i < (RX_RING_SIZE - 1)) + vp->rx_ring[i].next = + isa_virt_to_bus(&vp->rx_ring[i + 1]); + else + vp->rx_ring[i].next = 0; + vp->rx_ring[i].status = 0; /* Clear complete bit. */ + vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; + skb = netdev_alloc_skb(dev, PKT_BUF_SZ); + vp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[i].addr = isa_virt_to_bus(skb->data); + } + if (i != 0) + vp->rx_ring[i - 1].next = + isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ + outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); + } + if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ + vp->cur_tx = vp->dirty_tx = 0; + outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */ + /* Clear the Tx ring. */ + for (i = 0; i < TX_RING_SIZE; i++) + vp->tx_skbuff[i] = NULL; + outl(0, ioaddr + DownListPtr); + } + /* Set receiver mode: presumably accept b-case and phys addr only. */ + set_rx_mode(dev); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + + netif_start_queue(dev); + + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull | + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + (vp->full_bus_master_rx ? UpComplete : RxComplete) | + (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, + ioaddr + EL3_CMD); + + return 0; +} + +static void corkscrew_timer(struct timer_list *t) +{ +#ifdef AUTOMEDIA + struct corkscrew_private *vp = timer_container_of(vp, t, timer); + struct net_device *dev = vp->our_dev; + int ioaddr = dev->base_addr; + unsigned long flags; + int ok = 0; + + if (corkscrew_debug > 1) + pr_debug("%s: Media selection timer tick happened, %s.\n", + dev->name, media_tbl[dev->if_port].name); + + spin_lock_irqsave(&vp->lock, flags); + + { + int old_window = inw(ioaddr + EL3_CMD) >> 13; + int media_status; + EL3WINDOW(4); + media_status = inw(ioaddr + Wn4_Media); + switch (dev->if_port) { + case 0: + case 4: + case 5: /* 10baseT, 100baseTX, 100baseFX */ + if (media_status & Media_LnkBeat) { + ok = 1; + if (corkscrew_debug > 1) + pr_debug("%s: Media %s has link beat, %x.\n", + dev->name, + media_tbl[dev->if_port].name, + media_status); + } else if (corkscrew_debug > 1) + pr_debug("%s: Media %s is has no link beat, %x.\n", + dev->name, + media_tbl[dev->if_port].name, + media_status); + + break; + default: /* Other media types handled by Tx timeouts. */ + if (corkscrew_debug > 1) + pr_debug("%s: Media %s is has no indication, %x.\n", + dev->name, + media_tbl[dev->if_port].name, + media_status); + ok = 1; + } + if (!ok) { + __u32 config; + + do { + dev->if_port = + media_tbl[dev->if_port].next; + } + while (!(vp->available_media & media_tbl[dev->if_port].mask)); + + if (dev->if_port == 8) { /* Go back to default. */ + dev->if_port = vp->default_media; + if (corkscrew_debug > 1) + pr_debug("%s: Media selection failing, using default %s port.\n", + dev->name, + media_tbl[dev->if_port].name); + } else { + if (corkscrew_debug > 1) + pr_debug("%s: Media selection failed, now trying %s port.\n", + dev->name, + media_tbl[dev->if_port].name); + vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; + add_timer(&vp->timer); + } + outw((media_status & ~(Media_10TP | Media_SQE)) | + media_tbl[dev->if_port].media_bits, + ioaddr + Wn4_Media); + + EL3WINDOW(3); + config = inl(ioaddr + Wn3_Config); + config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); + outl(config, ioaddr + Wn3_Config); + + outw(dev->if_port == 3 ? StartCoax : StopCoax, + ioaddr + EL3_CMD); + } + EL3WINDOW(old_window); + } + + spin_unlock_irqrestore(&vp->lock, flags); + if (corkscrew_debug > 1) + pr_debug("%s: Media selection timer finished, %s.\n", + dev->name, media_tbl[dev->if_port].name); + +#endif /* AUTOMEDIA */ +} + +static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue) +{ + int i; + struct corkscrew_private *vp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n", + dev->name, inb(ioaddr + TxStatus), + inw(ioaddr + EL3_STATUS)); + /* Slight code bloat to be user friendly. */ + if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) + pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n", + dev->name); +#ifndef final_version + pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n", + vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, + vp->cur_tx); + pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), + &vp->tx_ring[0]); + for (i = 0; i < TX_RING_SIZE; i++) { + pr_debug(" %d: %p length %8.8x status %8.8x\n", i, + &vp->tx_ring[i], + vp->tx_ring[i].length, vp->tx_ring[i].status); + } +#endif + /* Issue TX_RESET and TX_START commands. */ + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0; i--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + outw(TxEnable, ioaddr + EL3_CMD); + netif_trans_update(dev); /* prevent tx timeout */ + dev->stats.tx_errors++; + dev->stats.tx_dropped++; + netif_wake_queue(dev); +} + +static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct corkscrew_private *vp = netdev_priv(dev); + int ioaddr = dev->base_addr; + + /* Block a timer-based transmit from overlapping. */ + + netif_stop_queue(dev); + + if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */ + /* Calculate the next Tx descriptor entry. */ + int entry = vp->cur_tx % TX_RING_SIZE; + struct boom_tx_desc *prev_entry; + unsigned long flags; + int i; + + if (vp->tx_full) /* No room to transmit with */ + return NETDEV_TX_BUSY; + if (vp->cur_tx != 0) + prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE]; + else + prev_entry = NULL; + if (corkscrew_debug > 3) + pr_debug("%s: Trying to send a packet, Tx index %d.\n", + dev->name, vp->cur_tx); + /* vp->tx_full = 1; */ + vp->tx_skbuff[entry] = skb; + vp->tx_ring[entry].next = 0; + vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data); + vp->tx_ring[entry].length = skb->len | 0x80000000; + vp->tx_ring[entry].status = skb->len | 0x80000000; + + spin_lock_irqsave(&vp->lock, flags); + outw(DownStall, ioaddr + EL3_CMD); + /* Wait for the stall to complete. */ + for (i = 20; i >= 0; i--) + if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) + break; + if (prev_entry) + prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]); + if (inl(ioaddr + DownListPtr) == 0) { + outl(isa_virt_to_bus(&vp->tx_ring[entry]), + ioaddr + DownListPtr); + queued_packet++; + } + outw(DownUnstall, ioaddr + EL3_CMD); + spin_unlock_irqrestore(&vp->lock, flags); + + vp->cur_tx++; + if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) + vp->tx_full = 1; + else { /* Clear previous interrupt enable. */ + if (prev_entry) + prev_entry->status &= ~0x80000000; + netif_wake_queue(dev); + } + return NETDEV_TX_OK; + } + /* Put out the doubleword header... */ + outl(skb->len, ioaddr + TX_FIFO); + dev->stats.tx_bytes += skb->len; +#ifdef VORTEX_BUS_MASTER + if (vp->bus_master) { + /* Set the bus-master controller to transfer the packet. */ + outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr); + outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); + vp->tx_skb = skb; + outw(StartDMADown, ioaddr + EL3_CMD); + /* queue will be woken at the DMADone interrupt. */ + } else { + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + dev_kfree_skb(skb); + if (inw(ioaddr + TxFree) > 1536) { + netif_wake_queue(dev); + } else + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(SetTxThreshold + (1536 >> 2), + ioaddr + EL3_CMD); + } +#else + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + dev_kfree_skb(skb); + if (inw(ioaddr + TxFree) > 1536) { + netif_wake_queue(dev); + } else + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD); +#endif /* bus master */ + + + /* Clear the Tx status stack. */ + { + short tx_status; + int i = 4; + + while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { + if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ + if (corkscrew_debug > 2) + pr_debug("%s: Tx error, status %2.2x.\n", + dev->name, tx_status); + if (tx_status & 0x04) + dev->stats.tx_fifo_errors++; + if (tx_status & 0x38) + dev->stats.tx_aborted_errors++; + if (tx_status & 0x30) { + int j; + outw(TxReset, ioaddr + EL3_CMD); + for (j = 20; j >= 0; j--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + } + outw(TxEnable, ioaddr + EL3_CMD); + } + outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ + } + } + return NETDEV_TX_OK; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ + +static irqreturn_t corkscrew_interrupt(int irq, void *dev_id) +{ + /* Use the now-standard shared IRQ implementation. */ + struct net_device *dev = dev_id; + struct corkscrew_private *lp = netdev_priv(dev); + int ioaddr, status; + int latency; + int i = max_interrupt_work; + + ioaddr = dev->base_addr; + latency = inb(ioaddr + Timer); + + spin_lock(&lp->lock); + + status = inw(ioaddr + EL3_STATUS); + + if (corkscrew_debug > 4) + pr_debug("%s: interrupt, status %4.4x, timer %d.\n", + dev->name, status, latency); + if ((status & 0xE000) != 0xE000) { + static int donedidthis; + /* Some interrupt controllers store a bogus interrupt from boot-time. + Ignore a single early interrupt, but don't hang the machine for + other interrupt problems. */ + if (donedidthis++ > 100) { + pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", + dev->name, status, netif_running(dev)); + free_irq(dev->irq, dev); + dev->irq = -1; + } + } + + do { + if (corkscrew_debug > 5) + pr_debug("%s: In interrupt loop, status %4.4x.\n", + dev->name, status); + if (status & RxComplete) + corkscrew_rx(dev); + + if (status & TxAvailable) { + if (corkscrew_debug > 5) + pr_debug(" TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + netif_wake_queue(dev); + } + if (status & DownComplete) { + unsigned int dirty_tx = lp->dirty_tx; + + while (lp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry])) + break; /* It still hasn't been processed. */ + if (lp->tx_skbuff[entry]) { + dev_consume_skb_irq(lp->tx_skbuff[entry]); + lp->tx_skbuff[entry] = NULL; + } + dirty_tx++; + } + lp->dirty_tx = dirty_tx; + outw(AckIntr | DownComplete, ioaddr + EL3_CMD); + if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { + lp->tx_full = 0; + netif_wake_queue(dev); + } + } +#ifdef VORTEX_BUS_MASTER + if (status & DMADone) { + outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ + dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */ + netif_wake_queue(dev); + } +#endif + if (status & UpComplete) { + boomerang_rx(dev); + outw(AckIntr | UpComplete, ioaddr + EL3_CMD); + } + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts at once. */ + if (status & RxEarly) { /* Rx early is unused. */ + corkscrew_rx(dev); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & StatsFull) { /* Empty statistics. */ + static int DoneDidThat; + if (corkscrew_debug > 4) + pr_debug("%s: Updating stats.\n", dev->name); + update_stats(ioaddr, dev); + /* DEBUG HACK: Disable statistics as an interrupt source. */ + /* This occurs when we have the wrong media type! */ + if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) { + int win, reg; + pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n", + dev->name); + for (win = 0; win < 8; win++) { + EL3WINDOW(win); + pr_notice("Vortex window %d:", win); + for (reg = 0; reg < 16; reg++) + pr_cont(" %2.2x", inb(ioaddr + reg)); + pr_cont("\n"); + } + EL3WINDOW(7); + outw(SetIntrEnb | TxAvailable | + RxComplete | AdapterFailure | + UpComplete | DownComplete | + TxComplete, ioaddr + EL3_CMD); + DoneDidThat++; + } + } + if (status & AdapterFailure) { + /* Adapter failure requires Rx reset and reinit. */ + outw(RxReset, ioaddr + EL3_CMD); + /* Set the Rx filter to the current state. */ + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ + outw(AckIntr | AdapterFailure, + ioaddr + EL3_CMD); + } + } + + if (--i < 0) { + pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n", + dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); + /* Disable all pending interrupts. */ + outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); + outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + + } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + + spin_unlock(&lp->lock); + + if (corkscrew_debug > 4) + pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status); + return IRQ_HANDLED; +} + +static int corkscrew_rx(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + int i; + short rx_status; + + if (corkscrew_debug > 5) + pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); + while ((rx_status = inw(ioaddr + RxStatus)) > 0) { + if (rx_status & 0x4000) { /* Error, update stats. */ + unsigned char rx_error = inb(ioaddr + RxErrors); + if (corkscrew_debug > 2) + pr_debug(" Rx error: status %2.2x.\n", + rx_error); + dev->stats.rx_errors++; + if (rx_error & 0x01) + dev->stats.rx_over_errors++; + if (rx_error & 0x02) + dev->stats.rx_length_errors++; + if (rx_error & 0x04) + dev->stats.rx_frame_errors++; + if (rx_error & 0x08) + dev->stats.rx_crc_errors++; + if (rx_error & 0x10) + dev->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, pkt_len + 5 + 2); + if (corkscrew_debug > 4) + pr_debug("Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + if (skb != NULL) { + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + /* 'skb_put()' points to the start of sk_buff data area. */ + insl(ioaddr + RX_FIFO, + skb_put(skb, pkt_len), + (pkt_len + 3) >> 2); + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + /* Wait a limited time to go to next packet. */ + for (i = 200; i >= 0; i--) + if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + continue; + } else if (corkscrew_debug) + pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); + } + outw(RxDiscard, ioaddr + EL3_CMD); + dev->stats.rx_dropped++; + /* Wait a limited time to skip this packet. */ + for (i = 200; i >= 0; i--) + if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + } + return 0; +} + +static int boomerang_rx(struct net_device *dev) +{ + struct corkscrew_private *vp = netdev_priv(dev); + int entry = vp->cur_rx % RX_RING_SIZE; + int ioaddr = dev->base_addr; + int rx_status; + + if (corkscrew_debug > 5) + pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); + while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { + if (rx_status & RxDError) { /* Error, update stats. */ + unsigned char rx_error = rx_status >> 16; + if (corkscrew_debug > 2) + pr_debug(" Rx error: status %2.2x.\n", + rx_error); + dev->stats.rx_errors++; + if (rx_error & 0x01) + dev->stats.rx_over_errors++; + if (rx_error & 0x02) + dev->stats.rx_length_errors++; + if (rx_error & 0x04) + dev->stats.rx_frame_errors++; + if (rx_error & 0x08) + dev->stats.rx_crc_errors++; + if (rx_error & 0x10) + dev->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + dev->stats.rx_bytes += pkt_len; + if (corkscrew_debug > 4) + pr_debug("Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + + /* Check if the packet is long enough to just accept without + copying to a properly sized skbuff. */ + if (pkt_len < rx_copybreak && + (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) { + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + /* 'skb_put()' points to the start of sk_buff data area. */ + skb_put_data(skb, + isa_bus_to_virt(vp->rx_ring[entry].addr), + pkt_len); + rx_copy++; + } else { + void *temp; + /* Pass up the skbuff already on the Rx ring. */ + skb = vp->rx_skbuff[entry]; + vp->rx_skbuff[entry] = NULL; + temp = skb_put(skb, pkt_len); + /* Remove this checking code for final release. */ + if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp) + pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n", + dev->name, + isa_bus_to_virt(vp->rx_ring[entry].addr), + skb->head, temp); + rx_nocopy++; + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + } + entry = (++vp->cur_rx) % RX_RING_SIZE; + } + /* Refill the Rx ring buffers. */ + for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { + struct sk_buff *skb; + entry = vp->dirty_rx % RX_RING_SIZE; + if (vp->rx_skbuff[entry] == NULL) { + skb = netdev_alloc_skb(dev, PKT_BUF_SZ); + if (skb == NULL) + break; /* Bad news! */ + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data); + vp->rx_skbuff[entry] = skb; + } + vp->rx_ring[entry].status = 0; /* Clear complete bit. */ + } + return 0; +} + +static int corkscrew_close(struct net_device *dev) +{ + struct corkscrew_private *vp = netdev_priv(dev); + int ioaddr = dev->base_addr; + int i; + + netif_stop_queue(dev); + + if (corkscrew_debug > 1) { + pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n", + dev->name, inw(ioaddr + EL3_STATUS), + inb(ioaddr + TxStatus)); + pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n", + dev->name, rx_nocopy, rx_copy, queued_packet); + } + + timer_delete_sync(&vp->timer); + + /* Turn off statistics ASAP. We update lp->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + if (dev->if_port == XCVR_10base2) + /* Turn off thinnet power. Green! */ + outw(StopCoax, ioaddr + EL3_CMD); + + free_irq(dev->irq, dev); + + outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); + + update_stats(ioaddr, dev); + if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ + outl(0, ioaddr + UpListPtr); + for (i = 0; i < RX_RING_SIZE; i++) + if (vp->rx_skbuff[i]) { + dev_kfree_skb(vp->rx_skbuff[i]); + vp->rx_skbuff[i] = NULL; + } + } + if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ + outl(0, ioaddr + DownListPtr); + for (i = 0; i < TX_RING_SIZE; i++) + if (vp->tx_skbuff[i]) { + dev_kfree_skb(vp->tx_skbuff[i]); + vp->tx_skbuff[i] = NULL; + } + } + + return 0; +} + +static struct net_device_stats *corkscrew_get_stats(struct net_device *dev) +{ + struct corkscrew_private *vp = netdev_priv(dev); + unsigned long flags; + + if (netif_running(dev)) { + spin_lock_irqsave(&vp->lock, flags); + update_stats(dev->base_addr, dev); + spin_unlock_irqrestore(&vp->lock, flags); + } + return &dev->stats; +} + +/* Update statistics. + Unlike with the EL3 we need not worry about interrupts changing + the window setting from underneath us, but we must still guard + against a race condition with a StatsUpdate interrupt updating the + table. This is done by checking that the ASM (!) code generated uses + atomic updates with '+='. + */ +static void update_stats(int ioaddr, struct net_device *dev) +{ + /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + dev->stats.tx_carrier_errors += inb(ioaddr + 0); + dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + dev->stats.collisions += inb(ioaddr + 3); + dev->stats.tx_window_errors += inb(ioaddr + 4); + dev->stats.rx_fifo_errors += inb(ioaddr + 5); + dev->stats.tx_packets += inb(ioaddr + 6); + dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; + /* Rx packets */ inb(ioaddr + 7); + /* Must read to clear */ + /* Tx deferrals */ inb(ioaddr + 8); + /* Don't bother with register 9, an extension of registers 6&7. + If we do use the 6&7 values the atomic update assumption above + is invalid. */ + inw(ioaddr + 10); /* Total Rx and Tx octets. */ + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(ioaddr + 12); + + /* We change back to window 7 (not 1) with the Vortex. */ + EL3WINDOW(7); +} + +/* This new version of set_rx_mode() supports v1.4 kernels. + The Vortex chip has no documented multicast filter, so the only + multicast setting is to receive all multicast frames. At least + the chip has a very clean way to set the mode, unlike many others. */ +static void set_rx_mode(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + unsigned short new_mode; + + if (dev->flags & IFF_PROMISC) { + if (corkscrew_debug > 3) + pr_debug("%s: Setting promiscuous mode.\n", + dev->name); + new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm; + } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) { + new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast; + } else + new_mode = SetRxFilter | RxStation | RxBroadcast; + + outw(new_mode, ioaddr + EL3_CMD); +} + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", + dev->base_addr); +} + +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return corkscrew_debug; +} + +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + corkscrew_debug = level; +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; + +#ifdef MODULE +static void __exit corkscrew_exit_module(void) +{ + while (!list_empty(&root_corkscrew_dev)) { + struct net_device *dev; + struct corkscrew_private *vp; + + vp = list_entry(root_corkscrew_dev.next, + struct corkscrew_private, list); + dev = vp->our_dev; + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } +} +module_exit(corkscrew_exit_module); +#endif /* MODULE */ diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c new file mode 100644 index 000000000000..1f2070497a75 --- /dev/null +++ b/drivers/net/ethernet/3com/3c574_cs.c @@ -0,0 +1,1164 @@ +/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner". + + Written 1993-1998 by + Donald Becker, becker@scyld.com, (driver core) and + David Hinds, dahinds@users.sourceforge.net (from his PC card code). + Locking fixes (C) Copyright 2003 Red Hat Inc + + This software may be used and distributed according to the terms of + the GNU General Public License, incorporated herein by reference. + + This driver derives from Donald Becker's 3c509 core, which has the + following copyright: + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + +*/ + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com 3c574 PC card Fast Ethernet +Adapter. + +II. Board-specific settings + +None -- PC cards are autoconfigured. + +III. Driver operation + +The 3c574 uses a Boomerang-style interface, without the bus-master capability. +See the Boomerang driver and documentation for most details. + +IV. Notes and chip documentation. + +Two added registers are used to enhance PIO performance, RunnerRdCtrl and +RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the +count of word (16 bits) reads or writes the driver is about to do to the Rx +or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card +translation latency by buffering the I/O operations with an 8 word FIFO. +Note: No other chip accesses are permitted when this buffer is used. + +A second enhancement is that both attribute and common memory space +0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster +with *some* PCcard bridges) may be used instead of I/O operations. +This is enabled by setting the 0x10 bit in the PCMCIA LAN COR. + +Some slow PC card bridges work better if they never see a WAIT signal. +This is configured by setting the 0x20 bit in the PCMCIA LAN COR. +Only do this after testing that it is reliable and improves performance. + +The upper five bits of RunnerRdCtrl are used to window into PCcard +configuration space registers. Window 0 is the regular Boomerang/Odie +register set, 1-5 are various PC card control registers, and 16-31 are +the (reversed!) CIS table. + +A final note: writing the InternalConfig register in window 3 with an +invalid ramWidth is Very Bad. + +V. References + +http://www.scyld.com/expert/NWay.html +http://www.national.com/opf/DP/DP83840A.html + +Thanks to Terry Murphy of 3Com for providing development information for +earlier 3Com products. + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds "); +MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver"); +MODULE_LICENSE("GPL"); + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +INT_MODULE_PARM(max_interrupt_work, 32); + +/* Force full duplex modes? */ +INT_MODULE_PARM(full_duplex, 0); + +/* Autodetect link polarity reversal? */ +INT_MODULE_PARM(auto_polarity, 1); + + +/*====================================================================*/ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((800*HZ)/1000) + +/* To minimize the size of the driver source and make the driver more + readable not all constants are symbolically defined. + You'll need the manual if you want to understand driver details anyway. */ +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. */ +enum el3_cmds { + TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, + TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, + SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, + SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, + StatsDisable = 22<<11, StopCoax = 23<<11, +}; + +enum elxl_status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 }; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 +}; + +enum Window0 { + Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */ + IntrStatus=0x0E, /* Valid in all windows. */ +}; +/* These assumes the larger EEPROM. */ +enum Win0_EEPROM_cmds { + EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300, + EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ + EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ +}; + +/* Register window 1 offsets, the window used in normal operation. + On the "Odie" this window is always mapped at offsets 0x10-0x1f. + Except for TxFree, which is overlapped by RunnerWrCtrl. */ +enum Window1 { + TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, + RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B, + TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */ + RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c, +}; + +enum Window3 { /* Window 3: MAC/config bits. */ + Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, +}; +enum wn3_config { + Ram_size = 7, + Ram_width = 8, + Ram_speed = 0x30, + Rom_size = 0xc0, + Ram_split_shift = 16, + Ram_split = 3 << Ram_split_shift, + Xcvr_shift = 20, + Xcvr = 7 << Xcvr_shift, + Autoselect = 0x1000000, +}; + +enum Window4 { /* Window 4: Xcvr/media bits. */ + Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10, +}; + +#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ + +struct el3_private { + struct pcmcia_device *p_dev; + u16 advertising, partner; /* NWay media advertisement */ + unsigned char phys; /* MII device address */ + unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */ + /* for transceiver monitoring */ + struct timer_list media; + unsigned short media_status; + unsigned short fast_poll; + unsigned long last_irq; + spinlock_t window_lock; /* Guards the Window selection */ +}; + +/* Set iff a MII transceiver on any interface requires mdio preamble. + This only set with the original DP83840 on older 3c905 boards, so the extra + code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 0; + +/* Index of functions. */ + +static int tc574_config(struct pcmcia_device *link); +static void tc574_release(struct pcmcia_device *link); + +static void mdio_sync(unsigned int ioaddr, int bits); +static int mdio_read(unsigned int ioaddr, int phy_id, int location); +static void mdio_write(unsigned int ioaddr, int phy_id, int location, + int value); +static unsigned short read_eeprom(unsigned int ioaddr, int index); +static void tc574_wait_for_completion(struct net_device *dev, int cmd); + +static void tc574_reset(struct net_device *dev); +static void media_check(struct timer_list *t); +static int el3_open(struct net_device *dev); +static netdev_tx_t el3_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t el3_interrupt(int irq, void *dev_id); +static void update_stats(struct net_device *dev); +static struct net_device_stats *el3_get_stats(struct net_device *dev); +static int el3_rx(struct net_device *dev, int worklimit); +static int el3_close(struct net_device *dev); +static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); +static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); + +static void tc574_detach(struct pcmcia_device *p_dev); + +/* + tc574_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. +*/ +static const struct net_device_ops el3_netdev_ops = { + .ndo_open = el3_open, + .ndo_stop = el3_close, + .ndo_start_xmit = el3_start_xmit, + .ndo_tx_timeout = el3_tx_timeout, + .ndo_get_stats = el3_get_stats, + .ndo_eth_ioctl = el3_ioctl, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int tc574_probe(struct pcmcia_device *link) +{ + struct el3_private *lp; + struct net_device *dev; + + dev_dbg(&link->dev, "3c574_attach()\n"); + + /* Create the PC card device object. */ + dev = alloc_etherdev(sizeof(struct el3_private)); + if (!dev) + return -ENOMEM; + lp = netdev_priv(dev); + link->priv = dev; + lp->p_dev = link; + + spin_lock_init(&lp->window_lock); + link->resource[0]->end = 32; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; + link->config_flags |= CONF_ENABLE_IRQ; + link->config_index = 1; + + dev->netdev_ops = &el3_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + return tc574_config(link); +} + +static void tc574_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + dev_dbg(&link->dev, "3c574_detach()\n"); + + unregister_netdev(dev); + + tc574_release(link); + + free_netdev(dev); +} /* tc574_detach */ + +static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; + +static int tc574_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct el3_private *lp = netdev_priv(dev); + int ret, i, j; + __be16 addr[ETH_ALEN / 2]; + unsigned int ioaddr; + char *cardname; + __u32 config; + u8 *buf; + size_t len; + + dev_dbg(&link->dev, "3c574_config()\n"); + + link->io_lines = 16; + + for (i = j = 0; j < 0x400; j += 0x20) { + link->resource[0]->start = j ^ 0x300; + i = pcmcia_request_io(link); + if (i == 0) + break; + } + if (i != 0) + goto failed; + + ret = pcmcia_request_irq(link, el3_interrupt); + if (ret) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + + ioaddr = dev->base_addr; + + /* The 3c574 normally uses an EEPROM for configuration info, including + the hardware address. The future products may include a modem chip + and put the address in the CIS. */ + + len = pcmcia_get_tuple(link, 0x88, &buf); + if (buf && len >= 6) { + for (i = 0; i < 3; i++) + addr[i] = htons(le16_to_cpu(buf[i * 2])); + kfree(buf); + } else { + kfree(buf); /* 0 < len < 6 */ + EL3WINDOW(0); + for (i = 0; i < 3; i++) + addr[i] = htons(read_eeprom(ioaddr, i + 10)); + if (addr[0] == htons(0x6060)) { + pr_notice("IO port conflict at 0x%03lx-0x%03lx\n", + dev->base_addr, dev->base_addr+15); + goto failed; + } + } + eth_hw_addr_set(dev, (u8 *)addr); + if (link->prod_id[1]) + cardname = link->prod_id[1]; + else + cardname = "3Com 3c574"; + + { + u_char mcr; + outw(2<<11, ioaddr + RunnerRdCtrl); + mcr = inb(ioaddr + 2); + outw(0<<11, ioaddr + RunnerRdCtrl); + pr_info(" ASIC rev %d,", mcr>>3); + EL3WINDOW(3); + config = inl(ioaddr + Wn3_Config); + lp->default_media = (config & Xcvr) >> Xcvr_shift; + lp->autoselect = config & Autoselect ? 1 : 0; + } + + timer_setup(&lp->media, media_check, 0); + + { + int phy; + + /* Roadrunner only: Turn on the MII transceiver */ + outw(0x8040, ioaddr + Wn3_Options); + mdelay(1); + outw(0xc040, ioaddr + Wn3_Options); + tc574_wait_for_completion(dev, TxReset); + tc574_wait_for_completion(dev, RxReset); + mdelay(1); + outw(0x8040, ioaddr + Wn3_Options); + + EL3WINDOW(4); + for (phy = 1; phy <= 32; phy++) { + int mii_status; + mdio_sync(ioaddr, 32); + mii_status = mdio_read(ioaddr, phy & 0x1f, 1); + if (mii_status != 0xffff) { + lp->phys = phy & 0x1f; + dev_dbg(&link->dev, " MII transceiver at " + "index %d, status %x.\n", + phy, mii_status); + if ((mii_status & 0x0040) == 0) + mii_preamble_required = 1; + break; + } + } + if (phy > 32) { + pr_notice(" No MII transceivers found!\n"); + goto failed; + } + i = mdio_read(ioaddr, lp->phys, 16) | 0x40; + mdio_write(ioaddr, lp->phys, 16, i); + lp->advertising = mdio_read(ioaddr, lp->phys, 4); + if (full_duplex) { + /* Only advertise the FD media types. */ + lp->advertising &= ~0x02a0; + mdio_write(ioaddr, lp->phys, 4, lp->advertising); + } + } + + SET_NETDEV_DEV(dev, &link->dev); + + if (register_netdev(dev) != 0) { + pr_notice("register_netdev() failed\n"); + goto failed; + } + + netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n", + cardname, dev->base_addr, dev->irq, dev->dev_addr); + netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n", + 8 << (config & Ram_size), + ram_split[(config & Ram_split) >> Ram_split_shift], + config & Autoselect ? "autoselect " : ""); + + return 0; + +failed: + tc574_release(link); + return -ENODEV; + +} /* tc574_config */ + +static void tc574_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} + +static int tc574_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int tc574_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) { + tc574_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + +static void dump_status(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + EL3WINDOW(1); + netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x, tx free %04x\n", + inw(ioaddr+EL3_STATUS), + inw(ioaddr+RxStatus), inb(ioaddr+TxStatus), + inw(ioaddr+TxFree)); + EL3WINDOW(4); + netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n", + inw(ioaddr+0x04), inw(ioaddr+0x06), + inw(ioaddr+0x08), inw(ioaddr+0x0a)); + EL3WINDOW(1); +} + +/* + Use this for commands that may take time to finish +*/ +static void tc574_wait_for_completion(struct net_device *dev, int cmd) +{ + int i = 1500; + outw(cmd, dev->base_addr + EL3_CMD); + while (--i > 0) + if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; + if (i == 0) + netdev_notice(dev, "command 0x%04x did not complete!\n", cmd); +} + +/* Read a word from the EEPROM using the regular EEPROM access register. + Assume that we are in register window zero. + */ +static unsigned short read_eeprom(unsigned int ioaddr, int index) +{ + int timer; + outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 usec for the read to take place. */ + for (timer = 1620; timer >= 0; timer--) { + if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) + break; + } + return inw(ioaddr + Wn0EepromData); +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + The maxium data clock rate is 2.5 Mhz. The timing is easily met by the + slow PC card interface. */ + +#define MDIO_SHIFT_CLK 0x01 +#define MDIO_DIR_WRITE 0x04 +#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) +#define MDIO_DATA_READ 0x02 +#define MDIO_ENB_IN 0x00 + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(unsigned int ioaddr, int bits) +{ + unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + /* Establish sync by sending at least 32 logic ones. */ + while (-- bits >= 0) { + outw(MDIO_DATA_WRITE1, mdio_addr); + outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + } +} + +static int mdio_read(unsigned int ioaddr, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + unsigned int retval = 0; + unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the read command bits out. */ + for (i = 14; i >= 0; i--) { + int dataval = (read_cmd&(1< 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value) +{ + int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; + unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + int i; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (write_cmd&(1<= 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + } +} + +/* Reset and restore all of the 3c574 registers. */ +static void tc574_reset(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + int i; + unsigned int ioaddr = dev->base_addr; + unsigned long flags; + + tc574_wait_for_completion(dev, TotalReset|0x10); + + spin_lock_irqsave(&lp->window_lock, flags); + /* Clear any transactions in progress. */ + outw(0, ioaddr + RunnerWrCtrl); + outw(0, ioaddr + RunnerRdCtrl); + + /* Set the station address and mask. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + for (; i < 12; i+=2) + outw(0, ioaddr + i); + + /* Reset config options */ + EL3WINDOW(3); + outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); + outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b, + ioaddr + Wn3_Config); + /* Roadrunner only: Turn on the MII transceiver. */ + outw(0x8040, ioaddr + Wn3_Options); + mdelay(1); + outw(0xc040, ioaddr + Wn3_Options); + EL3WINDOW(1); + spin_unlock_irqrestore(&lp->window_lock, flags); + + tc574_wait_for_completion(dev, TxReset); + tc574_wait_for_completion(dev, RxReset); + mdelay(1); + spin_lock_irqsave(&lp->window_lock, flags); + EL3WINDOW(3); + outw(0x8040, ioaddr + Wn3_Options); + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(ioaddr + i); + inw(ioaddr + 10); + inw(ioaddr + 12); + EL3WINDOW(4); + inb(ioaddr + 12); + inb(ioaddr + 13); + + /* .. enable any extra statistics bits.. */ + outw(0x0040, ioaddr + Wn4_NetDiag); + + EL3WINDOW(1); + spin_unlock_irqrestore(&lp->window_lock, flags); + + /* .. re-sync MII and re-fill what NWay is advertising. */ + mdio_sync(ioaddr, 32); + mdio_write(ioaddr, lp->phys, 4, lp->advertising); + if (!auto_polarity) { + /* works for TDK 78Q2120 series MII's */ + i = mdio_read(ioaddr, lp->phys, 16) | 0x20; + mdio_write(ioaddr, lp->phys, 16, i); + } + + spin_lock_irqsave(&lp->window_lock, flags); + /* Switch to register set 1 for normal use, just for TxFree. */ + set_rx_mode(dev); + spin_unlock_irqrestore(&lp->window_lock, flags); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | AdapterFailure | RxEarly, ioaddr + EL3_CMD); +} + +static int el3_open(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + if (!pcmcia_dev_present(link)) + return -ENODEV; + + link->open++; + netif_start_queue(dev); + + tc574_reset(dev); + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); + + dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", + dev->name, inw(dev->base_addr + EL3_STATUS)); + + return 0; +} + +static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + unsigned int ioaddr = dev->base_addr; + + netdev_notice(dev, "Transmit timed out!\n"); + dump_status(dev); + dev->stats.tx_errors++; + netif_trans_update(dev); /* prevent tx timeout */ + /* Issue TX_RESET and TX_START commands. */ + tc574_wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + netif_wake_queue(dev); +} + +static void pop_tx_status(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + int i; + + /* Clear the Tx status stack. */ + for (i = 32; i > 0; i--) { + u_char tx_status = inb(ioaddr + TxStatus); + if (!(tx_status & 0x84)) + break; + /* reset transmitter on jabber error or underrun */ + if (tx_status & 0x30) + tc574_wait_for_completion(dev, TxReset); + if (tx_status & 0x38) { + pr_debug("%s: transmit error: status 0x%02x\n", + dev->name, tx_status); + outw(TxEnable, ioaddr + EL3_CMD); + dev->stats.tx_aborted_errors++; + } + outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ + } +} + +static netdev_tx_t el3_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct el3_private *lp = netdev_priv(dev); + unsigned long flags; + + pr_debug("%s: el3_start_xmit(length = %ld) called, " + "status %4.4x.\n", dev->name, (long)skb->len, + inw(ioaddr + EL3_STATUS)); + + spin_lock_irqsave(&lp->window_lock, flags); + + dev->stats.tx_bytes += skb->len; + + /* Put out the doubleword header... */ + outw(skb->len, ioaddr + TX_FIFO); + outw(0, ioaddr + TX_FIFO); + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); + + /* TxFree appears only in Window 1, not offset 0x1c. */ + if (inw(ioaddr + TxFree) <= 1536) { + netif_stop_queue(dev); + /* Interrupt us when the FIFO has room for max-sized packet. + The threshold is in units of dwords. */ + outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); + } + + pop_tx_status(dev); + spin_unlock_irqrestore(&lp->window_lock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +/* The EL3 interrupt handler. */ +static irqreturn_t el3_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct el3_private *lp = netdev_priv(dev); + unsigned int ioaddr; + unsigned status; + int work_budget = max_interrupt_work; + int handled = 0; + + if (!netif_device_present(dev)) + return IRQ_NONE; + ioaddr = dev->base_addr; + + pr_debug("%s: interrupt, status %4.4x.\n", + dev->name, inw(ioaddr + EL3_STATUS)); + + spin_lock(&lp->window_lock); + + while ((status = inw(ioaddr + EL3_STATUS)) & + (IntLatch | RxComplete | RxEarly | StatsFull)) { + if (!netif_device_present(dev) || + ((status & 0xe000) != 0x2000)) { + pr_debug("%s: Interrupt from dead card\n", dev->name); + break; + } + + handled = 1; + + if (status & RxComplete) + work_budget = el3_rx(dev, work_budget); + + if (status & TxAvailable) { + pr_debug(" TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + netif_wake_queue(dev); + } + + if (status & TxComplete) + pop_tx_status(dev); + + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts. */ + if (status & StatsFull) + update_stats(dev); + if (status & RxEarly) { + work_budget = el3_rx(dev, work_budget); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & AdapterFailure) { + u16 fifo_diag; + EL3WINDOW(4); + fifo_diag = inw(ioaddr + Wn4_FIFODiag); + EL3WINDOW(1); + netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n", + fifo_diag); + if (fifo_diag & 0x0400) { + /* Tx overrun */ + tc574_wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + } + if (fifo_diag & 0x2000) { + /* Rx underrun */ + tc574_wait_for_completion(dev, RxReset); + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); + } + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } + } + + if (--work_budget < 0) { + pr_debug("%s: Too much work in interrupt, " + "status %4.4x.\n", dev->name, status); + /* Clear all interrupts */ + outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + } + + pr_debug("%s: exiting interrupt, status %4.4x.\n", + dev->name, inw(ioaddr + EL3_STATUS)); + + spin_unlock(&lp->window_lock); + return IRQ_RETVAL(handled); +} + +/* + This timer serves two purposes: to check for missed interrupts + (and as a last resort, poll the NIC for events), and to monitor + the MII, reporting changes in cable status. +*/ +static void media_check(struct timer_list *t) +{ + struct el3_private *lp = timer_container_of(lp, t, media); + struct net_device *dev = lp->p_dev->priv; + unsigned int ioaddr = dev->base_addr; + unsigned long flags; + unsigned short /* cable, */ media, partner; + + if (!netif_device_present(dev)) + goto reschedule; + + /* Check for pending interrupt with expired latency timer: with + this, we can limp along even if the interrupt is blocked */ + if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) { + if (!lp->fast_poll) + netdev_info(dev, "interrupt(s) dropped!\n"); + + local_irq_save(flags); + el3_interrupt(dev->irq, dev); + local_irq_restore(flags); + + lp->fast_poll = HZ; + } + if (lp->fast_poll) { + lp->fast_poll--; + lp->media.expires = jiffies + 2*HZ/100; + add_timer(&lp->media); + return; + } + + spin_lock_irqsave(&lp->window_lock, flags); + EL3WINDOW(4); + media = mdio_read(ioaddr, lp->phys, 1); + partner = mdio_read(ioaddr, lp->phys, 5); + EL3WINDOW(1); + + if (media != lp->media_status) { + if ((media ^ lp->media_status) & 0x0004) + netdev_info(dev, "%s link beat\n", + (lp->media_status & 0x0004) ? "lost" : "found"); + if ((media ^ lp->media_status) & 0x0020) { + lp->partner = 0; + if (lp->media_status & 0x0020) { + netdev_info(dev, "autonegotiation restarted\n"); + } else if (partner) { + partner &= lp->advertising; + lp->partner = partner; + netdev_info(dev, "autonegotiation complete: " + "%dbaseT-%cD selected\n", + (partner & 0x0180) ? 100 : 10, + (partner & 0x0140) ? 'F' : 'H'); + } else { + netdev_info(dev, "link partner did not autonegotiate\n"); + } + + EL3WINDOW(3); + outb((partner & 0x0140 ? 0x20 : 0) | + (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); + EL3WINDOW(1); + + } + if (media & 0x0010) + netdev_info(dev, "remote fault detected\n"); + if (media & 0x0002) + netdev_info(dev, "jabber detected\n"); + lp->media_status = media; + } + spin_unlock_irqrestore(&lp->window_lock, flags); + +reschedule: + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); +} + +static struct net_device_stats *el3_get_stats(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + + if (netif_device_present(dev)) { + unsigned long flags; + spin_lock_irqsave(&lp->window_lock, flags); + update_stats(dev); + spin_unlock_irqrestore(&lp->window_lock, flags); + } + return &dev->stats; +} + +/* Update statistics. + Surprisingly this need not be run single-threaded, but it effectively is. + The counters clear when read, so the adds must merely be atomic. + */ +static void update_stats(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + u8 up; + + pr_debug("%s: updating the statistics.\n", dev->name); + + if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */ + return; + + /* Unlike the 3c509 we need not turn off stats updates while reading. */ + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + dev->stats.tx_carrier_errors += inb(ioaddr + 0); + dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + dev->stats.collisions += inb(ioaddr + 3); + dev->stats.tx_window_errors += inb(ioaddr + 4); + dev->stats.rx_fifo_errors += inb(ioaddr + 5); + dev->stats.tx_packets += inb(ioaddr + 6); + up = inb(ioaddr + 9); + dev->stats.tx_packets += (up&0x30) << 4; + /* Rx packets */ inb(ioaddr + 7); + /* Tx deferrals */ inb(ioaddr + 8); + /* rx */ inw(ioaddr + 10); + /* tx */ inw(ioaddr + 12); + + EL3WINDOW(4); + /* BadSSD */ inb(ioaddr + 12); + up = inb(ioaddr + 13); + + EL3WINDOW(1); +} + +static int el3_rx(struct net_device *dev, int worklimit) +{ + unsigned int ioaddr = dev->base_addr; + short rx_status; + + pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n", + dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) && + worklimit > 0) { + worklimit--; + if (rx_status & 0x4000) { /* Error, update stats. */ + short error = rx_status & 0x3800; + dev->stats.rx_errors++; + switch (error) { + case 0x0000: dev->stats.rx_over_errors++; break; + case 0x0800: dev->stats.rx_length_errors++; break; + case 0x1000: dev->stats.rx_frame_errors++; break; + case 0x1800: dev->stats.rx_length_errors++; break; + case 0x2000: dev->stats.rx_frame_errors++; break; + case 0x2800: dev->stats.rx_crc_errors++; break; + } + } else { + short pkt_len = rx_status & 0x7ff; + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, pkt_len + 5); + + pr_debug(" Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + if (skb != NULL) { + skb_reserve(skb, 2); + insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), + ((pkt_len+3)>>2)); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } else { + pr_debug("%s: couldn't allocate a sk_buff of" + " size %d.\n", dev->name, pkt_len); + dev->stats.rx_dropped++; + } + } + tc574_wait_for_completion(dev, RxDiscard); + } + + return worklimit; +} + +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct el3_private *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + struct mii_ioctl_data *data = if_mii(rq); + int phy = lp->phys & 0x1f; + + pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", + dev->name, rq->ifr_ifrn.ifrn_name, cmd, + data->phy_id, data->reg_num, data->val_in, data->val_out); + + switch(cmd) { + case SIOCGMIIPHY: /* Get the address of the PHY in use. */ + data->phy_id = phy; + fallthrough; + case SIOCGMIIREG: /* Read the specified MII register. */ + { + int saved_window; + unsigned long flags; + + spin_lock_irqsave(&lp->window_lock, flags); + saved_window = inw(ioaddr + EL3_CMD) >> 13; + EL3WINDOW(4); + data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, + data->reg_num & 0x1f); + EL3WINDOW(saved_window); + spin_unlock_irqrestore(&lp->window_lock, flags); + return 0; + } + case SIOCSMIIREG: /* Write the specified MII register */ + { + int saved_window; + unsigned long flags; + + spin_lock_irqsave(&lp->window_lock, flags); + saved_window = inw(ioaddr + EL3_CMD) >> 13; + EL3WINDOW(4); + mdio_write(ioaddr, data->phy_id & 0x1f, + data->reg_num & 0x1f, data->val_in); + EL3WINDOW(saved_window); + spin_unlock_irqrestore(&lp->window_lock, flags); + return 0; + } + default: + return -EOPNOTSUPP; + } +} + +/* The Odie chip has a 64 bin multicast filter, but the bit layout is not + documented. Until it is we revert to receiving all multicast frames when + any multicast reception is desired. + Note: My other drivers emit a log message whenever promiscuous mode is + entered to help detect password sniffers. This is less desirable on + typical PC card machines, so we omit the message. + */ + +static void set_rx_mode(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + + if (dev->flags & IFF_PROMISC) + outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, + ioaddr + EL3_CMD); + else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI)) + outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); + else + outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); +} + +static void set_multicast_list(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&lp->window_lock, flags); + set_rx_mode(dev); + spin_unlock_irqrestore(&lp->window_lock, flags); +} + +static int el3_close(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct el3_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); + + if (pcmcia_dev_present(link)) { + unsigned long flags; + + /* Turn off statistics ASAP. We update lp->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + /* Note: Switching to window 0 may disable the IRQ. */ + EL3WINDOW(0); + spin_lock_irqsave(&lp->window_lock, flags); + update_stats(dev); + spin_unlock_irqrestore(&lp->window_lock, flags); + + /* force interrupts off */ + outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); + } + + link->open--; + netif_stop_queue(dev); + timer_delete_sync(&lp->media); + + return 0; +} + +static const struct pcmcia_device_id tc574_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, tc574_ids); + +static struct pcmcia_driver tc574_driver = { + .owner = THIS_MODULE, + .name = "3c574_cs", + .probe = tc574_probe, + .remove = tc574_detach, + .id_table = tc574_ids, + .suspend = tc574_suspend, + .resume = tc574_resume, +}; +module_pcmcia_driver(tc574_driver); diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c new file mode 100644 index 000000000000..ea49be43b8c3 --- /dev/null +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -0,0 +1,974 @@ +/* ====================================================================== + * + * A PCMCIA ethernet driver for the 3com 3c589 card. + * + * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net + * + * 3c589_cs.c 1.162 2001/10/13 00:08:50 + * + * The network driver code is based on Donald Becker's 3c589 code: + * + * Written 1994 by Donald Becker. + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. This software may be used and + * distributed according to the terms of the GNU General Public License, + * incorporated herein by reference. + * Donald Becker may be reached at becker@scyld.com + * + * Updated for 2.5.x by Alan Cox + * + * ====================================================================== + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "3c589_cs" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +/* To minimize the size of the driver source I only define operating + * constants if they are used several times. You'll need the manual + * if you want to understand driver details. + */ + +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_TIMER 0x0a +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +#define EEPROM_READ 0x0080 +#define EEPROM_BUSY 0x8000 + +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) + +/* The top five bits written to EL3_CMD are a command, the lower + * 11 bits are the parameter, if applicable. + */ + +enum c509cmd { + TotalReset = 0<<11, + SelectWindow = 1<<11, + StartCoax = 2<<11, + RxDisable = 3<<11, + RxEnable = 4<<11, + RxReset = 5<<11, + RxDiscard = 8<<11, + TxEnable = 9<<11, + TxDisable = 10<<11, + TxReset = 11<<11, + FakeIntr = 12<<11, + AckIntr = 13<<11, + SetIntrEnb = 14<<11, + SetStatusEnb = 15<<11, + SetRxFilter = 16<<11, + SetRxThreshold = 17<<11, + SetTxThreshold = 18<<11, + SetTxStart = 19<<11, + StatsEnable = 21<<11, + StatsDisable = 22<<11, + StopCoax = 23<<11 +}; + +enum c509status { + IntLatch = 0x0001, + AdapterFailure = 0x0002, + TxComplete = 0x0004, + TxAvailable = 0x0008, + RxComplete = 0x0010, + RxEarly = 0x0020, + IntReq = 0x0040, + StatsFull = 0x0080, + CmdBusy = 0x1000 +}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, + RxMulticast = 2, + RxBroadcast = 4, + RxProm = 8 +}; + +/* Register window 1 offsets, the window used in normal operation. */ +#define TX_FIFO 0x00 +#define RX_FIFO 0x00 +#define RX_STATUS 0x08 +#define TX_STATUS 0x0B +#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ + +#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ +#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ +#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ +#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */ + +/* Time in jiffies before concluding Tx hung */ +#define TX_TIMEOUT ((400*HZ)/1000) + +struct el3_private { + struct pcmcia_device *p_dev; + /* For transceiver monitoring */ + struct timer_list media; + u16 media_status; + u16 fast_poll; + unsigned long last_irq; + spinlock_t lock; +}; + +static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" }; + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds "); +MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver"); +MODULE_LICENSE("GPL"); + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) + +/* Special hook for setting if_port when module is loaded */ +INT_MODULE_PARM(if_port, 0); + + +/*====================================================================*/ + +static int tc589_config(struct pcmcia_device *link); +static void tc589_release(struct pcmcia_device *link); + +static u16 read_eeprom(unsigned int ioaddr, int index); +static void tc589_reset(struct net_device *dev); +static void media_check(struct timer_list *t); +static int el3_config(struct net_device *dev, struct ifmap *map); +static int el3_open(struct net_device *dev); +static netdev_tx_t el3_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t el3_interrupt(int irq, void *dev_id); +static void update_stats(struct net_device *dev); +static struct net_device_stats *el3_get_stats(struct net_device *dev); +static int el3_rx(struct net_device *dev); +static int el3_close(struct net_device *dev); +static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); +static void set_rx_mode(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static const struct ethtool_ops netdev_ethtool_ops; + +static void tc589_detach(struct pcmcia_device *p_dev); + +static const struct net_device_ops el3_netdev_ops = { + .ndo_open = el3_open, + .ndo_stop = el3_close, + .ndo_start_xmit = el3_start_xmit, + .ndo_tx_timeout = el3_tx_timeout, + .ndo_set_config = el3_config, + .ndo_get_stats = el3_get_stats, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int tc589_probe(struct pcmcia_device *link) +{ + struct el3_private *lp; + struct net_device *dev; + int ret; + + dev_dbg(&link->dev, "3c589_attach()\n"); + + /* Create new ethernet device */ + dev = alloc_etherdev(sizeof(struct el3_private)); + if (!dev) + return -ENOMEM; + lp = netdev_priv(dev); + link->priv = dev; + lp->p_dev = link; + + spin_lock_init(&lp->lock); + link->resource[0]->end = 16; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; + + link->config_flags |= CONF_ENABLE_IRQ; + link->config_index = 1; + + dev->netdev_ops = &el3_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->ethtool_ops = &netdev_ethtool_ops; + + ret = tc589_config(link); + if (ret) + goto err_free_netdev; + + return 0; + +err_free_netdev: + free_netdev(dev); + return ret; +} + +static void tc589_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + dev_dbg(&link->dev, "3c589_detach\n"); + + unregister_netdev(dev); + + tc589_release(link); + + free_netdev(dev); +} /* tc589_detach */ + +static int tc589_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + int ret, i, j, multi = 0, fifo; + __be16 addr[ETH_ALEN / 2]; + unsigned int ioaddr; + static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; + u8 *buf; + size_t len; + + dev_dbg(&link->dev, "3c589_config\n"); + + /* Is this a 3c562? */ + if (link->manf_id != MANFID_3COM) + dev_info(&link->dev, "hmmm, is this really a 3Com card??\n"); + multi = (link->card_id == PRODID_3COM_3C562); + + link->io_lines = 16; + + /* For the 3c562, the base address must be xx00-xx7f */ + for (i = j = 0; j < 0x400; j += 0x10) { + if (multi && (j & 0x80)) + continue; + link->resource[0]->start = j ^ 0x300; + i = pcmcia_request_io(link); + if (i == 0) + break; + } + if (i != 0) + goto failed; + + ret = pcmcia_request_irq(link, el3_interrupt); + if (ret) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + ioaddr = dev->base_addr; + EL3WINDOW(0); + + /* The 3c589 has an extra EEPROM for configuration info, including + * the hardware address. The 3c562 puts the address in the CIS. + */ + len = pcmcia_get_tuple(link, 0x88, &buf); + if (buf && len >= 6) { + for (i = 0; i < 3; i++) + addr[i] = htons(le16_to_cpu(buf[i*2])); + kfree(buf); + } else { + kfree(buf); /* 0 < len < 6 */ + for (i = 0; i < 3; i++) + addr[i] = htons(read_eeprom(ioaddr, i)); + if (addr[0] == htons(0x6060)) { + dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n", + dev->base_addr, dev->base_addr+15); + goto failed; + } + } + eth_hw_addr_set(dev, (u8 *)addr); + + /* The address and resource configuration register aren't loaded from + * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. + */ + + outw(0x3f00, ioaddr + 8); + fifo = inl(ioaddr); + + /* The if_port symbol can be set when the module is loaded */ + if ((if_port >= 0) && (if_port <= 3)) + dev->if_port = if_port; + else + dev_err(&link->dev, "invalid if_port requested\n"); + + SET_NETDEV_DEV(dev, &link->dev); + + if (register_netdev(dev) != 0) { + dev_err(&link->dev, "register_netdev() failed\n"); + goto failed; + } + + netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n", + (multi ? "562" : "589"), dev->base_addr, dev->irq, + dev->dev_addr); + netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n", + (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3], + if_names[dev->if_port]); + return 0; + +failed: + tc589_release(link); + return -ENODEV; +} /* tc589_config */ + +static void tc589_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} + +static int tc589_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int tc589_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) { + tc589_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + +/*====================================================================*/ + +/* Use this for commands that may take time to finish */ + +static void tc589_wait_for_completion(struct net_device *dev, int cmd) +{ + int i = 100; + outw(cmd, dev->base_addr + EL3_CMD); + while (--i > 0) + if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) + break; + if (i == 0) + netdev_warn(dev, "command 0x%04x did not complete!\n", cmd); +} + +/* Read a word from the EEPROM using the regular EEPROM access register. + * Assume that we are in register window zero. + */ + +static u16 read_eeprom(unsigned int ioaddr, int index) +{ + int i; + outw(EEPROM_READ + index, ioaddr + 10); + /* Reading the eeprom takes 162 us */ + for (i = 1620; i >= 0; i--) + if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) + break; + return inw(ioaddr + 12); +} + +/* Set transceiver type, perhaps to something other than what the user + * specified in dev->if_port. + */ + +static void tc589_set_xcvr(struct net_device *dev, int if_port) +{ + struct el3_private *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + + EL3WINDOW(0); + switch (if_port) { + case 0: + case 1: + outw(0, ioaddr + 6); + break; + case 2: + outw(3<<14, ioaddr + 6); + break; + case 3: + outw(1<<14, ioaddr + 6); + break; + } + /* On PCMCIA, this just turns on the LED */ + outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD); + /* 10baseT interface, enable link beat and jabber check. */ + EL3WINDOW(4); + outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA); + EL3WINDOW(1); + if (if_port == 2) + lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000); + else + lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800); +} + +static void dump_status(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + EL3WINDOW(1); + netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS), + inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE)); + EL3WINDOW(4); + netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n", + inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08), + inw(ioaddr+0x0a)); + EL3WINDOW(1); +} + +/* Reset and restore all of the 3c589 registers. */ +static void tc589_reset(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + int i; + + EL3WINDOW(0); + outw(0x0001, ioaddr + 4); /* Activate board. */ + outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */ + + /* Set the station address in window 2. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + + tc589_set_xcvr(dev, dev->if_port); + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 9; i++) + inb(ioaddr+i); + inw(ioaddr + 10); + inw(ioaddr + 12); + + /* Switch to register set 1 for normal use. */ + EL3WINDOW(1); + + set_rx_mode(dev); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | AdapterFailure, ioaddr + EL3_CMD); +} + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + snprintf(info->bus_info, sizeof(info->bus_info), + "PCMCIA 0x%lx", dev->base_addr); +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + +static int el3_config(struct net_device *dev, struct ifmap *map) +{ + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if (map->port <= 3) { + WRITE_ONCE(dev->if_port, map->port); + netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); + tc589_set_xcvr(dev, dev->if_port); + } else { + return -EINVAL; + } + } + return 0; +} + +static int el3_open(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + if (!pcmcia_dev_present(link)) + return -ENODEV; + + link->open++; + netif_start_queue(dev); + + tc589_reset(dev); + timer_setup(&lp->media, media_check, 0); + mod_timer(&lp->media, jiffies + HZ); + + dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", + dev->name, inw(dev->base_addr + EL3_STATUS)); + + return 0; +} + +static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + unsigned int ioaddr = dev->base_addr; + + netdev_warn(dev, "Transmit timed out!\n"); + dump_status(dev); + dev->stats.tx_errors++; + netif_trans_update(dev); /* prevent tx timeout */ + /* Issue TX_RESET and TX_START commands. */ + tc589_wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + netif_wake_queue(dev); +} + +static void pop_tx_status(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + int i; + + /* Clear the Tx status stack. */ + for (i = 32; i > 0; i--) { + u_char tx_status = inb(ioaddr + TX_STATUS); + if (!(tx_status & 0x84)) + break; + /* reset transmitter on jabber error or underrun */ + if (tx_status & 0x30) + tc589_wait_for_completion(dev, TxReset); + if (tx_status & 0x38) { + netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status); + outw(TxEnable, ioaddr + EL3_CMD); + dev->stats.tx_aborted_errors++; + } + outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + } +} + +static netdev_tx_t el3_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct el3_private *priv = netdev_priv(dev); + unsigned long flags; + + netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n", + (long)skb->len, inw(ioaddr + EL3_STATUS)); + + spin_lock_irqsave(&priv->lock, flags); + + dev->stats.tx_bytes += skb->len; + + /* Put out the doubleword header... */ + outw(skb->len, ioaddr + TX_FIFO); + outw(0x00, ioaddr + TX_FIFO); + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + + if (inw(ioaddr + TX_FREE) <= 1536) { + netif_stop_queue(dev); + /* Interrupt us when the FIFO has room for max-sized packet. */ + outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); + } + + pop_tx_status(dev); + spin_unlock_irqrestore(&priv->lock, flags); + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +/* The EL3 interrupt handler. */ +static irqreturn_t el3_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct el3_private *lp = netdev_priv(dev); + unsigned int ioaddr; + __u16 status; + int i = 0, handled = 1; + + if (!netif_device_present(dev)) + return IRQ_NONE; + + ioaddr = dev->base_addr; + + netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); + + spin_lock(&lp->lock); + while ((status = inw(ioaddr + EL3_STATUS)) & + (IntLatch | RxComplete | StatsFull)) { + if ((status & 0xe000) != 0x2000) { + netdev_dbg(dev, "interrupt from dead card\n"); + handled = 0; + break; + } + if (status & RxComplete) + el3_rx(dev); + if (status & TxAvailable) { + netdev_dbg(dev, " TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + netif_wake_queue(dev); + } + if (status & TxComplete) + pop_tx_status(dev); + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts. */ + if (status & StatsFull) /* Empty statistics. */ + update_stats(dev); + if (status & RxEarly) { + /* Rx early is unused. */ + el3_rx(dev); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & AdapterFailure) { + u16 fifo_diag; + EL3WINDOW(4); + fifo_diag = inw(ioaddr + 4); + EL3WINDOW(1); + netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n", + fifo_diag); + if (fifo_diag & 0x0400) { + /* Tx overrun */ + tc589_wait_for_completion(dev, TxReset); + outw(TxEnable, ioaddr + EL3_CMD); + } + if (fifo_diag & 0x2000) { + /* Rx underrun */ + tc589_wait_for_completion(dev, RxReset); + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); + } + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } + } + if (++i > 10) { + netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n", + status); + /* Clear all interrupts */ + outw(AckIntr | 0xFF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + } + lp->last_irq = jiffies; + spin_unlock(&lp->lock); + netdev_dbg(dev, "exiting interrupt, status %4.4x.\n", + inw(ioaddr + EL3_STATUS)); + return IRQ_RETVAL(handled); +} + +static void media_check(struct timer_list *t) +{ + struct el3_private *lp = timer_container_of(lp, t, media); + struct net_device *dev = lp->p_dev->priv; + unsigned int ioaddr = dev->base_addr; + u16 media, errs; + unsigned long flags; + + if (!netif_device_present(dev)) + goto reschedule; + + /* Check for pending interrupt with expired latency timer: with + * this, we can limp along even if the interrupt is blocked + */ + if ((inw(ioaddr + EL3_STATUS) & IntLatch) && + (inb(ioaddr + EL3_TIMER) == 0xff)) { + if (!lp->fast_poll) + netdev_warn(dev, "interrupt(s) dropped!\n"); + + local_irq_save(flags); + el3_interrupt(dev->irq, dev); + local_irq_restore(flags); + + lp->fast_poll = HZ; + } + if (lp->fast_poll) { + lp->fast_poll--; + lp->media.expires = jiffies + HZ/100; + add_timer(&lp->media); + return; + } + + /* lp->lock guards the EL3 window. Window should always be 1 except + * when the lock is held + */ + + spin_lock_irqsave(&lp->lock, flags); + EL3WINDOW(4); + media = inw(ioaddr+WN4_MEDIA) & 0xc810; + + /* Ignore collisions unless we've had no irq's recently */ + if (time_before(jiffies, lp->last_irq + HZ)) { + media &= ~0x0010; + } else { + /* Try harder to detect carrier errors */ + EL3WINDOW(6); + outw(StatsDisable, ioaddr + EL3_CMD); + errs = inb(ioaddr + 0); + outw(StatsEnable, ioaddr + EL3_CMD); + dev->stats.tx_carrier_errors += errs; + if (errs || (lp->media_status & 0x0010)) + media |= 0x0010; + } + + if (media != lp->media_status) { + if ((media & lp->media_status & 0x8000) && + ((lp->media_status ^ media) & 0x0800)) + netdev_info(dev, "%s link beat\n", + (lp->media_status & 0x0800 ? "lost" : "found")); + else if ((media & lp->media_status & 0x4000) && + ((lp->media_status ^ media) & 0x0010)) + netdev_info(dev, "coax cable %s\n", + (lp->media_status & 0x0010 ? "ok" : "problem")); + if (dev->if_port == 0) { + if (media & 0x8000) { + if (media & 0x0800) + netdev_info(dev, "flipped to 10baseT\n"); + else + tc589_set_xcvr(dev, 2); + } else if (media & 0x4000) { + if (media & 0x0010) + tc589_set_xcvr(dev, 1); + else + netdev_info(dev, "flipped to 10base2\n"); + } + } + lp->media_status = media; + } + + EL3WINDOW(1); + spin_unlock_irqrestore(&lp->lock, flags); + +reschedule: + lp->media.expires = jiffies + HZ; + add_timer(&lp->media); +} + +static struct net_device_stats *el3_get_stats(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + unsigned long flags; + struct pcmcia_device *link = lp->p_dev; + + if (pcmcia_dev_present(link)) { + spin_lock_irqsave(&lp->lock, flags); + update_stats(dev); + spin_unlock_irqrestore(&lp->lock, flags); + } + return &dev->stats; +} + +/* Update statistics. We change to register window 6, so this should be run +* single-threaded if the device is active. This is expected to be a rare +* operation, and it's simpler for the rest of the driver to assume that +* window 1 is always valid rather than use a special window-state variable. +* +* Caller must hold the lock for this +*/ + +static void update_stats(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + + netdev_dbg(dev, "updating the statistics.\n"); + /* Turn off statistics updates while reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + dev->stats.tx_carrier_errors += inb(ioaddr + 0); + dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ + inb(ioaddr + 2); + dev->stats.collisions += inb(ioaddr + 3); + dev->stats.tx_window_errors += inb(ioaddr + 4); + dev->stats.rx_fifo_errors += inb(ioaddr + 5); + dev->stats.tx_packets += inb(ioaddr + 6); + /* Rx packets */ + inb(ioaddr + 7); + /* Tx deferrals */ + inb(ioaddr + 8); + /* Rx octets */ + inw(ioaddr + 10); + /* Tx octets */ + inw(ioaddr + 12); + + /* Back to window 1, and turn statistics back on. */ + EL3WINDOW(1); + outw(StatsEnable, ioaddr + EL3_CMD); +} + +static int el3_rx(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + int worklimit = 32; + short rx_status; + + netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); + while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) && + worklimit > 0) { + worklimit--; + if (rx_status & 0x4000) { /* Error, update stats. */ + short error = rx_status & 0x3800; + dev->stats.rx_errors++; + switch (error) { + case 0x0000: + dev->stats.rx_over_errors++; + break; + case 0x0800: + dev->stats.rx_length_errors++; + break; + case 0x1000: + dev->stats.rx_frame_errors++; + break; + case 0x1800: + dev->stats.rx_length_errors++; + break; + case 0x2000: + dev->stats.rx_frame_errors++; + break; + case 0x2800: + dev->stats.rx_crc_errors++; + break; + } + } else { + short pkt_len = rx_status & 0x7ff; + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, pkt_len + 5); + + netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + if (skb != NULL) { + skb_reserve(skb, 2); + insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), + (pkt_len+3)>>2); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } else { + netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n", + pkt_len); + dev->stats.rx_dropped++; + } + } + /* Pop the top of the Rx FIFO */ + tc589_wait_for_completion(dev, RxDiscard); + } + if (worklimit == 0) + netdev_warn(dev, "too much work in el3_rx!\n"); + return 0; +} + +static void set_rx_mode(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + u16 opts = SetRxFilter | RxStation | RxBroadcast; + + if (dev->flags & IFF_PROMISC) + opts |= RxMulticast | RxProm; + else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI)) + opts |= RxMulticast; + outw(opts, ioaddr + EL3_CMD); +} + +static void set_multicast_list(struct net_device *dev) +{ + struct el3_private *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + set_rx_mode(dev); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int el3_close(struct net_device *dev) +{ + struct el3_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + unsigned int ioaddr = dev->base_addr; + + dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); + + if (pcmcia_dev_present(link)) { + /* Turn off statistics ASAP. We update dev->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + if (dev->if_port == 2) + /* Turn off thinnet power. Green! */ + outw(StopCoax, ioaddr + EL3_CMD); + else if (dev->if_port == 1) { + /* Disable link beat and jabber */ + EL3WINDOW(4); + outw(0, ioaddr + WN4_MEDIA); + } + + /* Switching back to window 0 disables the IRQ. */ + EL3WINDOW(0); + /* But we explicitly zero the IRQ line select anyway. */ + outw(0x0f00, ioaddr + WN0_IRQ); + + /* Check if the card still exists */ + if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000) + update_stats(dev); + } + + link->open--; + netif_stop_queue(dev); + timer_delete_sync(&lp->media); + + return 0; +} + +static const struct pcmcia_device_id tc589_ids[] = { + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562), + PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589), + PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, tc589_ids); + +static struct pcmcia_driver tc589_driver = { + .owner = THIS_MODULE, + .name = "3c589_cs", + .probe = tc589_probe, + .remove = tc589_detach, + .id_table = tc589_ids, + .suspend = tc589_suspend, + .resume = tc589_resume, +}; +module_pcmcia_driver(tc589_driver); diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig index 399cb6c56198..1fbab79e2be4 100644 --- a/drivers/net/ethernet/3com/Kconfig +++ b/drivers/net/ethernet/3com/Kconfig @@ -17,6 +17,37 @@ config NET_VENDOR_3COM To compile this driver as a module, choose M here. The module will be called 3c509. +config 3C515 + tristate "3c515 ISA \"Fast EtherLink\"" + depends on ISA && ISA_DMA_API && !PPC32 + select NETDEV_LEGACY_INIT + help + If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet + network card, say Y here. + + To compile this driver as a module, choose M here. The module + will be called 3c515. + +config PCMCIA_3C574 + tristate "3Com 3c574 PCMCIA support" + depends on PCMCIA && HAS_IOPORT + help + Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA + (PC-card) Fast Ethernet card to your computer. + + To compile this driver as a module, choose M here: the module will be + called 3c574_cs. If unsure, say N. + +config PCMCIA_3C589 + tristate "3Com 3c589 PCMCIA support" + depends on PCMCIA && HAS_IOPORT + help + Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA + (PC-card) Ethernet card to your computer. + + To compile this driver as a module, choose M here: the module will be + called 3c589_cs. If unsure, say N. + config VORTEX tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support" depends on (PCI || EISA) && HAS_IOPORT_MAP diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile index 5c4d07f1d456..f8b73babc510 100644 --- a/drivers/net/ethernet/3com/Makefile +++ b/drivers/net/ethernet/3com/Makefile @@ -3,5 +3,8 @@ # obj-$(CONFIG_EL3) += 3c509.o +obj-$(CONFIG_3C515) += 3c515.o +obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o +obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_TYPHOON) += typhoon.o diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index 5d12a595ab19..345f250781c6 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -17,6 +17,18 @@ config NET_VENDOR_8390 if NET_VENDOR_8390 +config PCMCIA_AXNET + tristate "Asix AX88190 PCMCIA support" + depends on PCMCIA && HAS_IOPORT + help + Say Y here if you intend to attach an Asix AX88190-based PCMCIA + (PC-card) Fast Ethernet card to your computer. These cards are + nearly NE2000 compatible but need a separate driver due to a few + misfeatures. + + To compile this driver as a module, choose M here: the module will be + called axnet_cs. If unsure, say N. + config AX88796 tristate "ASIX AX88796 NE2000 clone support" if !ZORRO depends on (ARM || MIPS || SUPERH || ZORRO || COMPILE_TEST) @@ -155,6 +167,35 @@ config STNIC If unsure, say N. +config ULTRA + tristate "SMC Ultra support" + depends on ISA + select NETDEV_LEGACY_INIT + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y here. + + Important: There have been many reports that, with some motherboards + mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, + such as some BusLogic models) causes corruption problems with many + operating systems. The Linux smc-ultra driver has a work-around for + this but keep it in mind if you have such a SCSI card and have + problems. + + To compile this driver as a module, choose M here. The module + will be called smc-ultra. + +config WD80x3 + tristate "WD80*3 support" + depends on ISA + select NETDEV_LEGACY_INIT + select CRC32 + help + If you have a network (Ethernet) card of this type, say Y here. + + To compile this driver as a module, choose M here. The module + will be called wd. + config ZORRO8390 tristate "Zorro NS8390-based Ethernet support" depends on ZORRO diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile index bca5babdadc7..85c83c566ec6 100644 --- a/drivers/net/ethernet/8390/Makefile +++ b/drivers/net/ethernet/8390/Makefile @@ -11,7 +11,10 @@ obj-$(CONFIG_HYDRA) += hydra.o obj-$(CONFIG_MCF8390) += mcf8390.o obj-$(CONFIG_NE2000) += ne.o 8390p.o obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o +obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o obj-$(CONFIG_STNIC) += stnic.o 8390.o +obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o +obj-$(CONFIG_WD80x3) += wd.o 8390.o obj-$(CONFIG_XSURF100) += xsurf100.o obj-$(CONFIG_ZORRO8390) += zorro8390.o diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c new file mode 100644 index 000000000000..7c8213011b5c --- /dev/null +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -0,0 +1,1707 @@ +// SPDX-License-Identifier: GPL-1.0+ + +/*====================================================================== + + A PCMCIA ethernet driver for Asix AX88190-based cards + + The Asix AX88190 is a NS8390-derived chipset with a few nasty + idiosyncracies that make it very inconvenient to support with a + standard 8390 driver. This driver is based on pcnet_cs, with the + tweaked 8390 code grafted on the end. Much of what I did was to + clean up and update a similar driver supplied by Asix, which was + adapted by William Lee, william@asix.com.tw. + + Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net + + axnet_cs.c 1.28 2002/06/29 06:27:37 + + The network driver code is based on Donald Becker's NE2000 code: + + Written 1992,1993 by Donald Becker. + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + Donald Becker may be reached at becker@scyld.com + +======================================================================*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "8390.h" + +#include +#include +#include +#include + +#include +#include +#include + +#define AXNET_CMD 0x00 +#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define AXNET_MII_EEP 0x14 /* Offset of MII access port */ +#define AXNET_TEST 0x15 /* Offset of TEST Register port */ +#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */ + +#define AXNET_START_PG 0x40 /* First page of TX buffer */ +#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */ + +#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */ + +#define IS_AX88190 0x0001 +#define IS_AX88790 0x0002 + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds "); +MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver"); +MODULE_LICENSE("GPL"); + + +/*====================================================================*/ + +static int axnet_config(struct pcmcia_device *link); +static void axnet_release(struct pcmcia_device *link); +static int axnet_open(struct net_device *dev); +static int axnet_close(struct net_device *dev); +static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue); +static irqreturn_t ei_irq_wrapper(int irq, void *dev_id); +static void ei_watchdog(struct timer_list *t); +static void axnet_reset_8390(struct net_device *dev); + +static int mdio_read(unsigned int addr, int phy_id, int loc); +static void mdio_write(unsigned int addr, int phy_id, int loc, int value); + +static void get_8390_hdr(struct net_device *, + struct e8390_pkt_hdr *, int); +static void block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void block_output(struct net_device *dev, int count, + const u_char *buf, const int start_page); + +static void axnet_detach(struct pcmcia_device *p_dev); + +static void AX88190_init(struct net_device *dev, int startp); +static int ax_open(struct net_device *dev); +static int ax_close(struct net_device *dev); +static irqreturn_t ax_interrupt(int irq, void *dev_id); + +/*====================================================================*/ + +struct axnet_dev { + struct pcmcia_device *p_dev; + caddr_t base; + struct timer_list watchdog; + int stale, fast_poll; + u_short link_status; + u_char duplex_flag; + int phy_id; + int flags; + int active_low; +}; + +static inline struct axnet_dev *PRIV(struct net_device *dev) +{ + void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device); + return p; +} + +static const struct net_device_ops axnet_netdev_ops = { + .ndo_open = axnet_open, + .ndo_stop = axnet_close, + .ndo_eth_ioctl = axnet_ioctl, + .ndo_start_xmit = axnet_start_xmit, + .ndo_tx_timeout = axnet_tx_timeout, + .ndo_get_stats = get_stats, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int axnet_probe(struct pcmcia_device *link) +{ + struct axnet_dev *info; + struct net_device *dev; + struct ei_device *ei_local; + + dev_dbg(&link->dev, "axnet_attach()\n"); + + dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(struct axnet_dev)); + if (!dev) + return -ENOMEM; + + ei_local = netdev_priv(dev); + spin_lock_init(&ei_local->page_lock); + + info = PRIV(dev); + info->p_dev = link; + link->priv = dev; + link->config_flags |= CONF_ENABLE_IRQ; + + dev->netdev_ops = &axnet_netdev_ops; + + dev->watchdog_timeo = TX_TIMEOUT; + + return axnet_config(link); +} /* axnet_attach */ + +static void axnet_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link); + + unregister_netdev(dev); + + axnet_release(link); + + free_netdev(dev); +} /* axnet_detach */ + +/*====================================================================== + + This probes for a card's hardware address by reading the PROM. + +======================================================================*/ + +static int get_prom(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + unsigned int ioaddr = dev->base_addr; + u8 addr[ETH_ALEN]; + int i, j; + + /* This is based on drivers/net/ethernet/8390/ne.c */ + struct { + u_char value, offset; + } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x01, EN0_DCFG}, /* Set word-wide access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {0x10, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */ + {0x04, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + + /* Not much of a test, but the alternatives are messy */ + if (link->config_base != 0x03c0) + return 0; + + axnet_reset_8390(dev); + mdelay(10); + + for (i = 0; i < ARRAY_SIZE(program_seq); i++) + outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); + + for (i = 0; i < 6; i += 2) { + j = inw(ioaddr + AXNET_DATAPORT); + addr[i] = j & 0xff; + addr[i+1] = j >> 8; + } + eth_hw_addr_set(dev, addr); + + return 1; +} /* get_prom */ + +static int try_io_port(struct pcmcia_device *link) +{ + int j, ret; + link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + if (link->resource[0]->end == 32) { + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + /* for master/slave multifunction cards */ + if (link->resource[1]->end > 0) + link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + } else { + /* This should be two 16-port windows */ + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; + } + if (link->resource[0]->start == 0) { + for (j = 0; j < 0x400; j += 0x20) { + link->resource[0]->start = j ^ 0x300; + link->resource[1]->start = (j ^ 0x300) + 0x10; + link->io_lines = 16; + ret = pcmcia_request_io(link); + if (ret == 0) + return ret; + } + return ret; + } else { + return pcmcia_request_io(link); + } +} + +static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + p_dev->config_index = 0x05; + if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32) + return -ENODEV; + + return try_io_port(p_dev); +} + +static int axnet_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct axnet_dev *info = PRIV(dev); + int i, j, j2, ret; + + dev_dbg(&link->dev, "axnet_config(0x%p)\n", link); + + /* don't trust the CIS on this; Linksys got it wrong */ + link->config_regs = 0x63; + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + ret = pcmcia_loop_config(link, axnet_configcheck, NULL); + if (ret != 0) + goto failed; + + if (!link->irq) + goto failed; + + if (resource_size(link->resource[1]) == 8) + link->config_flags |= CONF_ENABLE_SPKR; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + + if (!get_prom(link)) { + pr_notice("this is not an AX88190 card!\n"); + pr_notice("use pcnet_cs instead.\n"); + goto failed; + } + + ei_status.name = "AX88190"; + ei_status.word16 = 1; + ei_status.tx_start_page = AXNET_START_PG; + ei_status.rx_start_page = AXNET_START_PG + TX_PAGES; + ei_status.stop_page = AXNET_STOP_PG; + ei_status.reset_8390 = axnet_reset_8390; + ei_status.get_8390_hdr = get_8390_hdr; + ei_status.block_input = block_input; + ei_status.block_output = block_output; + + if (inb(dev->base_addr + AXNET_TEST) != 0) + info->flags |= IS_AX88790; + else + info->flags |= IS_AX88190; + + if (info->flags & IS_AX88790) + outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */ + + info->active_low = 0; + + for (i = 0; i < 32; i++) { + j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); + j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2); + if (j == j2) continue; + if ((j != 0) && (j != 0xffff)) break; + } + + if (i == 32) { + /* Maybe PHY is in power down mode. (PPD_SET = 1) + Bit 2 of CCSR is active low. */ + pcmcia_write_config_byte(link, CISREG_CCSR, 0x04); + for (i = 0; i < 32; i++) { + j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); + j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2); + if (j == j2) continue; + if ((j != 0) && (j != 0xffff)) { + info->active_low = 1; + break; + } + } + } + + info->phy_id = (i < 32) ? i : -1; + SET_NETDEV_DEV(dev, &link->dev); + + if (register_netdev(dev) != 0) { + pr_notice("register_netdev() failed\n"); + goto failed; + } + + netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n", + ((info->flags & IS_AX88790) ? 7 : 1), + dev->base_addr, dev->irq, dev->dev_addr); + if (info->phy_id != -1) { + netdev_dbg(dev, " MII transceiver at index %d, status %x\n", + info->phy_id, j); + } else { + netdev_notice(dev, " No MII transceivers found!\n"); + } + return 0; + +failed: + axnet_release(link); + return -ENODEV; +} /* axnet_config */ + +static void axnet_release(struct pcmcia_device *link) +{ + pcmcia_disable_device(link); +} + +static int axnet_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int axnet_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct axnet_dev *info = PRIV(dev); + + if (link->open) { + if (info->active_low == 1) + pcmcia_write_config_byte(link, CISREG_CCSR, 0x04); + + axnet_reset_8390(dev); + AX88190_init(dev, 1); + netif_device_attach(dev); + } + + return 0; +} + + +/*====================================================================== + + MII interface support + +======================================================================*/ + +#define MDIO_SHIFT_CLK 0x01 +#define MDIO_DATA_WRITE0 0x00 +#define MDIO_DATA_WRITE1 0x08 +#define MDIO_DATA_READ 0x04 +#define MDIO_MASK 0x0f +#define MDIO_ENB_IN 0x02 + +static void mdio_sync(unsigned int addr) +{ + int bits; + for (bits = 0; bits < 32; bits++) { + outb_p(MDIO_DATA_WRITE1, addr); + outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); + } +} + +static int mdio_read(unsigned int addr, int phy_id, int loc) +{ + u_int cmd = (0xf6<<10)|(phy_id<<5)|loc; + int i, retval = 0; + + mdio_sync(addr); + for (i = 14; i >= 0; i--) { + int dat = (cmd&(1< 0; i--) { + outb_p(MDIO_ENB_IN, addr); + retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0); + outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(unsigned int addr, int phy_id, int loc, int value) +{ + u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; + int i; + + mdio_sync(addr); + for (i = 31; i >= 0; i--) { + int dat = (cmd&(1<= 0; i--) { + outb_p(MDIO_ENB_IN, addr); + outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr); + } +} + +/*====================================================================*/ + +static int axnet_open(struct net_device *dev) +{ + int ret; + struct axnet_dev *info = PRIV(dev); + struct pcmcia_device *link = info->p_dev; + unsigned int nic_base = dev->base_addr; + + dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name); + + if (!pcmcia_dev_present(link)) + return -ENODEV; + + outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ + ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev); + if (ret) + return ret; + + link->open++; + + info->link_status = 0x00; + timer_setup(&info->watchdog, ei_watchdog, 0); + mod_timer(&info->watchdog, jiffies + HZ); + + return ax_open(dev); +} /* axnet_open */ + +/*====================================================================*/ + +static int axnet_close(struct net_device *dev) +{ + struct axnet_dev *info = PRIV(dev); + struct pcmcia_device *link = info->p_dev; + + dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name); + + ax_close(dev); + free_irq(dev->irq, dev); + + link->open--; + netif_stop_queue(dev); + timer_delete_sync(&info->watchdog); + + return 0; +} /* axnet_close */ + +/*====================================================================== + + Hard reset the card. This used to pause for the same period that + a 8390 reset command required, but that shouldn't be necessary. + +======================================================================*/ + +static void axnet_reset_8390(struct net_device *dev) +{ + unsigned int nic_base = dev->base_addr; + int i; + + ei_status.txing = ei_status.dmaing = 0; + + outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD); + + outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET); + + for (i = 0; i < 100; i++) { + if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0) + break; + udelay(100); + } + outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */ + + if (i == 100) + netdev_err(dev, "axnet_reset_8390() did not complete\n"); + +} /* axnet_reset_8390 */ + +/*====================================================================*/ + +static irqreturn_t ei_irq_wrapper(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + PRIV(dev)->stale = 0; + return ax_interrupt(irq, dev_id); +} + +static void ei_watchdog(struct timer_list *t) +{ + struct axnet_dev *info = timer_container_of(info, t, watchdog); + struct net_device *dev = info->p_dev->priv; + unsigned int nic_base = dev->base_addr; + unsigned int mii_addr = nic_base + AXNET_MII_EEP; + u_short link; + + if (!netif_device_present(dev)) goto reschedule; + + /* Check for pending interrupt with expired latency timer: with + this, we can limp along even if the interrupt is blocked */ + if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) { + if (!info->fast_poll) + netdev_info(dev, "interrupt(s) dropped!\n"); + ei_irq_wrapper(dev->irq, dev); + info->fast_poll = HZ; + } + if (info->fast_poll) { + info->fast_poll--; + info->watchdog.expires = jiffies + 1; + add_timer(&info->watchdog); + return; + } + + if (info->phy_id < 0) + goto reschedule; + link = mdio_read(mii_addr, info->phy_id, 1); + if (!link || (link == 0xffff)) { + netdev_info(dev, "MII is missing!\n"); + info->phy_id = -1; + goto reschedule; + } + + link &= 0x0004; + if (link != info->link_status) { + u_short p = mdio_read(mii_addr, info->phy_id, 5); + netdev_info(dev, "%s link beat\n", link ? "found" : "lost"); + if (link) { + info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00; + if (p) + netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n", + (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H'); + else + netdev_info(dev, "link partner did not autonegotiate\n"); + AX88190_init(dev, 1); + } + info->link_status = link; + } + +reschedule: + info->watchdog.expires = jiffies + HZ; + add_timer(&info->watchdog); +} + +/*====================================================================*/ + +static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct axnet_dev *info = PRIV(dev); + struct mii_ioctl_data *data = if_mii(rq); + unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP; + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = info->phy_id; + fallthrough; + case SIOCGMIIREG: /* Read MII PHY register. */ + data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f); + return 0; + case SIOCSMIIREG: /* Write MII PHY register. */ + mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in); + return 0; + } + return -EOPNOTSUPP; +} + +/*====================================================================*/ + +static void get_8390_hdr(struct net_device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page) +{ + unsigned int nic_base = dev->base_addr; + + outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ + outb_p(ring_page, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD); + + insw(nic_base + AXNET_DATAPORT, hdr, + sizeof(struct e8390_pkt_hdr)>>1); + /* Fix for big endian systems */ + hdr->count = le16_to_cpu(hdr->count); + +} + +/*====================================================================*/ + +static void block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + unsigned int nic_base = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + char *buf = skb->data; + + if ((netif_msg_rx_status(ei_local)) && (count != 4)) + netdev_dbg(dev, "[bi=%d]\n", count+4); + outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD); + + insw(nic_base + AXNET_DATAPORT,buf,count>>1); + if (count & 0x01) { + buf[count-1] = inb(nic_base + AXNET_DATAPORT); + } +} + +/*====================================================================*/ + +static void block_output(struct net_device *dev, int count, + const u_char *buf, const int start_page) +{ + unsigned int nic_base = dev->base_addr; + + pr_debug("%s: [bo=%d]\n", dev->name, count); + + /* Round the count up for word writes. Do we need to do this? + What effect will an odd byte count have on the 8390? + I should check someday. */ + if (count & 0x01) + count++; + + outb_p(0x00, nic_base + EN0_RSARLO); + outb_p(start_page, nic_base + EN0_RSARHI); + outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD); + outsw(nic_base + AXNET_DATAPORT, buf, count>>1); +} + +static const struct pcmcia_device_id axnet_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081), + PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106), + PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), + PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202), + PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090), + PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc), + PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef), + PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef), + PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1), + PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc), + PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061), + PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2), + PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8), + PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609), + PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058), + PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, axnet_ids); + +static struct pcmcia_driver axnet_cs_driver = { + .owner = THIS_MODULE, + .name = "axnet_cs", + .probe = axnet_probe, + .remove = axnet_detach, + .id_table = axnet_ids, + .suspend = axnet_suspend, + .resume = axnet_resume, +}; +module_pcmcia_driver(axnet_cs_driver); + +/*====================================================================*/ + +/* 8390.c: A general NS8390 ethernet driver core for linux. */ +/* + Written 1992-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + This is the chip-specific code for many 8390-based ethernet adaptors. + This is not a complete driver, it must be combined with board-specific + code such as ne.c, wd.c, 3c503.c, etc. + + Seeing how at least eight drivers use this code, (not counting the + PCMCIA ones either) it is easy to break some card by what seems like + a simple innocent change. Please contact me or Donald if you think + you have found something that needs changing. -- PG + + Changelog: + + Paul Gortmaker : remove set_bit lock, other cleanups. + Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to + ei_block_input() for eth_io_copy_and_sum(). + Paul Gortmaker : exchange static int ei_pingpong for a #define, + also add better Tx error handling. + Paul Gortmaker : rewrite Rx overrun handling as per NS specs. + Alexey Kuznetsov : use the 8390's six bit hash multicast filter. + Paul Gortmaker : tweak ANK's above multicast changes a bit. + Paul Gortmaker : update packet statistics for v2.1.x + Alan Cox : support arbitrary stupid port mappings on the + 68K Macintosh. Support >16bit I/O spaces + Paul Gortmaker : add kmod support for auto-loading of the 8390 + module by all drivers that require it. + Alan Cox : Spinlocking work, added 'BUG_83C690' + Paul Gortmaker : Separate out Tx timeout code from Tx path. + + Sources: + The National Semiconductor LAN Databook, and the 3Com 3c503 databook. + + */ + +#include +#include +#include +#include +#include + +#define BUG_83C690 + +/* These are the operational function interfaces to board-specific + routines. + void reset_8390(struct net_device *dev) + Resets the board associated with DEV, including a hardware reset of + the 8390. This is only called when there is a transmit timeout, and + it is always followed by 8390_init(). + void block_output(struct net_device *dev, int count, const unsigned char *buf, + int start_page) + Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The + "page" value uses the 8390's 256-byte pages. + void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page) + Read the 4 byte, page aligned 8390 header. *If* there is a + subsequent read, it will be of the rest of the packet. + void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) + Read COUNT bytes from the packet buffer into the skb data area. Start + reading from RING_OFFSET, the address as the 8390 sees it. This will always + follow the read of the 8390 header. +*/ +#define ei_reset_8390 (ei_local->reset_8390) +#define ei_block_output (ei_local->block_output) +#define ei_block_input (ei_local->block_input) +#define ei_get_8390_hdr (ei_local->get_8390_hdr) + +/* Index to functions. */ +static void ei_tx_intr(struct net_device *dev); +static void ei_tx_err(struct net_device *dev); +static void ei_receive(struct net_device *dev); +static void ei_rx_overrun(struct net_device *dev); + +/* Routines generic to NS8390-based boards. */ +static void NS8390_trigger_send(struct net_device *dev, unsigned int length, + int start_page); +static void do_set_multicast_list(struct net_device *dev); + +/* + * SMP and the 8390 setup. + * + * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is + * a page register that controls bank and packet buffer access. We guard + * this with ei_local->page_lock. Nobody should assume or set the page other + * than zero when the lock is not held. Lock holders must restore page 0 + * before unlocking. Even pure readers must take the lock to protect in + * page 0. + * + * To make life difficult the chip can also be very slow. We therefore can't + * just use spinlocks. For the longer lockups we disable the irq the device + * sits on and hold the lock. We must hold the lock because there is a dual + * processor case other than interrupts (get stats/set multicast list in + * parallel with each other and transmit). + * + * Note: in theory we can just disable the irq on the card _but_ there is + * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs" + * enter lock, take the queued irq. So we waddle instead of flying. + * + * Finally by special arrangement for the purpose of being generally + * annoying the transmit function is called bh atomic. That places + * restrictions on the user context callers as disable_irq won't save + * them. + */ + +/** + * ax_open - Open/initialize the board. + * @dev: network device to initialize + * + * This routine goes all-out, setting everything + * up anew at each open, even though many of these registers should only + * need to be set once at boot. + */ +static int ax_open(struct net_device *dev) +{ + unsigned long flags; + struct ei_device *ei_local = netdev_priv(dev); + + /* + * Grab the page lock so we own the register set, then call + * the init function. + */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + AX88190_init(dev, 1); + /* Set the flag before we drop the lock, That way the IRQ arrives + after its set and we get no silly warnings */ + netif_start_queue(dev); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + ei_local->irqlock = 0; + return 0; +} + +#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock) + +/** + * ax_close - shut down network device + * @dev: network device to close + * + * Opposite of ax_open(). Only used when "ifconfig down" is done. + */ +static int ax_close(struct net_device *dev) +{ + unsigned long flags; + + /* + * Hold the page lock during close + */ + + spin_lock_irqsave(&dev_lock(dev), flags); + AX88190_init(dev, 0); + spin_unlock_irqrestore(&dev_lock(dev), flags); + netif_stop_queue(dev); + return 0; +} + +/** + * axnet_tx_timeout - handle transmit time out condition + * @dev: network device which has apparently fallen asleep + * @txqueue: unused + * + * Called by kernel when device never acknowledges a transmit has + * completed (or failed) - i.e. never posted a Tx related interrupt. + */ + +static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + long e8390_base = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + int txsr, isr, tickssofar = jiffies - dev_trans_start(dev); + unsigned long flags; + + dev->stats.tx_errors++; + + spin_lock_irqsave(&ei_local->page_lock, flags); + txsr = inb(e8390_base+EN0_TSR); + isr = inb(e8390_base+EN0_ISR); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n", + (txsr & ENTSR_ABT) ? "excess collisions." : + (isr) ? "lost interrupt?" : "cable problem?", + txsr, isr, tickssofar); + + if (!isr && !dev->stats.tx_packets) + { + /* The 8390 probably hasn't gotten on the cable yet. */ + ei_local->interface_num ^= 1; /* Try a different xcvr. */ + } + + /* Ugly but a reset can be slow, yet must be protected */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + + /* Try to restart the card. Perhaps the user has fixed something. */ + ei_reset_8390(dev); + AX88190_init(dev, 1); + + spin_unlock_irqrestore(&ei_local->page_lock, flags); + netif_wake_queue(dev); +} + +/** + * axnet_start_xmit - begin packet transmission + * @skb: packet to be sent + * @dev: network device to which packet is sent + * + * Sends a packet to an 8390 network device. + */ + +static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + long e8390_base = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + int length, send_length, output_page; + unsigned long flags; + u8 packet[ETH_ZLEN]; + + netif_stop_queue(dev); + + length = skb->len; + + /* Mask interrupts from the ethercard. + SMP: We have to grab the lock here otherwise the IRQ handler + on another CPU can flip window and race the IRQ mask set. We end + up trashing the mcast filter not disabling irqs if we don't lock */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + outb_p(0x00, e8390_base + EN0_IMR); + + /* + * Slow phase with lock held. + */ + + ei_local->irqlock = 1; + + send_length = max(length, ETH_ZLEN); + + /* + * We have two Tx slots available for use. Find the first free + * slot, and then perform some sanity checks. With two Tx bufs, + * you get very close to transmitting back-to-back packets. With + * only one Tx buf, the transmitter sits idle while you reload the + * card, leaving a substantial gap between each transmitted packet. + */ + + if (ei_local->tx1 == 0) + { + output_page = ei_local->tx_start_page; + ei_local->tx1 = send_length; + if ((netif_msg_tx_queued(ei_local)) && + ei_local->tx2 > 0) + netdev_dbg(dev, + "idle transmitter tx2=%d, lasttx=%d, txing=%d\n", + ei_local->tx2, ei_local->lasttx, + ei_local->txing); + } + else if (ei_local->tx2 == 0) + { + output_page = ei_local->tx_start_page + TX_PAGES/2; + ei_local->tx2 = send_length; + if ((netif_msg_tx_queued(ei_local)) && + ei_local->tx1 > 0) + netdev_dbg(dev, + "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n", + ei_local->tx1, ei_local->lasttx, + ei_local->txing); + } + else + { /* We should never get here. */ + netif_dbg(ei_local, tx_err, dev, + "No Tx buffers free! tx1=%d tx2=%d last=%d\n", + ei_local->tx1, ei_local->tx2, + ei_local->lasttx); + ei_local->irqlock = 0; + netif_stop_queue(dev); + outb_p(ENISR_ALL, e8390_base + EN0_IMR); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + dev->stats.tx_errors++; + return NETDEV_TX_BUSY; + } + + /* + * Okay, now upload the packet and trigger a send if the transmitter + * isn't already sending. If it is busy, the interrupt handler will + * trigger the send later, upon receiving a Tx done interrupt. + */ + + if (length == skb->len) + ei_block_output(dev, length, skb->data, output_page); + else { + memset(packet, 0, ETH_ZLEN); + skb_copy_from_linear_data(skb, packet, skb->len); + ei_block_output(dev, length, packet, output_page); + } + + if (! ei_local->txing) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, send_length, output_page); + netif_trans_update(dev); + if (output_page == ei_local->tx_start_page) + { + ei_local->tx1 = -1; + ei_local->lasttx = -1; + } + else + { + ei_local->tx2 = -1; + ei_local->lasttx = -2; + } + } + else ei_local->txqueue++; + + if (ei_local->tx1 && ei_local->tx2) + netif_stop_queue(dev); + else + netif_start_queue(dev); + + /* Turn 8390 interrupts back on. */ + ei_local->irqlock = 0; + outb_p(ENISR_ALL, e8390_base + EN0_IMR); + + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + dev_kfree_skb (skb); + dev->stats.tx_bytes += send_length; + + return NETDEV_TX_OK; +} + +/** + * ax_interrupt - handle the interrupts from an 8390 + * @irq: interrupt number + * @dev_id: a pointer to the net_device + * + * Handle the ether interface interrupts. We pull packets from + * the 8390 via the card specific functions and fire them at the networking + * stack. We also handle transmit completions and wake the transmit path if + * necessary. We also update the counters and do other housekeeping as + * needed. + */ + +static irqreturn_t ax_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + long e8390_base; + int interrupts, nr_serviced = 0, i; + struct ei_device *ei_local; + int handled = 0; + unsigned long flags; + + e8390_base = dev->base_addr; + ei_local = netdev_priv(dev); + + /* + * Protect the irq test too. + */ + + spin_lock_irqsave(&ei_local->page_lock, flags); + + if (ei_local->irqlock) { +#if 1 /* This might just be an interrupt for a PCI device sharing this line */ + const char *msg; + /* The "irqlock" check is only for testing. */ + if (ei_local->irqlock) + msg = "Interrupted while interrupts are masked!"; + else + msg = "Reentering the interrupt handler!"; + netdev_info(dev, "%s, isr=%#2x imr=%#2x\n", + msg, + inb_p(e8390_base + EN0_ISR), + inb_p(e8390_base + EN0_IMR)); +#endif + spin_unlock_irqrestore(&ei_local->page_lock, flags); + return IRQ_NONE; + } + + netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n", + inb_p(e8390_base + EN0_ISR)); + + outb_p(0x00, e8390_base + EN0_ISR); + ei_local->irqlock = 1; + + /* !!Assumption!! -- we stay in page 0. Don't break this. */ + while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && + ++nr_serviced < MAX_SERVICE) + { + if (!netif_running(dev) || (interrupts == 0xff)) { + netif_warn(ei_local, intr, dev, + "interrupt from stopped card\n"); + outb_p(interrupts, e8390_base + EN0_ISR); + interrupts = 0; + break; + } + handled = 1; + + /* AX88190 bug fix. */ + outb_p(interrupts, e8390_base + EN0_ISR); + for (i = 0; i < 10; i++) { + if (!(inb(e8390_base + EN0_ISR) & interrupts)) + break; + outb_p(0, e8390_base + EN0_ISR); + outb_p(interrupts, e8390_base + EN0_ISR); + } + if (interrupts & ENISR_OVER) + ei_rx_overrun(dev); + else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) + { + /* Got a good (?) packet. */ + ei_receive(dev); + } + /* Push the next to-transmit packet through. */ + if (interrupts & ENISR_TX) + ei_tx_intr(dev); + else if (interrupts & ENISR_TX_ERR) + ei_tx_err(dev); + + if (interrupts & ENISR_COUNTERS) + { + dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0); + dev->stats.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1); + dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2); + } + } + + if (interrupts && (netif_msg_intr(ei_local))) + { + handled = 1; + if (nr_serviced >= MAX_SERVICE) + { + /* 0xFF is valid for a card removal */ + if (interrupts != 0xFF) + netdev_warn(dev, + "Too much work at interrupt, status %#2.2x\n", + interrupts); + outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */ + } else { + netdev_warn(dev, "unknown interrupt %#2x\n", + interrupts); + outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */ + } + } + + /* Turn 8390 interrupts back on. */ + ei_local->irqlock = 0; + outb_p(ENISR_ALL, e8390_base + EN0_IMR); + + spin_unlock_irqrestore(&ei_local->page_lock, flags); + return IRQ_RETVAL(handled); +} + +/** + * ei_tx_err - handle transmitter error + * @dev: network device which threw the exception + * + * A transmitter error has happened. Most likely excess collisions (which + * is a fairly normal condition). If the error is one where the Tx will + * have been aborted, we try and send another one right away, instead of + * letting the failed packet sit and collect dust in the Tx buffer. This + * is a much better solution as it avoids kernel based Tx timeouts, and + * an unnecessary card reset. + * + * Called with lock held. + */ + +static void ei_tx_err(struct net_device *dev) +{ + long e8390_base = dev->base_addr; + unsigned char txsr = inb_p(e8390_base+EN0_TSR); + unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU); + +#ifdef VERBOSE_ERROR_DUMP + netdev_dbg(dev, "transmitter error (%#2x):", txsr); + if (txsr & ENTSR_ABT) + pr_cont(" excess-collisions"); + if (txsr & ENTSR_ND) + pr_cont(" non-deferral"); + if (txsr & ENTSR_CRS) + pr_cont(" lost-carrier"); + if (txsr & ENTSR_FU) + pr_cont(" FIFO-underrun"); + if (txsr & ENTSR_CDH) + pr_cont(" lost-heartbeat"); + pr_cont("\n"); +#endif + + if (tx_was_aborted) + ei_tx_intr(dev); + else + { + dev->stats.tx_errors++; + if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++; + if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++; + if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++; + } +} + +/** + * ei_tx_intr - transmit interrupt handler + * @dev: network device for which tx intr is handled + * + * We have finished a transmit: check for errors and then trigger the next + * packet to be sent. Called with lock held. + */ + +static void ei_tx_intr(struct net_device *dev) +{ + long e8390_base = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + int status = inb(e8390_base + EN0_TSR); + + /* + * There are two Tx buffers, see which one finished, and trigger + * the send of another one if it exists. + */ + ei_local->txqueue--; + + if (ei_local->tx1 < 0) + { + if (ei_local->lasttx != 1 && ei_local->lasttx != -1) + netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n", + ei_local->name, ei_local->lasttx, + ei_local->tx1); + ei_local->tx1 = 0; + if (ei_local->tx2 > 0) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); + netif_trans_update(dev); + ei_local->tx2 = -1; + ei_local->lasttx = 2; + } else { + ei_local->lasttx = 20; + ei_local->txing = 0; + } + } + else if (ei_local->tx2 < 0) + { + if (ei_local->lasttx != 2 && ei_local->lasttx != -2) + netdev_err(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n", + ei_local->name, ei_local->lasttx, + ei_local->tx2); + ei_local->tx2 = 0; + if (ei_local->tx1 > 0) + { + ei_local->txing = 1; + NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); + netif_trans_update(dev); + ei_local->tx1 = -1; + ei_local->lasttx = 1; + } else { + ei_local->lasttx = 10; + ei_local->txing = 0; + } + } +// else +// netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n", +// ei_local->lasttx); + + /* Minimize Tx latency: update the statistics after we restart TXing. */ + if (status & ENTSR_COL) + dev->stats.collisions++; + if (status & ENTSR_PTX) + dev->stats.tx_packets++; + else + { + dev->stats.tx_errors++; + if (status & ENTSR_ABT) + { + dev->stats.tx_aborted_errors++; + dev->stats.collisions += 16; + } + if (status & ENTSR_CRS) + dev->stats.tx_carrier_errors++; + if (status & ENTSR_FU) + dev->stats.tx_fifo_errors++; + if (status & ENTSR_CDH) + dev->stats.tx_heartbeat_errors++; + if (status & ENTSR_OWC) + dev->stats.tx_window_errors++; + } + netif_wake_queue(dev); +} + +/** + * ei_receive - receive some packets + * @dev: network device with which receive will be run + * + * We have a good packet(s), get it/them out of the buffers. + * Called with lock held. + */ + +static void ei_receive(struct net_device *dev) +{ + long e8390_base = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + unsigned char rxing_page, this_frame, next_frame; + unsigned short current_offset; + int rx_pkt_count = 0; + struct e8390_pkt_hdr rx_frame; + + while (++rx_pkt_count < 10) + { + int pkt_len, pkt_stat; + + /* Get the rx page (incoming packet pointer). */ + rxing_page = inb_p(e8390_base + EN1_CURPAG -1); + + /* Remove one frame from the ring. Boundary is always a page behind. */ + this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1; + if (this_frame >= ei_local->stop_page) + this_frame = ei_local->rx_start_page; + + /* Someday we'll omit the previous, iff we never get this message. + (There is at least one clone claimed to have a problem.) + + Keep quiet if it looks like a card removal. One problem here + is that some clones crash in roughly the same way. + */ + if ((netif_msg_rx_err(ei_local)) && + this_frame != ei_local->current_page && + (this_frame != 0x0 || rxing_page != 0xFF)) + netdev_err(dev, "mismatched read page pointers %2x vs %2x\n", + this_frame, ei_local->current_page); + + if (this_frame == rxing_page) /* Read all the frames? */ + break; /* Done for now */ + + current_offset = this_frame << 8; + ei_get_8390_hdr(dev, &rx_frame, this_frame); + + pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); + pkt_stat = rx_frame.status; + + next_frame = this_frame + 1 + ((pkt_len+4)>>8); + + if (pkt_len < 60 || pkt_len > 1518) + { + netif_err(ei_local, rx_err, dev, + "bogus packet size: %d, status=%#2x nxpg=%#2x\n", + rx_frame.count, rx_frame.status, + rx_frame.next); + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; + } + else if ((pkt_stat & 0x0F) == ENRSR_RXOK) + { + struct sk_buff *skb; + + skb = netdev_alloc_skb(dev, pkt_len + 2); + if (skb == NULL) + { + netif_err(ei_local, rx_err, dev, + "Couldn't allocate a sk_buff of size %d\n", + pkt_len); + dev->stats.rx_dropped++; + break; + } + else + { + skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ + skb_put(skb, pkt_len); /* Make room */ + ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + if (pkt_stat & ENRSR_PHY) + dev->stats.multicast++; + } + } + else + { + netif_err(ei_local, rx_err, dev, + "bogus packet: status=%#2x nxpg=%#2x size=%d\n", + rx_frame.status, rx_frame.next, + rx_frame.count); + dev->stats.rx_errors++; + /* NB: The NIC counts CRC, frame and missed errors. */ + if (pkt_stat & ENRSR_FO) + dev->stats.rx_fifo_errors++; + } + next_frame = rx_frame.next; + + /* This _should_ never happen: it's here for avoiding bad clones. */ + if (next_frame >= ei_local->stop_page) { + netdev_info(dev, "next frame inconsistency, %#2x\n", + next_frame); + next_frame = ei_local->rx_start_page; + } + ei_local->current_page = next_frame; + outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); + } +} + +/** + * ei_rx_overrun - handle receiver overrun + * @dev: network device which threw exception + * + * We have a receiver overrun: we have to kick the 8390 to get it started + * again. Problem is that you have to kick it exactly as NS prescribes in + * the updated datasheets, or "the NIC may act in an unpredictable manner." + * This includes causing "the NIC to defer indefinitely when it is stopped + * on a busy network." Ugh. + * Called with lock held. Don't call this with the interrupts off or your + * computer will hate you - it takes 10ms or so. + */ + +static void ei_rx_overrun(struct net_device *dev) +{ + struct axnet_dev *info = PRIV(dev); + long e8390_base = dev->base_addr; + unsigned char was_txing, must_resend = 0; + struct ei_device *ei_local = netdev_priv(dev); + + /* + * Record whether a Tx was in progress and then issue the + * stop command. + */ + was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); + + netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n"); + dev->stats.rx_over_errors++; + + /* + * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. + * We wait at least 2ms. + */ + + mdelay(2); + + /* + * Reset RBCR[01] back to zero as per magic incantation. + */ + outb_p(0x00, e8390_base+EN0_RCNTLO); + outb_p(0x00, e8390_base+EN0_RCNTHI); + + /* + * See if any Tx was interrupted or not. According to NS, this + * step is vital, and skipping it will cause no end of havoc. + */ + + if (was_txing) + { + unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); + if (!tx_completed) + must_resend = 1; + } + + /* + * Have to enter loopback mode and then restart the NIC before + * you are allowed to slurp packets up off the ring. + */ + outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); + outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); + + /* + * Clear the Rx ring of all the debris, and ack the interrupt. + */ + ei_receive(dev); + + /* + * Leave loopback mode, and resend any packet that got stopped. + */ + outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR); + if (must_resend) + outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); +} + +/* + * Collect the stats. This is called unlocked and from several contexts. + */ + +static struct net_device_stats *get_stats(struct net_device *dev) +{ + long ioaddr = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + unsigned long flags; + + /* If the card is stopped, just return the present stats. */ + if (!netif_running(dev)) + return &dev->stats; + + spin_lock_irqsave(&ei_local->page_lock,flags); + /* Read the counter registers, assuming we are in page 0. */ + dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0); + dev->stats.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1); + dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2); + spin_unlock_irqrestore(&ei_local->page_lock, flags); + + return &dev->stats; +} + +/* + * Form the 64 bit 8390 multicast table from the linked list of addresses + * associated with this dev structure. + */ + +static inline void make_mc_bits(u8 *bits, struct net_device *dev) +{ + struct netdev_hw_addr *ha; + u32 crc; + + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc(ETH_ALEN, ha->addr); + /* + * The 8390 uses the 6 most significant bits of the + * CRC to index the multicast table. + */ + bits[crc>>29] |= (1<<((crc>>26)&7)); + } +} + +/** + * do_set_multicast_list - set/clear multicast filter + * @dev: net device for which multicast filter is adjusted + * + * Set or clear the multicast filter for this adaptor. + * Must be called with lock held. + */ + +static void do_set_multicast_list(struct net_device *dev) +{ + long e8390_base = dev->base_addr; + int i; + struct ei_device *ei_local = netdev_priv(dev); + + if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) { + memset(ei_local->mcfilter, 0, 8); + if (!netdev_mc_empty(dev)) + make_mc_bits(ei_local->mcfilter, dev); + } else { + /* set to accept-all */ + memset(ei_local->mcfilter, 0xFF, 8); + } + + outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD); + for(i = 0; i < 8; i++) + { + outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i)); + } + outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD); + + if(dev->flags&IFF_PROMISC) + outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR); + else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) + outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR); + else + outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); + + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); +} + +/* + * Called without lock held. This is invoked from user context and may + * be parallel to just about everything else. Its also fairly quick and + * not called too often. Must protect against both bh and irq users + */ + +static void set_multicast_list(struct net_device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev_lock(dev), flags); + do_set_multicast_list(dev); + spin_unlock_irqrestore(&dev_lock(dev), flags); +} + +/* This page of functions should be 8390 generic */ +/* Follow National Semi's recommendations for initializing the "NIC". */ + +/** + * AX88190_init - initialize 8390 hardware + * @dev: network device to initialize + * @startp: boolean. non-zero value to initiate chip processing + * + * Must be called with lock held. + */ + +static void AX88190_init(struct net_device *dev, int startp) +{ + struct axnet_dev *info = PRIV(dev); + long e8390_base = dev->base_addr; + struct ei_device *ei_local = netdev_priv(dev); + int i; + int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48; + + if(sizeof(struct e8390_pkt_hdr)!=4) + panic("8390.c: header struct mispacked\n"); + /* Follow National Semi's recommendations for initing the DP83902. */ + outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */ + outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */ + /* Clear the remote byte count registers. */ + outb_p(0x00, e8390_base + EN0_RCNTLO); + outb_p(0x00, e8390_base + EN0_RCNTHI); + /* Set to monitor and loopback mode -- this is vital!. */ + outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */ + outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ + /* Set the transmit page and receive ring. */ + outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR); + ei_local->tx1 = ei_local->tx2 = 0; + outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG); + outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ + ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */ + outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG); + /* Clear the pending interrupts and mask. */ + outb_p(0xFF, e8390_base + EN0_ISR); + outb_p(0x00, e8390_base + EN0_IMR); + + /* Copy the station address into the DS8390 registers. */ + + outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */ + for(i = 0; i < 6; i++) + { + outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i)); + if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) + netdev_err(dev, "Hw. address read/write mismap %d\n", i); + } + + outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); + outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); + + netif_start_queue(dev); + ei_local->tx1 = ei_local->tx2 = 0; + ei_local->txing = 0; + + if (info->flags & IS_AX88790) /* select Internal PHY */ + outb(0x10, e8390_base + AXNET_GPIO); + + if (startp) + { + outb_p(0xff, e8390_base + EN0_ISR); + outb_p(ENISR_ALL, e8390_base + EN0_IMR); + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); + outb_p(E8390_TXCONFIG | info->duplex_flag, + e8390_base + EN0_TXCR); /* xmit on. */ + /* 3c503 TechMan says rxconfig only after the NIC is started. */ + outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */ + do_set_multicast_list(dev); /* (re)load the mcast table */ + } +} + +/* Trigger a transmit start, assuming the length is valid. + Always called with the page lock held */ + +static void NS8390_trigger_send(struct net_device *dev, unsigned int length, + int start_page) +{ + long e8390_base = dev->base_addr; + struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev); + + if (inb_p(e8390_base) & E8390_TRANS) + { + netdev_warn(dev, "trigger_send() called with the transmitter busy\n"); + return; + } + outb_p(length & 0xff, e8390_base + EN0_TCNTLO); + outb_p(length >> 8, e8390_base + EN0_TCNTHI); + outb_p(start_page, e8390_base + EN0_TPSR); + outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD); +} diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c new file mode 100644 index 000000000000..22ca804b2e95 --- /dev/null +++ b/drivers/net/ethernet/8390/smc-ultra.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-1.0+ +/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ +/* + This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. + + Written 1993-1998 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + This driver uses the cards in the 8390-compatible mode. + Most of the run-time complexity is handled by the generic code in + 8390.c. The code in this file is responsible for + + ultra_probe() Detecting and initializing the card. + ultra_probe1() + ultra_probe_isapnp() + + ultra_open() The card-specific details of starting, stopping + ultra_reset_8390() and resetting the 8390 NIC core. + ultra_close() + + ultra_block_input() Routines for reading and writing blocks of + ultra_block_output() packet buffer memory. + ultra_pio_input() + ultra_pio_output() + + This driver enables the shared memory only when doing the actual data + transfers to avoid a bug in early version of the card that corrupted + data transferred by a AHA1542. + + This driver now supports the programmed-I/O (PIO) data transfer mode of + the EtherEZ. It does not use the non-8390-compatible "Altego" mode. + That support (if available) is in smc-ez.c. + + Changelog: + + Paul Gortmaker : multiple card support for module users. + Donald Becker : 4/17/96 PIO support, minor potential problems avoided. + Donald Becker : 6/6/96 correctly set auto-wrap bit. + Alexander Sotirov : 1/20/01 Added support for ISAPnP cards + + Note about the ISA PnP support: + + This driver can not autoprobe for more than one SMC EtherEZ PnP card. + You have to configure the second card manually through the /proc/isapnp + interface and then load the module with an explicit io=0x___ option. +*/ + +static const char version[] = + "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8390.h" + +#define DRV_NAME "smc-ultra" + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int ultra_portlist[] __initdata = +{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0}; + +static int ultra_probe1(struct net_device *dev, int ioaddr); + +#ifdef __ISAPNP__ +static int ultra_probe_isapnp(struct net_device *dev); +#endif + +static int ultra_open(struct net_device *dev); +static void ultra_reset_8390(struct net_device *dev); +static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ultra_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ultra_block_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page); +static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ultra_pio_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ultra_pio_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page); +static int ultra_close_card(struct net_device *dev); + +#ifdef __ISAPNP__ +static struct isapnp_device_id ultra_device_ids[] __initdata = { + { ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), + ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), + (long) "SMC EtherEZ (8416)" }, + { } /* terminate list */ +}; + +MODULE_DEVICE_TABLE(isapnp, ultra_device_ids); +#endif + +static u32 ultra_msg_enable; + +#define START_PG 0x00 /* First page of TX buffer */ + +#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */ +#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */ +#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */ +#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */ +#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */ +#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */ +#define ULTRA_IO_EXTENT 32 +#define EN0_ERWCNT 0x08 /* Early receive warning count. */ + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void ultra_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + ei_interrupt(dev->irq, dev); + enable_irq(dev->irq); +} +#endif +/* Probe for the Ultra. This looks like a 8013 with the station + address PROM at I/O ports +8 to +13, with a checksum + following. +*/ + +static int __init do_ultra_probe(struct net_device *dev) +{ + int i; + int base_addr = dev->base_addr; + int irq = dev->irq; + + if (base_addr > 0x1ff) /* Check a single specified location. */ + return ultra_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + +#ifdef __ISAPNP__ + /* Look for any installed ISAPnP cards */ + if (isapnp_present() && (ultra_probe_isapnp(dev) == 0)) + return 0; +#endif + + for (i = 0; ultra_portlist[i]; i++) { + dev->irq = irq; + if (ultra_probe1(dev, ultra_portlist[i]) == 0) + return 0; + } + + return -ENODEV; +} + +#ifndef MODULE +struct net_device * __init ultra_probe(int unit) +{ + struct net_device *dev = alloc_ei_netdev(); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_ultra_probe(dev); + if (err) + goto out; + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); +} +#endif + +static const struct net_device_ops ultra_netdev_ops = { + .ndo_open = ultra_open, + .ndo_stop = ultra_close_card, + + .ndo_start_xmit = ei_start_xmit, + .ndo_tx_timeout = ei_tx_timeout, + .ndo_get_stats = ei_get_stats, + .ndo_set_rx_mode = ei_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ultra_poll, +#endif +}; + +static int __init ultra_probe1(struct net_device *dev, int ioaddr) +{ + int i, retval; + int checksum = 0; + u8 macaddr[ETH_ALEN]; + const char *model_name; + unsigned char eeprom_irq = 0; + static unsigned version_printed; + /* Values from various config regs. */ + unsigned char num_pages, irqreg, addr, piomode; + unsigned char idreg = inb(ioaddr + 7); + unsigned char reg4 = inb(ioaddr + 4) & 0x7f; + struct ei_device *ei_local = netdev_priv(dev); + + if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) + return -EBUSY; + + /* Check the ID nibble. */ + if ((idreg & 0xF0) != 0x20 /* SMC Ultra */ + && (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */ + retval = -ENODEV; + goto out; + } + + /* Select the station address register set. */ + outb(reg4, ioaddr + 4); + + for (i = 0; i < 8; i++) + checksum += inb(ioaddr + 8 + i); + if ((checksum & 0xff) != 0xFF) { + retval = -ENODEV; + goto out; + } + + if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) + netdev_info(dev, version); + + model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; + + for (i = 0; i < 6; i++) + macaddr[i] = inb(ioaddr + 8 + i); + eth_hw_addr_set(dev, macaddr); + + netdev_info(dev, "%s at %#3x, %pM", model_name, + ioaddr, dev->dev_addr); + + /* Switch from the station address to the alternate register set and + read the useful registers there. */ + outb(0x80 | reg4, ioaddr + 4); + + /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ + outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); + piomode = inb(ioaddr + 0x8); + addr = inb(ioaddr + 0xb); + irqreg = inb(ioaddr + 0xd); + + /* Switch back to the station address register set so that the MS-DOS driver + can find the card after a warm boot. */ + outb(reg4, ioaddr + 4); + + if (dev->irq < 2) { + unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; + int irq; + + /* The IRQ bits are split. */ + irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)]; + + if (irq == 0) { + pr_cont(", failed to detect IRQ line.\n"); + retval = -EAGAIN; + goto out; + } + dev->irq = irq; + eeprom_irq = 1; + } + + /* The 8390 isn't at the base address, so fake the offset */ + dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; + + { + static const int addr_tbl[4] = { + 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000 + }; + static const short num_pages_tbl[4] = { + 0x20, 0x40, 0x80, 0xff + }; + + dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ; + num_pages = num_pages_tbl[(addr >> 4) & 3]; + } + + ei_status.name = model_name; + ei_status.word16 = 1; + ei_status.tx_start_page = START_PG; + ei_status.rx_start_page = START_PG + TX_PAGES; + ei_status.stop_page = num_pages; + + ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256); + if (!ei_status.mem) { + pr_cont(", failed to ioremap.\n"); + retval = -ENOMEM; + goto out; + } + + dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256; + + if (piomode) { + pr_cont(", %s IRQ %d programmed-I/O mode.\n", + eeprom_irq ? "EEPROM" : "assigned ", dev->irq); + ei_status.block_input = &ultra_pio_input; + ei_status.block_output = &ultra_pio_output; + ei_status.get_8390_hdr = &ultra_pio_get_hdr; + } else { + pr_cont(", %s IRQ %d memory %#lx-%#lx.\n", + eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start, + dev->mem_end-1); + ei_status.block_input = &ultra_block_input; + ei_status.block_output = &ultra_block_output; + ei_status.get_8390_hdr = &ultra_get_8390_hdr; + } + ei_status.reset_8390 = &ultra_reset_8390; + + dev->netdev_ops = &ultra_netdev_ops; + NS8390_init(dev, 0); + ei_local->msg_enable = ultra_msg_enable; + + retval = register_netdev(dev); + if (retval) + goto out; + return 0; +out: + release_region(ioaddr, ULTRA_IO_EXTENT); + return retval; +} + +#ifdef __ISAPNP__ +static int __init ultra_probe_isapnp(struct net_device *dev) +{ + int i; + + for (i = 0; ultra_device_ids[i].vendor != 0; i++) { + struct pnp_dev *idev = NULL; + + while ((idev = pnp_find_dev(NULL, + ultra_device_ids[i].vendor, + ultra_device_ids[i].function, + idev))) { + /* Avoid already found cards from previous calls */ + if (pnp_device_attach(idev) < 0) + continue; + if (pnp_activate_dev(idev) < 0) { + __again: + pnp_device_detach(idev); + continue; + } + /* if no io and irq, search for next */ + if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) + goto __again; + /* found it */ + dev->base_addr = pnp_port_start(idev, 0); + dev->irq = pnp_irq(idev, 0); + netdev_info(dev, + "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", + (char *) ultra_device_ids[i].driver_data, + dev->base_addr, dev->irq); + if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */ + netdev_err(dev, + "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", + dev->base_addr); + pnp_device_detach(idev); + return -ENXIO; + } + ei_status.priv = (unsigned long)idev; + break; + } + if (!idev) + continue; + return 0; + } + + return -ENODEV; +} +#endif + +static int +ultra_open(struct net_device *dev) +{ + int retval; + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, + 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; + + retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); + if (retval) + return retval; + + outb(0x00, ioaddr); /* Disable shared memory for safety. */ + outb(0x80, ioaddr + 5); + /* Set the IRQ line. */ + outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); + outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); + outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); + + if (ei_status.block_input == &ultra_pio_input) { + outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */ + outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */ + } else + outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */ + /* Set the early receive warning level in window 0 high enough not + to receive ERW interrupts. */ + outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); + outb(0xff, dev->base_addr + EN0_ERWCNT); + ei_open(dev); + return 0; +} + +static void +ultra_reset_8390(struct net_device *dev) +{ + int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */ + struct ei_device *ei_local = netdev_priv(dev); + + outb(ULTRA_RESET, cmd_port); + netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies); + ei_status.txing = 0; + + outb(0x00, cmd_port); /* Disable shared memory for safety. */ + outb(0x80, cmd_port + 5); + if (ei_status.block_input == &ultra_pio_input) + outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */ + else + outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */ + + netif_dbg(ei_local, hw, dev, "reset done\n"); +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void +ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8); + + outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */ +#ifdef __BIG_ENDIAN + /* Officially this is what we are doing, but the readl() is faster */ + /* unfortunately it isn't endian aware of the struct */ + memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); + hdr->count = le16_to_cpu(hdr->count); +#else + ((unsigned int*)hdr)[0] = readl(hdr_start); +#endif + outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */ +} + +/* Block input and output are easy on shared memory ethercards, the only + complication is when the ring buffer wraps. */ + +static void +ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8); + + /* Enable shared memory. */ + outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); + + if (ring_offset + count > ei_status.stop_page*256) { + /* We must wrap the input move. */ + int semi_count = ei_status.stop_page*256 - ring_offset; + memcpy_fromio(skb->data, xfer_start, semi_count); + count -= semi_count; + memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); + } else { + memcpy_fromio(skb->data, xfer_start, count); + } + + outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ +} + +static void +ultra_block_output(struct net_device *dev, int count, const unsigned char *buf, + int start_page) +{ + void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8); + + /* Enable shared memory. */ + outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); + + memcpy_toio(shmem, buf, count); + + outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ +} + +/* The identical operations for programmed I/O cards. + The PIO model is trivial to use: the 16 bit start address is written + byte-sequentially to IOPA, with no intervening I/O operations, and the + data is read or written to the IOPD data port. + The only potential complication is that the address register is shared + and must be always be rewritten between each read/write direction change. + This is no problem for us, as the 8390 code ensures that we are single + threaded. */ +static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ + outb(ring_page, ioaddr + IOPA); + insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1); +} + +static void ultra_pio_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + char *buf = skb->data; + + /* For now set the address again, although it should already be correct. */ + outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */ + outb(ring_offset >> 8, ioaddr + IOPA); + /* We know skbuffs are padded to at least word alignment. */ + insw(ioaddr + IOPD, buf, (count+1)>>1); +} +static void ultra_pio_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ + outb(start_page, ioaddr + IOPA); + /* An extra odd byte is OK here as well. */ + outsw(ioaddr + IOPD, buf, (count+1)>>1); +} + +static int +ultra_close_card(struct net_device *dev) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */ + struct ei_device *ei_local = netdev_priv(dev); + + netif_stop_queue(dev); + + netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); + + outb(0x00, ioaddr + 6); /* Disable interrupts. */ + free_irq(dev->irq, dev); + + NS8390_init(dev, 0); + + /* We should someday disable shared memory and change to 8-bit mode + "just in case"... */ + + return 0; +} + + +#ifdef MODULE +#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */ +static struct net_device *dev_ultra[MAX_ULTRA_CARDS]; +static int io[MAX_ULTRA_CARDS]; +static int irq[MAX_ULTRA_CARDS]; + +module_param_hw_array(io, int, ioport, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); +module_param_named(msg_enable, ultra_msg_enable, uint, 0444); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); +MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); +MODULE_LICENSE("GPL"); + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +static int __init ultra_init_module(void) +{ + struct net_device *dev; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); + } + dev = alloc_ei_netdev(); + if (!dev) + break; + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + if (do_ultra_probe(dev) == 0) { + dev_ultra[found++] = dev; + continue; + } + free_netdev(dev); + printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); + break; + } + if (found) + return 0; + return -ENXIO; +} +module_init(ultra_init_module); + +static void cleanup_card(struct net_device *dev) +{ + /* NB: ultra_close_card() does free_irq */ +#ifdef __ISAPNP__ + struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv; + if (idev) + pnp_device_detach(idev); +#endif + release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT); + iounmap(ei_status.mem); +} + +static void __exit ultra_cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { + struct net_device *dev = dev_ultra[this_dev]; + if (dev) { + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } + } +} +module_exit(ultra_cleanup_module); +#endif /* MODULE */ diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c new file mode 100644 index 000000000000..ffd639477dfc --- /dev/null +++ b/drivers/net/ethernet/8390/wd.c @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-1.0+ +/* wd.c: A WD80x3 ethernet driver for linux. */ +/* + Written 1993-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + This is a driver for WD8003 and WD8013 "compatible" ethercards. + + Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. + + Changelog: + + Paul Gortmaker : multiple card support for module users, support + for non-standard memory sizes. + + +*/ + +static const char version[] = + "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "8390.h" + +#define DRV_NAME "wd" + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int wd_portlist[] __initdata = +{0x300, 0x280, 0x380, 0x240, 0}; + +static int wd_probe1(struct net_device *dev, int ioaddr); + +static int wd_open(struct net_device *dev); +static void wd_reset_8390(struct net_device *dev); +static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void wd_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void wd_block_output(struct net_device *dev, int count, + const unsigned char *buf, int start_page); +static int wd_close(struct net_device *dev); + +static u32 wd_msg_enable; + +#define WD_START_PG 0x00 /* First page of TX buffer */ +#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ +#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ + +#define WD_CMDREG 0 /* Offset to ASIC command register. */ +#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */ +#define WD_MEMENB 0x40 /* Enable the shared memory. */ +#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */ +#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */ +#define NIC16 0x40 /* Enable 16 bit access from the 8390. */ +#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */ +#define WD_IO_EXTENT 32 + + +/* Probe for the WD8003 and WD8013. These cards have the station + address PROM at I/O ports +8 to +13, with a checksum + following. A Soundblaster can have the same checksum as an WDethercard, + so we have an extra exclusionary check for it. + + The wd_probe1() routine initializes the card and fills the + station address field. */ + +static int __init do_wd_probe(struct net_device *dev) +{ + int i; + struct resource *r; + int base_addr = dev->base_addr; + int irq = dev->irq; + int mem_start = dev->mem_start; + int mem_end = dev->mem_end; + + if (base_addr > 0x1ff) { /* Check a user specified location. */ + r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); + if ( r == NULL) + return -EBUSY; + i = wd_probe1(dev, base_addr); + if (i != 0) + release_region(base_addr, WD_IO_EXTENT); + else + r->name = dev->name; + return i; + } + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + for (i = 0; wd_portlist[i]; i++) { + int ioaddr = wd_portlist[i]; + r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); + if (r == NULL) + continue; + if (wd_probe1(dev, ioaddr) == 0) { + r->name = dev->name; + return 0; + } + release_region(ioaddr, WD_IO_EXTENT); + dev->irq = irq; + dev->mem_start = mem_start; + dev->mem_end = mem_end; + } + + return -ENODEV; +} + +#ifndef MODULE +struct net_device * __init wd_probe(int unit) +{ + struct net_device *dev = alloc_ei_netdev(); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_wd_probe(dev); + if (err) + goto out; + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); +} +#endif + +static const struct net_device_ops wd_netdev_ops = { + .ndo_open = wd_open, + .ndo_stop = wd_close, + .ndo_start_xmit = ei_start_xmit, + .ndo_tx_timeout = ei_tx_timeout, + .ndo_get_stats = ei_get_stats, + .ndo_set_rx_mode = ei_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ei_poll, +#endif +}; + +static int __init wd_probe1(struct net_device *dev, int ioaddr) +{ + int i; + int err; + int checksum = 0; + int ancient = 0; /* An old card without config registers. */ + int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */ + u8 addr[ETH_ALEN]; + const char *model_name; + static unsigned version_printed; + struct ei_device *ei_local = netdev_priv(dev); + + for (i = 0; i < 8; i++) + checksum += inb(ioaddr + 8 + i); + if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */ + || inb(ioaddr + 9) == 0xff + || (checksum & 0xff) != 0xFF) + return -ENODEV; + + /* Check for semi-valid mem_start/end values if supplied. */ + if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { + netdev_warn(dev, + "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); + dev->mem_start = 0; + dev->mem_end = 0; + } + + if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) + netdev_info(dev, version); + + for (i = 0; i < 6; i++) + addr[i] = inb(ioaddr + 8 + i); + eth_hw_addr_set(dev, addr); + + netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr); + + /* The following PureData probe code was contributed by + Mike Jagdis . Puredata does software + configuration differently from others so we have to check for them. + This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. + */ + if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { + unsigned char reg5 = inb(ioaddr+5); + + switch (inb(ioaddr+2)) { + case 0x03: word16 = 0; model_name = "PDI8023-8"; break; + case 0x05: word16 = 0; model_name = "PDUC8023"; break; + case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; + /* Either 0x01 (dumb) or they've released a new version. */ + default: word16 = 0; model_name = "PDI8023"; break; + } + dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; + dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; + } else { /* End of PureData probe */ + /* This method of checking for a 16-bit board is borrowed from the + we.c driver. A simpler method is just to look in ASIC reg. 0x03. + I'm comparing the two method in alpha test to make certain they + return the same result. */ + /* Check for the old 8 bit board - it has register 0/8 aliasing. + Do NOT check i>=6 here -- it hangs the old 8003 boards! */ + for (i = 0; i < 6; i++) + if (inb(ioaddr+i) != inb(ioaddr+8+i)) + break; + if (i >= 6) { + ancient = 1; + model_name = "WD8003-old"; + word16 = 0; + } else { + int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ + outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ + if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ + && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ + int asic_reg5 = inb(ioaddr+WD_CMDREG5); + /* Magic to set ASIC to word-wide mode. */ + outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); + outb(tmp, ioaddr+1); + model_name = "WD8013"; + word16 = 1; /* We have a 16bit board here! */ + } else { + model_name = "WD8003"; + word16 = 0; + } + outb(tmp, ioaddr+1); /* Restore original reg1 value. */ + } +#ifndef final_version + if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) + pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", + word16 ? 16 : 8, + (inb(ioaddr+1) & 0x01) ? 16 : 8); +#endif + } + +#if defined(WD_SHMEM) && WD_SHMEM > 0x80000 + /* Allow a compile-time override. */ + dev->mem_start = WD_SHMEM; +#else + if (dev->mem_start == 0) { + /* Sanity and old 8003 check */ + int reg0 = inb(ioaddr); + if (reg0 == 0xff || reg0 == 0) { + /* Future plan: this could check a few likely locations first. */ + dev->mem_start = 0xd0000; + pr_cont(" assigning address %#lx", dev->mem_start); + } else { + int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; + /* Some boards don't have the register 5 -- it returns 0xff. */ + if (high_addr_bits == 0x1f || word16 == 0) + high_addr_bits = 0x01; + dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); + } + } +#endif + + /* The 8390 isn't at the base address -- the ASIC regs are there! */ + dev->base_addr = ioaddr+WD_NIC_OFFSET; + + if (dev->irq < 2) { + static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4}; + int reg1 = inb(ioaddr+1); + int reg4 = inb(ioaddr+4); + if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */ + short nic_addr = ioaddr+WD_NIC_OFFSET; + unsigned long irq_mask; + + /* We have an old-style ethercard that doesn't report its IRQ + line. Do autoirq to find the IRQ line. Note that this IS NOT + a reliable way to trigger an interrupt. */ + outb_p(E8390_NODMA + E8390_STOP, nic_addr); + outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */ + + irq_mask = probe_irq_on(); + outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */ + outb_p(0x00, nic_addr + EN0_RCNTLO); + outb_p(0x00, nic_addr + EN0_RCNTHI); + outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ + mdelay(20); + dev->irq = probe_irq_off(irq_mask); + + outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */ + + if (wd_msg_enable & NETIF_MSG_PROBE) + pr_cont(" autoirq is %d", dev->irq); + if (dev->irq < 2) + dev->irq = word16 ? 10 : 5; + } else + dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; + } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */ + dev->irq = 9; + + /* Snarf the interrupt now. There's no point in waiting since we cannot + share and the board will usually be enabled. */ + i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); + if (i) { + pr_cont(" unable to get IRQ %d.\n", dev->irq); + return i; + } + + /* OK, were are certain this is going to work. Setup the device. */ + ei_status.name = model_name; + ei_status.word16 = word16; + ei_status.tx_start_page = WD_START_PG; + ei_status.rx_start_page = WD_START_PG + TX_PAGES; + + /* Don't map in the shared memory until the board is actually opened. */ + + /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ + if (dev->mem_end != 0) { + ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; + ei_status.priv = dev->mem_end - dev->mem_start; + } else { + ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; + dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; + ei_status.priv = (ei_status.stop_page - WD_START_PG)*256; + } + + ei_status.mem = ioremap(dev->mem_start, ei_status.priv); + if (!ei_status.mem) { + free_irq(dev->irq, dev); + return -ENOMEM; + } + + pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", + model_name, dev->irq, dev->mem_start, dev->mem_end-1); + + ei_status.reset_8390 = wd_reset_8390; + ei_status.block_input = wd_block_input; + ei_status.block_output = wd_block_output; + ei_status.get_8390_hdr = wd_get_8390_hdr; + + dev->netdev_ops = &wd_netdev_ops; + NS8390_init(dev, 0); + ei_local->msg_enable = wd_msg_enable; + +#if 1 + /* Enable interrupt generation on softconfig cards -- M.U */ + /* .. but possibly potentially unsafe - Donald */ + if (inb(ioaddr+14) & 0x20) + outb(inb(ioaddr+4)|0x80, ioaddr+4); +#endif + + err = register_netdev(dev); + if (err) { + free_irq(dev->irq, dev); + iounmap(ei_status.mem); + } + return err; +} + +static int +wd_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + + /* Map in the shared memory. Always set register 0 last to remain + compatible with very old boards. */ + ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; + ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; + + if (ei_status.word16) + outb(ei_status.reg5, ioaddr+WD_CMDREG5); + outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ + + return ei_open(dev); +} + +static void +wd_reset_8390(struct net_device *dev) +{ + int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + struct ei_device *ei_local = netdev_priv(dev); + + outb(WD_RESET, wd_cmd_port); + netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n", + jiffies); + ei_status.txing = 0; + + /* Set up the ASIC registers, just in case something changed them. */ + outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); + if (ei_status.word16) + outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); + + netif_dbg(ei_local, hw, dev, "reset done\n"); +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void +wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + + int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8); + + /* We'll always get a 4 byte header read followed by a packet read, so + we enable 16 bit mode before the header, and disable after the body. */ + if (ei_status.word16) + outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); + +#ifdef __BIG_ENDIAN + /* Officially this is what we are doing, but the readl() is faster */ + /* unfortunately it isn't endian aware of the struct */ + memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); + hdr->count = le16_to_cpu(hdr->count); +#else + ((unsigned int*)hdr)[0] = readl(hdr_start); +#endif +} + +/* Block input and output are easy on shared memory ethercards, and trivial + on the Western digital card where there is no choice of how to do it. + The only complications are that the ring buffer wraps, and need to map + switch between 8- and 16-bit modes. */ + +static void +wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + unsigned long offset = ring_offset - (WD_START_PG<<8); + void __iomem *xfer_start = ei_status.mem + offset; + + if (offset + count > ei_status.priv) { + /* We must wrap the input move. */ + int semi_count = ei_status.priv - offset; + memcpy_fromio(skb->data, xfer_start, semi_count); + count -= semi_count; + memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); + } else { + /* Packet is in one chunk -- we can copy + cksum. */ + memcpy_fromio(skb->data, xfer_start, count); + } + + /* Turn off 16 bit access so that reboot works. ISA brain-damage */ + if (ei_status.word16) + outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); +} + +static void +wd_block_output(struct net_device *dev, int count, const unsigned char *buf, + int start_page) +{ + int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8); + + + if (ei_status.word16) { + /* Turn on and off 16 bit access so that reboot works. */ + outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); + memcpy_toio(shmem, buf, count); + outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); + } else + memcpy_toio(shmem, buf, count); +} + + +static int +wd_close(struct net_device *dev) +{ + int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + struct ei_device *ei_local = netdev_priv(dev); + + netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); + ei_close(dev); + + /* Change from 16-bit to 8-bit shared memory so reboot works. */ + if (ei_status.word16) + outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); + + /* And disable the shared memory. */ + outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); + + return 0; +} + + +#ifdef MODULE +#define MAX_WD_CARDS 4 /* Max number of wd cards per module */ +static struct net_device *dev_wd[MAX_WD_CARDS]; +static int io[MAX_WD_CARDS]; +static int irq[MAX_WD_CARDS]; +static int mem[MAX_WD_CARDS]; +static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */ + +module_param_hw_array(io, int, ioport, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); +module_param_hw_array(mem, int, iomem, NULL, 0); +module_param_hw_array(mem_end, int, iomem, NULL, 0); +module_param_named(msg_enable, wd_msg_enable, uint, 0444); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); +MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); +MODULE_PARM_DESC(mem_end, "memory end address(es)"); +MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); +MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); +MODULE_LICENSE("GPL"); + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ + +static int __init wd_init_module(void) +{ + struct net_device *dev; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only autoprobe 1st one */ + printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); + } + dev = alloc_ei_netdev(); + if (!dev) + break; + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->mem_start = mem[this_dev]; + dev->mem_end = mem_end[this_dev]; + if (do_wd_probe(dev) == 0) { + dev_wd[found++] = dev; + continue; + } + free_netdev(dev); + printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); + break; + } + if (found) + return 0; + return -ENXIO; +} +module_init(wd_init_module); + +static void cleanup_card(struct net_device *dev) +{ + free_irq(dev->irq, dev); + release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT); + iounmap(ei_status.mem); +} + +static void __exit wd_cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { + struct net_device *dev = dev_wd[this_dev]; + if (dev) { + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } + } +} +module_exit(wd_cleanup_module); +#endif /* MODULE */ diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index b8f70e2a1763..bdc29d143160 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -61,6 +61,7 @@ source "drivers/net/ethernet/engleder/Kconfig" source "drivers/net/ethernet/ezchip/Kconfig" source "drivers/net/ethernet/faraday/Kconfig" source "drivers/net/ethernet/freescale/Kconfig" +source "drivers/net/ethernet/fujitsu/Kconfig" source "drivers/net/ethernet/fungible/Kconfig" source "drivers/net/ethernet/google/Kconfig" source "drivers/net/ethernet/hisilicon/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 57344fec6ce0..6bffb60ba644 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_NET_VENDOR_ENGLEDER) += engleder/ obj-$(CONFIG_NET_VENDOR_EZCHIP) += ezchip/ obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/ obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/ +obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/ obj-$(CONFIG_NET_VENDOR_FUNGIBLE) += fungible/ obj-$(CONFIG_NET_VENDOR_GOOGLE) += google/ obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/ diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index e35991141a1a..45e8d698781c 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -43,6 +43,17 @@ config AMD8111_ETH To compile this driver as a module, choose M here. The module will be called amd8111e. +config LANCE + tristate "AMD LANCE and PCnet (AT1500 and NE2100) support" + depends on ISA && ISA_DMA_API && !ARM && !PPC32 + select NETDEV_LEGACY_INIT + help + If you have a network (Ethernet) card of this type, say Y here. + Some LinkSys cards are of this type. + + To compile this driver as a module, choose M here: the module + will be called lance. This is recommended. + config PCNET32 tristate "AMD PCnet32 PCI support" depends on PCI && HAS_IOPORT @@ -109,6 +120,16 @@ config MVME147_NET driver for this chip in your kernel. To compile this driver as a module, choose M here. +config PCMCIA_NMCLAN + tristate "New Media PCMCIA support" + depends on PCMCIA && HAS_IOPORT + help + Say Y here if you intend to attach a New Media Ethernet or LiveWire + PCMCIA (PC-card) Ethernet card to your computer. + + To compile this driver as a module, choose M here: the module will be + called nmclan_cs. If unsure, say N. + config SUN3LANCE tristate "Sun3/Sun3x on-board LANCE support" depends on (SUN3 || SUN3X) diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile index e485fae235a7..2dcfb84731e1 100644 --- a/drivers/net/ethernet/amd/Makefile +++ b/drivers/net/ethernet/amd/Makefile @@ -9,8 +9,10 @@ obj-$(CONFIG_ARIADNE) += ariadne.o obj-$(CONFIG_ATARILANCE) += atarilance.o obj-$(CONFIG_DECLANCE) += declance.o obj-$(CONFIG_HPLANCE) += hplance.o 7990.o +obj-$(CONFIG_LANCE) += lance.o obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o +obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o obj-$(CONFIG_PCNET32) += pcnet32.o obj-$(CONFIG_SUN3LANCE) += sun3lance.o obj-$(CONFIG_SUNLANCE) += sunlance.o diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c new file mode 100644 index 000000000000..98afd8cb0efb --- /dev/null +++ b/drivers/net/ethernet/amd/lance.c @@ -0,0 +1,1317 @@ +/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */ +/* + Written/copyright 1993-1998 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + This driver is for the Allied Telesis AT1500 and HP J2405A, and should work + with most other LANCE-based bus-master (NE2100/NE2500) ethercards. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Andrey V. Savochkin: + - alignment problem with 1.3.* kernel and some minor changes. + Thomas Bogendoerfer (tsbogend@bigbug.franken.de): + - added support for Linux/Alpha, but removed most of it, because + it worked only for the PCI chip. + - added hook for the 32bit lance driver + - added PCnetPCI II (79C970A) to chip table + Paul Gortmaker (gpg109@rsphy1.anu.edu.au): + - hopefully fix above so Linux/Alpha can use ISA cards too. + 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb + v1.12 10/27/97 Module support -djb + v1.14 2/3/98 Module support modified, made PCI support optional -djb + v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed + before unregister_netdev() which caused NULL pointer + reference later in the chain (in rtnetlink_fill_ifinfo()) + -- Mika Kuoppala + + Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from + the 2.1 version of the old driver - Alan Cox + + Get rid of check_region, check kmalloc return in lance_probe1 + Arnaldo Carvalho de Melo - 11/01/2001 + + Reworked detection, added support for Racal InterLan EtherBlaster cards + Vesselin Kostadinov - 22/4/2004 +*/ + +static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0}; +static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options); +static int __init do_lance_probe(struct net_device *dev); + + +static struct card { + char id_offset14; + char id_offset15; +} cards[] = { + { //"normal" + .id_offset14 = 0x57, + .id_offset15 = 0x57, + }, + { //NI6510EB + .id_offset14 = 0x52, + .id_offset15 = 0x44, + }, + { //Racal InterLan EtherBlaster + .id_offset14 = 0x52, + .id_offset15 = 0x49, + }, +}; +#define NUM_CARDS 3 + +#ifdef LANCE_DEBUG +static int lance_debug = LANCE_DEBUG; +#else +static int lance_debug = 1; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the AMD 79C960, the "PCnet-ISA +single-chip ethernet controller for ISA". This chip is used in a wide +variety of boards from vendors such as Allied Telesis, HP, Kingston, +and Boca. This driver is also intended to work with older AMD 7990 +designs, such as the NE1500 and NE2100, and newer 79C961. For convenience, +I use the name LANCE to refer to all of the AMD chips, even though it properly +refers only to the original 7990. + +II. Board-specific settings + +The driver is designed to work the boards that use the faster +bus-master mode, rather than in shared memory mode. (Only older designs +have on-board buffer memory needed to support the slower shared memory mode.) + +Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA +channel. This driver probes the likely base addresses: +{0x300, 0x320, 0x340, 0x360}. +After the board is found it generates a DMA-timeout interrupt and uses +autoIRQ to find the IRQ line. The DMA channel can be set with the low bits +of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is +probed for by enabling each free DMA channel in turn and checking if +initialization succeeds. + +The HP-J2405A board is an exception: with this board it is easy to read the +EEPROM-set values for the base, IRQ, and DMA. (Of course you must already +_know_ the base address -- that field is for writing the EEPROM.) + +III. Driver operation + +IIIa. Ring buffers +The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes +the base and length of the data buffer, along with status bits. The length +of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of +the buffer length (rather than being directly the buffer length) for +implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to +ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries +needlessly uses extra space and reduces the chance that an upper layer will +be able to reorder queued Tx packets based on priority. Decreasing the number +of entries makes it more difficult to achieve back-to-back packet transmission +and increases the chance that Rx ring will overflow. (Consider the worst case +of receiving back-to-back minimum-sized packets.) + +The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver +statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to +avoid the administrative overhead. For the Rx side this avoids dynamically +allocating full-sized buffers "just in case", at the expense of a +memory-to-memory data copy for each packet received. For most systems this +is a good tradeoff: the Rx buffer will always be in low memory, the copy +is inexpensive, and it primes the cache for later packet processing. For Tx +the buffers are only used when needed as low-memory bounce buffers. + +IIIB. 16M memory limitations. +For the ISA bus master mode all structures used directly by the LANCE, +the initialization block, Rx and Tx rings, and data buffers, must be +accessible from the ISA bus, i.e. in the lower 16M of real memory. +This is a problem for current Linux kernels on >16M machines. The network +devices are initialized after memory initialization, and the kernel doles out +memory from the top of memory downward. The current solution is to have a +special network initialization routine that's called before memory +initialization; this will eventually be generalized for all network devices. +As mentioned before, low-memory "bounce-buffers" are used when needed. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.) After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +*/ + +/* Set the number of Tx and Rx buffers, using Log_2(# buffers). + Reasonable default values are 16 Tx buffers, and 16 Rx buffers. + That translates to 4 and 4 (16 == 2^^4). + This is a compile-time option for efficiency. + */ +#ifndef LANCE_LOG_TX_BUFFERS +#define LANCE_LOG_TX_BUFFERS 4 +#define LANCE_LOG_RX_BUFFERS 4 +#endif + +#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) + +#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) + +#define PKT_BUF_SZ 1544 + +/* Offsets from base I/O address. */ +#define LANCE_DATA 0x10 +#define LANCE_ADDR 0x12 +#define LANCE_RESET 0x14 +#define LANCE_BUS_IF 0x16 +#define LANCE_TOTAL_SIZE 0x18 + +#define TX_TIMEOUT (HZ/5) + +/* The LANCE Rx and Tx ring descriptors. */ +struct lance_rx_head { + s32 base; + s16 buf_length; /* This length is 2s complement (negative)! */ + s16 msg_length; /* This length is "normal". */ +}; + +struct lance_tx_head { + s32 base; + s16 length; /* Length is 2s complement (negative)! */ + s16 misc; +}; + +/* The LANCE initialization block, described in databook. */ +struct lance_init_block { + u16 mode; /* Pre-set mode (reg. 15) */ + u8 phys_addr[6]; /* Physical ethernet address */ + u32 filter[2]; /* Multicast filter (unused). */ + /* Receive and transmit ring base, along with extra bits. */ + u32 rx_ring; /* Tx and Rx ring base pointers */ + u32 tx_ring; +}; + +struct lance_private { + /* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */ + struct lance_rx_head rx_ring[RX_RING_SIZE]; + struct lance_tx_head tx_ring[TX_RING_SIZE]; + struct lance_init_block init_block; + const char *name; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + unsigned long rx_buffs; /* Address of Rx and Tx buffers. */ + /* Tx low-memory "bounce buffer" address. */ + char (*tx_bounce_buffs)[PKT_BUF_SZ]; + int cur_rx, cur_tx; /* The next free ring entry */ + int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + int dma; + unsigned char chip_version; /* See lance_chip_type. */ + spinlock_t devlock; +}; + +#define LANCE_MUST_PAD 0x00000001 +#define LANCE_ENABLE_AUTOSELECT 0x00000002 +#define LANCE_MUST_REINIT_RING 0x00000004 +#define LANCE_MUST_UNRESET 0x00000008 +#define LANCE_HAS_MISSED_FRAME 0x00000010 + +/* A mapping from the chip ID number to the part number and features. + These are from the datasheets -- in real life the '970 version + reportedly has the same ID as the '965. */ +static struct lance_chip_type { + int id_number; + const char *name; + int flags; +} chip_table[] = { + {0x0000, "LANCE 7990", /* Ancient lance chip. */ + LANCE_MUST_PAD + LANCE_MUST_UNRESET}, + {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */ + LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + + LANCE_HAS_MISSED_FRAME}, + {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */ + LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + + LANCE_HAS_MISSED_FRAME}, + {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */ + LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + + LANCE_HAS_MISSED_FRAME}, + /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call + it the PCnet32. */ + {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */ + LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + + LANCE_HAS_MISSED_FRAME}, + {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */ + LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + + LANCE_HAS_MISSED_FRAME}, + {0x0, "PCnet (unknown)", + LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + + LANCE_HAS_MISSED_FRAME}, +}; + +enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6}; + + +/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers. + Assume yes until we know the memory size. */ +static unsigned char lance_need_isa_bounce_buffers = 1; + +static int lance_open(struct net_device *dev); +static void lance_init_ring(struct net_device *dev, gfp_t mode); +static netdev_tx_t lance_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static int lance_rx(struct net_device *dev); +static irqreturn_t lance_interrupt(int irq, void *dev_id); +static int lance_close(struct net_device *dev); +static struct net_device_stats *lance_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue); + + + +#ifdef MODULE +#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */ + +static struct net_device *dev_lance[MAX_CARDS]; +static int io[MAX_CARDS]; +static int dma[MAX_CARDS]; +static int irq[MAX_CARDS]; + +module_param_hw_array(io, int, ioport, NULL, 0); +module_param_hw_array(dma, int, dma, NULL, 0); +module_param_hw_array(irq, int, irq, NULL, 0); +module_param(lance_debug, int, 0); +MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required"); +MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)"); +MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)"); +MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)"); + +static int __init lance_init_module(void) +{ + struct net_device *dev; + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { + if (io[this_dev] == 0) { + if (this_dev != 0) /* only complain once */ + break; + printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); + return -EPERM; + } + dev = alloc_etherdev(0); + if (!dev) + break; + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->dma = dma[this_dev]; + if (do_lance_probe(dev) == 0) { + dev_lance[found++] = dev; + continue; + } + free_netdev(dev); + break; + } + if (found != 0) + return 0; + return -ENXIO; +} +module_init(lance_init_module); + +static void cleanup_card(struct net_device *dev) +{ + struct lance_private *lp = dev->ml_priv; + if (dev->dma != 4) + free_dma(dev->dma); + release_region(dev->base_addr, LANCE_TOTAL_SIZE); + kfree(lp->tx_bounce_buffs); + kfree((void*)lp->rx_buffs); + kfree(lp); +} + +static void __exit lance_cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { + struct net_device *dev = dev_lance[this_dev]; + if (dev) { + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } + } +} +module_exit(lance_cleanup_module); +#endif /* MODULE */ +MODULE_DESCRIPTION("AMD LANCE/PCnet Ethernet driver"); +MODULE_LICENSE("GPL"); + + +/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other + board probes now that kmalloc() can allocate ISA DMA-able regions. + This also allows the LANCE driver to be used as a module. + */ +static int __init do_lance_probe(struct net_device *dev) +{ + unsigned int *port; + int result; + + if (high_memory <= phys_to_virt(16*1024*1024)) + lance_need_isa_bounce_buffers = 0; + + for (port = lance_portlist; *port; port++) { + int ioaddr = *port; + struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE, + "lance-probe"); + + if (r) { + /* Detect the card with minimal I/O reads */ + char offset14 = inb(ioaddr + 14); + int card; + for (card = 0; card < NUM_CARDS; ++card) + if (cards[card].id_offset14 == offset14) + break; + if (card < NUM_CARDS) {/*yes, the first byte matches*/ + char offset15 = inb(ioaddr + 15); + for (card = 0; card < NUM_CARDS; ++card) + if ((cards[card].id_offset14 == offset14) && + (cards[card].id_offset15 == offset15)) + break; + } + if (card < NUM_CARDS) { /*Signature OK*/ + result = lance_probe1(dev, ioaddr, 0, 0); + if (!result) { + struct lance_private *lp = dev->ml_priv; + int ver = lp->chip_version; + + r->name = chip_table[ver].name; + return 0; + } + } + release_region(ioaddr, LANCE_TOTAL_SIZE); + } + } + return -ENODEV; +} + +#ifndef MODULE +struct net_device * __init lance_probe(int unit) +{ + struct net_device *dev = alloc_etherdev(0); + int err; + + if (!dev) + return ERR_PTR(-ENODEV); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_lance_probe(dev); + if (err) + goto out; + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); +} +#endif + +static const struct net_device_ops lance_netdev_ops = { + .ndo_open = lance_open, + .ndo_start_xmit = lance_start_xmit, + .ndo_stop = lance_close, + .ndo_get_stats = lance_get_stats, + .ndo_set_rx_mode = set_multicast_list, + .ndo_tx_timeout = lance_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options) +{ + struct lance_private *lp; + unsigned long dma_channels; /* Mark spuriously-busy DMA channels */ + int i, reset_val, lance_version; + const char *chipname; + /* Flags for specific chips or boards. */ + unsigned char hpJ2405A = 0; /* HP ISA adaptor */ + int hp_builtin = 0; /* HP on-board ethernet. */ + static int did_version; /* Already printed version info. */ + unsigned long flags; + int err = -ENOMEM; + void __iomem *bios; + u8 addr[ETH_ALEN]; + + /* First we look for special cases. + Check for HP's on-board ethernet by looking for 'HP' in the BIOS. + There are two HP versions, check the BIOS for the configuration port. + This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com. + */ + bios = ioremap(0xf00f0, 0x14); + if (!bios) + return -ENOMEM; + if (readw(bios + 0x12) == 0x5048) { + static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360}; + int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99; + /* We can have boards other than the built-in! Verify this is on-board. */ + if ((inb(hp_port) & 0xc0) == 0x80 && + ioaddr_table[inb(hp_port) & 3] == ioaddr) + hp_builtin = hp_port; + } + iounmap(bios); + /* We also recognize the HP Vectra on-board here, but check below. */ + hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 && + inb(ioaddr+2) == 0x09); + + /* Reset the LANCE. */ + reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */ + + /* The Un-Reset needed is only needed for the real NE2100, and will + confuse the HP board. */ + if (!hpJ2405A) + outw(reset_val, ioaddr+LANCE_RESET); + + outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */ + if (inw(ioaddr+LANCE_DATA) != 0x0004) + return -ENODEV; + + /* Get the version of the chip. */ + outw(88, ioaddr+LANCE_ADDR); + if (inw(ioaddr+LANCE_ADDR) != 88) { + lance_version = 0; + } else { /* Good, it's a newer chip. */ + int chip_version = inw(ioaddr+LANCE_DATA); + outw(89, ioaddr+LANCE_ADDR); + chip_version |= inw(ioaddr+LANCE_DATA) << 16; + if (lance_debug > 2) + printk(" LANCE chip version is %#x.\n", chip_version); + if ((chip_version & 0xfff) != 0x003) + return -ENODEV; + chip_version = (chip_version >> 12) & 0xffff; + for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) { + if (chip_table[lance_version].id_number == chip_version) + break; + } + } + + /* We can't allocate private data from alloc_etherdev() because it must + a ISA DMA-able region. */ + chipname = chip_table[lance_version].name; + printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr); + + /* There is a 16 byte station address PROM at the base address. + The first six bytes are the station address. */ + for (i = 0; i < 6; i++) + addr[i] = inb(ioaddr + i); + eth_hw_addr_set(dev, addr); + printk("%pM", dev->dev_addr); + + dev->base_addr = ioaddr; + /* Make certain the data structures used by the LANCE are aligned and DMAble. */ + + lp = kzalloc_obj(*lp, GFP_DMA | GFP_KERNEL); + if (!lp) + return -ENOMEM; + if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp); + dev->ml_priv = lp; + lp->name = chipname; + lp->rx_buffs = (unsigned long)kmalloc_array(RX_RING_SIZE, PKT_BUF_SZ, + GFP_DMA | GFP_KERNEL); + if (!lp->rx_buffs) + goto out_lp; + if (lance_need_isa_bounce_buffers) { + lp->tx_bounce_buffs = kmalloc_array(TX_RING_SIZE, PKT_BUF_SZ, + GFP_DMA | GFP_KERNEL); + if (!lp->tx_bounce_buffs) + goto out_rx; + } else + lp->tx_bounce_buffs = NULL; + + lp->chip_version = lance_version; + spin_lock_init(&lp->devlock); + + lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */ + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; + lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; + + outw(0x0001, ioaddr+LANCE_ADDR); + inw(ioaddr+LANCE_ADDR); + outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); + outw(0x0002, ioaddr+LANCE_ADDR); + inw(ioaddr+LANCE_ADDR); + outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); + outw(0x0000, ioaddr+LANCE_ADDR); + inw(ioaddr+LANCE_ADDR); + + if (irq) { /* Set iff PCI card. */ + dev->dma = 4; /* Native bus-master, no DMA channel needed. */ + dev->irq = irq; + } else if (hp_builtin) { + static const char dma_tbl[4] = {3, 5, 6, 0}; + static const char irq_tbl[4] = {3, 4, 5, 9}; + unsigned char port_val = inb(hp_builtin); + dev->dma = dma_tbl[(port_val >> 4) & 3]; + dev->irq = irq_tbl[(port_val >> 2) & 3]; + printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma); + } else if (hpJ2405A) { + static const char dma_tbl[4] = {3, 5, 6, 7}; + static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15}; + short reset_val = inw(ioaddr+LANCE_RESET); + dev->dma = dma_tbl[(reset_val >> 2) & 3]; + dev->irq = irq_tbl[(reset_val >> 4) & 7]; + printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma); + } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */ + short bus_info; + outw(8, ioaddr+LANCE_ADDR); + bus_info = inw(ioaddr+LANCE_BUS_IF); + dev->dma = bus_info & 0x07; + dev->irq = (bus_info >> 4) & 0x0F; + } else { + /* The DMA channel may be passed in PARAM1. */ + if (dev->mem_start & 0x07) + dev->dma = dev->mem_start & 0x07; + } + + if (dev->dma == 0) { + /* Read the DMA channel status register, so that we can avoid + stuck DMA channels in the DMA detection below. */ + dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | + (inb(DMA2_STAT_REG) & 0xf0); + } + err = -ENODEV; + if (dev->irq >= 2) + printk(" assigned IRQ %d", dev->irq); + else if (lance_version != 0) { /* 7990 boards need DMA detection first. */ + unsigned long irq_mask; + + /* To auto-IRQ we enable the initialization-done and DMA error + interrupts. For ISA boards we get a DMA error, but VLB and PCI + boards will work. */ + irq_mask = probe_irq_on(); + + /* Trigger an initialization just for the interrupt. */ + outw(0x0041, ioaddr+LANCE_DATA); + + mdelay(20); + dev->irq = probe_irq_off(irq_mask); + if (dev->irq) + printk(", probed IRQ %d", dev->irq); + else { + printk(", failed to detect IRQ line.\n"); + goto out_tx; + } + + /* Check for the initialization done bit, 0x0100, which means + that we don't need a DMA channel. */ + if (inw(ioaddr+LANCE_DATA) & 0x0100) + dev->dma = 4; + } + + if (dev->dma == 4) { + printk(", no DMA needed.\n"); + } else if (dev->dma) { + if (request_dma(dev->dma, chipname)) { + printk("DMA %d allocation failed.\n", dev->dma); + goto out_tx; + } else + printk(", assigned DMA %d.\n", dev->dma); + } else { /* OK, we have to auto-DMA. */ + for (i = 0; i < 4; i++) { + static const char dmas[] = { 5, 6, 7, 3 }; + int dma = dmas[i]; + int boguscnt; + + /* Don't enable a permanently busy DMA channel, or the machine + will hang. */ + if (test_bit(dma, &dma_channels)) + continue; + outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */ + if (request_dma(dma, chipname)) + continue; + + flags=claim_dma_lock(); + set_dma_mode(dma, DMA_MODE_CASCADE); + enable_dma(dma); + release_dma_lock(flags); + + /* Trigger an initialization. */ + outw(0x0001, ioaddr+LANCE_DATA); + for (boguscnt = 100; boguscnt > 0; --boguscnt) + if (inw(ioaddr+LANCE_DATA) & 0x0900) + break; + if (inw(ioaddr+LANCE_DATA) & 0x0100) { + dev->dma = dma; + printk(", DMA %d.\n", dev->dma); + break; + } else { + flags=claim_dma_lock(); + disable_dma(dma); + release_dma_lock(flags); + free_dma(dma); + } + } + if (i == 4) { /* Failure: bail. */ + printk("DMA detection failed.\n"); + goto out_tx; + } + } + + if (lance_version == 0 && dev->irq == 0) { + /* We may auto-IRQ now that we have a DMA channel. */ + /* Trigger an initialization just for the interrupt. */ + unsigned long irq_mask; + + irq_mask = probe_irq_on(); + outw(0x0041, ioaddr+LANCE_DATA); + + mdelay(40); + dev->irq = probe_irq_off(irq_mask); + if (dev->irq == 0) { + printk(" Failed to detect the 7990 IRQ line.\n"); + goto out_dma; + } + printk(" Auto-IRQ detected IRQ%d.\n", dev->irq); + } + + if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { + /* Turn on auto-select of media (10baseT or BNC) so that the user + can watch the LEDs even if the board isn't opened. */ + outw(0x0002, ioaddr+LANCE_ADDR); + /* Don't touch 10base2 power bit. */ + outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); + } + + if (lance_debug > 0 && did_version++ == 0) + printk(version); + + /* The LANCE-specific entries in the device structure. */ + dev->netdev_ops = &lance_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + err = register_netdev(dev); + if (err) + goto out_dma; + return 0; +out_dma: + if (dev->dma != 4) + free_dma(dev->dma); +out_tx: + kfree(lp->tx_bounce_buffs); +out_rx: + kfree((void*)lp->rx_buffs); +out_lp: + kfree(lp); + return err; +} + + +static int +lance_open(struct net_device *dev) +{ + struct lance_private *lp = dev->ml_priv; + int ioaddr = dev->base_addr; + int i; + + if (dev->irq == 0 || + request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) { + return -EAGAIN; + } + + /* We used to allocate DMA here, but that was silly. + DMA lines can't be shared! We now permanently allocate them. */ + + /* Reset the LANCE */ + inw(ioaddr+LANCE_RESET); + + /* The DMA controller is used as a no-operation slave, "cascade mode". */ + if (dev->dma != 4) { + unsigned long flags=claim_dma_lock(); + enable_dma(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_CASCADE); + release_dma_lock(flags); + } + + /* Un-Reset the LANCE, needed only for the NE2100. */ + if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET) + outw(0, ioaddr+LANCE_RESET); + + if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { + /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */ + outw(0x0002, ioaddr+LANCE_ADDR); + /* Only touch autoselect bit. */ + outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); + } + + if (lance_debug > 1) + printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", + dev->name, dev->irq, dev->dma, + (u32) isa_virt_to_bus(lp->tx_ring), + (u32) isa_virt_to_bus(lp->rx_ring), + (u32) isa_virt_to_bus(&lp->init_block)); + + lance_init_ring(dev, GFP_KERNEL); + /* Re-initialize the LANCE, and start it when done. */ + outw(0x0001, ioaddr+LANCE_ADDR); + outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); + outw(0x0002, ioaddr+LANCE_ADDR); + outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); + + outw(0x0004, ioaddr+LANCE_ADDR); + outw(0x0915, ioaddr+LANCE_DATA); + + outw(0x0000, ioaddr+LANCE_ADDR); + outw(0x0001, ioaddr+LANCE_DATA); + + netif_start_queue (dev); + + i = 0; + while (i++ < 100) + if (inw(ioaddr+LANCE_DATA) & 0x0100) + break; + /* + * We used to clear the InitDone bit, 0x0100, here but Mark Stockton + * reports that doing so triggers a bug in the '974. + */ + outw(0x0042, ioaddr+LANCE_DATA); + + if (lance_debug > 2) + printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n", + dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA)); + + return 0; /* Always succeed */ +} + +/* The LANCE has been halted for one reason or another (busmaster memory + arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, + etc.). Modern LANCE variants always reload their ring-buffer + configuration when restarted, so we must reinitialize our ring + context before restarting. As part of this reinitialization, + find all packets still on the Tx ring and pretend that they had been + sent (in effect, drop the packets on the floor) - the higher-level + protocols will time out and retransmit. It'd be better to shuffle + these skbs to a temp list and then actually re-Tx them after + restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com +*/ + +static void +lance_purge_ring(struct net_device *dev) +{ + struct lance_private *lp = dev->ml_priv; + int i; + + /* Free all the skbuffs in the Rx and Tx queues. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = lp->rx_skbuff[i]; + lp->rx_skbuff[i] = NULL; + lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */ + if (skb) + dev_kfree_skb_any(skb); + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (lp->tx_skbuff[i]) { + dev_kfree_skb_any(lp->tx_skbuff[i]); + lp->tx_skbuff[i] = NULL; + } + } +} + + +/* Initialize the LANCE Rx and Tx rings. */ +static void +lance_init_ring(struct net_device *dev, gfp_t gfp) +{ + struct lance_private *lp = dev->ml_priv; + int i; + + lp->cur_rx = lp->cur_tx = 0; + lp->dirty_rx = lp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + void *rx_buff; + + skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp); + lp->rx_skbuff[i] = skb; + if (skb) + rx_buff = skb->data; + else + rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp); + if (!rx_buff) + lp->rx_ring[i].base = 0; + else + lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000; + lp->rx_ring[i].buf_length = -PKT_BUF_SZ; + } + /* The Tx buffer address is filled in as needed, but we do need to clear + the upper ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_skbuff[i] = NULL; + lp->tx_ring[i].base = 0; + } + + lp->init_block.mode = 0x0000; + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; + lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; +} + +static void +lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit) +{ + struct lance_private *lp = dev->ml_priv; + + if (must_reinit || + (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) { + lance_purge_ring(dev); + lance_init_ring(dev, GFP_ATOMIC); + } + outw(0x0000, dev->base_addr + LANCE_ADDR); + outw(csr0_bits, dev->base_addr + LANCE_DATA); +} + + +static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue) +{ + struct lance_private *lp = (struct lance_private *) dev->ml_priv; + int ioaddr = dev->base_addr; + + outw (0, ioaddr + LANCE_ADDR); + printk ("%s: transmit timed out, status %4.4x, resetting.\n", + dev->name, inw (ioaddr + LANCE_DATA)); + outw (0x0004, ioaddr + LANCE_DATA); + dev->stats.tx_errors++; +#ifndef final_version + if (lance_debug > 3) { + int i; + printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", + lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "", + lp->cur_rx); + for (i = 0; i < RX_RING_SIZE; i++) + printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", + lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, + lp->rx_ring[i].msg_length); + for (i = 0; i < TX_RING_SIZE; i++) + printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", + lp->tx_ring[i].base, -lp->tx_ring[i].length, + lp->tx_ring[i].misc); + printk ("\n"); + } +#endif + lance_restart (dev, 0x0043, 1); + + netif_trans_update(dev); /* prevent tx timeout */ + netif_wake_queue (dev); +} + + +static netdev_tx_t lance_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct lance_private *lp = dev->ml_priv; + int ioaddr = dev->base_addr; + int entry; + unsigned long flags; + + spin_lock_irqsave(&lp->devlock, flags); + + if (lance_debug > 3) { + outw(0x0000, ioaddr+LANCE_ADDR); + printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, + inw(ioaddr+LANCE_DATA)); + outw(0x0000, ioaddr+LANCE_DATA); + } + + /* Fill in a Tx ring entry */ + + /* Mask to ring buffer boundary. */ + entry = lp->cur_tx & TX_RING_MOD_MASK; + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* The old LANCE chips doesn't automatically pad buffers to min. size. */ + if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) { + if (skb->len < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) + goto out; + lp->tx_ring[entry].length = -ETH_ZLEN; + } + else + lp->tx_ring[entry].length = -skb->len; + } else + lp->tx_ring[entry].length = -skb->len; + + lp->tx_ring[entry].misc = 0x0000; + + dev->stats.tx_bytes += skb->len; + + /* If any part of this buffer is >16M we must copy it to a low-memory + buffer. */ + if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) { + if (lance_debug > 5) + printk("%s: bouncing a high-memory packet (%#x).\n", + dev->name, (u32)isa_virt_to_bus(skb->data)); + skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len); + lp->tx_ring[entry].base = + ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; + dev_consume_skb_irq(skb); + } else { + lp->tx_skbuff[entry] = skb; + lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000; + } + lp->cur_tx++; + + /* Trigger an immediate send poll. */ + outw(0x0000, ioaddr+LANCE_ADDR); + outw(0x0048, ioaddr+LANCE_DATA); + + if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE) + netif_stop_queue(dev); + +out: + spin_unlock_irqrestore(&lp->devlock, flags); + return NETDEV_TX_OK; +} + +/* The LANCE interrupt handler. */ +static irqreturn_t lance_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct lance_private *lp; + int csr0, ioaddr, boguscnt=10; + int must_restart; + + ioaddr = dev->base_addr; + lp = dev->ml_priv; + + spin_lock (&lp->devlock); + + outw(0x00, dev->base_addr + LANCE_ADDR); + while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 && + --boguscnt >= 0) { + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); + + must_restart = 0; + + if (lance_debug > 5) + printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", + dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); + + if (csr0 & 0x0400) /* Rx interrupt */ + lance_rx(dev); + + if (csr0 & 0x0200) { /* Tx-done interrupt */ + int dirty_tx = lp->dirty_tx; + + while (dirty_tx < lp->cur_tx) { + int entry = dirty_tx & TX_RING_MOD_MASK; + int status = lp->tx_ring[entry].base; + + if (status < 0) + break; /* It still hasn't been Txed */ + + lp->tx_ring[entry].base = 0; + + if (status & 0x40000000) { + /* There was an major error, log it. */ + int err_status = lp->tx_ring[entry].misc; + dev->stats.tx_errors++; + if (err_status & 0x0400) + dev->stats.tx_aborted_errors++; + if (err_status & 0x0800) + dev->stats.tx_carrier_errors++; + if (err_status & 0x1000) + dev->stats.tx_window_errors++; + if (err_status & 0x4000) { + /* Ackk! On FIFO errors the Tx unit is turned off! */ + dev->stats.tx_fifo_errors++; + /* Remove this verbosity later! */ + printk("%s: Tx FIFO error! Status %4.4x.\n", + dev->name, csr0); + /* Restart the chip. */ + must_restart = 1; + } + } else { + if (status & 0x18000000) + dev->stats.collisions++; + dev->stats.tx_packets++; + } + + /* We must free the original skb if it's not a data-only copy + in the bounce buffer. */ + if (lp->tx_skbuff[entry]) { + dev_consume_skb_irq(lp->tx_skbuff[entry]); + lp->tx_skbuff[entry] = NULL; + } + dirty_tx++; + } + +#ifndef final_version + if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { + printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n", + dirty_tx, lp->cur_tx, + netif_queue_stopped(dev) ? "yes" : "no"); + dirty_tx += TX_RING_SIZE; + } +#endif + + /* if the ring is no longer full, accept more packets */ + if (netif_queue_stopped(dev) && + dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) + netif_wake_queue (dev); + + lp->dirty_tx = dirty_tx; + } + + /* Log misc errors. */ + if (csr0 & 0x4000) + dev->stats.tx_errors++; /* Tx babble. */ + if (csr0 & 0x1000) + dev->stats.rx_errors++; /* Missed a Rx frame. */ + if (csr0 & 0x0800) { + printk("%s: Bus master arbitration failure, status %4.4x.\n", + dev->name, csr0); + /* Restart the chip. */ + must_restart = 1; + } + + if (must_restart) { + /* stop the chip to clear the error condition, then restart */ + outw(0x0000, dev->base_addr + LANCE_ADDR); + outw(0x0004, dev->base_addr + LANCE_DATA); + lance_restart(dev, 0x0002, 0); + } + } + + /* Clear any other interrupt, and set interrupt enable. */ + outw(0x0000, dev->base_addr + LANCE_ADDR); + outw(0x7940, dev->base_addr + LANCE_DATA); + + if (lance_debug > 4) + printk("%s: exiting interrupt, csr%d=%#4.4x.\n", + dev->name, inw(ioaddr + LANCE_ADDR), + inw(dev->base_addr + LANCE_DATA)); + + spin_unlock (&lp->devlock); + return IRQ_HANDLED; +} + +static int +lance_rx(struct net_device *dev) +{ + struct lance_private *lp = dev->ml_priv; + int entry = lp->cur_rx & RX_RING_MOD_MASK; + int i; + + /* If we own the next entry, it's a new packet. Send it up. */ + while (lp->rx_ring[entry].base >= 0) { + int status = lp->rx_ring[entry].base >> 24; + + if (status != 0x03) { /* There was an error. */ + /* There is a tricky error noted by John Murphy, + to Russ Nelson: Even with full-sized + buffers it's possible for a jabber packet to use two + buffers, with only the last correctly noting the error. */ + if (status & 0x01) /* Only count a general error at the */ + dev->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x20) + dev->stats.rx_frame_errors++; + if (status & 0x10) + dev->stats.rx_over_errors++; + if (status & 0x08) + dev->stats.rx_crc_errors++; + if (status & 0x04) + dev->stats.rx_fifo_errors++; + lp->rx_ring[entry].base &= 0x03ffffff; + } + else + { + /* Malloc up new buffer, compatible with net3. */ + short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4; + struct sk_buff *skb; + + if(pkt_len<60) + { + printk("%s: Runt packet!\n",dev->name); + dev->stats.rx_errors++; + } + else + { + skb = dev_alloc_skb(pkt_len+2); + if (!skb) + { + printk("%s: Memory squeeze, deferring packet.\n", dev->name); + for (i=0; i < RX_RING_SIZE; i++) + if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0) + break; + + if (i > RX_RING_SIZE -2) + { + dev->stats.rx_dropped++; + lp->rx_ring[entry].base |= 0x80000000; + lp->cur_rx++; + } + break; + } + skb_reserve(skb,2); /* 16 byte align */ + skb_put(skb,pkt_len); /* Make room */ + skb_copy_to_linear_data(skb, + (unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)), + pkt_len); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + } + /* The docs say that the buffer length isn't touched, but Andrew Boyd + of QNX reports that some revs of the 79C965 clear it. */ + lp->rx_ring[entry].buf_length = -PKT_BUF_SZ; + lp->rx_ring[entry].base |= 0x80000000; + entry = (++lp->cur_rx) & RX_RING_MOD_MASK; + } + + /* We should check that at least two ring entries are free. If not, + we should free one and mark stats->rx_dropped++. */ + + return 0; +} + +static int +lance_close(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct lance_private *lp = dev->ml_priv; + + netif_stop_queue (dev); + + if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { + outw(112, ioaddr+LANCE_ADDR); + dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); + } + outw(0, ioaddr+LANCE_ADDR); + + if (lance_debug > 1) + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inw(ioaddr+LANCE_DATA)); + + /* We stop the LANCE here -- it occasionally polls + memory if we don't. */ + outw(0x0004, ioaddr+LANCE_DATA); + + if (dev->dma != 4) + { + unsigned long flags=claim_dma_lock(); + disable_dma(dev->dma); + release_dma_lock(flags); + } + free_irq(dev->irq, dev); + + lance_purge_ring(dev); + + return 0; +} + +static struct net_device_stats *lance_get_stats(struct net_device *dev) +{ + struct lance_private *lp = dev->ml_priv; + + if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { + short ioaddr = dev->base_addr; + short saved_addr; + unsigned long flags; + + spin_lock_irqsave(&lp->devlock, flags); + saved_addr = inw(ioaddr+LANCE_ADDR); + outw(112, ioaddr+LANCE_ADDR); + dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); + outw(saved_addr, ioaddr+LANCE_ADDR); + spin_unlock_irqrestore(&lp->devlock, flags); + } + + return &dev->stats; +} + +/* Set or clear the multicast filter for this adaptor. + */ + +static void set_multicast_list(struct net_device *dev) +{ + short ioaddr = dev->base_addr; + + outw(0, ioaddr+LANCE_ADDR); + outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */ + + if (dev->flags&IFF_PROMISC) { + outw(15, ioaddr+LANCE_ADDR); + outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */ + } else { + short multicast_table[4]; + int i; + int num_addrs=netdev_mc_count(dev); + if(dev->flags&IFF_ALLMULTI) + num_addrs=1; + /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ + memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table)); + for (i = 0; i < 4; i++) { + outw(8 + i, ioaddr+LANCE_ADDR); + outw(multicast_table[i], ioaddr+LANCE_DATA); + } + outw(15, ioaddr+LANCE_ADDR); + outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */ + } + + lance_restart(dev, 0x0142, 0); /* Resume normal operation */ + +} + diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c new file mode 100644 index 000000000000..37054a670407 --- /dev/null +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -0,0 +1,1508 @@ +/* ---------------------------------------------------------------------------- +Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN. + nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao + + The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media + Access Controller for Ethernet (MACE). It is essentially the Am2150 + PCMCIA Ethernet card contained in the Am2150 Demo Kit. + +Written by Roger C. Pao + Copyright 1995 Roger C. Pao + Linux 2.5 cleanups Copyright Red Hat 2003 + + This software may be used and distributed according to the terms of + the GNU General Public License. + +Ported to Linux 1.3.* network driver environment by + Matti Aarnio + +References + + Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993 + Am79C940 (MACE) Data Sheet, 1994 + Am79C90 (C-LANCE) Data Sheet, 1994 + Linux PCMCIA Programmer's Guide v1.17 + /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8 + + Eric Mears, New Media Corporation + Tom Pollard, New Media Corporation + Dean Siasoyco, New Media Corporation + Ken Lesniak, Silicon Graphics, Inc. + Donald Becker + David Hinds + + The Linux client driver is based on the 3c589_cs.c client driver by + David Hinds. + + The Linux network driver outline is based on the 3c589_cs.c driver, + the 8390.c driver, and the example skeleton.c kernel code, which are + by Donald Becker. + + The Am2150 network driver hardware interface code is based on the + OS/9000 driver for the New Media Ethernet LAN by Eric Mears. + + Special thanks for testing and help in debugging this driver goes + to Ken Lesniak. + +------------------------------------------------------------------------------- +Driver Notes and Issues +------------------------------------------------------------------------------- + +1. Developed on a Dell 320SLi + PCMCIA Card Services 2.6.2 + Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386 + +2. rc.pcmcia may require loading pcmcia_core with io_speed=300: + 'insmod pcmcia_core.o io_speed=300'. + This will avoid problems with fast systems which causes rx_framecnt + to return random values. + +3. If hot extraction does not work for you, use 'ifconfig eth0 down' + before extraction. + +4. There is a bad slow-down problem in this driver. + +5. Future: Multicast processing. In the meantime, do _not_ compile your + kernel with multicast ip enabled. + +------------------------------------------------------------------------------- +History +------------------------------------------------------------------------------- +Log: nmclan_cs.c,v + * 2.5.75-ac1 2003/07/11 Alan Cox + * Fixed hang on card eject as we probe it + * Cleaned up to use new style locking. + * + * Revision 0.16 1995/07/01 06:42:17 rpao + * Bug fix: nmclan_reset() called CardServices incorrectly. + * + * Revision 0.15 1995/05/24 08:09:47 rpao + * Re-implement MULTI_TX dev->tbusy handling. + * + * Revision 0.14 1995/05/23 03:19:30 rpao + * Added, in nmclan_config(), "tuple.Attributes = 0;". + * Modified MACE ID check to ignore chip revision level. + * Avoid tx_free_frames race condition between _start_xmit and _interrupt. + * + * Revision 0.13 1995/05/18 05:56:34 rpao + * Statistics changes. + * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list. + * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup. + * + * Revision 0.12 1995/05/14 00:12:23 rpao + * Statistics overhaul. + * + +95/05/13 rpao V0.10a + Bug fix: MACE statistics counters used wrong I/O ports. + Bug fix: mace_interrupt() needed to allow statistics to be + processed without RX or TX interrupts pending. +95/05/11 rpao V0.10 + Multiple transmit request processing. + Modified statistics to use MACE counters where possible. +95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO. + *Released +95/05/10 rpao V0.08 + Bug fix: Make all non-exported functions private by using + static keyword. + Bug fix: Test IntrCnt _before_ reading MACE_IR. +95/05/10 rpao V0.07 Statistics. +95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states. + +---------------------------------------------------------------------------- */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "nmclan_cs" + +/* ---------------------------------------------------------------------------- +Conditional Compilation Options +---------------------------------------------------------------------------- */ + +#define MULTI_TX 0 +#define RESET_ON_TIMEOUT 1 +#define TX_INTERRUPTABLE 1 +#define RESET_XILINX 0 + +/* ---------------------------------------------------------------------------- +Include Files +---------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* ---------------------------------------------------------------------------- +Defines +---------------------------------------------------------------------------- */ + +#define MACE_LADRF_LEN 8 + /* 8 bytes in Logical Address Filter */ + +/* Loop Control Defines */ +#define MACE_MAX_IR_ITERATIONS 10 +#define MACE_MAX_RX_ITERATIONS 12 + /* + TBD: Dean brought this up, and I assumed the hardware would + handle it: + + If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be + non-zero when the isr exits. We may not get another interrupt + to process the remaining packets for some time. + */ + +/* +The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA) +which manages the interface between the MACE and the PCMCIA bus. It +also includes buffer management for the 32K x 8 SRAM to control up to +four transmit and 12 receive frames at a time. +*/ +#define AM2150_MAX_TX_FRAMES 4 +#define AM2150_MAX_RX_FRAMES 12 + +/* Am2150 Ethernet Card I/O Mapping */ +#define AM2150_RCV 0x00 +#define AM2150_XMT 0x04 +#define AM2150_XMT_SKIP 0x09 +#define AM2150_RCV_NEXT 0x0A +#define AM2150_RCV_FRAME_COUNT 0x0B +#define AM2150_MACE_BANK 0x0C +#define AM2150_MACE_BASE 0x10 + +/* MACE Registers */ +#define MACE_RCVFIFO 0 +#define MACE_XMTFIFO 1 +#define MACE_XMTFC 2 +#define MACE_XMTFS 3 +#define MACE_XMTRC 4 +#define MACE_RCVFC 5 +#define MACE_RCVFS 6 +#define MACE_FIFOFC 7 +#define MACE_IR 8 +#define MACE_IMR 9 +#define MACE_PR 10 +#define MACE_BIUCC 11 +#define MACE_FIFOCC 12 +#define MACE_MACCC 13 +#define MACE_PLSCC 14 +#define MACE_PHYCC 15 +#define MACE_CHIPIDL 16 +#define MACE_CHIPIDH 17 +#define MACE_IAC 18 +/* Reserved */ +#define MACE_LADRF 20 +#define MACE_PADR 21 +/* Reserved */ +/* Reserved */ +#define MACE_MPC 24 +/* Reserved */ +#define MACE_RNTPC 26 +#define MACE_RCVCC 27 +/* Reserved */ +#define MACE_UTR 29 +#define MACE_RTR1 30 +#define MACE_RTR2 31 + +/* MACE Bit Masks */ +#define MACE_XMTRC_EXDEF 0x80 +#define MACE_XMTRC_XMTRC 0x0F + +#define MACE_XMTFS_XMTSV 0x80 +#define MACE_XMTFS_UFLO 0x40 +#define MACE_XMTFS_LCOL 0x20 +#define MACE_XMTFS_MORE 0x10 +#define MACE_XMTFS_ONE 0x08 +#define MACE_XMTFS_DEFER 0x04 +#define MACE_XMTFS_LCAR 0x02 +#define MACE_XMTFS_RTRY 0x01 + +#define MACE_RCVFS_RCVSTS 0xF000 +#define MACE_RCVFS_OFLO 0x8000 +#define MACE_RCVFS_CLSN 0x4000 +#define MACE_RCVFS_FRAM 0x2000 +#define MACE_RCVFS_FCS 0x1000 + +#define MACE_FIFOFC_RCVFC 0xF0 +#define MACE_FIFOFC_XMTFC 0x0F + +#define MACE_IR_JAB 0x80 +#define MACE_IR_BABL 0x40 +#define MACE_IR_CERR 0x20 +#define MACE_IR_RCVCCO 0x10 +#define MACE_IR_RNTPCO 0x08 +#define MACE_IR_MPCO 0x04 +#define MACE_IR_RCVINT 0x02 +#define MACE_IR_XMTINT 0x01 + +#define MACE_MACCC_PROM 0x80 +#define MACE_MACCC_DXMT2PD 0x40 +#define MACE_MACCC_EMBA 0x20 +#define MACE_MACCC_RESERVED 0x10 +#define MACE_MACCC_DRCVPA 0x08 +#define MACE_MACCC_DRCVBC 0x04 +#define MACE_MACCC_ENXMT 0x02 +#define MACE_MACCC_ENRCV 0x01 + +#define MACE_PHYCC_LNKFL 0x80 +#define MACE_PHYCC_DLNKTST 0x40 +#define MACE_PHYCC_REVPOL 0x20 +#define MACE_PHYCC_DAPC 0x10 +#define MACE_PHYCC_LRT 0x08 +#define MACE_PHYCC_ASEL 0x04 +#define MACE_PHYCC_RWAKE 0x02 +#define MACE_PHYCC_AWAKE 0x01 + +#define MACE_IAC_ADDRCHG 0x80 +#define MACE_IAC_PHYADDR 0x04 +#define MACE_IAC_LOGADDR 0x02 + +#define MACE_UTR_RTRE 0x80 +#define MACE_UTR_RTRD 0x40 +#define MACE_UTR_RPA 0x20 +#define MACE_UTR_FCOLL 0x10 +#define MACE_UTR_RCVFCSE 0x08 +#define MACE_UTR_LOOP_INCL_MENDEC 0x06 +#define MACE_UTR_LOOP_NO_MENDEC 0x04 +#define MACE_UTR_LOOP_EXTERNAL 0x02 +#define MACE_UTR_LOOP_NONE 0x00 +#define MACE_UTR_RESERVED 0x01 + +/* Switch MACE register bank (only 0 and 1 are valid) */ +#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK) + +#define MACE_IMR_DEFAULT \ + (0xFF - \ + ( \ + MACE_IR_CERR | \ + MACE_IR_RCVCCO | \ + MACE_IR_RNTPCO | \ + MACE_IR_MPCO | \ + MACE_IR_RCVINT | \ + MACE_IR_XMTINT \ + ) \ + ) +#undef MACE_IMR_DEFAULT +#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */ + +#define TX_TIMEOUT ((400*HZ)/1000) + +/* ---------------------------------------------------------------------------- +Type Definitions +---------------------------------------------------------------------------- */ + +typedef struct _mace_statistics { + /* MACE_XMTFS */ + int xmtsv; + int uflo; + int lcol; + int more; + int one; + int defer; + int lcar; + int rtry; + + /* MACE_XMTRC */ + int exdef; + int xmtrc; + + /* RFS1--Receive Status (RCVSTS) */ + int oflo; + int clsn; + int fram; + int fcs; + + /* RFS2--Runt Packet Count (RNTPC) */ + int rfs_rntpc; + + /* RFS3--Receive Collision Count (RCVCC) */ + int rfs_rcvcc; + + /* MACE_IR */ + int jab; + int babl; + int cerr; + int rcvcco; + int rntpco; + int mpco; + + /* MACE_MPC */ + int mpc; + + /* MACE_RNTPC */ + int rntpc; + + /* MACE_RCVCC */ + int rcvcc; +} mace_statistics; + +typedef struct _mace_private { + struct pcmcia_device *p_dev; + mace_statistics mace_stats; /* MACE chip statistics counters */ + + /* restore_multicast_list() state variables */ + int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */ + int multicast_num_addrs; + + char tx_free_frames; /* Number of free transmit frame buffers */ + char tx_irq_disabled; /* MACE TX interrupt disabled */ + + spinlock_t bank_lock; /* Must be held if you step off bank 0 */ +} mace_private; + +/* ---------------------------------------------------------------------------- +Private Global Variables +---------------------------------------------------------------------------- */ + +static const char *if_names[]={ + "Auto", "10baseT", "BNC", +}; + +/* ---------------------------------------------------------------------------- +Parameters + These are the parameters that can be set during loading with + 'insmod'. +---------------------------------------------------------------------------- */ + +MODULE_DESCRIPTION("New Media PCMCIA ethernet driver"); +MODULE_LICENSE("GPL"); + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) + +/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */ +INT_MODULE_PARM(if_port, 0); + + +/* ---------------------------------------------------------------------------- +Function Prototypes +---------------------------------------------------------------------------- */ + +static int nmclan_config(struct pcmcia_device *link); +static void nmclan_release(struct pcmcia_device *link); + +static void nmclan_reset(struct net_device *dev); +static int mace_config(struct net_device *dev, struct ifmap *map); +static int mace_open(struct net_device *dev); +static int mace_close(struct net_device *dev); +static netdev_tx_t mace_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue); +static irqreturn_t mace_interrupt(int irq, void *dev_id); +static struct net_device_stats *mace_get_stats(struct net_device *dev); +static int mace_rx(struct net_device *dev, unsigned char RxCnt); +static void restore_multicast_list(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static const struct ethtool_ops netdev_ethtool_ops; + + +static void nmclan_detach(struct pcmcia_device *p_dev); + +static const struct net_device_ops mace_netdev_ops = { + .ndo_open = mace_open, + .ndo_stop = mace_close, + .ndo_start_xmit = mace_start_xmit, + .ndo_tx_timeout = mace_tx_timeout, + .ndo_set_config = mace_config, + .ndo_get_stats = mace_get_stats, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int nmclan_probe(struct pcmcia_device *link) +{ + mace_private *lp; + struct net_device *dev; + + dev_dbg(&link->dev, "nmclan_attach()\n"); + + /* Create new ethernet device */ + dev = alloc_etherdev(sizeof(mace_private)); + if (!dev) + return -ENOMEM; + lp = netdev_priv(dev); + lp->p_dev = link; + link->priv = dev; + + spin_lock_init(&lp->bank_lock); + link->resource[0]->end = 32; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + link->config_flags |= CONF_ENABLE_IRQ; + link->config_index = 1; + link->config_regs = PRESENT_OPTION; + + lp->tx_free_frames=AM2150_MAX_TX_FRAMES; + + dev->netdev_ops = &mace_netdev_ops; + dev->ethtool_ops = &netdev_ethtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + return nmclan_config(link); +} /* nmclan_attach */ + +static void nmclan_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + dev_dbg(&link->dev, "nmclan_detach\n"); + + unregister_netdev(dev); + + nmclan_release(link); + + free_netdev(dev); +} /* nmclan_detach */ + +/* ---------------------------------------------------------------------------- +mace_read + Reads a MACE register. This is bank independent; however, the + caller must ensure that this call is not interruptable. We are + assuming that during normal operation, the MACE is always in + bank 0. +---------------------------------------------------------------------------- */ +static int mace_read(mace_private *lp, unsigned int ioaddr, int reg) +{ + int data = 0xFF; + unsigned long flags; + + switch (reg >> 4) { + case 0: /* register 0-15 */ + data = inb(ioaddr + AM2150_MACE_BASE + reg); + break; + case 1: /* register 16-31 */ + spin_lock_irqsave(&lp->bank_lock, flags); + MACEBANK(1); + data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); + MACEBANK(0); + spin_unlock_irqrestore(&lp->bank_lock, flags); + break; + } + return data & 0xFF; +} /* mace_read */ + +/* ---------------------------------------------------------------------------- +mace_write + Writes to a MACE register. This is bank independent; however, + the caller must ensure that this call is not interruptable. We + are assuming that during normal operation, the MACE is always in + bank 0. +---------------------------------------------------------------------------- */ +static void mace_write(mace_private *lp, unsigned int ioaddr, int reg, + int data) +{ + unsigned long flags; + + switch (reg >> 4) { + case 0: /* register 0-15 */ + outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); + break; + case 1: /* register 16-31 */ + spin_lock_irqsave(&lp->bank_lock, flags); + MACEBANK(1); + outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); + MACEBANK(0); + spin_unlock_irqrestore(&lp->bank_lock, flags); + break; + } +} /* mace_write */ + +/* ---------------------------------------------------------------------------- +mace_init + Resets the MACE chip. +---------------------------------------------------------------------------- */ +static int mace_init(mace_private *lp, unsigned int ioaddr, + const char *enet_addr) +{ + int i; + int ct = 0; + + /* MACE Software reset */ + mace_write(lp, ioaddr, MACE_BIUCC, 1); + while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) { + /* Wait for reset bit to be cleared automatically after <= 200ns */; + if(++ct > 500) + { + pr_err("reset failed, card removed?\n"); + return -1; + } + udelay(1); + } + mace_write(lp, ioaddr, MACE_BIUCC, 0); + + /* The Am2150 requires that the MACE FIFOs operate in burst mode. */ + mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F); + + mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ + mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ + + /* + * Bit 2-1 PORTSEL[1-0] Port Select. + * 00 AUI/10Base-2 + * 01 10Base-T + * 10 DAI Port (reserved in Am2150) + * 11 GPSI + * For this card, only the first two are valid. + * So, PLSCC should be set to + * 0x00 for 10Base-2 + * 0x02 for 10Base-T + * Or just set ASEL in PHYCC below! + */ + switch (if_port) { + case 1: + mace_write(lp, ioaddr, MACE_PLSCC, 0x02); + break; + case 2: + mace_write(lp, ioaddr, MACE_PLSCC, 0x00); + break; + default: + mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4); + /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, + and the MACE device will automatically select the operating media + interface port. */ + break; + } + + mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); + /* Poll ADDRCHG bit */ + ct = 0; + while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + { + if(++ ct > 500) + { + pr_err("ADDRCHG timeout, card removed?\n"); + return -1; + } + } + /* Set PADR register */ + for (i = 0; i < ETH_ALEN; i++) + mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]); + + /* MAC Configuration Control Register should be written last */ + /* Let set_multicast_list set this. */ + /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ + mace_write(lp, ioaddr, MACE_MACCC, 0x00); + return 0; +} /* mace_init */ + +static int nmclan_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + mace_private *lp = netdev_priv(dev); + u8 *buf; + size_t len; + int i, ret; + unsigned int ioaddr; + + dev_dbg(&link->dev, "nmclan_config\n"); + + link->io_lines = 5; + ret = pcmcia_request_io(link); + if (ret) + goto failed; + ret = pcmcia_request_irq(link, mace_interrupt); + if (ret) + goto failed; + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + + ioaddr = dev->base_addr; + + /* Read the ethernet address from the CIS. */ + len = pcmcia_get_tuple(link, 0x80, &buf); + if (!buf || len < ETH_ALEN) { + kfree(buf); + goto failed; + } + eth_hw_addr_set(dev, buf); + kfree(buf); + + /* Verify configuration by reading the MACE ID. */ + { + char sig[2]; + + sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL); + sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH); + if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { + dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n", + sig[0], sig[1]); + } else { + pr_notice("mace id not found: %x %x should be 0x40 0x?9\n", + sig[0], sig[1]); + goto failed; + } + } + + if(mace_init(lp, ioaddr, dev->dev_addr) == -1) + goto failed; + + /* The if_port symbol can be set when the module is loaded */ + if (if_port <= 2) + dev->if_port = if_port; + else + pr_notice("invalid if_port requested\n"); + + SET_NETDEV_DEV(dev, &link->dev); + + i = register_netdev(dev); + if (i != 0) { + pr_notice("register_netdev() failed\n"); + goto failed; + } + + netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n", + dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr); + return 0; + +failed: + nmclan_release(link); + return -ENODEV; +} /* nmclan_config */ + +static void nmclan_release(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "nmclan_release\n"); + pcmcia_disable_device(link); +} + +static int nmclan_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int nmclan_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) { + nmclan_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + + +/* ---------------------------------------------------------------------------- +nmclan_reset + Reset and restore all of the Xilinx and MACE registers. +---------------------------------------------------------------------------- */ +static void nmclan_reset(struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + +#if RESET_XILINX + struct pcmcia_device *link = &lp->link; + u8 OrigCorValue; + + /* Save original COR value */ + pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue); + + /* Reset Xilinx */ + dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n", + OrigCorValue); + pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET); + /* Need to wait for 20 ms for PCMCIA to finish reset. */ + + /* Restore original COR configuration index */ + pcmcia_write_config_byte(link, CISREG_COR, + (COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK))); + /* Xilinx is now completely reset along with the MACE chip. */ + lp->tx_free_frames=AM2150_MAX_TX_FRAMES; + +#endif /* #if RESET_XILINX */ + + /* Xilinx is now completely reset along with the MACE chip. */ + lp->tx_free_frames=AM2150_MAX_TX_FRAMES; + + /* Reinitialize the MACE chip for operation. */ + mace_init(lp, dev->base_addr, dev->dev_addr); + mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); + + /* Restore the multicast list and enable TX and RX. */ + restore_multicast_list(dev); +} /* nmclan_reset */ + +/* ---------------------------------------------------------------------------- +mace_config + [Someone tell me what this is supposed to do? Is if_port a defined + standard? If so, there should be defines to indicate 1=10Base-T, + 2=10Base-2, etc. including limited automatic detection.] +---------------------------------------------------------------------------- */ +static int mace_config(struct net_device *dev, struct ifmap *map) +{ + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if (map->port <= 2) { + WRITE_ONCE(dev->if_port, map->port); + netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); + } else + return -EINVAL; + } + return 0; +} /* mace_config */ + +/* ---------------------------------------------------------------------------- +mace_open + Open device driver. +---------------------------------------------------------------------------- */ +static int mace_open(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + mace_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + if (!pcmcia_dev_present(link)) + return -ENODEV; + + link->open++; + + MACEBANK(0); + + netif_start_queue(dev); + nmclan_reset(dev); + + return 0; /* Always succeed */ +} /* mace_open */ + +/* ---------------------------------------------------------------------------- +mace_close + Closes device driver. +---------------------------------------------------------------------------- */ +static int mace_close(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + mace_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); + + /* Mask off all interrupts from the MACE chip. */ + outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR); + + link->open--; + netif_stop_queue(dev); + + return 0; +} /* mace_close */ + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + snprintf(info->bus_info, sizeof(info->bus_info), + "PCMCIA 0x%lx", dev->base_addr); +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + +/* ---------------------------------------------------------------------------- +mace_start_xmit + This routine begins the packet transmit function. When completed, + it will generate a transmit interrupt. + + According to /usr/src/linux/net/inet/dev.c, if _start_xmit + returns 0, the "packet is now solely the responsibility of the + driver." If _start_xmit returns non-zero, the "transmission + failed, put skb back into a list." +---------------------------------------------------------------------------- */ + +static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + mace_private *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + netdev_notice(dev, "transmit timed out -- "); +#if RESET_ON_TIMEOUT + pr_cont("resetting card\n"); + pcmcia_reset_card(link->socket); +#else /* #if RESET_ON_TIMEOUT */ + pr_cont("NOT resetting card\n"); +#endif /* #if RESET_ON_TIMEOUT */ + netif_trans_update(dev); /* prevent tx timeout */ + netif_wake_queue(dev); +} + +static netdev_tx_t mace_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + + netif_stop_queue(dev); + + pr_debug("%s: mace_start_xmit(length = %ld) called.\n", + dev->name, (long)skb->len); + +#if (!TX_INTERRUPTABLE) + /* Disable MACE TX interrupts. */ + outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT, + ioaddr + AM2150_MACE_BASE + MACE_IMR); + lp->tx_irq_disabled=1; +#endif /* #if (!TX_INTERRUPTABLE) */ + + { + /* This block must not be interrupted by another transmit request! + mace_tx_timeout will take care of timer-based retransmissions from + the upper layers. The interrupt handler is guaranteed never to + service a transmit interrupt while we are in here. + */ + + dev->stats.tx_bytes += skb->len; + lp->tx_free_frames--; + + /* WARNING: Write the _exact_ number of bytes written in the header! */ + /* Put out the word header [must be an outw()] . . . */ + outw(skb->len, ioaddr + AM2150_XMT); + /* . . . and the packet [may be any combination of outw() and outb()] */ + outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1); + if (skb->len & 1) { + /* Odd byte transfer */ + outb(skb->data[skb->len-1], ioaddr + AM2150_XMT); + } + +#if MULTI_TX + if (lp->tx_free_frames > 0) + netif_start_queue(dev); +#endif /* #if MULTI_TX */ + } + +#if (!TX_INTERRUPTABLE) + /* Re-enable MACE TX interrupts. */ + lp->tx_irq_disabled=0; + outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR); +#endif /* #if (!TX_INTERRUPTABLE) */ + + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} /* mace_start_xmit */ + +/* ---------------------------------------------------------------------------- +mace_interrupt + The interrupt handler. +---------------------------------------------------------------------------- */ +static irqreturn_t mace_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + mace_private *lp = netdev_priv(dev); + unsigned int ioaddr; + int status; + int IntrCnt = MACE_MAX_IR_ITERATIONS; + + if (!dev) { + pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n", + irq); + return IRQ_NONE; + } + + ioaddr = dev->base_addr; + + if (lp->tx_irq_disabled) { + const char *msg; + if (lp->tx_irq_disabled) + msg = "Interrupt with tx_irq_disabled"; + else + msg = "Re-entering the interrupt handler"; + netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n", + msg, + inb(ioaddr + AM2150_MACE_BASE + MACE_IR), + inb(ioaddr + AM2150_MACE_BASE + MACE_IMR)); + /* WARNING: MACE_IR has been read! */ + return IRQ_NONE; + } + + if (!netif_device_present(dev)) { + netdev_dbg(dev, "interrupt from dead card\n"); + return IRQ_NONE; + } + + do { + /* WARNING: MACE_IR is a READ/CLEAR port! */ + status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR); + if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS) + return IRQ_NONE; + + pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status); + + if (status & MACE_IR_RCVINT) { + mace_rx(dev, MACE_MAX_RX_ITERATIONS); + } + + if (status & MACE_IR_XMTINT) { + unsigned char fifofc; + unsigned char xmtrc; + unsigned char xmtfs; + + fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC); + if ((fifofc & MACE_FIFOFC_XMTFC)==0) { + dev->stats.tx_errors++; + outb(0xFF, ioaddr + AM2150_XMT_SKIP); + } + + /* Transmit Retry Count (XMTRC, reg 4) */ + xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC); + if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++; + lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC); + + if ( + (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) & + MACE_XMTFS_XMTSV /* Transmit Status Valid */ + ) { + lp->mace_stats.xmtsv++; + + if (xmtfs & ~MACE_XMTFS_XMTSV) { + if (xmtfs & MACE_XMTFS_UFLO) { + /* Underflow. Indicates that the Transmit FIFO emptied before + the end of frame was reached. */ + lp->mace_stats.uflo++; + } + if (xmtfs & MACE_XMTFS_LCOL) { + /* Late Collision */ + lp->mace_stats.lcol++; + } + if (xmtfs & MACE_XMTFS_MORE) { + /* MORE than one retry was needed */ + lp->mace_stats.more++; + } + if (xmtfs & MACE_XMTFS_ONE) { + /* Exactly ONE retry occurred */ + lp->mace_stats.one++; + } + if (xmtfs & MACE_XMTFS_DEFER) { + /* Transmission was defered */ + lp->mace_stats.defer++; + } + if (xmtfs & MACE_XMTFS_LCAR) { + /* Loss of carrier */ + lp->mace_stats.lcar++; + } + if (xmtfs & MACE_XMTFS_RTRY) { + /* Retry error: transmit aborted after 16 attempts */ + lp->mace_stats.rtry++; + } + } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */ + + } /* if (xmtfs & MACE_XMTFS_XMTSV) */ + + dev->stats.tx_packets++; + lp->tx_free_frames++; + netif_wake_queue(dev); + } /* if (status & MACE_IR_XMTINT) */ + + if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) { + if (status & MACE_IR_JAB) { + /* Jabber Error. Excessive transmit duration (20-150ms). */ + lp->mace_stats.jab++; + } + if (status & MACE_IR_BABL) { + /* Babble Error. >1518 bytes transmitted. */ + lp->mace_stats.babl++; + } + if (status & MACE_IR_CERR) { + /* Collision Error. CERR indicates the absence of the + Signal Quality Error Test message after a packet + transmission. */ + lp->mace_stats.cerr++; + } + if (status & MACE_IR_RCVCCO) { + /* Receive Collision Count Overflow; */ + lp->mace_stats.rcvcco++; + } + if (status & MACE_IR_RNTPCO) { + /* Runt Packet Count Overflow */ + lp->mace_stats.rntpco++; + } + if (status & MACE_IR_MPCO) { + /* Missed Packet Count Overflow */ + lp->mace_stats.mpco++; + } + } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */ + + } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt)); + + return IRQ_HANDLED; +} /* mace_interrupt */ + +/* ---------------------------------------------------------------------------- +mace_rx + Receives packets. +---------------------------------------------------------------------------- */ +static int mace_rx(struct net_device *dev, unsigned char RxCnt) +{ + mace_private *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + unsigned char rx_framecnt; + unsigned short rx_status; + + while ( + ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) && + (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */ + (RxCnt--) + ) { + rx_status = inw(ioaddr + AM2150_RCV); + + pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status" + " 0x%X.\n", dev->name, rx_framecnt, rx_status); + + if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */ + dev->stats.rx_errors++; + if (rx_status & MACE_RCVFS_OFLO) { + lp->mace_stats.oflo++; + } + if (rx_status & MACE_RCVFS_CLSN) { + lp->mace_stats.clsn++; + } + if (rx_status & MACE_RCVFS_FRAM) { + lp->mace_stats.fram++; + } + if (rx_status & MACE_RCVFS_FCS) { + lp->mace_stats.fcs++; + } + } else { + short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4; + /* Auto Strip is off, always subtract 4 */ + struct sk_buff *skb; + + lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV); + /* runt packet count */ + lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV); + /* rcv collision count */ + + pr_debug(" receiving packet size 0x%X rx_status" + " 0x%X.\n", pkt_len, rx_status); + + skb = netdev_alloc_skb(dev, pkt_len + 2); + + if (skb) { + skb_reserve(skb, 2); + insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); + if (pkt_len & 1) + *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV); + skb->protocol = eth_type_trans(skb, dev); + + netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ + + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ + continue; + } else { + pr_debug("%s: couldn't allocate a sk_buff of size" + " %d.\n", dev->name, pkt_len); + dev->stats.rx_dropped++; + } + } + outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ + } /* while */ + + return 0; +} /* mace_rx */ + +/* ---------------------------------------------------------------------------- +pr_linux_stats +---------------------------------------------------------------------------- */ +static void pr_linux_stats(struct net_device_stats *pstats) +{ + pr_debug("pr_linux_stats\n"); + pr_debug(" rx_packets=%-7ld tx_packets=%ld\n", + (long)pstats->rx_packets, (long)pstats->tx_packets); + pr_debug(" rx_errors=%-7ld tx_errors=%ld\n", + (long)pstats->rx_errors, (long)pstats->tx_errors); + pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n", + (long)pstats->rx_dropped, (long)pstats->tx_dropped); + pr_debug(" multicast=%-7ld collisions=%ld\n", + (long)pstats->multicast, (long)pstats->collisions); + + pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n", + (long)pstats->rx_length_errors, (long)pstats->rx_over_errors); + pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n", + (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors); + pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n", + (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors); + + pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n", + (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors); + pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n", + (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors); + pr_debug(" tx_window_errors=%ld\n", + (long)pstats->tx_window_errors); +} /* pr_linux_stats */ + +/* ---------------------------------------------------------------------------- +pr_mace_stats +---------------------------------------------------------------------------- */ +static void pr_mace_stats(mace_statistics *pstats) +{ + pr_debug("pr_mace_stats\n"); + + pr_debug(" xmtsv=%-7d uflo=%d\n", + pstats->xmtsv, pstats->uflo); + pr_debug(" lcol=%-7d more=%d\n", + pstats->lcol, pstats->more); + pr_debug(" one=%-7d defer=%d\n", + pstats->one, pstats->defer); + pr_debug(" lcar=%-7d rtry=%d\n", + pstats->lcar, pstats->rtry); + + /* MACE_XMTRC */ + pr_debug(" exdef=%-7d xmtrc=%d\n", + pstats->exdef, pstats->xmtrc); + + /* RFS1--Receive Status (RCVSTS) */ + pr_debug(" oflo=%-7d clsn=%d\n", + pstats->oflo, pstats->clsn); + pr_debug(" fram=%-7d fcs=%d\n", + pstats->fram, pstats->fcs); + + /* RFS2--Runt Packet Count (RNTPC) */ + /* RFS3--Receive Collision Count (RCVCC) */ + pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n", + pstats->rfs_rntpc, pstats->rfs_rcvcc); + + /* MACE_IR */ + pr_debug(" jab=%-7d babl=%d\n", + pstats->jab, pstats->babl); + pr_debug(" cerr=%-7d rcvcco=%d\n", + pstats->cerr, pstats->rcvcco); + pr_debug(" rntpco=%-7d mpco=%d\n", + pstats->rntpco, pstats->mpco); + + /* MACE_MPC */ + pr_debug(" mpc=%d\n", pstats->mpc); + + /* MACE_RNTPC */ + pr_debug(" rntpc=%d\n", pstats->rntpc); + + /* MACE_RCVCC */ + pr_debug(" rcvcc=%d\n", pstats->rcvcc); + +} /* pr_mace_stats */ + +/* ---------------------------------------------------------------------------- +update_stats + Update statistics. We change to register window 1, so this + should be run single-threaded if the device is active. This is + expected to be a rare operation, and it's simpler for the rest + of the driver to assume that window 0 is always valid rather + than use a special window-state variable. + + oflo & uflo should _never_ occur since it would mean the Xilinx + was not able to transfer data between the MACE FIFO and the + card's SRAM fast enough. If this happens, something is + seriously wrong with the hardware. +---------------------------------------------------------------------------- */ +static void update_stats(unsigned int ioaddr, struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + + lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC); + lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC); + lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC); + /* At this point, mace_stats is fully updated for this call. + We may now update the netdev stats. */ + + /* The MACE has no equivalent for netdev stats field which are commented + out. */ + + /* dev->stats.multicast; */ + dev->stats.collisions = + lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc; + /* Collision: The MACE may retry sending a packet 15 times + before giving up. The retry count is in XMTRC. + Does each retry constitute a collision? + If so, why doesn't the RCVCC record these collisions? */ + + /* detailed rx_errors: */ + dev->stats.rx_length_errors = + lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc; + /* dev->stats.rx_over_errors */ + dev->stats.rx_crc_errors = lp->mace_stats.fcs; + dev->stats.rx_frame_errors = lp->mace_stats.fram; + dev->stats.rx_fifo_errors = lp->mace_stats.oflo; + dev->stats.rx_missed_errors = + lp->mace_stats.mpco * 256 + lp->mace_stats.mpc; + + /* detailed tx_errors */ + dev->stats.tx_aborted_errors = lp->mace_stats.rtry; + dev->stats.tx_carrier_errors = lp->mace_stats.lcar; + /* LCAR usually results from bad cabling. */ + dev->stats.tx_fifo_errors = lp->mace_stats.uflo; + dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr; + /* dev->stats.tx_window_errors; */ +} /* update_stats */ + +/* ---------------------------------------------------------------------------- +mace_get_stats + Gathers ethernet statistics from the MACE chip. +---------------------------------------------------------------------------- */ +static struct net_device_stats *mace_get_stats(struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + + update_stats(dev->base_addr, dev); + + pr_debug("%s: updating the statistics.\n", dev->name); + pr_linux_stats(&dev->stats); + pr_mace_stats(&lp->mace_stats); + + return &dev->stats; +} /* net_device_stats */ + +/* ---------------------------------------------------------------------------- +updateCRC + Modified from Am79C90 data sheet. +---------------------------------------------------------------------------- */ + +#ifdef BROKEN_MULTICAST + +static void updateCRC(int *CRC, int bit) +{ + static const int poly[]={ + 1,1,1,0, 1,1,0,1, + 1,0,1,1, 1,0,0,0, + 1,0,0,0, 0,0,1,1, + 0,0,1,0, 0,0,0,0 + }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the + CRC generator polynomial. */ + + int j; + + /* shift CRC and control bit (CRC[32]) */ + for (j = 32; j > 0; j--) + CRC[j] = CRC[j-1]; + CRC[0] = 0; + + /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */ + if (bit ^ CRC[32]) + for (j = 0; j < 32; j++) + CRC[j] ^= poly[j]; +} /* updateCRC */ + +/* ---------------------------------------------------------------------------- +BuildLAF + Build logical address filter. + Modified from Am79C90 data sheet. + +Input + ladrf: logical address filter (contents initialized to 0) + adr: ethernet address +---------------------------------------------------------------------------- */ +static void BuildLAF(int *ladrf, int *adr) +{ + int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */ + + int i, byte; /* temporary array indices */ + int hashcode; /* the output object */ + + CRC[32]=0; + + for (byte = 0; byte < 6; byte++) + for (i = 0; i < 8; i++) + updateCRC(CRC, (adr[byte] >> i) & 1); + + hashcode = 0; + for (i = 0; i < 6; i++) + hashcode = (hashcode << 1) + CRC[i]; + + byte = hashcode >> 3; + ladrf[byte] |= (1 << (hashcode & 7)); + +#ifdef PCMCIA_DEBUG + if (0) + printk(KERN_DEBUG " adr =%pM\n", adr); + printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode); + for (i = 0; i < 8; i++) + pr_cont(" %02X", ladrf[i]); + pr_cont("\n"); +#endif +} /* BuildLAF */ + +/* ---------------------------------------------------------------------------- +restore_multicast_list + Restores the multicast filter for MACE chip to the last + set_multicast_list() call. + +Input + multicast_num_addrs + multicast_ladrf[] +---------------------------------------------------------------------------- */ +static void restore_multicast_list(struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + int num_addrs = lp->multicast_num_addrs; + int *ladrf = lp->multicast_ladrf; + unsigned int ioaddr = dev->base_addr; + int i; + + pr_debug("%s: restoring Rx mode to %d addresses.\n", + dev->name, num_addrs); + + if (num_addrs > 0) { + + pr_debug("Attempt to restore multicast list detected.\n"); + + mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); + /* Poll ADDRCHG bit */ + while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) + ; + /* Set LADRF register */ + for (i = 0; i < MACE_LADRF_LEN; i++) + mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]); + + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + + } else if (num_addrs < 0) { + + /* Promiscuous mode: receive all packets */ + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, + MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV + ); + + } else { + + /* Normal mode */ + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + + } +} /* restore_multicast_list */ + +/* ---------------------------------------------------------------------------- +set_multicast_list + Set or clear the multicast filter for this adaptor. + +Input + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. +Output + multicast_num_addrs + multicast_ladrf[] +---------------------------------------------------------------------------- */ + +static void set_multicast_list(struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + int adr[ETH_ALEN] = {0}; /* Ethernet address */ + struct netdev_hw_addr *ha; + +#ifdef PCMCIA_DEBUG + { + static int old; + if (netdev_mc_count(dev) != old) { + old = netdev_mc_count(dev); + pr_debug("%s: setting Rx mode to %d addresses.\n", + dev->name, old); + } + } +#endif + + /* Set multicast_num_addrs. */ + lp->multicast_num_addrs = netdev_mc_count(dev); + + /* Set multicast_ladrf. */ + if (num_addrs > 0) { + /* Calculate multicast logical address filter */ + memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN); + netdev_for_each_mc_addr(ha, dev) { + memcpy(adr, ha->addr, ETH_ALEN); + BuildLAF(lp->multicast_ladrf, adr); + } + } + + restore_multicast_list(dev); + +} /* set_multicast_list */ + +#endif /* BROKEN_MULTICAST */ + +static void restore_multicast_list(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + mace_private *lp = netdev_priv(dev); + + pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name, + lp->multicast_num_addrs); + + if (dev->flags & IFF_PROMISC) { + /* Promiscuous mode: receive all packets */ + mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, + MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV + ); + } else { + /* Normal mode */ + mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); + mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); + } +} /* restore_multicast_list */ + +static void set_multicast_list(struct net_device *dev) +{ + mace_private *lp = netdev_priv(dev); + +#ifdef PCMCIA_DEBUG + { + static int old; + if (netdev_mc_count(dev) != old) { + old = netdev_mc_count(dev); + pr_debug("%s: setting Rx mode to %d addresses.\n", + dev->name, old); + } + } +#endif + + lp->multicast_num_addrs = netdev_mc_count(dev); + restore_multicast_list(dev); + +} /* set_multicast_list */ + +static const struct pcmcia_device_id nmclan_ids[] = { + PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, nmclan_ids); + +static struct pcmcia_driver nmclan_cs_driver = { + .owner = THIS_MODULE, + .name = "nmclan_cs", + .probe = nmclan_probe, + .remove = nmclan_detach, + .id_table = nmclan_ids, + .suspend = nmclan_suspend, + .resume = nmclan_resume, +}; +module_pcmcia_driver(nmclan_cs_driver); diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig new file mode 100644 index 000000000000..06a28bce5d27 --- /dev/null +++ b/drivers/net/ethernet/fujitsu/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Fujitsu Network device configuration +# + +config NET_VENDOR_FUJITSU + bool "Fujitsu devices" + default y + depends on PCMCIA + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + the questions about Fujitsu cards. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_FUJITSU + +config PCMCIA_FMVJ18X + tristate "Fujitsu FMV-J18x PCMCIA support" + depends on PCMCIA && HAS_IOPORT + select CRC32 + help + Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible + PCMCIA (PC-card) Ethernet card to your computer. + + To compile this driver as a module, choose M here: the module will be + called fmvj18x_cs. If unsure, say N. + +endif # NET_VENDOR_FUJITSU diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile new file mode 100644 index 000000000000..74feebbf4572 --- /dev/null +++ b/drivers/net/ethernet/fujitsu/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Fujitsu network device drivers. +# + +obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c new file mode 100644 index 000000000000..4859493471db --- /dev/null +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -0,0 +1,1176 @@ +/*====================================================================== + fmvj18x_cs.c 2.8 2002/03/23 + + A fmvj18x (and its compatibles) PCMCIA client driver + + Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp + + TDK LAK-CD021 and CONTEC C-NET(PC)C support added by + Nobuhiro Katayama, kata-n@po.iijnet.or.jp + + The PCMCIA client code is based on code written by David Hinds. + Network code is based on the "FMV-18x driver" by Yutaka TAMIYA + but is actually largely Donald Becker's AT1700 driver, which + carries the following attribution: + + Written 1993-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + +======================================================================*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "fmvj18x_cs" +#define DRV_VERSION "2.9" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_DESCRIPTION("fmvj18x and compatible PCMCIA ethernet driver"); +MODULE_LICENSE("GPL"); + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) + +/* SRAM configuration */ +/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */ +INT_MODULE_PARM(sram_config, 0); + + +/*====================================================================*/ +/* + PCMCIA event handlers + */ +static int fmvj18x_config(struct pcmcia_device *link); +static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id); +static int fmvj18x_setup_mfc(struct pcmcia_device *link); +static void fmvj18x_release(struct pcmcia_device *link); +static void fmvj18x_detach(struct pcmcia_device *p_dev); + +/* + LAN controller(MBH86960A) specific routines + */ +static int fjn_config(struct net_device *dev, struct ifmap *map); +static int fjn_open(struct net_device *dev); +static int fjn_close(struct net_device *dev); +static netdev_tx_t fjn_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t fjn_interrupt(int irq, void *dev_id); +static void fjn_rx(struct net_device *dev); +static void fjn_reset(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue); +static const struct ethtool_ops netdev_ethtool_ops; + +/* + card type + */ +enum cardtype { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN, + XXX10304, NEC, KME +}; + +/* + driver specific data structure +*/ +struct local_info { + struct pcmcia_device *p_dev; + long open_time; + uint tx_started:1; + uint tx_queue; + u_short tx_queue_len; + enum cardtype cardtype; + u_short sent; + u_char __iomem *base; +}; + +#define MC_FILTERBREAK 64 + +/*====================================================================*/ +/* + ioport offset from the base address + */ +#define TX_STATUS 0 /* transmit status register */ +#define RX_STATUS 1 /* receive status register */ +#define TX_INTR 2 /* transmit interrupt mask register */ +#define RX_INTR 3 /* receive interrupt mask register */ +#define TX_MODE 4 /* transmit mode register */ +#define RX_MODE 5 /* receive mode register */ +#define CONFIG_0 6 /* configuration register 0 */ +#define CONFIG_1 7 /* configuration register 1 */ + +#define NODE_ID 8 /* node ID register (bank 0) */ +#define MAR_ADR 8 /* multicast address registers (bank 1) */ + +#define DATAPORT 8 /* buffer mem port registers (bank 2) */ +#define TX_START 10 /* transmit start register */ +#define COL_CTRL 11 /* 16 collision control register */ +#define BMPR12 12 /* reserved */ +#define BMPR13 13 /* reserved */ +#define RX_SKIP 14 /* skip received packet register */ + +#define LAN_CTRL 16 /* LAN card control register */ + +#define MAC_ID 0x1a /* hardware address */ +#define UNGERMANN_MAC_ID 0x18 /* UNGERMANN-BASS hardware address */ + +/* + control bits + */ +#define ENA_TMT_OK 0x80 +#define ENA_TMT_REC 0x20 +#define ENA_COL 0x04 +#define ENA_16_COL 0x02 +#define ENA_TBUS_ERR 0x01 + +#define ENA_PKT_RDY 0x80 +#define ENA_BUS_ERR 0x40 +#define ENA_LEN_ERR 0x08 +#define ENA_ALG_ERR 0x04 +#define ENA_CRC_ERR 0x02 +#define ENA_OVR_FLO 0x01 + +/* flags */ +#define F_TMT_RDY 0x80 /* can accept new packet */ +#define F_NET_BSY 0x40 /* carrier is detected */ +#define F_TMT_OK 0x20 /* send packet successfully */ +#define F_SRT_PKT 0x10 /* short packet error */ +#define F_COL_ERR 0x04 /* collision error */ +#define F_16_COL 0x02 /* 16 collision error */ +#define F_TBUS_ERR 0x01 /* bus read error */ + +#define F_PKT_RDY 0x80 /* packet(s) in buffer */ +#define F_BUS_ERR 0x40 /* bus read error */ +#define F_LEN_ERR 0x08 /* short packet */ +#define F_ALG_ERR 0x04 /* frame error */ +#define F_CRC_ERR 0x02 /* CRC error */ +#define F_OVR_FLO 0x01 /* overflow error */ + +#define F_BUF_EMP 0x40 /* receive buffer is empty */ + +#define F_SKP_PKT 0x05 /* drop packet in buffer */ + +/* default bitmaps */ +#define D_TX_INTR ( ENA_TMT_OK ) +#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \ + | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO ) +#define TX_STAT_M ( F_TMT_RDY ) +#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \ + | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO ) + +/* commands */ +#define D_TX_MODE 0x06 /* no tests, detect carrier */ +#define ID_MATCHED 0x02 /* (RX_MODE) */ +#define RECV_ALL 0x03 /* (RX_MODE) */ +#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */ +#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */ +#define CONFIG0_RST 0xda /* Data Link Controller off (CONFIG_0) */ +#define CONFIG0_RST_1 0xde /* Data Link Controller off (CONFIG_0) */ +#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */ +#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */ +#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */ +#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */ +#define DO_TX 0x80 /* do transmit packet */ +#define SEND_PKT 0x81 /* send a packet */ +#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */ +#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */ +#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */ +#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */ +#define INTR_OFF 0x0d /* LAN controller ignores interrupts */ +#define INTR_ON 0x1d /* LAN controller will catch interrupts */ + +#define TX_TIMEOUT ((400*HZ)/1000) + +#define BANK_0U 0x20 /* bank 0 (CONFIG_1) */ +#define BANK_1U 0x24 /* bank 1 (CONFIG_1) */ +#define BANK_2U 0x28 /* bank 2 (CONFIG_1) */ + +static const struct net_device_ops fjn_netdev_ops = { + .ndo_open = fjn_open, + .ndo_stop = fjn_close, + .ndo_start_xmit = fjn_start_xmit, + .ndo_tx_timeout = fjn_tx_timeout, + .ndo_set_config = fjn_config, + .ndo_set_rx_mode = set_rx_mode, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int fmvj18x_probe(struct pcmcia_device *link) +{ + struct local_info *lp; + struct net_device *dev; + + dev_dbg(&link->dev, "fmvj18x_attach()\n"); + + /* Make up a FMVJ18x specific data structure */ + dev = alloc_etherdev(sizeof(struct local_info)); + if (!dev) + return -ENOMEM; + lp = netdev_priv(dev); + link->priv = dev; + lp->p_dev = link; + lp->base = NULL; + + /* The io structure describes IO port mapping */ + link->resource[0]->end = 32; + link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + + /* General socket configuration */ + link->config_flags |= CONF_ENABLE_IRQ; + + dev->netdev_ops = &fjn_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->ethtool_ops = &netdev_ethtool_ops; + + return fmvj18x_config(link); +} /* fmvj18x_attach */ + +/*====================================================================*/ + +static void fmvj18x_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + dev_dbg(&link->dev, "fmvj18x_detach\n"); + + unregister_netdev(dev); + + fmvj18x_release(link); + + free_netdev(dev); +} /* fmvj18x_detach */ + +/*====================================================================*/ + +static int mfc_try_io_port(struct pcmcia_device *link) +{ + int i, ret; + static const unsigned int serial_base[5] = + { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + + for (i = 0; i < 5; i++) { + link->resource[1]->start = serial_base[i]; + link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + if (link->resource[1]->start == 0) { + link->resource[1]->end = 0; + pr_notice("out of resource for serial\n"); + } + ret = pcmcia_request_io(link); + if (ret == 0) + return ret; + } + return ret; +} + +static int ungermann_try_io_port(struct pcmcia_device *link) +{ + int ret; + unsigned int ioaddr; + /* + Ungermann-Bass Access/CARD accepts 0x300,0x320,0x340,0x360 + 0x380,0x3c0 only for ioport. + */ + for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) { + link->resource[0]->start = ioaddr; + ret = pcmcia_request_io(link); + if (ret == 0) { + /* calculate ConfigIndex value */ + link->config_index = + ((link->resource[0]->start & 0x0f0) >> 3) | 0x22; + return ret; + } + } + return ret; /* RequestIO failed */ +} + +static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, void *priv_data) +{ + return 0; /* strange, but that's what the code did already before... */ +} + +static int fmvj18x_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct local_info *lp = netdev_priv(dev); + int i, ret; + unsigned int ioaddr; + enum cardtype cardtype; + char *card_name = "unknown"; + u8 *buf; + size_t len; + u_char buggybuf[32]; + u8 addr[ETH_ALEN]; + + dev_dbg(&link->dev, "fmvj18x_config\n"); + + link->io_lines = 5; + + len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf); + kfree(buf); + + if (len) { + /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */ + ret = pcmcia_loop_config(link, fmvj18x_ioprobe, NULL); + if (ret != 0) + goto failed; + + switch (link->manf_id) { + case MANFID_TDK: + cardtype = TDK; + if (link->card_id == PRODID_TDK_GN3410 || + link->card_id == PRODID_TDK_NP9610 || + link->card_id == PRODID_TDK_MN3200) { + /* MultiFunction Card */ + link->config_base = 0x800; + link->config_index = 0x47; + link->resource[1]->end = 8; + } + break; + case MANFID_NEC: + cardtype = NEC; /* MultiFunction Card */ + link->config_base = 0x800; + link->config_index = 0x47; + link->resource[1]->end = 8; + break; + case MANFID_KME: + cardtype = KME; /* MultiFunction Card */ + link->config_base = 0x800; + link->config_index = 0x47; + link->resource[1]->end = 8; + break; + case MANFID_CONTEC: + cardtype = CONTEC; + break; + case MANFID_FUJITSU: + if (link->config_base == 0x0fe0) + cardtype = MBH10302; + else if (link->card_id == PRODID_FUJITSU_MBH10302) + /* RATOC REX-5588/9822/4886's PRODID are 0004(=MBH10302), + but these are MBH10304 based card. */ + cardtype = MBH10304; + else if (link->card_id == PRODID_FUJITSU_MBH10304) + cardtype = MBH10304; + else + cardtype = LA501; + break; + default: + cardtype = MBH10304; + } + } else { + /* old type card */ + switch (link->manf_id) { + case MANFID_FUJITSU: + if (link->card_id == PRODID_FUJITSU_MBH10304) { + cardtype = XXX10304; /* MBH10304 with buggy CIS */ + link->config_index = 0x20; + } else { + cardtype = MBH10302; /* NextCom NC5310, etc. */ + link->config_index = 1; + } + break; + case MANFID_UNGERMANN: + cardtype = UNGERMANN; + break; + default: + cardtype = MBH10302; + link->config_index = 1; + } + } + + if (link->resource[1]->end != 0) { + ret = mfc_try_io_port(link); + if (ret != 0) goto failed; + } else if (cardtype == UNGERMANN) { + ret = ungermann_try_io_port(link); + if (ret != 0) goto failed; + } else { + ret = pcmcia_request_io(link); + if (ret) + goto failed; + } + ret = pcmcia_request_irq(link, fjn_interrupt); + if (ret) + goto failed; + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + + if (resource_size(link->resource[1]) != 0) { + ret = fmvj18x_setup_mfc(link); + if (ret != 0) goto failed; + } + + ioaddr = dev->base_addr; + + /* Reset controller */ + if (sram_config == 0) + outb(CONFIG0_RST, ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1, ioaddr + CONFIG_0); + + /* Power On chip and select bank 0 */ + if (cardtype == MBH10302) + outb(BANK_0, ioaddr + CONFIG_1); + else + outb(BANK_0U, ioaddr + CONFIG_1); + + /* Set hardware address */ + switch (cardtype) { + case MBH10304: + case TDK: + case LA501: + case CONTEC: + case NEC: + case KME: + if (cardtype == MBH10304) { + card_name = "FMV-J182"; + + len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf); + if (len < 11) { + kfree(buf); + goto failed; + } + /* Read MACID from CIS */ + eth_hw_addr_set(dev, &buf[5]); + kfree(buf); + } else { + if (pcmcia_get_mac_from_cis(link, dev)) + goto failed; + if( cardtype == TDK ) { + card_name = "TDK LAK-CD021"; + } else if( cardtype == LA501 ) { + card_name = "LA501"; + } else if( cardtype == NEC ) { + card_name = "PK-UG-J001"; + } else if( cardtype == KME ) { + card_name = "Panasonic"; + } else { + card_name = "C-NET(PC)C"; + } + } + break; + case UNGERMANN: + /* Read MACID from register */ + for (i = 0; i < 6; i++) + addr[i] = inb(ioaddr + UNGERMANN_MAC_ID + i); + eth_hw_addr_set(dev, addr); + card_name = "Access/CARD"; + break; + case XXX10304: + /* Read MACID from Buggy CIS */ + if (fmvj18x_get_hwinfo(link, buggybuf) == -1) { + pr_notice("unable to read hardware net address\n"); + goto failed; + } + eth_hw_addr_set(dev, buggybuf); + card_name = "FMV-J182"; + break; + case MBH10302: + default: + /* Read MACID from register */ + for (i = 0; i < 6; i++) + addr[i] = inb(ioaddr + MAC_ID + i); + eth_hw_addr_set(dev, addr); + card_name = "FMV-J181"; + break; + } + + lp->cardtype = cardtype; + SET_NETDEV_DEV(dev, &link->dev); + + if (register_netdev(dev) != 0) { + pr_notice("register_netdev() failed\n"); + goto failed; + } + + /* print current configuration */ + netdev_info(dev, "%s, sram %s, port %#3lx, irq %d, hw_addr %pM\n", + card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", + dev->base_addr, dev->irq, dev->dev_addr); + + return 0; + +failed: + fmvj18x_release(link); + return -ENODEV; +} /* fmvj18x_config */ +/*====================================================================*/ + +static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) +{ + u_char __iomem *base; + int i, j; + + /* Allocate a small memory window */ + link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[2]->start = 0; link->resource[2]->end = 0; + i = pcmcia_request_window(link, link->resource[2], 0); + if (i != 0) + return -1; + + base = ioremap(link->resource[2]->start, resource_size(link->resource[2])); + if (!base) { + pcmcia_release_window(link, link->resource[2]); + return -1; + } + + pcmcia_map_mem_page(link, link->resource[2], 0); + + /* + * MBH10304 CISTPL_FUNCE_LAN_NODE_ID format + * 22 0d xx xx xx 04 06 yy yy yy yy yy yy ff + * 'xx' is garbage. + * 'yy' is MAC address. + */ + for (i = 0; i < 0x200; i++) { + if (readb(base+i*2) == 0x22) { + if (readb(base+(i-1)*2) == 0xff && + readb(base+(i+5)*2) == 0x04 && + readb(base+(i+6)*2) == 0x06 && + readb(base+(i+13)*2) == 0xff) + break; + } + } + + if (i != 0x200) { + for (j = 0 ; j < 6; j++,i++) { + node_id[j] = readb(base+(i+7)*2); + } + } + + iounmap(base); + j = pcmcia_release_window(link, link->resource[2]); + return (i != 0x200) ? 0 : -1; + +} /* fmvj18x_get_hwinfo */ +/*====================================================================*/ + +static int fmvj18x_setup_mfc(struct pcmcia_device *link) +{ + int i; + struct net_device *dev = link->priv; + unsigned int ioaddr; + struct local_info *lp = netdev_priv(dev); + + /* Allocate a small memory window */ + link->resource[3]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[3]->start = link->resource[3]->end = 0; + i = pcmcia_request_window(link, link->resource[3], 0); + if (i != 0) + return -1; + + lp->base = ioremap(link->resource[3]->start, + resource_size(link->resource[3])); + if (lp->base == NULL) { + netdev_notice(dev, "ioremap failed\n"); + return -1; + } + + i = pcmcia_map_mem_page(link, link->resource[3], 0); + if (i != 0) { + iounmap(lp->base); + lp->base = NULL; + return -1; + } + + ioaddr = dev->base_addr; + writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */ + writeb(0x0, lp->base+0x802); /* Config and Status Register */ + + writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */ + writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */ + + writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */ + writeb(0x8, lp->base+0x822); /* Config and Status Register */ + + return 0; + +} +/*====================================================================*/ + +static void fmvj18x_release(struct pcmcia_device *link) +{ + + struct net_device *dev = link->priv; + struct local_info *lp = netdev_priv(dev); + u_char __iomem *tmp; + + dev_dbg(&link->dev, "fmvj18x_release\n"); + + if (lp->base != NULL) { + tmp = lp->base; + lp->base = NULL; /* set NULL before iounmap */ + iounmap(tmp); + } + + pcmcia_disable_device(link); + +} + +static int fmvj18x_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int fmvj18x_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) { + fjn_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + +/*====================================================================*/ + +static const struct pcmcia_device_id fmvj18x_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004), + PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59), + PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922), + PCMCIA_DEVICE_PROD_ID12("Eiger labs,Inc.", "EPX-10BT PC Card Ethernet 10BT", 0xf47e6c66, 0x877f9922), + PCMCIA_DEVICE_PROD_ID12("FUJITSU", "LAN Card(FMV-J182)", 0x6ee5a3d8, 0x5baf31db), + PCMCIA_DEVICE_PROD_ID12("FUJITSU", "MBH10308", 0x6ee5a3d8, 0x3f04875e), + PCMCIA_DEVICE_PROD_ID12("FUJITSU TOWA", "LA501", 0xb8451188, 0x12939ba2), + PCMCIA_DEVICE_PROD_ID12("HITACHI", "HT-4840-11", 0xf4f43949, 0x773910f4), + PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310B Ver1.0 ", 0x8cef4d3a, 0x075fc7b6), + PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310 Ver1.0 ", 0x8cef4d3a, 0xbccf43e6), + PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "10BASE_T CARD R280", 0x85c10e17, 0xd9413666), + PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CD02x", 0x1eae9475, 0x8fa0ee70), + PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CF010", 0x1eae9475, 0x7683bc9a), + PCMCIA_DEVICE_PROD_ID1("CONTEC Co.,Ltd.", 0x58d8fee2), + PCMCIA_DEVICE_PROD_ID1("PCMCIA LAN MBH10304 ES", 0x2599f454), + PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da), + PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0e01), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0a05), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0b05), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x1101), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids); + +static struct pcmcia_driver fmvj18x_cs_driver = { + .owner = THIS_MODULE, + .name = "fmvj18x_cs", + .probe = fmvj18x_probe, + .remove = fmvj18x_detach, + .id_table = fmvj18x_ids, + .suspend = fmvj18x_suspend, + .resume = fmvj18x_resume, +}; +module_pcmcia_driver(fmvj18x_cs_driver); + +/*====================================================================*/ + +static irqreturn_t fjn_interrupt(int dummy, void *dev_id) +{ + struct net_device *dev = dev_id; + struct local_info *lp = netdev_priv(dev); + unsigned int ioaddr; + unsigned short tx_stat, rx_stat; + + ioaddr = dev->base_addr; + + /* avoid multiple interrupts */ + outw(0x0000, ioaddr + TX_INTR); + + /* wait for a while */ + udelay(1); + + /* get status */ + tx_stat = inb(ioaddr + TX_STATUS); + rx_stat = inb(ioaddr + RX_STATUS); + + /* clear status */ + outb(tx_stat, ioaddr + TX_STATUS); + outb(rx_stat, ioaddr + RX_STATUS); + + pr_debug("%s: interrupt, rx_status %02x.\n", dev->name, rx_stat); + pr_debug(" tx_status %02x.\n", tx_stat); + + if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { + /* there is packet(s) in rx buffer */ + fjn_rx(dev); + } + if (tx_stat & F_TMT_RDY) { + dev->stats.tx_packets += lp->sent ; + lp->sent = 0 ; + if (lp->tx_queue) { + outb(DO_TX | lp->tx_queue, ioaddr + TX_START); + lp->sent = lp->tx_queue ; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + netif_trans_update(dev); + } else { + lp->tx_started = 0; + } + netif_wake_queue(dev); + } + pr_debug("%s: exiting interrupt,\n", dev->name); + pr_debug(" tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat); + + outb(D_TX_INTR, ioaddr + TX_INTR); + outb(D_RX_INTR, ioaddr + RX_INTR); + + if (lp->base != NULL) { + /* Ack interrupt for multifunction card */ + writeb(0x01, lp->base+0x802); + writeb(0x09, lp->base+0x822); + } + + return IRQ_HANDLED; + +} /* fjn_interrupt */ + +/*====================================================================*/ + +static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct local_info *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + + netdev_notice(dev, "transmit timed out with status %04x, %s?\n", + htons(inw(ioaddr + TX_STATUS)), + inb(ioaddr + TX_STATUS) & F_TMT_RDY + ? "IRQ conflict" : "network cable problem"); + netdev_notice(dev, "timeout registers: %04x %04x %04x " + "%04x %04x %04x %04x %04x.\n", + htons(inw(ioaddr + 0)), htons(inw(ioaddr + 2)), + htons(inw(ioaddr + 4)), htons(inw(ioaddr + 6)), + htons(inw(ioaddr + 8)), htons(inw(ioaddr + 10)), + htons(inw(ioaddr + 12)), htons(inw(ioaddr + 14))); + dev->stats.tx_errors++; + /* ToDo: We should try to restart the adaptor... */ + local_irq_disable(); + fjn_reset(dev); + + lp->tx_started = 0; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + lp->sent = 0; + lp->open_time = jiffies; + local_irq_enable(); + netif_wake_queue(dev); +} + +static netdev_tx_t fjn_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct local_info *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + short length = skb->len; + + if (length < ETH_ZLEN) + { + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + length = ETH_ZLEN; + } + + netif_stop_queue(dev); + + { + unsigned char *buf = skb->data; + + if (length > ETH_FRAME_LEN) { + netdev_notice(dev, "Attempting to send a large packet (%d bytes)\n", + length); + return NETDEV_TX_BUSY; + } + + netdev_dbg(dev, "Transmitting a packet of length %lu\n", + (unsigned long)skb->len); + dev->stats.tx_bytes += skb->len; + + /* Disable both interrupts. */ + outw(0x0000, ioaddr + TX_INTR); + + /* wait for a while */ + udelay(1); + + outw(length, ioaddr + DATAPORT); + outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); + + lp->tx_queue++; + lp->tx_queue_len += ((length+3) & ~1); + + if (lp->tx_started == 0) { + /* If the Tx is idle, always trigger a transmit. */ + outb(DO_TX | lp->tx_queue, ioaddr + TX_START); + lp->sent = lp->tx_queue ; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + lp->tx_started = 1; + netif_start_queue(dev); + } else { + if( sram_config == 0 ) { + if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) ) + /* Yes, there is room for one more packet. */ + netif_start_queue(dev); + } else { + if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && + lp->tx_queue < 127 ) + /* Yes, there is room for one more packet. */ + netif_start_queue(dev); + } + } + + /* Re-enable interrupts */ + outb(D_TX_INTR, ioaddr + TX_INTR); + outb(D_RX_INTR, ioaddr + RX_INTR); + } + dev_kfree_skb (skb); + + return NETDEV_TX_OK; +} /* fjn_start_xmit */ + +/*====================================================================*/ + +static void fjn_reset(struct net_device *dev) +{ + struct local_info *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + int i; + + netdev_dbg(dev, "fjn_reset() called\n"); + + /* Reset controller */ + if( sram_config == 0 ) + outb(CONFIG0_RST, ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1, ioaddr + CONFIG_0); + + /* Power On chip and select bank 0 */ + if (lp->cardtype == MBH10302) + outb(BANK_0, ioaddr + CONFIG_1); + else + outb(BANK_0U, ioaddr + CONFIG_1); + + /* Set Tx modes */ + outb(D_TX_MODE, ioaddr + TX_MODE); + /* set Rx modes */ + outb(ID_MATCHED, ioaddr + RX_MODE); + + /* Set hardware address */ + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + NODE_ID + i); + + /* (re)initialize the multicast table */ + set_rx_mode(dev); + + /* Switch to bank 2 (runtime mode) */ + if (lp->cardtype == MBH10302) + outb(BANK_2, ioaddr + CONFIG_1); + else + outb(BANK_2U, ioaddr + CONFIG_1); + + /* set 16col ctrl bits */ + if( lp->cardtype == TDK || lp->cardtype == CONTEC) + outb(TDK_AUTO_MODE, ioaddr + COL_CTRL); + else + outb(AUTO_MODE, ioaddr + COL_CTRL); + + /* clear Reserved Regs */ + outb(0x00, ioaddr + BMPR12); + outb(0x00, ioaddr + BMPR13); + + /* reset Skip packet reg. */ + outb(0x01, ioaddr + RX_SKIP); + + /* Enable Tx and Rx */ + if( sram_config == 0 ) + outb(CONFIG0_DFL, ioaddr + CONFIG_0); + else + outb(CONFIG0_DFL_1, ioaddr + CONFIG_0); + + /* Init receive pointer ? */ + inw(ioaddr + DATAPORT); + inw(ioaddr + DATAPORT); + + /* Clear all status */ + outb(0xff, ioaddr + TX_STATUS); + outb(0xff, ioaddr + RX_STATUS); + + if (lp->cardtype == MBH10302) + outb(INTR_OFF, ioaddr + LAN_CTRL); + + /* Turn on Rx interrupts */ + outb(D_TX_INTR, ioaddr + TX_INTR); + outb(D_RX_INTR, ioaddr + RX_INTR); + + /* Turn on interrupts from LAN card controller */ + if (lp->cardtype == MBH10302) + outb(INTR_ON, ioaddr + LAN_CTRL); +} /* fjn_reset */ + +/*====================================================================*/ + +static void fjn_rx(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + int boguscount = 10; /* 5 -> 10: by agy 19940922 */ + + pr_debug("%s: in rx_packet(), rx_status %02x.\n", + dev->name, inb(ioaddr + RX_STATUS)); + + while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { + u_short status = inw(ioaddr + DATAPORT); + + netdev_dbg(dev, "Rxing packet mode %02x status %04x.\n", + inb(ioaddr + RX_MODE), status); +#ifndef final_version + if (status == 0) { + outb(F_SKP_PKT, ioaddr + RX_SKIP); + break; + } +#endif + if ((status & 0xF0) != 0x20) { /* There was an error. */ + dev->stats.rx_errors++; + if (status & F_LEN_ERR) dev->stats.rx_length_errors++; + if (status & F_ALG_ERR) dev->stats.rx_frame_errors++; + if (status & F_CRC_ERR) dev->stats.rx_crc_errors++; + if (status & F_OVR_FLO) dev->stats.rx_over_errors++; + } else { + u_short pkt_len = inw(ioaddr + DATAPORT); + /* Malloc up new buffer. */ + struct sk_buff *skb; + + if (pkt_len > 1550) { + netdev_notice(dev, "The FMV-18x claimed a very large packet, size %d\n", + pkt_len); + outb(F_SKP_PKT, ioaddr + RX_SKIP); + dev->stats.rx_errors++; + break; + } + skb = netdev_alloc_skb(dev, pkt_len + 2); + if (skb == NULL) { + outb(F_SKP_PKT, ioaddr + RX_SKIP); + dev->stats.rx_dropped++; + break; + } + + skb_reserve(skb, 2); + insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), + (pkt_len + 1) >> 1); + skb->protocol = eth_type_trans(skb, dev); + + { + int i; + pr_debug("%s: Rxed packet of length %d: ", + dev->name, pkt_len); + for (i = 0; i < 14; i++) + pr_debug(" %02x", skb->data[i]); + pr_debug(".\n"); + } + + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; + } + if (--boguscount <= 0) + break; + } + + /* If any worth-while packets have been received, dev_rint() + has done a netif_wake_queue() for us and will work on them + when we get to the bottom-half routine. */ +/* + if (lp->cardtype != TDK) { + int i; + for (i = 0; i < 20; i++) { + if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP) + break; + (void)inw(ioaddr + DATAPORT); /+ dummy status read +/ + outb(F_SKP_PKT, ioaddr + RX_SKIP); + } + + if (i > 0) + pr_debug("%s: Exint Rx packet with mode %02x after " + "%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i); + } +*/ +} /* fjn_rx */ + +/*====================================================================*/ + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + snprintf(info->bus_info, sizeof(info->bus_info), + "PCMCIA 0x%lx", dev->base_addr); +} + +static const struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + +static int fjn_config(struct net_device *dev, struct ifmap *map){ + return 0; +} + +static int fjn_open(struct net_device *dev) +{ + struct local_info *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + + pr_debug("fjn_open('%s').\n", dev->name); + + if (!pcmcia_dev_present(link)) + return -ENODEV; + + link->open++; + + fjn_reset(dev); + + lp->tx_started = 0; + lp->tx_queue = 0; + lp->tx_queue_len = 0; + lp->open_time = jiffies; + netif_start_queue(dev); + + return 0; +} /* fjn_open */ + +/*====================================================================*/ + +static int fjn_close(struct net_device *dev) +{ + struct local_info *lp = netdev_priv(dev); + struct pcmcia_device *link = lp->p_dev; + unsigned int ioaddr = dev->base_addr; + + pr_debug("fjn_close('%s').\n", dev->name); + + lp->open_time = 0; + netif_stop_queue(dev); + + /* Set configuration register 0 to disable Tx and Rx. */ + if( sram_config == 0 ) + outb(CONFIG0_RST ,ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0); + + /* Update the statistics -- ToDo. */ + + /* Power-down the chip. Green, green, green! */ + outb(CHIP_OFF ,ioaddr + CONFIG_1); + + /* Set the ethernet adaptor disable IRQ */ + if (lp->cardtype == MBH10302) + outb(INTR_OFF, ioaddr + LAN_CTRL); + + link->open--; + + return 0; +} /* fjn_close */ + +/*====================================================================*/ + +/* + Set the multicast/promiscuous mode for this adaptor. +*/ + +static void set_rx_mode(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + u_char mc_filter[8]; /* Multicast hash filter */ + u_long flags; + int i; + + int saved_bank; + int saved_config_0 = inb(ioaddr + CONFIG_0); + + local_irq_save(flags); + + /* Disable Tx and Rx */ + if (sram_config == 0) + outb(CONFIG0_RST, ioaddr + CONFIG_0); + else + outb(CONFIG0_RST_1, ioaddr + CONFIG_0); + + if (dev->flags & IFF_PROMISC) { + memset(mc_filter, 0xff, sizeof(mc_filter)); + outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ + } else if (netdev_mc_count(dev) > MC_FILTERBREAK || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + outb(2, ioaddr + RX_MODE); /* Use normal mode. */ + } else if (netdev_mc_empty(dev)) { + memset(mc_filter, 0x00, sizeof(mc_filter)); + outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ + } else { + struct netdev_hw_addr *ha; + + memset(mc_filter, 0, sizeof(mc_filter)); + netdev_for_each_mc_addr(ha, dev) { + unsigned int bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26; + mc_filter[bit >> 3] |= (1 << (bit & 7)); + } + outb(2, ioaddr + RX_MODE); /* Use normal mode. */ + } + + /* Switch to bank 1 and set the multicast table. */ + saved_bank = inb(ioaddr + CONFIG_1); + outb(0xe4, ioaddr + CONFIG_1); + + for (i = 0; i < 8; i++) + outb(mc_filter[i], ioaddr + MAR_ADR + i); + outb(saved_bank, ioaddr + CONFIG_1); + + outb(saved_config_0, ioaddr + CONFIG_0); + + local_irq_restore(flags); +} diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 66bca803b19c..13ce9086a9ca 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -19,6 +19,21 @@ config NET_VENDOR_SMSC if NET_VENDOR_SMSC +config SMC9194 + tristate "SMC 9194 support" + depends on ISA + select CRC32 + select NETDEV_LEGACY_INIT + help + This is support for the SMC9xxx based Ethernet cards. Choose this + option if you have a DELL laptop with the docking station, or + another SMC9192/9194 based chipset. Say Y if you want it compiled + into the kernel, and read the file + . + + To compile this driver as a module, choose M here. The module + will be called smc9194. + config SMC91X tristate "SMC 91C9x/91C1xxx support" select CRC32 @@ -37,6 +52,18 @@ config SMC91X The module will be called smc91x. If you want to compile it as a module, say M here and read . +config PCMCIA_SMC91C92 + tristate "SMC 91Cxx PCMCIA support" + depends on PCMCIA && HAS_IOPORT + select CRC32 + select MII + help + Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA + (PC-card) Ethernet or Fast Ethernet card to your computer. + + To compile this driver as a module, choose M here: the module will be + called smc91c92_cs. If unsure, say N. + config EPIC100 tristate "SMC EtherPower II" depends on PCI diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile index ab6f03f7ba17..1501fa364c13 100644 --- a/drivers/net/ethernet/smsc/Makefile +++ b/drivers/net/ethernet/smsc/Makefile @@ -3,7 +3,9 @@ # Makefile for the SMSC network device drivers. # +obj-$(CONFIG_SMC9194) += smc9194.o obj-$(CONFIG_SMC91X) += smc91x.o +obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o obj-$(CONFIG_EPIC100) += epic100.o obj-$(CONFIG_SMSC9420) += smsc9420.o obj-$(CONFIG_SMSC911X) += smsc911x.o diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c new file mode 100644 index 000000000000..e2e7b1c68563 --- /dev/null +++ b/drivers/net/ethernet/smsc/smc9194.c @@ -0,0 +1,1535 @@ +/*------------------------------------------------------------------------ + . smc9194.c + . This is a driver for SMC's 9000 series of Ethernet cards. + . + . Copyright (C) 1996 by Erik Stahlman + . This software may be used and distributed according to the terms + . of the GNU General Public License, incorporated herein by reference. + . + . "Features" of the SMC chip: + . 4608 byte packet memory. ( for the 91C92. Others have more ) + . EEPROM for configuration + . AUI/TP selection ( mine has 10Base2/10BaseT select ) + . + . Arguments: + . io = for the base address + . irq = for the IRQ + . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 ) + . + . author: + . Erik Stahlman ( erik@vt.edu ) + . contributors: + . Arnaldo Carvalho de Melo + . + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + . o SMC databook + . o skeleton.c by Donald Becker ( becker@scyld.com ) + . o ( a LOT of advice from Becker as well ) + . + . History: + . 12/07/95 Erik Stahlman written, got receive/xmit handled + . 01/03/96 Erik Stahlman worked out some bugs, actually usable!!! :-) + . 01/06/96 Erik Stahlman cleaned up some, better testing, etc + . 01/29/96 Erik Stahlman fixed autoirq, added multicast + . 02/01/96 Erik Stahlman 1. disabled all interrupts in smc_reset + . 2. got rid of post-decrementing bug -- UGH. + . 02/13/96 Erik Stahlman Tried to fix autoirq failure. Added more + . descriptive error messages. + . 02/15/96 Erik Stahlman Fixed typo that caused detection failure + . 02/23/96 Erik Stahlman Modified it to fit into kernel tree + . Added support to change hardware address + . Cleared stats on opens + . 02/26/96 Erik Stahlman Trial support for Kernel 1.2.13 + . Kludge for automatic IRQ detection + . 03/04/96 Erik Stahlman Fixed kernel 1.3.70 + + . Fixed bug reported by Gardner Buchanan in + . smc_enable, with outw instead of outb + . 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert + . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory + . allocation + . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet + . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" + . 11/08/01 Matt Domsch Use common crc32 function + ----------------------------------------------------------------------------*/ + +static const char version[] = + "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "smc9194.h" + +#define DRV_NAME "smc9194" + +/*------------------------------------------------------------------------ + . + . Configuration options, for the experienced user to change. + . + -------------------------------------------------------------------------*/ + +/* + . Do you want to use 32 bit xfers? This should work on all chips, as + . the chipset is designed to accommodate them. +*/ +#ifdef __sh__ +#undef USE_32_BIT +#else +#define USE_32_BIT 1 +#endif + +/* + .the SMC9194 can be at any of the following port addresses. To change, + .for a slightly different card, you can add it to the array. Keep in + .mind that the array must end in zero. +*/ + +struct devlist { + unsigned int port; + unsigned int irq; +}; + +static struct devlist smc_devlist[] __initdata = { + {.port = 0x200, .irq = 0}, + {.port = 0x220, .irq = 0}, + {.port = 0x240, .irq = 0}, + {.port = 0x260, .irq = 0}, + {.port = 0x280, .irq = 0}, + {.port = 0x2A0, .irq = 0}, + {.port = 0x2C0, .irq = 0}, + {.port = 0x2E0, .irq = 0}, + {.port = 0x300, .irq = 0}, + {.port = 0x320, .irq = 0}, + {.port = 0x340, .irq = 0}, + {.port = 0x360, .irq = 0}, + {.port = 0x380, .irq = 0}, + {.port = 0x3A0, .irq = 0}, + {.port = 0x3C0, .irq = 0}, + {.port = 0x3E0, .irq = 0}, + {.port = 0, .irq = 0}, +}; +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + +/* + . DEBUGGING LEVELS + . + . 0 for normal operation + . 1 for slightly more details + . >2 for various levels of increasingly useless information + . 2 for interrupt tracking, status flags + . 3 for packet dumps, etc. +*/ +#define SMC_DEBUG 0 + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(x) printk x +#else +#define PRINTK3(x) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(x) printk x +#else +#define PRINTK2(x) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(x) printk x +#else +#define PRINTK(x) +#endif + + +/*------------------------------------------------------------------------ + . + . The internal workings of the driver. If you are changing anything + . here with the SMC stuff, you should have the datasheet and known + . what you are doing. + . + -------------------------------------------------------------------------*/ +#define CARDNAME "SMC9194" + + +/* store this information for the driver.. */ +struct smc_local { + /* + If I have to wait until memory is available to send + a packet, I will store the skbuff here, until I get the + desired memory. Then, I'll send it out and free it. + */ + struct sk_buff * saved_skb; + + /* + . This keeps track of how many packets that I have + . sent out. When an TX_EMPTY interrupt comes, I know + . that all of these have been sent. + */ + int packets_waiting; +}; + + +/*----------------------------------------------------------------- + . + . The driver can be entered at any of the following entry points. + . + .------------------------------------------------------------------ */ + +/* + . This is called by register_netdev(). It is responsible for + . checking the portlist for the SMC9000 series chipset. If it finds + . one, then it will initialize the device, find the hardware information, + . and sets up the appropriate device parameters. + . NOTE: Interrupts are *OFF* when this procedure is called. + . + . NB:This shouldn't be static since it is referred to externally. +*/ +struct net_device *smc_init(int unit); + +/* + . The kernel calls this function when someone wants to use the device, + . typically 'ifconfig ethX up'. +*/ +static int smc_open(struct net_device *dev); + +/* + . Our watchdog timed out. Called by the networking layer +*/ +static void smc_timeout(struct net_device *dev, unsigned int txqueue); + +/* + . This is called by the kernel in response to 'ifconfig ethX down'. It + . is responsible for cleaning up everything that the open routine + . does, and maybe putting the card into a powerdown state. +*/ +static int smc_close(struct net_device *dev); + +/* + . Finally, a call to set promiscuous mode ( for TCPDUMP and related + . programs ) and multicast modes. +*/ +static void smc_set_multicast_list(struct net_device *dev); + + +/*--------------------------------------------------------------- + . + . Interrupt level calls.. + . + ----------------------------------------------------------------*/ + +/* + . Handles the actual interrupt +*/ +static irqreturn_t smc_interrupt(int irq, void *); +/* + . This is a separate procedure to handle the receipt of a packet, to + . leave the interrupt code looking slightly cleaner +*/ +static inline void smc_rcv( struct net_device *dev ); +/* + . This handles a TX interrupt, which is only called when an error + . relating to a packet is sent. +*/ +static inline void smc_tx( struct net_device * dev ); + +/* + ------------------------------------------------------------ + . + . Internal routines + . + ------------------------------------------------------------ +*/ + +/* + . Test if a given location contains a chip, trying to cause as + . little damage as possible if it's not a SMC chip. +*/ +static int smc_probe(struct net_device *dev, int ioaddr); + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet( byte *, int ); +#endif + +#define tx_done(dev) 1 + +/* this is called to actually send the packet to the chip */ +static void smc_hardware_send_packet( struct net_device * dev ); + +/* Since I am not sure if I will have enough room in the chip's ram + . to store the packet, I call this routine, which either sends it + . now, or generates an interrupt when the card is ready for the + . packet */ +static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb, + struct net_device *dev ); + +/* this does a soft reset on the device */ +static void smc_reset( int ioaddr ); + +/* Enable Interrupts, Receive, and Transmit */ +static void smc_enable( int ioaddr ); + +/* this puts the device in an inactive state */ +static void smc_shutdown( int ioaddr ); + +/* This routine will find the IRQ of the driver if one is not + . specified in the input to the device. */ +static int smc_findirq( int ioaddr ); + +/* + . Function: smc_reset( int ioaddr ) + . Purpose: + . This sets the SMC91xx chip to its normal state, hopefully from whatever + . mess that any other DOS driver has put it in. + . + . Maybe I should reset more registers to defaults in here? SOFTRESET should + . do that for me. + . + . Method: + . 1. send a SOFT RESET + . 2. wait for it to finish + . 3. enable autorelease mode + . 4. reset the memory management unit + . 5. clear all interrupts + . +*/ +static void smc_reset( int ioaddr ) +{ + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK( 0 ); + outw( RCR_SOFTRESET, ioaddr + RCR ); + + /* this should pause enough for the chip to be happy */ + SMC_DELAY( ); + + /* Set the transmit and receive configuration registers to + default values */ + outw( RCR_CLEAR, ioaddr + RCR ); + outw( TCR_CLEAR, ioaddr + TCR ); + + /* set the control register to automatically + release successfully transmitted packets, to make the best + use out of our limited memory */ + SMC_SELECT_BANK( 1 ); + outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL ); + + /* Reset the MMU */ + SMC_SELECT_BANK( 2 ); + outw( MC_RESET, ioaddr + MMU_CMD ); + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ + + outb( 0, ioaddr + INT_MASK ); +} + +/* + . Function: smc_enable + . Purpose: let the chip talk to the outside work + . Method: + . 1. Enable the transmitter + . 2. Enable the receiver + . 3. Enable interrupts +*/ +static void smc_enable( int ioaddr ) +{ + SMC_SELECT_BANK( 0 ); + /* see the header file for options in TCR/RCR NORMAL*/ + outw( TCR_NORMAL, ioaddr + TCR ); + outw( RCR_NORMAL, ioaddr + RCR ); + + /* now, enable interrupts */ + SMC_SELECT_BANK( 2 ); + outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); +} + +/* + . Function: smc_shutdown + . Purpose: closes down the SMC91xxx chip. + . Method: + . 1. zero the interrupt mask + . 2. clear the enable receive flag + . 3. clear the enable xmit flags + . + . TODO: + . (1) maybe utilize power down mode. + . Why not yet? Because while the chip will go into power down mode, + . the manual says that it will wake up in response to any I/O requests + . in the register space. Empirical results do not show this working. +*/ +static void smc_shutdown( int ioaddr ) +{ + /* no more interrupts for me */ + SMC_SELECT_BANK( 2 ); + outb( 0, ioaddr + INT_MASK ); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK( 0 ); + outb( RCR_CLEAR, ioaddr + RCR ); + outb( TCR_CLEAR, ioaddr + TCR ); +#if 0 + /* finally, shut the chip down */ + SMC_SELECT_BANK( 1 ); + outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL ); +#endif +} + + +/* + . Function: smc_setmulticast( int ioaddr, struct net_device *dev ) + . Purpose: + . This sets the internal hardware table to filter out unwanted multicast + . packets before they take up memory. + . + . The SMC chip uses a hash table where the high 6 bits of the CRC of + . address are the offset into the table. If that bit is 1, then the + . multicast packet is accepted. Otherwise, it's dropped silently. + . + . To use the 6 bits as an offset into the table, the high 3 bits are the + . number of the 8 bit register, while the low 3 bits are the bit within + . that register. + . + . This routine is based very heavily on the one provided by Peter Cammaert. +*/ + + +static void smc_setmulticast(int ioaddr, struct net_device *dev) +{ + int i; + unsigned char multicast_table[ 8 ]; + struct netdev_hw_addr *ha; + /* table for flipping the order of 3 bits */ + unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + /* start with a table of all zeros: reject all */ + memset( multicast_table, 0, sizeof( multicast_table ) ); + + netdev_for_each_mc_addr(ha, dev) { + int position; + + /* only use the low order bits */ + position = ether_crc_le(6, ha->addr) & 0x3f; + + /* do some messy swapping to put the bit in the right spot */ + multicast_table[invert3[position&7]] |= + (1<>3)&7]); + + } + /* now, the table can be loaded into the chipset */ + SMC_SELECT_BANK( 3 ); + + for ( i = 0; i < 8 ; i++ ) { + outb( multicast_table[i], ioaddr + MULTICAST1 + i ); + } +} + +/* + . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) + . Purpose: + . Attempt to allocate memory for a packet, if chip-memory is not + . available, then tell the card to generate an interrupt when it + . is available. + . + . Algorithm: + . + . o if the saved_skb is not currently null, then drop this packet + . on the floor. This should never happen, because of TBUSY. + . o if the saved_skb is null, then replace it with the current packet, + . o See if I can sending it now. + . o (NO): Enable interrupts and let the interrupt handler deal with it. + . o (YES):Send it now. +*/ +static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct smc_local *lp = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + word length; + unsigned short numPages; + word time_out; + + netif_stop_queue(dev); + /* Well, I want to send the packet.. but I don't know + if I can send it right now... */ + + if ( lp->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + dev->stats.tx_aborted_errors++; + printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); + return NETDEV_TX_BUSY; + } + lp->saved_skb = skb; + + length = skb->len; + + if (length < ETH_ZLEN) { + if (skb_padto(skb, ETH_ZLEN)) { + netif_wake_queue(dev); + return NETDEV_TX_OK; + } + length = ETH_ZLEN; + } + + /* + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** Pkt size for allocating is data length +6 (for additional status words, + ** length and ctl!) If odd size last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6) / 256; + + if (numPages > 7 ) { + printk(CARDNAME": Far too big packet error.\n"); + /* freeing the packet is a good thing here... but should + . any packets of this size get down here? */ + dev_kfree_skb (skb); + lp->saved_skb = NULL; + /* this IS an error, but, i don't want the skb saved */ + netif_wake_queue(dev); + return NETDEV_TX_OK; + } + /* either way, a packet is waiting now */ + lp->packets_waiting++; + + /* now, try to allocate the memory */ + SMC_SELECT_BANK( 2 ); + outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); + /* + . Performance Hack + . + . wait a short amount of time.. if I can send a packet now, I send + . it now. Otherwise, I enable an interrupt and wait for one to be + . available. + . + . I could have handled this a slightly different way, by checking to + . see if any memory was available in the FREE MEMORY register. However, + . either way, I need to generate an allocation, and the allocation works + . no matter what, so I saw no point in checking free memory. + */ + time_out = MEMORY_WAIT_TIME; + do { + word status; + + status = inb( ioaddr + INTERRUPT ); + if ( status & IM_ALLOC_INT ) { + /* acknowledge the interrupt */ + outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); + break; + } + } while ( -- time_out ); + + if ( !time_out ) { + /* oh well, wait until the chip finds memory later */ + SMC_ENABLE_INT( IM_ALLOC_INT ); + PRINTK2((CARDNAME": memory allocation deferred.\n")); + /* it's deferred, but I'll handle it later */ + return NETDEV_TX_OK; + } + /* or YES! I can send the packet now.. */ + smc_hardware_send_packet(dev); + netif_wake_queue(dev); + return NETDEV_TX_OK; +} + +/* + . Function: smc_hardware_send_packet(struct net_device * ) + . Purpose: + . This sends the actual packet to the SMC9xxx chip. + . + . Algorithm: + . First, see if a saved_skb is available. + . ( this should NOT be called if there is no 'saved_skb' + . Now, find the packet number that the chip allocated + . Point the data pointers at it in memory + . Set the length word in the chip's memory + . Dump the packet to chip memory + . Check if a last byte is needed ( odd length packet ) + . if so, set the control flag right + . Tell the card to send it + . Enable the transmit interrupt, so I know if it failed + . Free the kernel data if I actually sent it. +*/ +static void smc_hardware_send_packet( struct net_device * dev ) +{ + struct smc_local *lp = netdev_priv(dev); + byte packet_no; + struct sk_buff * skb = lp->saved_skb; + word length; + unsigned int ioaddr; + byte * buf; + + ioaddr = dev->base_addr; + + if ( !skb ) { + PRINTK((CARDNAME": In XMIT with no packet to send\n")); + return; + } + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buf = skb->data; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = inb( ioaddr + PNR_ARR + 1 ); + if ( packet_no & 0x80 ) { + /* or isn't there? BAD CHIP! */ + netdev_dbg(dev, CARDNAME": Memory allocation failed.\n"); + dev_kfree_skb_any(skb); + lp->saved_skb = NULL; + netif_wake_queue(dev); + return; + } + + /* we have a packet address, so tell the card to use it */ + outb( packet_no, ioaddr + PNR_ARR ); + + /* point to the beginning of the packet */ + outw( PTR_AUTOINC , ioaddr + POINTER ); + + PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length)); +#if SMC_DEBUG > 2 + print_packet( buf, length ); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ +#ifdef USE_32_BIT + outl( (length +6 ) << 16 , ioaddr + DATA_1 ); +#else + outw( 0, ioaddr + DATA_1 ); + /* send the packet length ( +6 for status words, length, and ctl*/ + outb( (length+6) & 0xFF,ioaddr + DATA_1 ); + outb( (length+6) >> 8 , ioaddr + DATA_1 ); +#endif + + /* send the actual data + . I _think_ it's faster to send the longs first, and then + . mop up by sending the last word. It depends heavily + . on alignment, at least on the 486. Maybe it would be + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ +#ifdef USE_32_BIT + if ( length & 0x2 ) { + outsl(ioaddr + DATA_1, buf, length >> 2 ); + outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); + } + else + outsl(ioaddr + DATA_1, buf, length >> 2 ); +#else + outsw(ioaddr + DATA_1 , buf, (length ) >> 1); +#endif + /* Send the last byte, if there is one. */ + + if ( (length & 1) == 0 ) { + outw( 0, ioaddr + DATA_1 ); + } else { + outb( buf[length -1 ], ioaddr + DATA_1 ); + outb( 0x20, ioaddr + DATA_1); + } + + /* enable the interrupts */ + SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); + + /* and let the chipset deal with it */ + outw( MC_ENQUEUE , ioaddr + MMU_CMD ); + + PRINTK2((CARDNAME": Sent packet of length %d\n", length)); + + lp->saved_skb = NULL; + dev_kfree_skb_any (skb); + + netif_trans_update(dev); + + /* we can send another packet */ + netif_wake_queue(dev); +} + +/*------------------------------------------------------------------------- + | + | smc_init(int unit) + | Input parameters: + | dev->base_addr == 0, try to find all possible locations + | dev->base_addr == 1, return failure code + | dev->base_addr == 2, always allocate space, and return success + | dev->base_addr == this is the address to check + | + | Output: + | pointer to net_device or ERR_PTR(error) + | + --------------------------------------------------------------------------- +*/ +static int io; +static int irq; +static int ifport; + +struct net_device * __init smc_init(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct smc_local)); + struct devlist *smcdev = smc_devlist; + int err = 0; + + if (!dev) + return ERR_PTR(-ENODEV); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + io = dev->base_addr; + irq = dev->irq; + } + + if (io > 0x1ff) { /* Check a single specified location. */ + err = smc_probe(dev, io); + } else if (io != 0) { /* Don't probe at all. */ + err = -ENXIO; + } else { + for (;smcdev->port; smcdev++) { + if (smc_probe(dev, smcdev->port) == 0) + break; + } + if (!smcdev->port) + err = -ENODEV; + } + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + free_irq(dev->irq, dev); + release_region(dev->base_addr, SMC_IO_EXTENT); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +/*---------------------------------------------------------------------- + . smc_findirq + . + . This routine has a simple purpose -- make the SMC chip generate an + . interrupt, so an auto-detect routine can detect it, and find the IRQ, + ------------------------------------------------------------------------ +*/ +static int __init smc_findirq(int ioaddr) +{ +#ifndef NO_AUTOPROBE + int timeout = 20; + unsigned long cookie; + + + cookie = probe_irq_on(); + + /* + * What I try to do here is trigger an ALLOC_INT. This is done + * by allocating a small chunk of memory, which will give an interrupt + * when done. + */ + + + SMC_SELECT_BANK(2); + /* enable ALLOCation interrupts ONLY */ + outb( IM_ALLOC_INT, ioaddr + INT_MASK ); + + /* + . Allocate 512 bytes of memory. Note that the chip was just + . reset so all the memory is available + */ + outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); + + /* + . Wait until positive that the interrupt has been generated + */ + while ( timeout ) { + byte int_status; + + int_status = inb( ioaddr + INTERRUPT ); + + if ( int_status & IM_ALLOC_INT ) + break; /* got the interrupt */ + timeout--; + } + /* there is really nothing that I can do here if timeout fails, + as probe_irq_off will return a 0 anyway, which is what I + want in this case. Plus, the clean up is needed in both + cases. */ + + /* DELAY HERE! + On a fast machine, the status might change before the interrupt + is given to the processor. This means that the interrupt was + never detected, and probe_irq_off fails to report anything. + This should fix probe_irq_* problems. + */ + SMC_DELAY(); + SMC_DELAY(); + + /* and disable all interrupts again */ + outb( 0, ioaddr + INT_MASK ); + + /* and return what I found */ + return probe_irq_off(cookie); +#else /* NO_AUTOPROBE */ + struct devlist *smcdev; + for (smcdev = smc_devlist; smcdev->port; smcdev++) { + if (smcdev->port == ioaddr) + return smcdev->irq; + } + return 0; +#endif +} + +static const struct net_device_ops smc_netdev_ops = { + .ndo_open = smc_open, + .ndo_stop = smc_close, + .ndo_start_xmit = smc_wait_to_send_packet, + .ndo_tx_timeout = smc_timeout, + .ndo_set_rx_mode = smc_set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +/*---------------------------------------------------------------------- + . Function: smc_probe( int ioaddr ) + . + . Purpose: + . Tests to see if a given ioaddr points to an SMC9xxx chip. + . Returns a 0 on success + . + . Algorithm: + . (1) see if the high byte of BANK_SELECT is 0x33 + . (2) compare the ioaddr with the base register's address + . (3) see if I recognize the chip ID in the appropriate register + . + .--------------------------------------------------------------------- + */ + +/*--------------------------------------------------------------- + . Here I do typical initialization tasks. + . + . o Initialize the structure if needed + . o print out my vanity message if not done so already + . o print out what type of hardware is detected + . o print out the ethernet address + . o find the IRQ + . o set up my private data + . o configure the dev structure with my subroutines + . o actually GRAB the irq. + . o GRAB the region + .----------------------------------------------------------------- +*/ +static int __init smc_probe(struct net_device *dev, int ioaddr) +{ + int i, memory, retval; + unsigned int bank; + + const char *version_string; + const char *if_string; + + /* registers */ + word revision_register; + word base_address_register; + word configuration_register; + word memory_info_register; + word memory_cfg_register; + u8 addr[ETH_ALEN]; + + /* Grab the region so that no one else tries to probe our ioports. */ + if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME)) + return -EBUSY; + + dev->irq = irq; + dev->if_port = ifport; + + /* First, see if the high byte is 0x33 */ + bank = inw( ioaddr + BANK_SELECT ); + if ( (bank & 0xFF00) != 0x3300 ) { + retval = -ENODEV; + goto err_out; + } + /* The above MIGHT indicate a device, but I need to write to further + test this. */ + outw( 0x0, ioaddr + BANK_SELECT ); + bank = inw( ioaddr + BANK_SELECT ); + if ( (bank & 0xFF00 ) != 0x3300 ) { + retval = -ENODEV; + goto err_out; + } + /* well, we've already written once, so hopefully another time won't + hurt. This time, I need to switch the bank register to bank 1, + so I can access the base address register */ + SMC_SELECT_BANK(1); + base_address_register = inw( ioaddr + BASE ); + if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) { + printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). " + "Probably not a SMC chip\n", + ioaddr, base_address_register >> 3 & 0x3E0 ); + /* well, the base address register didn't match. Must not have + been a SMC chip after all. */ + retval = -ENODEV; + goto err_out; + } + + /* check if the revision register is something that I recognize. + These might need to be added to later, as future revisions + could be added. */ + SMC_SELECT_BANK(3); + revision_register = inw( ioaddr + REVISION ); + if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { + /* I don't recognize this chip, so... */ + printk(CARDNAME ": IO %x: Unrecognized revision register:" + " %x, Contact author.\n", ioaddr, revision_register); + + retval = -ENODEV; + goto err_out; + } + + /* at this point I'll assume that the chip is an SMC9xxx. + It might be prudent to check a listing of MAC addresses + against the hardware address, or do some other tests. */ + + pr_info_once("%s\n", version); + + /* fill in some of the fields */ + dev->base_addr = ioaddr; + + /* + . Get the MAC address ( bank 1, regs 4 - 9 ) + */ + SMC_SELECT_BANK( 1 ); + for ( i = 0; i < 6; i += 2 ) { + word address; + + address = inw( ioaddr + ADDR0 + i ); + addr[i + 1] = address >> 8; + addr[i] = address & 0xFF; + } + eth_hw_addr_set(dev, addr); + + /* get the memory information */ + + SMC_SELECT_BANK( 0 ); + memory_info_register = inw( ioaddr + MIR ); + memory_cfg_register = inw( ioaddr + MCR ); + memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */ + memory *= 256 * ( memory_info_register & 0xFF ); + + /* + Now, I want to find out more about the chip. This is sort of + redundant, but it's cleaner to have it in both, rather than having + one VERY long probe procedure. + */ + SMC_SELECT_BANK(3); + revision_register = inw( ioaddr + REVISION ); + version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; + if ( !version_string ) { + /* I shouldn't get here because this call was done before.... */ + retval = -ENODEV; + goto err_out; + } + + /* is it using AUI or 10BaseT ? */ + if ( dev->if_port == 0 ) { + SMC_SELECT_BANK(1); + configuration_register = inw( ioaddr + CONFIG ); + if ( configuration_register & CFG_AUI_SELECT ) + dev->if_port = 2; + else + dev->if_port = 1; + } + if_string = interfaces[ dev->if_port - 1 ]; + + /* now, reset the chip, and put it into a known state */ + smc_reset( ioaddr ); + + /* + . If dev->irq is 0, then the device has to be banged on to see + . what the IRQ is. + . + . This banging doesn't always detect the IRQ, for unknown reasons. + . a workaround is to reset the chip and try again. + . + . Interestingly, the DOS packet driver *SETS* the IRQ on the card to + . be what is requested on the command line. I don't do that, mostly + . because the card that I have uses a non-standard method of accessing + . the IRQs, and because this _should_ work in most configurations. + . + . Specifying an IRQ is done with the assumption that the user knows + . what (s)he is doing. No checking is done!!!! + . + */ + if ( dev->irq < 2 ) { + int trials; + + trials = 3; + while ( trials-- ) { + dev->irq = smc_findirq( ioaddr ); + if ( dev->irq ) + break; + /* kick the card and try again */ + smc_reset( ioaddr ); + } + } + if (dev->irq == 0 ) { + printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); + retval = -ENODEV; + goto err_out; + } + + /* now, print out the card info, in a short format.. */ + + netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", + version_string, revision_register & 0xF, ioaddr, dev->irq, + if_string, memory); + /* + . Print the Ethernet address + */ + netdev_info(dev, "ADDR: %pM\n", dev->dev_addr); + + /* Grab the IRQ */ + retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev); + if (retval) { + netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n", + DRV_NAME, dev->irq, retval); + goto err_out; + } + + dev->netdev_ops = &smc_netdev_ops; + dev->watchdog_timeo = HZ/20; + + return 0; + +err_out: + release_region(ioaddr, SMC_IO_EXTENT); + return retval; +} + +#if SMC_DEBUG > 2 +static void print_packet( byte * buf, int length ) +{ +#if 0 + print_hex_dump_debug(DRV_NAME, DUMP_PREFIX_OFFSET, 16, 1, + buf, length, true); +#endif +} +#endif + + +/* + * Open and Initialize the board + * + * Set up everything, reset the card, etc .. + * + */ +static int smc_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + + int i; /* used to set hw ethernet address */ + + /* clear out all the junk that was put here before... */ + memset(netdev_priv(dev), 0, sizeof(struct smc_local)); + + /* reset the hardware */ + + smc_reset( ioaddr ); + smc_enable( ioaddr ); + + /* Select which interface to use */ + + SMC_SELECT_BANK( 1 ); + if ( dev->if_port == 1 ) { + outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, + ioaddr + CONFIG ); + } + else if ( dev->if_port == 2 ) { + outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, + ioaddr + CONFIG ); + } + + /* + According to Becker, I have to set the hardware address + at this point, because the (l)user can set it with an + ioctl. Easily done... + */ + SMC_SELECT_BANK( 1 ); + for ( i = 0; i < 6; i += 2 ) { + word address; + + address = dev->dev_addr[ i + 1 ] << 8 ; + address |= dev->dev_addr[ i ]; + outw( address, ioaddr + ADDR0 + i ); + } + + netif_start_queue(dev); + return 0; +} + +/*-------------------------------------------------------- + . Called by the kernel to send a packet out into the void + . of the net. This routine is largely based on + . skeleton.c, from Becker. + .-------------------------------------------------------- +*/ + +static void smc_timeout(struct net_device *dev, unsigned int txqueue) +{ + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + netdev_warn(dev, CARDNAME": transmit timed out, %s?\n", + tx_done(dev) ? "IRQ conflict" : "network cable problem"); + /* "kick" the adaptor */ + smc_reset( dev->base_addr ); + smc_enable( dev->base_addr ); + netif_trans_update(dev); /* prevent tx timeout */ + /* clear anything saved */ + ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL; + netif_wake_queue(dev); +} + +/*------------------------------------------------------------- + . + . smc_rcv - receive a packet from the card + . + . There is ( at least ) a packet waiting to be read from + . chip-memory. + . + . o Read the status + . o If an error, record it + . o otherwise, read in the packet + -------------------------------------------------------------- +*/ +static void smc_rcv(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + int packet_number; + word status; + word packet_length; + + /* assume bank 2 */ + + packet_number = inw( ioaddr + FIFO_PORTS ); + + if ( packet_number & FP_RXEMPTY ) { + /* we got called , but nothing was on the FIFO */ + PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n")); + /* don't need to restore anything */ + return; + } + + /* start reading from the start of the packet */ + outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); + + /* First two words are status and packet_length */ + status = inw( ioaddr + DATA_1 ); + packet_length = inw( ioaddr + DATA_1 ); + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); + /* + . the packet length contains 3 extra words : + . status, length, and an extra word with an odd byte . + */ + packet_length -= 6; + + if ( !(status & RS_ERRORS ) ){ + /* do stuff to make a new packet */ + struct sk_buff * skb; + byte * data; + + /* read one extra byte */ + if ( status & RS_ODDFRAME ) + packet_length++; + + /* set multicast stats */ + if ( status & RS_MULTICAST ) + dev->stats.multicast++; + + skb = netdev_alloc_skb(dev, packet_length + 5); + if ( skb == NULL ) { + dev->stats.rx_dropped++; + goto done; + } + + /* + ! This should work without alignment, but it could be + ! in the worse case + */ + + skb_reserve( skb, 2 ); /* 16 bit alignment */ + + data = skb_put( skb, packet_length); + +#ifdef USE_32_BIT + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + PRINTK3((" Reading %d dwords (and %d bytes)\n", + packet_length >> 2, packet_length & 3 )); + insl(ioaddr + DATA_1 , data, packet_length >> 2 ); + /* read the left over bytes */ + insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC), + packet_length & 0x3 ); +#else + PRINTK3((" Reading %d words and %d byte(s)\n", + (packet_length >> 1 ), packet_length & 1 )); + insw(ioaddr + DATA_1 , data, packet_length >> 1); + if ( packet_length & 1 ) { + data += packet_length & ~1; + *(data++) = inb( ioaddr + DATA_1 ); + } +#endif +#if SMC_DEBUG > 2 + print_packet( data, packet_length ); +#endif + + skb->protocol = eth_type_trans(skb, dev ); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += packet_length; + } else { + /* error ... */ + dev->stats.rx_errors++; + + if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++; + if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) + dev->stats.rx_length_errors++; + if ( status & RS_BADCRC) dev->stats.rx_crc_errors++; + } + +done: + /* error or good, tell the card to get rid of this packet */ + outw( MC_RELEASE, ioaddr + MMU_CMD ); +} + + +/************************************************************************* + . smc_tx + . + . Purpose: Handle a transmit error message. This will only be called + . when an error, because of the AUTO_RELEASE mode. + . + . Algorithm: + . Save pointer and packet no + . Get the packet no from the top of the queue + . check if it's valid ( if not, is this an error??? ) + . read the status word + . record the error + . ( resend? Not really, since we don't want old packets around ) + . Restore saved values + ************************************************************************/ +static void smc_tx( struct net_device * dev ) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = netdev_priv(dev); + byte saved_packet; + byte packet_no; + word tx_status; + + + /* assume bank 2 */ + + saved_packet = inb( ioaddr + PNR_ARR ); + packet_no = inw( ioaddr + FIFO_PORTS ); + packet_no &= 0x7F; + + /* select this as the packet to read from */ + outb( packet_no, ioaddr + PNR_ARR ); + + /* read the first word from this packet */ + outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); + + tx_status = inw( ioaddr + DATA_1 ); + PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status)); + + dev->stats.tx_errors++; + if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++; + if ( tx_status & TS_LATCOL ) { + netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n"); + dev->stats.tx_window_errors++; + } +#if 0 + if ( tx_status & TS_16COL ) { ... } +#endif + + if ( tx_status & TS_SUCCESS ) { + netdev_info(dev, CARDNAME": Successful packet caused interrupt\n"); + } + /* re-enable transmit */ + SMC_SELECT_BANK( 0 ); + outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); + + /* kill the packet */ + SMC_SELECT_BANK( 2 ); + outw( MC_FREEPKT, ioaddr + MMU_CMD ); + + /* one less packet waiting for me */ + lp->packets_waiting--; + + outb( saved_packet, ioaddr + PNR_ARR ); +} + +/*-------------------------------------------------------------------- + . + . This is the main routine of the driver, to handle the device when + . it needs some attention. + . + . So: + . first, save state of the chipset + . branch off into routines to handle each case, and acknowledge + . each to the interrupt register + . and finally restore state. + . + ---------------------------------------------------------------------*/ + +static irqreturn_t smc_interrupt(int irq, void * dev_id) +{ + struct net_device *dev = dev_id; + int ioaddr = dev->base_addr; + struct smc_local *lp = netdev_priv(dev); + + byte status; + word card_stats; + byte mask; + int timeout; + /* state registers */ + word saved_bank; + word saved_pointer; + int handled = 0; + + + PRINTK3((CARDNAME": SMC interrupt started\n")); + + saved_bank = inw( ioaddr + BANK_SELECT ); + + SMC_SELECT_BANK(2); + saved_pointer = inw( ioaddr + POINTER ); + + mask = inb( ioaddr + INT_MASK ); + /* clear all interrupts */ + outb( 0, ioaddr + INT_MASK ); + + + /* set a timeout value, so I don't stay here forever */ + timeout = 4; + + PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask)); + do { + /* read the status flag, and mask it */ + status = inb( ioaddr + INTERRUPT ) & mask; + if (!status ) + break; + + handled = 1; + + PRINTK3((KERN_WARNING CARDNAME + ": Handling interrupt status %x\n", status)); + + if (status & IM_RCV_INT) { + /* Got a packet(s). */ + PRINTK2((KERN_WARNING CARDNAME + ": Receive Interrupt\n")); + smc_rcv(dev); + } else if (status & IM_TX_INT ) { + PRINTK2((KERN_WARNING CARDNAME + ": TX ERROR handled\n")); + smc_tx(dev); + outb(IM_TX_INT, ioaddr + INTERRUPT ); + } else if (status & IM_TX_EMPTY_INT ) { + /* update stats */ + SMC_SELECT_BANK( 0 ); + card_stats = inw( ioaddr + COUNTER ); + /* single collisions */ + dev->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + dev->stats.collisions += card_stats & 0xF; + + /* these are for when linux supports these statistics */ + + SMC_SELECT_BANK( 2 ); + PRINTK2((KERN_WARNING CARDNAME + ": TX_BUFFER_EMPTY handled\n")); + outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); + mask &= ~IM_TX_EMPTY_INT; + dev->stats.tx_packets += lp->packets_waiting; + lp->packets_waiting = 0; + + } else if (status & IM_ALLOC_INT ) { + PRINTK2((KERN_DEBUG CARDNAME + ": Allocation interrupt\n")); + /* clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + + smc_hardware_send_packet( dev ); + + /* enable xmit interrupts based on this */ + mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + + /* and let the card send more packets to me */ + netif_wake_queue(dev); + + PRINTK2((CARDNAME": Handoff done successfully.\n")); + } else if (status & IM_RX_OVRN_INT ) { + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; + outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); + } else if (status & IM_EPH_INT ) { + PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n")); + } else if (status & IM_ERCV_INT ) { + PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n")); + outb( IM_ERCV_INT, ioaddr + INTERRUPT ); + } + } while ( timeout -- ); + + + /* restore state register */ + SMC_SELECT_BANK( 2 ); + outb( mask, ioaddr + INT_MASK ); + + PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask)); + outw( saved_pointer, ioaddr + POINTER ); + + SMC_SELECT_BANK( saved_bank ); + + PRINTK3((CARDNAME ": Interrupt done\n")); + return IRQ_RETVAL(handled); +} + + +/*---------------------------------------------------- + . smc_close + . + . this makes the board clean up everything that it can + . and not talk to the outside world. Caused by + . an 'ifconfig ethX down' + . + -----------------------------------------------------*/ +static int smc_close(struct net_device *dev) +{ + netif_stop_queue(dev); + /* clear everything */ + smc_shutdown( dev->base_addr ); + + /* Update the statistics here. */ + return 0; +} + +/*----------------------------------------------------------- + . smc_set_multicast_list + . + . This routine will, depending on the values passed to it, + . either make it accept multicast packets, go into + . promiscuous mode ( for TCPDUMP and cousins ) or accept + . a select set of multicast packets +*/ +static void smc_set_multicast_list(struct net_device *dev) +{ + short ioaddr = dev->base_addr; + + SMC_SELECT_BANK(0); + if ( dev->flags & IFF_PROMISC ) + outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); + +/* BUG? I never disable promiscuous mode if multicasting was turned on. + Now, I turn off promiscuous mode, but I don't do anything to multicasting + when promiscuous mode is turned on. +*/ + + /* Here, I am setting this to accept all multicast packets. + I don't need to zero the multicast table, because the flag is + checked before the table is + */ + else if (dev->flags & IFF_ALLMULTI) + outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR ); + + /* We just get all multicast packets even if we only want them + . from one source. This will be changed at some future + . point. */ + else if (!netdev_mc_empty(dev)) { + /* support hardware multicasting */ + + /* be sure I get rid of flags I might have set */ + outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), + ioaddr + RCR ); + /* NOTE: this has to set the bank, so make sure it is the + last thing called. The bank is set to zero at the top */ + smc_setmulticast(ioaddr, dev); + } + else { + outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), + ioaddr + RCR ); + + /* + since I'm disabling all multicast entirely, I need to + clear the multicast list + */ + SMC_SELECT_BANK( 3 ); + outw( 0, ioaddr + MULTICAST1 ); + outw( 0, ioaddr + MULTICAST2 ); + outw( 0, ioaddr + MULTICAST3 ); + outw( 0, ioaddr + MULTICAST4 ); + } +} + +#ifdef MODULE + +static struct net_device *devSMC9194; +MODULE_DESCRIPTION("SMC 9194 Ethernet driver"); +MODULE_LICENSE("GPL"); + +module_param_hw(io, int, ioport, 0); +module_param_hw(irq, int, irq, 0); +module_param(ifport, int, 0); +MODULE_PARM_DESC(io, "SMC 99194 I/O base address"); +MODULE_PARM_DESC(irq, "SMC 99194 IRQ number"); +MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)"); + +static int __init smc_init_module(void) +{ + if (io == 0) + printk(KERN_WARNING + CARDNAME": You shouldn't use auto-probing with insmod!\n" ); + + /* copy the parameters from insmod into the device structure */ + devSMC9194 = smc_init(-1); + return PTR_ERR_OR_ZERO(devSMC9194); +} +module_init(smc_init_module); + +static void __exit smc_cleanup_module(void) +{ + unregister_netdev(devSMC9194); + free_irq(devSMC9194->irq, devSMC9194); + release_region(devSMC9194->base_addr, SMC_IO_EXTENT); + free_netdev(devSMC9194); +} +module_exit(smc_cleanup_module); + +#endif /* MODULE */ diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c new file mode 100644 index 000000000000..cc0c75694351 --- /dev/null +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -0,0 +1,2059 @@ +/*====================================================================== + + A PCMCIA ethernet driver for SMC91c92-based cards. + + This driver supports Megahertz PCMCIA ethernet cards; and + Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem + multifunction cards. + + Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net + + smc91c92_cs.c 1.122 2002/10/25 06:26:39 + + This driver contains code written by Donald Becker + (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au), + David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman + (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of + Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've + incorporated some parts of his driver here. I (Dave) wrote most + of the PCMCIA glue code, and the Ositech support code. Kelly + Stephens (kstephen@holli.com) added support for the Motorola + Mariner, with help from Allen Brost. + + This software may be used and distributed according to the terms of + the GNU General Public License, incorporated herein by reference. + +======================================================================*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +/*====================================================================*/ + +static const char *if_names[] = { "auto", "10baseT", "10base2"}; + +/* Firmware name */ +#define FIRMWARE_NAME "ositech/Xilinx7OD.bin" + +/* Module parameters */ + +MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_NAME); + +#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) + +/* + Transceiver/media type. + 0 = auto + 1 = 10baseT (and autoselect if #define AUTOSELECT), + 2 = AUI/10base2, +*/ +INT_MODULE_PARM(if_port, 0); + + +#define DRV_NAME "smc91c92_cs" +#define DRV_VERSION "1.123" + +/*====================================================================*/ + +/* Operational parameter that usually are not changed. */ + +/* Time in jiffies before concluding Tx hung */ +#define TX_TIMEOUT ((400*HZ)/1000) + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +#define INTR_WORK 4 + +/* Times to check the check the chip before concluding that it doesn't + currently have room for another Tx packet. */ +#define MEMORY_WAIT_TIME 8 + +struct smc_private { + struct pcmcia_device *p_dev; + spinlock_t lock; + u_short manfid; + u_short cardid; + + struct sk_buff *saved_skb; + int packets_waiting; + void __iomem *base; + u_short cfg; + struct timer_list media; + int watchdog, tx_err; + u_short media_status; + u_short fast_poll; + u_short link_status; + struct mii_if_info mii_if; + int duplex; + int rx_ovrn; + unsigned long last_rx; +}; + +/* Special definitions for Megahertz multifunction cards */ +#define MEGAHERTZ_ISR 0x0380 + +/* Special function registers for Motorola Mariner */ +#define MOT_LAN 0x0000 +#define MOT_UART 0x0020 +#define MOT_EEPROM 0x20 + +#define MOT_NORMAL \ +(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA) + +/* Special function registers for Ositech cards */ +#define OSITECH_AUI_CTL 0x0c +#define OSITECH_PWRDOWN 0x0d +#define OSITECH_RESET 0x0e +#define OSITECH_ISR 0x0f +#define OSITECH_AUI_PWR 0x0c +#define OSITECH_RESET_ISR 0x0e + +#define OSI_AUI_PWR 0x40 +#define OSI_LAN_PWRDOWN 0x02 +#define OSI_MODEM_PWRDOWN 0x01 +#define OSI_LAN_RESET 0x02 +#define OSI_MODEM_RESET 0x01 + +/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */ +#define BANK_SELECT 14 /* Window select register. */ +#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); } + +/* Bank 0 registers. */ +#define TCR 0 /* transmit control register */ +#define TCR_CLEAR 0 /* do NOTHING */ +#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ +#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */ +#define TCR_MONCSN 0x0400 /* Monitor Carrier. */ +#define TCR_FDUPLX 0x0800 /* Full duplex mode. */ +#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN + +#define EPH 2 /* Ethernet Protocol Handler report. */ +#define EPH_TX_SUC 0x0001 +#define EPH_SNGLCOL 0x0002 +#define EPH_MULCOL 0x0004 +#define EPH_LTX_MULT 0x0008 +#define EPH_16COL 0x0010 +#define EPH_SQET 0x0020 +#define EPH_LTX_BRD 0x0040 +#define EPH_TX_DEFR 0x0080 +#define EPH_LAT_COL 0x0200 +#define EPH_LOST_CAR 0x0400 +#define EPH_EXC_DEF 0x0800 +#define EPH_CTR_ROL 0x1000 +#define EPH_RX_OVRN 0x2000 +#define EPH_LINK_OK 0x4000 +#define EPH_TX_UNRN 0x8000 +#define MEMINFO 8 /* Memory Information Register */ +#define MEMCFG 10 /* Memory Configuration Register */ + +/* Bank 1 registers. */ +#define CONFIG 0 +#define CFG_MII_SELECT 0x8000 /* 91C100 only */ +#define CFG_NO_WAIT 0x1000 +#define CFG_FULL_STEP 0x0400 +#define CFG_SET_SQLCH 0x0200 +#define CFG_AUI_SELECT 0x0100 +#define CFG_16BIT 0x0080 +#define CFG_DIS_LINK 0x0040 +#define CFG_STATIC 0x0030 +#define CFG_IRQ_SEL_1 0x0004 +#define CFG_IRQ_SEL_0 0x0002 +#define BASE_ADDR 2 +#define ADDR0 4 +#define GENERAL 10 +#define CONTROL 12 +#define CTL_STORE 0x0001 +#define CTL_RELOAD 0x0002 +#define CTL_EE_SELECT 0x0004 +#define CTL_TE_ENABLE 0x0020 +#define CTL_CR_ENABLE 0x0040 +#define CTL_LE_ENABLE 0x0080 +#define CTL_AUTO_RELEASE 0x0800 +#define CTL_POWERDOWN 0x2000 + +/* Bank 2 registers. */ +#define MMU_CMD 0 +#define MC_ALLOC 0x20 /* or with number of 256 byte packets */ +#define MC_RESET 0x40 +#define MC_RELEASE 0x80 /* remove and release the current rx packet */ +#define MC_FREEPKT 0xA0 /* Release packet in PNR register */ +#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */ +#define PNR_ARR 2 +#define FIFO_PORTS 4 +#define FP_RXEMPTY 0x8000 +#define POINTER 6 +#define PTR_AUTO_INC 0x0040 +#define PTR_READ 0x2000 +#define PTR_AUTOINC 0x4000 +#define PTR_RCV 0x8000 +#define DATA_1 8 +#define INTERRUPT 12 +#define IM_RCV_INT 0x1 +#define IM_TX_INT 0x2 +#define IM_TX_EMPTY_INT 0x4 +#define IM_ALLOC_INT 0x8 +#define IM_RX_OVRN_INT 0x10 +#define IM_EPH_INT 0x20 + +#define RCR 4 +enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002, + RxEnable = 0x0100, RxStripCRC = 0x0200}; +#define RCR_SOFTRESET 0x8000 /* resets the chip */ +#define RCR_STRIP_CRC 0x200 /* strips CRC */ +#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */ +#define RCR_ALMUL 0x4 /* receive all multicast packets */ +#define RCR_PROMISC 0x2 /* enable promiscuous mode */ + +/* the normal settings for the RCR register : */ +#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE) +#define RCR_CLEAR 0x0 /* set it to a base state */ +#define COUNTER 6 + +/* BANK 3 -- not the same values as in smc9194! */ +#define MULTICAST0 0 +#define MULTICAST2 2 +#define MULTICAST4 4 +#define MULTICAST6 6 +#define MGMT 8 +#define REVISION 0x0a + +/* Transmit status bits. */ +#define TS_SUCCESS 0x0001 +#define TS_16COL 0x0010 +#define TS_LATCOL 0x0200 +#define TS_LOSTCAR 0x0400 + +/* Receive status bits. */ +#define RS_ALGNERR 0x8000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + +#define set_bits(v, p) outw(inw(p)|(v), (p)) +#define mask_bits(v, p) outw(inw(p)&(v), (p)) + +/*====================================================================*/ + +static void smc91c92_detach(struct pcmcia_device *p_dev); +static int smc91c92_config(struct pcmcia_device *link); +static void smc91c92_release(struct pcmcia_device *link); + +static int smc_open(struct net_device *dev); +static int smc_close(struct net_device *dev); +static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue); +static netdev_tx_t smc_start_xmit(struct sk_buff *skb, + struct net_device *dev); +static irqreturn_t smc_interrupt(int irq, void *dev_id); +static void smc_rx(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static int s9k_config(struct net_device *dev, struct ifmap *map); +static void smc_set_xcvr(struct net_device *dev, int if_port); +static void smc_reset(struct net_device *dev); +static void media_check(struct timer_list *t); +static void mdio_sync(unsigned int addr); +static int mdio_read(struct net_device *dev, int phy_id, int loc); +static void mdio_write(struct net_device *dev, int phy_id, int loc, int value); +static int smc_link_ok(struct net_device *dev); +static const struct ethtool_ops ethtool_ops; + +static const struct net_device_ops smc_netdev_ops = { + .ndo_open = smc_open, + .ndo_stop = smc_close, + .ndo_start_xmit = smc_start_xmit, + .ndo_tx_timeout = smc_tx_timeout, + .ndo_set_config = s9k_config, + .ndo_set_rx_mode = set_rx_mode, + .ndo_eth_ioctl = smc_ioctl, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int smc91c92_probe(struct pcmcia_device *link) +{ + struct smc_private *smc; + struct net_device *dev; + + dev_dbg(&link->dev, "smc91c92_attach()\n"); + + /* Create new ethernet device */ + dev = alloc_etherdev(sizeof(struct smc_private)); + if (!dev) + return -ENOMEM; + smc = netdev_priv(dev); + smc->p_dev = link; + link->priv = dev; + + spin_lock_init(&smc->lock); + + /* The SMC91c92-specific entries in the device structure. */ + dev->netdev_ops = &smc_netdev_ops; + dev->ethtool_ops = ðtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + smc->mii_if.dev = dev; + smc->mii_if.mdio_read = mdio_read; + smc->mii_if.mdio_write = mdio_write; + smc->mii_if.phy_id_mask = 0x1f; + smc->mii_if.reg_num_mask = 0x1f; + + return smc91c92_config(link); +} /* smc91c92_attach */ + +static void smc91c92_detach(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + dev_dbg(&link->dev, "smc91c92_detach\n"); + + unregister_netdev(dev); + + smc91c92_release(link); + + free_netdev(dev); +} /* smc91c92_detach */ + +/*====================================================================*/ + +static int cvt_ascii_address(struct net_device *dev, char *s) +{ + u8 mac[ETH_ALEN]; + int i, j, da, c; + + if (strlen(s) != 12) + return -1; + for (i = 0; i < 6; i++) { + da = 0; + for (j = 0; j < 2; j++) { + c = *s++; + da <<= 4; + da += ((c >= '0') && (c <= '9')) ? + (c - '0') : ((c & 0x0f) + 9); + } + mac[i] = da; + } + eth_hw_addr_set(dev, mac); + return 0; +} + +/*==================================================================== + + Configuration stuff for Megahertz cards + + mhz_3288_power() is used to power up a 3288's ethernet chip. + mhz_mfc_config() handles socket setup for multifunction (1144 + and 3288) cards. mhz_setup() gets a card's hardware ethernet + address. + +======================================================================*/ + +static int mhz_3288_power(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + u_char tmp; + + /* Read the ISR twice... */ + readb(smc->base+MEGAHERTZ_ISR); + udelay(5); + readb(smc->base+MEGAHERTZ_ISR); + + /* Pause 200ms... */ + mdelay(200); + + /* Now read and write the COR... */ + tmp = readb(smc->base + link->config_base + CISREG_COR); + udelay(5); + writeb(tmp, smc->base + link->config_base + CISREG_COR); + + return 0; +} + +static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + int k; + p_dev->io_lines = 16; + p_dev->resource[1]->start = p_dev->resource[0]->start; + p_dev->resource[1]->end = 8; + p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + for (k = 0; k < 0x400; k += 0x10) { + if (k & 0x80) + continue; + p_dev->resource[0]->start = k ^ 0x300; + if (!pcmcia_request_io(p_dev)) + return 0; + } + return -ENODEV; +} + +static int mhz_mfc_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + unsigned int offset; + int i; + + link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ | + CONF_AUTO_SET_IO; + + /* The Megahertz combo cards have modem-like CIS entries, so + we have to explicitly try a bunch of port combinations. */ + if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL)) + return -ENODEV; + + dev->base_addr = link->resource[0]->start; + + /* Allocate a memory window, for accessing the ISR */ + link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; + link->resource[2]->start = link->resource[2]->end = 0; + i = pcmcia_request_window(link, link->resource[2], 0); + if (i != 0) + return -ENODEV; + + smc->base = ioremap(link->resource[2]->start, + resource_size(link->resource[2])); + offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0; + i = pcmcia_map_mem_page(link, link->resource[2], offset); + if ((i == 0) && + (smc->manfid == MANFID_MEGAHERTZ) && + (smc->cardid == PRODID_MEGAHERTZ_EM3288)) + mhz_3288_power(link); + + return 0; +} + +static int pcmcia_get_versmac(struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv) +{ + struct net_device *dev = priv; + cisparse_t parse; + u8 *buf; + + if (pcmcia_parse_tuple(tuple, &parse)) + return -EINVAL; + + buf = parse.version_1.str + parse.version_1.ofs[3]; + + if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0)) + return 0; + + return -EINVAL; +}; + +static int mhz_setup(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + size_t len; + u8 *buf; + int rc; + + /* Read the station address from the CIS. It is stored as the last + (fourth) string in the Version 1 Version/ID tuple. */ + if ((link->prod_id[3]) && + (cvt_ascii_address(dev, link->prod_id[3]) == 0)) + return 0; + + /* Workarounds for broken cards start here. */ + /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */ + if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev)) + return 0; + + /* Another possibility: for the EM3288, in a special tuple */ + rc = -1; + len = pcmcia_get_tuple(link, 0x81, &buf); + if (buf && len >= 13) { + buf[12] = '\0'; + if (cvt_ascii_address(dev, buf) == 0) + rc = 0; + } + kfree(buf); + + return rc; +}; + +/*====================================================================== + + Configuration stuff for the Motorola Mariner + + mot_config() writes directly to the Mariner configuration + registers because the CIS is just bogus. + +======================================================================*/ + +static void mot_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + unsigned int iouart = link->resource[1]->start; + + /* Set UART base address and force map with COR bit 1 */ + writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0); + writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1); + writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR); + + /* Set SMC base address and force map with COR bit 1 */ + writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0); + writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1); + writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR); + + /* Wait for things to settle down */ + mdelay(100); +} + +static int mot_setup(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + unsigned int ioaddr = dev->base_addr; + int i, wait, loop; + u8 mac[ETH_ALEN]; + u_int addr; + + /* Read Ethernet address from Serial EEPROM */ + + for (i = 0; i < 3; i++) { + SMC_SELECT_BANK(2); + outw(MOT_EEPROM + i, ioaddr + POINTER); + SMC_SELECT_BANK(1); + outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL); + + for (loop = wait = 0; loop < 200; loop++) { + udelay(10); + wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL)); + if (wait == 0) break; + } + + if (wait) + return -1; + + addr = inw(ioaddr + GENERAL); + mac[2*i] = addr & 0xff; + mac[2*i+1] = (addr >> 8) & 0xff; + } + eth_hw_addr_set(dev, mac); + + return 0; +} + +/*====================================================================*/ + +static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data) +{ + p_dev->resource[0]->end = 16; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + + return pcmcia_request_io(p_dev); +} + +static int smc_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + int i; + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + i = pcmcia_loop_config(link, smc_configcheck, NULL); + if (!i) + dev->base_addr = link->resource[0]->start; + + return i; +} + + +static int smc_setup(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + /* Check for a LAN function extension tuple */ + if (!pcmcia_get_mac_from_cis(link, dev)) + return 0; + + /* Try the third string in the Version 1 Version/ID tuple. */ + if (link->prod_id[2]) { + if (cvt_ascii_address(dev, link->prod_id[2]) == 0) + return 0; + } + return -1; +} + +/*====================================================================*/ + +static int osi_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; + int i, j; + + link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ; + link->resource[0]->end = 64; + link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + link->resource[1]->end = 8; + + /* Enable Hard Decode, LAN, Modem */ + link->io_lines = 16; + link->config_index = 0x23; + + for (i = j = 0; j < 4; j++) { + link->resource[1]->start = com[j]; + i = pcmcia_request_io(link); + if (i == 0) + break; + } + if (i != 0) { + /* Fallback: turn off hard decode */ + link->config_index = 0x03; + link->resource[1]->end = 0; + i = pcmcia_request_io(link); + } + dev->base_addr = link->resource[0]->start + 0x10; + return i; +} + +static int osi_load_firmware(struct pcmcia_device *link) +{ + const struct firmware *fw; + int i, err; + + err = request_firmware(&fw, FIRMWARE_NAME, &link->dev); + if (err) { + pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME); + return err; + } + + /* Download the Seven of Diamonds firmware */ + for (i = 0; i < fw->size; i++) { + outb(fw->data[i], link->resource[0]->start + 2); + udelay(50); + } + release_firmware(fw); + return err; +} + +static int pcmcia_osi_mac(struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv) +{ + struct net_device *dev = priv; + + if (tuple->TupleDataLen < 8) + return -EINVAL; + if (tuple->TupleData[0] != 0x04) + return -EINVAL; + + eth_hw_addr_set(dev, &tuple->TupleData[2]); + return 0; +}; + + +static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid) +{ + struct net_device *dev = link->priv; + int rc; + + /* Read the station address from tuple 0x90, subtuple 0x04 */ + if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev)) + return -1; + + if (((manfid == MANFID_OSITECH) && + (cardid == PRODID_OSITECH_SEVEN)) || + ((manfid == MANFID_PSION) && + (cardid == PRODID_PSION_NET100))) { + rc = osi_load_firmware(link); + if (rc) + return rc; + } else if (manfid == MANFID_OSITECH) { + /* Make sure both functions are powered up */ + set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR); + /* Now, turn on the interrupt for both card functions */ + set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR); + dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n", + inw(link->resource[0]->start + OSITECH_AUI_PWR), + inw(link->resource[0]->start + OSITECH_RESET_ISR)); + } + return 0; +} + +static int smc91c92_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int smc91c92_resume(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + int i; + + if ((smc->manfid == MANFID_MEGAHERTZ) && + (smc->cardid == PRODID_MEGAHERTZ_EM3288)) + mhz_3288_power(link); + if (smc->manfid == MANFID_MOTOROLA) + mot_config(link); + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) { + /* Power up the card and enable interrupts */ + set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); + set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); + } + if (((smc->manfid == MANFID_OSITECH) && + (smc->cardid == PRODID_OSITECH_SEVEN)) || + ((smc->manfid == MANFID_PSION) && + (smc->cardid == PRODID_PSION_NET100))) { + i = osi_load_firmware(link); + if (i) { + netdev_err(dev, "Failed to load firmware\n"); + return i; + } + } + if (link->open) { + smc_reset(dev); + netif_device_attach(dev); + } + + return 0; +} + + +/*====================================================================== + + This verifies that the chip is some SMC91cXX variant, and returns + the revision code if successful. Otherwise, it returns -ENODEV. + +======================================================================*/ + +static int check_sig(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + unsigned int ioaddr = dev->base_addr; + int width; + u_short s; + + SMC_SELECT_BANK(1); + if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) { + /* Try powering up the chip */ + outw(0, ioaddr + CONTROL); + mdelay(55); + } + + /* Try setting bus width */ + width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO); + s = inb(ioaddr + CONFIG); + if (width) + s |= CFG_16BIT; + else + s &= ~CFG_16BIT; + outb(s, ioaddr + CONFIG); + + /* Check Base Address Register to make sure bus width is OK */ + s = inw(ioaddr + BASE_ADDR); + if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) && + ((s >> 8) != (s & 0xff))) { + SMC_SELECT_BANK(3); + s = inw(ioaddr + REVISION); + return s & 0xff; + } + + if (width) { + netdev_info(dev, "using 8-bit IO window\n"); + + smc91c92_suspend(link); + pcmcia_fixup_iowidth(link); + smc91c92_resume(link); + return check_sig(link); + } + return -ENODEV; +} + +static int smc91c92_config(struct pcmcia_device *link) +{ + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + char *name; + int i, rev, j = 0; + unsigned int ioaddr; + u_long mir; + + dev_dbg(&link->dev, "smc91c92_config\n"); + + smc->manfid = link->manf_id; + smc->cardid = link->card_id; + + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) { + i = osi_config(link); + } else if ((smc->manfid == MANFID_MOTOROLA) || + ((smc->manfid == MANFID_MEGAHERTZ) && + ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) || + (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) { + i = mhz_mfc_config(link); + } else { + i = smc_config(link); + } + if (i) + goto config_failed; + + i = pcmcia_request_irq(link, smc_interrupt); + if (i) + goto config_failed; + i = pcmcia_enable_device(link); + if (i) + goto config_failed; + + if (smc->manfid == MANFID_MOTOROLA) + mot_config(link); + + dev->irq = link->irq; + + if ((if_port >= 0) && (if_port <= 2)) + dev->if_port = if_port; + else + dev_notice(&link->dev, "invalid if_port requested\n"); + + switch (smc->manfid) { + case MANFID_OSITECH: + case MANFID_PSION: + i = osi_setup(link, smc->manfid, smc->cardid); break; + case MANFID_SMC: + case MANFID_NEW_MEDIA: + i = smc_setup(link); break; + case 0x128: /* For broken Megahertz cards */ + case MANFID_MEGAHERTZ: + i = mhz_setup(link); break; + case MANFID_MOTOROLA: + default: /* get the hw address from EEPROM */ + i = mot_setup(link); break; + } + + if (i != 0) { + dev_notice(&link->dev, "Unable to find hardware address.\n"); + goto config_failed; + } + + smc->duplex = 0; + smc->rx_ovrn = 0; + + rev = check_sig(link); + name = "???"; + if (rev > 0) + switch (rev >> 4) { + case 3: name = "92"; break; + case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break; + case 5: name = "95"; break; + case 7: name = "100"; break; + case 8: name = "100-FD"; break; + case 9: name = "110"; break; + } + + ioaddr = dev->base_addr; + if (rev > 0) { + u_long mcr; + SMC_SELECT_BANK(0); + mir = inw(ioaddr + MEMINFO) & 0xff; + if (mir == 0xff) mir++; + /* Get scale factor for memory size */ + mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; + mir *= 128 * (1<<((mcr >> 9) & 7)); + SMC_SELECT_BANK(1); + smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; + smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC; + if (smc->manfid == MANFID_OSITECH) + smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0; + if ((rev >> 4) >= 7) + smc->cfg |= CFG_MII_SELECT; + } else + mir = 0; + + if (smc->cfg & CFG_MII_SELECT) { + SMC_SELECT_BANK(3); + + for (i = 0; i < 32; i++) { + j = mdio_read(dev, i, 1); + if ((j != 0) && (j != 0xffff)) break; + } + smc->mii_if.phy_id = (i < 32) ? i : -1; + + SMC_SELECT_BANK(0); + } + + SET_NETDEV_DEV(dev, &link->dev); + + if (register_netdev(dev) != 0) { + dev_err(&link->dev, "register_netdev() failed\n"); + goto config_undo; + } + + netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n", + name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr); + + if (rev > 0) { + if (mir & 0x3ff) + netdev_info(dev, " %lu byte", mir); + else + netdev_info(dev, " %lu kb", mir>>10); + pr_cont(" buffer, %s xcvr\n", + (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]); + } + + if (smc->cfg & CFG_MII_SELECT) { + if (smc->mii_if.phy_id != -1) { + netdev_dbg(dev, " MII transceiver at index %d, status %x\n", + smc->mii_if.phy_id, j); + } else { + netdev_notice(dev, " No MII transceivers found!\n"); + } + } + return 0; + +config_undo: + unregister_netdev(dev); +config_failed: + smc91c92_release(link); + free_netdev(dev); + return -ENODEV; +} /* smc91c92_config */ + +static void smc91c92_release(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "smc91c92_release\n"); + if (link->resource[2]->end) { + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + iounmap(smc->base); + } + pcmcia_disable_device(link); +} + +/*====================================================================== + + MII interface support for SMC91cXX based cards +======================================================================*/ + +#define MDIO_SHIFT_CLK 0x04 +#define MDIO_DATA_OUT 0x01 +#define MDIO_DIR_WRITE 0x08 +#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT) +#define MDIO_DATA_READ 0x02 + +static void mdio_sync(unsigned int addr) +{ + int bits; + for (bits = 0; bits < 32; bits++) { + outb(MDIO_DATA_WRITE1, addr); + outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); + } +} + +static int mdio_read(struct net_device *dev, int phy_id, int loc) +{ + unsigned int addr = dev->base_addr + MGMT; + u_int cmd = (0x06<<10)|(phy_id<<5)|loc; + int i, retval = 0; + + mdio_sync(addr); + for (i = 13; i >= 0; i--) { + int dat = (cmd&(1< 0; i--) { + outb(0, addr); + retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); + outb(MDIO_SHIFT_CLK, addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int loc, int value) +{ + unsigned int addr = dev->base_addr + MGMT; + u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; + int i; + + mdio_sync(addr); + for (i = 31; i >= 0; i--) { + int dat = (cmd&(1<= 0; i--) { + outb(0, addr); + outb(MDIO_SHIFT_CLK, addr); + } +} + +/*====================================================================== + + The driver core code, most of which should be common with a + non-PCMCIA implementation. + +======================================================================*/ + +#ifdef PCMCIA_DEBUG +static void smc_dump(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + u_short i, w, save; + save = inw(ioaddr + BANK_SELECT); + for (w = 0; w < 4; w++) { + SMC_SELECT_BANK(w); + netdev_dbg(dev, "bank %d: ", w); + for (i = 0; i < 14; i += 2) + pr_cont(" %04x", inw(ioaddr + i)); + pr_cont("\n"); + } + outw(save, ioaddr + BANK_SELECT); +} +#endif + +static int smc_open(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + struct pcmcia_device *link = smc->p_dev; + + dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n", + dev->name, dev, inw(dev->base_addr + BANK_SELECT)); +#ifdef PCMCIA_DEBUG + smc_dump(dev); +#endif + + /* Check that the PCMCIA card is still here. */ + if (!pcmcia_dev_present(link)) + return -ENODEV; + /* Physical device present signature. */ + if (check_sig(link) < 0) { + netdev_info(dev, "Yikes! Bad chip signature!\n"); + return -ENODEV; + } + link->open++; + + netif_start_queue(dev); + smc->saved_skb = NULL; + smc->packets_waiting = 0; + + smc_reset(dev); + timer_setup(&smc->media, media_check, 0); + mod_timer(&smc->media, jiffies + HZ); + + return 0; +} /* smc_open */ + +/*====================================================================*/ + +static int smc_close(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + struct pcmcia_device *link = smc->p_dev; + unsigned int ioaddr = dev->base_addr; + + dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n", + dev->name, inw(ioaddr + BANK_SELECT)); + + netif_stop_queue(dev); + + /* Shut off all interrupts, and turn off the Tx and Rx sections. + Don't bother to check for chip present. */ + SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */ + outw(0, ioaddr + INTERRUPT); + SMC_SELECT_BANK(0); + mask_bits(0xff00, ioaddr + RCR); + mask_bits(0xff00, ioaddr + TCR); + + /* Put the chip into power-down mode. */ + SMC_SELECT_BANK(1); + outw(CTL_POWERDOWN, ioaddr + CONTROL ); + + link->open--; + timer_delete_sync(&smc->media); + + return 0; +} /* smc_close */ + +/*====================================================================== + + Transfer a packet to the hardware and trigger the packet send. + This may be called at either from either the Tx queue code + or the interrupt handler. + +======================================================================*/ + +static void smc_hardware_send_packet(struct net_device * dev) +{ + struct smc_private *smc = netdev_priv(dev); + struct sk_buff *skb = smc->saved_skb; + unsigned int ioaddr = dev->base_addr; + u_char packet_no; + + if (!skb) { + netdev_err(dev, "In XMIT with no packet to send\n"); + return; + } + + /* There should be a packet slot waiting. */ + packet_no = inw(ioaddr + PNR_ARR) >> 8; + if (packet_no & 0x80) { + /* If not, there is a hardware problem! Likely an ejected card. */ + netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n", + packet_no); + dev_kfree_skb_irq(skb); + smc->saved_skb = NULL; + netif_start_queue(dev); + return; + } + + dev->stats.tx_bytes += skb->len; + /* The card should use the just-allocated buffer. */ + outw(packet_no, ioaddr + PNR_ARR); + /* point to the beginning of the packet */ + outw(PTR_AUTOINC , ioaddr + POINTER); + + /* Send the packet length (+6 for status, length and ctl byte) + and the status word (set to zeros). */ + { + u_char *buf = skb->data; + u_int length = skb->len; /* The chip will pad to ethernet min. */ + + netdev_dbg(dev, "Trying to xmit packet of length %d\n", length); + + /* send the packet length: +6 for status word, length, and ctl */ + outw(0, ioaddr + DATA_1); + outw(length + 6, ioaddr + DATA_1); + outsw(ioaddr + DATA_1, buf, length >> 1); + + /* The odd last byte, if there is one, goes in the control word. */ + outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1); + } + + /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ + outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | + (inw(ioaddr + INTERRUPT) & 0xff00), + ioaddr + INTERRUPT); + + /* The chip does the rest of the work. */ + outw(MC_ENQUEUE , ioaddr + MMU_CMD); + + smc->saved_skb = NULL; + dev_kfree_skb_irq(skb); + netif_trans_update(dev); + netif_start_queue(dev); +} + +/*====================================================================*/ + +static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + + netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n", + inw(ioaddr)&0xff, inw(ioaddr + 2)); + dev->stats.tx_errors++; + smc_reset(dev); + netif_trans_update(dev); /* prevent tx timeout */ + smc->saved_skb = NULL; + netif_wake_queue(dev); +} + +static netdev_tx_t smc_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + u_short num_pages; + short time_out, ir; + unsigned long flags; + + netif_stop_queue(dev); + + netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n", + skb->len, inw(ioaddr + 2)); + + if (smc->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + dev->stats.tx_aborted_errors++; + netdev_dbg(dev, "Internal error -- sent packet while busy\n"); + return NETDEV_TX_BUSY; + } + smc->saved_skb = skb; + + num_pages = skb->len >> 8; + + if (num_pages > 7) { + netdev_err(dev, "Far too big packet error: %d pages\n", num_pages); + dev_kfree_skb (skb); + smc->saved_skb = NULL; + dev->stats.tx_dropped++; + return NETDEV_TX_OK; /* Do not re-queue this packet. */ + } + /* A packet is now waiting. */ + smc->packets_waiting++; + + spin_lock_irqsave(&smc->lock, flags); + SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */ + + /* need MC_RESET to keep the memory consistent. errata? */ + if (smc->rx_ovrn) { + outw(MC_RESET, ioaddr + MMU_CMD); + smc->rx_ovrn = 0; + } + + /* Allocate the memory; send the packet now if we win. */ + outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD); + for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { + ir = inw(ioaddr+INTERRUPT); + if (ir & IM_ALLOC_INT) { + /* Acknowledge the interrupt, send the packet. */ + outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); + smc_hardware_send_packet(dev); /* Send the packet now.. */ + spin_unlock_irqrestore(&smc->lock, flags); + return NETDEV_TX_OK; + } + } + + /* Otherwise defer until the Tx-space-allocated interrupt. */ + netdev_dbg(dev, "memory allocation deferred.\n"); + outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); + spin_unlock_irqrestore(&smc->lock, flags); + + return NETDEV_TX_OK; +} + +/*====================================================================== + + Handle a Tx anomalous event. Entered while in Window 2. + +======================================================================*/ + +static void smc_tx_err(struct net_device * dev) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; + int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; + int tx_status; + + /* select this as the packet to read from */ + outw(packet_no, ioaddr + PNR_ARR); + + /* read the first word from this packet */ + outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER); + + tx_status = inw(ioaddr + DATA_1); + + dev->stats.tx_errors++; + if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++; + if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++; + if (tx_status & TS_16COL) { + dev->stats.tx_aborted_errors++; + smc->tx_err++; + } + + if (tx_status & TS_SUCCESS) { + netdev_notice(dev, "Successful packet caused error interrupt?\n"); + } + /* re-enable transmit */ + SMC_SELECT_BANK(0); + outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); + SMC_SELECT_BANK(2); + + outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */ + + /* one less packet waiting for me */ + smc->packets_waiting--; + + outw(saved_packet, ioaddr + PNR_ARR); +} + +/*====================================================================*/ + +static void smc_eph_irq(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + u_short card_stats, ephs; + + SMC_SELECT_BANK(0); + ephs = inw(ioaddr + EPH); + netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n", + ephs); + /* Could be a counter roll-over warning: update stats. */ + card_stats = inw(ioaddr + COUNTER); + /* single collisions */ + dev->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + dev->stats.collisions += card_stats & 0xF; +#if 0 /* These are for when linux supports these statistics */ + card_stats >>= 4; /* deferred */ + card_stats >>= 4; /* excess deferred */ +#endif + /* If we had a transmit error we must re-enable the transmitter. */ + outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); + + /* Clear a link error interrupt. */ + SMC_SELECT_BANK(1); + outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); + outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, + ioaddr + CONTROL); + SMC_SELECT_BANK(2); +} + +/*====================================================================*/ + +static irqreturn_t smc_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr; + u_short saved_bank, saved_pointer, mask, status; + unsigned int handled = 1; + char bogus_cnt = INTR_WORK; /* Work we are willing to do. */ + + if (!netif_device_present(dev)) + return IRQ_NONE; + + ioaddr = dev->base_addr; + + netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n", + irq, ioaddr); + + spin_lock(&smc->lock); + smc->watchdog = 0; + saved_bank = inw(ioaddr + BANK_SELECT); + if ((saved_bank & 0xff00) != 0x3300) { + /* The device does not exist -- the card could be off-line, or + maybe it has been ejected. */ + netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n", + irq); + handled = 0; + goto irq_done; + } + + SMC_SELECT_BANK(2); + saved_pointer = inw(ioaddr + POINTER); + mask = inw(ioaddr + INTERRUPT) >> 8; + /* clear all interrupts */ + outw(0, ioaddr + INTERRUPT); + + do { /* read the status flag, and mask it */ + status = inw(ioaddr + INTERRUPT) & 0xff; + netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n", + status, mask); + if ((status & mask) == 0) { + if (bogus_cnt == INTR_WORK) + handled = 0; + break; + } + if (status & IM_RCV_INT) { + /* Got a packet(s). */ + smc_rx(dev); + } + if (status & IM_TX_INT) { + smc_tx_err(dev); + outw(IM_TX_INT, ioaddr + INTERRUPT); + } + status &= mask; + if (status & IM_TX_EMPTY_INT) { + outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT); + mask &= ~IM_TX_EMPTY_INT; + dev->stats.tx_packets += smc->packets_waiting; + smc->packets_waiting = 0; + } + if (status & IM_ALLOC_INT) { + /* Clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + + smc_hardware_send_packet(dev); + + /* enable xmit interrupts based on this */ + mask |= (IM_TX_EMPTY_INT | IM_TX_INT); + + /* and let the card send more packets to me */ + netif_wake_queue(dev); + } + if (status & IM_RX_OVRN_INT) { + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; + if (smc->duplex) + smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */ + outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT); + } + if (status & IM_EPH_INT) + smc_eph_irq(dev); + } while (--bogus_cnt); + + netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n", + mask, saved_bank, saved_pointer); + + /* restore state register */ + outw((mask<<8), ioaddr + INTERRUPT); + outw(saved_pointer, ioaddr + POINTER); + SMC_SELECT_BANK(saved_bank); + + netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq); + +irq_done: + + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) { + /* Retrigger interrupt if needed */ + mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR); + set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR); + } + if (smc->manfid == MANFID_MOTOROLA) { + u_char cor; + cor = readb(smc->base + MOT_UART + CISREG_COR); + writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR); + writeb(cor, smc->base + MOT_UART + CISREG_COR); + cor = readb(smc->base + MOT_LAN + CISREG_COR); + writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR); + writeb(cor, smc->base + MOT_LAN + CISREG_COR); + } + + if ((smc->base != NULL) && /* Megahertz MFC's */ + (smc->manfid == MANFID_MEGAHERTZ) && + (smc->cardid == PRODID_MEGAHERTZ_EM3288)) { + + u_char tmp; + tmp = readb(smc->base+MEGAHERTZ_ISR); + tmp = readb(smc->base+MEGAHERTZ_ISR); + + /* Retrigger interrupt if needed */ + writeb(tmp, smc->base + MEGAHERTZ_ISR); + writeb(tmp, smc->base + MEGAHERTZ_ISR); + } + + spin_unlock(&smc->lock); + return IRQ_RETVAL(handled); +} + +/*====================================================================*/ + +static void smc_rx(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + int rx_status; + int packet_length; /* Caution: not frame length, rather words + to transfer from the chip. */ + + /* Assertion: we are in Window 2. */ + + if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) { + netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n"); + return; + } + + /* Reset the read pointer, and read the status and packet length. */ + outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER); + rx_status = inw(ioaddr + DATA_1); + packet_length = inw(ioaddr + DATA_1) & 0x07ff; + + netdev_dbg(dev, "Receive status %4.4x length %d.\n", + rx_status, packet_length); + + if (!(rx_status & RS_ERRORS)) { + /* do stuff to make a new packet */ + struct sk_buff *skb; + struct smc_private *smc = netdev_priv(dev); + + /* Note: packet_length adds 5 or 6 extra bytes here! */ + skb = netdev_alloc_skb(dev, packet_length+2); + + if (skb == NULL) { + netdev_dbg(dev, "Low memory, packet dropped.\n"); + dev->stats.rx_dropped++; + outw(MC_RELEASE, ioaddr + MMU_CMD); + return; + } + + packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6); + skb_reserve(skb, 2); + insw(ioaddr+DATA_1, skb_put(skb, packet_length), + (packet_length+1)>>1); + skb->protocol = eth_type_trans(skb, dev); + + netif_rx(skb); + smc->last_rx = jiffies; + dev->stats.rx_packets++; + dev->stats.rx_bytes += packet_length; + if (rx_status & RS_MULTICAST) + dev->stats.multicast++; + } else { + /* error ... */ + dev->stats.rx_errors++; + + if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++; + if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) + dev->stats.rx_length_errors++; + if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++; + } + /* Let the MMU free the memory of this packet. */ + outw(MC_RELEASE, ioaddr + MMU_CMD); +} + +/*====================================================================== + + Set the receive mode. + + This routine is used by both the protocol level to notify us of + promiscuous/multicast mode changes, and by the open/reset code to + initialize the Rx registers. We always set the multicast list and + leave the receiver running. + +======================================================================*/ + +static void set_rx_mode(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_private *smc = netdev_priv(dev); + unsigned char multicast_table[8]; + unsigned long flags; + u_short rx_cfg_setting; + int i; + + memset(multicast_table, 0, sizeof(multicast_table)); + + if (dev->flags & IFF_PROMISC) { + rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; + } else if (dev->flags & IFF_ALLMULTI) + rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; + else { + if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + + netdev_for_each_mc_addr(ha, dev) { + u_int position = ether_crc(6, ha->addr); + multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); + } + } + rx_cfg_setting = RxStripCRC | RxEnable; + } + + /* Load MC table and Rx setting into the chip without interrupts. */ + spin_lock_irqsave(&smc->lock, flags); + SMC_SELECT_BANK(3); + for (i = 0; i < 8; i++) + outb(multicast_table[i], ioaddr + MULTICAST0 + i); + SMC_SELECT_BANK(0); + outw(rx_cfg_setting, ioaddr + RCR); + SMC_SELECT_BANK(2); + spin_unlock_irqrestore(&smc->lock, flags); +} + +/*====================================================================== + + Senses when a card's config changes. Here, it's coax or TP. + +======================================================================*/ + +static int s9k_config(struct net_device *dev, struct ifmap *map) +{ + struct smc_private *smc = netdev_priv(dev); + if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { + if (smc->cfg & CFG_MII_SELECT) + return -EOPNOTSUPP; + else if (map->port > 2) + return -EINVAL; + WRITE_ONCE(dev->if_port, map->port); + netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); + smc_reset(dev); + } + return 0; +} + +/*====================================================================== + + Reset the chip, reloading every register that might be corrupted. + +======================================================================*/ + +/* + Set transceiver type, perhaps to something other than what the user + specified in dev->if_port. +*/ +static void smc_set_xcvr(struct net_device *dev, int if_port) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + u_short saved_bank; + + saved_bank = inw(ioaddr + BANK_SELECT); + SMC_SELECT_BANK(1); + if (if_port == 2) { + outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG); + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) + set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); + smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002); + } else { + outw(smc->cfg, ioaddr + CONFIG); + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) + mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); + smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001); + } + SMC_SELECT_BANK(saved_bank); +} + +static void smc_reset(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_private *smc = netdev_priv(dev); + int i; + + netdev_dbg(dev, "smc91c92 reset called.\n"); + + /* The first interaction must be a write to bring the chip out + of sleep mode. */ + SMC_SELECT_BANK(0); + /* Reset the chip. */ + outw(RCR_SOFTRESET, ioaddr + RCR); + udelay(10); + + /* Clear the transmit and receive configuration registers. */ + outw(RCR_CLEAR, ioaddr + RCR); + outw(TCR_CLEAR, ioaddr + TCR); + + /* Set the Window 1 control, configuration and station addr registers. + No point in writing the I/O base register ;-> */ + SMC_SELECT_BANK(1); + /* Automatically release successfully transmitted packets, + Accept link errors, counter and Tx error interrupts. */ + outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, + ioaddr + CONTROL); + smc_set_xcvr(dev, dev->if_port); + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) + outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) | + (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00), + ioaddr - 0x10 + OSITECH_AUI_PWR); + + /* Fill in the physical address. The databook is wrong about the order! */ + for (i = 0; i < 6; i += 2) + outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i], + ioaddr + ADDR0 + i); + + /* Reset the MMU */ + SMC_SELECT_BANK(2); + outw(MC_RESET, ioaddr + MMU_CMD); + outw(0, ioaddr + INTERRUPT); + + /* Re-enable the chip. */ + SMC_SELECT_BANK(0); + outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) | + TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR); + set_rx_mode(dev); + + if (smc->cfg & CFG_MII_SELECT) { + SMC_SELECT_BANK(3); + + /* Reset MII */ + mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000); + + /* Advertise 100F, 100H, 10F, 10H */ + mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1); + + /* Restart MII autonegotiation */ + mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000); + mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200); + } + + /* Enable interrupts. */ + SMC_SELECT_BANK(2); + outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8, + ioaddr + INTERRUPT); +} + +/*====================================================================== + + Media selection timer routine + +======================================================================*/ + +static void media_check(struct timer_list *t) +{ + struct smc_private *smc = timer_container_of(smc, t, media); + struct net_device *dev = smc->mii_if.dev; + unsigned int ioaddr = dev->base_addr; + u_short i, media, saved_bank; + u_short link; + unsigned long flags; + + spin_lock_irqsave(&smc->lock, flags); + + saved_bank = inw(ioaddr + BANK_SELECT); + + if (!netif_device_present(dev)) + goto reschedule; + + SMC_SELECT_BANK(2); + + /* need MC_RESET to keep the memory consistent. errata? */ + if (smc->rx_ovrn) { + outw(MC_RESET, ioaddr + MMU_CMD); + smc->rx_ovrn = 0; + } + i = inw(ioaddr + INTERRUPT); + SMC_SELECT_BANK(0); + media = inw(ioaddr + EPH) & EPH_LINK_OK; + SMC_SELECT_BANK(1); + media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1; + + SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&smc->lock, flags); + + /* Check for pending interrupt with watchdog flag set: with + this, we can limp along even if the interrupt is blocked */ + if (smc->watchdog++ && ((i>>8) & i)) { + if (!smc->fast_poll) + netdev_info(dev, "interrupt(s) dropped!\n"); + local_irq_save(flags); + smc_interrupt(dev->irq, dev); + local_irq_restore(flags); + smc->fast_poll = HZ; + } + if (smc->fast_poll) { + smc->fast_poll--; + smc->media.expires = jiffies + HZ/100; + add_timer(&smc->media); + return; + } + + spin_lock_irqsave(&smc->lock, flags); + + saved_bank = inw(ioaddr + BANK_SELECT); + + if (smc->cfg & CFG_MII_SELECT) { + if (smc->mii_if.phy_id < 0) + goto reschedule; + + SMC_SELECT_BANK(3); + link = mdio_read(dev, smc->mii_if.phy_id, 1); + if (!link || (link == 0xffff)) { + netdev_info(dev, "MII is missing!\n"); + smc->mii_if.phy_id = -1; + goto reschedule; + } + + link &= 0x0004; + if (link != smc->link_status) { + u_short p = mdio_read(dev, smc->mii_if.phy_id, 5); + netdev_info(dev, "%s link beat\n", link ? "found" : "lost"); + smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40)) + ? TCR_FDUPLX : 0); + if (link) { + netdev_info(dev, "autonegotiation complete: " + "%dbaseT-%cD selected\n", + (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H'); + } + SMC_SELECT_BANK(0); + outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR); + smc->link_status = link; + } + goto reschedule; + } + + /* Ignore collisions unless we've had no rx's recently */ + if (time_after(jiffies, smc->last_rx + HZ)) { + if (smc->tx_err || (smc->media_status & EPH_16COL)) + media |= EPH_16COL; + } + smc->tx_err = 0; + + if (media != smc->media_status) { + if ((media & smc->media_status & 1) && + ((smc->media_status ^ media) & EPH_LINK_OK)) + netdev_info(dev, "%s link beat\n", + smc->media_status & EPH_LINK_OK ? "lost" : "found"); + else if ((media & smc->media_status & 2) && + ((smc->media_status ^ media) & EPH_16COL)) + netdev_info(dev, "coax cable %s\n", + media & EPH_16COL ? "problem" : "ok"); + if (dev->if_port == 0) { + if (media & 1) { + if (media & EPH_LINK_OK) + netdev_info(dev, "flipped to 10baseT\n"); + else + smc_set_xcvr(dev, 2); + } else { + if (media & EPH_16COL) + smc_set_xcvr(dev, 1); + else + netdev_info(dev, "flipped to 10base2\n"); + } + } + smc->media_status = media; + } + +reschedule: + smc->media.expires = jiffies + HZ; + add_timer(&smc->media); + SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&smc->lock, flags); +} + +static int smc_link_ok(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_private *smc = netdev_priv(dev); + + if (smc->cfg & CFG_MII_SELECT) { + return mii_link_ok(&smc->mii_if); + } else { + SMC_SELECT_BANK(0); + return inw(ioaddr + EPH) & EPH_LINK_OK; + } +} + +static void smc_netdev_get_ecmd(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) +{ + u16 tmp; + unsigned int ioaddr = dev->base_addr; + u32 supported; + + supported = (SUPPORTED_TP | SUPPORTED_AUI | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); + + SMC_SELECT_BANK(1); + tmp = inw(ioaddr + CONFIG); + ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; + ecmd->base.speed = SPEED_10; + ecmd->base.phy_address = ioaddr + MGMT; + + SMC_SELECT_BANK(0); + tmp = inw(ioaddr + TCR); + ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; + + ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, + supported); +} + +static int smc_netdev_set_ecmd(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) +{ + u16 tmp; + unsigned int ioaddr = dev->base_addr; + + if (ecmd->base.speed != SPEED_10) + return -EINVAL; + if (ecmd->base.duplex != DUPLEX_HALF && + ecmd->base.duplex != DUPLEX_FULL) + return -EINVAL; + if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI) + return -EINVAL; + + if (ecmd->base.port == PORT_AUI) + smc_set_xcvr(dev, 1); + else + smc_set_xcvr(dev, 0); + + SMC_SELECT_BANK(0); + tmp = inw(ioaddr + TCR); + if (ecmd->base.duplex == DUPLEX_FULL) + tmp |= TCR_FDUPLX; + else + tmp &= ~TCR_FDUPLX; + outw(tmp, ioaddr + TCR); + + return 0; +} + +static int check_if_running(struct net_device *dev) +{ + if (!netif_running(dev)) + return -EINVAL; + return 0; +} + +static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); +} + +static int smc_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); + unsigned long flags; + + spin_lock_irqsave(&smc->lock, flags); + SMC_SELECT_BANK(3); + if (smc->cfg & CFG_MII_SELECT) + mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd); + else + smc_netdev_get_ecmd(dev, ecmd); + SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&smc->lock, flags); + return 0; +} + +static int smc_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); + int ret; + unsigned long flags; + + spin_lock_irqsave(&smc->lock, flags); + SMC_SELECT_BANK(3); + if (smc->cfg & CFG_MII_SELECT) + ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd); + else + ret = smc_netdev_set_ecmd(dev, ecmd); + SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&smc->lock, flags); + return ret; +} + +static u32 smc_get_link(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + unsigned int ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); + u32 ret; + unsigned long flags; + + spin_lock_irqsave(&smc->lock, flags); + SMC_SELECT_BANK(3); + ret = smc_link_ok(dev); + SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&smc->lock, flags); + return ret; +} + +static int smc_nway_reset(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + if (smc->cfg & CFG_MII_SELECT) { + unsigned int ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); + int res; + + SMC_SELECT_BANK(3); + res = mii_nway_restart(&smc->mii_if); + SMC_SELECT_BANK(saved_bank); + + return res; + } else + return -EOPNOTSUPP; +} + +static const struct ethtool_ops ethtool_ops = { + .begin = check_if_running, + .get_drvinfo = smc_get_drvinfo, + .get_link = smc_get_link, + .nway_reset = smc_nway_reset, + .get_link_ksettings = smc_get_link_ksettings, + .set_link_ksettings = smc_set_link_ksettings, +}; + +static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct smc_private *smc = netdev_priv(dev); + struct mii_ioctl_data *mii = if_mii(rq); + int rc = 0; + u16 saved_bank; + unsigned int ioaddr = dev->base_addr; + unsigned long flags; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irqsave(&smc->lock, flags); + saved_bank = inw(ioaddr + BANK_SELECT); + SMC_SELECT_BANK(3); + rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL); + SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&smc->lock, flags); + return rc; +} + +static const struct pcmcia_device_id smc91c92_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020), + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023), + PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb), + PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc), + PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1), + PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5), + PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9), + PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953), + PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a), + PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314), + PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc), + PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9), + PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d), + /* These conflict with other cards! */ + /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */ + /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids); + +static struct pcmcia_driver smc91c92_cs_driver = { + .owner = THIS_MODULE, + .name = "smc91c92_cs", + .probe = smc91c92_probe, + .remove = smc91c92_detach, + .id_table = smc91c92_ids, + .suspend = smc91c92_suspend, + .resume = smc91c92_resume, +}; +module_pcmcia_driver(smc91c92_cs_driver);