sgiseeq.c 22.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4
/*
 * sgiseeq.c: Seeq8003 ethernet driver for SGI machines.
 *
5
 * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
Linus Torvalds's avatar
Linus Torvalds committed
6
 */
7 8 9

#undef DEBUG

10
#include <linux/dma-mapping.h>
Linus Torvalds's avatar
Linus Torvalds committed
11 12
#include <linux/kernel.h>
#include <linux/module.h>
13
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
14 15 16 17 18 19
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
20
#include <linux/platform_device.h>
Linus Torvalds's avatar
Linus Torvalds committed
21 22 23 24 25
#include <linux/etherdevice.h>
#include <linux/skbuff.h>

#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
26
#include <asm/sgi/seeq.h>
Linus Torvalds's avatar
Linus Torvalds committed
27 28 29 30 31 32 33 34 35 36

#include "sgiseeq.h"

static char *sgiseeqstr = "SGI Seeq8003";

/*
 * If you want speed, you do something silly, it always has worked for me.  So,
 * with that in mind, I've decided to make this driver look completely like a
 * stupid Lance from a driver architecture perspective.  Only difference is that
 * here our "ring buffer" looks and acts like a real Lance one does but is
Lucas De Marchi's avatar
Lucas De Marchi committed
37
 * laid out like how the HPC DMA and the Seeq want it to.  You'd be surprised
Linus Torvalds's avatar
Linus Torvalds committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
 * how a stupid idea like this can pay off in performance, not to mention
 * making this driver 2,000 times easier to write. ;-)
 */

/* Tune these if we tend to run out often etc. */
#define SEEQ_RX_BUFFERS  16
#define SEEQ_TX_BUFFERS  16

#define PKT_BUF_SZ       1584

#define NEXT_RX(i)  (((i) + 1) & (SEEQ_RX_BUFFERS - 1))
#define NEXT_TX(i)  (((i) + 1) & (SEEQ_TX_BUFFERS - 1))
#define PREV_RX(i)  (((i) - 1) & (SEEQ_RX_BUFFERS - 1))
#define PREV_TX(i)  (((i) - 1) & (SEEQ_TX_BUFFERS - 1))

#define TX_BUFFS_AVAIL(sp) ((sp->tx_old <= sp->tx_new) ? \
			    sp->tx_old + (SEEQ_TX_BUFFERS - 1) - sp->tx_new : \
			    sp->tx_old - sp->tx_new - 1)

57 58 59 60 61 62 63 64 65 66 67
#define VIRT_TO_DMA(sp, v) ((sp)->srings_dma +                                 \
				  (dma_addr_t)((unsigned long)(v) -            \
					       (unsigned long)((sp)->rx_desc)))

/* Copy frames shorter than rx_copybreak, otherwise pass on up in
 * a full sized sk_buff.  Value of 100 stolen from tulip.c (!alpha).
 */
static int rx_copybreak = 100;

#define PAD_SIZE    (128 - sizeof(struct hpc_dma_desc) - sizeof(void *))

Linus Torvalds's avatar
Linus Torvalds committed
68 69
struct sgiseeq_rx_desc {
	volatile struct hpc_dma_desc rdma;
70 71
	u8 padding[PAD_SIZE];
	struct sk_buff *skb;
Linus Torvalds's avatar
Linus Torvalds committed
72 73 74 75
};

struct sgiseeq_tx_desc {
	volatile struct hpc_dma_desc tdma;
76 77
	u8 padding[PAD_SIZE];
	struct sk_buff *skb;
Linus Torvalds's avatar
Linus Torvalds committed
78 79 80
};

/*
Lucas De Marchi's avatar
Lucas De Marchi committed
81
 * Warning: This structure is laid out in a certain way because HPC dma
Linus Torvalds's avatar
Linus Torvalds committed
82 83 84 85 86 87 88 89 90 91
 *          descriptors must be 8-byte aligned.  So don't touch this without
 *          some care.
 */
struct sgiseeq_init_block { /* Note the name ;-) */
	struct sgiseeq_rx_desc rxvector[SEEQ_RX_BUFFERS];
	struct sgiseeq_tx_desc txvector[SEEQ_TX_BUFFERS];
};

struct sgiseeq_private {
	struct sgiseeq_init_block *srings;
92
	dma_addr_t srings_dma;
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

	/* Ptrs to the descriptors in uncached space. */
	struct sgiseeq_rx_desc *rx_desc;
	struct sgiseeq_tx_desc *tx_desc;

	char *name;
	struct hpc3_ethregs *hregs;
	struct sgiseeq_regs *sregs;

	/* Ring entry counters. */
	unsigned int rx_new, tx_new;
	unsigned int rx_old, tx_old;

	int is_edlc;
	unsigned char control;
	unsigned char mode;

