162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Driver for the Macintosh 68K onboard MACE controller with PSC
462306a36Sopenharmony_ci *	driven DMA. The MACE driver code is derived from mace.c. The
562306a36Sopenharmony_ci *	Mac68k theory of operation is courtesy of the MacBSD wizards.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *	Copyright (C) 1996 Paul Mackerras.
862306a36Sopenharmony_ci *	Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *	Modified heavily by Joshua M. Thompson based on Dave Huang's NetBSD driver
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *	Copyright (C) 2007 Finn Thain
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	Converted to DMA API, converted to unified driver model,
1562306a36Sopenharmony_ci *	sync'd some routines with mace.c and fixed various bugs.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/netdevice.h>
2262306a36Sopenharmony_ci#include <linux/etherdevice.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/string.h>
2562306a36Sopenharmony_ci#include <linux/crc32.h>
2662306a36Sopenharmony_ci#include <linux/bitrev.h>
2762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2862306a36Sopenharmony_ci#include <linux/platform_device.h>
2962306a36Sopenharmony_ci#include <linux/gfp.h>
3062306a36Sopenharmony_ci#include <linux/interrupt.h>
3162306a36Sopenharmony_ci#include <asm/io.h>
3262306a36Sopenharmony_ci#include <asm/macints.h>
3362306a36Sopenharmony_ci#include <asm/mac_psc.h>
3462306a36Sopenharmony_ci#include <asm/page.h>
3562306a36Sopenharmony_ci#include "mace.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic char mac_mace_string[] = "macmace";
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define N_TX_BUFF_ORDER	0
4062306a36Sopenharmony_ci#define N_TX_RING	(1 << N_TX_BUFF_ORDER)
4162306a36Sopenharmony_ci#define N_RX_BUFF_ORDER	3
4262306a36Sopenharmony_ci#define N_RX_RING	(1 << N_RX_BUFF_ORDER)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define TX_TIMEOUT	HZ
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define MACE_BUFF_SIZE	0x800
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Chip rev needs workaround on HW & multicast addr change */
4962306a36Sopenharmony_ci#define BROKEN_ADDRCHG_REV	0x0941
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* The MACE is simply wired down on a Mac68K box */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define MACE_BASE	(void *)(0x50F1C000)
5462306a36Sopenharmony_ci#define MACE_PROM	(void *)(0x50F08001)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct mace_data {
5762306a36Sopenharmony_ci	volatile struct mace *mace;
5862306a36Sopenharmony_ci	unsigned char *tx_ring;
5962306a36Sopenharmony_ci	dma_addr_t tx_ring_phys;
6062306a36Sopenharmony_ci	unsigned char *rx_ring;
6162306a36Sopenharmony_ci	dma_addr_t rx_ring_phys;
6262306a36Sopenharmony_ci	int dma_intr;
6362306a36Sopenharmony_ci	int rx_slot, rx_tail;
6462306a36Sopenharmony_ci	int tx_slot, tx_sloti, tx_count;
6562306a36Sopenharmony_ci	int chipid;
6662306a36Sopenharmony_ci	struct device *device;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistruct mace_frame {
7062306a36Sopenharmony_ci	u8	rcvcnt;
7162306a36Sopenharmony_ci	u8	pad1;
7262306a36Sopenharmony_ci	u8	rcvsts;
7362306a36Sopenharmony_ci	u8	pad2;
7462306a36Sopenharmony_ci	u8	rntpc;
7562306a36Sopenharmony_ci	u8	pad3;
7662306a36Sopenharmony_ci	u8	rcvcc;
7762306a36Sopenharmony_ci	u8	pad4;
7862306a36Sopenharmony_ci	u32	pad5;
7962306a36Sopenharmony_ci	u32	pad6;
8062306a36Sopenharmony_ci	DECLARE_FLEX_ARRAY(u8, data);
8162306a36Sopenharmony_ci	/* And frame continues.. */
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define PRIV_BYTES	sizeof(struct mace_data)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int mace_open(struct net_device *dev);
8762306a36Sopenharmony_cistatic int mace_close(struct net_device *dev);
8862306a36Sopenharmony_cistatic netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev);
8962306a36Sopenharmony_cistatic void mace_set_multicast(struct net_device *dev);
9062306a36Sopenharmony_cistatic int mace_set_address(struct net_device *dev, void *addr);
9162306a36Sopenharmony_cistatic void mace_reset(struct net_device *dev);
9262306a36Sopenharmony_cistatic irqreturn_t mace_interrupt(int irq, void *dev_id);
9362306a36Sopenharmony_cistatic irqreturn_t mace_dma_intr(int irq, void *dev_id);
9462306a36Sopenharmony_cistatic void mace_tx_timeout(struct net_device *dev, unsigned int txqueue);
9562306a36Sopenharmony_cistatic void __mace_set_address(struct net_device *dev, const void *addr);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * Load a receive DMA channel with a base address and ring length
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void mace_load_rxdma_base(struct net_device *dev, int set)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CMD + set, 0x0100);
10662306a36Sopenharmony_ci	psc_write_long(PSC_ENETRD_ADDR + set, (u32) mp->rx_ring_phys);
10762306a36Sopenharmony_ci	psc_write_long(PSC_ENETRD_LEN + set, N_RX_RING);
10862306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CMD + set, 0x9800);
10962306a36Sopenharmony_ci	mp->rx_tail = 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * Reset the receive DMA subsystem
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void mace_rxdma_reset(struct net_device *dev)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
11962306a36Sopenharmony_ci	volatile struct mace *mace = mp->mace;
12062306a36Sopenharmony_ci	u8 maccc = mace->maccc;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	mace->maccc = maccc & ~ENRCV;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x8800);
12562306a36Sopenharmony_ci	mace_load_rxdma_base(dev, 0x00);
12662306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x0400);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x8800);
12962306a36Sopenharmony_ci	mace_load_rxdma_base(dev, 0x10);
13062306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x0400);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	mace->maccc = maccc;
13362306a36Sopenharmony_ci	mp->rx_slot = 0;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CMD + PSC_SET0, 0x9800);
13662306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CMD + PSC_SET1, 0x9800);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * Reset the transmit DMA subsystem
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void mace_txdma_reset(struct net_device *dev)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
14662306a36Sopenharmony_ci	volatile struct mace *mace = mp->mace;
14762306a36Sopenharmony_ci	u8 maccc;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CTL, 0x8800);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	maccc = mace->maccc;
15262306a36Sopenharmony_ci	mace->maccc = maccc & ~ENXMT;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	mp->tx_slot = mp->tx_sloti = 0;
15562306a36Sopenharmony_ci	mp->tx_count = N_TX_RING;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CTL, 0x0400);
15862306a36Sopenharmony_ci	mace->maccc = maccc;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * Disable DMA
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void mace_dma_off(struct net_device *dev)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x8800);
16862306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x1000);
16962306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CMD + PSC_SET0, 0x1100);
17062306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CMD + PSC_SET1, 0x1100);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CTL, 0x8800);
17362306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CTL, 0x1000);
17462306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CMD + PSC_SET0, 0x1100);
17562306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CMD + PSC_SET1, 0x1100);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic const struct net_device_ops mace_netdev_ops = {
17962306a36Sopenharmony_ci	.ndo_open		= mace_open,
18062306a36Sopenharmony_ci	.ndo_stop		= mace_close,
18162306a36Sopenharmony_ci	.ndo_start_xmit		= mace_xmit_start,
18262306a36Sopenharmony_ci	.ndo_tx_timeout		= mace_tx_timeout,
18362306a36Sopenharmony_ci	.ndo_set_rx_mode	= mace_set_multicast,
18462306a36Sopenharmony_ci	.ndo_set_mac_address	= mace_set_address,
18562306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Not really much of a probe. The hardware table tells us if this
19062306a36Sopenharmony_ci * model of Macintrash has a MACE (AV macintoshes)
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int mace_probe(struct platform_device *pdev)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int j;
19662306a36Sopenharmony_ci	struct mace_data *mp;
19762306a36Sopenharmony_ci	unsigned char *addr;
19862306a36Sopenharmony_ci	struct net_device *dev;
19962306a36Sopenharmony_ci	unsigned char checksum = 0;
20062306a36Sopenharmony_ci	u8 macaddr[ETH_ALEN];
20162306a36Sopenharmony_ci	int err;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	dev = alloc_etherdev(PRIV_BYTES);
20462306a36Sopenharmony_ci	if (!dev)
20562306a36Sopenharmony_ci		return -ENOMEM;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	mp = netdev_priv(dev);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	mp->device = &pdev->dev;
21062306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
21162306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	dev->base_addr = (u32)MACE_BASE;
21462306a36Sopenharmony_ci	mp->mace = MACE_BASE;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	dev->irq = IRQ_MAC_MACE;
21762306a36Sopenharmony_ci	mp->dma_intr = IRQ_MAC_MACE_DMA;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	mp->chipid = mp->mace->chipid_hi << 8 | mp->mace->chipid_lo;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/*
22262306a36Sopenharmony_ci	 * The PROM contains 8 bytes which total 0xFF when XOR'd
22362306a36Sopenharmony_ci	 * together. Due to the usual peculiar apple brain damage
22462306a36Sopenharmony_ci	 * the bytes are spaced out in a strange boundary and the
22562306a36Sopenharmony_ci	 * bits are reversed.
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	addr = MACE_PROM;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	for (j = 0; j < 6; ++j) {
23162306a36Sopenharmony_ci		u8 v = bitrev8(addr[j<<4]);
23262306a36Sopenharmony_ci		checksum ^= v;
23362306a36Sopenharmony_ci		macaddr[j] = v;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	eth_hw_addr_set(dev, macaddr);
23662306a36Sopenharmony_ci	for (; j < 8; ++j) {
23762306a36Sopenharmony_ci		checksum ^= bitrev8(addr[j<<4]);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (checksum != 0xFF) {
24162306a36Sopenharmony_ci		free_netdev(dev);
24262306a36Sopenharmony_ci		return -ENODEV;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dev->netdev_ops		= &mace_netdev_ops;
24662306a36Sopenharmony_ci	dev->watchdog_timeo	= TX_TIMEOUT;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	pr_info("Onboard MACE, hardware address %pM, chip revision 0x%04X\n",
24962306a36Sopenharmony_ci		dev->dev_addr, mp->chipid);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	err = register_netdev(dev);
25262306a36Sopenharmony_ci	if (!err)
25362306a36Sopenharmony_ci		return 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	free_netdev(dev);
25662306a36Sopenharmony_ci	return err;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * Reset the chip.
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void mace_reset(struct net_device *dev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
26662306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
26762306a36Sopenharmony_ci	int i;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* soft-reset the chip */
27062306a36Sopenharmony_ci	i = 200;
27162306a36Sopenharmony_ci	while (--i) {
27262306a36Sopenharmony_ci		mb->biucc = SWRST;
27362306a36Sopenharmony_ci		if (mb->biucc & SWRST) {
27462306a36Sopenharmony_ci			udelay(10);
27562306a36Sopenharmony_ci			continue;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci		break;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	if (!i) {
28062306a36Sopenharmony_ci		printk(KERN_ERR "macmace: cannot reset chip!\n");
28162306a36Sopenharmony_ci		return;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	mb->maccc = 0;	/* turn off tx, rx */
28562306a36Sopenharmony_ci	mb->imr = 0xFF;	/* disable all intrs for now */
28662306a36Sopenharmony_ci	i = mb->ir;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	mb->biucc = XMTSP_64;
28962306a36Sopenharmony_ci	mb->utr = RTRD;
29062306a36Sopenharmony_ci	mb->fifocc = XMTFW_8 | RCVFW_64 | XMTFWU | RCVFWU;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */
29362306a36Sopenharmony_ci	mb->rcvfc = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* load up the hardware address */
29662306a36Sopenharmony_ci	__mace_set_address(dev, dev->dev_addr);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* clear the multicast filter */
29962306a36Sopenharmony_ci	if (mp->chipid == BROKEN_ADDRCHG_REV)
30062306a36Sopenharmony_ci		mb->iac = LOGADDR;
30162306a36Sopenharmony_ci	else {
30262306a36Sopenharmony_ci		mb->iac = ADDRCHG | LOGADDR;
30362306a36Sopenharmony_ci		while ((mb->iac & ADDRCHG) != 0)
30462306a36Sopenharmony_ci			;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	for (i = 0; i < 8; ++i)
30762306a36Sopenharmony_ci		mb->ladrf = 0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* done changing address */
31062306a36Sopenharmony_ci	if (mp->chipid != BROKEN_ADDRCHG_REV)
31162306a36Sopenharmony_ci		mb->iac = 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	mb->plscc = PORTSEL_AUI;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/*
31762306a36Sopenharmony_ci * Load the address on a mace controller.
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic void __mace_set_address(struct net_device *dev, const void *addr)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
32362306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
32462306a36Sopenharmony_ci	const unsigned char *p = addr;
32562306a36Sopenharmony_ci	u8 macaddr[ETH_ALEN];
32662306a36Sopenharmony_ci	int i;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* load up the hardware address */
32962306a36Sopenharmony_ci	if (mp->chipid == BROKEN_ADDRCHG_REV)
33062306a36Sopenharmony_ci		mb->iac = PHYADDR;
33162306a36Sopenharmony_ci	else {
33262306a36Sopenharmony_ci		mb->iac = ADDRCHG | PHYADDR;
33362306a36Sopenharmony_ci		while ((mb->iac & ADDRCHG) != 0)
33462306a36Sopenharmony_ci			;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	for (i = 0; i < 6; ++i)
33762306a36Sopenharmony_ci		mb->padr = macaddr[i] = p[i];
33862306a36Sopenharmony_ci	eth_hw_addr_set(dev, macaddr);
33962306a36Sopenharmony_ci	if (mp->chipid != BROKEN_ADDRCHG_REV)
34062306a36Sopenharmony_ci		mb->iac = 0;
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int mace_set_address(struct net_device *dev, void *addr)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
34662306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
34762306a36Sopenharmony_ci	unsigned long flags;
34862306a36Sopenharmony_ci	u8 maccc;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	local_irq_save(flags);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	maccc = mb->maccc;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	__mace_set_address(dev, addr);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mb->maccc = maccc;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	local_irq_restore(flags);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/*
36462306a36Sopenharmony_ci * Open the Macintosh MACE. Most of this is playing with the DMA
36562306a36Sopenharmony_ci * engine. The ethernet chip is quite friendly.
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int mace_open(struct net_device *dev)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
37162306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* reset the chip */
37462306a36Sopenharmony_ci	mace_reset(dev);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (request_irq(dev->irq, mace_interrupt, 0, dev->name, dev)) {
37762306a36Sopenharmony_ci		printk(KERN_ERR "%s: can't get irq %d\n", dev->name, dev->irq);
37862306a36Sopenharmony_ci		return -EAGAIN;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	if (request_irq(mp->dma_intr, mace_dma_intr, 0, dev->name, dev)) {
38162306a36Sopenharmony_ci		printk(KERN_ERR "%s: can't get irq %d\n", dev->name, mp->dma_intr);
38262306a36Sopenharmony_ci		free_irq(dev->irq, dev);
38362306a36Sopenharmony_ci		return -EAGAIN;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Allocate the DMA ring buffers */
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	mp->tx_ring = dma_alloc_coherent(mp->device,
38962306a36Sopenharmony_ci					 N_TX_RING * MACE_BUFF_SIZE,
39062306a36Sopenharmony_ci					 &mp->tx_ring_phys, GFP_KERNEL);
39162306a36Sopenharmony_ci	if (mp->tx_ring == NULL)
39262306a36Sopenharmony_ci		goto out1;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	mp->rx_ring = dma_alloc_coherent(mp->device,
39562306a36Sopenharmony_ci					 N_RX_RING * MACE_BUFF_SIZE,
39662306a36Sopenharmony_ci					 &mp->rx_ring_phys, GFP_KERNEL);
39762306a36Sopenharmony_ci	if (mp->rx_ring == NULL)
39862306a36Sopenharmony_ci		goto out2;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	mace_dma_off(dev);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* Not sure what these do */
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CTL, 0x9000);
40562306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x9000);
40662306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CTL, 0x0400);
40762306a36Sopenharmony_ci	psc_write_word(PSC_ENETRD_CTL, 0x0400);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	mace_rxdma_reset(dev);
41062306a36Sopenharmony_ci	mace_txdma_reset(dev);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* turn it on! */
41362306a36Sopenharmony_ci	mb->maccc = ENXMT | ENRCV;
41462306a36Sopenharmony_ci	/* enable all interrupts except receive interrupts */
41562306a36Sopenharmony_ci	mb->imr = RCVINT;
41662306a36Sopenharmony_ci	return 0;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ciout2:
41962306a36Sopenharmony_ci	dma_free_coherent(mp->device, N_TX_RING * MACE_BUFF_SIZE,
42062306a36Sopenharmony_ci	                  mp->tx_ring, mp->tx_ring_phys);
42162306a36Sopenharmony_ciout1:
42262306a36Sopenharmony_ci	free_irq(dev->irq, dev);
42362306a36Sopenharmony_ci	free_irq(mp->dma_intr, dev);
42462306a36Sopenharmony_ci	return -ENOMEM;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci/*
42862306a36Sopenharmony_ci * Shut down the mace and its interrupt channel
42962306a36Sopenharmony_ci */
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int mace_close(struct net_device *dev)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
43462306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	mb->maccc = 0;		/* disable rx and tx	 */
43762306a36Sopenharmony_ci	mb->imr = 0xFF;		/* disable all irqs	 */
43862306a36Sopenharmony_ci	mace_dma_off(dev);	/* disable rx and tx dma */
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci/*
44462306a36Sopenharmony_ci * Transmit a frame
44562306a36Sopenharmony_ci */
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
45062306a36Sopenharmony_ci	unsigned long flags;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* Stop the queue since there's only the one buffer */
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	local_irq_save(flags);
45562306a36Sopenharmony_ci	netif_stop_queue(dev);
45662306a36Sopenharmony_ci	if (!mp->tx_count) {
45762306a36Sopenharmony_ci		printk(KERN_ERR "macmace: tx queue running but no free buffers.\n");
45862306a36Sopenharmony_ci		local_irq_restore(flags);
45962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	mp->tx_count--;
46262306a36Sopenharmony_ci	local_irq_restore(flags);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	dev->stats.tx_packets++;
46562306a36Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* We need to copy into our xmit buffer to take care of alignment and caching issues */
46862306a36Sopenharmony_ci	skb_copy_from_linear_data(skb, mp->tx_ring, skb->len);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* load the Tx DMA and fire it off */
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	psc_write_long(PSC_ENETWR_ADDR + mp->tx_slot, (u32)  mp->tx_ring_phys);
47362306a36Sopenharmony_ci	psc_write_long(PSC_ENETWR_LEN + mp->tx_slot, skb->len);
47462306a36Sopenharmony_ci	psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x9800);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	mp->tx_slot ^= 0x10;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	dev_kfree_skb(skb);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return NETDEV_TX_OK;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void mace_set_multicast(struct net_device *dev)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
48662306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
48762306a36Sopenharmony_ci	int i;
48862306a36Sopenharmony_ci	u32 crc;
48962306a36Sopenharmony_ci	u8 maccc;
49062306a36Sopenharmony_ci	unsigned long flags;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	local_irq_save(flags);
49362306a36Sopenharmony_ci	maccc = mb->maccc;
49462306a36Sopenharmony_ci	mb->maccc &= ~PROM;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
49762306a36Sopenharmony_ci		mb->maccc |= PROM;
49862306a36Sopenharmony_ci	} else {
49962306a36Sopenharmony_ci		unsigned char multicast_filter[8];
50062306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		if (dev->flags & IFF_ALLMULTI) {
50362306a36Sopenharmony_ci			for (i = 0; i < 8; i++) {
50462306a36Sopenharmony_ci				multicast_filter[i] = 0xFF;
50562306a36Sopenharmony_ci			}
50662306a36Sopenharmony_ci		} else {
50762306a36Sopenharmony_ci			for (i = 0; i < 8; i++)
50862306a36Sopenharmony_ci				multicast_filter[i] = 0;
50962306a36Sopenharmony_ci			netdev_for_each_mc_addr(ha, dev) {
51062306a36Sopenharmony_ci				crc = ether_crc_le(6, ha->addr);
51162306a36Sopenharmony_ci				/* bit number in multicast_filter */
51262306a36Sopenharmony_ci				i = crc >> 26;
51362306a36Sopenharmony_ci				multicast_filter[i >> 3] |= 1 << (i & 7);
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		if (mp->chipid == BROKEN_ADDRCHG_REV)
51862306a36Sopenharmony_ci			mb->iac = LOGADDR;
51962306a36Sopenharmony_ci		else {
52062306a36Sopenharmony_ci			mb->iac = ADDRCHG | LOGADDR;
52162306a36Sopenharmony_ci			while ((mb->iac & ADDRCHG) != 0)
52262306a36Sopenharmony_ci				;
52362306a36Sopenharmony_ci		}
52462306a36Sopenharmony_ci		for (i = 0; i < 8; ++i)
52562306a36Sopenharmony_ci			mb->ladrf = multicast_filter[i];
52662306a36Sopenharmony_ci		if (mp->chipid != BROKEN_ADDRCHG_REV)
52762306a36Sopenharmony_ci			mb->iac = 0;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	mb->maccc = maccc;
53162306a36Sopenharmony_ci	local_irq_restore(flags);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic void mace_handle_misc_intrs(struct net_device *dev, int intr)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
53762306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
53862306a36Sopenharmony_ci	static int mace_babbles, mace_jabbers;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (intr & MPCO)
54162306a36Sopenharmony_ci		dev->stats.rx_missed_errors += 256;
54262306a36Sopenharmony_ci	dev->stats.rx_missed_errors += mb->mpc;   /* reading clears it */
54362306a36Sopenharmony_ci	if (intr & RNTPCO)
54462306a36Sopenharmony_ci		dev->stats.rx_length_errors += 256;
54562306a36Sopenharmony_ci	dev->stats.rx_length_errors += mb->rntpc; /* reading clears it */
54662306a36Sopenharmony_ci	if (intr & CERR)
54762306a36Sopenharmony_ci		++dev->stats.tx_heartbeat_errors;
54862306a36Sopenharmony_ci	if (intr & BABBLE)
54962306a36Sopenharmony_ci		if (mace_babbles++ < 4)
55062306a36Sopenharmony_ci			printk(KERN_DEBUG "macmace: babbling transmitter\n");
55162306a36Sopenharmony_ci	if (intr & JABBER)
55262306a36Sopenharmony_ci		if (mace_jabbers++ < 4)
55362306a36Sopenharmony_ci			printk(KERN_DEBUG "macmace: jabbering transceiver\n");
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic irqreturn_t mace_interrupt(int irq, void *dev_id)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) dev_id;
55962306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
56062306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
56162306a36Sopenharmony_ci	int intr, fs;
56262306a36Sopenharmony_ci	unsigned long flags;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* don't want the dma interrupt handler to fire */
56562306a36Sopenharmony_ci	local_irq_save(flags);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	intr = mb->ir; /* read interrupt register */
56862306a36Sopenharmony_ci	mace_handle_misc_intrs(dev, intr);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (intr & XMTINT) {
57162306a36Sopenharmony_ci		fs = mb->xmtfs;
57262306a36Sopenharmony_ci		if ((fs & XMTSV) == 0) {
57362306a36Sopenharmony_ci			printk(KERN_ERR "macmace: xmtfs not valid! (fs=%x)\n", fs);
57462306a36Sopenharmony_ci			mace_reset(dev);
57562306a36Sopenharmony_ci			/*
57662306a36Sopenharmony_ci			 * XXX mace likes to hang the machine after a xmtfs error.
57762306a36Sopenharmony_ci			 * This is hard to reproduce, resetting *may* help
57862306a36Sopenharmony_ci			 */
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci		/* dma should have finished */
58162306a36Sopenharmony_ci		if (!mp->tx_count) {
58262306a36Sopenharmony_ci			printk(KERN_DEBUG "macmace: tx ring ran out? (fs=%x)\n", fs);
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci		/* Update stats */
58562306a36Sopenharmony_ci		if (fs & (UFLO|LCOL|LCAR|RTRY)) {
58662306a36Sopenharmony_ci			++dev->stats.tx_errors;
58762306a36Sopenharmony_ci			if (fs & LCAR)
58862306a36Sopenharmony_ci				++dev->stats.tx_carrier_errors;
58962306a36Sopenharmony_ci			else if (fs & (UFLO|LCOL|RTRY)) {
59062306a36Sopenharmony_ci				++dev->stats.tx_aborted_errors;
59162306a36Sopenharmony_ci				if (mb->xmtfs & UFLO) {
59262306a36Sopenharmony_ci					dev->stats.tx_fifo_errors++;
59362306a36Sopenharmony_ci					mace_txdma_reset(dev);
59462306a36Sopenharmony_ci				}
59562306a36Sopenharmony_ci			}
59662306a36Sopenharmony_ci		}
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (mp->tx_count)
60062306a36Sopenharmony_ci		netif_wake_queue(dev);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	local_irq_restore(flags);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return IRQ_HANDLED;
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void mace_tx_timeout(struct net_device *dev, unsigned int txqueue)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
61062306a36Sopenharmony_ci	volatile struct mace *mb = mp->mace;
61162306a36Sopenharmony_ci	unsigned long flags;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	local_irq_save(flags);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* turn off both tx and rx and reset the chip */
61662306a36Sopenharmony_ci	mb->maccc = 0;
61762306a36Sopenharmony_ci	printk(KERN_ERR "macmace: transmit timeout - resetting\n");
61862306a36Sopenharmony_ci	mace_txdma_reset(dev);
61962306a36Sopenharmony_ci	mace_reset(dev);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* restart rx dma */
62262306a36Sopenharmony_ci	mace_rxdma_reset(dev);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	mp->tx_count = N_TX_RING;
62562306a36Sopenharmony_ci	netif_wake_queue(dev);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* turn it on! */
62862306a36Sopenharmony_ci	mb->maccc = ENXMT | ENRCV;
62962306a36Sopenharmony_ci	/* enable all interrupts except receive interrupts */
63062306a36Sopenharmony_ci	mb->imr = RCVINT;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	local_irq_restore(flags);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * Handle a newly arrived frame
63762306a36Sopenharmony_ci */
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct sk_buff *skb;
64262306a36Sopenharmony_ci	unsigned int frame_status = mf->rcvsts;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (frame_status & (RS_OFLO | RS_CLSN | RS_FRAMERR | RS_FCSERR)) {
64562306a36Sopenharmony_ci		dev->stats.rx_errors++;
64662306a36Sopenharmony_ci		if (frame_status & RS_OFLO)
64762306a36Sopenharmony_ci			dev->stats.rx_fifo_errors++;
64862306a36Sopenharmony_ci		if (frame_status & RS_CLSN)
64962306a36Sopenharmony_ci			dev->stats.collisions++;
65062306a36Sopenharmony_ci		if (frame_status & RS_FRAMERR)
65162306a36Sopenharmony_ci			dev->stats.rx_frame_errors++;
65262306a36Sopenharmony_ci		if (frame_status & RS_FCSERR)
65362306a36Sopenharmony_ci			dev->stats.rx_crc_errors++;
65462306a36Sopenharmony_ci	} else {
65562306a36Sopenharmony_ci		unsigned int frame_length = mf->rcvcnt + ((frame_status & 0x0F) << 8 );
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		skb = netdev_alloc_skb(dev, frame_length + 2);
65862306a36Sopenharmony_ci		if (!skb) {
65962306a36Sopenharmony_ci			dev->stats.rx_dropped++;
66062306a36Sopenharmony_ci			return;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci		skb_reserve(skb, 2);
66362306a36Sopenharmony_ci		skb_put_data(skb, mf->data, frame_length);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
66662306a36Sopenharmony_ci		netif_rx(skb);
66762306a36Sopenharmony_ci		dev->stats.rx_packets++;
66862306a36Sopenharmony_ci		dev->stats.rx_bytes += frame_length;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci/*
67362306a36Sopenharmony_ci * The PSC has passed us a DMA interrupt event.
67462306a36Sopenharmony_ci */
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic irqreturn_t mace_dma_intr(int irq, void *dev_id)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) dev_id;
67962306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
68062306a36Sopenharmony_ci	int left, head;
68162306a36Sopenharmony_ci	u16 status;
68262306a36Sopenharmony_ci	u32 baka;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* Not sure what this does */
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	while ((baka = psc_read_long(PSC_MYSTERY)) != psc_read_long(PSC_MYSTERY));
68762306a36Sopenharmony_ci	if (!(baka & 0x60000000)) return IRQ_NONE;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/*
69062306a36Sopenharmony_ci	 * Process the read queue
69162306a36Sopenharmony_ci	 */
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	status = psc_read_word(PSC_ENETRD_CTL);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (status & 0x2000) {
69662306a36Sopenharmony_ci		mace_rxdma_reset(dev);
69762306a36Sopenharmony_ci	} else if (status & 0x0100) {
69862306a36Sopenharmony_ci		psc_write_word(PSC_ENETRD_CMD + mp->rx_slot, 0x1100);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		left = psc_read_long(PSC_ENETRD_LEN + mp->rx_slot);
70162306a36Sopenharmony_ci		head = N_RX_RING - left;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		/* Loop through the ring buffer and process new packages */
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		while (mp->rx_tail < head) {
70662306a36Sopenharmony_ci			mace_dma_rx_frame(dev, (struct mace_frame*) (mp->rx_ring
70762306a36Sopenharmony_ci				+ (mp->rx_tail * MACE_BUFF_SIZE)));
70862306a36Sopenharmony_ci			mp->rx_tail++;
70962306a36Sopenharmony_ci		}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		/* If we're out of buffers in this ring then switch to */
71262306a36Sopenharmony_ci		/* the other set, otherwise just reactivate this one.  */
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		if (!left) {
71562306a36Sopenharmony_ci			mace_load_rxdma_base(dev, mp->rx_slot);
71662306a36Sopenharmony_ci			mp->rx_slot ^= 0x10;
71762306a36Sopenharmony_ci		} else {
71862306a36Sopenharmony_ci			psc_write_word(PSC_ENETRD_CMD + mp->rx_slot, 0x9800);
71962306a36Sopenharmony_ci		}
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/*
72362306a36Sopenharmony_ci	 * Process the write queue
72462306a36Sopenharmony_ci	 */
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	status = psc_read_word(PSC_ENETWR_CTL);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (status & 0x2000) {
72962306a36Sopenharmony_ci		mace_txdma_reset(dev);
73062306a36Sopenharmony_ci	} else if (status & 0x0100) {
73162306a36Sopenharmony_ci		psc_write_word(PSC_ENETWR_CMD + mp->tx_sloti, 0x0100);
73262306a36Sopenharmony_ci		mp->tx_sloti ^= 0x10;
73362306a36Sopenharmony_ci		mp->tx_count++;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci	return IRQ_HANDLED;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
73962306a36Sopenharmony_ciMODULE_DESCRIPTION("Macintosh MACE ethernet driver");
74062306a36Sopenharmony_ciMODULE_ALIAS("platform:macmace");
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic int mac_mace_device_remove(struct platform_device *pdev)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
74562306a36Sopenharmony_ci	struct mace_data *mp = netdev_priv(dev);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	unregister_netdev(dev);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	free_irq(dev->irq, dev);
75062306a36Sopenharmony_ci	free_irq(IRQ_MAC_MACE_DMA, dev);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	dma_free_coherent(mp->device, N_RX_RING * MACE_BUFF_SIZE,
75362306a36Sopenharmony_ci	                  mp->rx_ring, mp->rx_ring_phys);
75462306a36Sopenharmony_ci	dma_free_coherent(mp->device, N_TX_RING * MACE_BUFF_SIZE,
75562306a36Sopenharmony_ci	                  mp->tx_ring, mp->tx_ring_phys);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	free_netdev(dev);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return 0;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic struct platform_driver mac_mace_driver = {
76362306a36Sopenharmony_ci	.probe  = mace_probe,
76462306a36Sopenharmony_ci	.remove = mac_mace_device_remove,
76562306a36Sopenharmony_ci	.driver	= {
76662306a36Sopenharmony_ci		.name	= mac_mace_string,
76762306a36Sopenharmony_ci	},
76862306a36Sopenharmony_ci};
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cimodule_platform_driver(mac_mace_driver);
771