18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/acorn/net/ether3.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995-2000 Russell King 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card 88c2ecf20Sopenharmony_ci * for Acorn machines 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * By Russell King, with some suggestions from borris@ant.co.uk 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Changelog: 138c2ecf20Sopenharmony_ci * 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet 148c2ecf20Sopenharmony_ci * address up to the higher levels - they're 158c2ecf20Sopenharmony_ci * silently ignored. I/F can now be put into 168c2ecf20Sopenharmony_ci * multicast mode. Receiver routine optimised. 178c2ecf20Sopenharmony_ci * 1.05 RMK 30/02/1996 Now claims interrupt at open when part of 188c2ecf20Sopenharmony_ci * the kernel rather than when a module. 198c2ecf20Sopenharmony_ci * 1.06 RMK 02/03/1996 Various code cleanups 208c2ecf20Sopenharmony_ci * 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit 218c2ecf20Sopenharmony_ci * routines. 228c2ecf20Sopenharmony_ci * 1.08 RMK 14/10/1996 Fixed problem with too many packets, 238c2ecf20Sopenharmony_ci * prevented the kernel message about dropped 248c2ecf20Sopenharmony_ci * packets appearing too many times a second. 258c2ecf20Sopenharmony_ci * Now does not disable all IRQs, only the IRQ 268c2ecf20Sopenharmony_ci * used by this card. 278c2ecf20Sopenharmony_ci * 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low, 288c2ecf20Sopenharmony_ci * but we still service the TX queue if we get a 298c2ecf20Sopenharmony_ci * RX interrupt. 308c2ecf20Sopenharmony_ci * 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004. 318c2ecf20Sopenharmony_ci * 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A. 328c2ecf20Sopenharmony_ci * 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1. 338c2ecf20Sopenharmony_ci * RMK 27/06/1998 Changed asm/delay.h to linux/delay.h. 348c2ecf20Sopenharmony_ci * 1.13 RMK 29/06/1998 Fixed problem with transmission of packets. 358c2ecf20Sopenharmony_ci * Chip seems to have a bug in, whereby if the 368c2ecf20Sopenharmony_ci * packet starts two bytes from the end of the 378c2ecf20Sopenharmony_ci * buffer, it corrupts the receiver chain, and 388c2ecf20Sopenharmony_ci * never updates the transmit status correctly. 398c2ecf20Sopenharmony_ci * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. 408c2ecf20Sopenharmony_ci * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy 418c2ecf20Sopenharmony_ci * hardware. 428c2ecf20Sopenharmony_ci * 1.16 RMK 10/02/2000 Updated for 2.3.43 438c2ecf20Sopenharmony_ci * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include <linux/module.h> 478c2ecf20Sopenharmony_ci#include <linux/kernel.h> 488c2ecf20Sopenharmony_ci#include <linux/types.h> 498c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 508c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 518c2ecf20Sopenharmony_ci#include <linux/ioport.h> 528c2ecf20Sopenharmony_ci#include <linux/in.h> 538c2ecf20Sopenharmony_ci#include <linux/slab.h> 548c2ecf20Sopenharmony_ci#include <linux/string.h> 558c2ecf20Sopenharmony_ci#include <linux/errno.h> 568c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 578c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 588c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 598c2ecf20Sopenharmony_ci#include <linux/device.h> 608c2ecf20Sopenharmony_ci#include <linux/init.h> 618c2ecf20Sopenharmony_ci#include <linux/delay.h> 628c2ecf20Sopenharmony_ci#include <linux/bitops.h> 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <asm/ecard.h> 658c2ecf20Sopenharmony_ci#include <asm/io.h> 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic char version[] = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#include "ether3.h" 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic unsigned int net_debug = NET_DEBUG; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void ether3_setmulticastlist(struct net_device *dev); 748c2ecf20Sopenharmony_cistatic int ether3_rx(struct net_device *dev, unsigned int maxcnt); 758c2ecf20Sopenharmony_cistatic void ether3_tx(struct net_device *dev); 768c2ecf20Sopenharmony_cistatic int ether3_open (struct net_device *dev); 778c2ecf20Sopenharmony_cistatic netdev_tx_t ether3_sendpacket(struct sk_buff *skb, 788c2ecf20Sopenharmony_ci struct net_device *dev); 798c2ecf20Sopenharmony_cistatic irqreturn_t ether3_interrupt (int irq, void *dev_id); 808c2ecf20Sopenharmony_cistatic int ether3_close (struct net_device *dev); 818c2ecf20Sopenharmony_cistatic void ether3_setmulticastlist (struct net_device *dev); 828c2ecf20Sopenharmony_cistatic void ether3_timeout(struct net_device *dev, unsigned int txqueue); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define BUS_16 2 858c2ecf20Sopenharmony_ci#define BUS_8 1 868c2ecf20Sopenharmony_ci#define BUS_UNKNOWN 0 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------- */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_citypedef enum { 918c2ecf20Sopenharmony_ci buffer_write, 928c2ecf20Sopenharmony_ci buffer_read 938c2ecf20Sopenharmony_ci} buffer_rw_t; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * ether3 read/write. Slow things down a bit... 978c2ecf20Sopenharmony_ci * The SEEQ8005 doesn't like us writing to its registers 988c2ecf20Sopenharmony_ci * too quickly. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistatic inline void ether3_outb(int v, void __iomem *r) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci writeb(v, r); 1038c2ecf20Sopenharmony_ci udelay(1); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic inline void ether3_outw(int v, void __iomem *r) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci writew(v, r); 1098c2ecf20Sopenharmony_ci udelay(1); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci#define ether3_inb(r) ({ unsigned int __v = readb((r)); udelay(1); __v; }) 1128c2ecf20Sopenharmony_ci#define ether3_inw(r) ({ unsigned int __v = readw((r)); udelay(1); __v; }) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int 1158c2ecf20Sopenharmony_ciether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int timeout = 1000; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); 1208c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command | CMD_FIFOWRITE, REG_COMMAND); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) { 1238c2ecf20Sopenharmony_ci if (!timeout--) { 1248c2ecf20Sopenharmony_ci printk("%s: setbuffer broken\n", dev->name); 1258c2ecf20Sopenharmony_ci priv(dev)->broken = 1; 1268c2ecf20Sopenharmony_ci return 1; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci udelay(1); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (read == buffer_read) { 1328c2ecf20Sopenharmony_ci ether3_outw(start, REG_DMAADDR); 1338c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command | CMD_FIFOREAD, REG_COMMAND); 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command | CMD_FIFOWRITE, REG_COMMAND); 1368c2ecf20Sopenharmony_ci ether3_outw(start, REG_DMAADDR); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/* 1428c2ecf20Sopenharmony_ci * write data to the buffer memory 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci#define ether3_writebuffer(dev,data,length) \ 1458c2ecf20Sopenharmony_ci writesw(REG_BUFWIN, (data), (length) >> 1) 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define ether3_writeword(dev,data) \ 1488c2ecf20Sopenharmony_ci writew((data), REG_BUFWIN) 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#define ether3_writelong(dev,data) { \ 1518c2ecf20Sopenharmony_ci void __iomem *reg_bufwin = REG_BUFWIN; \ 1528c2ecf20Sopenharmony_ci writew((data), reg_bufwin); \ 1538c2ecf20Sopenharmony_ci writew((data) >> 16, reg_bufwin); \ 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * read data from the buffer memory 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci#define ether3_readbuffer(dev,data,length) \ 1608c2ecf20Sopenharmony_ci readsw(REG_BUFWIN, (data), (length) >> 1) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define ether3_readword(dev) \ 1638c2ecf20Sopenharmony_ci readw(REG_BUFWIN) 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci#define ether3_readlong(dev) \ 1668c2ecf20Sopenharmony_ci readw(REG_BUFWIN) | (readw(REG_BUFWIN) << 16) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Switch LED off... 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cistatic void ether3_ledoff(struct timer_list *t) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct dev_priv *private = from_timer(private, t, timer); 1748c2ecf20Sopenharmony_ci struct net_device *dev = private->dev; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config2 |= CFG2_CTRLO, REG_CONFIG2); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * switch LED on... 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistatic inline void ether3_ledon(struct net_device *dev) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci del_timer(&priv(dev)->timer); 1858c2ecf20Sopenharmony_ci priv(dev)->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */ 1868c2ecf20Sopenharmony_ci add_timer(&priv(dev)->timer); 1878c2ecf20Sopenharmony_ci if (priv(dev)->regs.config2 & CFG2_CTRLO) 1888c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * Read the ethernet address string from the on board rom. 1938c2ecf20Sopenharmony_ci * This is an ascii string!!! 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_cistatic int 1968c2ecf20Sopenharmony_ciether3_addr(char *addr, struct expansion_card *ec) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct in_chunk_dir cd; 1998c2ecf20Sopenharmony_ci char *s; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci for (i = 0; i<6; i++) { 2048c2ecf20Sopenharmony_ci addr[i] = simple_strtoul(s + 1, &s, 0x10); 2058c2ecf20Sopenharmony_ci if (*s != (i==5?')' : ':' )) 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci if (i == 6) 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci /* I wonder if we should even let the user continue in this case 2128c2ecf20Sopenharmony_ci * - no, it would be better to disable the device 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n"); 2158c2ecf20Sopenharmony_ci return -ENODEV; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------- */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int 2218c2ecf20Sopenharmony_ciether3_ramtest(struct net_device *dev, unsigned char byte) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL); 2248c2ecf20Sopenharmony_ci int i,ret = 0; 2258c2ecf20Sopenharmony_ci int max_errors = 4; 2268c2ecf20Sopenharmony_ci int bad = -1; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!buffer) 2298c2ecf20Sopenharmony_ci return 1; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci memset(buffer, byte, RX_END); 2328c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, 0); 2338c2ecf20Sopenharmony_ci ether3_writebuffer(dev, buffer, TX_END); 2348c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, RX_START); 2358c2ecf20Sopenharmony_ci ether3_writebuffer(dev, buffer + RX_START, RX_LEN); 2368c2ecf20Sopenharmony_ci memset(buffer, byte ^ 0xff, RX_END); 2378c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_read, 0); 2388c2ecf20Sopenharmony_ci ether3_readbuffer(dev, buffer, TX_END); 2398c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_read, RX_START); 2408c2ecf20Sopenharmony_ci ether3_readbuffer(dev, buffer + RX_START, RX_LEN); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (i = 0; i < RX_END; i++) { 2438c2ecf20Sopenharmony_ci if (buffer[i] != byte) { 2448c2ecf20Sopenharmony_ci if (max_errors > 0 && bad != buffer[i]) { 2458c2ecf20Sopenharmony_ci printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X", 2468c2ecf20Sopenharmony_ci dev->name, buffer[i], byte, i); 2478c2ecf20Sopenharmony_ci ret = 2; 2488c2ecf20Sopenharmony_ci max_errors--; 2498c2ecf20Sopenharmony_ci bad = i; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } else { 2528c2ecf20Sopenharmony_ci if (bad != -1) { 2538c2ecf20Sopenharmony_ci if (bad != i - 1) 2548c2ecf20Sopenharmony_ci printk(" - 0x%04X\n", i - 1); 2558c2ecf20Sopenharmony_ci printk("\n"); 2568c2ecf20Sopenharmony_ci bad = -1; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci if (bad != -1) 2618c2ecf20Sopenharmony_ci printk(" - 0xffff\n"); 2628c2ecf20Sopenharmony_ci kfree(buffer); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------------- */ 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int ether3_init_2(struct net_device *dev) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int i; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci priv(dev)->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8; 2748c2ecf20Sopenharmony_ci priv(dev)->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC; 2758c2ecf20Sopenharmony_ci priv(dev)->regs.command = 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * Set up our hardware address 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); 2818c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 2828c2ecf20Sopenharmony_ci ether3_outb(dev->dev_addr[i], REG_BUFWIN); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) 2858c2ecf20Sopenharmony_ci priv(dev)->regs.config1 |= CFG1_RECVPROMISC; 2868c2ecf20Sopenharmony_ci else if (dev->flags & IFF_MULTICAST) 2878c2ecf20Sopenharmony_ci priv(dev)->regs.config1 |= CFG1_RECVSPECBRMULTI; 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci priv(dev)->regs.config1 |= CFG1_RECVSPECBROAD; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * There is a problem with the NQ8005 in that it occasionally loses the 2938c2ecf20Sopenharmony_ci * last two bytes. To get round this problem, we receive the CRC as 2948c2ecf20Sopenharmony_ci * well. That way, if we do lose the last two, then it doesn't matter. 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); 2978c2ecf20Sopenharmony_ci ether3_outw((TX_END>>8) - 1, REG_BUFWIN); 2988c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->rx_head, REG_RECVPTR); 2998c2ecf20Sopenharmony_ci ether3_outw(0, REG_TRANSMITPTR); 3008c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND); 3018c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config2, REG_CONFIG2); 3028c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); 3038c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command, REG_COMMAND); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci i = ether3_ramtest(dev, 0x5A); 3068c2ecf20Sopenharmony_ci if(i) 3078c2ecf20Sopenharmony_ci return i; 3088c2ecf20Sopenharmony_ci i = ether3_ramtest(dev, 0x1E); 3098c2ecf20Sopenharmony_ci if(i) 3108c2ecf20Sopenharmony_ci return i; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, 0); 3138c2ecf20Sopenharmony_ci ether3_writelong(dev, 0); 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void 3188c2ecf20Sopenharmony_ciether3_init_for_open(struct net_device *dev) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci int i; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Reset the chip */ 3238c2ecf20Sopenharmony_ci ether3_outw(CFG2_RESET, REG_CONFIG2); 3248c2ecf20Sopenharmony_ci udelay(4); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci priv(dev)->regs.command = 0; 3278c2ecf20Sopenharmony_ci ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); 3288c2ecf20Sopenharmony_ci while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)) 3298c2ecf20Sopenharmony_ci barrier(); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); 3328c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 3338c2ecf20Sopenharmony_ci ether3_outb(dev->dev_addr[i], REG_BUFWIN); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci priv(dev)->tx_head = 0; 3368c2ecf20Sopenharmony_ci priv(dev)->tx_tail = 0; 3378c2ecf20Sopenharmony_ci priv(dev)->regs.config2 |= CFG2_CTRLO; 3388c2ecf20Sopenharmony_ci priv(dev)->rx_head = RX_START; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); 3418c2ecf20Sopenharmony_ci ether3_outw((TX_END>>8) - 1, REG_BUFWIN); 3428c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->rx_head, REG_RECVPTR); 3438c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->rx_head >> 8, REG_RECVEND); 3448c2ecf20Sopenharmony_ci ether3_outw(0, REG_TRANSMITPTR); 3458c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config2, REG_CONFIG2); 3468c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, 0); 3498c2ecf20Sopenharmony_ci ether3_writelong(dev, 0); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci priv(dev)->regs.command = CMD_ENINTRX | CMD_ENINTTX; 3528c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic inline int 3568c2ecf20Sopenharmony_ciether3_probe_bus_8(struct net_device *dev, int val) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci int write_low, write_high, read_low, read_high; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci write_low = val & 255; 3618c2ecf20Sopenharmony_ci write_high = val >> 8; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ether3_outb(write_low, REG_RECVPTR); 3668c2ecf20Sopenharmony_ci ether3_outb(write_high, REG_RECVPTR + 4); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci read_low = ether3_inb(REG_RECVPTR); 3698c2ecf20Sopenharmony_ci read_high = ether3_inb(REG_RECVPTR + 4); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci printk(", read8 [%02X:%02X]\n", read_high, read_low); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return read_low == write_low && read_high == write_high; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic inline int 3778c2ecf20Sopenharmony_ciether3_probe_bus_16(struct net_device *dev, int val) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci int read_val; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ether3_outw(val, REG_RECVPTR); 3828c2ecf20Sopenharmony_ci read_val = ether3_inw(REG_RECVPTR); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return read_val == val; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * Open/initialize the board. This is called (in the current kernel) 3918c2ecf20Sopenharmony_ci * sometime after booting when the 'ifconfig' program is run. 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * This routine should set everything up anew at each open, even 3948c2ecf20Sopenharmony_ci * registers that "should" only need to be set once at boot, so that 3958c2ecf20Sopenharmony_ci * there is non-reboot way to recover if something goes wrong. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_cistatic int 3988c2ecf20Sopenharmony_ciether3_open(struct net_device *dev) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) 4018c2ecf20Sopenharmony_ci return -EAGAIN; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ether3_init_for_open(dev); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci netif_start_queue(dev); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* 4118c2ecf20Sopenharmony_ci * The inverse routine to ether3_open(). 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_cistatic int 4148c2ecf20Sopenharmony_ciether3_close(struct net_device *dev) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci disable_irq(dev->irq); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); 4218c2ecf20Sopenharmony_ci priv(dev)->regs.command = 0; 4228c2ecf20Sopenharmony_ci while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)) 4238c2ecf20Sopenharmony_ci barrier(); 4248c2ecf20Sopenharmony_ci ether3_outb(0x80, REG_CONFIG2 + 4); 4258c2ecf20Sopenharmony_ci ether3_outw(0, REG_COMMAND); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* 4338c2ecf20Sopenharmony_ci * Set or clear promiscuous/multicast mode filter for this adaptor. 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * We don't attempt any packet filtering. The card may have a SEEQ 8004 4368c2ecf20Sopenharmony_ci * in which does not have the other ethernet address registers present... 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_cistatic void ether3_setmulticastlist(struct net_device *dev) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci priv(dev)->regs.config1 &= ~CFG1_RECVPROMISC; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 4438c2ecf20Sopenharmony_ci /* promiscuous mode */ 4448c2ecf20Sopenharmony_ci priv(dev)->regs.config1 |= CFG1_RECVPROMISC; 4458c2ecf20Sopenharmony_ci } else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) { 4468c2ecf20Sopenharmony_ci priv(dev)->regs.config1 |= CFG1_RECVSPECBRMULTI; 4478c2ecf20Sopenharmony_ci } else 4488c2ecf20Sopenharmony_ci priv(dev)->regs.config1 |= CFG1_RECVSPECBROAD; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void ether3_timeout(struct net_device *dev, unsigned int txqueue) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci unsigned long flags; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci del_timer(&priv(dev)->timer); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci local_irq_save(flags); 4608c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name); 4618c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name, 4628c2ecf20Sopenharmony_ci ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); 4638c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, 4648c2ecf20Sopenharmony_ci ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); 4658c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, 4668c2ecf20Sopenharmony_ci priv(dev)->tx_head, priv(dev)->tx_tail); 4678c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_read, priv(dev)->tx_tail); 4688c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); 4698c2ecf20Sopenharmony_ci local_irq_restore(flags); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci priv(dev)->regs.config2 |= CFG2_CTRLO; 4728c2ecf20Sopenharmony_ci dev->stats.tx_errors += 1; 4738c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.config2, REG_CONFIG2); 4748c2ecf20Sopenharmony_ci priv(dev)->tx_head = priv(dev)->tx_tail = 0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci netif_wake_queue(dev); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci/* 4808c2ecf20Sopenharmony_ci * Transmit a packet 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_cistatic netdev_tx_t 4838c2ecf20Sopenharmony_ciether3_sendpacket(struct sk_buff *skb, struct net_device *dev) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci unsigned long flags; 4868c2ecf20Sopenharmony_ci unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; 4878c2ecf20Sopenharmony_ci unsigned int ptr, next_ptr; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (priv(dev)->broken) { 4908c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 4918c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 4928c2ecf20Sopenharmony_ci netif_start_queue(dev); 4938c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci length = (length + 1) & ~1; 4978c2ecf20Sopenharmony_ci if (length != skb->len) { 4988c2ecf20Sopenharmony_ci if (skb_padto(skb, length)) 4998c2ecf20Sopenharmony_ci goto out; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci next_ptr = (priv(dev)->tx_head + 1) & 15; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci local_irq_save(flags); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (priv(dev)->tx_tail == next_ptr) { 5078c2ecf20Sopenharmony_ci local_irq_restore(flags); 5088c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; /* unable to queue */ 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ptr = 0x600 * priv(dev)->tx_head; 5128c2ecf20Sopenharmony_ci priv(dev)->tx_head = next_ptr; 5138c2ecf20Sopenharmony_ci next_ptr *= 0x600; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, next_ptr); 5188c2ecf20Sopenharmony_ci ether3_writelong(dev, 0); 5198c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, ptr); 5208c2ecf20Sopenharmony_ci ether3_writelong(dev, 0); 5218c2ecf20Sopenharmony_ci ether3_writebuffer(dev, skb->data, length); 5228c2ecf20Sopenharmony_ci ether3_writeword(dev, htons(next_ptr)); 5238c2ecf20Sopenharmony_ci ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); 5248c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_write, ptr); 5258c2ecf20Sopenharmony_ci ether3_writeword(dev, htons((ptr + length + 4))); 5268c2ecf20Sopenharmony_ci ether3_writeword(dev, TXHDR_FLAGS >> 16); 5278c2ecf20Sopenharmony_ci ether3_ledon(dev); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (!(ether3_inw(REG_STATUS) & STAT_TXON)) { 5308c2ecf20Sopenharmony_ci ether3_outw(ptr, REG_TRANSMITPTR); 5318c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command | CMD_TXON, REG_COMMAND); 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci next_ptr = (priv(dev)->tx_head + 1) & 15; 5358c2ecf20Sopenharmony_ci local_irq_restore(flags); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (priv(dev)->tx_tail == next_ptr) 5408c2ecf20Sopenharmony_ci netif_stop_queue(dev); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci out: 5438c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic irqreturn_t 5478c2ecf20Sopenharmony_ciether3_interrupt(int irq, void *dev_id) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_id; 5508c2ecf20Sopenharmony_ci unsigned int status, handled = IRQ_NONE; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci#if NET_DEBUG > 1 5538c2ecf20Sopenharmony_ci if(net_debug & DEBUG_INT) 5548c2ecf20Sopenharmony_ci printk("eth3irq: %d ", irq); 5558c2ecf20Sopenharmony_ci#endif 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci status = ether3_inw(REG_STATUS); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (status & STAT_INTRX) { 5608c2ecf20Sopenharmony_ci ether3_outw(CMD_ACKINTRX | priv(dev)->regs.command, REG_COMMAND); 5618c2ecf20Sopenharmony_ci ether3_rx(dev, 12); 5628c2ecf20Sopenharmony_ci handled = IRQ_HANDLED; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (status & STAT_INTTX) { 5668c2ecf20Sopenharmony_ci ether3_outw(CMD_ACKINTTX | priv(dev)->regs.command, REG_COMMAND); 5678c2ecf20Sopenharmony_ci ether3_tx(dev); 5688c2ecf20Sopenharmony_ci handled = IRQ_HANDLED; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci#if NET_DEBUG > 1 5728c2ecf20Sopenharmony_ci if(net_debug & DEBUG_INT) 5738c2ecf20Sopenharmony_ci printk("done\n"); 5748c2ecf20Sopenharmony_ci#endif 5758c2ecf20Sopenharmony_ci return handled; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci/* 5798c2ecf20Sopenharmony_ci * If we have a good packet(s), get it/them out of the buffers. 5808c2ecf20Sopenharmony_ci */ 5818c2ecf20Sopenharmony_cistatic int ether3_rx(struct net_device *dev, unsigned int maxcnt) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci unsigned int next_ptr = priv(dev)->rx_head, received = 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ether3_ledon(dev); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci do { 5888c2ecf20Sopenharmony_ci unsigned int this_ptr, status; 5898c2ecf20Sopenharmony_ci unsigned char addrs[16]; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* 5928c2ecf20Sopenharmony_ci * read the first 16 bytes from the buffer. 5938c2ecf20Sopenharmony_ci * This contains the status bytes etc and ethernet addresses, 5948c2ecf20Sopenharmony_ci * and we also check the source ethernet address to see if 5958c2ecf20Sopenharmony_ci * it originated from us. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci { 5988c2ecf20Sopenharmony_ci unsigned int temp_ptr; 5998c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_read, next_ptr); 6008c2ecf20Sopenharmony_ci temp_ptr = ether3_readword(dev); 6018c2ecf20Sopenharmony_ci status = ether3_readword(dev); 6028c2ecf20Sopenharmony_ci if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) != 6038c2ecf20Sopenharmony_ci (RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr) 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci this_ptr = next_ptr + 4; 6078c2ecf20Sopenharmony_ci next_ptr = ntohs(temp_ptr); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_read, this_ptr); 6108c2ecf20Sopenharmony_ci ether3_readbuffer(dev, addrs+2, 12); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ciif (next_ptr < RX_START || next_ptr >= RX_END) { 6138c2ecf20Sopenharmony_ci printk("%s: bad next pointer @%04X: ", dev->name, priv(dev)->rx_head); 6148c2ecf20Sopenharmony_ci printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8); 6158c2ecf20Sopenharmony_ci printk("%pM %pM\n", addrs + 2, addrs + 8); 6168c2ecf20Sopenharmony_ci next_ptr = priv(dev)->rx_head; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * ignore our own packets... 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) && 6238c2ecf20Sopenharmony_ci !(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) { 6248c2ecf20Sopenharmony_ci maxcnt ++; /* compensate for loopedback packet */ 6258c2ecf20Sopenharmony_ci ether3_outw(next_ptr >> 8, REG_RECVEND); 6268c2ecf20Sopenharmony_ci } else 6278c2ecf20Sopenharmony_ci if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) { 6288c2ecf20Sopenharmony_ci unsigned int length = next_ptr - this_ptr; 6298c2ecf20Sopenharmony_ci struct sk_buff *skb; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (next_ptr <= this_ptr) 6328c2ecf20Sopenharmony_ci length += RX_END - RX_START; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, length + 2); 6358c2ecf20Sopenharmony_ci if (skb) { 6368c2ecf20Sopenharmony_ci unsigned char *buf; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 6398c2ecf20Sopenharmony_ci buf = skb_put(skb, length); 6408c2ecf20Sopenharmony_ci ether3_readbuffer(dev, buf + 12, length - 12); 6418c2ecf20Sopenharmony_ci ether3_outw(next_ptr >> 8, REG_RECVEND); 6428c2ecf20Sopenharmony_ci *(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2); 6438c2ecf20Sopenharmony_ci *(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4); 6448c2ecf20Sopenharmony_ci *(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8); 6458c2ecf20Sopenharmony_ci *(unsigned short *)(buf + 10) = *(unsigned short *)(addrs + 12); 6468c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 6478c2ecf20Sopenharmony_ci netif_rx(skb); 6488c2ecf20Sopenharmony_ci received ++; 6498c2ecf20Sopenharmony_ci } else { 6508c2ecf20Sopenharmony_ci ether3_outw(next_ptr >> 8, REG_RECVEND); 6518c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 6528c2ecf20Sopenharmony_ci goto done; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci } else { 6558c2ecf20Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 6568c2ecf20Sopenharmony_ci ether3_outw(next_ptr >> 8, REG_RECVEND); 6578c2ecf20Sopenharmony_ci if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++; 6588c2ecf20Sopenharmony_ci if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++; 6598c2ecf20Sopenharmony_ci if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++; 6608c2ecf20Sopenharmony_ci if (status & RXSTAT_SHORTPACKET) stats->rx_length_errors ++; 6618c2ecf20Sopenharmony_ci stats->rx_errors++; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci while (-- maxcnt); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cidone: 6678c2ecf20Sopenharmony_ci dev->stats.rx_packets += received; 6688c2ecf20Sopenharmony_ci priv(dev)->rx_head = next_ptr; 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * If rx went off line, then that means that the buffer may be full. We 6718c2ecf20Sopenharmony_ci * have dropped at least one packet. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci if (!(ether3_inw(REG_STATUS) & STAT_RXON)) { 6748c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 6758c2ecf20Sopenharmony_ci ether3_outw(next_ptr, REG_RECVPTR); 6768c2ecf20Sopenharmony_ci ether3_outw(priv(dev)->regs.command | CMD_RXON, REG_COMMAND); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return maxcnt; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/* 6838c2ecf20Sopenharmony_ci * Update stats for the transmitted packet(s) 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_cistatic void ether3_tx(struct net_device *dev) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci unsigned int tx_tail = priv(dev)->tx_tail; 6888c2ecf20Sopenharmony_ci int max_work = 14; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci do { 6918c2ecf20Sopenharmony_ci unsigned long status; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* 6948c2ecf20Sopenharmony_ci * Read the packet header 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); 6978c2ecf20Sopenharmony_ci status = ether3_readlong(dev); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* 7008c2ecf20Sopenharmony_ci * Check to see if this packet has been transmitted 7018c2ecf20Sopenharmony_ci */ 7028c2ecf20Sopenharmony_ci if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) != 7038c2ecf20Sopenharmony_ci (TXSTAT_DONE | TXHDR_TRANSMIT)) 7048c2ecf20Sopenharmony_ci break; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* 7078c2ecf20Sopenharmony_ci * Update errors 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS))) 7108c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 7118c2ecf20Sopenharmony_ci else { 7128c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 7138c2ecf20Sopenharmony_ci if (status & TXSTAT_16COLLISIONS) 7148c2ecf20Sopenharmony_ci dev->stats.collisions += 16; 7158c2ecf20Sopenharmony_ci if (status & TXSTAT_BABBLED) 7168c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci tx_tail = (tx_tail + 1) & 15; 7208c2ecf20Sopenharmony_ci } while (--max_work); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (priv(dev)->tx_tail != tx_tail) { 7238c2ecf20Sopenharmony_ci priv(dev)->tx_tail = tx_tail; 7248c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic void ether3_banner(void) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci static unsigned version_printed = 0; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (net_debug && version_printed++ == 0) 7338c2ecf20Sopenharmony_ci printk(KERN_INFO "%s", version); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic const struct net_device_ops ether3_netdev_ops = { 7378c2ecf20Sopenharmony_ci .ndo_open = ether3_open, 7388c2ecf20Sopenharmony_ci .ndo_stop = ether3_close, 7398c2ecf20Sopenharmony_ci .ndo_start_xmit = ether3_sendpacket, 7408c2ecf20Sopenharmony_ci .ndo_set_rx_mode = ether3_setmulticastlist, 7418c2ecf20Sopenharmony_ci .ndo_tx_timeout = ether3_timeout, 7428c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 7438c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic int 7478c2ecf20Sopenharmony_ciether3_probe(struct expansion_card *ec, const struct ecard_id *id) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci const struct ether3_data *data = id->data; 7508c2ecf20Sopenharmony_ci struct net_device *dev; 7518c2ecf20Sopenharmony_ci int bus_type, ret; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ether3_banner(); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ret = ecard_request_resources(ec); 7568c2ecf20Sopenharmony_ci if (ret) 7578c2ecf20Sopenharmony_ci goto out; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct dev_priv)); 7608c2ecf20Sopenharmony_ci if (!dev) { 7618c2ecf20Sopenharmony_ci ret = -ENOMEM; 7628c2ecf20Sopenharmony_ci goto release; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &ec->dev); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci priv(dev)->base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); 7688c2ecf20Sopenharmony_ci if (!priv(dev)->base) { 7698c2ecf20Sopenharmony_ci ret = -ENOMEM; 7708c2ecf20Sopenharmony_ci goto free; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci ec->irqaddr = priv(dev)->base + data->base_offset; 7748c2ecf20Sopenharmony_ci ec->irqmask = 0xf0; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci priv(dev)->seeq = priv(dev)->base + data->base_offset; 7778c2ecf20Sopenharmony_ci dev->irq = ec->irq; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ether3_addr(dev->dev_addr, ec); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci priv(dev)->dev = dev; 7828c2ecf20Sopenharmony_ci timer_setup(&priv(dev)->timer, ether3_ledoff, 0); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Reset card... 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci ether3_outb(0x80, REG_CONFIG2 + 4); 7878c2ecf20Sopenharmony_ci bus_type = BUS_UNKNOWN; 7888c2ecf20Sopenharmony_ci udelay(4); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Test using Receive Pointer (16-bit register) to find out 7918c2ecf20Sopenharmony_ci * how the ether3 is connected to the bus... 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_ci if (ether3_probe_bus_8(dev, 0x100) && 7948c2ecf20Sopenharmony_ci ether3_probe_bus_8(dev, 0x201)) 7958c2ecf20Sopenharmony_ci bus_type = BUS_8; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (bus_type == BUS_UNKNOWN && 7988c2ecf20Sopenharmony_ci ether3_probe_bus_16(dev, 0x101) && 7998c2ecf20Sopenharmony_ci ether3_probe_bus_16(dev, 0x201)) 8008c2ecf20Sopenharmony_ci bus_type = BUS_16; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci switch (bus_type) { 8038c2ecf20Sopenharmony_ci case BUS_UNKNOWN: 8048c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); 8058c2ecf20Sopenharmony_ci ret = -ENODEV; 8068c2ecf20Sopenharmony_ci goto free; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci case BUS_8: 8098c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: %s found, but is an unsupported " 8108c2ecf20Sopenharmony_ci "8-bit card\n", dev->name, data->name); 8118c2ecf20Sopenharmony_ci ret = -ENODEV; 8128c2ecf20Sopenharmony_ci goto free; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci default: 8158c2ecf20Sopenharmony_ci break; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (ether3_init_2(dev)) { 8198c2ecf20Sopenharmony_ci ret = -ENODEV; 8208c2ecf20Sopenharmony_ci goto free; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci dev->netdev_ops = ðer3_netdev_ops; 8248c2ecf20Sopenharmony_ci dev->watchdog_timeo = 5 * HZ / 100; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci ret = register_netdev(dev); 8278c2ecf20Sopenharmony_ci if (ret) 8288c2ecf20Sopenharmony_ci goto free; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci printk("%s: %s in slot %d, %pM\n", 8318c2ecf20Sopenharmony_ci dev->name, data->name, ec->slot_no, dev->dev_addr); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, dev); 8348c2ecf20Sopenharmony_ci return 0; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci free: 8378c2ecf20Sopenharmony_ci free_netdev(dev); 8388c2ecf20Sopenharmony_ci release: 8398c2ecf20Sopenharmony_ci ecard_release_resources(ec); 8408c2ecf20Sopenharmony_ci out: 8418c2ecf20Sopenharmony_ci return ret; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic void ether3_remove(struct expansion_card *ec) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct net_device *dev = ecard_get_drvdata(ec); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, NULL); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci unregister_netdev(dev); 8518c2ecf20Sopenharmony_ci free_netdev(dev); 8528c2ecf20Sopenharmony_ci ecard_release_resources(ec); 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic struct ether3_data ether3 = { 8568c2ecf20Sopenharmony_ci .name = "ether3", 8578c2ecf20Sopenharmony_ci .base_offset = 0, 8588c2ecf20Sopenharmony_ci}; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic struct ether3_data etherb = { 8618c2ecf20Sopenharmony_ci .name = "etherb", 8628c2ecf20Sopenharmony_ci .base_offset = 0x800, 8638c2ecf20Sopenharmony_ci}; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic const struct ecard_id ether3_ids[] = { 8668c2ecf20Sopenharmony_ci { MANU_ANT2, PROD_ANT_ETHER3, ðer3 }, 8678c2ecf20Sopenharmony_ci { MANU_ANT, PROD_ANT_ETHER3, ðer3 }, 8688c2ecf20Sopenharmony_ci { MANU_ANT, PROD_ANT_ETHERB, ðerb }, 8698c2ecf20Sopenharmony_ci { 0xffff, 0xffff } 8708c2ecf20Sopenharmony_ci}; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic struct ecard_driver ether3_driver = { 8738c2ecf20Sopenharmony_ci .probe = ether3_probe, 8748c2ecf20Sopenharmony_ci .remove = ether3_remove, 8758c2ecf20Sopenharmony_ci .id_table = ether3_ids, 8768c2ecf20Sopenharmony_ci .drv = { 8778c2ecf20Sopenharmony_ci .name = "ether3", 8788c2ecf20Sopenharmony_ci }, 8798c2ecf20Sopenharmony_ci}; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic int __init ether3_init(void) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci return ecard_register_driver(ðer3_driver); 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic void __exit ether3_exit(void) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci ecard_remove_driver(ðer3_driver); 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cimodule_init(ether3_init); 8928c2ecf20Sopenharmony_cimodule_exit(ether3_exit); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 895