18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/acorn/net/ether1.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1996-2000 Russell King 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Acorn ether1 driver (82586 chip) for Acorn machines 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * We basically keep two queues in the cards memory - one for transmit 108c2ecf20Sopenharmony_ci * and one for receive. Each has a head and a tail. The head is where 118c2ecf20Sopenharmony_ci * we/the chip adds packets to be transmitted/received, and the tail 128c2ecf20Sopenharmony_ci * is where the transmitter has got to/where the receiver will stop. 138c2ecf20Sopenharmony_ci * Both of these queues are circular, and since the chip is running 148c2ecf20Sopenharmony_ci * all the time, we have to be careful when we modify the pointers etc 158c2ecf20Sopenharmony_ci * so that the buffer memory contents is valid all the time. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Change log: 188c2ecf20Sopenharmony_ci * 1.00 RMK Released 198c2ecf20Sopenharmony_ci * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now. 208c2ecf20Sopenharmony_ci * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready 218c2ecf20Sopenharmony_ci * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt. 228c2ecf20Sopenharmony_ci * Should prevent lockup. 238c2ecf20Sopenharmony_ci * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong. 248c2ecf20Sopenharmony_ci * TDR now only reports failure when chip reports non-zero 258c2ecf20Sopenharmony_ci * TDR time-distance. 268c2ecf20Sopenharmony_ci * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 278c2ecf20Sopenharmony_ci * 1.06 RMK 10/02/2000 Updated for 2.3.43 288c2ecf20Sopenharmony_ci * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/module.h> 328c2ecf20Sopenharmony_ci#include <linux/kernel.h> 338c2ecf20Sopenharmony_ci#include <linux/types.h> 348c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 358c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 368c2ecf20Sopenharmony_ci#include <linux/ioport.h> 378c2ecf20Sopenharmony_ci#include <linux/in.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/string.h> 408c2ecf20Sopenharmony_ci#include <linux/errno.h> 418c2ecf20Sopenharmony_ci#include <linux/device.h> 428c2ecf20Sopenharmony_ci#include <linux/init.h> 438c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 448c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 458c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 468c2ecf20Sopenharmony_ci#include <linux/bitops.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <asm/io.h> 498c2ecf20Sopenharmony_ci#include <asm/dma.h> 508c2ecf20Sopenharmony_ci#include <asm/ecard.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define __ETHER1_C 538c2ecf20Sopenharmony_ci#include "ether1.h" 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic unsigned int net_debug = NET_DEBUG; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define BUFFER_SIZE 0x10000 588c2ecf20Sopenharmony_ci#define TX_AREA_START 0x00100 598c2ecf20Sopenharmony_ci#define TX_AREA_END 0x05000 608c2ecf20Sopenharmony_ci#define RX_AREA_START 0x05000 618c2ecf20Sopenharmony_ci#define RX_AREA_END 0x0fc00 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int ether1_open(struct net_device *dev); 648c2ecf20Sopenharmony_cistatic netdev_tx_t ether1_sendpacket(struct sk_buff *skb, 658c2ecf20Sopenharmony_ci struct net_device *dev); 668c2ecf20Sopenharmony_cistatic irqreturn_t ether1_interrupt(int irq, void *dev_id); 678c2ecf20Sopenharmony_cistatic int ether1_close(struct net_device *dev); 688c2ecf20Sopenharmony_cistatic void ether1_setmulticastlist(struct net_device *dev); 698c2ecf20Sopenharmony_cistatic void ether1_timeout(struct net_device *dev, unsigned int txqueue); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic char version[] = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define BUS_16 16 768c2ecf20Sopenharmony_ci#define BUS_8 8 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define DISABLEIRQS 1 818c2ecf20Sopenharmony_ci#define NORMALIRQS 0 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define ether1_readw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs) 848c2ecf20Sopenharmony_ci#define ether1_writew(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline unsigned short 878c2ecf20Sopenharmony_ciether1_inw_p (struct net_device *dev, int addr, int svflgs) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci unsigned long flags; 908c2ecf20Sopenharmony_ci unsigned short ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (svflgs) 938c2ecf20Sopenharmony_ci local_irq_save (flags); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci writeb(addr >> 12, REG_PAGE); 968c2ecf20Sopenharmony_ci ret = readw(ETHER1_RAM + ((addr & 4095) << 1)); 978c2ecf20Sopenharmony_ci if (svflgs) 988c2ecf20Sopenharmony_ci local_irq_restore (flags); 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline void 1038c2ecf20Sopenharmony_ciether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned long flags; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (svflgs) 1088c2ecf20Sopenharmony_ci local_irq_save (flags); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci writeb(addr >> 12, REG_PAGE); 1118c2ecf20Sopenharmony_ci writew(val, ETHER1_RAM + ((addr & 4095) << 1)); 1128c2ecf20Sopenharmony_ci if (svflgs) 1138c2ecf20Sopenharmony_ci local_irq_restore (flags); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Some inline assembler to allow fast transfers on to/off of the card. 1188c2ecf20Sopenharmony_ci * Since this driver depends on some features presented by the ARM 1198c2ecf20Sopenharmony_ci * specific architecture, and that you can't configure this driver 1208c2ecf20Sopenharmony_ci * without specifiing ARM mode, this is not a problem. 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * This routine is essentially an optimised memcpy from the card's 1238c2ecf20Sopenharmony_ci * onboard RAM to kernel memory. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic void 1268c2ecf20Sopenharmony_ciether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned int page, thislen, offset; 1298c2ecf20Sopenharmony_ci void __iomem *addr; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci offset = start & 4095; 1328c2ecf20Sopenharmony_ci page = start >> 12; 1338c2ecf20Sopenharmony_ci addr = ETHER1_RAM + (offset << 1); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (offset + length > 4096) 1368c2ecf20Sopenharmony_ci thislen = 4096 - offset; 1378c2ecf20Sopenharmony_ci else 1388c2ecf20Sopenharmony_ci thislen = length; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci do { 1418c2ecf20Sopenharmony_ci int used; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci writeb(page, REG_PAGE); 1448c2ecf20Sopenharmony_ci length -= thislen; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci __asm__ __volatile__( 1478c2ecf20Sopenharmony_ci "subs %3, %3, #2\n\ 1488c2ecf20Sopenharmony_ci bmi 2f\n\ 1498c2ecf20Sopenharmony_ci1: ldr %0, [%1], #2\n\ 1508c2ecf20Sopenharmony_ci mov %0, %0, lsl #16\n\ 1518c2ecf20Sopenharmony_ci orr %0, %0, %0, lsr #16\n\ 1528c2ecf20Sopenharmony_ci str %0, [%2], #4\n\ 1538c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 1548c2ecf20Sopenharmony_ci bmi 2f\n\ 1558c2ecf20Sopenharmony_ci ldr %0, [%1], #2\n\ 1568c2ecf20Sopenharmony_ci mov %0, %0, lsl #16\n\ 1578c2ecf20Sopenharmony_ci orr %0, %0, %0, lsr #16\n\ 1588c2ecf20Sopenharmony_ci str %0, [%2], #4\n\ 1598c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 1608c2ecf20Sopenharmony_ci bmi 2f\n\ 1618c2ecf20Sopenharmony_ci ldr %0, [%1], #2\n\ 1628c2ecf20Sopenharmony_ci mov %0, %0, lsl #16\n\ 1638c2ecf20Sopenharmony_ci orr %0, %0, %0, lsr #16\n\ 1648c2ecf20Sopenharmony_ci str %0, [%2], #4\n\ 1658c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 1668c2ecf20Sopenharmony_ci bmi 2f\n\ 1678c2ecf20Sopenharmony_ci ldr %0, [%1], #2\n\ 1688c2ecf20Sopenharmony_ci mov %0, %0, lsl #16\n\ 1698c2ecf20Sopenharmony_ci orr %0, %0, %0, lsr #16\n\ 1708c2ecf20Sopenharmony_ci str %0, [%2], #4\n\ 1718c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 1728c2ecf20Sopenharmony_ci bpl 1b\n\ 1738c2ecf20Sopenharmony_ci2: adds %3, %3, #1\n\ 1748c2ecf20Sopenharmony_ci ldreqb %0, [%1]\n\ 1758c2ecf20Sopenharmony_ci streqb %0, [%2]" 1768c2ecf20Sopenharmony_ci : "=&r" (used), "=&r" (data) 1778c2ecf20Sopenharmony_ci : "r" (addr), "r" (thislen), "1" (data)); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci addr = ETHER1_RAM; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci thislen = length; 1828c2ecf20Sopenharmony_ci if (thislen > 4096) 1838c2ecf20Sopenharmony_ci thislen = 4096; 1848c2ecf20Sopenharmony_ci page++; 1858c2ecf20Sopenharmony_ci } while (thislen); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void 1898c2ecf20Sopenharmony_ciether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci unsigned int page, thislen, offset; 1928c2ecf20Sopenharmony_ci void __iomem *addr; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci offset = start & 4095; 1958c2ecf20Sopenharmony_ci page = start >> 12; 1968c2ecf20Sopenharmony_ci addr = ETHER1_RAM + (offset << 1); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (offset + length > 4096) 1998c2ecf20Sopenharmony_ci thislen = 4096 - offset; 2008c2ecf20Sopenharmony_ci else 2018c2ecf20Sopenharmony_ci thislen = length; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci do { 2048c2ecf20Sopenharmony_ci int used; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci writeb(page, REG_PAGE); 2078c2ecf20Sopenharmony_ci length -= thislen; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci __asm__ __volatile__( 2108c2ecf20Sopenharmony_ci "subs %3, %3, #2\n\ 2118c2ecf20Sopenharmony_ci bmi 2f\n\ 2128c2ecf20Sopenharmony_ci1: ldr %0, [%2], #4\n\ 2138c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2148c2ecf20Sopenharmony_ci mov %0, %0, lsr #8\n\ 2158c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2168c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 2178c2ecf20Sopenharmony_ci bmi 2f\n\ 2188c2ecf20Sopenharmony_ci ldr %0, [%2], #4\n\ 2198c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2208c2ecf20Sopenharmony_ci mov %0, %0, lsr #8\n\ 2218c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2228c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 2238c2ecf20Sopenharmony_ci bmi 2f\n\ 2248c2ecf20Sopenharmony_ci ldr %0, [%2], #4\n\ 2258c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2268c2ecf20Sopenharmony_ci mov %0, %0, lsr #8\n\ 2278c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2288c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 2298c2ecf20Sopenharmony_ci bmi 2f\n\ 2308c2ecf20Sopenharmony_ci ldr %0, [%2], #4\n\ 2318c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2328c2ecf20Sopenharmony_ci mov %0, %0, lsr #8\n\ 2338c2ecf20Sopenharmony_ci strb %0, [%1], #1\n\ 2348c2ecf20Sopenharmony_ci subs %3, %3, #2\n\ 2358c2ecf20Sopenharmony_ci bpl 1b\n\ 2368c2ecf20Sopenharmony_ci2: adds %3, %3, #1\n\ 2378c2ecf20Sopenharmony_ci ldreqb %0, [%2]\n\ 2388c2ecf20Sopenharmony_ci streqb %0, [%1]" 2398c2ecf20Sopenharmony_ci : "=&r" (used), "=&r" (data) 2408c2ecf20Sopenharmony_ci : "r" (addr), "r" (thislen), "1" (data)); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci addr = ETHER1_RAM; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci thislen = length; 2458c2ecf20Sopenharmony_ci if (thislen > 4096) 2468c2ecf20Sopenharmony_ci thislen = 4096; 2478c2ecf20Sopenharmony_ci page++; 2488c2ecf20Sopenharmony_ci } while (thislen); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int 2528c2ecf20Sopenharmony_ciether1_ramtest(struct net_device *dev, unsigned char byte) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); 2558c2ecf20Sopenharmony_ci int i, ret = BUFFER_SIZE; 2568c2ecf20Sopenharmony_ci int max_errors = 15; 2578c2ecf20Sopenharmony_ci int bad = -1; 2588c2ecf20Sopenharmony_ci int bad_start = 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!buffer) 2618c2ecf20Sopenharmony_ci return 1; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci memset (buffer, byte, BUFFER_SIZE); 2648c2ecf20Sopenharmony_ci ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE); 2658c2ecf20Sopenharmony_ci memset (buffer, byte ^ 0xff, BUFFER_SIZE); 2668c2ecf20Sopenharmony_ci ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci for (i = 0; i < BUFFER_SIZE; i++) { 2698c2ecf20Sopenharmony_ci if (buffer[i] != byte) { 2708c2ecf20Sopenharmony_ci if (max_errors >= 0 && bad != buffer[i]) { 2718c2ecf20Sopenharmony_ci if (bad != -1) 2728c2ecf20Sopenharmony_ci printk ("\n"); 2738c2ecf20Sopenharmony_ci printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X", 2748c2ecf20Sopenharmony_ci dev->name, buffer[i], byte, i); 2758c2ecf20Sopenharmony_ci ret = -ENODEV; 2768c2ecf20Sopenharmony_ci max_errors --; 2778c2ecf20Sopenharmony_ci bad = buffer[i]; 2788c2ecf20Sopenharmony_ci bad_start = i; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci if (bad != -1) { 2828c2ecf20Sopenharmony_ci if (bad_start == i - 1) 2838c2ecf20Sopenharmony_ci printk ("\n"); 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci printk (" - 0x%04X\n", i - 1); 2868c2ecf20Sopenharmony_ci bad = -1; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (bad != -1) 2928c2ecf20Sopenharmony_ci printk (" - 0x%04X\n", BUFFER_SIZE); 2938c2ecf20Sopenharmony_ci kfree (buffer); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int 2998c2ecf20Sopenharmony_ciether1_reset (struct net_device *dev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci writeb(CTRL_RST|CTRL_ACK, REG_CONTROL); 3028c2ecf20Sopenharmony_ci return BUS_16; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int 3068c2ecf20Sopenharmony_ciether1_init_2(struct net_device *dev) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci int i; 3098c2ecf20Sopenharmony_ci dev->mem_start = 0; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci i = ether1_ramtest (dev, 0x5a); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (i > 0) 3148c2ecf20Sopenharmony_ci i = ether1_ramtest (dev, 0x1e); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (i <= 0) 3178c2ecf20Sopenharmony_ci return -ENODEV; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dev->mem_end = i; 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* 3248c2ecf20Sopenharmony_ci * These are the structures that are loaded into the ether RAM card to 3258c2ecf20Sopenharmony_ci * initialise the 82586 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* at 0x0100 */ 3298c2ecf20Sopenharmony_ci#define NOP_ADDR (TX_AREA_START) 3308c2ecf20Sopenharmony_ci#define NOP_SIZE (0x06) 3318c2ecf20Sopenharmony_cistatic nop_t init_nop = { 3328c2ecf20Sopenharmony_ci 0, 3338c2ecf20Sopenharmony_ci CMD_NOP, 3348c2ecf20Sopenharmony_ci NOP_ADDR 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* at 0x003a */ 3388c2ecf20Sopenharmony_ci#define TDR_ADDR (0x003a) 3398c2ecf20Sopenharmony_ci#define TDR_SIZE (0x08) 3408c2ecf20Sopenharmony_cistatic tdr_t init_tdr = { 3418c2ecf20Sopenharmony_ci 0, 3428c2ecf20Sopenharmony_ci CMD_TDR | CMD_INTR, 3438c2ecf20Sopenharmony_ci NOP_ADDR, 3448c2ecf20Sopenharmony_ci 0 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/* at 0x002e */ 3488c2ecf20Sopenharmony_ci#define MC_ADDR (0x002e) 3498c2ecf20Sopenharmony_ci#define MC_SIZE (0x0c) 3508c2ecf20Sopenharmony_cistatic mc_t init_mc = { 3518c2ecf20Sopenharmony_ci 0, 3528c2ecf20Sopenharmony_ci CMD_SETMULTICAST, 3538c2ecf20Sopenharmony_ci TDR_ADDR, 3548c2ecf20Sopenharmony_ci 0, 3558c2ecf20Sopenharmony_ci { { 0, } } 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* at 0x0022 */ 3598c2ecf20Sopenharmony_ci#define SA_ADDR (0x0022) 3608c2ecf20Sopenharmony_ci#define SA_SIZE (0x0c) 3618c2ecf20Sopenharmony_cistatic sa_t init_sa = { 3628c2ecf20Sopenharmony_ci 0, 3638c2ecf20Sopenharmony_ci CMD_SETADDRESS, 3648c2ecf20Sopenharmony_ci MC_ADDR, 3658c2ecf20Sopenharmony_ci { 0, } 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* at 0x0010 */ 3698c2ecf20Sopenharmony_ci#define CFG_ADDR (0x0010) 3708c2ecf20Sopenharmony_ci#define CFG_SIZE (0x12) 3718c2ecf20Sopenharmony_cistatic cfg_t init_cfg = { 3728c2ecf20Sopenharmony_ci 0, 3738c2ecf20Sopenharmony_ci CMD_CONFIG, 3748c2ecf20Sopenharmony_ci SA_ADDR, 3758c2ecf20Sopenharmony_ci 8, 3768c2ecf20Sopenharmony_ci 8, 3778c2ecf20Sopenharmony_ci CFG8_SRDY, 3788c2ecf20Sopenharmony_ci CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6), 3798c2ecf20Sopenharmony_ci 0, 3808c2ecf20Sopenharmony_ci 0x60, 3818c2ecf20Sopenharmony_ci 0, 3828c2ecf20Sopenharmony_ci CFG13_RETRY(15) | CFG13_SLOTH(2), 3838c2ecf20Sopenharmony_ci 0, 3848c2ecf20Sopenharmony_ci}; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* at 0x0000 */ 3878c2ecf20Sopenharmony_ci#define SCB_ADDR (0x0000) 3888c2ecf20Sopenharmony_ci#define SCB_SIZE (0x10) 3898c2ecf20Sopenharmony_cistatic scb_t init_scb = { 3908c2ecf20Sopenharmony_ci 0, 3918c2ecf20Sopenharmony_ci SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX, 3928c2ecf20Sopenharmony_ci CFG_ADDR, 3938c2ecf20Sopenharmony_ci RX_AREA_START, 3948c2ecf20Sopenharmony_ci 0, 3958c2ecf20Sopenharmony_ci 0, 3968c2ecf20Sopenharmony_ci 0, 3978c2ecf20Sopenharmony_ci 0 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci/* at 0xffee */ 4018c2ecf20Sopenharmony_ci#define ISCP_ADDR (0xffee) 4028c2ecf20Sopenharmony_ci#define ISCP_SIZE (0x08) 4038c2ecf20Sopenharmony_cistatic iscp_t init_iscp = { 4048c2ecf20Sopenharmony_ci 1, 4058c2ecf20Sopenharmony_ci SCB_ADDR, 4068c2ecf20Sopenharmony_ci 0x0000, 4078c2ecf20Sopenharmony_ci 0x0000 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* at 0xfff6 */ 4118c2ecf20Sopenharmony_ci#define SCP_ADDR (0xfff6) 4128c2ecf20Sopenharmony_ci#define SCP_SIZE (0x0a) 4138c2ecf20Sopenharmony_cistatic scp_t init_scp = { 4148c2ecf20Sopenharmony_ci SCP_SY_16BBUS, 4158c2ecf20Sopenharmony_ci { 0, 0 }, 4168c2ecf20Sopenharmony_ci ISCP_ADDR, 4178c2ecf20Sopenharmony_ci 0 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci#define RFD_SIZE (0x16) 4218c2ecf20Sopenharmony_cistatic rfd_t init_rfd = { 4228c2ecf20Sopenharmony_ci 0, 4238c2ecf20Sopenharmony_ci 0, 4248c2ecf20Sopenharmony_ci 0, 4258c2ecf20Sopenharmony_ci 0, 4268c2ecf20Sopenharmony_ci { 0, }, 4278c2ecf20Sopenharmony_ci { 0, }, 4288c2ecf20Sopenharmony_ci 0 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci#define RBD_SIZE (0x0a) 4328c2ecf20Sopenharmony_cistatic rbd_t init_rbd = { 4338c2ecf20Sopenharmony_ci 0, 4348c2ecf20Sopenharmony_ci 0, 4358c2ecf20Sopenharmony_ci 0, 4368c2ecf20Sopenharmony_ci 0, 4378c2ecf20Sopenharmony_ci ETH_FRAME_LEN + 8 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci#define TX_SIZE (0x08) 4418c2ecf20Sopenharmony_ci#define TBD_SIZE (0x08) 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int 4448c2ecf20Sopenharmony_ciether1_init_for_open (struct net_device *dev) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int i, status, addr, next, next2; 4478c2ecf20Sopenharmony_ci int failures = 0; 4488c2ecf20Sopenharmony_ci unsigned long timeout; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci writeb(CTRL_RST|CTRL_ACK, REG_CONTROL); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 4538c2ecf20Sopenharmony_ci init_sa.sa_addr[i] = dev->dev_addr[i]; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* load data structures into ether1 RAM */ 4568c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE); 4578c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE); 4588c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE); 4598c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE); 4608c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE); 4618c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE); 4628c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE); 4638c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (ether1_readw(dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) { 4668c2ecf20Sopenharmony_ci printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n", 4678c2ecf20Sopenharmony_ci dev->name); 4688c2ecf20Sopenharmony_ci return 1; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * setup circularly linked list of { rfd, rbd, buffer }, with 4738c2ecf20Sopenharmony_ci * all rfds circularly linked, rbds circularly linked. 4748c2ecf20Sopenharmony_ci * First rfd is linked to scp, first rbd is linked to first 4758c2ecf20Sopenharmony_ci * rfd. Last rbd has a suspend command. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci addr = RX_AREA_START; 4788c2ecf20Sopenharmony_ci do { 4798c2ecf20Sopenharmony_ci next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; 4808c2ecf20Sopenharmony_ci next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (next2 >= RX_AREA_END) { 4838c2ecf20Sopenharmony_ci next = RX_AREA_START; 4848c2ecf20Sopenharmony_ci init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND; 4858c2ecf20Sopenharmony_ci priv(dev)->rx_tail = addr; 4868c2ecf20Sopenharmony_ci } else 4878c2ecf20Sopenharmony_ci init_rfd.rfd_command = 0; 4888c2ecf20Sopenharmony_ci if (addr == RX_AREA_START) 4898c2ecf20Sopenharmony_ci init_rfd.rfd_rbdoffset = addr + RFD_SIZE; 4908c2ecf20Sopenharmony_ci else 4918c2ecf20Sopenharmony_ci init_rfd.rfd_rbdoffset = 0; 4928c2ecf20Sopenharmony_ci init_rfd.rfd_link = next; 4938c2ecf20Sopenharmony_ci init_rbd.rbd_link = next + RFD_SIZE; 4948c2ecf20Sopenharmony_ci init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE); 4978c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE); 4988c2ecf20Sopenharmony_ci addr = next; 4998c2ecf20Sopenharmony_ci } while (next2 < RX_AREA_END); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci priv(dev)->tx_link = NOP_ADDR; 5028c2ecf20Sopenharmony_ci priv(dev)->tx_head = NOP_ADDR + NOP_SIZE; 5038c2ecf20Sopenharmony_ci priv(dev)->tx_tail = TDR_ADDR; 5048c2ecf20Sopenharmony_ci priv(dev)->rx_head = RX_AREA_START; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* release reset & give 586 a prod */ 5078c2ecf20Sopenharmony_ci priv(dev)->resetting = 1; 5088c2ecf20Sopenharmony_ci priv(dev)->initialising = 1; 5098c2ecf20Sopenharmony_ci writeb(CTRL_RST, REG_CONTROL); 5108c2ecf20Sopenharmony_ci writeb(0, REG_CONTROL); 5118c2ecf20Sopenharmony_ci writeb(CTRL_CA, REG_CONTROL); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* 586 should now unset iscp.busy */ 5148c2ecf20Sopenharmony_ci timeout = jiffies + HZ/2; 5158c2ecf20Sopenharmony_ci while (ether1_readw(dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { 5168c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5178c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); 5188c2ecf20Sopenharmony_ci return 1; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* check status of commands that we issued */ 5238c2ecf20Sopenharmony_ci timeout += HZ/10; 5248c2ecf20Sopenharmony_ci while (((status = ether1_readw(dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) 5258c2ecf20Sopenharmony_ci & STAT_COMPLETE) == 0) { 5268c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { 5318c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status); 5328c2ecf20Sopenharmony_ci printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, 5338c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), 5348c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), 5358c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), 5368c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); 5378c2ecf20Sopenharmony_ci failures += 1; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci timeout += HZ/10; 5418c2ecf20Sopenharmony_ci while (((status = ether1_readw(dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) 5428c2ecf20Sopenharmony_ci & STAT_COMPLETE) == 0) { 5438c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { 5488c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status); 5498c2ecf20Sopenharmony_ci printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, 5508c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), 5518c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), 5528c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), 5538c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); 5548c2ecf20Sopenharmony_ci failures += 1; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci timeout += HZ/10; 5588c2ecf20Sopenharmony_ci while (((status = ether1_readw(dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) 5598c2ecf20Sopenharmony_ci & STAT_COMPLETE) == 0) { 5608c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { 5658c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status); 5668c2ecf20Sopenharmony_ci printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, 5678c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), 5688c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), 5698c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), 5708c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); 5718c2ecf20Sopenharmony_ci failures += 1; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci timeout += HZ; 5758c2ecf20Sopenharmony_ci while (((status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) 5768c2ecf20Sopenharmony_ci & STAT_COMPLETE) == 0) { 5778c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { 5828c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name); 5838c2ecf20Sopenharmony_ci printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, 5848c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), 5858c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), 5868c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), 5878c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); 5888c2ecf20Sopenharmony_ci } else { 5898c2ecf20Sopenharmony_ci status = ether1_readw(dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS); 5908c2ecf20Sopenharmony_ci if (status & TDR_XCVRPROB) 5918c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name); 5928c2ecf20Sopenharmony_ci else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) { 5938c2ecf20Sopenharmony_ci#ifdef FANCY 5948c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name, 5958c2ecf20Sopenharmony_ci status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10, 5968c2ecf20Sopenharmony_ci (status & TDR_TIME) % 10); 5978c2ecf20Sopenharmony_ci#else 5988c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name, 5998c2ecf20Sopenharmony_ci status & TDR_SHORT ? "short" : "open", (status & TDR_TIME)); 6008c2ecf20Sopenharmony_ci#endif 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (failures) 6058c2ecf20Sopenharmony_ci ether1_reset (dev); 6068c2ecf20Sopenharmony_ci return failures ? 1 : 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int 6128c2ecf20Sopenharmony_ciether1_txalloc (struct net_device *dev, int size) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci int start, tail; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci size = (size + 1) & ~1; 6178c2ecf20Sopenharmony_ci tail = priv(dev)->tx_tail; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (priv(dev)->tx_head + size > TX_AREA_END) { 6208c2ecf20Sopenharmony_ci if (tail > priv(dev)->tx_head) 6218c2ecf20Sopenharmony_ci return -1; 6228c2ecf20Sopenharmony_ci start = TX_AREA_START; 6238c2ecf20Sopenharmony_ci if (start + size > tail) 6248c2ecf20Sopenharmony_ci return -1; 6258c2ecf20Sopenharmony_ci priv(dev)->tx_head = start + size; 6268c2ecf20Sopenharmony_ci } else { 6278c2ecf20Sopenharmony_ci if (priv(dev)->tx_head < tail && (priv(dev)->tx_head + size) > tail) 6288c2ecf20Sopenharmony_ci return -1; 6298c2ecf20Sopenharmony_ci start = priv(dev)->tx_head; 6308c2ecf20Sopenharmony_ci priv(dev)->tx_head += size; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return start; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int 6378c2ecf20Sopenharmony_ciether1_open (struct net_device *dev) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev)) 6408c2ecf20Sopenharmony_ci return -EAGAIN; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (ether1_init_for_open (dev)) { 6438c2ecf20Sopenharmony_ci free_irq (dev->irq, dev); 6448c2ecf20Sopenharmony_ci return -EAGAIN; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci netif_start_queue(dev); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return 0; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void 6538c2ecf20Sopenharmony_ciether1_timeout(struct net_device *dev, unsigned int txqueue) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n", 6568c2ecf20Sopenharmony_ci dev->name); 6578c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: resetting device\n", dev->name); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci ether1_reset (dev); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (ether1_init_for_open (dev)) 6628c2ecf20Sopenharmony_ci printk (KERN_ERR "%s: unable to restart interface\n", dev->name); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 6658c2ecf20Sopenharmony_ci netif_wake_queue(dev); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic netdev_tx_t 6698c2ecf20Sopenharmony_ciether1_sendpacket (struct sk_buff *skb, struct net_device *dev) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr; 6728c2ecf20Sopenharmony_ci unsigned long flags; 6738c2ecf20Sopenharmony_ci tx_t tx; 6748c2ecf20Sopenharmony_ci tbd_t tbd; 6758c2ecf20Sopenharmony_ci nop_t nop; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (priv(dev)->restart) { 6788c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: resetting device\n", dev->name); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci ether1_reset(dev); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (ether1_init_for_open(dev)) 6838c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: unable to restart interface\n", dev->name); 6848c2ecf20Sopenharmony_ci else 6858c2ecf20Sopenharmony_ci priv(dev)->restart = 0; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (skb->len < ETH_ZLEN) { 6898c2ecf20Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) 6908c2ecf20Sopenharmony_ci goto out; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* 6948c2ecf20Sopenharmony_ci * insert packet followed by a nop 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci txaddr = ether1_txalloc (dev, TX_SIZE); 6978c2ecf20Sopenharmony_ci tbdaddr = ether1_txalloc (dev, TBD_SIZE); 6988c2ecf20Sopenharmony_ci dataddr = ether1_txalloc (dev, skb->len); 6998c2ecf20Sopenharmony_ci nopaddr = ether1_txalloc (dev, NOP_SIZE); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci tx.tx_status = 0; 7028c2ecf20Sopenharmony_ci tx.tx_command = CMD_TX | CMD_INTR; 7038c2ecf20Sopenharmony_ci tx.tx_link = nopaddr; 7048c2ecf20Sopenharmony_ci tx.tx_tbdoffset = tbdaddr; 7058c2ecf20Sopenharmony_ci tbd.tbd_opts = TBD_EOL | skb->len; 7068c2ecf20Sopenharmony_ci tbd.tbd_link = I82586_NULL; 7078c2ecf20Sopenharmony_ci tbd.tbd_bufl = dataddr; 7088c2ecf20Sopenharmony_ci tbd.tbd_bufh = 0; 7098c2ecf20Sopenharmony_ci nop.nop_status = 0; 7108c2ecf20Sopenharmony_ci nop.nop_command = CMD_NOP; 7118c2ecf20Sopenharmony_ci nop.nop_link = nopaddr; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci local_irq_save(flags); 7148c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); 7158c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); 7168c2ecf20Sopenharmony_ci ether1_writebuffer (dev, skb->data, dataddr, skb->len); 7178c2ecf20Sopenharmony_ci ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE); 7188c2ecf20Sopenharmony_ci tmp = priv(dev)->tx_link; 7198c2ecf20Sopenharmony_ci priv(dev)->tx_link = nopaddr; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* now reset the previous nop pointer */ 7228c2ecf20Sopenharmony_ci ether1_writew(dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci local_irq_restore(flags); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* handle transmit */ 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* check to see if we have room for a full sized ether frame */ 7298c2ecf20Sopenharmony_ci tmp = priv(dev)->tx_head; 7308c2ecf20Sopenharmony_ci tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); 7318c2ecf20Sopenharmony_ci priv(dev)->tx_head = tmp; 7328c2ecf20Sopenharmony_ci dev_kfree_skb (skb); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (tst == -1) 7358c2ecf20Sopenharmony_ci netif_stop_queue(dev); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci out: 7388c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void 7428c2ecf20Sopenharmony_ciether1_xmit_done (struct net_device *dev) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci nop_t nop; 7458c2ecf20Sopenharmony_ci int caddr, tst; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci caddr = priv(dev)->tx_tail; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ciagain: 7508c2ecf20Sopenharmony_ci ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci switch (nop.nop_command & CMD_MASK) { 7538c2ecf20Sopenharmony_ci case CMD_TDR: 7548c2ecf20Sopenharmony_ci /* special case */ 7558c2ecf20Sopenharmony_ci if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) 7568c2ecf20Sopenharmony_ci != (unsigned short)I82586_NULL) { 7578c2ecf20Sopenharmony_ci ether1_writew(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, 7588c2ecf20Sopenharmony_ci scb_command, NORMALIRQS); 7598c2ecf20Sopenharmony_ci writeb(CTRL_CA, REG_CONTROL); 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci priv(dev)->tx_tail = NOP_ADDR; 7628c2ecf20Sopenharmony_ci return; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci case CMD_NOP: 7658c2ecf20Sopenharmony_ci if (nop.nop_link == caddr) { 7668c2ecf20Sopenharmony_ci if (priv(dev)->initialising == 0) 7678c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name); 7688c2ecf20Sopenharmony_ci else 7698c2ecf20Sopenharmony_ci priv(dev)->initialising = 0; 7708c2ecf20Sopenharmony_ci return; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci if (caddr == nop.nop_link) 7738c2ecf20Sopenharmony_ci return; 7748c2ecf20Sopenharmony_ci caddr = nop.nop_link; 7758c2ecf20Sopenharmony_ci goto again; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci case CMD_TX: 7788c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_COMPLETE) 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name); 7818c2ecf20Sopenharmony_ci priv(dev)->restart = 1; 7828c2ecf20Sopenharmony_ci return; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci default: 7858c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name, 7868c2ecf20Sopenharmony_ci nop.nop_command & CMD_MASK, caddr); 7878c2ecf20Sopenharmony_ci priv(dev)->restart = 1; 7888c2ecf20Sopenharmony_ci return; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci while (nop.nop_status & STAT_COMPLETE) { 7928c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_OK) { 7938c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 7948c2ecf20Sopenharmony_ci dev->stats.collisions += (nop.nop_status & STAT_COLLISIONS); 7958c2ecf20Sopenharmony_ci } else { 7968c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_COLLAFTERTX) 7998c2ecf20Sopenharmony_ci dev->stats.collisions++; 8008c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_NOCARRIER) 8018c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors++; 8028c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_TXLOSTCTS) 8038c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: cts lost\n", dev->name); 8048c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_TXSLOWDMA) 8058c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 8068c2ecf20Sopenharmony_ci if (nop.nop_status & STAT_COLLEXCESSIVE) 8078c2ecf20Sopenharmony_ci dev->stats.collisions += 16; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (nop.nop_link == caddr) { 8118c2ecf20Sopenharmony_ci printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name); 8128c2ecf20Sopenharmony_ci break; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci caddr = nop.nop_link; 8168c2ecf20Sopenharmony_ci ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); 8178c2ecf20Sopenharmony_ci if ((nop.nop_command & CMD_MASK) != CMD_NOP) { 8188c2ecf20Sopenharmony_ci printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name); 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (caddr == nop.nop_link) 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci caddr = nop.nop_link; 8268c2ecf20Sopenharmony_ci ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); 8278c2ecf20Sopenharmony_ci if ((nop.nop_command & CMD_MASK) != CMD_TX) { 8288c2ecf20Sopenharmony_ci printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name); 8298c2ecf20Sopenharmony_ci break; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci priv(dev)->tx_tail = caddr; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci caddr = priv(dev)->tx_head; 8358c2ecf20Sopenharmony_ci tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); 8368c2ecf20Sopenharmony_ci priv(dev)->tx_head = caddr; 8378c2ecf20Sopenharmony_ci if (tst != -1) 8388c2ecf20Sopenharmony_ci netif_wake_queue(dev); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic void 8428c2ecf20Sopenharmony_ciether1_recv_done (struct net_device *dev) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci int status; 8458c2ecf20Sopenharmony_ci int nexttail, rbdaddr; 8468c2ecf20Sopenharmony_ci rbd_t rbd; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci do { 8498c2ecf20Sopenharmony_ci status = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_status, NORMALIRQS); 8508c2ecf20Sopenharmony_ci if ((status & RFD_COMPLETE) == 0) 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci rbdaddr = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS); 8548c2ecf20Sopenharmony_ci ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) { 8578c2ecf20Sopenharmony_ci int length = rbd.rbd_status & RBD_ACNT; 8588c2ecf20Sopenharmony_ci struct sk_buff *skb; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci length = (length + 1) & ~1; 8618c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, length + 2); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (skb) { 8648c2ecf20Sopenharmony_ci skb_reserve (skb, 2); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans (skb, dev); 8698c2ecf20Sopenharmony_ci netif_rx (skb); 8708c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 8718c2ecf20Sopenharmony_ci } else 8728c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 8738c2ecf20Sopenharmony_ci } else { 8748c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: %s\n", dev->name, 8758c2ecf20Sopenharmony_ci (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid"); 8768c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci nexttail = ether1_readw(dev, priv(dev)->rx_tail, rfd_t, rfd_link, NORMALIRQS); 8808c2ecf20Sopenharmony_ci /* nexttail should be rx_head */ 8818c2ecf20Sopenharmony_ci if (nexttail != priv(dev)->rx_head) 8828c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n", 8838c2ecf20Sopenharmony_ci dev->name, nexttail, priv(dev)->rx_head); 8848c2ecf20Sopenharmony_ci ether1_writew(dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS); 8858c2ecf20Sopenharmony_ci ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_command, NORMALIRQS); 8868c2ecf20Sopenharmony_ci ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_status, NORMALIRQS); 8878c2ecf20Sopenharmony_ci ether1_writew(dev, 0, priv(dev)->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci priv(dev)->rx_tail = nexttail; 8908c2ecf20Sopenharmony_ci priv(dev)->rx_head = ether1_readw(dev, priv(dev)->rx_head, rfd_t, rfd_link, NORMALIRQS); 8918c2ecf20Sopenharmony_ci } while (1); 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic irqreturn_t 8958c2ecf20Sopenharmony_ciether1_interrupt (int irq, void *dev_id) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_id; 8988c2ecf20Sopenharmony_ci int status; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci status = ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (status) { 9038c2ecf20Sopenharmony_ci ether1_writew(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX), 9048c2ecf20Sopenharmony_ci SCB_ADDR, scb_t, scb_command, NORMALIRQS); 9058c2ecf20Sopenharmony_ci writeb(CTRL_CA | CTRL_ACK, REG_CONTROL); 9068c2ecf20Sopenharmony_ci if (status & SCB_STCX) { 9078c2ecf20Sopenharmony_ci ether1_xmit_done (dev); 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci if (status & SCB_STCNA) { 9108c2ecf20Sopenharmony_ci if (priv(dev)->resetting == 0) 9118c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name); 9128c2ecf20Sopenharmony_ci else 9138c2ecf20Sopenharmony_ci priv(dev)->resetting += 1; 9148c2ecf20Sopenharmony_ci if (ether1_readw(dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) 9158c2ecf20Sopenharmony_ci != (unsigned short)I82586_NULL) { 9168c2ecf20Sopenharmony_ci ether1_writew(dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS); 9178c2ecf20Sopenharmony_ci writeb(CTRL_CA, REG_CONTROL); 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci if (priv(dev)->resetting == 2) 9208c2ecf20Sopenharmony_ci priv(dev)->resetting = 0; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci if (status & SCB_STFR) { 9238c2ecf20Sopenharmony_ci ether1_recv_done (dev); 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci if (status & SCB_STRNR) { 9268c2ecf20Sopenharmony_ci if (ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) { 9278c2ecf20Sopenharmony_ci printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name); 9288c2ecf20Sopenharmony_ci ether1_writew(dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS); 9298c2ecf20Sopenharmony_ci writeb(CTRL_CA, REG_CONTROL); 9308c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; /* we suspended due to lack of buffer space */ 9318c2ecf20Sopenharmony_ci } else 9328c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name, 9338c2ecf20Sopenharmony_ci ether1_readw(dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS)); 9348c2ecf20Sopenharmony_ci printk (KERN_WARNING "RU ptr = %04X\n", ether1_readw(dev, SCB_ADDR, scb_t, scb_rfa_offset, 9358c2ecf20Sopenharmony_ci NORMALIRQS)); 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci } else 9388c2ecf20Sopenharmony_ci writeb(CTRL_ACK, REG_CONTROL); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic int 9448c2ecf20Sopenharmony_ciether1_close (struct net_device *dev) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci ether1_reset (dev); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci return 0; 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * Set or clear the multicast filter for this adaptor. 9558c2ecf20Sopenharmony_ci * num_addrs == -1 Promiscuous mode, receive all packets. 9568c2ecf20Sopenharmony_ci * num_addrs == 0 Normal mode, clear multicast list. 9578c2ecf20Sopenharmony_ci * num_addrs > 0 Multicast mode, receive normal and MC packets, and do 9588c2ecf20Sopenharmony_ci * best-effort filtering. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_cistatic void 9618c2ecf20Sopenharmony_ciether1_setmulticastlist (struct net_device *dev) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic void ether1_banner(void) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci static unsigned int version_printed = 0; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (net_debug && version_printed++ == 0) 9728c2ecf20Sopenharmony_ci printk(KERN_INFO "%s", version); 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic const struct net_device_ops ether1_netdev_ops = { 9768c2ecf20Sopenharmony_ci .ndo_open = ether1_open, 9778c2ecf20Sopenharmony_ci .ndo_stop = ether1_close, 9788c2ecf20Sopenharmony_ci .ndo_start_xmit = ether1_sendpacket, 9798c2ecf20Sopenharmony_ci .ndo_set_rx_mode = ether1_setmulticastlist, 9808c2ecf20Sopenharmony_ci .ndo_tx_timeout = ether1_timeout, 9818c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 9828c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 9838c2ecf20Sopenharmony_ci}; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int 9868c2ecf20Sopenharmony_ciether1_probe(struct expansion_card *ec, const struct ecard_id *id) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct net_device *dev; 9898c2ecf20Sopenharmony_ci int i, ret = 0; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci ether1_banner(); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci ret = ecard_request_resources(ec); 9948c2ecf20Sopenharmony_ci if (ret) 9958c2ecf20Sopenharmony_ci goto out; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct ether1_priv)); 9988c2ecf20Sopenharmony_ci if (!dev) { 9998c2ecf20Sopenharmony_ci ret = -ENOMEM; 10008c2ecf20Sopenharmony_ci goto release; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &ec->dev); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci dev->irq = ec->irq; 10068c2ecf20Sopenharmony_ci priv(dev)->base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); 10078c2ecf20Sopenharmony_ci if (!priv(dev)->base) { 10088c2ecf20Sopenharmony_ci ret = -ENOMEM; 10098c2ecf20Sopenharmony_ci goto free; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if ((priv(dev)->bus_type = ether1_reset(dev)) == 0) { 10138c2ecf20Sopenharmony_ci ret = -ENODEV; 10148c2ecf20Sopenharmony_ci goto free; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 10188c2ecf20Sopenharmony_ci dev->dev_addr[i] = readb(IDPROM_ADDRESS + (i << 2)); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (ether1_init_2(dev)) { 10218c2ecf20Sopenharmony_ci ret = -ENODEV; 10228c2ecf20Sopenharmony_ci goto free; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci dev->netdev_ops = ðer1_netdev_ops; 10268c2ecf20Sopenharmony_ci dev->watchdog_timeo = 5 * HZ / 100; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci ret = register_netdev(dev); 10298c2ecf20Sopenharmony_ci if (ret) 10308c2ecf20Sopenharmony_ci goto free; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: ether1 in slot %d, %pM\n", 10338c2ecf20Sopenharmony_ci dev->name, ec->slot_no, dev->dev_addr); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, dev); 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci free: 10398c2ecf20Sopenharmony_ci free_netdev(dev); 10408c2ecf20Sopenharmony_ci release: 10418c2ecf20Sopenharmony_ci ecard_release_resources(ec); 10428c2ecf20Sopenharmony_ci out: 10438c2ecf20Sopenharmony_ci return ret; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic void ether1_remove(struct expansion_card *ec) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct net_device *dev = ecard_get_drvdata(ec); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, NULL); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci unregister_netdev(dev); 10538c2ecf20Sopenharmony_ci free_netdev(dev); 10548c2ecf20Sopenharmony_ci ecard_release_resources(ec); 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic const struct ecard_id ether1_ids[] = { 10588c2ecf20Sopenharmony_ci { MANU_ACORN, PROD_ACORN_ETHER1 }, 10598c2ecf20Sopenharmony_ci { 0xffff, 0xffff } 10608c2ecf20Sopenharmony_ci}; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic struct ecard_driver ether1_driver = { 10638c2ecf20Sopenharmony_ci .probe = ether1_probe, 10648c2ecf20Sopenharmony_ci .remove = ether1_remove, 10658c2ecf20Sopenharmony_ci .id_table = ether1_ids, 10668c2ecf20Sopenharmony_ci .drv = { 10678c2ecf20Sopenharmony_ci .name = "ether1", 10688c2ecf20Sopenharmony_ci }, 10698c2ecf20Sopenharmony_ci}; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic int __init ether1_init(void) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci return ecard_register_driver(ðer1_driver); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic void __exit ether1_exit(void) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci ecard_remove_driver(ðer1_driver); 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cimodule_init(ether1_init); 10828c2ecf20Sopenharmony_cimodule_exit(ether1_exit); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1085