hydra.c 18.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9
/* Linux/68k Hydra Amiganet board driver v2.1 BETA                          */
/* copyleft by Topi Kanerva (topi@susanna.oulu.fi)                          */
/* also some code & lots of fixes by Timo Rossi (trossi@cc.jyu.fi)          */

/* The code is mostly based on the linux/68k Ariadne driver                 */
/* copyrighted by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) */
/* and Peter De Schrijver (Peter.DeSchrijver@linux.cc.kuleuven.ac.be)       */

/* This file is subject to the terms and conditions of the GNU General      */
Linus Torvalds's avatar
Linus Torvalds committed
10 11
/* Public License.  See the file COPYING in the main directory of the       */
/* Linux distribution for more details.                                     */
Linus Torvalds's avatar
Linus Torvalds committed
12 13 14 15 16 17

/* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a    */
/* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM  */
/* and 10BASE-2 (thin coax) and AUI connectors.                             */


Linus Torvalds's avatar
Linus Torvalds committed
18
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
19 20 21 22 23 24 25 26 27
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
Linus Torvalds's avatar
Linus Torvalds committed
28
#include <linux/etherdevice.h>
Linus Torvalds's avatar
Linus Torvalds committed
29
#include <linux/skbuff.h>
Linus Torvalds's avatar
Linus Torvalds committed
30
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
31 32 33 34 35 36 37

#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/irq.h>

#include <asm/amigaints.h>
#include <asm/amigahw.h>
Linus Torvalds's avatar
Linus Torvalds committed
38
#include <linux/zorro.h>
Linus Torvalds's avatar
Linus Torvalds committed
39 40 41 42 43 44 45 46 47 48 49 50

#include "hydra.h"


#define HYDRA_DEBUG
#undef HAVE_MULTICAST

#define HYDRA_VERSION "v2.1 BETA"

#undef HYDRA_DEBUG        /* define this for (lots of) debugging information */

#if 0                         /* currently hardwired to one transmit buffer */
Linus Torvalds's avatar
Linus Torvalds committed
51 52
	#define TX_RING_SIZE	5
	#define RX_RING_SIZE	16
Linus Torvalds's avatar
Linus Torvalds committed
53
#else
Linus Torvalds's avatar
Linus Torvalds committed
54 55
	#define TX_RING_SIZE 1
	#define RX_RING_SIZE 8
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59 60 61 62 63 64 65 66 67 68
#endif

#define ETHER_MIN_LEN 64
#define ETHER_MAX_LEN 1518
#define ETHER_ADDR_LEN 6


/*
 *   let's define here nice macros for writing and reading NIC registers
 *
 * the CIA accesses here are uses to make sure the minimum time
 * requirement between NIC chip selects is met.
 */
Linus Torvalds's avatar
Linus Torvalds committed
69 70
#define WRITE_REG(reg, val) (ciaa.pra, ((u8)(*(nicbase+(reg))=val)))
#define READ_REG(reg) (ciaa.pra, ((u8)(*(nicbase+(reg)))))
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75 76 77 78 79 80 81

/* mask value for the interrupts we use */
#define NIC_INTS (ISR_PRX | ISR_PTX | ISR_RXE | ISR_TXE | ISR_OVW | ISR_CNT)

/* only broadcasts, no promiscuous mode for now */
#define NIC_RCRBITS (0)

/*
 *   Private Device Data
 */
struct hydra_private
Linus Torvalds's avatar
Linus Torvalds committed
82 83 84 85 86 87 88 89
{
	u8 *hydra_base;
	u8 *hydra_nic_base;
	u16 tx_page_start;
	u16 rx_page_start;
	u16 rx_page_stop;
	u16 next_pkt;
	struct net_device_stats stats;
Linus Torvalds's avatar
Linus Torvalds committed
90
	unsigned int key;
Linus Torvalds's avatar
Linus Torvalds committed
91
};
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94

static int hydra_open(struct device *dev);
static int hydra_start_xmit(struct sk_buff *skb, struct device *dev);
Linus Torvalds's avatar
Linus Torvalds committed
95
static void hydra_interrupt(int irq, void *data, struct pt_regs *fp);
Linus Torvalds's avatar
Linus Torvalds committed
96
static void __inline__ hydra_rx(struct device *dev, struct hydra_private *priv, volatile u8 *nicbase);
Linus Torvalds's avatar
Linus Torvalds committed
97
static int hydra_close(struct device *dev);
Linus Torvalds's avatar
Linus Torvalds committed
98
static struct net_device_stats *hydra_get_stats(struct device *dev);
Linus Torvalds's avatar
Linus Torvalds committed
99 100 101 102 103
#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif


Linus Torvalds's avatar
Linus Torvalds committed
104 105 106
/* this is now coherent with the C version below, */
/* compile the source with -D__USE_ASM__ if you   */
/* want it - it'll only be some 10% faster though */
Linus Torvalds's avatar
Linus Torvalds committed
107 108 109

