162306a36Sopenharmony_ci/* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci	Written 1996 by Russell Nelson, with reference to skeleton.c
462306a36Sopenharmony_ci	written 1993-1994 by Donald Becker.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci	This software may be used and distributed according to the terms
762306a36Sopenharmony_ci	of the GNU General Public License, incorporated herein by reference.
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci	The author may be reached at nelson@crynwr.com, Crynwr
1062306a36Sopenharmony_ci	Software, 11 Grant St., Potsdam, NY 13676
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci  Changelog:
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci  Mike Cruse        : mcruse@cti-ltd.com
1562306a36Sopenharmony_ci                    : Changes for Linux 2.0 compatibility.
1662306a36Sopenharmony_ci                    : Added dev_id parameter in net_interrupt(),
1762306a36Sopenharmony_ci                    : request_irq() and free_irq(). Just NULL for now.
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci  Mike Cruse        : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros
2062306a36Sopenharmony_ci                    : in net_open() and net_close() so kerneld would know
2162306a36Sopenharmony_ci                    : that the module is in use and wouldn't eject the
2262306a36Sopenharmony_ci                    : driver prematurely.
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci  Mike Cruse        : Rewrote init_module() and cleanup_module using 8390.c
2562306a36Sopenharmony_ci                    : as an example. Disabled autoprobing in init_module(),
2662306a36Sopenharmony_ci                    : not a good thing to do to other devices while Linux
2762306a36Sopenharmony_ci                    : is running from all accounts.
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci  Alan Cox          : Removed 1.2 support, added 2.1 extra counters.
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci  David Huggins-Daines <dhd@debian.org>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci  Split this off into mac89x0.c, and gutted it of all parts which are
3462306a36Sopenharmony_ci  not relevant to the existing CS8900 cards on the Macintosh
3562306a36Sopenharmony_ci  (i.e. basically the Daynaport CS and LC cards).  To be precise:
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci    * Removed all the media-detection stuff, because these cards are
3862306a36Sopenharmony_ci    TP-only.
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci    * Lobotomized the ISA interrupt bogosity, because these cards use
4162306a36Sopenharmony_ci    a hardwired NuBus interrupt and a magic ISAIRQ value in the card.
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci    * Basically eliminated everything not relevant to getting the
4462306a36Sopenharmony_ci    cards minimally functioning on the Macintosh.
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci  I might add that these cards are badly designed even from the Mac
4762306a36Sopenharmony_ci  standpoint, in that Dayna, in their infinite wisdom, used NuBus slot
4862306a36Sopenharmony_ci  I/O space and NuBus interrupts for these cards, but neglected to
4962306a36Sopenharmony_ci  provide anything even remotely resembling a NuBus ROM.  Therefore we
5062306a36Sopenharmony_ci  have to probe for them in a brain-damaged ISA-like fashion.
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci  Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
5362306a36Sopenharmony_ci  check kmalloc and release the allocated memory on failure in
5462306a36Sopenharmony_ci  mac89x0_probe and in init_module
5562306a36Sopenharmony_ci  use local_irq_{save,restore}(flags) in net_get_stat, not just
5662306a36Sopenharmony_ci  local_irq_{dis,en}able()
5762306a36Sopenharmony_ci*/
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic const char version[] =
6262306a36Sopenharmony_ci"cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#include <linux/module.h>
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci  Sources:
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	Crynwr packet driver epktisa.
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	Crystal Semiconductor data sheets.
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci*/
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#include <linux/kernel.h>
7662306a36Sopenharmony_ci#include <linux/types.h>
7762306a36Sopenharmony_ci#include <linux/fcntl.h>
7862306a36Sopenharmony_ci#include <linux/interrupt.h>
7962306a36Sopenharmony_ci#include <linux/ioport.h>
8062306a36Sopenharmony_ci#include <linux/in.h>
8162306a36Sopenharmony_ci#include <linux/string.h>
8262306a36Sopenharmony_ci#include <linux/nubus.h>
8362306a36Sopenharmony_ci#include <linux/errno.h>
8462306a36Sopenharmony_ci#include <linux/init.h>
8562306a36Sopenharmony_ci#include <linux/netdevice.h>
8662306a36Sopenharmony_ci#include <linux/platform_device.h>
8762306a36Sopenharmony_ci#include <linux/etherdevice.h>
8862306a36Sopenharmony_ci#include <linux/skbuff.h>
8962306a36Sopenharmony_ci#include <linux/delay.h>
9062306a36Sopenharmony_ci#include <linux/bitops.h>
9162306a36Sopenharmony_ci#include <linux/gfp.h>
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#include <asm/io.h>
9462306a36Sopenharmony_ci#include <asm/hwtest.h>
9562306a36Sopenharmony_ci#include <asm/macints.h>
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#include "cs89x0.h"
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int debug = -1;
10062306a36Sopenharmony_cimodule_param(debug, int, 0);
10162306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug message level");
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* Information that need to be kept for each board. */
10462306a36Sopenharmony_cistruct net_local {
10562306a36Sopenharmony_ci	int msg_enable;
10662306a36Sopenharmony_ci	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
10762306a36Sopenharmony_ci	char chip_revision;	/* revision letter of the chip ('A'...) */
10862306a36Sopenharmony_ci	int send_cmd;		/* the propercommand used to send a packet. */
10962306a36Sopenharmony_ci	int rx_mode;
11062306a36Sopenharmony_ci	int curr_rx_cfg;
11162306a36Sopenharmony_ci        int send_underrun;      /* keep track of how many underruns in a row we get */
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* Index to functions, as function prototypes. */
11562306a36Sopenharmony_cistatic int net_open(struct net_device *dev);
11662306a36Sopenharmony_cistatic netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
11762306a36Sopenharmony_cistatic irqreturn_t net_interrupt(int irq, void *dev_id);
11862306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev);
11962306a36Sopenharmony_cistatic void net_rx(struct net_device *dev);
12062306a36Sopenharmony_cistatic int net_close(struct net_device *dev);
12162306a36Sopenharmony_cistatic struct net_device_stats *net_get_stats(struct net_device *dev);
12262306a36Sopenharmony_cistatic int set_mac_address(struct net_device *dev, void *addr);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* For reading/writing registers ISA-style */
12562306a36Sopenharmony_cistatic inline int
12662306a36Sopenharmony_cireadreg_io(struct net_device *dev, int portno)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
12962306a36Sopenharmony_ci	return swab16(nubus_readw(dev->base_addr + DATA_PORT));
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic inline void
13362306a36Sopenharmony_ciwritereg_io(struct net_device *dev, int portno, int value)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	nubus_writew(swab16(portno), dev->base_addr + ADD_PORT);
13662306a36Sopenharmony_ci	nubus_writew(swab16(value), dev->base_addr + DATA_PORT);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* These are for reading/writing registers in shared memory */
14062306a36Sopenharmony_cistatic inline int
14162306a36Sopenharmony_cireadreg(struct net_device *dev, int portno)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	return swab16(nubus_readw(dev->mem_start + portno));
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline void
14762306a36Sopenharmony_ciwritereg(struct net_device *dev, int portno, int value)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	nubus_writew(swab16(value), dev->mem_start + portno);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic const struct net_device_ops mac89x0_netdev_ops = {
15362306a36Sopenharmony_ci	.ndo_open		= net_open,
15462306a36Sopenharmony_ci	.ndo_stop		= net_close,
15562306a36Sopenharmony_ci	.ndo_start_xmit		= net_send_packet,
15662306a36Sopenharmony_ci	.ndo_get_stats		= net_get_stats,
15762306a36Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
15862306a36Sopenharmony_ci	.ndo_set_mac_address	= set_mac_address,
15962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* Probe for the CS8900 card in slot E.  We won't bother looking
16362306a36Sopenharmony_ci   anywhere else until we have a really good reason to do so. */
16462306a36Sopenharmony_cistatic int mac89x0_device_probe(struct platform_device *pdev)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct net_device *dev;
16762306a36Sopenharmony_ci	struct net_local *lp;
16862306a36Sopenharmony_ci	int i, slot;
16962306a36Sopenharmony_ci	unsigned rev_type = 0;
17062306a36Sopenharmony_ci	unsigned long ioaddr;
17162306a36Sopenharmony_ci	unsigned short sig;
17262306a36Sopenharmony_ci	int err = -ENODEV;
17362306a36Sopenharmony_ci	struct nubus_rsrc *fres;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct net_local));
17662306a36Sopenharmony_ci	if (!dev)
17762306a36Sopenharmony_ci		return -ENOMEM;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* We might have to parameterize this later */
18062306a36Sopenharmony_ci	slot = 0xE;
18162306a36Sopenharmony_ci	/* Get out now if there's a real NuBus card in slot E */
18262306a36Sopenharmony_ci	for_each_func_rsrc(fres)
18362306a36Sopenharmony_ci		if (fres->board->slot == slot)
18462306a36Sopenharmony_ci			goto out;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* The pseudo-ISA bits always live at offset 0x300 (gee,
18762306a36Sopenharmony_ci           wonder why...) */
18862306a36Sopenharmony_ci	ioaddr = (unsigned long)
18962306a36Sopenharmony_ci		nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE);
19062306a36Sopenharmony_ci	{
19162306a36Sopenharmony_ci		int card_present;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		card_present = (hwreg_present((void *)ioaddr + 4) &&
19462306a36Sopenharmony_ci				hwreg_present((void *)ioaddr + DATA_PORT));
19562306a36Sopenharmony_ci		if (!card_present)
19662306a36Sopenharmony_ci			goto out;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	nubus_writew(0, ioaddr + ADD_PORT);
20062306a36Sopenharmony_ci	sig = nubus_readw(ioaddr + DATA_PORT);
20162306a36Sopenharmony_ci	if (sig != swab16(CHIP_EISA_ID_SIG))
20262306a36Sopenharmony_ci		goto out;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Initialize the net_device structure. */
20762306a36Sopenharmony_ci	lp = netdev_priv(dev);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	lp->msg_enable = netif_msg_init(debug, 0);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Fill in the 'dev' fields. */
21262306a36Sopenharmony_ci	dev->base_addr = ioaddr;
21362306a36Sopenharmony_ci	dev->mem_start = (unsigned long)
21462306a36Sopenharmony_ci		nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE);
21562306a36Sopenharmony_ci	dev->mem_end = dev->mem_start + 0x1000;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Turn on shared memory */
21862306a36Sopenharmony_ci	writereg_io(dev, PP_BusCTL, MEMORY_ON);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* get the chip type */
22162306a36Sopenharmony_ci	rev_type = readreg(dev, PRODUCT_ID_ADD);
22262306a36Sopenharmony_ci	lp->chip_type = rev_type &~ REVISON_BITS;
22362306a36Sopenharmony_ci	lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Check the chip type and revision in order to set the correct send command
22662306a36Sopenharmony_ci	CS8920 revision C and CS8900 revision F can use the faster send. */
22762306a36Sopenharmony_ci	lp->send_cmd = TX_AFTER_381;
22862306a36Sopenharmony_ci	if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
22962306a36Sopenharmony_ci		lp->send_cmd = TX_NOW;
23062306a36Sopenharmony_ci	if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
23162306a36Sopenharmony_ci		lp->send_cmd = TX_NOW;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	netif_dbg(lp, drv, dev, "%s", version);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	pr_info("cs89%c0%s rev %c found at %#8lx\n",
23662306a36Sopenharmony_ci		lp->chip_type == CS8900 ? '0' : '2',
23762306a36Sopenharmony_ci		lp->chip_type == CS8920M ? "M" : "",
23862306a36Sopenharmony_ci		lp->chip_revision, dev->base_addr);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Try to read the MAC address */
24162306a36Sopenharmony_ci	if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) {
24262306a36Sopenharmony_ci		pr_info("No EEPROM, giving up now.\n");
24362306a36Sopenharmony_ci		goto out1;
24462306a36Sopenharmony_ci        } else {
24562306a36Sopenharmony_ci		u8 addr[ETH_ALEN];
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci                for (i = 0; i < ETH_ALEN; i += 2) {
24862306a36Sopenharmony_ci			/* Big-endian (why??!) */
24962306a36Sopenharmony_ci			unsigned short s = readreg(dev, PP_IA + i);
25062306a36Sopenharmony_ci			addr[i] = s >> 8;
25162306a36Sopenharmony_ci			addr[i+1] = s & 0xff;
25262306a36Sopenharmony_ci                }
25362306a36Sopenharmony_ci		eth_hw_addr_set(dev, addr);
25462306a36Sopenharmony_ci        }
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	dev->irq = SLOT2IRQ(slot);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* print the IRQ and ethernet address. */
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	pr_info("MAC %pM, IRQ %d\n", dev->dev_addr, dev->irq);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	dev->netdev_ops		= &mac89x0_netdev_ops;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	err = register_netdev(dev);
26562306a36Sopenharmony_ci	if (err)
26662306a36Sopenharmony_ci		goto out1;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ciout1:
27162306a36Sopenharmony_ci	nubus_writew(0, dev->base_addr + ADD_PORT);
27262306a36Sopenharmony_ciout:
27362306a36Sopenharmony_ci	free_netdev(dev);
27462306a36Sopenharmony_ci	return err;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/* Open/initialize the board.  This is called (in the current kernel)
27862306a36Sopenharmony_ci   sometime after booting when the 'ifconfig' program is run.
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci   This routine should set everything up anew at each open, even
28162306a36Sopenharmony_ci   registers that "should" only need to be set once at boot, so that
28262306a36Sopenharmony_ci   there is non-reboot way to recover if something goes wrong.
28362306a36Sopenharmony_ci   */
28462306a36Sopenharmony_cistatic int
28562306a36Sopenharmony_cinet_open(struct net_device *dev)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
28862306a36Sopenharmony_ci	int i;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Disable the interrupt for now */
29162306a36Sopenharmony_ci	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Grab the interrupt */
29462306a36Sopenharmony_ci	if (request_irq(dev->irq, net_interrupt, 0, "cs89x0", dev))
29562306a36Sopenharmony_ci		return -EAGAIN;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* Set up the IRQ - Apparently magic */
29862306a36Sopenharmony_ci	if (lp->chip_type == CS8900)
29962306a36Sopenharmony_ci		writereg(dev, PP_CS8900_ISAINT, 0);
30062306a36Sopenharmony_ci	else
30162306a36Sopenharmony_ci		writereg(dev, PP_CS8920_ISAINT, 0);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* set the Ethernet address */
30462306a36Sopenharmony_ci	for (i=0; i < ETH_ALEN/2; i++)
30562306a36Sopenharmony_ci		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* Turn on both receive and transmit operations */
30862306a36Sopenharmony_ci	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Receive only error free packets addressed to this card */
31162306a36Sopenharmony_ci	lp->rx_mode = 0;
31262306a36Sopenharmony_ci	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
31962306a36Sopenharmony_ci	       TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
32262306a36Sopenharmony_ci		 TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* now that we've got our act together, enable everything */
32562306a36Sopenharmony_ci	writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
32662306a36Sopenharmony_ci	netif_start_queue(dev);
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic netdev_tx_t
33162306a36Sopenharmony_cinet_send_packet(struct sk_buff *skb, struct net_device *dev)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
33462306a36Sopenharmony_ci	unsigned long flags;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	netif_dbg(lp, tx_queued, dev, "sent %d byte packet of type %x\n",
33762306a36Sopenharmony_ci		  skb->len, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
33862306a36Sopenharmony_ci		  skb->data[ETH_ALEN + ETH_ALEN + 1]);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* keep the upload from being interrupted, since we
34162306a36Sopenharmony_ci	   ask the chip to start transmitting before the
34262306a36Sopenharmony_ci	   whole packet has been completely uploaded. */
34362306a36Sopenharmony_ci	local_irq_save(flags);
34462306a36Sopenharmony_ci	netif_stop_queue(dev);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* initiate a transmit sequence */
34762306a36Sopenharmony_ci	writereg(dev, PP_TxCMD, lp->send_cmd);
34862306a36Sopenharmony_ci	writereg(dev, PP_TxLength, skb->len);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Test to see if the chip has allocated memory for the packet */
35162306a36Sopenharmony_ci	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
35262306a36Sopenharmony_ci		/* Gasp!  It hasn't.  But that shouldn't happen since
35362306a36Sopenharmony_ci		   we're waiting for TxOk, so return 1 and requeue this packet. */
35462306a36Sopenharmony_ci		local_irq_restore(flags);
35562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Write the contents of the packet */
35962306a36Sopenharmony_ci	skb_copy_from_linear_data(skb, (void *)(dev->mem_start + PP_TxFrame),
36062306a36Sopenharmony_ci				  skb->len+1);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	local_irq_restore(flags);
36362306a36Sopenharmony_ci	dev_kfree_skb (skb);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return NETDEV_TX_OK;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* The typical workload of the driver:
36962306a36Sopenharmony_ci   Handle the network interface interrupts. */
37062306a36Sopenharmony_cistatic irqreturn_t net_interrupt(int irq, void *dev_id)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct net_device *dev = dev_id;
37362306a36Sopenharmony_ci	struct net_local *lp;
37462306a36Sopenharmony_ci	int ioaddr, status;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ioaddr = dev->base_addr;
37762306a36Sopenharmony_ci	lp = netdev_priv(dev);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* we MUST read all the events out of the ISQ, otherwise we'll never
38062306a36Sopenharmony_ci           get interrupted again.  As a consequence, we can't have any limit
38162306a36Sopenharmony_ci           on the number of times we loop in the interrupt handler.  The
38262306a36Sopenharmony_ci           hardware guarantees that eventually we'll run out of events.  Of
38362306a36Sopenharmony_ci           course, if you're on a slow machine, and packets are arriving
38462306a36Sopenharmony_ci           faster than you can read them off, you're screwed.  Hasta la
38562306a36Sopenharmony_ci           vista, baby!  */
38662306a36Sopenharmony_ci	while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) {
38762306a36Sopenharmony_ci		netif_dbg(lp, intr, dev, "status=%04x\n", status);
38862306a36Sopenharmony_ci		switch(status & ISQ_EVENT_MASK) {
38962306a36Sopenharmony_ci		case ISQ_RECEIVER_EVENT:
39062306a36Sopenharmony_ci			/* Got a packet(s). */
39162306a36Sopenharmony_ci			net_rx(dev);
39262306a36Sopenharmony_ci			break;
39362306a36Sopenharmony_ci		case ISQ_TRANSMITTER_EVENT:
39462306a36Sopenharmony_ci			dev->stats.tx_packets++;
39562306a36Sopenharmony_ci			netif_wake_queue(dev);
39662306a36Sopenharmony_ci			if ((status & TX_OK) == 0)
39762306a36Sopenharmony_ci				dev->stats.tx_errors++;
39862306a36Sopenharmony_ci			if (status & TX_LOST_CRS)
39962306a36Sopenharmony_ci				dev->stats.tx_carrier_errors++;
40062306a36Sopenharmony_ci			if (status & TX_SQE_ERROR)
40162306a36Sopenharmony_ci				dev->stats.tx_heartbeat_errors++;
40262306a36Sopenharmony_ci			if (status & TX_LATE_COL)
40362306a36Sopenharmony_ci				dev->stats.tx_window_errors++;
40462306a36Sopenharmony_ci			if (status & TX_16_COL)
40562306a36Sopenharmony_ci				dev->stats.tx_aborted_errors++;
40662306a36Sopenharmony_ci			break;
40762306a36Sopenharmony_ci		case ISQ_BUFFER_EVENT:
40862306a36Sopenharmony_ci			if (status & READY_FOR_TX) {
40962306a36Sopenharmony_ci				/* we tried to transmit a packet earlier,
41062306a36Sopenharmony_ci                                   but inexplicably ran out of buffers.
41162306a36Sopenharmony_ci                                   That shouldn't happen since we only ever
41262306a36Sopenharmony_ci                                   load one packet.  Shrug.  Do the right
41362306a36Sopenharmony_ci                                   thing anyway. */
41462306a36Sopenharmony_ci				netif_wake_queue(dev);
41562306a36Sopenharmony_ci			}
41662306a36Sopenharmony_ci			if (status & TX_UNDERRUN) {
41762306a36Sopenharmony_ci				netif_dbg(lp, tx_err, dev, "transmit underrun\n");
41862306a36Sopenharmony_ci                                lp->send_underrun++;
41962306a36Sopenharmony_ci                                if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381;
42062306a36Sopenharmony_ci                                else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
42162306a36Sopenharmony_ci                        }
42262306a36Sopenharmony_ci			break;
42362306a36Sopenharmony_ci		case ISQ_RX_MISS_EVENT:
42462306a36Sopenharmony_ci			dev->stats.rx_missed_errors += (status >> 6);
42562306a36Sopenharmony_ci			break;
42662306a36Sopenharmony_ci		case ISQ_TX_COL_EVENT:
42762306a36Sopenharmony_ci			dev->stats.collisions += (status >> 6);
42862306a36Sopenharmony_ci			break;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci	return IRQ_HANDLED;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* We have a good packet(s), get it/them out of the buffers. */
43562306a36Sopenharmony_cistatic void
43662306a36Sopenharmony_cinet_rx(struct net_device *dev)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
43962306a36Sopenharmony_ci	struct sk_buff *skb;
44062306a36Sopenharmony_ci	int status, length;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	status = readreg(dev, PP_RxStatus);
44362306a36Sopenharmony_ci	if ((status & RX_OK) == 0) {
44462306a36Sopenharmony_ci		dev->stats.rx_errors++;
44562306a36Sopenharmony_ci		if (status & RX_RUNT)
44662306a36Sopenharmony_ci				dev->stats.rx_length_errors++;
44762306a36Sopenharmony_ci		if (status & RX_EXTRA_DATA)
44862306a36Sopenharmony_ci				dev->stats.rx_length_errors++;
44962306a36Sopenharmony_ci		if ((status & RX_CRC_ERROR) &&
45062306a36Sopenharmony_ci		    !(status & (RX_EXTRA_DATA|RX_RUNT)))
45162306a36Sopenharmony_ci			/* per str 172 */
45262306a36Sopenharmony_ci			dev->stats.rx_crc_errors++;
45362306a36Sopenharmony_ci		if (status & RX_DRIBBLE)
45462306a36Sopenharmony_ci				dev->stats.rx_frame_errors++;
45562306a36Sopenharmony_ci		return;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	length = readreg(dev, PP_RxLength);
45962306a36Sopenharmony_ci	/* Malloc up new buffer. */
46062306a36Sopenharmony_ci	skb = alloc_skb(length, GFP_ATOMIC);
46162306a36Sopenharmony_ci	if (skb == NULL) {
46262306a36Sopenharmony_ci		dev->stats.rx_dropped++;
46362306a36Sopenharmony_ci		return;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci	skb_put(skb, length);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	skb_copy_to_linear_data(skb, (void *)(dev->mem_start + PP_RxFrame),
46862306a36Sopenharmony_ci				length);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	netif_dbg(lp, rx_status, dev, "received %d byte packet of type %x\n",
47162306a36Sopenharmony_ci		  length, skb->data[ETH_ALEN + ETH_ALEN] << 8 |
47262306a36Sopenharmony_ci		  skb->data[ETH_ALEN + ETH_ALEN + 1]);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci        skb->protocol=eth_type_trans(skb,dev);
47562306a36Sopenharmony_ci	netif_rx(skb);
47662306a36Sopenharmony_ci	dev->stats.rx_packets++;
47762306a36Sopenharmony_ci	dev->stats.rx_bytes += length;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/* The inverse routine to net_open(). */
48162306a36Sopenharmony_cistatic int
48262306a36Sopenharmony_cinet_close(struct net_device *dev)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	writereg(dev, PP_RxCFG, 0);
48662306a36Sopenharmony_ci	writereg(dev, PP_TxCFG, 0);
48762306a36Sopenharmony_ci	writereg(dev, PP_BufCFG, 0);
48862306a36Sopenharmony_ci	writereg(dev, PP_BusCTL, 0);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	netif_stop_queue(dev);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	free_irq(dev->irq, dev);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* Update the statistics here. */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/* Get the current statistics.	This may be called with the card open or
50162306a36Sopenharmony_ci   closed. */
50262306a36Sopenharmony_cistatic struct net_device_stats *
50362306a36Sopenharmony_cinet_get_stats(struct net_device *dev)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	unsigned long flags;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	local_irq_save(flags);
50862306a36Sopenharmony_ci	/* Update the statistics from the device registers. */
50962306a36Sopenharmony_ci	dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
51062306a36Sopenharmony_ci	dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
51162306a36Sopenharmony_ci	local_irq_restore(flags);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return &dev->stats;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if(dev->flags&IFF_PROMISC)
52162306a36Sopenharmony_ci	{
52262306a36Sopenharmony_ci		lp->rx_mode = RX_ALL_ACCEPT;
52362306a36Sopenharmony_ci	} else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) {
52462306a36Sopenharmony_ci		/* The multicast-accept list is initialized to accept-all, and we
52562306a36Sopenharmony_ci		   rely on higher-level filtering for now. */
52662306a36Sopenharmony_ci		lp->rx_mode = RX_MULTCAST_ACCEPT;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	else
52962306a36Sopenharmony_ci		lp->rx_mode = 0;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
53462306a36Sopenharmony_ci	writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
53562306a36Sopenharmony_ci	     (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int set_mac_address(struct net_device *dev, void *addr)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct sockaddr *saddr = addr;
54262306a36Sopenharmony_ci	int i;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (!is_valid_ether_addr(saddr->sa_data))
54562306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	eth_hw_addr_set(dev, saddr->sa_data);
54862306a36Sopenharmony_ci	netdev_info(dev, "Setting MAC address to %pM\n", dev->dev_addr);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* set the Ethernet address */
55162306a36Sopenharmony_ci	for (i=0; i < ETH_ALEN/2; i++)
55262306a36Sopenharmony_ci		writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return 0;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int mac89x0_device_remove(struct platform_device *pdev)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	unregister_netdev(dev);
56462306a36Sopenharmony_ci	nubus_writew(0, dev->base_addr + ADD_PORT);
56562306a36Sopenharmony_ci	free_netdev(dev);
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic struct platform_driver mac89x0_platform_driver = {
57062306a36Sopenharmony_ci	.probe = mac89x0_device_probe,
57162306a36Sopenharmony_ci	.remove = mac89x0_device_remove,
57262306a36Sopenharmony_ci	.driver = {
57362306a36Sopenharmony_ci		.name = "mac89x0",
57462306a36Sopenharmony_ci	},
57562306a36Sopenharmony_ci};
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cimodule_platform_driver(mac89x0_platform_driver);
578