	spinlock_t tx_lock;
};

113 114
static inline void dma_sync_desc_cpu(struct net_device *dev, void *addr)
{
115 116 117 118
	struct sgiseeq_private *sp = netdev_priv(dev);

	dma_sync_single_for_cpu(dev->dev.parent, VIRT_TO_DMA(sp, addr),
			sizeof(struct sgiseeq_rx_desc), DMA_BIDIRECTIONAL);
119 120 121 122
}

static inline void dma_sync_desc_dev(struct net_device *dev, void *addr)
{
123 124 125 126
	struct sgiseeq_private *sp = netdev_priv(dev);

	dma_sync_single_for_device(dev->dev.parent, VIRT_TO_DMA(sp, addr),
			sizeof(struct sgiseeq_rx_desc), DMA_BIDIRECTIONAL);
127 128
}

Linus Torvalds's avatar
Linus Torvalds committed
129 130
static inline void hpc3_eth_reset(struct hpc3_ethregs *hregs)
{
131
	hregs->reset = HPC3_ERST_CRESET | HPC3_ERST_CLRIRQ;
Linus Torvalds's avatar
Linus Torvalds committed
132
	udelay(20);
133
	hregs->reset = 0;
Linus Torvalds's avatar
Linus Torvalds committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
}

static inline void reset_hpc3_and_seeq(struct hpc3_ethregs *hregs,
				       struct sgiseeq_regs *sregs)
{
	hregs->rx_ctrl = hregs->tx_ctrl = 0;
	hpc3_eth_reset(hregs);
}

#define RSTAT_GO_BITS (SEEQ_RCMD_IGOOD | SEEQ_RCMD_IEOF | SEEQ_RCMD_ISHORT | \
		       SEEQ_RCMD_IDRIB | SEEQ_RCMD_ICRC)

static inline void seeq_go(struct sgiseeq_private *sp,
			   struct hpc3_ethregs *hregs,
			   struct sgiseeq_regs *sregs)
{
	sregs->rstat = sp->mode | RSTAT_GO_BITS;
	hregs->rx_ctrl = HPC3_ERXCTRL_ACTIVE;
}

static inline void __sgiseeq_set_mac_address(struct net_device *dev)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct sgiseeq_regs *sregs = sp->sregs;
	int i;

	sregs->tstat = SEEQ_TCMD_RB0;
	for (i = 0; i < 6; i++)
		sregs->rw.eth_addr[i] = dev->dev_addr[i];
}

static int sgiseeq_set_mac_address(struct net_device *dev, void *addr)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct sockaddr *sa = addr;

	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);

	spin_lock_irq(&sp->tx_lock);
	__sgiseeq_set_mac_address(dev);
	spin_unlock_irq(&sp->tx_lock);

	return 0;
}

#define TCNTINFO_INIT (HPCDMA_EOX | HPCDMA_ETXD)
#define RCNTCFG_INIT  (HPCDMA_OWN | HPCDMA_EORP | HPCDMA_XIE)
#define RCNTINFO_INIT (RCNTCFG_INIT | (PKT_BUF_SZ & HPCDMA_BCNT))

