18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Amiga Linux/m68k Ariadne Ethernet Driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * © Copyright 1995-2003 by Geert Uytterhoeven (geert@linux-m68k.org) 58c2ecf20Sopenharmony_ci * Peter De Schrijver (p2@mind.be) 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is based on 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * lance.c: An AMD LANCE ethernet driver for linux. 128c2ecf20Sopenharmony_ci * Written 1993-94 by Donald Becker. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Am79C960: PCnet(tm)-ISA Single-Chip Ethernet Controller 158c2ecf20Sopenharmony_ci * Advanced Micro Devices 168c2ecf20Sopenharmony_ci * Publication #16907, Rev. B, Amendment/0, May 1994 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * MC68230: Parallel Interface/Timer (PI/T) 198c2ecf20Sopenharmony_ci * Motorola Semiconductors, December, 1983 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 248c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of the Linux 258c2ecf20Sopenharmony_ci * distribution for more details. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * The Ariadne is a Zorro-II board made by Village Tronic. It contains: 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * - an Am79C960 PCnet-ISA Single-Chip Ethernet Controller with both 328c2ecf20Sopenharmony_ci * 10BASE-2 (thin coax) and 10BASE-T (UTP) connectors 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * - an MC68230 Parallel Interface/Timer configured as 2 parallel ports 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 388c2ecf20Sopenharmony_ci/*#define DEBUG*/ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/stddef.h> 428c2ecf20Sopenharmony_ci#include <linux/kernel.h> 438c2ecf20Sopenharmony_ci#include <linux/string.h> 448c2ecf20Sopenharmony_ci#include <linux/errno.h> 458c2ecf20Sopenharmony_ci#include <linux/ioport.h> 468c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 478c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 488c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 498c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 508c2ecf20Sopenharmony_ci#include <linux/init.h> 518c2ecf20Sopenharmony_ci#include <linux/zorro.h> 528c2ecf20Sopenharmony_ci#include <linux/bitops.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 558c2ecf20Sopenharmony_ci#include <asm/amigaints.h> 568c2ecf20Sopenharmony_ci#include <asm/amigahw.h> 578c2ecf20Sopenharmony_ci#include <asm/irq.h> 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#include "ariadne.h" 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#ifdef ARIADNE_DEBUG 628c2ecf20Sopenharmony_ciint ariadne_debug = ARIADNE_DEBUG; 638c2ecf20Sopenharmony_ci#else 648c2ecf20Sopenharmony_ciint ariadne_debug = 1; 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Macros to Fix Endianness problems */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Swap the Bytes in a WORD */ 708c2ecf20Sopenharmony_ci#define swapw(x) (((x >> 8) & 0x00ff) | ((x << 8) & 0xff00)) 718c2ecf20Sopenharmony_ci/* Get the Low BYTE in a WORD */ 728c2ecf20Sopenharmony_ci#define lowb(x) (x & 0xff) 738c2ecf20Sopenharmony_ci/* Get the Swapped High WORD in a LONG */ 748c2ecf20Sopenharmony_ci#define swhighw(x) ((((x) >> 8) & 0xff00) | (((x) >> 24) & 0x00ff)) 758c2ecf20Sopenharmony_ci/* Get the Swapped Low WORD in a LONG */ 768c2ecf20Sopenharmony_ci#define swloww(x) ((((x) << 8) & 0xff00) | (((x) >> 8) & 0x00ff)) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Transmit/Receive Ring Definitions */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define TX_RING_SIZE 5 818c2ecf20Sopenharmony_ci#define RX_RING_SIZE 16 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define PKT_BUF_SIZE 1520 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Private Device Data */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct ariadne_private { 888c2ecf20Sopenharmony_ci volatile struct TDRE *tx_ring[TX_RING_SIZE]; 898c2ecf20Sopenharmony_ci volatile struct RDRE *rx_ring[RX_RING_SIZE]; 908c2ecf20Sopenharmony_ci volatile u_short *tx_buff[TX_RING_SIZE]; 918c2ecf20Sopenharmony_ci volatile u_short *rx_buff[RX_RING_SIZE]; 928c2ecf20Sopenharmony_ci int cur_tx, cur_rx; /* The next free ring entry */ 938c2ecf20Sopenharmony_ci int dirty_tx; /* The ring entries to be free()ed */ 948c2ecf20Sopenharmony_ci char tx_full; 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* Structure Created in the Ariadne's RAM Buffer */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct lancedata { 1008c2ecf20Sopenharmony_ci struct TDRE tx_ring[TX_RING_SIZE]; 1018c2ecf20Sopenharmony_ci struct RDRE rx_ring[RX_RING_SIZE]; 1028c2ecf20Sopenharmony_ci u_short tx_buff[TX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)]; 1038c2ecf20Sopenharmony_ci u_short rx_buff[RX_RING_SIZE][PKT_BUF_SIZE / sizeof(u_short)]; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void memcpyw(volatile u_short *dest, u_short *src, int len) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci while (len >= 2) { 1098c2ecf20Sopenharmony_ci *(dest++) = *(src++); 1108c2ecf20Sopenharmony_ci len -= 2; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci if (len == 1) 1138c2ecf20Sopenharmony_ci *dest = (*(u_char *)src) << 8; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void ariadne_init_ring(struct net_device *dev) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct ariadne_private *priv = netdev_priv(dev); 1198c2ecf20Sopenharmony_ci volatile struct lancedata *lancedata = (struct lancedata *)dev->mem_start; 1208c2ecf20Sopenharmony_ci int i; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci netif_stop_queue(dev); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci priv->tx_full = 0; 1258c2ecf20Sopenharmony_ci priv->cur_rx = priv->cur_tx = 0; 1268c2ecf20Sopenharmony_ci priv->dirty_tx = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Set up TX Ring */ 1298c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 1308c2ecf20Sopenharmony_ci volatile struct TDRE *t = &lancedata->tx_ring[i]; 1318c2ecf20Sopenharmony_ci t->TMD0 = swloww(ARIADNE_RAM + 1328c2ecf20Sopenharmony_ci offsetof(struct lancedata, tx_buff[i])); 1338c2ecf20Sopenharmony_ci t->TMD1 = swhighw(ARIADNE_RAM + 1348c2ecf20Sopenharmony_ci offsetof(struct lancedata, tx_buff[i])) | 1358c2ecf20Sopenharmony_ci TF_STP | TF_ENP; 1368c2ecf20Sopenharmony_ci t->TMD2 = swapw((u_short)-PKT_BUF_SIZE); 1378c2ecf20Sopenharmony_ci t->TMD3 = 0; 1388c2ecf20Sopenharmony_ci priv->tx_ring[i] = &lancedata->tx_ring[i]; 1398c2ecf20Sopenharmony_ci priv->tx_buff[i] = lancedata->tx_buff[i]; 1408c2ecf20Sopenharmony_ci netdev_dbg(dev, "TX Entry %2d at %p, Buf at %p\n", 1418c2ecf20Sopenharmony_ci i, &lancedata->tx_ring[i], lancedata->tx_buff[i]); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Set up RX Ring */ 1458c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 1468c2ecf20Sopenharmony_ci volatile struct RDRE *r = &lancedata->rx_ring[i]; 1478c2ecf20Sopenharmony_ci r->RMD0 = swloww(ARIADNE_RAM + 1488c2ecf20Sopenharmony_ci offsetof(struct lancedata, rx_buff[i])); 1498c2ecf20Sopenharmony_ci r->RMD1 = swhighw(ARIADNE_RAM + 1508c2ecf20Sopenharmony_ci offsetof(struct lancedata, rx_buff[i])) | 1518c2ecf20Sopenharmony_ci RF_OWN; 1528c2ecf20Sopenharmony_ci r->RMD2 = swapw((u_short)-PKT_BUF_SIZE); 1538c2ecf20Sopenharmony_ci r->RMD3 = 0x0000; 1548c2ecf20Sopenharmony_ci priv->rx_ring[i] = &lancedata->rx_ring[i]; 1558c2ecf20Sopenharmony_ci priv->rx_buff[i] = lancedata->rx_buff[i]; 1568c2ecf20Sopenharmony_ci netdev_dbg(dev, "RX Entry %2d at %p, Buf at %p\n", 1578c2ecf20Sopenharmony_ci i, &lancedata->rx_ring[i], lancedata->rx_buff[i]); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int ariadne_rx(struct net_device *dev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct ariadne_private *priv = netdev_priv(dev); 1648c2ecf20Sopenharmony_ci int entry = priv->cur_rx % RX_RING_SIZE; 1658c2ecf20Sopenharmony_ci int i; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* If we own the next entry, it's a new packet. Send it up */ 1688c2ecf20Sopenharmony_ci while (!(lowb(priv->rx_ring[entry]->RMD1) & RF_OWN)) { 1698c2ecf20Sopenharmony_ci int status = lowb(priv->rx_ring[entry]->RMD1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (status != (RF_STP | RF_ENP)) { /* There was an error */ 1728c2ecf20Sopenharmony_ci /* There is a tricky error noted by 1738c2ecf20Sopenharmony_ci * John Murphy <murf@perftech.com> to Russ Nelson: 1748c2ecf20Sopenharmony_ci * Even with full-sized buffers it's possible for a 1758c2ecf20Sopenharmony_ci * jabber packet to use two buffers, with only the 1768c2ecf20Sopenharmony_ci * last correctly noting the error 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci /* Only count a general error at the end of a packet */ 1798c2ecf20Sopenharmony_ci if (status & RF_ENP) 1808c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 1818c2ecf20Sopenharmony_ci if (status & RF_FRAM) 1828c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 1838c2ecf20Sopenharmony_ci if (status & RF_OFLO) 1848c2ecf20Sopenharmony_ci dev->stats.rx_over_errors++; 1858c2ecf20Sopenharmony_ci if (status & RF_CRC) 1868c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 1878c2ecf20Sopenharmony_ci if (status & RF_BUFF) 1888c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors++; 1898c2ecf20Sopenharmony_ci priv->rx_ring[entry]->RMD1 &= 0xff00 | RF_STP | RF_ENP; 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci /* Malloc up new buffer, compatible with net-3 */ 1928c2ecf20Sopenharmony_ci short pkt_len = swapw(priv->rx_ring[entry]->RMD3); 1938c2ecf20Sopenharmony_ci struct sk_buff *skb; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, pkt_len + 2); 1968c2ecf20Sopenharmony_ci if (skb == NULL) { 1978c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) 1988c2ecf20Sopenharmony_ci if (lowb(priv->rx_ring[(entry + i) % RX_RING_SIZE]->RMD1) & RF_OWN) 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (i > RX_RING_SIZE - 2) { 2028c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 2038c2ecf20Sopenharmony_ci priv->rx_ring[entry]->RMD1 |= RF_OWN; 2048c2ecf20Sopenharmony_ci priv->cur_rx++; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align */ 2118c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); /* Make room */ 2128c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, 2138c2ecf20Sopenharmony_ci (const void *)priv->rx_buff[entry], 2148c2ecf20Sopenharmony_ci pkt_len); 2158c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 2168c2ecf20Sopenharmony_ci netdev_dbg(dev, "RX pkt type 0x%04x from %pM to %pM data %p len %u\n", 2178c2ecf20Sopenharmony_ci ((u_short *)skb->data)[6], 2188c2ecf20Sopenharmony_ci skb->data + 6, skb->data, 2198c2ecf20Sopenharmony_ci skb->data, skb->len); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci netif_rx(skb); 2228c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 2238c2ecf20Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci priv->rx_ring[entry]->RMD1 |= RF_OWN; 2278c2ecf20Sopenharmony_ci entry = (++priv->cur_rx) % RX_RING_SIZE; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci priv->cur_rx = priv->cur_rx % RX_RING_SIZE; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* We should check that at least two ring entries are free. 2338c2ecf20Sopenharmony_ci * If not, we should free one and mark stats->rx_dropped++ 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic irqreturn_t ariadne_interrupt(int irq, void *data) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *)data; 2428c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 2438c2ecf20Sopenharmony_ci struct ariadne_private *priv; 2448c2ecf20Sopenharmony_ci int csr0, boguscnt; 2458c2ecf20Sopenharmony_ci int handled = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!(lance->RDP & INTR)) /* Check if any interrupt has been */ 2508c2ecf20Sopenharmony_ci return IRQ_NONE; /* generated by the board */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci boguscnt = 10; 2558c2ecf20Sopenharmony_ci while ((csr0 = lance->RDP) & (ERR | RINT | TINT) && --boguscnt >= 0) { 2568c2ecf20Sopenharmony_ci /* Acknowledge all of the current interrupt sources ASAP */ 2578c2ecf20Sopenharmony_ci lance->RDP = csr0 & ~(INEA | TDMD | STOP | STRT | INIT); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#ifdef DEBUG 2608c2ecf20Sopenharmony_ci if (ariadne_debug > 5) { 2618c2ecf20Sopenharmony_ci netdev_dbg(dev, "interrupt csr0=%#02x new csr=%#02x [", 2628c2ecf20Sopenharmony_ci csr0, lance->RDP); 2638c2ecf20Sopenharmony_ci if (csr0 & INTR) 2648c2ecf20Sopenharmony_ci pr_cont(" INTR"); 2658c2ecf20Sopenharmony_ci if (csr0 & INEA) 2668c2ecf20Sopenharmony_ci pr_cont(" INEA"); 2678c2ecf20Sopenharmony_ci if (csr0 & RXON) 2688c2ecf20Sopenharmony_ci pr_cont(" RXON"); 2698c2ecf20Sopenharmony_ci if (csr0 & TXON) 2708c2ecf20Sopenharmony_ci pr_cont(" TXON"); 2718c2ecf20Sopenharmony_ci if (csr0 & TDMD) 2728c2ecf20Sopenharmony_ci pr_cont(" TDMD"); 2738c2ecf20Sopenharmony_ci if (csr0 & STOP) 2748c2ecf20Sopenharmony_ci pr_cont(" STOP"); 2758c2ecf20Sopenharmony_ci if (csr0 & STRT) 2768c2ecf20Sopenharmony_ci pr_cont(" STRT"); 2778c2ecf20Sopenharmony_ci if (csr0 & INIT) 2788c2ecf20Sopenharmony_ci pr_cont(" INIT"); 2798c2ecf20Sopenharmony_ci if (csr0 & ERR) 2808c2ecf20Sopenharmony_ci pr_cont(" ERR"); 2818c2ecf20Sopenharmony_ci if (csr0 & BABL) 2828c2ecf20Sopenharmony_ci pr_cont(" BABL"); 2838c2ecf20Sopenharmony_ci if (csr0 & CERR) 2848c2ecf20Sopenharmony_ci pr_cont(" CERR"); 2858c2ecf20Sopenharmony_ci if (csr0 & MISS) 2868c2ecf20Sopenharmony_ci pr_cont(" MISS"); 2878c2ecf20Sopenharmony_ci if (csr0 & MERR) 2888c2ecf20Sopenharmony_ci pr_cont(" MERR"); 2898c2ecf20Sopenharmony_ci if (csr0 & RINT) 2908c2ecf20Sopenharmony_ci pr_cont(" RINT"); 2918c2ecf20Sopenharmony_ci if (csr0 & TINT) 2928c2ecf20Sopenharmony_ci pr_cont(" TINT"); 2938c2ecf20Sopenharmony_ci if (csr0 & IDON) 2948c2ecf20Sopenharmony_ci pr_cont(" IDON"); 2958c2ecf20Sopenharmony_ci pr_cont(" ]\n"); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci#endif 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (csr0 & RINT) { /* Rx interrupt */ 3008c2ecf20Sopenharmony_ci handled = 1; 3018c2ecf20Sopenharmony_ci ariadne_rx(dev); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (csr0 & TINT) { /* Tx-done interrupt */ 3058c2ecf20Sopenharmony_ci int dirty_tx = priv->dirty_tx; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci handled = 1; 3088c2ecf20Sopenharmony_ci while (dirty_tx < priv->cur_tx) { 3098c2ecf20Sopenharmony_ci int entry = dirty_tx % TX_RING_SIZE; 3108c2ecf20Sopenharmony_ci int status = lowb(priv->tx_ring[entry]->TMD1); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (status & TF_OWN) 3138c2ecf20Sopenharmony_ci break; /* It still hasn't been Txed */ 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci priv->tx_ring[entry]->TMD1 &= 0xff00; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (status & TF_ERR) { 3188c2ecf20Sopenharmony_ci /* There was an major error, log it */ 3198c2ecf20Sopenharmony_ci int err_status = priv->tx_ring[entry]->TMD3; 3208c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 3218c2ecf20Sopenharmony_ci if (err_status & EF_RTRY) 3228c2ecf20Sopenharmony_ci dev->stats.tx_aborted_errors++; 3238c2ecf20Sopenharmony_ci if (err_status & EF_LCAR) 3248c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors++; 3258c2ecf20Sopenharmony_ci if (err_status & EF_LCOL) 3268c2ecf20Sopenharmony_ci dev->stats.tx_window_errors++; 3278c2ecf20Sopenharmony_ci if (err_status & EF_UFLO) { 3288c2ecf20Sopenharmony_ci /* Ackk! On FIFO errors the Tx unit is turned off! */ 3298c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 3308c2ecf20Sopenharmony_ci /* Remove this verbosity later! */ 3318c2ecf20Sopenharmony_ci netdev_err(dev, "Tx FIFO error! Status %04x\n", 3328c2ecf20Sopenharmony_ci csr0); 3338c2ecf20Sopenharmony_ci /* Restart the chip */ 3348c2ecf20Sopenharmony_ci lance->RDP = STRT; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci if (status & (TF_MORE | TF_ONE)) 3388c2ecf20Sopenharmony_ci dev->stats.collisions++; 3398c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci dirty_tx++; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci#ifndef final_version 3458c2ecf20Sopenharmony_ci if (priv->cur_tx - dirty_tx >= TX_RING_SIZE) { 3468c2ecf20Sopenharmony_ci netdev_err(dev, "out-of-sync dirty pointer, %d vs. %d, full=%d\n", 3478c2ecf20Sopenharmony_ci dirty_tx, priv->cur_tx, 3488c2ecf20Sopenharmony_ci priv->tx_full); 3498c2ecf20Sopenharmony_ci dirty_tx += TX_RING_SIZE; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci#endif 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (priv->tx_full && netif_queue_stopped(dev) && 3548c2ecf20Sopenharmony_ci dirty_tx > priv->cur_tx - TX_RING_SIZE + 2) { 3558c2ecf20Sopenharmony_ci /* The ring is no longer full */ 3568c2ecf20Sopenharmony_ci priv->tx_full = 0; 3578c2ecf20Sopenharmony_ci netif_wake_queue(dev); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci priv->dirty_tx = dirty_tx; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Log misc errors */ 3648c2ecf20Sopenharmony_ci if (csr0 & BABL) { 3658c2ecf20Sopenharmony_ci handled = 1; 3668c2ecf20Sopenharmony_ci dev->stats.tx_errors++; /* Tx babble */ 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci if (csr0 & MISS) { 3698c2ecf20Sopenharmony_ci handled = 1; 3708c2ecf20Sopenharmony_ci dev->stats.rx_errors++; /* Missed a Rx frame */ 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci if (csr0 & MERR) { 3738c2ecf20Sopenharmony_ci handled = 1; 3748c2ecf20Sopenharmony_ci netdev_err(dev, "Bus master arbitration failure, status %04x\n", 3758c2ecf20Sopenharmony_ci csr0); 3768c2ecf20Sopenharmony_ci /* Restart the chip */ 3778c2ecf20Sopenharmony_ci lance->RDP = STRT; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Clear any other interrupt, and set interrupt enable */ 3828c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 3838c2ecf20Sopenharmony_ci lance->RDP = INEA | BABL | CERR | MISS | MERR | IDON; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (ariadne_debug > 4) 3868c2ecf20Sopenharmony_ci netdev_dbg(dev, "exiting interrupt, csr%d=%#04x\n", 3878c2ecf20Sopenharmony_ci lance->RAP, lance->RDP); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int ariadne_open(struct net_device *dev) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 3958c2ecf20Sopenharmony_ci u_short in; 3968c2ecf20Sopenharmony_ci u_long version; 3978c2ecf20Sopenharmony_ci int i; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Reset the LANCE */ 4008c2ecf20Sopenharmony_ci in = lance->Reset; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Stop the LANCE */ 4038c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 4048c2ecf20Sopenharmony_ci lance->RDP = STOP; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* Check the LANCE version */ 4078c2ecf20Sopenharmony_ci lance->RAP = CSR88; /* Chip ID */ 4088c2ecf20Sopenharmony_ci version = swapw(lance->RDP); 4098c2ecf20Sopenharmony_ci lance->RAP = CSR89; /* Chip ID */ 4108c2ecf20Sopenharmony_ci version |= swapw(lance->RDP) << 16; 4118c2ecf20Sopenharmony_ci if ((version & 0x00000fff) != 0x00000003) { 4128c2ecf20Sopenharmony_ci pr_warn("Couldn't find AMD Ethernet Chip\n"); 4138c2ecf20Sopenharmony_ci return -EAGAIN; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if ((version & 0x0ffff000) != 0x00003000) { 4168c2ecf20Sopenharmony_ci pr_warn("Couldn't find Am79C960 (Wrong part number = %ld)\n", 4178c2ecf20Sopenharmony_ci (version & 0x0ffff000) >> 12); 4188c2ecf20Sopenharmony_ci return -EAGAIN; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci netdev_dbg(dev, "Am79C960 (PCnet-ISA) Revision %ld\n", 4228c2ecf20Sopenharmony_ci (version & 0xf0000000) >> 28); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci ariadne_init_ring(dev); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Miscellaneous Stuff */ 4278c2ecf20Sopenharmony_ci lance->RAP = CSR3; /* Interrupt Masks and Deferral Control */ 4288c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 4298c2ecf20Sopenharmony_ci lance->RAP = CSR4; /* Test and Features Control */ 4308c2ecf20Sopenharmony_ci lance->RDP = DPOLL | APAD_XMT | MFCOM | RCVCCOM | TXSTRTM | JABM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Set the Multicast Table */ 4338c2ecf20Sopenharmony_ci lance->RAP = CSR8; /* Logical Address Filter, LADRF[15:0] */ 4348c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 4358c2ecf20Sopenharmony_ci lance->RAP = CSR9; /* Logical Address Filter, LADRF[31:16] */ 4368c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 4378c2ecf20Sopenharmony_ci lance->RAP = CSR10; /* Logical Address Filter, LADRF[47:32] */ 4388c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 4398c2ecf20Sopenharmony_ci lance->RAP = CSR11; /* Logical Address Filter, LADRF[63:48] */ 4408c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Set the Ethernet Hardware Address */ 4438c2ecf20Sopenharmony_ci lance->RAP = CSR12; /* Physical Address Register, PADR[15:0] */ 4448c2ecf20Sopenharmony_ci lance->RDP = ((u_short *)&dev->dev_addr[0])[0]; 4458c2ecf20Sopenharmony_ci lance->RAP = CSR13; /* Physical Address Register, PADR[31:16] */ 4468c2ecf20Sopenharmony_ci lance->RDP = ((u_short *)&dev->dev_addr[0])[1]; 4478c2ecf20Sopenharmony_ci lance->RAP = CSR14; /* Physical Address Register, PADR[47:32] */ 4488c2ecf20Sopenharmony_ci lance->RDP = ((u_short *)&dev->dev_addr[0])[2]; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* Set the Init Block Mode */ 4518c2ecf20Sopenharmony_ci lance->RAP = CSR15; /* Mode Register */ 4528c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Set the Transmit Descriptor Ring Pointer */ 4558c2ecf20Sopenharmony_ci lance->RAP = CSR30; /* Base Address of Transmit Ring */ 4568c2ecf20Sopenharmony_ci lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, tx_ring)); 4578c2ecf20Sopenharmony_ci lance->RAP = CSR31; /* Base Address of transmit Ring */ 4588c2ecf20Sopenharmony_ci lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, tx_ring)); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Set the Receive Descriptor Ring Pointer */ 4618c2ecf20Sopenharmony_ci lance->RAP = CSR24; /* Base Address of Receive Ring */ 4628c2ecf20Sopenharmony_ci lance->RDP = swloww(ARIADNE_RAM + offsetof(struct lancedata, rx_ring)); 4638c2ecf20Sopenharmony_ci lance->RAP = CSR25; /* Base Address of Receive Ring */ 4648c2ecf20Sopenharmony_ci lance->RDP = swhighw(ARIADNE_RAM + offsetof(struct lancedata, rx_ring)); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Set the Number of RX and TX Ring Entries */ 4678c2ecf20Sopenharmony_ci lance->RAP = CSR76; /* Receive Ring Length */ 4688c2ecf20Sopenharmony_ci lance->RDP = swapw(((u_short)-RX_RING_SIZE)); 4698c2ecf20Sopenharmony_ci lance->RAP = CSR78; /* Transmit Ring Length */ 4708c2ecf20Sopenharmony_ci lance->RDP = swapw(((u_short)-TX_RING_SIZE)); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* Enable Media Interface Port Auto Select (10BASE-2/10BASE-T) */ 4738c2ecf20Sopenharmony_ci lance->RAP = ISACSR2; /* Miscellaneous Configuration */ 4748c2ecf20Sopenharmony_ci lance->IDP = ASEL; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* LED Control */ 4778c2ecf20Sopenharmony_ci lance->RAP = ISACSR5; /* LED1 Status */ 4788c2ecf20Sopenharmony_ci lance->IDP = PSE|XMTE; 4798c2ecf20Sopenharmony_ci lance->RAP = ISACSR6; /* LED2 Status */ 4808c2ecf20Sopenharmony_ci lance->IDP = PSE|COLE; 4818c2ecf20Sopenharmony_ci lance->RAP = ISACSR7; /* LED3 Status */ 4828c2ecf20Sopenharmony_ci lance->IDP = PSE|RCVE; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci netif_start_queue(dev); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci i = request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, IRQF_SHARED, 4878c2ecf20Sopenharmony_ci dev->name, dev); 4888c2ecf20Sopenharmony_ci if (i) 4898c2ecf20Sopenharmony_ci return i; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 4928c2ecf20Sopenharmony_ci lance->RDP = INEA | STRT; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int ariadne_close(struct net_device *dev) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci netif_stop_queue(dev); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci lance->RAP = CSR112; /* Missed Frame Count */ 5048c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors = swapw(lance->RDP); 5058c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (ariadne_debug > 1) { 5088c2ecf20Sopenharmony_ci netdev_dbg(dev, "Shutting down ethercard, status was %02x\n", 5098c2ecf20Sopenharmony_ci lance->RDP); 5108c2ecf20Sopenharmony_ci netdev_dbg(dev, "%lu packets missed\n", 5118c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* We stop the LANCE here -- it occasionally polls memory if we don't */ 5158c2ecf20Sopenharmony_ci lance->RDP = STOP; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci free_irq(IRQ_AMIGA_PORTS, dev); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic inline void ariadne_reset(struct net_device *dev) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 5278c2ecf20Sopenharmony_ci lance->RDP = STOP; 5288c2ecf20Sopenharmony_ci ariadne_init_ring(dev); 5298c2ecf20Sopenharmony_ci lance->RDP = INEA | STRT; 5308c2ecf20Sopenharmony_ci netif_start_queue(dev); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void ariadne_tx_timeout(struct net_device *dev, unsigned int txqueue) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci netdev_err(dev, "transmit timed out, status %04x, resetting\n", 5388c2ecf20Sopenharmony_ci lance->RDP); 5398c2ecf20Sopenharmony_ci ariadne_reset(dev); 5408c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic netdev_tx_t ariadne_start_xmit(struct sk_buff *skb, 5448c2ecf20Sopenharmony_ci struct net_device *dev) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct ariadne_private *priv = netdev_priv(dev); 5478c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 5488c2ecf20Sopenharmony_ci int entry; 5498c2ecf20Sopenharmony_ci unsigned long flags; 5508c2ecf20Sopenharmony_ci int len = skb->len; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci#if 0 5538c2ecf20Sopenharmony_ci if (ariadne_debug > 3) { 5548c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 5558c2ecf20Sopenharmony_ci netdev_dbg(dev, "%s: csr0 %04x\n", __func__, lance->RDP); 5568c2ecf20Sopenharmony_ci lance->RDP = 0x0000; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci#endif 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* FIXME: is the 79C960 new enough to do its own padding right ? */ 5618c2ecf20Sopenharmony_ci if (skb->len < ETH_ZLEN) { 5628c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) 5638c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5648c2ecf20Sopenharmony_ci len = ETH_ZLEN; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Fill in a Tx ring entry */ 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci netdev_dbg(dev, "TX pkt type 0x%04x from %pM to %pM data %p len %u\n", 5708c2ecf20Sopenharmony_ci ((u_short *)skb->data)[6], 5718c2ecf20Sopenharmony_ci skb->data + 6, skb->data, 5728c2ecf20Sopenharmony_ci skb->data, skb->len); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci local_irq_save(flags); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci entry = priv->cur_tx % TX_RING_SIZE; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* Caution: the write order is important here, set the base address with 5798c2ecf20Sopenharmony_ci the "ownership" bits last */ 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci priv->tx_ring[entry]->TMD2 = swapw((u_short)-skb->len); 5828c2ecf20Sopenharmony_ci priv->tx_ring[entry]->TMD3 = 0x0000; 5838c2ecf20Sopenharmony_ci memcpyw(priv->tx_buff[entry], (u_short *)skb->data, len); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci#ifdef DEBUG 5868c2ecf20Sopenharmony_ci print_hex_dump(KERN_DEBUG, "tx_buff: ", DUMP_PREFIX_OFFSET, 16, 1, 5878c2ecf20Sopenharmony_ci (void *)priv->tx_buff[entry], 5888c2ecf20Sopenharmony_ci skb->len > 64 ? 64 : skb->len, true); 5898c2ecf20Sopenharmony_ci#endif 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci priv->tx_ring[entry]->TMD1 = (priv->tx_ring[entry]->TMD1 & 0xff00) 5928c2ecf20Sopenharmony_ci | TF_OWN | TF_STP | TF_ENP; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci priv->cur_tx++; 5978c2ecf20Sopenharmony_ci if ((priv->cur_tx >= TX_RING_SIZE) && 5988c2ecf20Sopenharmony_ci (priv->dirty_tx >= TX_RING_SIZE)) { 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci netdev_dbg(dev, "*** Subtracting TX_RING_SIZE from cur_tx (%d) and dirty_tx (%d)\n", 6018c2ecf20Sopenharmony_ci priv->cur_tx, priv->dirty_tx); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci priv->cur_tx -= TX_RING_SIZE; 6048c2ecf20Sopenharmony_ci priv->dirty_tx -= TX_RING_SIZE; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci dev->stats.tx_bytes += len; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Trigger an immediate send poll */ 6098c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 6108c2ecf20Sopenharmony_ci lance->RDP = INEA | TDMD; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (lowb(priv->tx_ring[(entry + 1) % TX_RING_SIZE]->TMD1) != 0) { 6138c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6148c2ecf20Sopenharmony_ci priv->tx_full = 1; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci local_irq_restore(flags); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic struct net_device_stats *ariadne_get_stats(struct net_device *dev) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 6248c2ecf20Sopenharmony_ci short saved_addr; 6258c2ecf20Sopenharmony_ci unsigned long flags; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci local_irq_save(flags); 6288c2ecf20Sopenharmony_ci saved_addr = lance->RAP; 6298c2ecf20Sopenharmony_ci lance->RAP = CSR112; /* Missed Frame Count */ 6308c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors = swapw(lance->RDP); 6318c2ecf20Sopenharmony_ci lance->RAP = saved_addr; 6328c2ecf20Sopenharmony_ci local_irq_restore(flags); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return &dev->stats; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 6388c2ecf20Sopenharmony_ci * num_addrs == -1 Promiscuous mode, receive all packets 6398c2ecf20Sopenharmony_ci * num_addrs == 0 Normal mode, clear multicast list 6408c2ecf20Sopenharmony_ci * num_addrs > 0 Multicast mode, receive normal and MC packets, 6418c2ecf20Sopenharmony_ci * and do best-effort filtering. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_cistatic void set_multicast_list(struct net_device *dev) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci volatile struct Am79C960 *lance = (struct Am79C960 *)dev->base_addr; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (!netif_running(dev)) 6488c2ecf20Sopenharmony_ci return; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* We take the simple way out and always enable promiscuous mode */ 6538c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 6548c2ecf20Sopenharmony_ci lance->RDP = STOP; /* Temporarily stop the lance */ 6558c2ecf20Sopenharmony_ci ariadne_init_ring(dev); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 6588c2ecf20Sopenharmony_ci lance->RAP = CSR15; /* Mode Register */ 6598c2ecf20Sopenharmony_ci lance->RDP = PROM; /* Set promiscuous mode */ 6608c2ecf20Sopenharmony_ci } else { 6618c2ecf20Sopenharmony_ci short multicast_table[4]; 6628c2ecf20Sopenharmony_ci int num_addrs = netdev_mc_count(dev); 6638c2ecf20Sopenharmony_ci int i; 6648c2ecf20Sopenharmony_ci /* We don't use the multicast table, 6658c2ecf20Sopenharmony_ci * but rely on upper-layer filtering 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ci memset(multicast_table, (num_addrs == 0) ? 0 : -1, 6688c2ecf20Sopenharmony_ci sizeof(multicast_table)); 6698c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 6708c2ecf20Sopenharmony_ci lance->RAP = CSR8 + (i << 8); 6718c2ecf20Sopenharmony_ci /* Logical Address Filter */ 6728c2ecf20Sopenharmony_ci lance->RDP = swapw(multicast_table[i]); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci lance->RAP = CSR15; /* Mode Register */ 6758c2ecf20Sopenharmony_ci lance->RDP = 0x0000; /* Unset promiscuous mode */ 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci lance->RAP = CSR0; /* PCnet-ISA Controller Status */ 6798c2ecf20Sopenharmony_ci lance->RDP = INEA | STRT | IDON;/* Resume normal operation */ 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci netif_wake_queue(dev); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic void ariadne_remove_one(struct zorro_dev *z) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct net_device *dev = zorro_get_drvdata(z); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci unregister_netdev(dev); 6908c2ecf20Sopenharmony_ci release_mem_region(ZTWO_PADDR(dev->base_addr), sizeof(struct Am79C960)); 6918c2ecf20Sopenharmony_ci release_mem_region(ZTWO_PADDR(dev->mem_start), ARIADNE_RAM_SIZE); 6928c2ecf20Sopenharmony_ci free_netdev(dev); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic const struct zorro_device_id ariadne_zorro_tbl[] = { 6968c2ecf20Sopenharmony_ci { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, 6978c2ecf20Sopenharmony_ci { 0 } 6988c2ecf20Sopenharmony_ci}; 6998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(zorro, ariadne_zorro_tbl); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic const struct net_device_ops ariadne_netdev_ops = { 7028c2ecf20Sopenharmony_ci .ndo_open = ariadne_open, 7038c2ecf20Sopenharmony_ci .ndo_stop = ariadne_close, 7048c2ecf20Sopenharmony_ci .ndo_start_xmit = ariadne_start_xmit, 7058c2ecf20Sopenharmony_ci .ndo_tx_timeout = ariadne_tx_timeout, 7068c2ecf20Sopenharmony_ci .ndo_get_stats = ariadne_get_stats, 7078c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 7088c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 7098c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 7108c2ecf20Sopenharmony_ci}; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int ariadne_init_one(struct zorro_dev *z, 7138c2ecf20Sopenharmony_ci const struct zorro_device_id *ent) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci unsigned long board = z->resource.start; 7168c2ecf20Sopenharmony_ci unsigned long base_addr = board + ARIADNE_LANCE; 7178c2ecf20Sopenharmony_ci unsigned long mem_start = board + ARIADNE_RAM; 7188c2ecf20Sopenharmony_ci struct resource *r1, *r2; 7198c2ecf20Sopenharmony_ci struct net_device *dev; 7208c2ecf20Sopenharmony_ci u32 serial; 7218c2ecf20Sopenharmony_ci int err; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci r1 = request_mem_region(base_addr, sizeof(struct Am79C960), "Am79C960"); 7248c2ecf20Sopenharmony_ci if (!r1) 7258c2ecf20Sopenharmony_ci return -EBUSY; 7268c2ecf20Sopenharmony_ci r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM"); 7278c2ecf20Sopenharmony_ci if (!r2) { 7288c2ecf20Sopenharmony_ci release_mem_region(base_addr, sizeof(struct Am79C960)); 7298c2ecf20Sopenharmony_ci return -EBUSY; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct ariadne_private)); 7338c2ecf20Sopenharmony_ci if (dev == NULL) { 7348c2ecf20Sopenharmony_ci release_mem_region(base_addr, sizeof(struct Am79C960)); 7358c2ecf20Sopenharmony_ci release_mem_region(mem_start, ARIADNE_RAM_SIZE); 7368c2ecf20Sopenharmony_ci return -ENOMEM; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci r1->name = dev->name; 7408c2ecf20Sopenharmony_ci r2->name = dev->name; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci serial = be32_to_cpu(z->rom.er_SerialNumber); 7438c2ecf20Sopenharmony_ci dev->dev_addr[0] = 0x00; 7448c2ecf20Sopenharmony_ci dev->dev_addr[1] = 0x60; 7458c2ecf20Sopenharmony_ci dev->dev_addr[2] = 0x30; 7468c2ecf20Sopenharmony_ci dev->dev_addr[3] = (serial >> 16) & 0xff; 7478c2ecf20Sopenharmony_ci dev->dev_addr[4] = (serial >> 8) & 0xff; 7488c2ecf20Sopenharmony_ci dev->dev_addr[5] = serial & 0xff; 7498c2ecf20Sopenharmony_ci dev->base_addr = (unsigned long)ZTWO_VADDR(base_addr); 7508c2ecf20Sopenharmony_ci dev->mem_start = (unsigned long)ZTWO_VADDR(mem_start); 7518c2ecf20Sopenharmony_ci dev->mem_end = dev->mem_start + ARIADNE_RAM_SIZE; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci dev->netdev_ops = &ariadne_netdev_ops; 7548c2ecf20Sopenharmony_ci dev->watchdog_timeo = 5 * HZ; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci err = register_netdev(dev); 7578c2ecf20Sopenharmony_ci if (err) { 7588c2ecf20Sopenharmony_ci release_mem_region(base_addr, sizeof(struct Am79C960)); 7598c2ecf20Sopenharmony_ci release_mem_region(mem_start, ARIADNE_RAM_SIZE); 7608c2ecf20Sopenharmony_ci free_netdev(dev); 7618c2ecf20Sopenharmony_ci return err; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci zorro_set_drvdata(z, dev); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci netdev_info(dev, "Ariadne at 0x%08lx, Ethernet Address %pM\n", 7668c2ecf20Sopenharmony_ci board, dev->dev_addr); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic struct zorro_driver ariadne_driver = { 7728c2ecf20Sopenharmony_ci .name = "ariadne", 7738c2ecf20Sopenharmony_ci .id_table = ariadne_zorro_tbl, 7748c2ecf20Sopenharmony_ci .probe = ariadne_init_one, 7758c2ecf20Sopenharmony_ci .remove = ariadne_remove_one, 7768c2ecf20Sopenharmony_ci}; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic int __init ariadne_init_module(void) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci return zorro_register_driver(&ariadne_driver); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic void __exit ariadne_cleanup_module(void) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci zorro_unregister_driver(&ariadne_driver); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cimodule_init(ariadne_init_module); 7898c2ecf20Sopenharmony_cimodule_exit(ariadne_cleanup_module); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 792