162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Amiga Linux/m68k Ariadne Ethernet Driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * © Copyright 1995-2003 by Geert Uytterhoeven (geert@linux-m68k.org) 562306a36Sopenharmony_ci * Peter De Schrijver (p2@mind.be) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * --------------------------------------------------------------------------- 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is based on 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * lance.c: An AMD LANCE ethernet driver for linux. 1262306a36Sopenharmony_ci * Written 1993-94 by Donald Becker. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller 1562306a36Sopenharmony_ci * Advanced Micro Devices 1662306a36Sopenharmony_ci * Publication #16907, Rev. B, Amendment/0, May 1994 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * MC68230: Parallel Interface/Timer (PI/T) 1962306a36Sopenharmony_ci * Motorola Semiconductors, December, 1983 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * --------------------------------------------------------------------------- 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 2462306a36Sopenharmony_ci * License. See the file COPYING in the main directory of the Linux 2562306a36Sopenharmony_ci * distribution for more details. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * --------------------------------------------------------------------------- 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * The Ariadne is a Zorro-II board made by Village Tronic. It contains: 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both 3262306a36Sopenharmony_ci * 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * - an MC68230 Parallel Interface/Timer configured as 2 parallel ports 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3862306a36Sopenharmony_ci/*#define DEBUG*/ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <linux/module.h> 4162306a36Sopenharmony_ci#include <linux/stddef.h> 4262306a36Sopenharmony_ci#include <linux/kernel.h> 4362306a36Sopenharmony_ci#include <linux/string.h> 4462306a36Sopenharmony_ci#include <linux/errno.h> 4562306a36Sopenharmony_ci#include <linux/ioport.h> 4662306a36Sopenharmony_ci#include <linux/netdevice.h> 4762306a36Sopenharmony_ci#include <linux/etherdevice.h> 4862306a36Sopenharmony_ci#include <linux/interrupt.h> 4962306a36Sopenharmony_ci#include <linux/skbuff.h> 5062306a36Sopenharmony_ci#include <linux/init.h> 5162306a36Sopenharmony_ci#include <linux/zorro.h> 5262306a36Sopenharmony_ci#include <linux/bitops.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <asm/byteorder.h> 5562306a36Sopenharmony_ci#include <asm/amigaints.h> 5662306a36Sopenharmony_ci#include <asm/amigahw.h> 5762306a36Sopenharmony_ci#include <asm/irq.h> 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#include "ariadne.h" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#ifdef ARIADNE_DEBUG 6262306a36Sopenharmony_ciint ariadne_debug = ARIADNE_DEBUG; 6362306a36Sopenharmony_ci#else 6462306a36Sopenharmony_ciint ariadne_debug = 1; 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Macros to Fix Endianness problems */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Swap the Bytes in a WORD */ 7062306a36Sopenharmony_ci#define swapw(x) (((x >> 8) & 0x00ff) | ((x << 8) & 0xff00)) 7162306a36Sopenharmony_ci/* Get the Low BYTE in a WORD */ 7262306a36Sopenharmony_ci#define lowb(x) (x & 0xff) 7362306a36Sopenharmony_ci/* Get the Swapped High WORD in a LONG */ 7462306a36Sopenharmony_ci#define swhighw(x) ((((x) >> 8) & 0xff00) | (((x) >> 24) & 0x00ff)) 7562306a36Sopenharmony_ci/* Get the Swapped Low WORD in a LONG */ 7662306a36Sopenharmony_ci#define swloww(x) ((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff)) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Transmit/Receive Ring Definitions */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define TX_RING_SIZE 5 8162306a36Sopenharmony_ci#define RX_RING_SIZE 16 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define PKT_BUF_SIZE 1520 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Private Device Data */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct ariadne_private { 8862306a36Sopenharmony_ci volatile struct TDRE *tx_ring[TX_RING_SIZE]; 8962306a36Sopenharmony_ci volatile struct RDRE *rx_ring[RX_RING_SIZE]; 9062306a36Sopenharmony_ci volatile u_short *tx_buff[TX_RING_SIZE]; 9162306a36Sopenharmony_ci volatile u_short *rx_buff[RX_RING_SIZE]; 9262306a36Sopenharmony_ci int cur_tx, cur_rx; /* The next free ring entry */ 9362306a36Sopenharmony_ci int dirty_tx; /* The ring entries to be free()ed */ 9462306a36Sopenharmony_ci char tx_full; 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Structure Created in the Ariadne's RAM Buffer */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct lancedata { 10062306a36Sopenharmony_ci struct TDRE tx_ring[TX_RING_SIZE]; 10162306a36Sopenharmony_ci struct RDRE rx_ring[RX_RING_SIZE]; 10262306a36Sopenharmony_ci u_short tx_buff[TX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)]; 10362306a36Sopenharmony_ci u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)]; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void memcpyw(volatile u_short *dest, u_short *src, int len) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci while (len >= 2) { 10962306a36Sopenharmony_ci *(dest++) = *(src++); 11062306a36Sopenharmony_ci len -= 2; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci if (len == 1) 11362306a36Sopenharmony_ci *dest = (*(u_char *)src) << 8; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void ariadne_init_ring(struct net_device *dev) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct ariadne_private *priv = netdev_priv(dev); 11962306a36Sopenharmony_ci volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start; 12062306a36Sopenharmony_ci int i; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci netif_stop_queue(dev); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci priv->tx_full = 0; 12562306a36Sopenharmony_ci priv->cur_rx = priv->cur_tx = 0; 12662306a36Sopenharmony_ci priv->dirty_tx = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Set up TX Ring */ 12962306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 13062306a36Sopenharmony_ci volatile struct TDRE *t = &lancedata->tx_ring[i]; 13162306a36Sopenharmony_ci t->TMD0 = swloww(ARIADNE_RAM + 13262306a36Sopenharmony_ci offsetof(struct lancedata, tx_buff[i])); 13362306a36Sopenharmony_ci t->TMD1 = swhighw(ARIADNE_RAM + 13462306a36Sopenharmony_ci offsetof(struct lancedata, tx_buff[i])) | 13562306a36Sopenharmony_ci TF_STP | TF_ENP; 13662306a36Sopenharmony_ci t->TMD2 = swapw((u_short)-PKT_BUF_SIZE); 13762306a36Sopenharmony_ci t->TMD3 = 0; 13862306a36Sopenharmony_ci priv->tx_ring[i] = &lancedata->tx_ring[i]; 13962306a36Sopenharmony_ci priv->tx_buff[i] = lancedata->tx_buff[i]; 14062306a36Sopenharmony_ci netdev_dbg(dev, "TX Entry %2d at %p, Buf at %p\n", 14162306a36Sopenharmony_ci i, &lancedata->tx_ring[i], lancedata->tx_buff[i]); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Set up RX Ring */ 14562306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 14662306a36Sopenharmony_ci volatile struct RDRE *r = &lancedata->rx_ring[i]; 14762306a36Sopenharmony_ci r->RMD0 = swloww(ARIADNE_RAM + 14862306a36Sopenharmony_ci offsetof(struct lancedata, rx_buff[i])); 14962306a36Sopenharmony_ci r->RMD1 = swhighw(ARIADNE_RAM + 15062306a36Sopenharmony_ci offsetof(struct lancedata, rx_buff[i])) | 15162306a36Sopenharmony_ci RF_OWN; 15262306a36Sopenharmony_ci r->RMD2 = swapw((u_short)-PKT_BUF_SIZE); 15362306a36Sopenharmony_ci r->RMD3 = 0x0000; 15462306a36Sopenharmony_ci priv->rx_ring[i] = &lancedata->rx_ring[i]; 15562306a36Sopenharmony_ci priv->rx_buff[i] = lancedata->rx_buff[i]; 15662306a36Sopenharmony_ci netdev_dbg(dev, "RX Entry %2d at %p, Buf at %p\n", 15762306a36Sopenharmony_ci i, &lancedata->rx_ring[i], lancedata->rx_buff[i]); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int ariadne_rx(struct net_device *dev) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct ariadne_private *priv = netdev_priv(dev); 16462306a36Sopenharmony_ci int entry = priv->cur_rx % RX_RING_SIZE; 16562306a36Sopenharmony_ci int i; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* If we own the next entry, it's a new packet. Send it up */ 16862306a36Sopenharmony_ci while (!(lowb(priv->rx_ring[entry]->RMD1) & RF_OWN)) { 16962306a36Sopenharmony_ci int status = lowb(priv->rx_ring[entry]->RMD1); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (status != (RF_STP | RF_ENP)) { /* There was an error */ 17262306a36Sopenharmony_ci /* There is a tricky error noted by 17362306a36Sopenharmony_ci * John Murphy <murf@perftech.com> to Russ Nelson: 17462306a36Sopenharmony_ci * Even with full-sized buffers it's possible for a 17562306a36Sopenharmony_ci * jabber packet to use two buffers, with only the 17662306a36Sopenharmony_ci * last correctly noting the error 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci /* Only count a general error at the end of a packet */ 17962306a36Sopenharmony_ci if (status & RF_ENP) 18062306a36Sopenharmony_ci dev->stats.rx_errors++; 18162306a36Sopenharmony_ci if (status & RF_FRAM) 18262306a36Sopenharmony_ci dev->stats.rx_frame_errors++; 18362306a36Sopenharmony_ci if (status & RF_OFLO) 18462306a36Sopenharmony_ci dev->stats.rx_over_errors++; 18562306a36Sopenharmony_ci if (status & RF_CRC) 18662306a36Sopenharmony_ci dev->stats.rx_crc_errors++; 18762306a36Sopenharmony_ci if (status & RF_BUFF) 18862306a36Sopenharmony_ci dev->stats.rx_fifo_errors++; 18962306a36Sopenharmony_ci priv->rx_ring[entry]->RMD1 &= 0xff00 | RF_STP | RF_ENP; 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci /* Malloc up new buffer, compatible with net-3 */ 19262306a36Sopenharmony_ci short pkt_len = swapw(priv->rx_ring[entry]->RMD3); 19362306a36Sopenharmony_ci struct sk_buff *skb; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, pkt_len + 2); 19662306a36Sopenharmony_ci if (!skb) { 19762306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) 19862306a36Sopenharmony_ci if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN) 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (i > RX_RING_SIZE - 2) { 20262306a36Sopenharmony_ci dev->stats.rx_dropped++; 20362306a36Sopenharmony_ci priv->rx_ring[entry]->RMD1 |= RF_OWN; 20462306a36Sopenharmony_ci priv->cur_rx++; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align */ 21162306a36Sopenharmony_ci skb_put(skb, pkt_len); /* Make room */ 21262306a36Sopenharmony_ci skb_copy_to_linear_data(skb, 21362306a36Sopenharmony_ci (const void *)priv->rx_buff[entry], 21462306a36Sopenharmony_ci pkt_len); 21562306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 21662306a36Sopenharmony_ci netdev_dbg(dev, "RX pkt type 0x%04x from %pM to %pM data %p len %u\n", 21762306a36Sopenharmony_ci ((u_short *)skb->data)[6], 21862306a36Sopenharmony_ci skb->data + 6, skb->data, 21962306a36Sopenharmony_ci skb->data, skb->len); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci netif_rx(skb); 22262306a36Sopenharmony_ci dev->stats.rx_packets++; 22362306a36Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci priv->rx_ring[entry]->RMD1 |= RF_OWN; 22762306a36Sopenharmony_ci entry = (++priv->cur_rx) % RX_RING_SIZE; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci priv->cur_rx = priv->cur_rx % RX_RING_SIZE; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* We should check that at least two ring entries are free. 23362306a36Sopenharmony_ci * If not, we should free one and mark stats->rx_dropped++ 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic irqreturn_t ariadne_interrupt(int irq, void *data) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)data; 24262306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 24362306a36Sopenharmony_ci struct ariadne_private *priv; 24462306a36Sopenharmony_ci int csr0, boguscnt; 24562306a36Sopenharmony_ci int handled = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!(lance->RDP & INTR)) /* Check if any interrupt has been */ 25062306a36Sopenharmony_ci return IRQ_NONE; /* generated by the board */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci priv = netdev_priv(dev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci boguscnt = 10; 25562306a36Sopenharmony_ci while ((csr0 = lance->RDP) & (ERR | RINT | TINT) && --boguscnt >= 0) { 25662306a36Sopenharmony_ci /* Acknowledge all of the current interrupt sources ASAP */ 25762306a36Sopenharmony_ci lance->RDP = csr0 & ~(INEA | TDMD | STOP | STRT | INIT); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci#ifdef DEBUG 26062306a36Sopenharmony_ci if (ariadne_debug > 5) { 26162306a36Sopenharmony_ci netdev_dbg(dev, "interrupt csr0=%#02x new csr=%#02x [", 26262306a36Sopenharmony_ci csr0, lance->RDP); 26362306a36Sopenharmony_ci if (csr0 & INTR) 26462306a36Sopenharmony_ci pr_cont(" INTR"); 26562306a36Sopenharmony_ci if (csr0 & INEA) 26662306a36Sopenharmony_ci pr_cont(" INEA"); 26762306a36Sopenharmony_ci if (csr0 & RXON) 26862306a36Sopenharmony_ci pr_cont(" RXON"); 26962306a36Sopenharmony_ci if (csr0 & TXON) 27062306a36Sopenharmony_ci pr_cont(" TXON"); 27162306a36Sopenharmony_ci if (csr0 & TDMD) 27262306a36Sopenharmony_ci pr_cont(" TDMD"); 27362306a36Sopenharmony_ci if (csr0 & STOP) 27462306a36Sopenharmony_ci pr_cont(" STOP"); 27562306a36Sopenharmony_ci if (csr0 & STRT) 27662306a36Sopenharmony_ci pr_cont(" STRT"); 27762306a36Sopenharmony_ci if (csr0 & INIT) 27862306a36Sopenharmony_ci pr_cont(" INIT"); 27962306a36Sopenharmony_ci if (csr0 & ERR) 28062306a36Sopenharmony_ci pr_cont(" ERR"); 28162306a36Sopenharmony_ci if (csr0 & BABL) 28262306a36Sopenharmony_ci pr_cont(" BABL"); 28362306a36Sopenharmony_ci if (csr0 & CERR) 28462306a36Sopenharmony_ci pr_cont(" CERR"); 28562306a36Sopenharmony_ci if (csr0 & MISS) 28662306a36Sopenharmony_ci pr_cont(" MISS"); 28762306a36Sopenharmony_ci if (csr0 & MERR) 28862306a36Sopenharmony_ci pr_cont(" MERR"); 28962306a36Sopenharmony_ci if (csr0 & RINT) 29062306a36Sopenharmony_ci pr_cont(" RINT"); 29162306a36Sopenharmony_ci if (csr0 & TINT) 29262306a36Sopenharmony_ci pr_cont(" TINT"); 29362306a36Sopenharmony_ci if (csr0 & IDON) 29462306a36Sopenharmony_ci pr_cont(" IDON"); 29562306a36Sopenharmony_ci pr_cont(" ]\n"); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci#endif 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (csr0 & RINT) { /* Rx interrupt */ 30062306a36Sopenharmony_ci handled = 1; 30162306a36Sopenharmony_ci ariadne_rx(dev); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (csr0 & TINT) { /* Tx-done interrupt */ 30562306a36Sopenharmony_ci int dirty_tx = priv->dirty_tx; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci handled = 1; 30862306a36Sopenharmony_ci while (dirty_tx < priv->cur_tx) { 30962306a36Sopenharmony_ci int entry = dirty_tx % TX_RING_SIZE; 31062306a36Sopenharmony_ci int status = lowb(priv->tx_ring[entry]->TMD1); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (status & TF_OWN) 31362306a36Sopenharmony_ci break; /* It still hasn't been Txed */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci priv->tx_ring[entry]->TMD1 &= 0xff00; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (status & TF_ERR) { 31862306a36Sopenharmony_ci /* There was an major error, log it */ 31962306a36Sopenharmony_ci int err_status = priv->tx_ring[entry]->TMD3; 32062306a36Sopenharmony_ci dev->stats.tx_errors++; 32162306a36Sopenharmony_ci if (err_status & EF_RTRY) 32262306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 32362306a36Sopenharmony_ci if (err_status & EF_LCAR) 32462306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 32562306a36Sopenharmony_ci if (err_status & EF_LCOL) 32662306a36Sopenharmony_ci dev->stats.tx_window_errors++; 32762306a36Sopenharmony_ci if (err_status & EF_UFLO) { 32862306a36Sopenharmony_ci /* Ackk! On FIFO errors the Tx unit is turned off! */ 32962306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 33062306a36Sopenharmony_ci /* Remove this verbosity later! */ 33162306a36Sopenharmony_ci netdev_err(dev, "Tx FIFO error! Status %04x\n", 33262306a36Sopenharmony_ci csr0); 33362306a36Sopenharmony_ci /* Restart the chip */ 33462306a36Sopenharmony_ci lance->RDP = STRT; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci } else { 33762306a36Sopenharmony_ci if (status & (TF_MORE | TF_ONE)) 33862306a36Sopenharmony_ci dev->stats.collisions++; 33962306a36Sopenharmony_ci dev->stats.tx_packets++; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci dirty_tx++; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci#ifndef final_version 34562306a36Sopenharmony_ci if (priv->cur_tx - dirty_tx >= TX_RING_SIZE) { 34662306a36Sopenharmony_ci netdev_err(dev, "out-of-sync dirty pointer, %d vs. %d, full=%d\n", 34762306a36Sopenharmony_ci dirty_tx, priv->cur_tx, 34862306a36Sopenharmony_ci priv->tx_full); 34962306a36Sopenharmony_ci dirty_tx += TX_RING_SIZE; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci#endif 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (priv->tx_full && netif_queue_stopped(dev) && 35462306a36Sopenharmony_ci dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) { 35562306a36Sopenharmony_ci /* The ring is no longer full */ 35662306a36Sopenharmony_ci priv->tx_full = 0; 35762306a36Sopenharmony_ci netif_wake_queue(dev); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci priv->dirty_tx = dirty_tx; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Log misc errors */ 36462306a36Sopenharmony_ci if (csr0 & BABL) { 36562306a36Sopenharmony_ci handled = 1; 36662306a36Sopenharmony_ci dev->stats.tx_errors++; /* Tx babble */ 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci if (csr0 & MISS) { 36962306a36Sopenharmony_ci handled = 1; 37062306a36Sopenharmony_ci dev->stats.rx_errors++; /* Missed a Rx frame */ 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci if (csr0 & MERR) { 37362306a36Sopenharmony_ci handled = 1; 37462306a36Sopenharmony_ci netdev_err(dev, "Bus master arbitration failure, status %04x\n", 37562306a36Sopenharmony_ci csr0); 37662306a36Sopenharmony_ci /* Restart the chip */ 37762306a36Sopenharmony_ci lance->RDP = STRT; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Clear any other interrupt, and set interrupt enable */ 38262306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 38362306a36Sopenharmony_ci lance->RDP = INEA | BABL | CERR | MISS | MERR | IDON; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (ariadne_debug > 4) 38662306a36Sopenharmony_ci netdev_dbg(dev, "exiting interrupt, csr%d=%#04x\n", 38762306a36Sopenharmony_ci lance->RAP, lance->RDP); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return IRQ_RETVAL(handled); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int ariadne_open(struct net_device *dev) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 39562306a36Sopenharmony_ci u_short in; 39662306a36Sopenharmony_ci u_long version; 39762306a36Sopenharmony_ci int i; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Reset the LANCE */ 40062306a36Sopenharmony_ci in = lance->Reset; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Stop the LANCE */ 40362306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 40462306a36Sopenharmony_ci lance->RDP = STOP; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Check the LANCE version */ 40762306a36Sopenharmony_ci lance->RAP = CSR88; /* Chip ID */ 40862306a36Sopenharmony_ci version = swapw(lance->RDP); 40962306a36Sopenharmony_ci lance->RAP = CSR89; /* Chip ID */ 41062306a36Sopenharmony_ci version |= swapw(lance->RDP) << 16; 41162306a36Sopenharmony_ci if ((version & 0x00000fff) != 0x00000003) { 41262306a36Sopenharmony_ci pr_warn("Couldn't find AMD Ethernet Chip\n"); 41362306a36Sopenharmony_ci return -EAGAIN; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci if ((version & 0x0ffff000) != 0x00003000) { 41662306a36Sopenharmony_ci pr_warn("Couldn't find Am79C960 (Wrong part number = %ld)\n", 41762306a36Sopenharmony_ci (version & 0x0ffff000) >> 12); 41862306a36Sopenharmony_ci return -EAGAIN; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci netdev_dbg(dev, "Am79C960 (PCnet-ISA) Revision %ld\n", 42262306a36Sopenharmony_ci (version & 0xf0000000) >> 28); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ariadne_init_ring(dev); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Miscellaneous Stuff */ 42762306a36Sopenharmony_ci lance->RAP = CSR3; /* Interrupt Masks and Deferral Control */ 42862306a36Sopenharmony_ci lance->RDP = 0x0000; 42962306a36Sopenharmony_ci lance->RAP = CSR4; /* Test and Features Control */ 43062306a36Sopenharmony_ci lance->RDP = DPOLL | APAD_XMT | MFCOM | RCVCCOM | TXSTRTM | JABM; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Set the Multicast Table */ 43362306a36Sopenharmony_ci lance->RAP = CSR8; /* Logical Address Filter, LADRF[15:0] */ 43462306a36Sopenharmony_ci lance->RDP = 0x0000; 43562306a36Sopenharmony_ci lance->RAP = CSR9; /* Logical Address Filter, LADRF[31:16] */ 43662306a36Sopenharmony_ci lance->RDP = 0x0000; 43762306a36Sopenharmony_ci lance->RAP = CSR10; /* Logical Address Filter, LADRF[47:32] */ 43862306a36Sopenharmony_ci lance->RDP = 0x0000; 43962306a36Sopenharmony_ci lance->RAP = CSR11; /* Logical Address Filter, LADRF[63:48] */ 44062306a36Sopenharmony_ci lance->RDP = 0x0000; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Set the Ethernet Hardware Address */ 44362306a36Sopenharmony_ci lance->RAP = CSR12; /* Physical Address Register, PADR[15:0] */ 44462306a36Sopenharmony_ci lance->RDP = ((const u_short *)&dev->dev_addr[0])[0]; 44562306a36Sopenharmony_ci lance->RAP = CSR13; /* Physical Address Register, PADR[31:16] */ 44662306a36Sopenharmony_ci lance->RDP = ((const u_short *)&dev->dev_addr[0])[1]; 44762306a36Sopenharmony_ci lance->RAP = CSR14; /* Physical Address Register, PADR[47:32] */ 44862306a36Sopenharmony_ci lance->RDP = ((const u_short *)&dev->dev_addr[0])[2]; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Set the Init Block Mode */ 45162306a36Sopenharmony_ci lance->RAP = CSR15; /* Mode Register */ 45262306a36Sopenharmony_ci lance->RDP = 0x0000; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* Set the Transmit Descriptor Ring Pointer */ 45562306a36Sopenharmony_ci lance->RAP = CSR30; /* Base Address of Transmit Ring */ 45662306a36Sopenharmony_ci lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, tx_ring)); 45762306a36Sopenharmony_ci lance->RAP = CSR31; /* Base Address of transmit Ring */ 45862306a36Sopenharmony_ci lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, tx_ring)); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Set the Receive Descriptor Ring Pointer */ 46162306a36Sopenharmony_ci lance->RAP = CSR24; /* Base Address of Receive Ring */ 46262306a36Sopenharmony_ci lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, rx_ring)); 46362306a36Sopenharmony_ci lance->RAP = CSR25; /* Base Address of Receive Ring */ 46462306a36Sopenharmony_ci lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, rx_ring)); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Set the Number of RX and TX Ring Entries */ 46762306a36Sopenharmony_ci lance->RAP = CSR76; /* Receive Ring Length */ 46862306a36Sopenharmony_ci lance->RDP = swapw(((u_short)-RX_RING_SIZE)); 46962306a36Sopenharmony_ci lance->RAP = CSR78; /* Transmit Ring Length */ 47062306a36Sopenharmony_ci lance->RDP = swapw(((u_short)-TX_RING_SIZE)); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Enable Media Interface Port Auto Select (10BASE-2/10BASE-T) */ 47362306a36Sopenharmony_ci lance->RAP = ISACSR2; /* Miscellaneous Configuration */ 47462306a36Sopenharmony_ci lance->IDP = ASEL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* LED Control */ 47762306a36Sopenharmony_ci lance->RAP = ISACSR5; /* LED1 Status */ 47862306a36Sopenharmony_ci lance->IDP = PSE|XMTE; 47962306a36Sopenharmony_ci lance->RAP = ISACSR6; /* LED2 Status */ 48062306a36Sopenharmony_ci lance->IDP = PSE|COLE; 48162306a36Sopenharmony_ci lance->RAP = ISACSR7; /* LED3 Status */ 48262306a36Sopenharmony_ci lance->IDP = PSE|RCVE; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci netif_start_queue(dev); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, IRQF_SHARED, 48762306a36Sopenharmony_ci dev->name, dev); 48862306a36Sopenharmony_ci if (i) 48962306a36Sopenharmony_ci return i; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 49262306a36Sopenharmony_ci lance->RDP = INEA | STRT; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int ariadne_close(struct net_device *dev) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci netif_stop_queue(dev); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci lance->RAP = CSR112; /* Missed Frame Count */ 50462306a36Sopenharmony_ci dev->stats.rx_missed_errors = swapw(lance->RDP); 50562306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (ariadne_debug > 1) { 50862306a36Sopenharmony_ci netdev_dbg(dev, "Shutting down ethercard, status was %02x\n", 50962306a36Sopenharmony_ci lance->RDP); 51062306a36Sopenharmony_ci netdev_dbg(dev, "%lu packets missed\n", 51162306a36Sopenharmony_ci dev->stats.rx_missed_errors); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* We stop the LANCE here -- it occasionally polls memory if we don't */ 51562306a36Sopenharmony_ci lance->RDP = STOP; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci free_irq(IRQ_AMIGA_PORTS, dev); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic inline void ariadne_reset(struct net_device *dev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 52762306a36Sopenharmony_ci lance->RDP = STOP; 52862306a36Sopenharmony_ci ariadne_init_ring(dev); 52962306a36Sopenharmony_ci lance->RDP = INEA | STRT; 53062306a36Sopenharmony_ci netif_start_queue(dev); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void ariadne_tx_timeout(struct net_device *dev, unsigned int txqueue) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci netdev_err(dev, "transmit timed out, status %04x, resetting\n", 53862306a36Sopenharmony_ci lance->RDP); 53962306a36Sopenharmony_ci ariadne_reset(dev); 54062306a36Sopenharmony_ci netif_wake_queue(dev); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic netdev_tx_t ariadne_start_xmit(struct sk_buff *skb, 54462306a36Sopenharmony_ci struct net_device *dev) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct ariadne_private *priv = netdev_priv(dev); 54762306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 54862306a36Sopenharmony_ci int entry; 54962306a36Sopenharmony_ci unsigned long flags; 55062306a36Sopenharmony_ci int len = skb->len; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci#if 0 55362306a36Sopenharmony_ci if (ariadne_debug > 3) { 55462306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 55562306a36Sopenharmony_ci netdev_dbg(dev, "%s: csr0 %04x\n", __func__, lance->RDP); 55662306a36Sopenharmony_ci lance->RDP = 0x0000; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci#endif 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* FIXME: is the 79C960 new enough to do its own padding right ? */ 56162306a36Sopenharmony_ci if (skb->len < ETH_ZLEN) { 56262306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) 56362306a36Sopenharmony_ci return NETDEV_TX_OK; 56462306a36Sopenharmony_ci len = ETH_ZLEN; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Fill in a Tx ring entry */ 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci netdev_dbg(dev, "TX pkt type 0x%04x from %pM to %pM data %p len %u\n", 57062306a36Sopenharmony_ci ((u_short *)skb->data)[6], 57162306a36Sopenharmony_ci skb->data + 6, skb->data, 57262306a36Sopenharmony_ci skb->data, skb->len); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci local_irq_save(flags); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci entry = priv->cur_tx % TX_RING_SIZE; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* Caution: the write order is important here, set the base address with 57962306a36Sopenharmony_ci the "ownership" bits last */ 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len); 58262306a36Sopenharmony_ci priv->tx_ring[entry]->TMD3 = 0x0000; 58362306a36Sopenharmony_ci memcpyw(priv->tx_buff[entry], (u_short *)skb->data, len); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci#ifdef DEBUG 58662306a36Sopenharmony_ci print_hex_dump(KERN_DEBUG, "tx_buff: ", DUMP_PREFIX_OFFSET, 16, 1, 58762306a36Sopenharmony_ci (void *)priv->tx_buff[entry], 58862306a36Sopenharmony_ci skb->len > 64 ? 64 : skb->len, true); 58962306a36Sopenharmony_ci#endif 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci priv->tx_ring[entry]->TMD1 = (priv->tx_ring[entry]->TMD1 & 0xff00) 59262306a36Sopenharmony_ci | TF_OWN | TF_STP | TF_ENP; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci dev_kfree_skb(skb); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci priv->cur_tx++; 59762306a36Sopenharmony_ci if ((priv->cur_tx >= TX_RING_SIZE) && 59862306a36Sopenharmony_ci (priv->dirty_tx >= TX_RING_SIZE)) { 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci netdev_dbg(dev, "*** Subtracting TX_RING_SIZE from cur_tx (%d) and dirty_tx (%d)\n", 60162306a36Sopenharmony_ci priv->cur_tx, priv->dirty_tx); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci priv->cur_tx -= TX_RING_SIZE; 60462306a36Sopenharmony_ci priv->dirty_tx -= TX_RING_SIZE; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci dev->stats.tx_bytes += len; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Trigger an immediate send poll */ 60962306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 61062306a36Sopenharmony_ci lance->RDP = INEA | TDMD; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (lowb(priv->tx_ring[(entry + 1) % TX_RING_SIZE]->TMD1) != 0) { 61362306a36Sopenharmony_ci netif_stop_queue(dev); 61462306a36Sopenharmony_ci priv->tx_full = 1; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci local_irq_restore(flags); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return NETDEV_TX_OK; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic struct net_device_stats *ariadne_get_stats(struct net_device *dev) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 62462306a36Sopenharmony_ci short saved_addr; 62562306a36Sopenharmony_ci unsigned long flags; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci local_irq_save(flags); 62862306a36Sopenharmony_ci saved_addr = lance->RAP; 62962306a36Sopenharmony_ci lance->RAP = CSR112; /* Missed Frame Count */ 63062306a36Sopenharmony_ci dev->stats.rx_missed_errors = swapw(lance->RDP); 63162306a36Sopenharmony_ci lance->RAP = saved_addr; 63262306a36Sopenharmony_ci local_irq_restore(flags); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return &dev->stats; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 63862306a36Sopenharmony_ci * num_addrs == -1 Promiscuous mode, receive all packets 63962306a36Sopenharmony_ci * num_addrs == 0 Normal mode, clear multicast list 64062306a36Sopenharmony_ci * num_addrs > 0 Multicast mode, receive normal and MC packets, 64162306a36Sopenharmony_ci * and do best-effort filtering. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (!netif_running(dev)) 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci netif_stop_queue(dev); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* We take the simple way out and always enable promiscuous mode */ 65362306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 65462306a36Sopenharmony_ci lance->RDP = STOP; /* Temporarily stop the lance */ 65562306a36Sopenharmony_ci ariadne_init_ring(dev); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 65862306a36Sopenharmony_ci lance->RAP = CSR15; /* Mode Register */ 65962306a36Sopenharmony_ci lance->RDP = PROM; /* Set promiscuous mode */ 66062306a36Sopenharmony_ci } else { 66162306a36Sopenharmony_ci short multicast_table[4]; 66262306a36Sopenharmony_ci int num_addrs = netdev_mc_count(dev); 66362306a36Sopenharmony_ci int i; 66462306a36Sopenharmony_ci /* We don't use the multicast table, 66562306a36Sopenharmony_ci * but rely on upper-layer filtering 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci memset(multicast_table, (num_addrs == 0) ? 0 : -1, 66862306a36Sopenharmony_ci sizeof(multicast_table)); 66962306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 67062306a36Sopenharmony_ci lance->RAP = CSR8 + (i << 8); 67162306a36Sopenharmony_ci /* Logical Address Filter */ 67262306a36Sopenharmony_ci lance->RDP = swapw(multicast_table[i]); 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci lance->RAP = CSR15; /* Mode Register */ 67562306a36Sopenharmony_ci lance->RDP = 0x0000; /* Unset promiscuous mode */ 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 67962306a36Sopenharmony_ci lance->RDP = INEA | STRT | IDON;/* Resume normal operation */ 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci netif_wake_queue(dev); 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic void ariadne_remove_one(struct zorro_dev *z) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct net_device *dev = zorro_get_drvdata(z); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci unregister_netdev(dev); 69062306a36Sopenharmony_ci release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960)); 69162306a36Sopenharmony_ci release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE); 69262306a36Sopenharmony_ci free_netdev(dev); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic const struct zorro_device_id ariadne_zorro_tbl[] = { 69662306a36Sopenharmony_ci { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, 69762306a36Sopenharmony_ci { 0 } 69862306a36Sopenharmony_ci}; 69962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic const struct net_device_ops ariadne_netdev_ops = { 70262306a36Sopenharmony_ci .ndo_open = ariadne_open, 70362306a36Sopenharmony_ci .ndo_stop = ariadne_close, 70462306a36Sopenharmony_ci .ndo_start_xmit = ariadne_start_xmit, 70562306a36Sopenharmony_ci .ndo_tx_timeout = ariadne_tx_timeout, 70662306a36Sopenharmony_ci .ndo_get_stats = ariadne_get_stats, 70762306a36Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 70862306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 70962306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 71062306a36Sopenharmony_ci}; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int ariadne_init_one(struct zorro_dev *z, 71362306a36Sopenharmony_ci const struct zorro_device_id *ent) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci unsigned long board = z->resource.start; 71662306a36Sopenharmony_ci unsigned long base_addr = board + ARIADNE_LANCE; 71762306a36Sopenharmony_ci unsigned long mem_start = board + ARIADNE_RAM; 71862306a36Sopenharmony_ci struct resource *r1, *r2; 71962306a36Sopenharmony_ci struct net_device *dev; 72062306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 72162306a36Sopenharmony_ci u32 serial; 72262306a36Sopenharmony_ci int err; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960"); 72562306a36Sopenharmony_ci if (!r1) 72662306a36Sopenharmony_ci return -EBUSY; 72762306a36Sopenharmony_ci r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM"); 72862306a36Sopenharmony_ci if (!r2) { 72962306a36Sopenharmony_ci release_mem_region(base_addr, sizeof(struct Am79C960)); 73062306a36Sopenharmony_ci return -EBUSY; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct ariadne_private)); 73462306a36Sopenharmony_ci if (!dev) { 73562306a36Sopenharmony_ci release_mem_region(base_addr, sizeof(struct Am79C960)); 73662306a36Sopenharmony_ci release_mem_region(mem_start, ARIADNE_RAM_SIZE); 73762306a36Sopenharmony_ci return -ENOMEM; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci r1->name = dev->name; 74162306a36Sopenharmony_ci r2->name = dev->name; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci serial = be32_to_cpu(z->rom.er_SerialNumber); 74462306a36Sopenharmony_ci addr[0] = 0x00; 74562306a36Sopenharmony_ci addr[1] = 0x60; 74662306a36Sopenharmony_ci addr[2] = 0x30; 74762306a36Sopenharmony_ci addr[3] = (serial >> 16) & 0xff; 74862306a36Sopenharmony_ci addr[4] = (serial >> 8) & 0xff; 74962306a36Sopenharmony_ci addr[5] = serial & 0xff; 75062306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 75162306a36Sopenharmony_ci dev->base_addr = (unsigned long)ZTWO_VADDR(base_addr); 75262306a36Sopenharmony_ci dev->mem_start = (unsigned long)ZTWO_VADDR(mem_start); 75362306a36Sopenharmony_ci dev->mem_end = dev->mem_start + ARIADNE_RAM_SIZE; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci dev->netdev_ops = &ariadne_netdev_ops; 75662306a36Sopenharmony_ci dev->watchdog_timeo = 5 * HZ; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci err = register_netdev(dev); 75962306a36Sopenharmony_ci if (err) { 76062306a36Sopenharmony_ci release_mem_region(base_addr, sizeof(struct Am79C960)); 76162306a36Sopenharmony_ci release_mem_region(mem_start, ARIADNE_RAM_SIZE); 76262306a36Sopenharmony_ci free_netdev(dev); 76362306a36Sopenharmony_ci return err; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci zorro_set_drvdata(z, dev); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci netdev_info(dev, "Ariadne at 0x%08lx, Ethernet Address %pM\n", 76862306a36Sopenharmony_ci board, dev->dev_addr); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic struct zorro_driver ariadne_driver = { 77462306a36Sopenharmony_ci .name = "ariadne", 77562306a36Sopenharmony_ci .id_table = ariadne_zorro_tbl, 77662306a36Sopenharmony_ci .probe = ariadne_init_one, 77762306a36Sopenharmony_ci .remove = ariadne_remove_one, 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int __init ariadne_init_module(void) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci return zorro_register_driver(&ariadne_driver); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void __exit ariadne_cleanup_module(void) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci zorro_unregister_driver(&ariadne_driver); 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cimodule_init(ariadne_init_module); 79162306a36Sopenharmony_cimodule_exit(ariadne_cleanup_module); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 794