static int seeq_init_ring(struct net_device *dev)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	int i;

	netif_stop_queue(dev);
	sp->rx_new = sp->tx_new = 0;
	sp->rx_old = sp->tx_old = 0;

	__sgiseeq_set_mac_address(dev);

	/* Setup tx ring. */
	for(i = 0; i < SEEQ_TX_BUFFERS; i++) {
		sp->tx_desc[i].tdma.cntinfo = TCNTINFO_INIT;
197
		dma_sync_desc_dev(dev, &sp->tx_desc[i]);
Linus Torvalds's avatar
Linus Torvalds committed
198 199 200 201
	}

	/* And now the rx ring. */
	for (i = 0; i < SEEQ_RX_BUFFERS; i++) {
202
		if (!sp->rx_desc[i].skb) {
203 204
			dma_addr_t dma_addr;
			struct sk_buff *skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
Linus Torvalds's avatar
Linus Torvalds committed
205

206
			if (skb == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
207
				return -ENOMEM;
208 209 210 211 212 213
			skb_reserve(skb, 2);
			dma_addr = dma_map_single(dev->dev.parent,
						  skb->data - 2,
						  PKT_BUF_SZ, DMA_FROM_DEVICE);
			sp->rx_desc[i].skb = skb;
			sp->rx_desc[i].rdma.pbuf = dma_addr;
Linus Torvalds's avatar
Linus Torvalds committed
214 215
		}
		sp->rx_desc[i].rdma.cntinfo = RCNTINFO_INIT;
216
		dma_sync_desc_dev(dev, &sp->rx_desc[i]);
Linus Torvalds's avatar
Linus Torvalds committed
217 218
	}
	sp->rx_desc[i - 1].rdma.cntinfo |= HPCDMA_EOR;
219
	dma_sync_desc_dev(dev, &sp->rx_desc[i - 1]);
Linus Torvalds's avatar
Linus Torvalds committed
220 221 222
	return 0;
}

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
static void seeq_purge_ring(struct net_device *dev)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	int i;

	/* clear tx ring. */
	for (i = 0; i < SEEQ_TX_BUFFERS; i++) {
		if (sp->tx_desc[i].skb) {
			dev_kfree_skb(sp->tx_desc[i].skb);
			sp->tx_desc[i].skb = NULL;
		}
	}

	/* And now the rx ring. */
	for (i = 0; i < SEEQ_RX_BUFFERS; i++) {
		if (sp->rx_desc[i].skb) {
			dev_kfree_skb(sp->rx_desc[i].skb);
			sp->rx_desc[i].skb = NULL;
		}
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
245 246 247 248
#ifdef DEBUG
static struct sgiseeq_private *gpriv;
static struct net_device *gdev;

249
static void sgiseeq_dump_rings(void)
Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
{
	static int once;
	struct sgiseeq_rx_desc *r = gpriv->rx_desc;
	struct sgiseeq_tx_desc *t = gpriv->tx_desc;
	struct hpc3_ethregs *hregs = gpriv->hregs;
	int i;

	if (once)
		return;
	once++;
	printk("RING DUMP:\n");
	for (i = 0; i < SEEQ_RX_BUFFERS; i++) {
		printk("RX [%d]: @(%p) [%08x,%08x,%08x] ",
		       i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo,
		       r[i].rdma.pnext);
		i += 1;
		printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n",
		       i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo,
		       r[i].rdma.pnext);
	}
	for (i = 0; i < SEEQ_TX_BUFFERS; i++) {
		printk("TX [%d]: @(%p) [%08x,%08x,%08x] ",
		       i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo,
		       t[i].tdma.pnext);
		i += 1;
		printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n",
		       i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo,
		       t[i].tdma.pnext);
	}
	printk("INFO: [rx_new = %d rx_old=%d] [tx_new = %d tx_old = %d]\n",
	       gpriv->rx_new, gpriv->rx_old, gpriv->tx_new, gpriv->tx_old);
	printk("RREGS: rx_cbptr[%08x] rx_ndptr[%08x] rx_ctrl[%08x]\n",
	       hregs->rx_cbptr, hregs->rx_ndptr, hregs->rx_ctrl);
	printk("TREGS: tx_cbptr[%08x] tx_ndptr[%08x] tx_ctrl[%08x]\n",
	       hregs->tx_cbptr, hregs->tx_ndptr, hregs->tx_ctrl);
}
#endif

#define TSTAT_INIT_SEEQ (SEEQ_TCMD_IPT|SEEQ_TCMD_I16|SEEQ_TCMD_IC|SEEQ_TCMD_IUF)
#define TSTAT_INIT_EDLC ((TSTAT_INIT_SEEQ) | SEEQ_TCMD_RB2)

static int init_seeq(struct net_device *dev, struct sgiseeq_private *sp,
		     struct sgiseeq_regs *sregs)
{
	struct hpc3_ethregs *hregs = sp->hregs;
	int err;

	reset_hpc3_and_seeq(hregs, sregs);
	err = seeq_init_ring(dev);
	if (err)
		return err;

	/* Setup to field the proper interrupt types. */
	if (sp->is_edlc) {
		sregs->tstat = TSTAT_INIT_EDLC;
		sregs->rw.wregs.control = sp->control;
		sregs->rw.wregs.frame_gap = 0;
	} else {
		sregs->tstat = TSTAT_INIT_SEEQ;
	}

311 312
	hregs->rx_ndptr = VIRT_TO_DMA(sp, sp->rx_desc);
	hregs->tx_ndptr = VIRT_TO_DMA(sp, sp->tx_desc);
Linus Torvalds's avatar
Linus Torvalds committed
313 314 315 316 317

	seeq_go(sp, hregs, sregs);
	return 0;
}

318
static void record_rx_errors(struct net_device *dev, unsigned char status)
Linus Torvalds's avatar
Linus Torvalds committed
319 320 321
{
	if (status & SEEQ_RSTAT_OVERF ||
	    status & SEEQ_RSTAT_SFRAME)
322
		dev->stats.rx_over_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
323
	if (status & SEEQ_RSTAT_CERROR)
324
		dev->stats.rx_crc_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
325
	if (status & SEEQ_RSTAT_DERROR)
326
		dev->stats.rx_frame_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
327
	if (status & SEEQ_RSTAT_REOF)
328
		dev->stats.rx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
329 330 331 332 333 334 335
}