#if defined (__GNUC__) && defined (__mc68000__) && defined (USE_ASM)

Linus Torvalds's avatar
Linus Torvalds committed
110 111
static __inline__ void *memcpyw(u16 *dest, u16 *src, int len)
{
Linus Torvalds's avatar
Linus Torvalds committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
    __asm__("   move.l %0,%/a1; move.l %1,%/a0; move.l %2,%/d0 \n\t"
	    "   cmpi.l #2,%/d0 \n\t"
	    "1: bcs.s  2f \n\t"
            "   move.w %/a0@+,%/a1@+ \n\t"
	    "   subq.l #2,%/d0 \n\t"
	    "   bra.s  1b \n\t"
	    "2: cmpi.l #1,%/d0 \n\t"
	    "   bne.s  3f \n\t"
	    "   move.w %/a0@,%/d0 \n\t"
	    "   swap.w %/d0 \n\t"
	    "   move.b %/d0,%/a1@ \n\t"
	    "3: moveq  #0,%/d0 \n\t"
	  :
	  : "g" (dest), "g" (src), "g" (len)
	  : "a1", "a0", "d0");
    return;
}

#else

/* hydra memory can only be read or written as words or longwords.  */
/* that will mean that we'll have to write a special memcpy for it. */
/* this one here relies on the fact that _writes_ to hydra memory   */
/* are guaranteed to be of even length. (reads can be arbitrary)    */

Linus Torvalds's avatar
Linus Torvalds committed
137 138 139 140 141 142 143 144 145 146
/*
 *	FIXME: Surely we should be using the OS generic stuff and do
 *
 *	memcpy(dest,src,(len+1)&~1);
 *
 *	Can a 68K guy with this card check that ? - better yet
 *	use a copy/checksum on it.
 */
 
static void memcpyw(u16 *dest, u16 *src, int len)
Linus Torvalds's avatar
Linus Torvalds committed
147
{
Linus Torvalds's avatar
Linus Torvalds committed
148 149
	if(len & 1)
		len++;
Linus Torvalds's avatar
Linus Torvalds committed
150

Linus Torvalds's avatar
Linus Torvalds committed
151 152 153 154 155
	while (len >= 2) 
	{
		*(dest++) = *(src++);
		len -= 2;
	}
Linus Torvalds's avatar
Linus Torvalds committed
156 157 158 159
}

#endif

