162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * sgiseeq.c: Seeq8003 ethernet driver for SGI machines.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#undef DEBUG
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/string.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/netdevice.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/etherdevice.h>
2262306a36Sopenharmony_ci#include <linux/skbuff.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/sgi/hpc3.h>
2562306a36Sopenharmony_ci#include <asm/sgi/ip22.h>
2662306a36Sopenharmony_ci#include <asm/sgi/seeq.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "sgiseeq.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic char *sgiseeqstr = "SGI Seeq8003";
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * If you want speed, you do something silly, it always has worked for me.  So,
3462306a36Sopenharmony_ci * with that in mind, I've decided to make this driver look completely like a
3562306a36Sopenharmony_ci * stupid Lance from a driver architecture perspective.  Only difference is that
3662306a36Sopenharmony_ci * here our "ring buffer" looks and acts like a real Lance one does but is
3762306a36Sopenharmony_ci * laid out like how the HPC DMA and the Seeq want it to.  You'd be surprised
3862306a36Sopenharmony_ci * how a stupid idea like this can pay off in performance, not to mention
3962306a36Sopenharmony_ci * making this driver 2,000 times easier to write. ;-)
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Tune these if we tend to run out often etc. */
4362306a36Sopenharmony_ci#define SEEQ_RX_BUFFERS  16
4462306a36Sopenharmony_ci#define SEEQ_TX_BUFFERS  16
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define PKT_BUF_SZ       1584
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define NEXT_RX(i)  (((i) + 1) & (SEEQ_RX_BUFFERS - 1))
4962306a36Sopenharmony_ci#define NEXT_TX(i)  (((i) + 1) & (SEEQ_TX_BUFFERS - 1))
5062306a36Sopenharmony_ci#define PREV_RX(i)  (((i) - 1) & (SEEQ_RX_BUFFERS - 1))
5162306a36Sopenharmony_ci#define PREV_TX(i)  (((i) - 1) & (SEEQ_TX_BUFFERS - 1))
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define TX_BUFFS_AVAIL(sp) ((sp->tx_old <= sp->tx_new) ? \
5462306a36Sopenharmony_ci			    sp->tx_old + (SEEQ_TX_BUFFERS - 1) - sp->tx_new : \
5562306a36Sopenharmony_ci			    sp->tx_old - sp->tx_new - 1)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define VIRT_TO_DMA(sp, v) ((sp)->srings_dma +                                 \
5862306a36Sopenharmony_ci				  (dma_addr_t)((unsigned long)(v) -            \
5962306a36Sopenharmony_ci					       (unsigned long)((sp)->rx_desc)))
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Copy frames shorter than rx_copybreak, otherwise pass on up in
6262306a36Sopenharmony_ci * a full sized sk_buff.  Value of 100 stolen from tulip.c (!alpha).
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic int rx_copybreak = 100;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define PAD_SIZE    (128 - sizeof(struct hpc_dma_desc) - sizeof(void *))
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct sgiseeq_rx_desc {
6962306a36Sopenharmony_ci	volatile struct hpc_dma_desc rdma;
7062306a36Sopenharmony_ci	u8 padding[PAD_SIZE];
7162306a36Sopenharmony_ci	struct sk_buff *skb;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistruct sgiseeq_tx_desc {
7562306a36Sopenharmony_ci	volatile struct hpc_dma_desc tdma;
7662306a36Sopenharmony_ci	u8 padding[PAD_SIZE];
7762306a36Sopenharmony_ci	struct sk_buff *skb;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * Warning: This structure is laid out in a certain way because HPC dma
8262306a36Sopenharmony_ci *          descriptors must be 8-byte aligned.  So don't touch this without
8362306a36Sopenharmony_ci *          some care.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistruct sgiseeq_init_block { /* Note the name ;-) */
8662306a36Sopenharmony_ci	struct sgiseeq_rx_desc rxvector[SEEQ_RX_BUFFERS];
8762306a36Sopenharmony_ci	struct sgiseeq_tx_desc txvector[SEEQ_TX_BUFFERS];
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistruct sgiseeq_private {
9162306a36Sopenharmony_ci	struct sgiseeq_init_block *srings;
9262306a36Sopenharmony_ci	dma_addr_t srings_dma;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Ptrs to the descriptors in uncached space. */
9562306a36Sopenharmony_ci	struct sgiseeq_rx_desc *rx_desc;
9662306a36Sopenharmony_ci	struct sgiseeq_tx_desc *tx_desc;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	char *name;
9962306a36Sopenharmony_ci	struct hpc3_ethregs *hregs;
10062306a36Sopenharmony_ci	struct sgiseeq_regs *sregs;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Ring entry counters. */
10362306a36Sopenharmony_ci	unsigned int rx_new, tx_new;
10462306a36Sopenharmony_ci	unsigned int rx_old, tx_old;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	int is_edlc;
10762306a36Sopenharmony_ci	unsigned char control;
10862306a36Sopenharmony_ci	unsigned char mode;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	spinlock_t tx_lock;
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline void dma_sync_desc_cpu(struct net_device *dev, void *addr)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dma_sync_single_for_cpu(dev->dev.parent, VIRT_TO_DMA(sp, addr),
11862306a36Sopenharmony_ci			sizeof(struct sgiseeq_rx_desc), DMA_BIDIRECTIONAL);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline void dma_sync_desc_dev(struct net_device *dev, void *addr)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	dma_sync_single_for_device(dev->dev.parent, VIRT_TO_DMA(sp, addr),
12662306a36Sopenharmony_ci			sizeof(struct sgiseeq_rx_desc), DMA_BIDIRECTIONAL);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline void hpc3_eth_reset(struct hpc3_ethregs *hregs)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	hregs->reset = HPC3_ERST_CRESET | HPC3_ERST_CLRIRQ;
13262306a36Sopenharmony_ci	udelay(20);
13362306a36Sopenharmony_ci	hregs->reset = 0;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic inline void reset_hpc3_and_seeq(struct hpc3_ethregs *hregs,
13762306a36Sopenharmony_ci				       struct sgiseeq_regs *sregs)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	hregs->rx_ctrl = hregs->tx_ctrl = 0;
14062306a36Sopenharmony_ci	hpc3_eth_reset(hregs);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define RSTAT_GO_BITS (SEEQ_RCMD_IGOOD | SEEQ_RCMD_IEOF | SEEQ_RCMD_ISHORT | \
14462306a36Sopenharmony_ci		       SEEQ_RCMD_IDRIB | SEEQ_RCMD_ICRC)
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline void seeq_go(struct sgiseeq_private *sp,
14762306a36Sopenharmony_ci			   struct hpc3_ethregs *hregs,
14862306a36Sopenharmony_ci			   struct sgiseeq_regs *sregs)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	sregs->rstat = sp->mode | RSTAT_GO_BITS;
15162306a36Sopenharmony_ci	hregs->rx_ctrl = HPC3_ERXCTRL_ACTIVE;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic inline void __sgiseeq_set_mac_address(struct net_device *dev)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
15762306a36Sopenharmony_ci	struct sgiseeq_regs *sregs = sp->sregs;
15862306a36Sopenharmony_ci	int i;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	sregs->tstat = SEEQ_TCMD_RB0;
16162306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
16262306a36Sopenharmony_ci		sregs->rw.eth_addr[i] = dev->dev_addr[i];
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int sgiseeq_set_mac_address(struct net_device *dev, void *addr)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
16862306a36Sopenharmony_ci	struct sockaddr *sa = addr;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	eth_hw_addr_set(dev, sa->sa_data);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	spin_lock_irq(&sp->tx_lock);
17362306a36Sopenharmony_ci	__sgiseeq_set_mac_address(dev);
17462306a36Sopenharmony_ci	spin_unlock_irq(&sp->tx_lock);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci#define TCNTINFO_INIT (HPCDMA_EOX | HPCDMA_ETXD)
18062306a36Sopenharmony_ci#define RCNTCFG_INIT  (HPCDMA_OWN | HPCDMA_EORP | HPCDMA_XIE)
18162306a36Sopenharmony_ci#define RCNTINFO_INIT (RCNTCFG_INIT | (PKT_BUF_SZ & HPCDMA_BCNT))
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int seeq_init_ring(struct net_device *dev)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
18662306a36Sopenharmony_ci	int i;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	netif_stop_queue(dev);
18962306a36Sopenharmony_ci	sp->rx_new = sp->tx_new = 0;
19062306a36Sopenharmony_ci	sp->rx_old = sp->tx_old = 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	__sgiseeq_set_mac_address(dev);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Setup tx ring. */
19562306a36Sopenharmony_ci	for(i = 0; i < SEEQ_TX_BUFFERS; i++) {
19662306a36Sopenharmony_ci		sp->tx_desc[i].tdma.cntinfo = TCNTINFO_INIT;
19762306a36Sopenharmony_ci		dma_sync_desc_dev(dev, &sp->tx_desc[i]);
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* And now the rx ring. */
20162306a36Sopenharmony_ci	for (i = 0; i < SEEQ_RX_BUFFERS; i++) {
20262306a36Sopenharmony_ci		if (!sp->rx_desc[i].skb) {
20362306a36Sopenharmony_ci			dma_addr_t dma_addr;
20462306a36Sopenharmony_ci			struct sk_buff *skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			if (skb == NULL)
20762306a36Sopenharmony_ci				return -ENOMEM;
20862306a36Sopenharmony_ci			skb_reserve(skb, 2);
20962306a36Sopenharmony_ci			dma_addr = dma_map_single(dev->dev.parent,
21062306a36Sopenharmony_ci						  skb->data - 2,
21162306a36Sopenharmony_ci						  PKT_BUF_SZ, DMA_FROM_DEVICE);
21262306a36Sopenharmony_ci			sp->rx_desc[i].skb = skb;
21362306a36Sopenharmony_ci			sp->rx_desc[i].rdma.pbuf = dma_addr;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci		sp->rx_desc[i].rdma.cntinfo = RCNTINFO_INIT;
21662306a36Sopenharmony_ci		dma_sync_desc_dev(dev, &sp->rx_desc[i]);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci	sp->rx_desc[i - 1].rdma.cntinfo |= HPCDMA_EOR;
21962306a36Sopenharmony_ci	dma_sync_desc_dev(dev, &sp->rx_desc[i - 1]);
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void seeq_purge_ring(struct net_device *dev)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
22662306a36Sopenharmony_ci	int i;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* clear tx ring. */
22962306a36Sopenharmony_ci	for (i = 0; i < SEEQ_TX_BUFFERS; i++) {
23062306a36Sopenharmony_ci		if (sp->tx_desc[i].skb) {
23162306a36Sopenharmony_ci			dev_kfree_skb(sp->tx_desc[i].skb);
23262306a36Sopenharmony_ci			sp->tx_desc[i].skb = NULL;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* And now the rx ring. */
23762306a36Sopenharmony_ci	for (i = 0; i < SEEQ_RX_BUFFERS; i++) {
23862306a36Sopenharmony_ci		if (sp->rx_desc[i].skb) {
23962306a36Sopenharmony_ci			dev_kfree_skb(sp->rx_desc[i].skb);
24062306a36Sopenharmony_ci			sp->rx_desc[i].skb = NULL;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci#ifdef DEBUG
24662306a36Sopenharmony_cistatic struct sgiseeq_private *gpriv;
24762306a36Sopenharmony_cistatic struct net_device *gdev;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void sgiseeq_dump_rings(void)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	static int once;
25262306a36Sopenharmony_ci	struct sgiseeq_rx_desc *r = gpriv->rx_desc;
25362306a36Sopenharmony_ci	struct sgiseeq_tx_desc *t = gpriv->tx_desc;
25462306a36Sopenharmony_ci	struct hpc3_ethregs *hregs = gpriv->hregs;
25562306a36Sopenharmony_ci	int i;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (once)
25862306a36Sopenharmony_ci		return;
25962306a36Sopenharmony_ci	once++;
26062306a36Sopenharmony_ci	printk("RING DUMP:\n");
26162306a36Sopenharmony_ci	for (i = 0; i < SEEQ_RX_BUFFERS; i++) {
26262306a36Sopenharmony_ci		printk("RX [%d]: @(%p) [%08x,%08x,%08x] ",
26362306a36Sopenharmony_ci		       i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo,
26462306a36Sopenharmony_ci		       r[i].rdma.pnext);
26562306a36Sopenharmony_ci		i += 1;
26662306a36Sopenharmony_ci		printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n",
26762306a36Sopenharmony_ci		       i, (&r[i]), r[i].rdma.pbuf, r[i].rdma.cntinfo,
26862306a36Sopenharmony_ci		       r[i].rdma.pnext);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci	for (i = 0; i < SEEQ_TX_BUFFERS; i++) {
27162306a36Sopenharmony_ci		printk("TX [%d]: @(%p) [%08x,%08x,%08x] ",
27262306a36Sopenharmony_ci		       i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo,
27362306a36Sopenharmony_ci		       t[i].tdma.pnext);
27462306a36Sopenharmony_ci		i += 1;
27562306a36Sopenharmony_ci		printk("-- [%d]: @(%p) [%08x,%08x,%08x]\n",
27662306a36Sopenharmony_ci		       i, (&t[i]), t[i].tdma.pbuf, t[i].tdma.cntinfo,
27762306a36Sopenharmony_ci		       t[i].tdma.pnext);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	printk("INFO: [rx_new = %d rx_old=%d] [tx_new = %d tx_old = %d]\n",
28062306a36Sopenharmony_ci	       gpriv->rx_new, gpriv->rx_old, gpriv->tx_new, gpriv->tx_old);
28162306a36Sopenharmony_ci	printk("RREGS: rx_cbptr[%08x] rx_ndptr[%08x] rx_ctrl[%08x]\n",
28262306a36Sopenharmony_ci	       hregs->rx_cbptr, hregs->rx_ndptr, hregs->rx_ctrl);
28362306a36Sopenharmony_ci	printk("TREGS: tx_cbptr[%08x] tx_ndptr[%08x] tx_ctrl[%08x]\n",
28462306a36Sopenharmony_ci	       hregs->tx_cbptr, hregs->tx_ndptr, hregs->tx_ctrl);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci#endif
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci#define TSTAT_INIT_SEEQ (SEEQ_TCMD_IPT|SEEQ_TCMD_I16|SEEQ_TCMD_IC|SEEQ_TCMD_IUF)
28962306a36Sopenharmony_ci#define TSTAT_INIT_EDLC ((TSTAT_INIT_SEEQ) | SEEQ_TCMD_RB2)
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int init_seeq(struct net_device *dev, struct sgiseeq_private *sp,
29262306a36Sopenharmony_ci		     struct sgiseeq_regs *sregs)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct hpc3_ethregs *hregs = sp->hregs;
29562306a36Sopenharmony_ci	int err;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	reset_hpc3_and_seeq(hregs, sregs);
29862306a36Sopenharmony_ci	err = seeq_init_ring(dev);
29962306a36Sopenharmony_ci	if (err)
30062306a36Sopenharmony_ci		return err;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Setup to field the proper interrupt types. */
30362306a36Sopenharmony_ci	if (sp->is_edlc) {
30462306a36Sopenharmony_ci		sregs->tstat = TSTAT_INIT_EDLC;
30562306a36Sopenharmony_ci		sregs->rw.wregs.control = sp->control;
30662306a36Sopenharmony_ci		sregs->rw.wregs.frame_gap = 0;
30762306a36Sopenharmony_ci	} else {
30862306a36Sopenharmony_ci		sregs->tstat = TSTAT_INIT_SEEQ;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	hregs->rx_ndptr = VIRT_TO_DMA(sp, sp->rx_desc);
31262306a36Sopenharmony_ci	hregs->tx_ndptr = VIRT_TO_DMA(sp, sp->tx_desc);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	seeq_go(sp, hregs, sregs);
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void record_rx_errors(struct net_device *dev, unsigned char status)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	if (status & SEEQ_RSTAT_OVERF ||
32162306a36Sopenharmony_ci	    status & SEEQ_RSTAT_SFRAME)
32262306a36Sopenharmony_ci		dev->stats.rx_over_errors++;
32362306a36Sopenharmony_ci	if (status & SEEQ_RSTAT_CERROR)
32462306a36Sopenharmony_ci		dev->stats.rx_crc_errors++;
32562306a36Sopenharmony_ci	if (status & SEEQ_RSTAT_DERROR)
32662306a36Sopenharmony_ci		dev->stats.rx_frame_errors++;
32762306a36Sopenharmony_ci	if (status & SEEQ_RSTAT_REOF)
32862306a36Sopenharmony_ci		dev->stats.rx_errors++;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic inline void rx_maybe_restart(struct sgiseeq_private *sp,
33262306a36Sopenharmony_ci				    struct hpc3_ethregs *hregs,
33362306a36Sopenharmony_ci				    struct sgiseeq_regs *sregs)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	if (!(hregs->rx_ctrl & HPC3_ERXCTRL_ACTIVE)) {
33662306a36Sopenharmony_ci		hregs->rx_ndptr = VIRT_TO_DMA(sp, sp->rx_desc + sp->rx_new);
33762306a36Sopenharmony_ci		seeq_go(sp, hregs, sregs);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp,
34262306a36Sopenharmony_ci			      struct hpc3_ethregs *hregs,
34362306a36Sopenharmony_ci			      struct sgiseeq_regs *sregs)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct sgiseeq_rx_desc *rd;
34662306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
34762306a36Sopenharmony_ci	struct sk_buff *newskb;
34862306a36Sopenharmony_ci	unsigned char pkt_status;
34962306a36Sopenharmony_ci	int len = 0;
35062306a36Sopenharmony_ci	unsigned int orig_end = PREV_RX(sp->rx_new);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Service every received packet. */
35362306a36Sopenharmony_ci	rd = &sp->rx_desc[sp->rx_new];
35462306a36Sopenharmony_ci	dma_sync_desc_cpu(dev, rd);
35562306a36Sopenharmony_ci	while (!(rd->rdma.cntinfo & HPCDMA_OWN)) {
35662306a36Sopenharmony_ci		len = PKT_BUF_SZ - (rd->rdma.cntinfo & HPCDMA_BCNT) - 3;
35762306a36Sopenharmony_ci		dma_unmap_single(dev->dev.parent, rd->rdma.pbuf,
35862306a36Sopenharmony_ci				 PKT_BUF_SZ, DMA_FROM_DEVICE);
35962306a36Sopenharmony_ci		pkt_status = rd->skb->data[len];
36062306a36Sopenharmony_ci		if (pkt_status & SEEQ_RSTAT_FIG) {
36162306a36Sopenharmony_ci			/* Packet is OK. */
36262306a36Sopenharmony_ci			/* We don't want to receive our own packets */
36362306a36Sopenharmony_ci			if (!ether_addr_equal(rd->skb->data + 6, dev->dev_addr)) {
36462306a36Sopenharmony_ci				if (len > rx_copybreak) {
36562306a36Sopenharmony_ci					skb = rd->skb;
36662306a36Sopenharmony_ci					newskb = netdev_alloc_skb(dev, PKT_BUF_SZ);
36762306a36Sopenharmony_ci					if (!newskb) {
36862306a36Sopenharmony_ci						newskb = skb;
36962306a36Sopenharmony_ci						skb = NULL;
37062306a36Sopenharmony_ci						goto memory_squeeze;
37162306a36Sopenharmony_ci					}
37262306a36Sopenharmony_ci					skb_reserve(newskb, 2);
37362306a36Sopenharmony_ci				} else {
37462306a36Sopenharmony_ci					skb = netdev_alloc_skb_ip_align(dev, len);
37562306a36Sopenharmony_ci					if (skb)
37662306a36Sopenharmony_ci						skb_copy_to_linear_data(skb, rd->skb->data, len);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci					newskb = rd->skb;
37962306a36Sopenharmony_ci				}
38062306a36Sopenharmony_cimemory_squeeze:
38162306a36Sopenharmony_ci				if (skb) {
38262306a36Sopenharmony_ci					skb_put(skb, len);
38362306a36Sopenharmony_ci					skb->protocol = eth_type_trans(skb, dev);
38462306a36Sopenharmony_ci					netif_rx(skb);
38562306a36Sopenharmony_ci					dev->stats.rx_packets++;
38662306a36Sopenharmony_ci					dev->stats.rx_bytes += len;
38762306a36Sopenharmony_ci				} else {
38862306a36Sopenharmony_ci					dev->stats.rx_dropped++;
38962306a36Sopenharmony_ci				}
39062306a36Sopenharmony_ci			} else {
39162306a36Sopenharmony_ci				/* Silently drop my own packets */
39262306a36Sopenharmony_ci				newskb = rd->skb;
39362306a36Sopenharmony_ci			}
39462306a36Sopenharmony_ci		} else {
39562306a36Sopenharmony_ci			record_rx_errors(dev, pkt_status);
39662306a36Sopenharmony_ci			newskb = rd->skb;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci		rd->skb = newskb;
39962306a36Sopenharmony_ci		rd->rdma.pbuf = dma_map_single(dev->dev.parent,
40062306a36Sopenharmony_ci					       newskb->data - 2,
40162306a36Sopenharmony_ci					       PKT_BUF_SZ, DMA_FROM_DEVICE);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		/* Return the entry to the ring pool. */
40462306a36Sopenharmony_ci		rd->rdma.cntinfo = RCNTINFO_INIT;
40562306a36Sopenharmony_ci		sp->rx_new = NEXT_RX(sp->rx_new);
40662306a36Sopenharmony_ci		dma_sync_desc_dev(dev, rd);
40762306a36Sopenharmony_ci		rd = &sp->rx_desc[sp->rx_new];
40862306a36Sopenharmony_ci		dma_sync_desc_cpu(dev, rd);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	dma_sync_desc_dev(dev, rd);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	dma_sync_desc_cpu(dev, &sp->rx_desc[orig_end]);
41362306a36Sopenharmony_ci	sp->rx_desc[orig_end].rdma.cntinfo &= ~(HPCDMA_EOR);
41462306a36Sopenharmony_ci	dma_sync_desc_dev(dev, &sp->rx_desc[orig_end]);
41562306a36Sopenharmony_ci	dma_sync_desc_cpu(dev, &sp->rx_desc[PREV_RX(sp->rx_new)]);
41662306a36Sopenharmony_ci	sp->rx_desc[PREV_RX(sp->rx_new)].rdma.cntinfo |= HPCDMA_EOR;
41762306a36Sopenharmony_ci	dma_sync_desc_dev(dev, &sp->rx_desc[PREV_RX(sp->rx_new)]);
41862306a36Sopenharmony_ci	rx_maybe_restart(sp, hregs, sregs);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic inline void tx_maybe_reset_collisions(struct sgiseeq_private *sp,
42262306a36Sopenharmony_ci					     struct sgiseeq_regs *sregs)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	if (sp->is_edlc) {
42562306a36Sopenharmony_ci		sregs->rw.wregs.control = sp->control & ~(SEEQ_CTRL_XCNT);
42662306a36Sopenharmony_ci		sregs->rw.wregs.control = sp->control;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic inline void kick_tx(struct net_device *dev,
43162306a36Sopenharmony_ci			   struct sgiseeq_private *sp,
43262306a36Sopenharmony_ci			   struct hpc3_ethregs *hregs)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct sgiseeq_tx_desc *td;
43562306a36Sopenharmony_ci	int i = sp->tx_old;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* If the HPC aint doin nothin, and there are more packets
43862306a36Sopenharmony_ci	 * with ETXD cleared and XIU set we must make very certain
43962306a36Sopenharmony_ci	 * that we restart the HPC else we risk locking up the
44062306a36Sopenharmony_ci	 * adapter.  The following code is only safe iff the HPCDMA
44162306a36Sopenharmony_ci	 * is not active!
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	td = &sp->tx_desc[i];
44462306a36Sopenharmony_ci	dma_sync_desc_cpu(dev, td);
44562306a36Sopenharmony_ci	while ((td->tdma.cntinfo & (HPCDMA_XIU | HPCDMA_ETXD)) ==
44662306a36Sopenharmony_ci	      (HPCDMA_XIU | HPCDMA_ETXD)) {
44762306a36Sopenharmony_ci		i = NEXT_TX(i);
44862306a36Sopenharmony_ci		td = &sp->tx_desc[i];
44962306a36Sopenharmony_ci		dma_sync_desc_cpu(dev, td);
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	if (td->tdma.cntinfo & HPCDMA_XIU) {
45262306a36Sopenharmony_ci		dma_sync_desc_dev(dev, td);
45362306a36Sopenharmony_ci		hregs->tx_ndptr = VIRT_TO_DMA(sp, td);
45462306a36Sopenharmony_ci		hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic inline void sgiseeq_tx(struct net_device *dev, struct sgiseeq_private *sp,
45962306a36Sopenharmony_ci			      struct hpc3_ethregs *hregs,
46062306a36Sopenharmony_ci			      struct sgiseeq_regs *sregs)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct sgiseeq_tx_desc *td;
46362306a36Sopenharmony_ci	unsigned long status = hregs->tx_ctrl;
46462306a36Sopenharmony_ci	int j;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	tx_maybe_reset_collisions(sp, sregs);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (!(status & (HPC3_ETXCTRL_ACTIVE | SEEQ_TSTAT_PTRANS))) {
46962306a36Sopenharmony_ci		/* Oops, HPC detected some sort of error. */
47062306a36Sopenharmony_ci		if (status & SEEQ_TSTAT_R16)
47162306a36Sopenharmony_ci			dev->stats.tx_aborted_errors++;
47262306a36Sopenharmony_ci		if (status & SEEQ_TSTAT_UFLOW)
47362306a36Sopenharmony_ci			dev->stats.tx_fifo_errors++;
47462306a36Sopenharmony_ci		if (status & SEEQ_TSTAT_LCLS)
47562306a36Sopenharmony_ci			dev->stats.collisions++;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* Ack 'em... */
47962306a36Sopenharmony_ci	for (j = sp->tx_old; j != sp->tx_new; j = NEXT_TX(j)) {
48062306a36Sopenharmony_ci		td = &sp->tx_desc[j];
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		dma_sync_desc_cpu(dev, td);
48362306a36Sopenharmony_ci		if (!(td->tdma.cntinfo & (HPCDMA_XIU)))
48462306a36Sopenharmony_ci			break;
48562306a36Sopenharmony_ci		if (!(td->tdma.cntinfo & (HPCDMA_ETXD))) {
48662306a36Sopenharmony_ci			dma_sync_desc_dev(dev, td);
48762306a36Sopenharmony_ci			if (!(status & HPC3_ETXCTRL_ACTIVE)) {
48862306a36Sopenharmony_ci				hregs->tx_ndptr = VIRT_TO_DMA(sp, td);
48962306a36Sopenharmony_ci				hregs->tx_ctrl = HPC3_ETXCTRL_ACTIVE;
49062306a36Sopenharmony_ci			}
49162306a36Sopenharmony_ci			break;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		dev->stats.tx_packets++;
49462306a36Sopenharmony_ci		sp->tx_old = NEXT_TX(sp->tx_old);
49562306a36Sopenharmony_ci		td->tdma.cntinfo &= ~(HPCDMA_XIU | HPCDMA_XIE);
49662306a36Sopenharmony_ci		td->tdma.cntinfo |= HPCDMA_EOX;
49762306a36Sopenharmony_ci		if (td->skb) {
49862306a36Sopenharmony_ci			dev_kfree_skb_any(td->skb);
49962306a36Sopenharmony_ci			td->skb = NULL;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci		dma_sync_desc_dev(dev, td);
50262306a36Sopenharmony_ci	}
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic irqreturn_t sgiseeq_interrupt(int irq, void *dev_id)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) dev_id;
50862306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
50962306a36Sopenharmony_ci	struct hpc3_ethregs *hregs = sp->hregs;
51062306a36Sopenharmony_ci	struct sgiseeq_regs *sregs = sp->sregs;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	spin_lock(&sp->tx_lock);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Ack the IRQ and set software state. */
51562306a36Sopenharmony_ci	hregs->reset = HPC3_ERST_CLRIRQ;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Always check for received packets. */
51862306a36Sopenharmony_ci	sgiseeq_rx(dev, sp, hregs, sregs);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* Only check for tx acks if we have something queued. */
52162306a36Sopenharmony_ci	if (sp->tx_old != sp->tx_new)
52262306a36Sopenharmony_ci		sgiseeq_tx(dev, sp, hregs, sregs);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if ((TX_BUFFS_AVAIL(sp) > 0) && netif_queue_stopped(dev)) {
52562306a36Sopenharmony_ci		netif_wake_queue(dev);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci	spin_unlock(&sp->tx_lock);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return IRQ_HANDLED;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int sgiseeq_open(struct net_device *dev)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
53562306a36Sopenharmony_ci	struct sgiseeq_regs *sregs = sp->sregs;
53662306a36Sopenharmony_ci	unsigned int irq = dev->irq;
53762306a36Sopenharmony_ci	int err;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (request_irq(irq, sgiseeq_interrupt, 0, sgiseeqstr, dev)) {
54062306a36Sopenharmony_ci		printk(KERN_ERR "Seeq8003: Can't get irq %d\n", dev->irq);
54162306a36Sopenharmony_ci		return -EAGAIN;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	err = init_seeq(dev, sp, sregs);
54562306a36Sopenharmony_ci	if (err)
54662306a36Sopenharmony_ci		goto out_free_irq;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	netif_start_queue(dev);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return 0;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ciout_free_irq:
55362306a36Sopenharmony_ci	free_irq(irq, dev);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return err;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int sgiseeq_close(struct net_device *dev)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
56162306a36Sopenharmony_ci	struct sgiseeq_regs *sregs = sp->sregs;
56262306a36Sopenharmony_ci	unsigned int irq = dev->irq;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	netif_stop_queue(dev);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* Shutdown the Seeq. */
56762306a36Sopenharmony_ci	reset_hpc3_and_seeq(sp->hregs, sregs);
56862306a36Sopenharmony_ci	free_irq(irq, dev);
56962306a36Sopenharmony_ci	seeq_purge_ring(dev);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return 0;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic inline int sgiseeq_reset(struct net_device *dev)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
57762306a36Sopenharmony_ci	struct sgiseeq_regs *sregs = sp->sregs;
57862306a36Sopenharmony_ci	int err;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	err = init_seeq(dev, sp, sregs);
58162306a36Sopenharmony_ci	if (err)
58262306a36Sopenharmony_ci		return err;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
58562306a36Sopenharmony_ci	netif_wake_queue(dev);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic netdev_tx_t
59162306a36Sopenharmony_cisgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
59462306a36Sopenharmony_ci	struct hpc3_ethregs *hregs = sp->hregs;
59562306a36Sopenharmony_ci	unsigned long flags;
59662306a36Sopenharmony_ci	struct sgiseeq_tx_desc *td;
59762306a36Sopenharmony_ci	int len, entry;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	spin_lock_irqsave(&sp->tx_lock, flags);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Setup... */
60262306a36Sopenharmony_ci	len = skb->len;
60362306a36Sopenharmony_ci	if (len < ETH_ZLEN) {
60462306a36Sopenharmony_ci		if (skb_padto(skb, ETH_ZLEN)) {
60562306a36Sopenharmony_ci			spin_unlock_irqrestore(&sp->tx_lock, flags);
60662306a36Sopenharmony_ci			return NETDEV_TX_OK;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci		len = ETH_ZLEN;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	dev->stats.tx_bytes += len;
61262306a36Sopenharmony_ci	entry = sp->tx_new;
61362306a36Sopenharmony_ci	td = &sp->tx_desc[entry];
61462306a36Sopenharmony_ci	dma_sync_desc_cpu(dev, td);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/* Create entry.  There are so many races with adding a new
61762306a36Sopenharmony_ci	 * descriptor to the chain:
61862306a36Sopenharmony_ci	 * 1) Assume that the HPC is off processing a DMA chain while
61962306a36Sopenharmony_ci	 *    we are changing all of the following.
62062306a36Sopenharmony_ci	 * 2) Do no allow the HPC to look at a new descriptor until
62162306a36Sopenharmony_ci	 *    we have completely set up it's state.  This means, do
62262306a36Sopenharmony_ci	 *    not clear HPCDMA_EOX in the current last descritptor
62362306a36Sopenharmony_ci	 *    until the one we are adding looks consistent and could
62462306a36Sopenharmony_ci	 *    be processes right now.
62562306a36Sopenharmony_ci	 * 3) The tx interrupt code must notice when we've added a new
62662306a36Sopenharmony_ci	 *    entry and the HPC got to the end of the chain before we
62762306a36Sopenharmony_ci	 *    added this new entry and restarted it.
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	td->skb = skb;
63062306a36Sopenharmony_ci	td->tdma.pbuf = dma_map_single(dev->dev.parent, skb->data,
63162306a36Sopenharmony_ci				       len, DMA_TO_DEVICE);
63262306a36Sopenharmony_ci	td->tdma.cntinfo = (len & HPCDMA_BCNT) |
63362306a36Sopenharmony_ci	                   HPCDMA_XIU | HPCDMA_EOXP | HPCDMA_XIE | HPCDMA_EOX;
63462306a36Sopenharmony_ci	dma_sync_desc_dev(dev, td);
63562306a36Sopenharmony_ci	if (sp->tx_old != sp->tx_new) {
63662306a36Sopenharmony_ci		struct sgiseeq_tx_desc *backend;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		backend = &sp->tx_desc[PREV_TX(sp->tx_new)];
63962306a36Sopenharmony_ci		dma_sync_desc_cpu(dev, backend);
64062306a36Sopenharmony_ci		backend->tdma.cntinfo &= ~HPCDMA_EOX;
64162306a36Sopenharmony_ci		dma_sync_desc_dev(dev, backend);
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci	sp->tx_new = NEXT_TX(sp->tx_new); /* Advance. */
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* Maybe kick the HPC back into motion. */
64662306a36Sopenharmony_ci	if (!(hregs->tx_ctrl & HPC3_ETXCTRL_ACTIVE))
64762306a36Sopenharmony_ci		kick_tx(dev, sp, hregs);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (!TX_BUFFS_AVAIL(sp))
65062306a36Sopenharmony_ci		netif_stop_queue(dev);
65162306a36Sopenharmony_ci	spin_unlock_irqrestore(&sp->tx_lock, flags);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return NETDEV_TX_OK;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic void timeout(struct net_device *dev, unsigned int txqueue)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name);
65962306a36Sopenharmony_ci	sgiseeq_reset(dev);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
66262306a36Sopenharmony_ci	netif_wake_queue(dev);
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_cistatic void sgiseeq_set_multicast(struct net_device *dev)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
66862306a36Sopenharmony_ci	unsigned char oldmode = sp->mode;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if(dev->flags & IFF_PROMISC)
67162306a36Sopenharmony_ci		sp->mode = SEEQ_RCMD_RANY;
67262306a36Sopenharmony_ci	else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
67362306a36Sopenharmony_ci		sp->mode = SEEQ_RCMD_RBMCAST;
67462306a36Sopenharmony_ci	else
67562306a36Sopenharmony_ci		sp->mode = SEEQ_RCMD_RBCAST;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* XXX I know this sucks, but is there a better way to reprogram
67862306a36Sopenharmony_ci	 * XXX the receiver? At least, this shouldn't happen too often.
67962306a36Sopenharmony_ci	 */
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (oldmode != sp->mode)
68262306a36Sopenharmony_ci		sgiseeq_reset(dev);
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic inline void setup_tx_ring(struct net_device *dev,
68662306a36Sopenharmony_ci				 struct sgiseeq_tx_desc *buf,
68762306a36Sopenharmony_ci				 int nbufs)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
69062306a36Sopenharmony_ci	int i = 0;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	while (i < (nbufs - 1)) {
69362306a36Sopenharmony_ci		buf[i].tdma.pnext = VIRT_TO_DMA(sp, buf + i + 1);
69462306a36Sopenharmony_ci		buf[i].tdma.pbuf = 0;
69562306a36Sopenharmony_ci		dma_sync_desc_dev(dev, &buf[i]);
69662306a36Sopenharmony_ci		i++;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci	buf[i].tdma.pnext = VIRT_TO_DMA(sp, buf);
69962306a36Sopenharmony_ci	dma_sync_desc_dev(dev, &buf[i]);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic inline void setup_rx_ring(struct net_device *dev,
70362306a36Sopenharmony_ci				 struct sgiseeq_rx_desc *buf,
70462306a36Sopenharmony_ci				 int nbufs)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
70762306a36Sopenharmony_ci	int i = 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	while (i < (nbufs - 1)) {
71062306a36Sopenharmony_ci		buf[i].rdma.pnext = VIRT_TO_DMA(sp, buf + i + 1);
71162306a36Sopenharmony_ci		buf[i].rdma.pbuf = 0;
71262306a36Sopenharmony_ci		dma_sync_desc_dev(dev, &buf[i]);
71362306a36Sopenharmony_ci		i++;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci	buf[i].rdma.pbuf = 0;
71662306a36Sopenharmony_ci	buf[i].rdma.pnext = VIRT_TO_DMA(sp, buf);
71762306a36Sopenharmony_ci	dma_sync_desc_dev(dev, &buf[i]);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic const struct net_device_ops sgiseeq_netdev_ops = {
72162306a36Sopenharmony_ci	.ndo_open		= sgiseeq_open,
72262306a36Sopenharmony_ci	.ndo_stop		= sgiseeq_close,
72362306a36Sopenharmony_ci	.ndo_start_xmit		= sgiseeq_start_xmit,
72462306a36Sopenharmony_ci	.ndo_tx_timeout		= timeout,
72562306a36Sopenharmony_ci	.ndo_set_rx_mode	= sgiseeq_set_multicast,
72662306a36Sopenharmony_ci	.ndo_set_mac_address	= sgiseeq_set_mac_address,
72762306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
72862306a36Sopenharmony_ci};
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic int sgiseeq_probe(struct platform_device *pdev)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct sgiseeq_platform_data *pd = dev_get_platdata(&pdev->dev);
73362306a36Sopenharmony_ci	struct hpc3_regs *hpcregs = pd->hpc;
73462306a36Sopenharmony_ci	struct sgiseeq_init_block *sr;
73562306a36Sopenharmony_ci	unsigned int irq = pd->irq;
73662306a36Sopenharmony_ci	struct sgiseeq_private *sp;
73762306a36Sopenharmony_ci	struct net_device *dev;
73862306a36Sopenharmony_ci	int err;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof (struct sgiseeq_private));
74162306a36Sopenharmony_ci	if (!dev) {
74262306a36Sopenharmony_ci		err = -ENOMEM;
74362306a36Sopenharmony_ci		goto err_out;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
74762306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
74862306a36Sopenharmony_ci	sp = netdev_priv(dev);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* Make private data page aligned */
75162306a36Sopenharmony_ci	sr = dma_alloc_noncoherent(&pdev->dev, sizeof(*sp->srings),
75262306a36Sopenharmony_ci			&sp->srings_dma, DMA_BIDIRECTIONAL, GFP_KERNEL);
75362306a36Sopenharmony_ci	if (!sr) {
75462306a36Sopenharmony_ci		printk(KERN_ERR "Sgiseeq: Page alloc failed, aborting.\n");
75562306a36Sopenharmony_ci		err = -ENOMEM;
75662306a36Sopenharmony_ci		goto err_out_free_dev;
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci	sp->srings = sr;
75962306a36Sopenharmony_ci	sp->rx_desc = sp->srings->rxvector;
76062306a36Sopenharmony_ci	sp->tx_desc = sp->srings->txvector;
76162306a36Sopenharmony_ci	spin_lock_init(&sp->tx_lock);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* A couple calculations now, saves many cycles later. */
76462306a36Sopenharmony_ci	setup_rx_ring(dev, sp->rx_desc, SEEQ_RX_BUFFERS);
76562306a36Sopenharmony_ci	setup_tx_ring(dev, sp->tx_desc, SEEQ_TX_BUFFERS);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	eth_hw_addr_set(dev, pd->mac);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci#ifdef DEBUG
77062306a36Sopenharmony_ci	gpriv = sp;
77162306a36Sopenharmony_ci	gdev = dev;
77262306a36Sopenharmony_ci#endif
77362306a36Sopenharmony_ci	sp->sregs = (struct sgiseeq_regs *) &hpcregs->eth_ext[0];
77462306a36Sopenharmony_ci	sp->hregs = &hpcregs->ethregs;
77562306a36Sopenharmony_ci	sp->name = sgiseeqstr;
77662306a36Sopenharmony_ci	sp->mode = SEEQ_RCMD_RBCAST;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* Setup PIO and DMA transfer timing */
77962306a36Sopenharmony_ci	sp->hregs->pconfig = 0x161;
78062306a36Sopenharmony_ci	sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP |
78162306a36Sopenharmony_ci			     HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* Setup PIO and DMA transfer timing */
78462306a36Sopenharmony_ci	sp->hregs->pconfig = 0x161;
78562306a36Sopenharmony_ci	sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP |
78662306a36Sopenharmony_ci			     HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/* Reset the chip. */
78962306a36Sopenharmony_ci	hpc3_eth_reset(sp->hregs);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	sp->is_edlc = !(sp->sregs->rw.rregs.collision_tx[0] & 0xff);
79262306a36Sopenharmony_ci	if (sp->is_edlc)
79362306a36Sopenharmony_ci		sp->control = SEEQ_CTRL_XCNT | SEEQ_CTRL_ACCNT |
79462306a36Sopenharmony_ci			      SEEQ_CTRL_SFLAG | SEEQ_CTRL_ESHORT |
79562306a36Sopenharmony_ci			      SEEQ_CTRL_ENCARR;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	dev->netdev_ops		= &sgiseeq_netdev_ops;
79862306a36Sopenharmony_ci	dev->watchdog_timeo	= (200 * HZ) / 1000;
79962306a36Sopenharmony_ci	dev->irq		= irq;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (register_netdev(dev)) {
80262306a36Sopenharmony_ci		printk(KERN_ERR "Sgiseeq: Cannot register net device, "
80362306a36Sopenharmony_ci		       "aborting.\n");
80462306a36Sopenharmony_ci		err = -ENODEV;
80562306a36Sopenharmony_ci		goto err_out_free_attrs;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	printk(KERN_INFO "%s: %s %pM\n", dev->name, sgiseeqstr, dev->dev_addr);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	return 0;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cierr_out_free_attrs:
81362306a36Sopenharmony_ci	dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings,
81462306a36Sopenharmony_ci		       sp->srings_dma, DMA_BIDIRECTIONAL);
81562306a36Sopenharmony_cierr_out_free_dev:
81662306a36Sopenharmony_ci	free_netdev(dev);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_cierr_out:
81962306a36Sopenharmony_ci	return err;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int sgiseeq_remove(struct platform_device *pdev)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
82562306a36Sopenharmony_ci	struct sgiseeq_private *sp = netdev_priv(dev);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	unregister_netdev(dev);
82862306a36Sopenharmony_ci	dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings,
82962306a36Sopenharmony_ci		       sp->srings_dma, DMA_BIDIRECTIONAL);
83062306a36Sopenharmony_ci	free_netdev(dev);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return 0;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic struct platform_driver sgiseeq_driver = {
83662306a36Sopenharmony_ci	.probe	= sgiseeq_probe,
83762306a36Sopenharmony_ci	.remove	= sgiseeq_remove,
83862306a36Sopenharmony_ci	.driver = {
83962306a36Sopenharmony_ci		.name	= "sgiseeq",
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci};
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cimodule_platform_driver(sgiseeq_driver);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI Seeq 8003 driver");
84662306a36Sopenharmony_ciMODULE_AUTHOR("Linux/MIPS Mailing List <linux-mips@linux-mips.org>");
84762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
84862306a36Sopenharmony_ciMODULE_ALIAS("platform:sgiseeq");
849