static inline void rx_maybe_restart(struct sgiseeq_private *sp,
				    struct hpc3_ethregs *hregs,
				    struct sgiseeq_regs *sregs)
{
	if (!(hregs->rx_ctrl & HPC3_ERXCTRL_ACTIVE)) {
336
		hregs->rx_ndptr = VIRT_TO_DMA(sp, sp->rx_desc + sp->rx_new);
Linus Torvalds's avatar
Linus Torvalds committed
337 338 339 340 341 342 343 344 345
		seeq_go(sp, hregs, sregs);
	}
}

static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp,
			      struct hpc3_ethregs *hregs,
			      struct sgiseeq_regs *sregs)
{
	struct sgiseeq_rx_desc *rd;
346
	struct sk_buff *skb = NULL;
347
	struct sk_buff *newskb;
Linus Torvalds's avatar
Linus Torvalds committed
348 349 350 351 352
	unsigned char pkt_status;
	int len = 0;
	unsigned int orig_end = PREV_RX(sp->rx_new);

	/* Service every received packet. */
353
	rd = &sp->rx_desc[sp->rx_new];
354
	dma_sync_desc_cpu(dev, rd);
355
	while (!(rd->rdma.cntinfo & HPCDMA_OWN)) {
Linus Torvalds's avatar
Linus Torvalds committed
356
		len = PKT_BUF_SZ - (rd->rdma.cntinfo & HPCDMA_BCNT) - 3;
357 358 359
		dma_unmap_single(dev->dev.parent, rd->rdma.pbuf,
				 PKT_BUF_SZ, DMA_FROM_DEVICE);
		pkt_status = rd->skb->data[len];
Linus Torvalds's avatar
Linus Torvalds committed
360 361
		if (pkt_status & SEEQ_RSTAT_FIG) {
			/* Packet is OK. */
362
			/* We don't want to receive our own packets */
363
			if (!ether_addr_equal(rd->skb->data + 6, dev->dev_addr)) {
364 365 366 367 368 369 370 371 372 373
				if (len > rx_copybreak) {
					skb = rd->skb;
					newskb = netdev_alloc_skb(dev, PKT_BUF_SZ);
					if (!newskb) {
						newskb = skb;
						skb = NULL;
						goto memory_squeeze;
					}
					skb_reserve(newskb, 2);
				} else {
374 375
					skb = netdev_alloc_skb_ip_align(dev, len);
					if (skb)
376
						skb_copy_to_linear_data(skb, rd->skb->data, len);
377

378 379 380 381 382 383
					newskb = rd->skb;
				}
memory_squeeze:
				if (skb) {
					skb_put(skb, len);
					skb->protocol = eth_type_trans(skb, dev);
Linus Torvalds's avatar
Linus Torvalds committed
384
					netif_rx(skb);
385 386
					dev->stats.rx_packets++;
					dev->stats.rx_bytes += len;
Linus Torvalds's avatar
Linus Torvalds committed
387
				} else {
388
					dev->stats.rx_dropped++;
Linus Torvalds's avatar
Linus Torvalds committed
389 390
				}
			} else {
391 392
				/* Silently drop my own packets */
				newskb = rd->skb;
Linus Torvalds's avatar
Linus Torvalds committed
393 394
			}
		} else {
395
			record_rx_errors(dev, pkt_status);
396
			newskb = rd->skb;
Linus Torvalds's avatar
Linus Torvalds committed
397
		}
398 399 400 401
		rd->skb = newskb;
		rd->rdma.pbuf = dma_map_single(dev->dev.parent,
					       newskb->data - 2,
					       PKT_BUF_SZ, DMA_FROM_DEVICE);
Linus Torvalds's avatar
Linus Torvalds committed
402 403 404 405

		/* Return the entry to the ring pool. */
		rd->rdma.cntinfo = RCNTINFO_INIT;
		sp->rx_new = NEXT_RX(sp->rx_new);
406
		dma_sync_desc_dev(dev, rd);
407
		rd = &sp->rx_desc[sp->rx_new];
408
		dma_sync_desc_cpu(dev, rd);
Linus Torvalds's avatar
Linus Torvalds committed
409
	}
410 411
	dma_sync_desc_dev(dev, rd);

412
	dma_sync_desc_cpu(dev, &sp->rx_desc[orig_end]);
Linus Torvalds's avatar
Linus Torvalds committed
413
	sp->rx_desc[orig_end].rdma.cntinfo &= ~(HPCDMA_EOR);
414 415
	dma_sync_desc_dev(dev, &sp->rx_desc[orig_end]);
	dma_sync_desc_cpu(dev, &sp->rx_desc[PREV_RX(sp->rx_new)]);
Linus Torvalds's avatar
Linus Torvalds committed
416
	sp->rx_desc[PREV_RX(sp->rx_new)].rdma.cntinfo |= HPCDMA_EOR;
417
	dma_sync_desc_dev(dev, &sp->rx_desc[PREV_RX(sp->rx_new)]);
Linus Torvalds's avatar
Linus Torvalds committed
418 419 420 421 422 423 424 425 426 427 428 429
	rx_maybe_restart(sp, hregs, sregs);
}