Linus Torvalds's avatar
Linus Torvalds committed
160
__initfunc(int hydra_probe(struct device *dev))
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163
{
	struct hydra_private *priv;
	u32 board;
Linus Torvalds's avatar
Linus Torvalds committed
164 165
	unsigned int key;
	const struct ConfigDev *cd;
Linus Torvalds's avatar
Linus Torvalds committed
166
	int j;
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169 170 171

#ifdef HYDRA_DEBUG
 printk("hydra_probe(%x)\n", dev);
#endif

Linus Torvalds's avatar
Linus Torvalds committed
172
	if ((key = zorro_find(ZORRO_PROD_HYDRA_SYSTEMS_AMIGANET, 0, 0))) 
Linus Torvalds's avatar
Linus Torvalds committed
173 174 175 176 177 178
	{
		cd = zorro_get_board(key);
		if((board = (u32) cd->cd_BoardAddr))
		{
			for(j = 0; j < ETHER_ADDR_LEN; j++)
				dev->dev_addr[j] = *((u8 *)ZTWO_VADDR(board + HYDRA_ADDRPROM + 2*j));
Linus Torvalds's avatar
Linus Torvalds committed
179
	    
Linus Torvalds's avatar
Linus Torvalds committed
180 181 182 183
			printk("%s: hydra at 0x%08x, address %02x:%02x:%02x:%02x:%02x:%02x (hydra.c " HYDRA_VERSION ")\n",
				dev->name, (int)board, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
				dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
			init_etherdev(dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
184
	    
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187
			dev->priv = kmalloc(sizeof(struct hydra_private), GFP_KERNEL);
			priv = (struct hydra_private *)dev->priv;
			memset(priv, 0, sizeof(struct hydra_private));
Linus Torvalds's avatar
Linus Torvalds committed
188
	    
Linus Torvalds's avatar
Linus Torvalds committed
189 190 191
			priv->hydra_base = (u8 *) ZTWO_VADDR(board);
			priv->hydra_nic_base = (u8 *) ZTWO_VADDR(board) + HYDRA_NIC_BASE;
			priv->key = key;
Linus Torvalds's avatar
Linus Torvalds committed
192
	    
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196
			dev->open = &hydra_open;
			dev->stop = &hydra_close;
			dev->hard_start_xmit = &hydra_start_xmit;
			dev->get_stats = &hydra_get_stats;
Linus Torvalds's avatar
Linus Torvalds committed
197 198 199
#ifdef HAVE_MULTICAST
	 		dev->set_multicast_list = &set_multicast_list;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203 204 205 206 207 208
	 		
	 		/*
	 		 *	Cannot yet do multicast
	 		 */
	 		dev->flags&=~IFF_MULTICAST;
			zorro_config_board(key, 0);
			return(0);
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
209
	return(-ENODEV);
Linus Torvalds's avatar
Linus Torvalds committed
210
}
Linus Torvalds's avatar
Linus Torvalds committed
211 212 213


static int hydra_open(struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216 217
{
	struct hydra_private *priv = (struct hydra_private *)dev->priv;
	volatile u8 *nicbase = priv->hydra_nic_base;
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
218 219
    
#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
220
	printk("hydra_open(0x%x)\n", dev);
Linus Torvalds's avatar
Linus Torvalds committed
221 222
#endif

Linus Torvalds's avatar
Linus Torvalds committed
223 224 225 226
	/* first, initialize the private structure */
	priv->tx_page_start = 0;   /* these are 256 byte buffers for NS8390 */
	priv->rx_page_start = 6;
	priv->rx_page_stop  = 62;  /* these values are hard coded for now */
Linus Torvalds's avatar
Linus Torvalds committed
227

Linus Torvalds's avatar
Linus Torvalds committed
228 229
	 /* Reset the NS8390 NIC */
	WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_STOP);
Linus Torvalds's avatar
Linus Torvalds committed
230

Linus Torvalds's avatar
Linus Torvalds committed
231 232
	/* be sure that the NIC is in stopped state */
	while(!(READ_REG(NIC_ISR) & ISR_RST));
Linus Torvalds's avatar
Linus Torvalds committed
233

Linus Torvalds's avatar
Linus Torvalds committed
234 235
	/* word transfer, big endian bytes, loopback, FIFO threshold 4 bytes */
	WRITE_REG(NIC_DCR, DCR_WTS | DCR_BOS | DCR_LS | DCR_FT0);
Linus Torvalds's avatar
Linus Torvalds committed
236

Linus Torvalds's avatar
Linus Torvalds committed
237 238 239
	/* clear remote byte count registers */
	WRITE_REG(NIC_RBCR0, 0);
	WRITE_REG(NIC_RBCR1, 0);
Linus Torvalds's avatar
Linus Torvalds committed
240

Linus Torvalds's avatar
Linus Torvalds committed
241 242
	/* accept packets addressed to this card and also broadcast packets */
	WRITE_REG(NIC_RCR, NIC_RCRBITS);
Linus Torvalds's avatar
Linus Torvalds committed
243

Linus Torvalds's avatar
Linus Torvalds committed
244 245
	/* enable loopback mode 1 */
	WRITE_REG(NIC_TCR, TCR_LB1);
Linus Torvalds's avatar
Linus Torvalds committed
246

Linus Torvalds's avatar
Linus Torvalds committed
247 248 249 250
	/* initialize receive buffer ring */
	WRITE_REG(NIC_PSTART, priv->rx_page_start);
	WRITE_REG(NIC_PSTOP, priv->rx_page_stop);
	WRITE_REG(NIC_BNDRY, priv->rx_page_start);
Linus Torvalds's avatar
Linus Torvalds committed
251

Linus Torvalds's avatar
Linus Torvalds committed
252 253
	/* clear interrupts */
	WRITE_REG(NIC_ISR, 0xff);
Linus Torvalds's avatar
Linus Torvalds committed
254

Linus Torvalds's avatar
Linus Torvalds committed
255 256
	/* enable interrupts */
	WRITE_REG(NIC_IMR, NIC_INTS);
Linus Torvalds's avatar
Linus Torvalds committed
257

Linus Torvalds's avatar
Linus Torvalds committed
258 259
	/* set the ethernet hardware address */
	WRITE_REG(NIC_CR, CR_PAGE1 | CR_NODMA | CR_STOP); /* goto page 1 */
Linus Torvalds's avatar
Linus Torvalds committed
260

Linus Torvalds's avatar
Linus Torvalds committed
261 262 263 264 265 266
	WRITE_REG(NIC_PAR0, dev->dev_addr[0]);
	WRITE_REG(NIC_PAR1, dev->dev_addr[1]);
	WRITE_REG(NIC_PAR2, dev->dev_addr[2]);
	WRITE_REG(NIC_PAR3, dev->dev_addr[3]);
	WRITE_REG(NIC_PAR4, dev->dev_addr[4]);
	WRITE_REG(NIC_PAR5, dev->dev_addr[5]);
Linus Torvalds's avatar
Linus Torvalds committed
267

Linus Torvalds's avatar
Linus Torvalds committed
268 269 270
	/* clear multicast hash table */
	for(i = 0; i < 8; i++)
		WRITE_REG(NIC_MAR0 + 2*i, 0);
Linus Torvalds's avatar
Linus Torvalds committed
271

Linus Torvalds's avatar
Linus Torvalds committed
272 273
	priv->next_pkt = priv->rx_page_start+1; /* init our s/w variable */
	WRITE_REG(NIC_CURR, priv->next_pkt);    /* set the next buf for current */
Linus Torvalds's avatar
Linus Torvalds committed
274

Linus Torvalds's avatar
Linus Torvalds committed
275 276
	/* goto page 0, start NIC */
	WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_START);
Linus Torvalds's avatar
Linus Torvalds committed
277

Linus Torvalds's avatar
Linus Torvalds committed
278 279
	/* take interface out of loopback */
	WRITE_REG(NIC_TCR, 0);
Linus Torvalds's avatar
Linus Torvalds committed
280

Linus Torvalds's avatar
Linus Torvalds committed
281 282 283
	dev->tbusy = 0;
	dev->interrupt = 0;
	dev->start = 1;
Linus Torvalds's avatar
Linus Torvalds committed
284
    
Linus Torvalds's avatar
Linus Torvalds committed
285 286
	if(request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, 0, "Hydra Ethernet", dev))
		return(-EAGAIN);
Linus Torvalds's avatar
Linus Torvalds committed
287

Linus Torvalds's avatar
Linus Torvalds committed
288
	MOD_INC_USE_COUNT;
Linus Torvalds's avatar
Linus Torvalds committed
289

Linus Torvalds's avatar
Linus Torvalds committed
290 291
	return(0);
}
Linus Torvalds's avatar
Linus Torvalds committed
292 293 294 295


static int hydra_close(struct device *dev)
{
Linus Torvalds's avatar
Linus Torvalds committed
296 297 298
	struct hydra_private *priv = (struct hydra_private *)dev->priv;
	volatile u8 *nicbase = priv->hydra_nic_base;
	int n = 5000;
Linus Torvalds's avatar
Linus Torvalds committed
299

Linus Torvalds's avatar
Linus Torvalds committed
300 301
	dev->start = 0;
	dev->tbusy = 1;
Linus Torvalds's avatar
Linus Torvalds committed
302 303

#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
304 305
	printk("%s: Shutting down ethercard\n", dev->name);
	printk("%s: %d packets missed\n", dev->name, priv->stats.rx_missed_errors);
Linus Torvalds's avatar
Linus Torvalds committed
306 307
#endif

Linus Torvalds's avatar
Linus Torvalds committed
308
	WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_STOP);
Linus Torvalds's avatar
Linus Torvalds committed
309

Linus Torvalds's avatar
Linus Torvalds committed
310 311
	/* wait for NIC to stop (what a nice timeout..) */
	while(((READ_REG(NIC_ISR) & ISR_RST) == 0) && --n);
Linus Torvalds's avatar
Linus Torvalds committed
312
    
Linus Torvalds's avatar
Linus Torvalds committed
313
	free_irq(IRQ_AMIGA_PORTS, dev);
Linus Torvalds's avatar
Linus Torvalds committed
314

Linus Torvalds's avatar
Linus Torvalds committed
315
	MOD_DEC_USE_COUNT;
Linus Torvalds's avatar
Linus Torvalds committed
316

Linus Torvalds's avatar
Linus Torvalds committed
317
	return(0);
Linus Torvalds's avatar
Linus Torvalds committed
318 319 320
}


Linus Torvalds's avatar
Linus Torvalds committed
321
static void hydra_interrupt(int irq, void *data, struct pt_regs *fp)
Linus Torvalds's avatar
Linus Torvalds committed
322 323
{
	volatile u8 *nicbase;
Linus Torvalds's avatar
Linus Torvalds committed
324
  
Linus Torvalds's avatar
Linus Torvalds committed
325 326 327
	struct device *dev = (struct device *) data;
	struct hydra_private *priv;
	u16 intbits;
Linus Torvalds's avatar
Linus Torvalds committed
328

Linus Torvalds's avatar
Linus Torvalds committed
329 330 331 332 333
	if(dev == NULL)
	{
		printk("hydra_interrupt(): irq for unknown device\n");
		return;
	}
Linus Torvalds's avatar
Linus Torvalds committed
334

Linus Torvalds's avatar
Linus Torvalds committed
335 336 337
	/* this is not likely a problem - i think */
	if(dev->interrupt)
		printk("%s: re-entering the interrupt handler\n", dev->name);
Linus Torvalds's avatar
Linus Torvalds committed
338

Linus Torvalds's avatar
Linus Torvalds committed
339
	dev->interrupt = 1;
Linus Torvalds's avatar
Linus Torvalds committed
340

Linus Torvalds's avatar
Linus Torvalds committed
341 342
	priv = (struct hydra_private *) dev->priv;
	nicbase = (u8 *) priv->hydra_nic_base;
Linus Torvalds's avatar
Linus Torvalds committed
343

Linus Torvalds's avatar
Linus Torvalds committed
344 345
	/* select page 0 */
	WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
Linus Torvalds's avatar
Linus Torvalds committed
346

Linus Torvalds's avatar
Linus Torvalds committed
347 348
	intbits = READ_REG(NIC_ISR) & NIC_INTS;
	if(intbits == 0)
Linus Torvalds's avatar
Linus Torvalds committed
349
        {
Linus Torvalds's avatar
Linus Torvalds committed
350 351 352
		dev->interrupt = 0;
		return;
	}
Linus Torvalds's avatar
Linus Torvalds committed
353 354 355 356 357

	/* acknowledge all interrupts, by clearing the interrupt flag */
	WRITE_REG(NIC_ISR, intbits);

	if((intbits & ISR_PTX) && !(intbits & ISR_TXE))
Linus Torvalds's avatar
Linus Torvalds committed
358 359 360 361
	{
		dev->tbusy = 0;
		mark_bh(NET_BH);
	}
Linus Torvalds's avatar
Linus Torvalds committed
362 363
	
	if((intbits & ISR_PRX) && !(intbits & ISR_RXE))/* packet received OK */
Linus Torvalds's avatar
Linus Torvalds committed
364
		hydra_rx(dev, priv, nicbase);
Linus Torvalds's avatar
Linus Torvalds committed
365 366

        if(intbits & ISR_TXE)
Linus Torvalds's avatar
Linus Torvalds committed
367
		priv->stats.tx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
368 369

        if(intbits & ISR_RXE)
Linus Torvalds's avatar
Linus Torvalds committed
370
		priv->stats.rx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
371

Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375 376 377
	if(intbits & ISR_CNT) 
	{
		/*
		 * read the tally counters and (currently) ignore the values
		 * might be useful because of bugs of some versions of the 8390 NIC
		 */
Linus Torvalds's avatar
Linus Torvalds committed
378
#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
379
		printk("hydra_interrupt(): ISR_CNT\n");
Linus Torvalds's avatar
Linus Torvalds committed
380
#endif
Linus Torvalds's avatar
Linus Torvalds committed
381 382 383
		(void)READ_REG(NIC_CNTR0);
		(void)READ_REG(NIC_CNTR1);
		(void)READ_REG(NIC_CNTR2);
Linus Torvalds's avatar
Linus Torvalds committed
384 385 386
	}
	
	if(intbits & ISR_OVW)
Linus Torvalds's avatar
Linus Torvalds committed
387 388 389
	{
#ifdef HYDRA_DEBUG
		WRITE_REG(NIC_CR, CR_PAGE1 | CR_START | CR_NODMA);
Linus Torvalds's avatar
Linus Torvalds committed
390 391 392
/* another one just too much for me to comprehend - basically this could  */
/* only occur because of invalid access to hydra ram, thus invalidating  */
/* the interrupt bits read - in average usage these do not occur at all */
Linus Torvalds's avatar
Linus Torvalds committed
393 394 395 396
		printk("hydra_interrupt(): overwrite warning, NIC_ISR %02x, NIC_CURR %02x\n",
			intbits, READ_REG(NIC_CURR));
		WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
397 398
	    

Linus Torvalds's avatar
Linus Torvalds committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
		/* overwrite warning occurred, stop NIC & check the BOUNDARY pointer */
		/* FIXME - real overwrite handling needed !! */

		printk("hydra_interrupt(): overwrite warning, resetting NIC\n");
		WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_STOP);
		while(!(READ_REG(NIC_ISR) & ISR_RST));
		/* wait for NIC to reset */
		WRITE_REG(NIC_DCR, DCR_WTS | DCR_BOS | DCR_LS | DCR_FT0);
		WRITE_REG(NIC_RBCR0, 0);
		WRITE_REG(NIC_RBCR1, 0);
		WRITE_REG(NIC_RCR, NIC_RCRBITS);
		WRITE_REG(NIC_TCR, TCR_LB1);
		WRITE_REG(NIC_PSTART, priv->rx_page_start);
		WRITE_REG(NIC_PSTOP, priv->rx_page_stop);
		WRITE_REG(NIC_BNDRY, priv->rx_page_start);
		WRITE_REG(NIC_ISR, 0xff);
		WRITE_REG(NIC_IMR, NIC_INTS);
		/* currently this _won't_ reset my hydra, even though it is */
		/* basically the same code as in the board init - any ideas? */

		priv->next_pkt = priv->rx_page_start+1; /* init our s/w variable */
		WRITE_REG(NIC_CURR, priv->next_pkt);    /* set the next buf for current */
Linus Torvalds's avatar
Linus Torvalds committed
421
	    
Linus Torvalds's avatar
Linus Torvalds committed
422
		WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_START);
Linus Torvalds's avatar
Linus Torvalds committed
423

Linus Torvalds's avatar
Linus Torvalds committed
424 425 426 427 428
		WRITE_REG(NIC_TCR, 0);
	}

	dev->interrupt = 0;
	return;
