162306a36Sopenharmony_ci/* atp.c: Attached (pocket) ethernet adapter driver for linux. */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci This is a driver for commonly OEM pocket (parallel port) 462306a36Sopenharmony_ci ethernet adapters based on the Realtek RTL8002 and RTL8012 chips. 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Written 1993-2000 by Donald Becker. 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci This software may be used and distributed according to the terms of 962306a36Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 1062306a36Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 1162306a36Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 1262306a36Sopenharmony_ci a complete program and may only be used when the entire operating 1362306a36Sopenharmony_ci system is licensed under the GPL. 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci Copyright 1993 United States Government as represented by the Director, 1662306a36Sopenharmony_ci National Security Agency. Copyright 1994-2000 retained by the original 1762306a36Sopenharmony_ci author, Donald Becker. The timer-based reset code was supplied in 1995 1862306a36Sopenharmony_ci by Bill Carlson, wwc@super.org. 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 2162306a36Sopenharmony_ci Scyld Computing Corporation 2262306a36Sopenharmony_ci 410 Severn Ave., Suite 210 2362306a36Sopenharmony_ci Annapolis MD 21403 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci Support information and updates available at 2662306a36Sopenharmony_ci http://www.scyld.com/network/atp.html 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci Modular support/softnet added by Alan Cox. 3062306a36Sopenharmony_ci _bit abuse fixed up by Alan Cox 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci*/ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic const char version[] = 3562306a36Sopenharmony_ci"atp.c:v1.09=ac 2002/10/01 Donald Becker <becker@scyld.com>\n"; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* The user-configurable values. 3862306a36Sopenharmony_ci These may be modified when a driver module is loaded.*/ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ 4162306a36Sopenharmony_ci#define net_debug debug 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ 4462306a36Sopenharmony_cistatic int max_interrupt_work = 15; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define NUM_UNITS 2 4762306a36Sopenharmony_ci/* The standard set of ISA module parameters. */ 4862306a36Sopenharmony_cistatic int io[NUM_UNITS]; 4962306a36Sopenharmony_cistatic int irq[NUM_UNITS]; 5062306a36Sopenharmony_cistatic int xcvr[NUM_UNITS]; /* The data transfer mode. */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Operational parameters that are set at compile time. */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 5562306a36Sopenharmony_ci#define TX_TIMEOUT (400*HZ/1000) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* 5862306a36Sopenharmony_ci This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket 5962306a36Sopenharmony_ci ethernet adapter. This is a common low-cost OEM pocket ethernet 6062306a36Sopenharmony_ci adapter, sold under many names. 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci Sources: 6362306a36Sopenharmony_ci This driver was written from the packet driver assembly code provided by 6462306a36Sopenharmony_ci Vincent Bono of AT-Lan-Tec. Ever try to figure out how a complicated 6562306a36Sopenharmony_ci device works just from the assembly code? It ain't pretty. The following 6662306a36Sopenharmony_ci description is written based on guesses and writing lots of special-purpose 6762306a36Sopenharmony_ci code to test my theorized operation. 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci In 1997 Realtek made available the documentation for the second generation 7062306a36Sopenharmony_ci RTL8012 chip, which has lead to several driver improvements. 7162306a36Sopenharmony_ci http://www.realtek.com.tw/ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci Theory of Operation 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci The RTL8002 adapter seems to be built around a custom spin of the SEEQ 7662306a36Sopenharmony_ci controller core. It probably has a 16K or 64K internal packet buffer, of 7762306a36Sopenharmony_ci which the first 4K is devoted to transmit and the rest to receive. 7862306a36Sopenharmony_ci The controller maintains the queue of received packet and the packet buffer 7962306a36Sopenharmony_ci access pointer internally, with only 'reset to beginning' and 'skip to next 8062306a36Sopenharmony_ci packet' commands visible. The transmit packet queue holds two (or more?) 8162306a36Sopenharmony_ci packets: both 'retransmit this packet' (due to collision) and 'transmit next 8262306a36Sopenharmony_ci packet' commands must be started by hand. 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci The station address is stored in a standard bit-serial EEPROM which must be 8562306a36Sopenharmony_ci read (ughh) by the device driver. (Provisions have been made for 8662306a36Sopenharmony_ci substituting a 74S288 PROM, but I haven't gotten reports of any models 8762306a36Sopenharmony_ci using it.) Unlike built-in devices, a pocket adapter can temporarily lose 8862306a36Sopenharmony_ci power without indication to the device driver. The major effect is that 8962306a36Sopenharmony_ci the station address, receive filter (promiscuous, etc.) and transceiver 9062306a36Sopenharmony_ci must be reset. 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci The controller itself has 16 registers, some of which use only the lower 9362306a36Sopenharmony_ci bits. The registers are read and written 4 bits at a time. The four bit 9462306a36Sopenharmony_ci register address is presented on the data lines along with a few additional 9562306a36Sopenharmony_ci timing and control bits. The data is then read from status port or written 9662306a36Sopenharmony_ci to the data port. 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci Correction: the controller has two banks of 16 registers. The second 9962306a36Sopenharmony_ci bank contains only the multicast filter table (now used) and the EEPROM 10062306a36Sopenharmony_ci access registers. 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci Since the bulk data transfer of the actual packets through the slow 10362306a36Sopenharmony_ci parallel port dominates the driver's running time, four distinct data 10462306a36Sopenharmony_ci (non-register) transfer modes are provided by the adapter, two in each 10562306a36Sopenharmony_ci direction. In the first mode timing for the nibble transfers is 10662306a36Sopenharmony_ci provided through the data port. In the second mode the same timing is 10762306a36Sopenharmony_ci provided through the control port. In either case the data is read from 10862306a36Sopenharmony_ci the status port and written to the data port, just as it is accessing 10962306a36Sopenharmony_ci registers. 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci In addition to the basic data transfer methods, several more are modes are 11262306a36Sopenharmony_ci created by adding some delay by doing multiple reads of the data to allow 11362306a36Sopenharmony_ci it to stabilize. This delay seems to be needed on most machines. 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci The data transfer mode is stored in the 'dev->if_port' field. Its default 11662306a36Sopenharmony_ci value is '4'. It may be overridden at boot-time using the third parameter 11762306a36Sopenharmony_ci to the "ether=..." initialization. 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci The header file <atp.h> provides inline functions that encapsulate the 12062306a36Sopenharmony_ci register and data access methods. These functions are hand-tuned to 12162306a36Sopenharmony_ci generate reasonable object code. This header file also documents my 12262306a36Sopenharmony_ci interpretations of the device registers. 12362306a36Sopenharmony_ci*/ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#include <linux/kernel.h> 12662306a36Sopenharmony_ci#include <linux/module.h> 12762306a36Sopenharmony_ci#include <linux/types.h> 12862306a36Sopenharmony_ci#include <linux/fcntl.h> 12962306a36Sopenharmony_ci#include <linux/interrupt.h> 13062306a36Sopenharmony_ci#include <linux/ioport.h> 13162306a36Sopenharmony_ci#include <linux/in.h> 13262306a36Sopenharmony_ci#include <linux/string.h> 13362306a36Sopenharmony_ci#include <linux/errno.h> 13462306a36Sopenharmony_ci#include <linux/init.h> 13562306a36Sopenharmony_ci#include <linux/crc32.h> 13662306a36Sopenharmony_ci#include <linux/netdevice.h> 13762306a36Sopenharmony_ci#include <linux/etherdevice.h> 13862306a36Sopenharmony_ci#include <linux/skbuff.h> 13962306a36Sopenharmony_ci#include <linux/spinlock.h> 14062306a36Sopenharmony_ci#include <linux/delay.h> 14162306a36Sopenharmony_ci#include <linux/bitops.h> 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#include <asm/io.h> 14462306a36Sopenharmony_ci#include <asm/dma.h> 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#include "atp.h" 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>"); 14962306a36Sopenharmony_ciMODULE_DESCRIPTION("RealTek RTL8002/8012 parallel port Ethernet driver"); 15062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cimodule_param(max_interrupt_work, int, 0); 15362306a36Sopenharmony_cimodule_param(debug, int, 0); 15462306a36Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0); 15562306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0); 15662306a36Sopenharmony_cimodule_param_array(xcvr, int, NULL, 0); 15762306a36Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "ATP maximum events handled per interrupt"); 15862306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "ATP debug level (0-7)"); 15962306a36Sopenharmony_ciMODULE_PARM_DESC(io, "ATP I/O base address(es)"); 16062306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "ATP IRQ number(s)"); 16162306a36Sopenharmony_ciMODULE_PARM_DESC(xcvr, "ATP transceiver(s) (0=internal, 1=external)"); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* The number of low I/O ports used by the ethercard. */ 16462306a36Sopenharmony_ci#define ETHERCARD_TOTAL_SIZE 3 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* Sequence to switch an 8012 from printer mux to ethernet mode. */ 16762306a36Sopenharmony_cistatic char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct net_local { 17062306a36Sopenharmony_ci spinlock_t lock; 17162306a36Sopenharmony_ci struct net_device *next_module; 17262306a36Sopenharmony_ci struct timer_list timer; /* Media selection timer. */ 17362306a36Sopenharmony_ci struct net_device *dev; /* Timer dev. */ 17462306a36Sopenharmony_ci unsigned long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ 17562306a36Sopenharmony_ci int saved_tx_size; 17662306a36Sopenharmony_ci unsigned int tx_unit_busy:1; 17762306a36Sopenharmony_ci unsigned char re_tx, /* Number of packet retransmissions. */ 17862306a36Sopenharmony_ci addr_mode, /* Current Rx filter e.g. promiscuous, etc. */ 17962306a36Sopenharmony_ci pac_cnt_in_tx_buf; 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci/* This code, written by wwc@super.org, resets the adapter every 18362306a36Sopenharmony_ci TIMED_CHECKER ticks. This recovers from an unknown error which 18462306a36Sopenharmony_ci hangs the device. */ 18562306a36Sopenharmony_ci#define TIMED_CHECKER (HZ/4) 18662306a36Sopenharmony_ci#ifdef TIMED_CHECKER 18762306a36Sopenharmony_ci#include <linux/timer.h> 18862306a36Sopenharmony_cistatic void atp_timed_checker(struct timer_list *t); 18962306a36Sopenharmony_ci#endif 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* Index to functions, as function prototypes. */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic int atp_probe1(long ioaddr); 19462306a36Sopenharmony_cistatic void get_node_ID(struct net_device *dev); 19562306a36Sopenharmony_cistatic unsigned short eeprom_op(long ioaddr, unsigned int cmd); 19662306a36Sopenharmony_cistatic int net_open(struct net_device *dev); 19762306a36Sopenharmony_cistatic void hardware_init(struct net_device *dev); 19862306a36Sopenharmony_cistatic void write_packet(long ioaddr, int length, unsigned char *packet, int pad, int mode); 19962306a36Sopenharmony_cistatic void trigger_send(long ioaddr, int length); 20062306a36Sopenharmony_cistatic netdev_tx_t atp_send_packet(struct sk_buff *skb, 20162306a36Sopenharmony_ci struct net_device *dev); 20262306a36Sopenharmony_cistatic irqreturn_t atp_interrupt(int irq, void *dev_id); 20362306a36Sopenharmony_cistatic void net_rx(struct net_device *dev); 20462306a36Sopenharmony_cistatic void read_block(long ioaddr, int length, unsigned char *buffer, int data_mode); 20562306a36Sopenharmony_cistatic int net_close(struct net_device *dev); 20662306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev); 20762306a36Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* A list of all installed ATP devices, for removing the driver module. */ 21162306a36Sopenharmony_cistatic struct net_device *root_atp_dev; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* Check for a network adapter of this type, and return '0' iff one exists. 21462306a36Sopenharmony_ci If dev->base_addr == 0, probe all likely locations. 21562306a36Sopenharmony_ci If dev->base_addr == 1, always return failure. 21662306a36Sopenharmony_ci If dev->base_addr == 2, allocate space for the device and return success 21762306a36Sopenharmony_ci (detachable devices only). 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci FIXME: we should use the parport layer for this 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_cistatic int __init atp_init(void) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int *port, ports[] = {0x378, 0x278, 0x3bc, 0}; 22462306a36Sopenharmony_ci int base_addr = io[0]; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (base_addr > 0x1ff) /* Check a single specified location. */ 22762306a36Sopenharmony_ci return atp_probe1(base_addr); 22862306a36Sopenharmony_ci else if (base_addr == 1) /* Don't probe at all. */ 22962306a36Sopenharmony_ci return -ENXIO; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (port = ports; *port; port++) { 23262306a36Sopenharmony_ci long ioaddr = *port; 23362306a36Sopenharmony_ci outb(0x57, ioaddr + PAR_DATA); 23462306a36Sopenharmony_ci if (inb(ioaddr + PAR_DATA) != 0x57) 23562306a36Sopenharmony_ci continue; 23662306a36Sopenharmony_ci if (atp_probe1(ioaddr) == 0) 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return -ENODEV; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic const struct net_device_ops atp_netdev_ops = { 24462306a36Sopenharmony_ci .ndo_open = net_open, 24562306a36Sopenharmony_ci .ndo_stop = net_close, 24662306a36Sopenharmony_ci .ndo_start_xmit = atp_send_packet, 24762306a36Sopenharmony_ci .ndo_set_rx_mode = set_rx_mode, 24862306a36Sopenharmony_ci .ndo_tx_timeout = tx_timeout, 24962306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 25062306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 25162306a36Sopenharmony_ci}; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int __init atp_probe1(long ioaddr) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct net_device *dev = NULL; 25662306a36Sopenharmony_ci struct net_local *lp; 25762306a36Sopenharmony_ci int saved_ctrl_reg, status, i; 25862306a36Sopenharmony_ci int res; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci outb(0xff, ioaddr + PAR_DATA); 26162306a36Sopenharmony_ci /* Save the original value of the Control register, in case we guessed 26262306a36Sopenharmony_ci wrong. */ 26362306a36Sopenharmony_ci saved_ctrl_reg = inb(ioaddr + PAR_CONTROL); 26462306a36Sopenharmony_ci if (net_debug > 3) 26562306a36Sopenharmony_ci printk("atp: Control register was %#2.2x.\n", saved_ctrl_reg); 26662306a36Sopenharmony_ci /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */ 26762306a36Sopenharmony_ci outb(0x04, ioaddr + PAR_CONTROL); 26862306a36Sopenharmony_ci#ifndef final_version 26962306a36Sopenharmony_ci if (net_debug > 3) { 27062306a36Sopenharmony_ci /* Turn off the printer multiplexer on the 8012. */ 27162306a36Sopenharmony_ci for (i = 0; i < 8; i++) 27262306a36Sopenharmony_ci outb(mux_8012[i], ioaddr + PAR_DATA); 27362306a36Sopenharmony_ci write_reg(ioaddr, MODSEL, 0x00); 27462306a36Sopenharmony_ci printk("atp: Registers are "); 27562306a36Sopenharmony_ci for (i = 0; i < 32; i++) 27662306a36Sopenharmony_ci printk(" %2.2x", read_nibble(ioaddr, i)); 27762306a36Sopenharmony_ci printk(".\n"); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci#endif 28062306a36Sopenharmony_ci /* Turn off the printer multiplexer on the 8012. */ 28162306a36Sopenharmony_ci for (i = 0; i < 8; i++) 28262306a36Sopenharmony_ci outb(mux_8012[i], ioaddr + PAR_DATA); 28362306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RESET); 28462306a36Sopenharmony_ci /* udelay() here? */ 28562306a36Sopenharmony_ci status = read_nibble(ioaddr, CMR1); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (net_debug > 3) { 28862306a36Sopenharmony_ci printk(KERN_DEBUG "atp: Status nibble was %#2.2x..", status); 28962306a36Sopenharmony_ci for (i = 0; i < 32; i++) 29062306a36Sopenharmony_ci printk(" %2.2x", read_nibble(ioaddr, i)); 29162306a36Sopenharmony_ci printk("\n"); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if ((status & 0x78) != 0x08) { 29562306a36Sopenharmony_ci /* The pocket adapter probe failed, restore the control register. */ 29662306a36Sopenharmony_ci outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); 29762306a36Sopenharmony_ci return -ENODEV; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci status = read_nibble(ioaddr, CMR2_h); 30062306a36Sopenharmony_ci if ((status & 0x78) != 0x10) { 30162306a36Sopenharmony_ci outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); 30262306a36Sopenharmony_ci return -ENODEV; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct net_local)); 30662306a36Sopenharmony_ci if (!dev) 30762306a36Sopenharmony_ci return -ENOMEM; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Find the IRQ used by triggering an interrupt. */ 31062306a36Sopenharmony_ci write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */ 31162306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */ 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Omit autoIRQ routine for now. Use "table lookup" instead. Uhgggh. */ 31462306a36Sopenharmony_ci if (irq[0]) 31562306a36Sopenharmony_ci dev->irq = irq[0]; 31662306a36Sopenharmony_ci else if (ioaddr == 0x378) 31762306a36Sopenharmony_ci dev->irq = 7; 31862306a36Sopenharmony_ci else 31962306a36Sopenharmony_ci dev->irq = 5; 32062306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_TxRxOFF); /* Disable Tx and Rx units. */ 32162306a36Sopenharmony_ci write_reg(ioaddr, CMR2, CMR2_NULL); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci dev->base_addr = ioaddr; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Read the station address PROM. */ 32662306a36Sopenharmony_ci get_node_ID(dev); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci#ifndef MODULE 32962306a36Sopenharmony_ci if (net_debug) 33062306a36Sopenharmony_ci printk(KERN_INFO "%s", version); 33162306a36Sopenharmony_ci#endif 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci printk(KERN_NOTICE "%s: Pocket adapter found at %#3lx, IRQ %d, " 33462306a36Sopenharmony_ci "SAPROM %pM.\n", 33562306a36Sopenharmony_ci dev->name, dev->base_addr, dev->irq, dev->dev_addr); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Reset the ethernet hardware and activate the printer pass-through. */ 33862306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci lp = netdev_priv(dev); 34162306a36Sopenharmony_ci lp->addr_mode = CMR2h_Normal; 34262306a36Sopenharmony_ci spin_lock_init(&lp->lock); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* For the ATP adapter the "if_port" is really the data transfer mode. */ 34562306a36Sopenharmony_ci if (xcvr[0]) 34662306a36Sopenharmony_ci dev->if_port = xcvr[0]; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4; 34962306a36Sopenharmony_ci if (dev->mem_end & 0xf) 35062306a36Sopenharmony_ci net_debug = dev->mem_end & 7; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev->netdev_ops = &atp_netdev_ops; 35362306a36Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci res = register_netdev(dev); 35662306a36Sopenharmony_ci if (res) { 35762306a36Sopenharmony_ci free_netdev(dev); 35862306a36Sopenharmony_ci return res; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci lp->next_module = root_atp_dev; 36262306a36Sopenharmony_ci root_atp_dev = dev; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/* Read the station address PROM, usually a word-wide EEPROM. */ 36862306a36Sopenharmony_cistatic void __init get_node_ID(struct net_device *dev) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci long ioaddr = dev->base_addr; 37162306a36Sopenharmony_ci __be16 addr[ETH_ALEN / 2]; 37262306a36Sopenharmony_ci int sa_offset = 0; 37362306a36Sopenharmony_ci int i; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */ 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Some adapters have the station address at offset 15 instead of offset 37862306a36Sopenharmony_ci zero. Check for it, and fix it if needed. */ 37962306a36Sopenharmony_ci if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff) 38062306a36Sopenharmony_ci sa_offset = 15; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for (i = 0; i < 3; i++) 38362306a36Sopenharmony_ci addr[i] = 38462306a36Sopenharmony_ci cpu_to_be16(eeprom_op(ioaddr, EE_READ(sa_offset + i))); 38562306a36Sopenharmony_ci eth_hw_addr_set(dev, (u8 *)addr); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci write_reg(ioaddr, CMR2, CMR2_NULL); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* 39162306a36Sopenharmony_ci An EEPROM read command starts by shifting out 0x60+address, and then 39262306a36Sopenharmony_ci shifting in the serial data. See the NatSemi databook for details. 39362306a36Sopenharmony_ci * ________________ 39462306a36Sopenharmony_ci * CS : __| 39562306a36Sopenharmony_ci * ___ ___ 39662306a36Sopenharmony_ci * CLK: ______| |___| | 39762306a36Sopenharmony_ci * __ _______ _______ 39862306a36Sopenharmony_ci * DI : __X_______X_______X 39962306a36Sopenharmony_ci * DO : _________X_______X 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic unsigned short __init eeprom_op(long ioaddr, u32 cmd) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci unsigned eedata_out = 0; 40562306a36Sopenharmony_ci int num_bits = EE_CMD_SIZE; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci while (--num_bits >= 0) { 40862306a36Sopenharmony_ci char outval = (cmd & (1<<num_bits)) ? EE_DATA_WRITE : 0; 40962306a36Sopenharmony_ci write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW); 41062306a36Sopenharmony_ci write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH); 41162306a36Sopenharmony_ci eedata_out <<= 1; 41262306a36Sopenharmony_ci if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ) 41362306a36Sopenharmony_ci eedata_out++; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS); 41662306a36Sopenharmony_ci return eedata_out; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* Open/initialize the board. This is called (in the current kernel) 42162306a36Sopenharmony_ci sometime after booting when the 'ifconfig' program is run. 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci This routine sets everything up anew at each open, even 42462306a36Sopenharmony_ci registers that "should" only need to be set once at boot, so that 42562306a36Sopenharmony_ci there is non-reboot way to recover if something goes wrong. 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci This is an attachable device: if there is no private entry then it wasn't 42862306a36Sopenharmony_ci probed for at boot-time, and we need to probe for it again. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic int net_open(struct net_device *dev) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 43362306a36Sopenharmony_ci int ret; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* The interrupt line is turned off (tri-stated) when the device isn't in 43662306a36Sopenharmony_ci use. That's especially important for "attached" interfaces where the 43762306a36Sopenharmony_ci port or interrupt may be shared. */ 43862306a36Sopenharmony_ci ret = request_irq(dev->irq, atp_interrupt, 0, dev->name, dev); 43962306a36Sopenharmony_ci if (ret) 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci hardware_init(dev); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci lp->dev = dev; 44562306a36Sopenharmony_ci timer_setup(&lp->timer, atp_timed_checker, 0); 44662306a36Sopenharmony_ci lp->timer.expires = jiffies + TIMED_CHECKER; 44762306a36Sopenharmony_ci add_timer(&lp->timer); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci netif_start_queue(dev); 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* This routine resets the hardware. We initialize everything, assuming that 45462306a36Sopenharmony_ci the hardware may have been temporarily detached. */ 45562306a36Sopenharmony_cistatic void hardware_init(struct net_device *dev) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 45862306a36Sopenharmony_ci long ioaddr = dev->base_addr; 45962306a36Sopenharmony_ci int i; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Turn off the printer multiplexer on the 8012. */ 46262306a36Sopenharmony_ci for (i = 0; i < 8; i++) 46362306a36Sopenharmony_ci outb(mux_8012[i], ioaddr + PAR_DATA); 46462306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RESET); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 46762306a36Sopenharmony_ci write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci write_reg_high(ioaddr, CMR2, lp->addr_mode); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (net_debug > 2) { 47262306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Reset: current Rx mode %d.\n", dev->name, 47362306a36Sopenharmony_ci (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci write_reg(ioaddr, CMR2, CMR2_IRQOUT); 47762306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Enable the interrupt line from the serial port. */ 48062306a36Sopenharmony_ci outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Unmask the interesting interrupts. */ 48362306a36Sopenharmony_ci write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); 48462306a36Sopenharmony_ci write_reg_high(ioaddr, IMR, ISRh_RxErr); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci lp->tx_unit_busy = 0; 48762306a36Sopenharmony_ci lp->pac_cnt_in_tx_buf = 0; 48862306a36Sopenharmony_ci lp->saved_tx_size = 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic void trigger_send(long ioaddr, int length) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci write_reg_byte(ioaddr, TxCNT0, length & 0xff); 49462306a36Sopenharmony_ci write_reg(ioaddr, TxCNT1, length >> 8); 49562306a36Sopenharmony_ci write_reg(ioaddr, CMR1, CMR1_Xmit); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic void write_packet(long ioaddr, int length, unsigned char *packet, int pad_len, int data_mode) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci if (length & 1) 50162306a36Sopenharmony_ci { 50262306a36Sopenharmony_ci length++; 50362306a36Sopenharmony_ci pad_len++; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci outb(EOC+MAR, ioaddr + PAR_DATA); 50762306a36Sopenharmony_ci if ((data_mode & 1) == 0) { 50862306a36Sopenharmony_ci /* Write the packet out, starting with the write addr. */ 50962306a36Sopenharmony_ci outb(WrAddr+MAR, ioaddr + PAR_DATA); 51062306a36Sopenharmony_ci do { 51162306a36Sopenharmony_ci write_byte_mode0(ioaddr, *packet++); 51262306a36Sopenharmony_ci } while (--length > pad_len) ; 51362306a36Sopenharmony_ci do { 51462306a36Sopenharmony_ci write_byte_mode0(ioaddr, 0); 51562306a36Sopenharmony_ci } while (--length > 0) ; 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci /* Write the packet out in slow mode. */ 51862306a36Sopenharmony_ci unsigned char outbyte = *packet++; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL); 52162306a36Sopenharmony_ci outb(WrAddr+MAR, ioaddr + PAR_DATA); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA); 52462306a36Sopenharmony_ci outb(outbyte & 0x0f, ioaddr + PAR_DATA); 52562306a36Sopenharmony_ci outbyte >>= 4; 52662306a36Sopenharmony_ci outb(outbyte & 0x0f, ioaddr + PAR_DATA); 52762306a36Sopenharmony_ci outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL); 52862306a36Sopenharmony_ci while (--length > pad_len) 52962306a36Sopenharmony_ci write_byte_mode1(ioaddr, *packet++); 53062306a36Sopenharmony_ci while (--length > 0) 53162306a36Sopenharmony_ci write_byte_mode1(ioaddr, 0); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci /* Terminate the Tx frame. End of write: ECB. */ 53462306a36Sopenharmony_ci outb(0xff, ioaddr + PAR_DATA); 53562306a36Sopenharmony_ci outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci long ioaddr = dev->base_addr; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci printk(KERN_WARNING "%s: Transmit timed out, %s?\n", dev->name, 54362306a36Sopenharmony_ci inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem" 54462306a36Sopenharmony_ci : "IRQ conflict"); 54562306a36Sopenharmony_ci dev->stats.tx_errors++; 54662306a36Sopenharmony_ci /* Try to restart the adapter. */ 54762306a36Sopenharmony_ci hardware_init(dev); 54862306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 54962306a36Sopenharmony_ci netif_wake_queue(dev); 55062306a36Sopenharmony_ci dev->stats.tx_errors++; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic netdev_tx_t atp_send_packet(struct sk_buff *skb, 55462306a36Sopenharmony_ci struct net_device *dev) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 55762306a36Sopenharmony_ci long ioaddr = dev->base_addr; 55862306a36Sopenharmony_ci int length; 55962306a36Sopenharmony_ci unsigned long flags; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci netif_stop_queue(dev); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Disable interrupts by writing 0x00 to the Interrupt Mask Register. 56662306a36Sopenharmony_ci This sequence must not be interrupted by an incoming packet. */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 56962306a36Sopenharmony_ci write_reg(ioaddr, IMR, 0); 57062306a36Sopenharmony_ci write_reg_high(ioaddr, IMR, 0); 57162306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci write_packet(ioaddr, length, skb->data, length-skb->len, dev->if_port); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci lp->pac_cnt_in_tx_buf++; 57662306a36Sopenharmony_ci if (lp->tx_unit_busy == 0) { 57762306a36Sopenharmony_ci trigger_send(ioaddr, length); 57862306a36Sopenharmony_ci lp->saved_tx_size = 0; /* Redundant */ 57962306a36Sopenharmony_ci lp->re_tx = 0; 58062306a36Sopenharmony_ci lp->tx_unit_busy = 1; 58162306a36Sopenharmony_ci } else 58262306a36Sopenharmony_ci lp->saved_tx_size = length; 58362306a36Sopenharmony_ci /* Re-enable the LPT interrupts. */ 58462306a36Sopenharmony_ci write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); 58562306a36Sopenharmony_ci write_reg_high(ioaddr, IMR, ISRh_RxErr); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci dev_kfree_skb (skb); 58862306a36Sopenharmony_ci return NETDEV_TX_OK; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci/* The typical workload of the driver: 59362306a36Sopenharmony_ci Handle the network interface interrupts. */ 59462306a36Sopenharmony_cistatic irqreturn_t atp_interrupt(int irq, void *dev_instance) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct net_device *dev = dev_instance; 59762306a36Sopenharmony_ci struct net_local *lp; 59862306a36Sopenharmony_ci long ioaddr; 59962306a36Sopenharmony_ci static int num_tx_since_rx; 60062306a36Sopenharmony_ci int boguscount = max_interrupt_work; 60162306a36Sopenharmony_ci int handled = 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ioaddr = dev->base_addr; 60462306a36Sopenharmony_ci lp = netdev_priv(dev); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci spin_lock(&lp->lock); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Disable additional spurious interrupts. */ 60962306a36Sopenharmony_ci outb(Ctrl_SelData, ioaddr + PAR_CONTROL); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* The adapter's output is currently the IRQ line, switch it to data. */ 61262306a36Sopenharmony_ci write_reg(ioaddr, CMR2, CMR2_NULL); 61362306a36Sopenharmony_ci write_reg(ioaddr, IMR, 0); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (net_debug > 5) 61662306a36Sopenharmony_ci printk(KERN_DEBUG "%s: In interrupt ", dev->name); 61762306a36Sopenharmony_ci while (--boguscount > 0) { 61862306a36Sopenharmony_ci int status = read_nibble(ioaddr, ISR); 61962306a36Sopenharmony_ci if (net_debug > 5) 62062306a36Sopenharmony_ci printk("loop status %02x..", status); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (status & (ISR_RxOK<<3)) { 62362306a36Sopenharmony_ci handled = 1; 62462306a36Sopenharmony_ci write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */ 62562306a36Sopenharmony_ci do { 62662306a36Sopenharmony_ci int read_status = read_nibble(ioaddr, CMR1); 62762306a36Sopenharmony_ci if (net_debug > 6) 62862306a36Sopenharmony_ci printk("handling Rx packet %02x..", read_status); 62962306a36Sopenharmony_ci /* We acknowledged the normal Rx interrupt, so if the interrupt 63062306a36Sopenharmony_ci is still outstanding we must have a Rx error. */ 63162306a36Sopenharmony_ci if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */ 63262306a36Sopenharmony_ci dev->stats.rx_over_errors++; 63362306a36Sopenharmony_ci /* Set to no-accept mode long enough to remove a packet. */ 63462306a36Sopenharmony_ci write_reg_high(ioaddr, CMR2, CMR2h_OFF); 63562306a36Sopenharmony_ci net_rx(dev); 63662306a36Sopenharmony_ci /* Clear the interrupt and return to normal Rx mode. */ 63762306a36Sopenharmony_ci write_reg_high(ioaddr, ISR, ISRh_RxErr); 63862306a36Sopenharmony_ci write_reg_high(ioaddr, CMR2, lp->addr_mode); 63962306a36Sopenharmony_ci } else if ((read_status & (CMR1_BufEnb << 3)) == 0) { 64062306a36Sopenharmony_ci net_rx(dev); 64162306a36Sopenharmony_ci num_tx_since_rx = 0; 64262306a36Sopenharmony_ci } else 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci } while (--boguscount > 0); 64562306a36Sopenharmony_ci } else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) { 64662306a36Sopenharmony_ci handled = 1; 64762306a36Sopenharmony_ci if (net_debug > 6) 64862306a36Sopenharmony_ci printk("handling Tx done.."); 64962306a36Sopenharmony_ci /* Clear the Tx interrupt. We should check for too many failures 65062306a36Sopenharmony_ci and reinitialize the adapter. */ 65162306a36Sopenharmony_ci write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK); 65262306a36Sopenharmony_ci if (status & (ISR_TxErr<<3)) { 65362306a36Sopenharmony_ci dev->stats.collisions++; 65462306a36Sopenharmony_ci if (++lp->re_tx > 15) { 65562306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 65662306a36Sopenharmony_ci hardware_init(dev); 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci /* Attempt to retransmit. */ 66062306a36Sopenharmony_ci if (net_debug > 6) printk("attempting to ReTx"); 66162306a36Sopenharmony_ci write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit); 66262306a36Sopenharmony_ci } else { 66362306a36Sopenharmony_ci /* Finish up the transmit. */ 66462306a36Sopenharmony_ci dev->stats.tx_packets++; 66562306a36Sopenharmony_ci lp->pac_cnt_in_tx_buf--; 66662306a36Sopenharmony_ci if ( lp->saved_tx_size) { 66762306a36Sopenharmony_ci trigger_send(ioaddr, lp->saved_tx_size); 66862306a36Sopenharmony_ci lp->saved_tx_size = 0; 66962306a36Sopenharmony_ci lp->re_tx = 0; 67062306a36Sopenharmony_ci } else 67162306a36Sopenharmony_ci lp->tx_unit_busy = 0; 67262306a36Sopenharmony_ci netif_wake_queue(dev); /* Inform upper layers. */ 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci num_tx_since_rx++; 67562306a36Sopenharmony_ci } else if (num_tx_since_rx > 8 && 67662306a36Sopenharmony_ci time_after(jiffies, lp->last_rx_time + HZ)) { 67762306a36Sopenharmony_ci if (net_debug > 2) 67862306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Missed packet? No Rx after %d Tx and " 67962306a36Sopenharmony_ci "%ld jiffies status %02x CMR1 %02x.\n", dev->name, 68062306a36Sopenharmony_ci num_tx_since_rx, jiffies - lp->last_rx_time, status, 68162306a36Sopenharmony_ci (read_nibble(ioaddr, CMR1) >> 3) & 15); 68262306a36Sopenharmony_ci dev->stats.rx_missed_errors++; 68362306a36Sopenharmony_ci hardware_init(dev); 68462306a36Sopenharmony_ci num_tx_since_rx = 0; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci } else 68762306a36Sopenharmony_ci break; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* This following code fixes a rare (and very difficult to track down) 69162306a36Sopenharmony_ci problem where the adapter forgets its ethernet address. */ 69262306a36Sopenharmony_ci { 69362306a36Sopenharmony_ci int i; 69462306a36Sopenharmony_ci for (i = 0; i < 6; i++) 69562306a36Sopenharmony_ci write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); 69662306a36Sopenharmony_ci#if 0 && defined(TIMED_CHECKER) 69762306a36Sopenharmony_ci mod_timer(&lp->timer, jiffies + TIMED_CHECKER); 69862306a36Sopenharmony_ci#endif 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* Tell the adapter that it can go back to using the output line as IRQ. */ 70262306a36Sopenharmony_ci write_reg(ioaddr, CMR2, CMR2_IRQOUT); 70362306a36Sopenharmony_ci /* Enable the physical interrupt line, which is sure to be low until.. */ 70462306a36Sopenharmony_ci outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL); 70562306a36Sopenharmony_ci /* .. we enable the interrupt sources. */ 70662306a36Sopenharmony_ci write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); 70762306a36Sopenharmony_ci write_reg_high(ioaddr, IMR, ISRh_RxErr); /* Hmmm, really needed? */ 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci spin_unlock(&lp->lock); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (net_debug > 5) printk("exiting interrupt.\n"); 71262306a36Sopenharmony_ci return IRQ_RETVAL(handled); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci#ifdef TIMED_CHECKER 71662306a36Sopenharmony_ci/* This following code fixes a rare (and very difficult to track down) 71762306a36Sopenharmony_ci problem where the adapter forgets its ethernet address. */ 71862306a36Sopenharmony_cistatic void atp_timed_checker(struct timer_list *t) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct net_local *lp = from_timer(lp, t, timer); 72162306a36Sopenharmony_ci struct net_device *dev = lp->dev; 72262306a36Sopenharmony_ci long ioaddr = dev->base_addr; 72362306a36Sopenharmony_ci int tickssofar = jiffies - lp->last_rx_time; 72462306a36Sopenharmony_ci int i; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci spin_lock(&lp->lock); 72762306a36Sopenharmony_ci if (tickssofar > 2*HZ) { 72862306a36Sopenharmony_ci#if 1 72962306a36Sopenharmony_ci for (i = 0; i < 6; i++) 73062306a36Sopenharmony_ci write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); 73162306a36Sopenharmony_ci lp->last_rx_time = jiffies; 73262306a36Sopenharmony_ci#else 73362306a36Sopenharmony_ci for (i = 0; i < 6; i++) 73462306a36Sopenharmony_ci if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) 73562306a36Sopenharmony_ci { 73662306a36Sopenharmony_ci struct net_local *lp = netdev_priv(atp_timed_dev); 73762306a36Sopenharmony_ci write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); 73862306a36Sopenharmony_ci if (i == 2) 73962306a36Sopenharmony_ci dev->stats.tx_errors++; 74062306a36Sopenharmony_ci else if (i == 3) 74162306a36Sopenharmony_ci dev->stats.tx_dropped++; 74262306a36Sopenharmony_ci else if (i == 4) 74362306a36Sopenharmony_ci dev->stats.collisions++; 74462306a36Sopenharmony_ci else 74562306a36Sopenharmony_ci dev->stats.rx_errors++; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci#endif 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci spin_unlock(&lp->lock); 75062306a36Sopenharmony_ci lp->timer.expires = jiffies + TIMED_CHECKER; 75162306a36Sopenharmony_ci add_timer(&lp->timer); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci#endif 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci/* We have a good packet(s), get it/them out of the buffers. */ 75662306a36Sopenharmony_cistatic void net_rx(struct net_device *dev) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 75962306a36Sopenharmony_ci long ioaddr = dev->base_addr; 76062306a36Sopenharmony_ci struct rx_header rx_head; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Process the received packet. */ 76362306a36Sopenharmony_ci outb(EOC+MAR, ioaddr + PAR_DATA); 76462306a36Sopenharmony_ci read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port); 76562306a36Sopenharmony_ci if (net_debug > 5) 76662306a36Sopenharmony_ci printk(KERN_DEBUG " rx_count %04x %04x %04x %04x..", rx_head.pad, 76762306a36Sopenharmony_ci rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr); 76862306a36Sopenharmony_ci if ((rx_head.rx_status & 0x77) != 0x01) { 76962306a36Sopenharmony_ci dev->stats.rx_errors++; 77062306a36Sopenharmony_ci if (rx_head.rx_status & 0x0004) dev->stats.rx_frame_errors++; 77162306a36Sopenharmony_ci else if (rx_head.rx_status & 0x0002) dev->stats.rx_crc_errors++; 77262306a36Sopenharmony_ci if (net_debug > 3) 77362306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Unknown ATP Rx error %04x.\n", 77462306a36Sopenharmony_ci dev->name, rx_head.rx_status); 77562306a36Sopenharmony_ci if (rx_head.rx_status & 0x0020) { 77662306a36Sopenharmony_ci dev->stats.rx_fifo_errors++; 77762306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE); 77862306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); 77962306a36Sopenharmony_ci } else if (rx_head.rx_status & 0x0050) 78062306a36Sopenharmony_ci hardware_init(dev); 78162306a36Sopenharmony_ci return; 78262306a36Sopenharmony_ci } else { 78362306a36Sopenharmony_ci /* Malloc up new buffer. The "-4" omits the FCS (CRC). */ 78462306a36Sopenharmony_ci int pkt_len = (rx_head.rx_count & 0x7ff) - 4; 78562306a36Sopenharmony_ci struct sk_buff *skb; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, pkt_len + 2); 78862306a36Sopenharmony_ci if (skb == NULL) { 78962306a36Sopenharmony_ci dev->stats.rx_dropped++; 79062306a36Sopenharmony_ci goto done; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 79462306a36Sopenharmony_ci read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); 79562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 79662306a36Sopenharmony_ci netif_rx(skb); 79762306a36Sopenharmony_ci dev->stats.rx_packets++; 79862306a36Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci done: 80162306a36Sopenharmony_ci write_reg(ioaddr, CMR1, CMR1_NextPkt); 80262306a36Sopenharmony_ci lp->last_rx_time = jiffies; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic void read_block(long ioaddr, int length, unsigned char *p, int data_mode) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci if (data_mode <= 3) { /* Mode 0 or 1 */ 80862306a36Sopenharmony_ci outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL); 80962306a36Sopenharmony_ci outb(length == 8 ? RdAddr | HNib | MAR : RdAddr | MAR, 81062306a36Sopenharmony_ci ioaddr + PAR_DATA); 81162306a36Sopenharmony_ci if (data_mode <= 1) { /* Mode 0 or 1 */ 81262306a36Sopenharmony_ci do { *p++ = read_byte_mode0(ioaddr); } while (--length > 0); 81362306a36Sopenharmony_ci } else { /* Mode 2 or 3 */ 81462306a36Sopenharmony_ci do { *p++ = read_byte_mode2(ioaddr); } while (--length > 0); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci } else if (data_mode <= 5) { 81762306a36Sopenharmony_ci do { *p++ = read_byte_mode4(ioaddr); } while (--length > 0); 81862306a36Sopenharmony_ci } else { 81962306a36Sopenharmony_ci do { *p++ = read_byte_mode6(ioaddr); } while (--length > 0); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci outb(EOC+HNib+MAR, ioaddr + PAR_DATA); 82362306a36Sopenharmony_ci outb(Ctrl_SelData, ioaddr + PAR_CONTROL); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* The inverse routine to net_open(). */ 82762306a36Sopenharmony_cistatic int 82862306a36Sopenharmony_cinet_close(struct net_device *dev) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 83162306a36Sopenharmony_ci long ioaddr = dev->base_addr; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci netif_stop_queue(dev); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci del_timer_sync(&lp->timer); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Flush the Tx and disable Rx here. */ 83862306a36Sopenharmony_ci lp->addr_mode = CMR2h_OFF; 83962306a36Sopenharmony_ci write_reg_high(ioaddr, CMR2, CMR2h_OFF); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Free the IRQ line. */ 84262306a36Sopenharmony_ci outb(0x00, ioaddr + PAR_CONTROL); 84362306a36Sopenharmony_ci free_irq(dev->irq, dev); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* Reset the ethernet hardware and activate the printer pass-through. */ 84662306a36Sopenharmony_ci write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci/* 85162306a36Sopenharmony_ci * Set or clear the multicast filter for this adapter. 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 85762306a36Sopenharmony_ci long ioaddr = dev->base_addr; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (!netdev_mc_empty(dev) || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) 86062306a36Sopenharmony_ci lp->addr_mode = CMR2h_PROMISC; 86162306a36Sopenharmony_ci else 86262306a36Sopenharmony_ci lp->addr_mode = CMR2h_Normal; 86362306a36Sopenharmony_ci write_reg_high(ioaddr, CMR2, lp->addr_mode); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int __init atp_init_module(void) { 86762306a36Sopenharmony_ci if (debug) /* Emit version even if no cards detected. */ 86862306a36Sopenharmony_ci printk(KERN_INFO "%s", version); 86962306a36Sopenharmony_ci return atp_init(); 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void __exit atp_cleanup_module(void) { 87362306a36Sopenharmony_ci struct net_device *next_dev; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci while (root_atp_dev) { 87662306a36Sopenharmony_ci struct net_local *atp_local = netdev_priv(root_atp_dev); 87762306a36Sopenharmony_ci next_dev = atp_local->next_module; 87862306a36Sopenharmony_ci unregister_netdev(root_atp_dev); 87962306a36Sopenharmony_ci /* No need to release_region(), since we never snarf it. */ 88062306a36Sopenharmony_ci free_netdev(root_atp_dev); 88162306a36Sopenharmony_ci root_atp_dev = next_dev; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cimodule_init(atp_init_module); 88662306a36Sopenharmony_cimodule_exit(atp_cleanup_module); 887