static inline void tx_maybe_reset_collisions(struct sgiseeq_private *sp,
					     struct sgiseeq_regs *sregs)
{
	if (sp->is_edlc) {
		sregs->rw.wregs.control = sp->control & ~(SEEQ_CTRL_XCNT);
		sregs->rw.wregs.control = sp->control;
	}
}

430 431
static inline void kick_tx(struct net_device *dev,
			   struct sgiseeq_private *sp,
Linus Torvalds's avatar
Linus Torvalds committed
432 433
			   struct hpc3_ethregs *hregs)
{
434 435 436
	struct sgiseeq_tx_desc *td;
	int i = sp->tx_old;

Linus Torvalds's avatar
Linus Torvalds committed
437 438 439 440 441 442
	/* If the HPC aint doin nothin, and there are more packets
	 * with ETXD cleared and XIU set we must make very certain
	 * that we restart the HPC else we risk locking up the
	 * adapter.  The following code is only safe iff the HPCDMA
	 * is not active!
	 */
443
	td = &sp->tx_desc[i];
444
	dma_sync_desc_cpu(dev, td);
Linus Torvalds's avatar
Linus Torvalds committed
445
	while ((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) ==
446 447 448
	      (HPCDMA_XIU | HPCDMA_ETXD)) {
		i = NEXT_TX(i);
		td = &sp->tx_desc[i];
449
		dma_sync_desc_cpu(dev, td);
450
	}
Linus Torvalds's avatar
Linus Torvalds committed
451
	if (td->tdma.cntinfo & HPCDMA_XIU) {
452
		dma_sync_desc_dev(dev, td);
453
		hregs->tx_ndptr = VIRT_TO_DMA(sp, td);
Linus Torvalds's avatar
Linus Torvalds committed
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
		hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;
	}
}

static inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp,
			      struct hpc3_ethregs *hregs,
			      struct sgiseeq_regs *sregs)
{
	struct sgiseeq_tx_desc *td;
	unsigned long status = hregs->tx_ctrl;
	int j;

	tx_maybe_reset_collisions(sp, sregs);

	if (!(status & (HPC3_ETXCTRL_ACTIVE | SEEQ_TSTAT_PTRANS))) {
		/* Oops, HPC detected some sort of error. */
		if (status & SEEQ_TSTAT_R16)
471
			dev->stats.tx_aborted_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
472
		if (status & SEEQ_TSTAT_UFLOW)
473
			dev->stats.tx_fifo_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
474
		if (status & SEEQ_TSTAT_LCLS)
475
			dev->stats.collisions++;
Linus Torvalds's avatar
Linus Torvalds committed
476 477 478 479 480 481
	}

	/* Ack 'em... */
	for (j = sp->tx_old; j != sp->tx_new; j = NEXT_TX(j)) {
		td = &sp->tx_desc[j];

482
		dma_sync_desc_cpu(dev, td);
Linus Torvalds's avatar
Linus Torvalds committed
483 484 485
		if (!(td->tdma.cntinfo & (HPCDMA_XIU)))
			break;
		if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) {
486
			dma_sync_desc_dev(dev, td);
Linus Torvalds's avatar
Linus Torvalds committed
487
			if (!(status & HPC3_ETXCTRL_ACTIVE)) {
488
				hregs->tx_ndptr = VIRT_TO_DMA(sp, td);
Linus Torvalds's avatar
Linus Torvalds committed
489 490 491 492
				hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;
			}
			break;
		}
493
		dev->stats.tx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
494 495 496
		sp->tx_old = NEXT_TX(sp->tx_old);
		td->tdma.cntinfo &= ~(HPCDMA_XIU | HPCDMA_XIE);
		td->tdma.cntinfo |= HPCDMA_EOX;
497 498 499 500
		if (td->skb) {
			dev_kfree_skb_any(td->skb);
			td->skb = NULL;
		}
501
		dma_sync_desc_dev(dev, td);
Linus Torvalds's avatar
Linus Torvalds committed
502 503 504
	}
}