Linus Torvalds's avatar
Linus Torvalds committed
429 430 431 432 433 434
    }


/*
 * packet transmit routine
 */
Linus Torvalds's avatar
Linus Torvalds committed
435

Linus Torvalds's avatar
Linus Torvalds committed
436
static int hydra_start_xmit(struct sk_buff *skb, struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439 440
{
	struct hydra_private *priv = (struct hydra_private *)dev->priv;
	volatile u8 *nicbase = priv->hydra_nic_base;
	int len, len1;
Linus Torvalds's avatar
Linus Torvalds committed
441 442 443

	/* Transmitter timeout, serious problems. */

Linus Torvalds's avatar
Linus Torvalds committed
444
	if(dev->tbusy)
Linus Torvalds's avatar
Linus Torvalds committed
445
	{
Linus Torvalds's avatar
Linus Torvalds committed
446 447 448 449 450 451 452 453 454
		int tickssofar = jiffies - dev->trans_start;
		if(tickssofar < 20)
			return(1);
		WRITE_REG(NIC_CR, CR_STOP);
		printk("%s: transmit timed out, status %4.4x, resetting.\n", dev->name, 0);
		priv->stats.tx_errors++;
		dev->tbusy = 0;
		dev->trans_start = jiffies;
		return(0);
Linus Torvalds's avatar
Linus Torvalds committed
455 456
	}

Linus Torvalds's avatar
Linus Torvalds committed
457
	len=skb->len;
Linus Torvalds's avatar
Linus Torvalds committed
458

Linus Torvalds's avatar
Linus Torvalds committed
459
	/* fill in a tx ring entry */
Linus Torvalds's avatar
Linus Torvalds committed
460 461
    
#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
462
	printk("TX pkt type 0x%04x from ", ((u16 *)skb->data)[6]);
Linus Torvalds's avatar
Linus Torvalds committed
463 464
	{
		int i;
Linus Torvalds's avatar
Linus Torvalds committed
465
		u8 *ptr = &((u8 *)skb->data)[6];
Linus Torvalds's avatar
Linus Torvalds committed
466 467 468 469 470 471
		for (i = 0; i < 6; i++)
			printk("%02x", ptr[i]);
	}
	printk(" to ");
	{
		int i;
Linus Torvalds's avatar
Linus Torvalds committed
472
		u8 *ptr = (u8 *)skb->data;
Linus Torvalds's avatar
Linus Torvalds committed
473 474 475 476 477 478
		for (i = 0; i < 6; i++)
			printk("%02x", ptr[i]);
	}
	printk(" data 0x%08x len %d\n", (int)skb->data, len);
#endif

Linus Torvalds's avatar
Linus Torvalds committed
479 480 481 482 483 484 485
	/*
	 * make sure that the packet size is at least the minimum
	 * allowed ethernet packet length.
	 * (FIXME: Should also clear the unused space...)
	 * note: minimum packet length is 64, including CRC
	 */
	len1 = len;
Linus Torvalds's avatar
Linus Torvalds committed
486

Linus Torvalds's avatar
Linus Torvalds committed
487 488
	if(len < (ETHER_MIN_LEN-4))
		len = (ETHER_MIN_LEN-1);
Linus Torvalds's avatar
Linus Torvalds committed
489

Linus Torvalds's avatar
Linus Torvalds committed
490 491
	/* make sure we've got an even number of bytes to copy to hydra's mem */
	if(len & 1) len++;
Linus Torvalds's avatar
Linus Torvalds committed
492

Linus Torvalds's avatar
Linus Torvalds committed
493 494
	if((u32)(priv->hydra_base + (priv->tx_page_start << 8)) < 0x80000000)
		printk("weirdness: memcpyw(txbuf, skbdata, len): txbuf = 0x%x\n", (u_int)(priv->hydra_base+(priv->tx_page_start<<8)));
Linus Torvalds's avatar
Linus Torvalds committed
495

Linus Torvalds's avatar
Linus Torvalds committed
496 497 498 499 500 501 502 503
	/* copy the packet data to the transmit buffer 
	   in the ethernet card RAM */
	memcpyw((u16 *)(priv->hydra_base + (priv->tx_page_start << 8)),
		(u16 *)skb->data, len);
	/* clear the unused space */
	for(; len1<len; len1++)
		(u16)*(priv->hydra_base + (priv->tx_page_start<<8) + len1) = 0;
	dev_kfree_skb(skb, FREE_WRITE);
Linus Torvalds's avatar
Linus Torvalds committed
504

Linus Torvalds's avatar
Linus Torvalds committed
505
	priv->stats.tx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
506

Linus Torvalds's avatar
Linus Torvalds committed
507 508 509 510 511 512 513 514 515 516 517 518 519
	cli();
	/* make sure we are on the correct page */
	WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_START);

	/* here we configure the transmit page start register etc */
	/* notice that this code is hardwired to one transmit buffer */
	WRITE_REG(NIC_TPSR, priv->tx_page_start);
	WRITE_REG(NIC_TBCR0, len & 0xff);
	WRITE_REG(NIC_TBCR1, len >> 8);

	/* commit the packet to the wire */
	WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA | CR_TXP);
	sti();
