162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Written 1993-1998 by Donald Becker. 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci Copyright 1993 United States Government as represented by the 962306a36Sopenharmony_ci Director, National Security Agency. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 1262306a36Sopenharmony_ci Scyld Computing Corporation 1362306a36Sopenharmony_ci 410 Severn Ave., Suite 210 1462306a36Sopenharmony_ci Annapolis MD 21403 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci This driver uses the cards in the 8390-compatible mode. 1762306a36Sopenharmony_ci Most of the run-time complexity is handled by the generic code in 1862306a36Sopenharmony_ci 8390.c. The code in this file is responsible for 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci ultra_probe() Detecting and initializing the card. 2162306a36Sopenharmony_ci ultra_probe1() 2262306a36Sopenharmony_ci ultra_probe_isapnp() 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci ultra_open() The card-specific details of starting, stopping 2562306a36Sopenharmony_ci ultra_reset_8390() and resetting the 8390 NIC core. 2662306a36Sopenharmony_ci ultra_close() 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci ultra_block_input() Routines for reading and writing blocks of 2962306a36Sopenharmony_ci ultra_block_output() packet buffer memory. 3062306a36Sopenharmony_ci ultra_pio_input() 3162306a36Sopenharmony_ci ultra_pio_output() 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci This driver enables the shared memory only when doing the actual data 3462306a36Sopenharmony_ci transfers to avoid a bug in early version of the card that corrupted 3562306a36Sopenharmony_ci data transferred by a AHA1542. 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci This driver now supports the programmed-I/O (PIO) data transfer mode of 3862306a36Sopenharmony_ci the EtherEZ. It does not use the non-8390-compatible "Altego" mode. 3962306a36Sopenharmony_ci That support (if available) is in smc-ez.c. 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci Changelog: 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci Paul Gortmaker : multiple card support for module users. 4462306a36Sopenharmony_ci Donald Becker : 4/17/96 PIO support, minor potential problems avoided. 4562306a36Sopenharmony_ci Donald Becker : 6/6/96 correctly set auto-wrap bit. 4662306a36Sopenharmony_ci Alexander Sotirov : 1/20/01 Added support for ISAPnP cards 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci Note about the ISA PnP support: 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci This driver can not autoprobe for more than one SMC EtherEZ PnP card. 5162306a36Sopenharmony_ci You have to configure the second card manually through the /proc/isapnp 5262306a36Sopenharmony_ci interface and then load the module with an explicit io=0x___ option. 5362306a36Sopenharmony_ci*/ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const char version[] = 5662306a36Sopenharmony_ci "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <linux/module.h> 5962306a36Sopenharmony_ci#include <linux/kernel.h> 6062306a36Sopenharmony_ci#include <linux/errno.h> 6162306a36Sopenharmony_ci#include <linux/string.h> 6262306a36Sopenharmony_ci#include <linux/init.h> 6362306a36Sopenharmony_ci#include <linux/interrupt.h> 6462306a36Sopenharmony_ci#include <linux/isapnp.h> 6562306a36Sopenharmony_ci#include <linux/netdevice.h> 6662306a36Sopenharmony_ci#include <linux/etherdevice.h> 6762306a36Sopenharmony_ci#include <net/Space.h> 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#include <asm/io.h> 7062306a36Sopenharmony_ci#include <asm/irq.h> 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#include "8390.h" 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define DRV_NAME "smc-ultra" 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* A zero-terminated list of I/O addresses to be probed. */ 7762306a36Sopenharmony_cistatic unsigned int ultra_portlist[] __initdata = 7862306a36Sopenharmony_ci{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int ultra_probe1(struct net_device *dev, int ioaddr); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#ifdef __ISAPNP__ 8362306a36Sopenharmony_cistatic int ultra_probe_isapnp(struct net_device *dev); 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int ultra_open(struct net_device *dev); 8762306a36Sopenharmony_cistatic void ultra_reset_8390(struct net_device *dev); 8862306a36Sopenharmony_cistatic void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, 8962306a36Sopenharmony_ci int ring_page); 9062306a36Sopenharmony_cistatic void ultra_block_input(struct net_device *dev, int count, 9162306a36Sopenharmony_ci struct sk_buff *skb, int ring_offset); 9262306a36Sopenharmony_cistatic void ultra_block_output(struct net_device *dev, int count, 9362306a36Sopenharmony_ci const unsigned char *buf, const int start_page); 9462306a36Sopenharmony_cistatic void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, 9562306a36Sopenharmony_ci int ring_page); 9662306a36Sopenharmony_cistatic void ultra_pio_input(struct net_device *dev, int count, 9762306a36Sopenharmony_ci struct sk_buff *skb, int ring_offset); 9862306a36Sopenharmony_cistatic void ultra_pio_output(struct net_device *dev, int count, 9962306a36Sopenharmony_ci const unsigned char *buf, const int start_page); 10062306a36Sopenharmony_cistatic int ultra_close_card(struct net_device *dev); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#ifdef __ISAPNP__ 10362306a36Sopenharmony_cistatic struct isapnp_device_id ultra_device_ids[] __initdata = { 10462306a36Sopenharmony_ci { ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), 10562306a36Sopenharmony_ci ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), 10662306a36Sopenharmony_ci (long) "SMC EtherEZ (8416)" }, 10762306a36Sopenharmony_ci { } /* terminate list */ 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(isapnp, ultra_device_ids); 11162306a36Sopenharmony_ci#endif 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic u32 ultra_msg_enable; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define START_PG 0x00 /* First page of TX buffer */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */ 11862306a36Sopenharmony_ci#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */ 11962306a36Sopenharmony_ci#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */ 12062306a36Sopenharmony_ci#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */ 12162306a36Sopenharmony_ci#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */ 12262306a36Sopenharmony_ci#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */ 12362306a36Sopenharmony_ci#define ULTRA_IO_EXTENT 32 12462306a36Sopenharmony_ci#define EN0_ERWCNT 0x08 /* Early receive warning count. */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 12762306a36Sopenharmony_cistatic void ultra_poll(struct net_device *dev) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci disable_irq(dev->irq); 13062306a36Sopenharmony_ci ei_interrupt(dev->irq, dev); 13162306a36Sopenharmony_ci enable_irq(dev->irq); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci#endif 13462306a36Sopenharmony_ci/* Probe for the Ultra. This looks like a 8013 with the station 13562306a36Sopenharmony_ci address PROM at I/O ports <base>+8 to <base>+13, with a checksum 13662306a36Sopenharmony_ci following. 13762306a36Sopenharmony_ci*/ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int __init do_ultra_probe(struct net_device *dev) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci int i; 14262306a36Sopenharmony_ci int base_addr = dev->base_addr; 14362306a36Sopenharmony_ci int irq = dev->irq; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (base_addr > 0x1ff) /* Check a single specified location. */ 14662306a36Sopenharmony_ci return ultra_probe1(dev, base_addr); 14762306a36Sopenharmony_ci else if (base_addr != 0) /* Don't probe at all. */ 14862306a36Sopenharmony_ci return -ENXIO; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#ifdef __ISAPNP__ 15162306a36Sopenharmony_ci /* Look for any installed ISAPnP cards */ 15262306a36Sopenharmony_ci if (isapnp_present() && (ultra_probe_isapnp(dev) == 0)) 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci for (i = 0; ultra_portlist[i]; i++) { 15762306a36Sopenharmony_ci dev->irq = irq; 15862306a36Sopenharmony_ci if (ultra_probe1(dev, ultra_portlist[i]) == 0) 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return -ENODEV; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#ifndef MODULE 16662306a36Sopenharmony_cistruct net_device * __init ultra_probe(int unit) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct net_device *dev = alloc_ei_netdev(); 16962306a36Sopenharmony_ci int err; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!dev) 17262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci sprintf(dev->name, "eth%d", unit); 17562306a36Sopenharmony_ci netdev_boot_setup_check(dev); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci err = do_ultra_probe(dev); 17862306a36Sopenharmony_ci if (err) 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci return dev; 18162306a36Sopenharmony_ciout: 18262306a36Sopenharmony_ci free_netdev(dev); 18362306a36Sopenharmony_ci return ERR_PTR(err); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct net_device_ops ultra_netdev_ops = { 18862306a36Sopenharmony_ci .ndo_open = ultra_open, 18962306a36Sopenharmony_ci .ndo_stop = ultra_close_card, 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci .ndo_start_xmit = ei_start_xmit, 19262306a36Sopenharmony_ci .ndo_tx_timeout = ei_tx_timeout, 19362306a36Sopenharmony_ci .ndo_get_stats = ei_get_stats, 19462306a36Sopenharmony_ci .ndo_set_rx_mode = ei_set_multicast_list, 19562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 19662306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 19762306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 19862306a36Sopenharmony_ci .ndo_poll_controller = ultra_poll, 19962306a36Sopenharmony_ci#endif 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int __init ultra_probe1(struct net_device *dev, int ioaddr) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci int i, retval; 20562306a36Sopenharmony_ci int checksum = 0; 20662306a36Sopenharmony_ci u8 macaddr[ETH_ALEN]; 20762306a36Sopenharmony_ci const char *model_name; 20862306a36Sopenharmony_ci unsigned char eeprom_irq = 0; 20962306a36Sopenharmony_ci static unsigned version_printed; 21062306a36Sopenharmony_ci /* Values from various config regs. */ 21162306a36Sopenharmony_ci unsigned char num_pages, irqreg, addr, piomode; 21262306a36Sopenharmony_ci unsigned char idreg = inb(ioaddr + 7); 21362306a36Sopenharmony_ci unsigned char reg4 = inb(ioaddr + 4) & 0x7f; 21462306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) 21762306a36Sopenharmony_ci return -EBUSY; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Check the ID nibble. */ 22062306a36Sopenharmony_ci if ((idreg & 0xF0) != 0x20 /* SMC Ultra */ 22162306a36Sopenharmony_ci && (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */ 22262306a36Sopenharmony_ci retval = -ENODEV; 22362306a36Sopenharmony_ci goto out; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Select the station address register set. */ 22762306a36Sopenharmony_ci outb(reg4, ioaddr + 4); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci for (i = 0; i < 8; i++) 23062306a36Sopenharmony_ci checksum += inb(ioaddr + 8 + i); 23162306a36Sopenharmony_ci if ((checksum & 0xff) != 0xFF) { 23262306a36Sopenharmony_ci retval = -ENODEV; 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) 23762306a36Sopenharmony_ci netdev_info(dev, version); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci for (i = 0; i < 6; i++) 24262306a36Sopenharmony_ci macaddr[i] = inb(ioaddr + 8 + i); 24362306a36Sopenharmony_ci eth_hw_addr_set(dev, macaddr); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci netdev_info(dev, "%s at %#3x, %pM", model_name, 24662306a36Sopenharmony_ci ioaddr, dev->dev_addr); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Switch from the station address to the alternate register set and 24962306a36Sopenharmony_ci read the useful registers there. */ 25062306a36Sopenharmony_ci outb(0x80 | reg4, ioaddr + 4); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ 25362306a36Sopenharmony_ci outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); 25462306a36Sopenharmony_ci piomode = inb(ioaddr + 0x8); 25562306a36Sopenharmony_ci addr = inb(ioaddr + 0xb); 25662306a36Sopenharmony_ci irqreg = inb(ioaddr + 0xd); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Switch back to the station address register set so that the MS-DOS driver 25962306a36Sopenharmony_ci can find the card after a warm boot. */ 26062306a36Sopenharmony_ci outb(reg4, ioaddr + 4); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (dev->irq < 2) { 26362306a36Sopenharmony_ci unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; 26462306a36Sopenharmony_ci int irq; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* The IRQ bits are split. */ 26762306a36Sopenharmony_ci irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)]; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (irq == 0) { 27062306a36Sopenharmony_ci pr_cont(", failed to detect IRQ line.\n"); 27162306a36Sopenharmony_ci retval = -EAGAIN; 27262306a36Sopenharmony_ci goto out; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci dev->irq = irq; 27562306a36Sopenharmony_ci eeprom_irq = 1; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* The 8390 isn't at the base address, so fake the offset */ 27962306a36Sopenharmony_ci dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci { 28262306a36Sopenharmony_ci static const int addr_tbl[4] = { 28362306a36Sopenharmony_ci 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000 28462306a36Sopenharmony_ci }; 28562306a36Sopenharmony_ci static const short num_pages_tbl[4] = { 28662306a36Sopenharmony_ci 0x20, 0x40, 0x80, 0xff 28762306a36Sopenharmony_ci }; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ; 29062306a36Sopenharmony_ci num_pages = num_pages_tbl[(addr >> 4) & 3]; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ei_status.name = model_name; 29462306a36Sopenharmony_ci ei_status.word16 = 1; 29562306a36Sopenharmony_ci ei_status.tx_start_page = START_PG; 29662306a36Sopenharmony_ci ei_status.rx_start_page = START_PG + TX_PAGES; 29762306a36Sopenharmony_ci ei_status.stop_page = num_pages; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256); 30062306a36Sopenharmony_ci if (!ei_status.mem) { 30162306a36Sopenharmony_ci pr_cont(", failed to ioremap.\n"); 30262306a36Sopenharmony_ci retval = -ENOMEM; 30362306a36Sopenharmony_ci goto out; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (piomode) { 30962306a36Sopenharmony_ci pr_cont(", %s IRQ %d programmed-I/O mode.\n", 31062306a36Sopenharmony_ci eeprom_irq ? "EEPROM" : "assigned ", dev->irq); 31162306a36Sopenharmony_ci ei_status.block_input = &ultra_pio_input; 31262306a36Sopenharmony_ci ei_status.block_output = &ultra_pio_output; 31362306a36Sopenharmony_ci ei_status.get_8390_hdr = &ultra_pio_get_hdr; 31462306a36Sopenharmony_ci } else { 31562306a36Sopenharmony_ci pr_cont(", %s IRQ %d memory %#lx-%#lx.\n", 31662306a36Sopenharmony_ci eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start, 31762306a36Sopenharmony_ci dev->mem_end-1); 31862306a36Sopenharmony_ci ei_status.block_input = &ultra_block_input; 31962306a36Sopenharmony_ci ei_status.block_output = &ultra_block_output; 32062306a36Sopenharmony_ci ei_status.get_8390_hdr = &ultra_get_8390_hdr; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci ei_status.reset_8390 = &ultra_reset_8390; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci dev->netdev_ops = &ultra_netdev_ops; 32562306a36Sopenharmony_ci NS8390_init(dev, 0); 32662306a36Sopenharmony_ci ei_local->msg_enable = ultra_msg_enable; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci retval = register_netdev(dev); 32962306a36Sopenharmony_ci if (retval) 33062306a36Sopenharmony_ci goto out; 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ciout: 33362306a36Sopenharmony_ci release_region(ioaddr, ULTRA_IO_EXTENT); 33462306a36Sopenharmony_ci return retval; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci#ifdef __ISAPNP__ 33862306a36Sopenharmony_cistatic int __init ultra_probe_isapnp(struct net_device *dev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci int i; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; ultra_device_ids[i].vendor != 0; i++) { 34362306a36Sopenharmony_ci struct pnp_dev *idev = NULL; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci while ((idev = pnp_find_dev(NULL, 34662306a36Sopenharmony_ci ultra_device_ids[i].vendor, 34762306a36Sopenharmony_ci ultra_device_ids[i].function, 34862306a36Sopenharmony_ci idev))) { 34962306a36Sopenharmony_ci /* Avoid already found cards from previous calls */ 35062306a36Sopenharmony_ci if (pnp_device_attach(idev) < 0) 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci if (pnp_activate_dev(idev) < 0) { 35362306a36Sopenharmony_ci __again: 35462306a36Sopenharmony_ci pnp_device_detach(idev); 35562306a36Sopenharmony_ci continue; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci /* if no io and irq, search for next */ 35862306a36Sopenharmony_ci if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) 35962306a36Sopenharmony_ci goto __again; 36062306a36Sopenharmony_ci /* found it */ 36162306a36Sopenharmony_ci dev->base_addr = pnp_port_start(idev, 0); 36262306a36Sopenharmony_ci dev->irq = pnp_irq(idev, 0); 36362306a36Sopenharmony_ci netdev_info(dev, 36462306a36Sopenharmony_ci "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", 36562306a36Sopenharmony_ci (char *) ultra_device_ids[i].driver_data, 36662306a36Sopenharmony_ci dev->base_addr, dev->irq); 36762306a36Sopenharmony_ci if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */ 36862306a36Sopenharmony_ci netdev_err(dev, 36962306a36Sopenharmony_ci "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", 37062306a36Sopenharmony_ci dev->base_addr); 37162306a36Sopenharmony_ci pnp_device_detach(idev); 37262306a36Sopenharmony_ci return -ENXIO; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci ei_status.priv = (unsigned long)idev; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci if (!idev) 37862306a36Sopenharmony_ci continue; 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return -ENODEV; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int 38762306a36Sopenharmony_ciultra_open(struct net_device *dev) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci int retval; 39062306a36Sopenharmony_ci int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ 39162306a36Sopenharmony_ci unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, 39262306a36Sopenharmony_ci 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); 39562306a36Sopenharmony_ci if (retval) 39662306a36Sopenharmony_ci return retval; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci outb(0x00, ioaddr); /* Disable shared memory for safety. */ 39962306a36Sopenharmony_ci outb(0x80, ioaddr + 5); 40062306a36Sopenharmony_ci /* Set the IRQ line. */ 40162306a36Sopenharmony_ci outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); 40262306a36Sopenharmony_ci outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); 40362306a36Sopenharmony_ci outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (ei_status.block_input == &ultra_pio_input) { 40662306a36Sopenharmony_ci outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */ 40762306a36Sopenharmony_ci outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */ 40862306a36Sopenharmony_ci } else 40962306a36Sopenharmony_ci outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */ 41062306a36Sopenharmony_ci /* Set the early receive warning level in window 0 high enough not 41162306a36Sopenharmony_ci to receive ERW interrupts. */ 41262306a36Sopenharmony_ci outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); 41362306a36Sopenharmony_ci outb(0xff, dev->base_addr + EN0_ERWCNT); 41462306a36Sopenharmony_ci ei_open(dev); 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void 41962306a36Sopenharmony_ciultra_reset_8390(struct net_device *dev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */ 42262306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci outb(ULTRA_RESET, cmd_port); 42562306a36Sopenharmony_ci netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies); 42662306a36Sopenharmony_ci ei_status.txing = 0; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci outb(0x00, cmd_port); /* Disable shared memory for safety. */ 42962306a36Sopenharmony_ci outb(0x80, cmd_port + 5); 43062306a36Sopenharmony_ci if (ei_status.block_input == &ultra_pio_input) 43162306a36Sopenharmony_ci outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */ 43262306a36Sopenharmony_ci else 43362306a36Sopenharmony_ci outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */ 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci netif_dbg(ei_local, hw, dev, "reset done\n"); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/* Grab the 8390 specific header. Similar to the block_input routine, but 43962306a36Sopenharmony_ci we don't need to be concerned with ring wrap as the header will be at 44062306a36Sopenharmony_ci the start of a page, so we optimize accordingly. */ 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void 44362306a36Sopenharmony_ciultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */ 44862306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 44962306a36Sopenharmony_ci /* Officially this is what we are doing, but the readl() is faster */ 45062306a36Sopenharmony_ci /* unfortunately it isn't endian aware of the struct */ 45162306a36Sopenharmony_ci memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); 45262306a36Sopenharmony_ci hdr->count = le16_to_cpu(hdr->count); 45362306a36Sopenharmony_ci#else 45462306a36Sopenharmony_ci ((unsigned int*)hdr)[0] = readl(hdr_start); 45562306a36Sopenharmony_ci#endif 45662306a36Sopenharmony_ci outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */ 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* Block input and output are easy on shared memory ethercards, the only 46062306a36Sopenharmony_ci complication is when the ring buffer wraps. */ 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void 46362306a36Sopenharmony_ciultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* Enable shared memory. */ 46862306a36Sopenharmony_ci outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (ring_offset + count > ei_status.stop_page*256) { 47162306a36Sopenharmony_ci /* We must wrap the input move. */ 47262306a36Sopenharmony_ci int semi_count = ei_status.stop_page*256 - ring_offset; 47362306a36Sopenharmony_ci memcpy_fromio(skb->data, xfer_start, semi_count); 47462306a36Sopenharmony_ci count -= semi_count; 47562306a36Sopenharmony_ci memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci memcpy_fromio(skb->data, xfer_start, count); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void 48462306a36Sopenharmony_ciultra_block_output(struct net_device *dev, int count, const unsigned char *buf, 48562306a36Sopenharmony_ci int start_page) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Enable shared memory. */ 49062306a36Sopenharmony_ci outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci memcpy_toio(shmem, buf, count); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/* The identical operations for programmed I/O cards. 49862306a36Sopenharmony_ci The PIO model is trivial to use: the 16 bit start address is written 49962306a36Sopenharmony_ci byte-sequentially to IOPA, with no intervening I/O operations, and the 50062306a36Sopenharmony_ci data is read or written to the IOPD data port. 50162306a36Sopenharmony_ci The only potential complication is that the address register is shared 50262306a36Sopenharmony_ci and must be always be rewritten between each read/write direction change. 50362306a36Sopenharmony_ci This is no problem for us, as the 8390 code ensures that we are single 50462306a36Sopenharmony_ci threaded. */ 50562306a36Sopenharmony_cistatic void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, 50662306a36Sopenharmony_ci int ring_page) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ 50962306a36Sopenharmony_ci outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ 51062306a36Sopenharmony_ci outb(ring_page, ioaddr + IOPA); 51162306a36Sopenharmony_ci insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void ultra_pio_input(struct net_device *dev, int count, 51562306a36Sopenharmony_ci struct sk_buff *skb, int ring_offset) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ 51862306a36Sopenharmony_ci char *buf = skb->data; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* For now set the address again, although it should already be correct. */ 52162306a36Sopenharmony_ci outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */ 52262306a36Sopenharmony_ci outb(ring_offset >> 8, ioaddr + IOPA); 52362306a36Sopenharmony_ci /* We know skbuffs are padded to at least word alignment. */ 52462306a36Sopenharmony_ci insw(ioaddr + IOPD, buf, (count+1)>>1); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_cistatic void ultra_pio_output(struct net_device *dev, int count, 52762306a36Sopenharmony_ci const unsigned char *buf, const int start_page) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ 53062306a36Sopenharmony_ci outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ 53162306a36Sopenharmony_ci outb(start_page, ioaddr + IOPA); 53262306a36Sopenharmony_ci /* An extra odd byte is OK here as well. */ 53362306a36Sopenharmony_ci outsw(ioaddr + IOPD, buf, (count+1)>>1); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int 53762306a36Sopenharmony_ciultra_close_card(struct net_device *dev) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */ 54062306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci netif_stop_queue(dev); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci outb(0x00, ioaddr + 6); /* Disable interrupts. */ 54762306a36Sopenharmony_ci free_irq(dev->irq, dev); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci NS8390_init(dev, 0); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* We should someday disable shared memory and change to 8-bit mode 55262306a36Sopenharmony_ci "just in case"... */ 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci#ifdef MODULE 55962306a36Sopenharmony_ci#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */ 56062306a36Sopenharmony_cistatic struct net_device *dev_ultra[MAX_ULTRA_CARDS]; 56162306a36Sopenharmony_cistatic int io[MAX_ULTRA_CARDS]; 56262306a36Sopenharmony_cistatic int irq[MAX_ULTRA_CARDS]; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0); 56562306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 56662306a36Sopenharmony_cimodule_param_named(msg_enable, ultra_msg_enable, uint, 0444); 56762306a36Sopenharmony_ciMODULE_PARM_DESC(io, "I/O base address(es)"); 56862306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); 56962306a36Sopenharmony_ciMODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); 57062306a36Sopenharmony_ciMODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); 57162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* This is set up so that only a single autoprobe takes place per call. 57462306a36Sopenharmony_ciISA device autoprobes on a running machine are not recommended. */ 57562306a36Sopenharmony_cistatic int __init ultra_init_module(void) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct net_device *dev; 57862306a36Sopenharmony_ci int this_dev, found = 0; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { 58162306a36Sopenharmony_ci if (io[this_dev] == 0) { 58262306a36Sopenharmony_ci if (this_dev != 0) break; /* only autoprobe 1st one */ 58362306a36Sopenharmony_ci printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci dev = alloc_ei_netdev(); 58662306a36Sopenharmony_ci if (!dev) 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci dev->irq = irq[this_dev]; 58962306a36Sopenharmony_ci dev->base_addr = io[this_dev]; 59062306a36Sopenharmony_ci if (do_ultra_probe(dev) == 0) { 59162306a36Sopenharmony_ci dev_ultra[found++] = dev; 59262306a36Sopenharmony_ci continue; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci free_netdev(dev); 59562306a36Sopenharmony_ci printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci if (found) 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci return -ENXIO; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_cimodule_init(ultra_init_module); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void cleanup_card(struct net_device *dev) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci /* NB: ultra_close_card() does free_irq */ 60762306a36Sopenharmony_ci#ifdef __ISAPNP__ 60862306a36Sopenharmony_ci struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv; 60962306a36Sopenharmony_ci if (idev) 61062306a36Sopenharmony_ci pnp_device_detach(idev); 61162306a36Sopenharmony_ci#endif 61262306a36Sopenharmony_ci release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT); 61362306a36Sopenharmony_ci iounmap(ei_status.mem); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void __exit ultra_cleanup_module(void) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci int this_dev; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { 62162306a36Sopenharmony_ci struct net_device *dev = dev_ultra[this_dev]; 62262306a36Sopenharmony_ci if (dev) { 62362306a36Sopenharmony_ci unregister_netdev(dev); 62462306a36Sopenharmony_ci cleanup_card(dev); 62562306a36Sopenharmony_ci free_netdev(dev); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_cimodule_exit(ultra_cleanup_module); 63062306a36Sopenharmony_ci#endif /* MODULE */ 631