505
static irqreturn_t sgiseeq_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
506 507 508 509 510 511 512 513 514
{
	struct net_device *dev = (struct net_device *) dev_id;
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct hpc3_ethregs *hregs = sp->hregs;
	struct sgiseeq_regs *sregs = sp->sregs;

	spin_lock(&sp->tx_lock);

	/* Ack the IRQ and set software state. */
515
	hregs->reset = HPC3_ERST_CLRIRQ;
Linus Torvalds's avatar
Linus Torvalds committed
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540

	/* Always check for received packets. */
	sgiseeq_rx(dev, sp, hregs, sregs);

	/* Only check for tx acks if we have something queued. */
	if (sp->tx_old != sp->tx_new)
		sgiseeq_tx(dev, sp, hregs, sregs);

	if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) {
		netif_wake_queue(dev);
	}
	spin_unlock(&sp->tx_lock);

	return IRQ_HANDLED;
}

static int sgiseeq_open(struct net_device *dev)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct sgiseeq_regs *sregs = sp->sregs;
	unsigned int irq = dev->irq;
	int err;

	if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) {
		printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq);
541
		return -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
	}

	err = init_seeq(dev, sp, sregs);
	if (err)
		goto out_free_irq;

	netif_start_queue(dev);

	return 0;

out_free_irq:
	free_irq(irq, dev);

	return err;
}

static int sgiseeq_close(struct net_device *dev)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct sgiseeq_regs *sregs = sp->sregs;
562
	unsigned int irq = dev->irq;
Linus Torvalds's avatar
Linus Torvalds committed
563 564 565 566 567

	netif_stop_queue(dev);

	/* Shutdown the Seeq. */
	reset_hpc3_and_seeq(sp->hregs, sregs);
568
	free_irq(irq, dev);
569
	seeq_purge_ring(dev);
Linus Torvalds's avatar
Linus Torvalds committed
570 571 572 573 574 575 576 577 578 579 580 581 582 583

	return 0;
}

static inline int sgiseeq_reset(struct net_device *dev)
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct sgiseeq_regs *sregs = sp->sregs;
	int err;

	err = init_seeq(dev, sp, sregs);
	if (err)
		return err;

584
	netif_trans_update(dev); /* prevent tx timeout */
Linus Torvalds's avatar
Linus Torvalds committed
585 586 587 588 589
	netif_wake_queue(dev);

	return 0;
}

590 591
static netdev_tx_t
sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
592 593 594 595 596
{
	struct sgiseeq_private *sp = netdev_priv(dev);
	struct hpc3_ethregs *hregs = sp->hregs;
	unsigned long flags;
	struct sgiseeq_tx_desc *td;
597
	int len, entry;
Linus Torvalds's avatar
Linus Torvalds committed
598 599 600 601

	spin_lock_irqsave(&sp->tx_lock, flags);

	/* Setup... */
602 603
	len = skb->len;
	if (len < ETH_ZLEN) {
604 605
		if (skb_padto(skb, ETH_ZLEN)) {
			spin_unlock_irqrestore(&sp->tx_lock, flags);
606
			return NETDEV_TX_OK;
607
		}
608 609 610
		len = ETH_ZLEN;
	}

611
	dev->stats.tx_bytes += len;
Linus Torvalds's avatar
Linus Torvalds committed
612 613
	entry = sp->tx_new;
	td = &sp->tx_desc[entry];
614
	dma_sync_desc_cpu(dev, td);
Linus Torvalds's avatar
Linus Torvalds committed
615 616 617 618 619 620 621 622 623 624 625 626 627 628

	/* Create entry.  There are so many races with adding a new
	 * descriptor to the chain:
	 * 1) Assume that the HPC is off processing a DMA chain while
	 *    we are changing all of the following.
	 * 2) Do no allow the HPC to look at a new descriptor until
	 *    we have completely set up it's state.  This means, do
	 *    not clear HPCDMA_EOX in the current last descritptor
	 *    until the one we are adding looks consistent and could
	 *    be processes right now.
	 * 3) The tx interrupt code must notice when we've added a new
	 *    entry and the HPC got to the end of the chain before we
	 *    added this new entry and restarted it.
	 */
629 630 631
	td->skb = skb;
	td->tdma.pbuf = dma_map_single(dev->dev.parent, skb->data,
				       len, DMA_TO_DEVICE);
Linus Torvalds's avatar
Linus Torvalds committed
632 633
	td->tdma.cntinfo = (len & HPCDMA_BCNT) |
	                   HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX;
634
	dma_sync_desc_dev(dev, td);
Linus Torvalds's avatar
Linus Torvalds committed
635 636 637 638
	if (sp->tx_old != sp->tx_new) {
		struct sgiseeq_tx_desc *backend;

		backend = &sp->tx_desc[PREV_TX(sp->tx_new)];
639
		dma_sync_desc_cpu(dev, backend);
Linus Torvalds's avatar
Linus Torvalds committed
640
		backend->tdma.cntinfo &= ~HPCDMA_EOX;
641
		dma_sync_desc_dev(dev, backend);
Linus Torvalds's avatar
Linus Torvalds committed
642 643 644 645 646
	}
	sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */

	/* Maybe kick the HPC back into motion. */
	if (!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE))