Linus Torvalds's avatar
Linus Torvalds committed
520

Linus Torvalds's avatar
Linus Torvalds committed
521
	 dev->trans_start = jiffies;
Linus Torvalds's avatar
Linus Torvalds committed
522

Linus Torvalds's avatar
Linus Torvalds committed
523 524
	return(0);
}
Linus Torvalds's avatar
Linus Torvalds committed
525 526


Linus Torvalds's avatar
Linus Torvalds committed
527 528 529 530 531 532 533 534 535 536 537 538
static void __inline__ hydra_rx(struct device *dev, struct hydra_private *priv, volatile u8 *nicbase)
{
	volatile u16 *board_ram_ptr;
	struct sk_buff *skb;
	int hdr_next_pkt, pkt_len, len1, boundary;


	/* remove packet(s) from the ring and commit them to TCP layer */
	WRITE_REG(NIC_CR, CR_PAGE1 | CR_NODMA | CR_START); /* page 1 */
	while(priv->next_pkt != READ_REG(NIC_CURR)) /* should read this only once? */
	{
		board_ram_ptr = (u16 *)(priv->hydra_base + (priv->next_pkt << 8));
Linus Torvalds's avatar
Linus Torvalds committed
539 540
	
#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
541
		printk("next_pkt = 0x%x, board_ram_ptr = 0x%x\n", priv->next_pkt, board_ram_ptr);
Linus Torvalds's avatar
Linus Torvalds committed
542 543
#endif
	
Linus Torvalds's avatar
Linus Torvalds committed
544 545 546 547 548
		/* the following must be done with two steps, or
		   GCC optimizes it to a byte access to Hydra memory,
		   which doesn't work... */
		hdr_next_pkt = board_ram_ptr[0];
		hdr_next_pkt >>= 8;
Linus Torvalds's avatar
Linus Torvalds committed
549
	
Linus Torvalds's avatar
Linus Torvalds committed
550 551
		pkt_len = board_ram_ptr[1];
		pkt_len = ((pkt_len >> 8) | ((pkt_len & 0xff) << 8));
Linus Torvalds's avatar
Linus Torvalds committed
552 553
	
#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
554
		printk("hydra_interrupt(): hdr_next_pkt = 0x%02x, len = %d\n", hdr_next_pkt, pkt_len);
Linus Torvalds's avatar
Linus Torvalds committed
555 556
#endif
	
Linus Torvalds's avatar
Linus Torvalds committed
557 558 559 560
		if(pkt_len >= ETHER_MIN_LEN && pkt_len <= ETHER_MAX_LEN)
		{
			/* note that board_ram_ptr is u16 */
			/* CRC is not included in the packet length */
Linus Torvalds's avatar
Linus Torvalds committed
561
		
Linus Torvalds's avatar
Linus Torvalds committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
			pkt_len -= 4;
			skb = dev_alloc_skb(pkt_len+2);
			if(skb == NULL)
		  	{
				printk(KERN_INFO "%s: memory squeeze, dropping packet.\n", dev->name);
				priv->stats.rx_dropped++;
			}
			else
			{
				skb->dev = dev;
				skb_reserve(skb, 2);
				if(hdr_next_pkt < priv->next_pkt && hdr_next_pkt != priv->rx_page_start)
				{
					/* here, the packet is wrapped */
					len1 = ((priv->rx_page_stop - priv->next_pkt)<<8)-4;
			
					memcpyw((u16 *)skb_put(skb, len1), (u16 *)(board_ram_ptr+2), len1);
					memcpyw((u16 *)skb_put(skb, pkt_len-len1),  (u16 *)(priv->hydra_base+(priv->rx_page_start<<8)), pkt_len-len1);
			
Linus Torvalds's avatar
Linus Torvalds committed
581
#ifdef HYDRA_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
582
					printk("wrapped packet: %d/%d bytes\n", len1, pkt_len-len1);
Linus Torvalds's avatar
Linus Torvalds committed
583
#endif
Linus Torvalds's avatar
Linus Torvalds committed
584 585 586 587 588 589 590 591 592
				}  /* ... here, packet is not wrapped */
				else
					memcpyw((u16 *) skb_put(skb, pkt_len), (u16 *)(board_ram_ptr+2), pkt_len);
			}
		}
		else
		{
			WRITE_REG(NIC_CR, CR_PAGE1 | CR_START | CR_NODMA);
			printk("hydra_interrupt(): invalid packet len: %d, NIC_CURR = %02x\n", pkt_len, READ_REG(NIC_CURR));
Linus Torvalds's avatar
Linus Torvalds committed
593 594 595 596 597 598 599
/*
this is the error i kept getting until i switched to 0.9.10. it still doesn't
mean that the bug would have gone away - so be alarmed. the packet is likely
being fetched from a wrong memory location - but why - dunno
   
note-for-v2.1: not really problem anymore. hasn't been for a long time.
*/
Linus Torvalds's avatar
Linus Torvalds committed
600 601 602 603 604 605 606
		
			WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
			/* should probably reset the NIC here ?? */
		
			hydra_open(dev);  /* FIXME - i shouldn't really be doing this. */
			return;
		}
Linus Torvalds's avatar
Linus Torvalds committed
607
	
Linus Torvalds's avatar
Linus Torvalds committed
608 609 610 611 612
		/* now, update the next_pkt pointer */
		if(hdr_next_pkt < priv->rx_page_stop)
			priv->next_pkt = hdr_next_pkt;
		else
			printk("hydra_interrupt(): invalid next_pkt pointer %d\n", hdr_next_pkt);
Linus Torvalds's avatar
Linus Torvalds committed
613
	
Linus Torvalds's avatar
Linus Torvalds committed
614 615 616 617
		/* update the boundary pointer */
		boundary = priv->next_pkt - 1;
		if(boundary < priv->rx_page_start)
			boundary = priv->rx_page_stop - 1;
Linus Torvalds's avatar
Linus Torvalds committed
618
	
Linus Torvalds's avatar
Linus Torvalds committed
619 620 621
		/* set NIC to page 0 to update the NIC_BNDRY register */
		WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
		WRITE_REG(NIC_BNDRY, boundary);
Linus Torvalds's avatar
Linus Torvalds committed
622
	
Linus Torvalds's avatar
Linus Torvalds committed
623 624
		/* select page1 to access the NIC_CURR register */
		WRITE_REG(NIC_CR, CR_PAGE1 | CR_START | CR_NODMA);
Linus Torvalds's avatar
Linus Torvalds committed
625 626
	
	
Linus Torvalds's avatar
Linus Torvalds committed
627 628 629
		skb->protocol = eth_type_trans(skb, dev);
		netif_rx(skb);
		priv->stats.rx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
630
	
Linus Torvalds's avatar
Linus Torvalds committed
631 632 633
	}
	return;
}
Linus Torvalds's avatar
Linus Torvalds committed
634 635
    