647
		kick_tx(dev, sp, hregs);
Linus Torvalds's avatar
Linus Torvalds committed
648 649 650 651 652

	if (!TX_BUFFS_AVAIL(sp))
		netif_stop_queue(dev);
	spin_unlock_irqrestore(&sp->tx_lock, flags);

653
	return NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
654 655
}

656
static void timeout(struct net_device *dev, unsigned int txqueue)
Linus Torvalds's avatar
Linus Torvalds committed
657 658 659 660
{
	printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name);
	sgiseeq_reset(dev);

661
	netif_trans_update(dev); /* prevent tx timeout */
Linus Torvalds's avatar
Linus Torvalds committed
662 663 664 665 666
	netif_wake_queue(dev);
}

static void sgiseeq_set_multicast(struct net_device *dev)
{
667
	struct sgiseeq_private *sp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
668 669 670 671
	unsigned char oldmode = sp->mode;

	if(dev->flags & IFF_PROMISC)
		sp->mode = SEEQ_RCMD_RANY;
672
	else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
Linus Torvalds's avatar
Linus Torvalds committed
673 674 675 676 677 678 679 680 681 682 683 684
		sp->mode = SEEQ_RCMD_RBMCAST;
	else
		sp->mode = SEEQ_RCMD_RBCAST;

	/* XXX I know this sucks, but is there a better way to reprogram
	 * XXX the receiver? At least, this shouldn't happen too often.
	 */

	if (oldmode != sp->mode)
		sgiseeq_reset(dev);
}

685 686 687
static inline void setup_tx_ring(struct net_device *dev,
				 struct sgiseeq_tx_desc *buf,
				 int nbufs)
Linus Torvalds's avatar
Linus Torvalds committed
688
{
689
	struct sgiseeq_private *sp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
690 691 692
	int i = 0;

	while (i < (nbufs - 1)) {
693
		buf[i].tdma.pnext = VIRT_TO_DMA(sp, buf + i + 1);
Linus Torvalds's avatar
Linus Torvalds committed
694
		buf[i].tdma.pbuf = 0;
695
		dma_sync_desc_dev(dev, &buf[i]);
Linus Torvalds's avatar
Linus Torvalds committed
696 697
		i++;
	}
698
	buf[i].tdma.pnext = VIRT_TO_DMA(sp, buf);
699
	dma_sync_desc_dev(dev, &buf[i]);
Linus Torvalds's avatar
Linus Torvalds committed
700 701
}

702 703 704
static inline void setup_rx_ring(struct net_device *dev,
				 struct sgiseeq_rx_desc *buf,
				 int nbufs)
Linus Torvalds's avatar
Linus Torvalds committed
705
{
706
	struct sgiseeq_private *sp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
707 708 709
	int i = 0;

	while (i < (nbufs - 1)) {
710
		buf[i].rdma.pnext = VIRT_TO_DMA(sp, buf + i + 1);
Linus Torvalds's avatar
Linus Torvalds committed
711
		buf[i].rdma.pbuf = 0;
712
		dma_sync_desc_dev(dev, &buf[i]);
Linus Torvalds's avatar
Linus Torvalds committed
713 714 715
		i++;
	}
	buf[i].rdma.pbuf = 0;
716
	buf[i].rdma.pnext = VIRT_TO_DMA(sp, buf);
717
	dma_sync_desc_dev(dev, &buf[i]);
Linus Torvalds's avatar
Linus Torvalds committed
718 719
}

720 721 722 723 724
static const struct net_device_ops sgiseeq_netdev_ops = {
	.ndo_open		= sgiseeq_open,
	.ndo_stop		= sgiseeq_close,
	.ndo_start_xmit		= sgiseeq_start_xmit,
	.ndo_tx_timeout		= timeout,
725
	.ndo_set_rx_mode	= sgiseeq_set_multicast,
726 727 728 729
	.ndo_set_mac_address	= sgiseeq_set_mac_address,
	.ndo_validate_addr	= eth_validate_addr,
};