Linus Torvalds's avatar
Linus Torvalds committed
636
static struct net_device_stats *hydra_get_stats(struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
637 638 639
{
	struct hydra_private *priv = (struct hydra_private *)dev->priv;
#if 0
Linus Torvalds's avatar
Linus Torvalds committed
640
	u8 *board = priv->hydra_base; 
Linus Torvalds's avatar
Linus Torvalds committed
641 642 643 644 645 646 647 648

	short saved_addr;
#endif
/* currently does nothing :) i'll finish this later */

	return(&priv->stats);
}

Linus Torvalds's avatar
Linus Torvalds committed
649
#ifdef HAVE_MULTICAST
Linus Torvalds's avatar
Linus Torvalds committed
650
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
Linus Torvalds's avatar
Linus Torvalds committed
651 652 653
{
	struct hydra_private *priv = (struct hydra_private *)dev->priv;
	u8 *board = priv->hydra_base;
Linus Torvalds's avatar
Linus Torvalds committed
654

Linus Torvalds's avatar
Linus Torvalds committed
655 656 657 658
	/* yes, this code is also waiting for someone to complete.. :) */
	/* (personally i don't care about multicasts at all :) */
	return;
}
Linus Torvalds's avatar
Linus Torvalds committed
659
#endif
Linus Torvalds's avatar
Linus Torvalds committed
660

Linus Torvalds's avatar
Linus Torvalds committed
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694

#ifdef MODULE
static char devicename[9] = { 0, };

static struct device hydra_dev =
{
	devicename,			/* filled in by register_netdev() */
	0, 0, 0, 0,			/* memory */
	0, 0,				/* base, irq */
	0, 0, 0, NULL, hydra_probe,
};

int init_module(void)
{
	int err;

	if ((err = register_netdev(&hydra_dev))) {
		if (err == -EIO)
			printk("No Hydra board found. Module not loaded.\n");
		return(err);
	}
	return(0);
}

void cleanup_module(void)
{
	struct hydra_private *priv = (struct hydra_private *)hydra_dev.priv;

	unregister_netdev(&hydra_dev);
	zorro_unconfig_board(priv->key, 0);
	kfree(priv);
}

#endif /* MODULE */