730
static int sgiseeq_probe(struct platform_device *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
731
{
732
	struct sgiseeq_platform_data *pd = dev_get_platdata(&pdev->dev);
733
	struct hpc3_regs *hpcregs = pd->hpc;
Linus Torvalds's avatar
Linus Torvalds committed
734
	struct sgiseeq_init_block *sr;
735
	unsigned int irq = pd->irq;
Linus Torvalds's avatar
Linus Torvalds committed
736 737
	struct sgiseeq_private *sp;
	struct net_device *dev;
738
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
739 740 741 742 743 744

	dev = alloc_etherdev(sizeof (struct sgiseeq_private));
	if (!dev) {
		err = -ENOMEM;
		goto err_out;
	}
745 746

	platform_set_drvdata(pdev, dev);
747
	SET_NETDEV_DEV(dev, &pdev->dev);
Linus Torvalds's avatar
Linus Torvalds committed
748 749 750
	sp = netdev_priv(dev);

	/* Make private data page aligned */
751 752
	sr = dma_alloc_noncoherent(&pdev->dev, sizeof(*sp->srings),
			&sp->srings_dma, DMA_BIDIRECTIONAL, GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
753 754 755 756 757 758
	if (!sr) {
		printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n");
		err = -ENOMEM;
		goto err_out_free_dev;
	}
	sp->srings = sr;
759 760
	sp->rx_desc = sp->srings->rxvector;
	sp->tx_desc = sp->srings->txvector;
761
	spin_lock_init(&sp->tx_lock);
762 763

	/* A couple calculations now, saves many cycles later. */
764 765
	setup_rx_ring(dev, sp->rx_desc, SEEQ_RX_BUFFERS);
	setup_tx_ring(dev, sp->tx_desc, SEEQ_TX_BUFFERS);
Linus Torvalds's avatar
Linus Torvalds committed
766

767
	memcpy(dev->dev_addr, pd->mac, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
768 769 770 771 772

#ifdef DEBUG
	gpriv = sp;
	gdev = dev;
#endif
773 774
	sp->sregs = (struct sgiseeq_regs *) &hpcregs->eth_ext[0];
	sp->hregs = &hpcregs->ethregs;
Linus Torvalds's avatar
Linus Torvalds committed
775 776 777
	sp->name = sgiseeqstr;
	sp->mode = SEEQ_RCMD_RBCAST;

778 779 780 781 782
	/* Setup PIO and DMA transfer timing */
	sp->hregs->pconfig = 0x161;
	sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP |
			     HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026;

783 784 785 786 787
	/* Setup PIO and DMA transfer timing */
	sp->hregs->pconfig = 0x161;
	sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP |
			     HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026;

Linus Torvalds's avatar
Linus Torvalds committed
788 789 790 791 792 793 794 795 796
	/* Reset the chip. */
	hpc3_eth_reset(sp->hregs);

	sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff);
	if (sp->is_edlc)
		sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT |
			      SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT |
			      SEEQ_CTRL_ENCARR;

797
	dev->netdev_ops		= &sgiseeq_netdev_ops;
Linus Torvalds's avatar
Linus Torvalds committed
798 799 800 801 802 803 804
	dev->watchdog_timeo	= (200 * HZ) / 1000;
	dev->irq		= irq;

	if (register_netdev(dev)) {
		printk(KERN_ERR "Sgiseeq: Cannot register net device, "
		       "aborting.\n");
		err = -ENODEV;
805
		goto err_out_free_attrs;
Linus Torvalds's avatar
Linus Torvalds committed
806 807
	}

808
	printk(KERN_INFO "%s: %s %pM\n", dev->name, sgiseeqstr, dev->dev_addr);
Linus Torvalds's avatar
Linus Torvalds committed
809 810 811

	return 0;

812
err_out_free_attrs:
813 814
	dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings,
		       sp->srings_dma, DMA_BIDIRECTIONAL);
Linus Torvalds's avatar
Linus Torvalds committed
815
err_out_free_dev:
816
	free_netdev(dev);
Linus Torvalds's avatar
Linus Torvalds committed
817 818 819 820 821

err_out:
	return err;
}

822
static int sgiseeq_remove(struct platform_device *pdev)
Linus Torvalds's avatar
Linus Torvalds committed
823
{
824 825
	struct net_device *dev = platform_get_drvdata(pdev);
	struct sgiseeq_private *sp = netdev_priv(dev);
826

827
	unregister_netdev(dev);
828 829
	dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings,
		       sp->srings_dma, DMA_BIDIRECTIONAL);
830
	free_netdev(dev);
831 832

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
833 834
}

835 836
static struct platform_driver sgiseeq_driver = {
	.probe	= sgiseeq_probe,
837
	.remove	= sgiseeq_remove,
838
	.driver = {
839
		.name	= "sgiseeq",
840 841
	}
};
Linus Torvalds's avatar
Linus Torvalds committed
842

843
module_platform_driver(sgiseeq_driver);
Linus Torvalds's avatar
Linus Torvalds committed
844

845 846
MODULE_DESCRIPTION("SGI Seeq 8003 driver");
MODULE_AUTHOR("Linux/MIPS Mailing List <linux-mips@linux-mips.org>");
Linus Torvalds's avatar
Linus Torvalds committed
847
MODULE_LICENSE("GPL");
848
MODULE_ALIAS("platform:sgiseeq");