162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Network device driver for the MACE ethernet controller on
462306a36Sopenharmony_ci * Apple Powermacs.  Assumes it's under a DBDMA controller.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/etherdevice.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/timer.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/crc32.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <linux/bitrev.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/pgtable.h>
2362306a36Sopenharmony_ci#include <asm/dbdma.h>
2462306a36Sopenharmony_ci#include <asm/io.h>
2562306a36Sopenharmony_ci#include <asm/macio.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "mace.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int port_aaui = -1;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define N_RX_RING	8
3262306a36Sopenharmony_ci#define N_TX_RING	6
3362306a36Sopenharmony_ci#define MAX_TX_ACTIVE	1
3462306a36Sopenharmony_ci#define NCMDS_TX	1	/* dma commands per element in tx ring */
3562306a36Sopenharmony_ci#define RX_BUFLEN	(ETH_FRAME_LEN + 8)
3662306a36Sopenharmony_ci#define TX_TIMEOUT	HZ	/* 1 second */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Chip rev needs workaround on HW & multicast addr change */
3962306a36Sopenharmony_ci#define BROKEN_ADDRCHG_REV	0x0941
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Bits in transmit DMA status */
4262306a36Sopenharmony_ci#define TX_DMA_ERR	0x80
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct mace_data {
4562306a36Sopenharmony_ci    volatile struct mace __iomem *mace;
4662306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *tx_dma;
4762306a36Sopenharmony_ci    int tx_dma_intr;
4862306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *rx_dma;
4962306a36Sopenharmony_ci    int rx_dma_intr;
5062306a36Sopenharmony_ci    volatile struct dbdma_cmd *tx_cmds;	/* xmit dma command list */
5162306a36Sopenharmony_ci    volatile struct dbdma_cmd *rx_cmds;	/* recv dma command list */
5262306a36Sopenharmony_ci    struct sk_buff *rx_bufs[N_RX_RING];
5362306a36Sopenharmony_ci    int rx_fill;
5462306a36Sopenharmony_ci    int rx_empty;
5562306a36Sopenharmony_ci    struct sk_buff *tx_bufs[N_TX_RING];
5662306a36Sopenharmony_ci    int tx_fill;
5762306a36Sopenharmony_ci    int tx_empty;
5862306a36Sopenharmony_ci    unsigned char maccc;
5962306a36Sopenharmony_ci    unsigned char tx_fullup;
6062306a36Sopenharmony_ci    unsigned char tx_active;
6162306a36Sopenharmony_ci    unsigned char tx_bad_runt;
6262306a36Sopenharmony_ci    struct timer_list tx_timeout;
6362306a36Sopenharmony_ci    int timeout_active;
6462306a36Sopenharmony_ci    int port_aaui;
6562306a36Sopenharmony_ci    int chipid;
6662306a36Sopenharmony_ci    struct macio_dev *mdev;
6762306a36Sopenharmony_ci    spinlock_t lock;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Number of bytes of private data per MACE: allow enough for
7262306a36Sopenharmony_ci * the rx and tx dma commands plus a branch dma command each,
7362306a36Sopenharmony_ci * and another 16 bytes to allow us to align the dma command
7462306a36Sopenharmony_ci * buffers on a 16 byte boundary.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_ci#define PRIV_BYTES	(sizeof(struct mace_data) \
7762306a36Sopenharmony_ci	+ (N_RX_RING + NCMDS_TX * N_TX_RING + 3) * sizeof(struct dbdma_cmd))
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int mace_open(struct net_device *dev);
8062306a36Sopenharmony_cistatic int mace_close(struct net_device *dev);
8162306a36Sopenharmony_cistatic netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev);
8262306a36Sopenharmony_cistatic void mace_set_multicast(struct net_device *dev);
8362306a36Sopenharmony_cistatic void mace_reset(struct net_device *dev);
8462306a36Sopenharmony_cistatic int mace_set_address(struct net_device *dev, void *addr);
8562306a36Sopenharmony_cistatic irqreturn_t mace_interrupt(int irq, void *dev_id);
8662306a36Sopenharmony_cistatic irqreturn_t mace_txdma_intr(int irq, void *dev_id);
8762306a36Sopenharmony_cistatic irqreturn_t mace_rxdma_intr(int irq, void *dev_id);
8862306a36Sopenharmony_cistatic void mace_set_timeout(struct net_device *dev);
8962306a36Sopenharmony_cistatic void mace_tx_timeout(struct timer_list *t);
9062306a36Sopenharmony_cistatic inline void dbdma_reset(volatile struct dbdma_regs __iomem *dma);
9162306a36Sopenharmony_cistatic inline void mace_clean_rings(struct mace_data *mp);
9262306a36Sopenharmony_cistatic void __mace_set_address(struct net_device *dev, const void *addr);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * If we can't get a skbuff when we need it, we use this area for DMA.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistatic unsigned char *dummy_buf;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct net_device_ops mace_netdev_ops = {
10062306a36Sopenharmony_ci	.ndo_open		= mace_open,
10162306a36Sopenharmony_ci	.ndo_stop		= mace_close,
10262306a36Sopenharmony_ci	.ndo_start_xmit		= mace_xmit_start,
10362306a36Sopenharmony_ci	.ndo_set_rx_mode	= mace_set_multicast,
10462306a36Sopenharmony_ci	.ndo_set_mac_address	= mace_set_address,
10562306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int mace_probe(struct macio_dev *mdev, const struct of_device_id *match)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct device_node *mace = macio_get_of_node(mdev);
11162306a36Sopenharmony_ci	struct net_device *dev;
11262306a36Sopenharmony_ci	struct mace_data *mp;
11362306a36Sopenharmony_ci	const unsigned char *addr;
11462306a36Sopenharmony_ci	u8 macaddr[ETH_ALEN];
11562306a36Sopenharmony_ci	int j, rev, rc = -EBUSY;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) {
11862306a36Sopenharmony_ci		printk(KERN_ERR "can't use MACE %pOF: need 3 addrs and 3 irqs\n",
11962306a36Sopenharmony_ci		       mace);
12062306a36Sopenharmony_ci		return -ENODEV;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	addr = of_get_property(mace, "mac-address", NULL);
12462306a36Sopenharmony_ci	if (addr == NULL) {
12562306a36Sopenharmony_ci		addr = of_get_property(mace, "local-mac-address", NULL);
12662306a36Sopenharmony_ci		if (addr == NULL) {
12762306a36Sopenharmony_ci			printk(KERN_ERR "Can't get mac-address for MACE %pOF\n",
12862306a36Sopenharmony_ci			       mace);
12962306a36Sopenharmony_ci			return -ENODEV;
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/*
13462306a36Sopenharmony_ci	 * lazy allocate the driver-wide dummy buffer. (Note that we
13562306a36Sopenharmony_ci	 * never have more than one MACE in the system anyway)
13662306a36Sopenharmony_ci	 */
13762306a36Sopenharmony_ci	if (dummy_buf == NULL) {
13862306a36Sopenharmony_ci		dummy_buf = kmalloc(RX_BUFLEN+2, GFP_KERNEL);
13962306a36Sopenharmony_ci		if (dummy_buf == NULL)
14062306a36Sopenharmony_ci			return -ENOMEM;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (macio_request_resources(mdev, "mace")) {
14462306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't request IO resources !\n");
14562306a36Sopenharmony_ci		return -EBUSY;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	dev = alloc_etherdev(PRIV_BYTES);
14962306a36Sopenharmony_ci	if (!dev) {
15062306a36Sopenharmony_ci		rc = -ENOMEM;
15162306a36Sopenharmony_ci		goto err_release;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &mdev->ofdev.dev);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	mp = netdev_priv(dev);
15662306a36Sopenharmony_ci	mp->mdev = mdev;
15762306a36Sopenharmony_ci	macio_set_drvdata(mdev, dev);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	dev->base_addr = macio_resource_start(mdev, 0);
16062306a36Sopenharmony_ci	mp->mace = ioremap(dev->base_addr, 0x1000);
16162306a36Sopenharmony_ci	if (mp->mace == NULL) {
16262306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't map IO resources !\n");
16362306a36Sopenharmony_ci		rc = -ENOMEM;
16462306a36Sopenharmony_ci		goto err_free;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	dev->irq = macio_irq(mdev, 0);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	rev = addr[0] == 0 && addr[1] == 0xA0;
16962306a36Sopenharmony_ci	for (j = 0; j < 6; ++j) {
17062306a36Sopenharmony_ci		macaddr[j] = rev ? bitrev8(addr[j]): addr[j];
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	eth_hw_addr_set(dev, macaddr);
17362306a36Sopenharmony_ci	mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) |
17462306a36Sopenharmony_ci			in_8(&mp->mace->chipid_lo);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	mp = netdev_priv(dev);
17862306a36Sopenharmony_ci	mp->maccc = ENXMT | ENRCV;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	mp->tx_dma = ioremap(macio_resource_start(mdev, 1), 0x1000);
18162306a36Sopenharmony_ci	if (mp->tx_dma == NULL) {
18262306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't map TX DMA resources !\n");
18362306a36Sopenharmony_ci		rc = -ENOMEM;
18462306a36Sopenharmony_ci		goto err_unmap_io;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	mp->tx_dma_intr = macio_irq(mdev, 1);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	mp->rx_dma = ioremap(macio_resource_start(mdev, 2), 0x1000);
18962306a36Sopenharmony_ci	if (mp->rx_dma == NULL) {
19062306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't map RX DMA resources !\n");
19162306a36Sopenharmony_ci		rc = -ENOMEM;
19262306a36Sopenharmony_ci		goto err_unmap_tx_dma;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	mp->rx_dma_intr = macio_irq(mdev, 2);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	mp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(mp + 1);
19762306a36Sopenharmony_ci	mp->rx_cmds = mp->tx_cmds + NCMDS_TX * N_TX_RING + 1;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	memset((char *) mp->tx_cmds, 0,
20062306a36Sopenharmony_ci	       (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
20162306a36Sopenharmony_ci	timer_setup(&mp->tx_timeout, mace_tx_timeout, 0);
20262306a36Sopenharmony_ci	spin_lock_init(&mp->lock);
20362306a36Sopenharmony_ci	mp->timeout_active = 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (port_aaui >= 0)
20662306a36Sopenharmony_ci		mp->port_aaui = port_aaui;
20762306a36Sopenharmony_ci	else {
20862306a36Sopenharmony_ci		/* Apple Network Server uses the AAUI port */
20962306a36Sopenharmony_ci		if (of_machine_is_compatible("AAPL,ShinerESB"))
21062306a36Sopenharmony_ci			mp->port_aaui = 1;
21162306a36Sopenharmony_ci		else {
21262306a36Sopenharmony_ci#ifdef CONFIG_MACE_AAUI_PORT
21362306a36Sopenharmony_ci			mp->port_aaui = 1;
21462306a36Sopenharmony_ci#else
21562306a36Sopenharmony_ci			mp->port_aaui = 0;
21662306a36Sopenharmony_ci#endif
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dev->netdev_ops = &mace_netdev_ops;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/*
22362306a36Sopenharmony_ci	 * Most of what is below could be moved to mace_open()
22462306a36Sopenharmony_ci	 */
22562306a36Sopenharmony_ci	mace_reset(dev);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	rc = request_irq(dev->irq, mace_interrupt, 0, "MACE", dev);
22862306a36Sopenharmony_ci	if (rc) {
22962306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
23062306a36Sopenharmony_ci		goto err_unmap_rx_dma;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci	rc = request_irq(mp->tx_dma_intr, mace_txdma_intr, 0, "MACE-txdma", dev);
23362306a36Sopenharmony_ci	if (rc) {
23462306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't get irq %d\n", mp->tx_dma_intr);
23562306a36Sopenharmony_ci		goto err_free_irq;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	rc = request_irq(mp->rx_dma_intr, mace_rxdma_intr, 0, "MACE-rxdma", dev);
23862306a36Sopenharmony_ci	if (rc) {
23962306a36Sopenharmony_ci		printk(KERN_ERR "MACE: can't get irq %d\n", mp->rx_dma_intr);
24062306a36Sopenharmony_ci		goto err_free_tx_irq;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	rc = register_netdev(dev);
24462306a36Sopenharmony_ci	if (rc) {
24562306a36Sopenharmony_ci		printk(KERN_ERR "MACE: Cannot register net device, aborting.\n");
24662306a36Sopenharmony_ci		goto err_free_rx_irq;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	printk(KERN_INFO "%s: MACE at %pM, chip revision %d.%d\n",
25062306a36Sopenharmony_ci	       dev->name, dev->dev_addr,
25162306a36Sopenharmony_ci	       mp->chipid >> 8, mp->chipid & 0xff);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci err_free_rx_irq:
25662306a36Sopenharmony_ci	free_irq(macio_irq(mdev, 2), dev);
25762306a36Sopenharmony_ci err_free_tx_irq:
25862306a36Sopenharmony_ci	free_irq(macio_irq(mdev, 1), dev);
25962306a36Sopenharmony_ci err_free_irq:
26062306a36Sopenharmony_ci	free_irq(macio_irq(mdev, 0), dev);
26162306a36Sopenharmony_ci err_unmap_rx_dma:
26262306a36Sopenharmony_ci	iounmap(mp->rx_dma);
26362306a36Sopenharmony_ci err_unmap_tx_dma:
26462306a36Sopenharmony_ci	iounmap(mp->tx_dma);
26562306a36Sopenharmony_ci err_unmap_io:
26662306a36Sopenharmony_ci	iounmap(mp->mace);
26762306a36Sopenharmony_ci err_free:
26862306a36Sopenharmony_ci	free_netdev(dev);
26962306a36Sopenharmony_ci err_release:
27062306a36Sopenharmony_ci	macio_release_resources(mdev);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return rc;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int mace_remove(struct macio_dev *mdev)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct net_device *dev = macio_get_drvdata(mdev);
27862306a36Sopenharmony_ci	struct mace_data *mp;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	BUG_ON(dev == NULL);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	macio_set_drvdata(mdev, NULL);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	mp = netdev_priv(dev);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	unregister_netdev(dev);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	free_irq(dev->irq, dev);
28962306a36Sopenharmony_ci	free_irq(mp->tx_dma_intr, dev);
29062306a36Sopenharmony_ci	free_irq(mp->rx_dma_intr, dev);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	iounmap(mp->rx_dma);
29362306a36Sopenharmony_ci	iounmap(mp->tx_dma);
29462306a36Sopenharmony_ci	iounmap(mp->mace);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	free_netdev(dev);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	macio_release_resources(mdev);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void dbdma_reset(volatile struct dbdma_regs __iomem *dma)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci    int i;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci    out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci    /*
31062306a36Sopenharmony_ci     * Yes this looks peculiar, but apparently it needs to be this
31162306a36Sopenharmony_ci     * way on some machines.
31262306a36Sopenharmony_ci     */
31362306a36Sopenharmony_ci    for (i = 200; i > 0; --i)
31462306a36Sopenharmony_ci	if (le32_to_cpu(dma->control) & RUN)
31562306a36Sopenharmony_ci	    udelay(1);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic void mace_reset(struct net_device *dev)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
32162306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
32262306a36Sopenharmony_ci    int i;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci    /* soft-reset the chip */
32562306a36Sopenharmony_ci    i = 200;
32662306a36Sopenharmony_ci    while (--i) {
32762306a36Sopenharmony_ci	out_8(&mb->biucc, SWRST);
32862306a36Sopenharmony_ci	if (in_8(&mb->biucc) & SWRST) {
32962306a36Sopenharmony_ci	    udelay(10);
33062306a36Sopenharmony_ci	    continue;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	break;
33362306a36Sopenharmony_ci    }
33462306a36Sopenharmony_ci    if (!i) {
33562306a36Sopenharmony_ci	printk(KERN_ERR "mace: cannot reset chip!\n");
33662306a36Sopenharmony_ci	return;
33762306a36Sopenharmony_ci    }
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci    out_8(&mb->imr, 0xff);	/* disable all intrs for now */
34062306a36Sopenharmony_ci    i = in_8(&mb->ir);
34162306a36Sopenharmony_ci    out_8(&mb->maccc, 0);	/* turn off tx, rx */
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci    out_8(&mb->biucc, XMTSP_64);
34462306a36Sopenharmony_ci    out_8(&mb->utr, RTRD);
34562306a36Sopenharmony_ci    out_8(&mb->fifocc, RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST);
34662306a36Sopenharmony_ci    out_8(&mb->xmtfc, AUTO_PAD_XMIT); /* auto-pad short frames */
34762306a36Sopenharmony_ci    out_8(&mb->rcvfc, 0);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci    /* load up the hardware address */
35062306a36Sopenharmony_ci    __mace_set_address(dev, dev->dev_addr);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci    /* clear the multicast filter */
35362306a36Sopenharmony_ci    if (mp->chipid == BROKEN_ADDRCHG_REV)
35462306a36Sopenharmony_ci	out_8(&mb->iac, LOGADDR);
35562306a36Sopenharmony_ci    else {
35662306a36Sopenharmony_ci	out_8(&mb->iac, ADDRCHG | LOGADDR);
35762306a36Sopenharmony_ci	while ((in_8(&mb->iac) & ADDRCHG) != 0)
35862306a36Sopenharmony_ci		;
35962306a36Sopenharmony_ci    }
36062306a36Sopenharmony_ci    for (i = 0; i < 8; ++i)
36162306a36Sopenharmony_ci	out_8(&mb->ladrf, 0);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci    /* done changing address */
36462306a36Sopenharmony_ci    if (mp->chipid != BROKEN_ADDRCHG_REV)
36562306a36Sopenharmony_ci	out_8(&mb->iac, 0);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci    if (mp->port_aaui)
36862306a36Sopenharmony_ci	out_8(&mb->plscc, PORTSEL_AUI + ENPLSIO);
36962306a36Sopenharmony_ci    else
37062306a36Sopenharmony_ci	out_8(&mb->plscc, PORTSEL_GPSI + ENPLSIO);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void __mace_set_address(struct net_device *dev, const void *addr)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
37662306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
37762306a36Sopenharmony_ci    const unsigned char *p = addr;
37862306a36Sopenharmony_ci    u8 macaddr[ETH_ALEN];
37962306a36Sopenharmony_ci    int i;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci    /* load up the hardware address */
38262306a36Sopenharmony_ci    if (mp->chipid == BROKEN_ADDRCHG_REV)
38362306a36Sopenharmony_ci	out_8(&mb->iac, PHYADDR);
38462306a36Sopenharmony_ci    else {
38562306a36Sopenharmony_ci	out_8(&mb->iac, ADDRCHG | PHYADDR);
38662306a36Sopenharmony_ci	while ((in_8(&mb->iac) & ADDRCHG) != 0)
38762306a36Sopenharmony_ci	    ;
38862306a36Sopenharmony_ci    }
38962306a36Sopenharmony_ci    for (i = 0; i < 6; ++i)
39062306a36Sopenharmony_ci        out_8(&mb->padr, macaddr[i] = p[i]);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci    eth_hw_addr_set(dev, macaddr);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci    if (mp->chipid != BROKEN_ADDRCHG_REV)
39562306a36Sopenharmony_ci        out_8(&mb->iac, 0);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int mace_set_address(struct net_device *dev, void *addr)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
40162306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
40262306a36Sopenharmony_ci    unsigned long flags;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci    __mace_set_address(dev, addr);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci    /* note: setting ADDRCHG clears ENRCV */
40962306a36Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
41262306a36Sopenharmony_ci    return 0;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic inline void mace_clean_rings(struct mace_data *mp)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci    int i;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci    /* free some skb's */
42062306a36Sopenharmony_ci    for (i = 0; i < N_RX_RING; ++i) {
42162306a36Sopenharmony_ci	if (mp->rx_bufs[i] != NULL) {
42262306a36Sopenharmony_ci	    dev_kfree_skb(mp->rx_bufs[i]);
42362306a36Sopenharmony_ci	    mp->rx_bufs[i] = NULL;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci    }
42662306a36Sopenharmony_ci    for (i = mp->tx_empty; i != mp->tx_fill; ) {
42762306a36Sopenharmony_ci	dev_kfree_skb(mp->tx_bufs[i]);
42862306a36Sopenharmony_ci	if (++i >= N_TX_RING)
42962306a36Sopenharmony_ci	    i = 0;
43062306a36Sopenharmony_ci    }
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int mace_open(struct net_device *dev)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
43662306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
43762306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
43862306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
43962306a36Sopenharmony_ci    volatile struct dbdma_cmd *cp;
44062306a36Sopenharmony_ci    int i;
44162306a36Sopenharmony_ci    struct sk_buff *skb;
44262306a36Sopenharmony_ci    unsigned char *data;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci    /* reset the chip */
44562306a36Sopenharmony_ci    mace_reset(dev);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci    /* initialize list of sk_buffs for receiving and set up recv dma */
44862306a36Sopenharmony_ci    mace_clean_rings(mp);
44962306a36Sopenharmony_ci    memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd));
45062306a36Sopenharmony_ci    cp = mp->rx_cmds;
45162306a36Sopenharmony_ci    for (i = 0; i < N_RX_RING - 1; ++i) {
45262306a36Sopenharmony_ci	skb = netdev_alloc_skb(dev, RX_BUFLEN + 2);
45362306a36Sopenharmony_ci	if (!skb) {
45462306a36Sopenharmony_ci	    data = dummy_buf;
45562306a36Sopenharmony_ci	} else {
45662306a36Sopenharmony_ci	    skb_reserve(skb, 2);	/* so IP header lands on 4-byte bdry */
45762306a36Sopenharmony_ci	    data = skb->data;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	mp->rx_bufs[i] = skb;
46062306a36Sopenharmony_ci	cp->req_count = cpu_to_le16(RX_BUFLEN);
46162306a36Sopenharmony_ci	cp->command = cpu_to_le16(INPUT_LAST + INTR_ALWAYS);
46262306a36Sopenharmony_ci	cp->phy_addr = cpu_to_le32(virt_to_bus(data));
46362306a36Sopenharmony_ci	cp->xfer_status = 0;
46462306a36Sopenharmony_ci	++cp;
46562306a36Sopenharmony_ci    }
46662306a36Sopenharmony_ci    mp->rx_bufs[i] = NULL;
46762306a36Sopenharmony_ci    cp->command = cpu_to_le16(DBDMA_STOP);
46862306a36Sopenharmony_ci    mp->rx_fill = i;
46962306a36Sopenharmony_ci    mp->rx_empty = 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci    /* Put a branch back to the beginning of the receive command list */
47262306a36Sopenharmony_ci    ++cp;
47362306a36Sopenharmony_ci    cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
47462306a36Sopenharmony_ci    cp->cmd_dep = cpu_to_le32(virt_to_bus(mp->rx_cmds));
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci    /* start rx dma */
47762306a36Sopenharmony_ci    out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
47862306a36Sopenharmony_ci    out_le32(&rd->cmdptr, virt_to_bus(mp->rx_cmds));
47962306a36Sopenharmony_ci    out_le32(&rd->control, (RUN << 16) | RUN);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci    /* put a branch at the end of the tx command list */
48262306a36Sopenharmony_ci    cp = mp->tx_cmds + NCMDS_TX * N_TX_RING;
48362306a36Sopenharmony_ci    cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
48462306a36Sopenharmony_ci    cp->cmd_dep = cpu_to_le32(virt_to_bus(mp->tx_cmds));
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci    /* reset tx dma */
48762306a36Sopenharmony_ci    out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
48862306a36Sopenharmony_ci    out_le32(&td->cmdptr, virt_to_bus(mp->tx_cmds));
48962306a36Sopenharmony_ci    mp->tx_fill = 0;
49062306a36Sopenharmony_ci    mp->tx_empty = 0;
49162306a36Sopenharmony_ci    mp->tx_fullup = 0;
49262306a36Sopenharmony_ci    mp->tx_active = 0;
49362306a36Sopenharmony_ci    mp->tx_bad_runt = 0;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci    /* turn it on! */
49662306a36Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
49762306a36Sopenharmony_ci    /* enable all interrupts except receive interrupts */
49862306a36Sopenharmony_ci    out_8(&mb->imr, RCVINT);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci    return 0;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int mace_close(struct net_device *dev)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
50662306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
50762306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
50862306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci    /* disable rx and tx */
51162306a36Sopenharmony_ci    out_8(&mb->maccc, 0);
51262306a36Sopenharmony_ci    out_8(&mb->imr, 0xff);		/* disable all intrs */
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci    /* disable rx and tx dma */
51562306a36Sopenharmony_ci    rd->control = cpu_to_le32((RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
51662306a36Sopenharmony_ci    td->control = cpu_to_le32((RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci    mace_clean_rings(mp);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci    return 0;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic inline void mace_set_timeout(struct net_device *dev)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci    if (mp->timeout_active)
52862306a36Sopenharmony_ci	del_timer(&mp->tx_timeout);
52962306a36Sopenharmony_ci    mp->tx_timeout.expires = jiffies + TX_TIMEOUT;
53062306a36Sopenharmony_ci    add_timer(&mp->tx_timeout);
53162306a36Sopenharmony_ci    mp->timeout_active = 1;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
53762306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
53862306a36Sopenharmony_ci    volatile struct dbdma_cmd *cp, *np;
53962306a36Sopenharmony_ci    unsigned long flags;
54062306a36Sopenharmony_ci    int fill, next, len;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci    /* see if there's a free slot in the tx ring */
54362306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
54462306a36Sopenharmony_ci    fill = mp->tx_fill;
54562306a36Sopenharmony_ci    next = fill + 1;
54662306a36Sopenharmony_ci    if (next >= N_TX_RING)
54762306a36Sopenharmony_ci	next = 0;
54862306a36Sopenharmony_ci    if (next == mp->tx_empty) {
54962306a36Sopenharmony_ci	netif_stop_queue(dev);
55062306a36Sopenharmony_ci	mp->tx_fullup = 1;
55162306a36Sopenharmony_ci	spin_unlock_irqrestore(&mp->lock, flags);
55262306a36Sopenharmony_ci	return NETDEV_TX_BUSY;		/* can't take it at the moment */
55362306a36Sopenharmony_ci    }
55462306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci    /* partially fill in the dma command block */
55762306a36Sopenharmony_ci    len = skb->len;
55862306a36Sopenharmony_ci    if (len > ETH_FRAME_LEN) {
55962306a36Sopenharmony_ci	printk(KERN_DEBUG "mace: xmit frame too long (%d)\n", len);
56062306a36Sopenharmony_ci	len = ETH_FRAME_LEN;
56162306a36Sopenharmony_ci    }
56262306a36Sopenharmony_ci    mp->tx_bufs[fill] = skb;
56362306a36Sopenharmony_ci    cp = mp->tx_cmds + NCMDS_TX * fill;
56462306a36Sopenharmony_ci    cp->req_count = cpu_to_le16(len);
56562306a36Sopenharmony_ci    cp->phy_addr = cpu_to_le32(virt_to_bus(skb->data));
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci    np = mp->tx_cmds + NCMDS_TX * next;
56862306a36Sopenharmony_ci    out_le16(&np->command, DBDMA_STOP);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci    /* poke the tx dma channel */
57162306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
57262306a36Sopenharmony_ci    mp->tx_fill = next;
57362306a36Sopenharmony_ci    if (!mp->tx_bad_runt && mp->tx_active < MAX_TX_ACTIVE) {
57462306a36Sopenharmony_ci	out_le16(&cp->xfer_status, 0);
57562306a36Sopenharmony_ci	out_le16(&cp->command, OUTPUT_LAST);
57662306a36Sopenharmony_ci	out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
57762306a36Sopenharmony_ci	++mp->tx_active;
57862306a36Sopenharmony_ci	mace_set_timeout(dev);
57962306a36Sopenharmony_ci    }
58062306a36Sopenharmony_ci    if (++next >= N_TX_RING)
58162306a36Sopenharmony_ci	next = 0;
58262306a36Sopenharmony_ci    if (next == mp->tx_empty)
58362306a36Sopenharmony_ci	netif_stop_queue(dev);
58462306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci    return NETDEV_TX_OK;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void mace_set_multicast(struct net_device *dev)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
59262306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
59362306a36Sopenharmony_ci    int i;
59462306a36Sopenharmony_ci    u32 crc;
59562306a36Sopenharmony_ci    unsigned long flags;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
59862306a36Sopenharmony_ci    mp->maccc &= ~PROM;
59962306a36Sopenharmony_ci    if (dev->flags & IFF_PROMISC) {
60062306a36Sopenharmony_ci	mp->maccc |= PROM;
60162306a36Sopenharmony_ci    } else {
60262306a36Sopenharmony_ci	unsigned char multicast_filter[8];
60362306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
60662306a36Sopenharmony_ci	    for (i = 0; i < 8; i++)
60762306a36Sopenharmony_ci		multicast_filter[i] = 0xff;
60862306a36Sopenharmony_ci	} else {
60962306a36Sopenharmony_ci	    for (i = 0; i < 8; i++)
61062306a36Sopenharmony_ci		multicast_filter[i] = 0;
61162306a36Sopenharmony_ci	    netdev_for_each_mc_addr(ha, dev) {
61262306a36Sopenharmony_ci	        crc = ether_crc_le(6, ha->addr);
61362306a36Sopenharmony_ci		i = crc >> 26;	/* bit number in multicast_filter */
61462306a36Sopenharmony_ci		multicast_filter[i >> 3] |= 1 << (i & 7);
61562306a36Sopenharmony_ci	    }
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci#if 0
61862306a36Sopenharmony_ci	printk("Multicast filter :");
61962306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
62062306a36Sopenharmony_ci	    printk("%02x ", multicast_filter[i]);
62162306a36Sopenharmony_ci	printk("\n");
62262306a36Sopenharmony_ci#endif
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (mp->chipid == BROKEN_ADDRCHG_REV)
62562306a36Sopenharmony_ci	    out_8(&mb->iac, LOGADDR);
62662306a36Sopenharmony_ci	else {
62762306a36Sopenharmony_ci	    out_8(&mb->iac, ADDRCHG | LOGADDR);
62862306a36Sopenharmony_ci	    while ((in_8(&mb->iac) & ADDRCHG) != 0)
62962306a36Sopenharmony_ci		;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci	for (i = 0; i < 8; ++i)
63262306a36Sopenharmony_ci	    out_8(&mb->ladrf, multicast_filter[i]);
63362306a36Sopenharmony_ci	if (mp->chipid != BROKEN_ADDRCHG_REV)
63462306a36Sopenharmony_ci	    out_8(&mb->iac, 0);
63562306a36Sopenharmony_ci    }
63662306a36Sopenharmony_ci    /* reset maccc */
63762306a36Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
63862306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic void mace_handle_misc_intrs(struct mace_data *mp, int intr, struct net_device *dev)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
64462306a36Sopenharmony_ci    static int mace_babbles, mace_jabbers;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci    if (intr & MPCO)
64762306a36Sopenharmony_ci	dev->stats.rx_missed_errors += 256;
64862306a36Sopenharmony_ci    dev->stats.rx_missed_errors += in_8(&mb->mpc);   /* reading clears it */
64962306a36Sopenharmony_ci    if (intr & RNTPCO)
65062306a36Sopenharmony_ci	dev->stats.rx_length_errors += 256;
65162306a36Sopenharmony_ci    dev->stats.rx_length_errors += in_8(&mb->rntpc); /* reading clears it */
65262306a36Sopenharmony_ci    if (intr & CERR)
65362306a36Sopenharmony_ci	++dev->stats.tx_heartbeat_errors;
65462306a36Sopenharmony_ci    if (intr & BABBLE)
65562306a36Sopenharmony_ci	if (mace_babbles++ < 4)
65662306a36Sopenharmony_ci	    printk(KERN_DEBUG "mace: babbling transmitter\n");
65762306a36Sopenharmony_ci    if (intr & JABBER)
65862306a36Sopenharmony_ci	if (mace_jabbers++ < 4)
65962306a36Sopenharmony_ci	    printk(KERN_DEBUG "mace: jabbering transceiver\n");
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic irqreturn_t mace_interrupt(int irq, void *dev_id)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci    struct net_device *dev = (struct net_device *) dev_id;
66562306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
66662306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
66762306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
66862306a36Sopenharmony_ci    volatile struct dbdma_cmd *cp;
66962306a36Sopenharmony_ci    int intr, fs, i, stat, x;
67062306a36Sopenharmony_ci    int xcount, dstat;
67162306a36Sopenharmony_ci    unsigned long flags;
67262306a36Sopenharmony_ci    /* static int mace_last_fs, mace_last_xcount; */
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
67562306a36Sopenharmony_ci    intr = in_8(&mb->ir);		/* read interrupt register */
67662306a36Sopenharmony_ci    in_8(&mb->xmtrc);			/* get retries */
67762306a36Sopenharmony_ci    mace_handle_misc_intrs(mp, intr, dev);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci    i = mp->tx_empty;
68062306a36Sopenharmony_ci    while (in_8(&mb->pr) & XMTSV) {
68162306a36Sopenharmony_ci	del_timer(&mp->tx_timeout);
68262306a36Sopenharmony_ci	mp->timeout_active = 0;
68362306a36Sopenharmony_ci	/*
68462306a36Sopenharmony_ci	 * Clear any interrupt indication associated with this status
68562306a36Sopenharmony_ci	 * word.  This appears to unlatch any error indication from
68662306a36Sopenharmony_ci	 * the DMA controller.
68762306a36Sopenharmony_ci	 */
68862306a36Sopenharmony_ci	intr = in_8(&mb->ir);
68962306a36Sopenharmony_ci	if (intr != 0)
69062306a36Sopenharmony_ci	    mace_handle_misc_intrs(mp, intr, dev);
69162306a36Sopenharmony_ci	if (mp->tx_bad_runt) {
69262306a36Sopenharmony_ci	    fs = in_8(&mb->xmtfs);
69362306a36Sopenharmony_ci	    mp->tx_bad_runt = 0;
69462306a36Sopenharmony_ci	    out_8(&mb->xmtfc, AUTO_PAD_XMIT);
69562306a36Sopenharmony_ci	    continue;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci	dstat = le32_to_cpu(td->status);
69862306a36Sopenharmony_ci	/* stop DMA controller */
69962306a36Sopenharmony_ci	out_le32(&td->control, RUN << 16);
70062306a36Sopenharmony_ci	/*
70162306a36Sopenharmony_ci	 * xcount is the number of complete frames which have been
70262306a36Sopenharmony_ci	 * written to the fifo but for which status has not been read.
70362306a36Sopenharmony_ci	 */
70462306a36Sopenharmony_ci	xcount = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK;
70562306a36Sopenharmony_ci	if (xcount == 0 || (dstat & DEAD)) {
70662306a36Sopenharmony_ci	    /*
70762306a36Sopenharmony_ci	     * If a packet was aborted before the DMA controller has
70862306a36Sopenharmony_ci	     * finished transferring it, it seems that there are 2 bytes
70962306a36Sopenharmony_ci	     * which are stuck in some buffer somewhere.  These will get
71062306a36Sopenharmony_ci	     * transmitted as soon as we read the frame status (which
71162306a36Sopenharmony_ci	     * reenables the transmit data transfer request).  Turning
71262306a36Sopenharmony_ci	     * off the DMA controller and/or resetting the MACE doesn't
71362306a36Sopenharmony_ci	     * help.  So we disable auto-padding and FCS transmission
71462306a36Sopenharmony_ci	     * so the two bytes will only be a runt packet which should
71562306a36Sopenharmony_ci	     * be ignored by other stations.
71662306a36Sopenharmony_ci	     */
71762306a36Sopenharmony_ci	    out_8(&mb->xmtfc, DXMTFCS);
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci	fs = in_8(&mb->xmtfs);
72062306a36Sopenharmony_ci	if ((fs & XMTSV) == 0) {
72162306a36Sopenharmony_ci	    printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n",
72262306a36Sopenharmony_ci		   fs, xcount, dstat);
72362306a36Sopenharmony_ci	    mace_reset(dev);
72462306a36Sopenharmony_ci		/*
72562306a36Sopenharmony_ci		 * XXX mace likes to hang the machine after a xmtfs error.
72662306a36Sopenharmony_ci		 * This is hard to reproduce, resetting *may* help
72762306a36Sopenharmony_ci		 */
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	cp = mp->tx_cmds + NCMDS_TX * i;
73062306a36Sopenharmony_ci	stat = le16_to_cpu(cp->xfer_status);
73162306a36Sopenharmony_ci	if ((fs & (UFLO|LCOL|LCAR|RTRY)) || (dstat & DEAD) || xcount == 0) {
73262306a36Sopenharmony_ci	    /*
73362306a36Sopenharmony_ci	     * Check whether there were in fact 2 bytes written to
73462306a36Sopenharmony_ci	     * the transmit FIFO.
73562306a36Sopenharmony_ci	     */
73662306a36Sopenharmony_ci	    udelay(1);
73762306a36Sopenharmony_ci	    x = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK;
73862306a36Sopenharmony_ci	    if (x != 0) {
73962306a36Sopenharmony_ci		/* there were two bytes with an end-of-packet indication */
74062306a36Sopenharmony_ci		mp->tx_bad_runt = 1;
74162306a36Sopenharmony_ci		mace_set_timeout(dev);
74262306a36Sopenharmony_ci	    } else {
74362306a36Sopenharmony_ci		/*
74462306a36Sopenharmony_ci		 * Either there weren't the two bytes buffered up, or they
74562306a36Sopenharmony_ci		 * didn't have an end-of-packet indication.
74662306a36Sopenharmony_ci		 * We flush the transmit FIFO just in case (by setting the
74762306a36Sopenharmony_ci		 * XMTFWU bit with the transmitter disabled).
74862306a36Sopenharmony_ci		 */
74962306a36Sopenharmony_ci		out_8(&mb->maccc, in_8(&mb->maccc) & ~ENXMT);
75062306a36Sopenharmony_ci		out_8(&mb->fifocc, in_8(&mb->fifocc) | XMTFWU);
75162306a36Sopenharmony_ci		udelay(1);
75262306a36Sopenharmony_ci		out_8(&mb->maccc, in_8(&mb->maccc) | ENXMT);
75362306a36Sopenharmony_ci		out_8(&mb->xmtfc, AUTO_PAD_XMIT);
75462306a36Sopenharmony_ci	    }
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci	/* dma should have finished */
75762306a36Sopenharmony_ci	if (i == mp->tx_fill) {
75862306a36Sopenharmony_ci	    printk(KERN_DEBUG "mace: tx ring ran out? (fs=%x xc=%d ds=%x)\n",
75962306a36Sopenharmony_ci		   fs, xcount, dstat);
76062306a36Sopenharmony_ci	    continue;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci	/* Update stats */
76362306a36Sopenharmony_ci	if (fs & (UFLO|LCOL|LCAR|RTRY)) {
76462306a36Sopenharmony_ci	    ++dev->stats.tx_errors;
76562306a36Sopenharmony_ci	    if (fs & LCAR)
76662306a36Sopenharmony_ci		++dev->stats.tx_carrier_errors;
76762306a36Sopenharmony_ci	    if (fs & (UFLO|LCOL|RTRY))
76862306a36Sopenharmony_ci		++dev->stats.tx_aborted_errors;
76962306a36Sopenharmony_ci	} else {
77062306a36Sopenharmony_ci	    dev->stats.tx_bytes += mp->tx_bufs[i]->len;
77162306a36Sopenharmony_ci	    ++dev->stats.tx_packets;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci	dev_consume_skb_irq(mp->tx_bufs[i]);
77462306a36Sopenharmony_ci	--mp->tx_active;
77562306a36Sopenharmony_ci	if (++i >= N_TX_RING)
77662306a36Sopenharmony_ci	    i = 0;
77762306a36Sopenharmony_ci#if 0
77862306a36Sopenharmony_ci	mace_last_fs = fs;
77962306a36Sopenharmony_ci	mace_last_xcount = xcount;
78062306a36Sopenharmony_ci#endif
78162306a36Sopenharmony_ci    }
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci    if (i != mp->tx_empty) {
78462306a36Sopenharmony_ci	mp->tx_fullup = 0;
78562306a36Sopenharmony_ci	netif_wake_queue(dev);
78662306a36Sopenharmony_ci    }
78762306a36Sopenharmony_ci    mp->tx_empty = i;
78862306a36Sopenharmony_ci    i += mp->tx_active;
78962306a36Sopenharmony_ci    if (i >= N_TX_RING)
79062306a36Sopenharmony_ci	i -= N_TX_RING;
79162306a36Sopenharmony_ci    if (!mp->tx_bad_runt && i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE) {
79262306a36Sopenharmony_ci	do {
79362306a36Sopenharmony_ci	    /* set up the next one */
79462306a36Sopenharmony_ci	    cp = mp->tx_cmds + NCMDS_TX * i;
79562306a36Sopenharmony_ci	    out_le16(&cp->xfer_status, 0);
79662306a36Sopenharmony_ci	    out_le16(&cp->command, OUTPUT_LAST);
79762306a36Sopenharmony_ci	    ++mp->tx_active;
79862306a36Sopenharmony_ci	    if (++i >= N_TX_RING)
79962306a36Sopenharmony_ci		i = 0;
80062306a36Sopenharmony_ci	} while (i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE);
80162306a36Sopenharmony_ci	out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
80262306a36Sopenharmony_ci	mace_set_timeout(dev);
80362306a36Sopenharmony_ci    }
80462306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
80562306a36Sopenharmony_ci    return IRQ_HANDLED;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic void mace_tx_timeout(struct timer_list *t)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci    struct mace_data *mp = from_timer(mp, t, tx_timeout);
81162306a36Sopenharmony_ci    struct net_device *dev = macio_get_drvdata(mp->mdev);
81262306a36Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
81362306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
81462306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
81562306a36Sopenharmony_ci    volatile struct dbdma_cmd *cp;
81662306a36Sopenharmony_ci    unsigned long flags;
81762306a36Sopenharmony_ci    int i;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
82062306a36Sopenharmony_ci    mp->timeout_active = 0;
82162306a36Sopenharmony_ci    if (mp->tx_active == 0 && !mp->tx_bad_runt)
82262306a36Sopenharmony_ci	goto out;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci    /* update various counters */
82562306a36Sopenharmony_ci    mace_handle_misc_intrs(mp, in_8(&mb->ir), dev);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci    cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci    /* turn off both tx and rx and reset the chip */
83062306a36Sopenharmony_ci    out_8(&mb->maccc, 0);
83162306a36Sopenharmony_ci    printk(KERN_ERR "mace: transmit timeout - resetting\n");
83262306a36Sopenharmony_ci    dbdma_reset(td);
83362306a36Sopenharmony_ci    mace_reset(dev);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci    /* restart rx dma */
83662306a36Sopenharmony_ci    cp = bus_to_virt(le32_to_cpu(rd->cmdptr));
83762306a36Sopenharmony_ci    dbdma_reset(rd);
83862306a36Sopenharmony_ci    out_le16(&cp->xfer_status, 0);
83962306a36Sopenharmony_ci    out_le32(&rd->cmdptr, virt_to_bus(cp));
84062306a36Sopenharmony_ci    out_le32(&rd->control, (RUN << 16) | RUN);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci    /* fix up the transmit side */
84362306a36Sopenharmony_ci    i = mp->tx_empty;
84462306a36Sopenharmony_ci    mp->tx_active = 0;
84562306a36Sopenharmony_ci    ++dev->stats.tx_errors;
84662306a36Sopenharmony_ci    if (mp->tx_bad_runt) {
84762306a36Sopenharmony_ci	mp->tx_bad_runt = 0;
84862306a36Sopenharmony_ci    } else if (i != mp->tx_fill) {
84962306a36Sopenharmony_ci	dev_kfree_skb_irq(mp->tx_bufs[i]);
85062306a36Sopenharmony_ci	if (++i >= N_TX_RING)
85162306a36Sopenharmony_ci	    i = 0;
85262306a36Sopenharmony_ci	mp->tx_empty = i;
85362306a36Sopenharmony_ci    }
85462306a36Sopenharmony_ci    mp->tx_fullup = 0;
85562306a36Sopenharmony_ci    netif_wake_queue(dev);
85662306a36Sopenharmony_ci    if (i != mp->tx_fill) {
85762306a36Sopenharmony_ci	cp = mp->tx_cmds + NCMDS_TX * i;
85862306a36Sopenharmony_ci	out_le16(&cp->xfer_status, 0);
85962306a36Sopenharmony_ci	out_le16(&cp->command, OUTPUT_LAST);
86062306a36Sopenharmony_ci	out_le32(&td->cmdptr, virt_to_bus(cp));
86162306a36Sopenharmony_ci	out_le32(&td->control, (RUN << 16) | RUN);
86262306a36Sopenharmony_ci	++mp->tx_active;
86362306a36Sopenharmony_ci	mace_set_timeout(dev);
86462306a36Sopenharmony_ci    }
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci    /* turn it back on */
86762306a36Sopenharmony_ci    out_8(&mb->imr, RCVINT);
86862306a36Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ciout:
87162306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic irqreturn_t mace_txdma_intr(int irq, void *dev_id)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	return IRQ_HANDLED;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic irqreturn_t mace_rxdma_intr(int irq, void *dev_id)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci    struct net_device *dev = (struct net_device *) dev_id;
88262306a36Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
88362306a36Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
88462306a36Sopenharmony_ci    volatile struct dbdma_cmd *cp, *np;
88562306a36Sopenharmony_ci    int i, nb, stat, next;
88662306a36Sopenharmony_ci    struct sk_buff *skb;
88762306a36Sopenharmony_ci    unsigned frame_status;
88862306a36Sopenharmony_ci    static int mace_lost_status;
88962306a36Sopenharmony_ci    unsigned char *data;
89062306a36Sopenharmony_ci    unsigned long flags;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
89362306a36Sopenharmony_ci    for (i = mp->rx_empty; i != mp->rx_fill; ) {
89462306a36Sopenharmony_ci	cp = mp->rx_cmds + i;
89562306a36Sopenharmony_ci	stat = le16_to_cpu(cp->xfer_status);
89662306a36Sopenharmony_ci	if ((stat & ACTIVE) == 0) {
89762306a36Sopenharmony_ci	    next = i + 1;
89862306a36Sopenharmony_ci	    if (next >= N_RX_RING)
89962306a36Sopenharmony_ci		next = 0;
90062306a36Sopenharmony_ci	    np = mp->rx_cmds + next;
90162306a36Sopenharmony_ci	    if (next != mp->rx_fill &&
90262306a36Sopenharmony_ci		(le16_to_cpu(np->xfer_status) & ACTIVE) != 0) {
90362306a36Sopenharmony_ci		printk(KERN_DEBUG "mace: lost a status word\n");
90462306a36Sopenharmony_ci		++mace_lost_status;
90562306a36Sopenharmony_ci	    } else
90662306a36Sopenharmony_ci		break;
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci	nb = le16_to_cpu(cp->req_count) - le16_to_cpu(cp->res_count);
90962306a36Sopenharmony_ci	out_le16(&cp->command, DBDMA_STOP);
91062306a36Sopenharmony_ci	/* got a packet, have a look at it */
91162306a36Sopenharmony_ci	skb = mp->rx_bufs[i];
91262306a36Sopenharmony_ci	if (!skb) {
91362306a36Sopenharmony_ci	    ++dev->stats.rx_dropped;
91462306a36Sopenharmony_ci	} else if (nb > 8) {
91562306a36Sopenharmony_ci	    data = skb->data;
91662306a36Sopenharmony_ci	    frame_status = (data[nb-3] << 8) + data[nb-4];
91762306a36Sopenharmony_ci	    if (frame_status & (RS_OFLO|RS_CLSN|RS_FRAMERR|RS_FCSERR)) {
91862306a36Sopenharmony_ci		++dev->stats.rx_errors;
91962306a36Sopenharmony_ci		if (frame_status & RS_OFLO)
92062306a36Sopenharmony_ci		    ++dev->stats.rx_over_errors;
92162306a36Sopenharmony_ci		if (frame_status & RS_FRAMERR)
92262306a36Sopenharmony_ci		    ++dev->stats.rx_frame_errors;
92362306a36Sopenharmony_ci		if (frame_status & RS_FCSERR)
92462306a36Sopenharmony_ci		    ++dev->stats.rx_crc_errors;
92562306a36Sopenharmony_ci	    } else {
92662306a36Sopenharmony_ci		/* Mace feature AUTO_STRIP_RCV is on by default, dropping the
92762306a36Sopenharmony_ci		 * FCS on frames with 802.3 headers. This means that Ethernet
92862306a36Sopenharmony_ci		 * frames have 8 extra octets at the end, while 802.3 frames
92962306a36Sopenharmony_ci		 * have only 4. We need to correctly account for this. */
93062306a36Sopenharmony_ci		if (*(unsigned short *)(data+12) < 1536) /* 802.3 header */
93162306a36Sopenharmony_ci		    nb -= 4;
93262306a36Sopenharmony_ci		else	/* Ethernet header; mace includes FCS */
93362306a36Sopenharmony_ci		    nb -= 8;
93462306a36Sopenharmony_ci		skb_put(skb, nb);
93562306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
93662306a36Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
93762306a36Sopenharmony_ci		netif_rx(skb);
93862306a36Sopenharmony_ci		mp->rx_bufs[i] = NULL;
93962306a36Sopenharmony_ci		++dev->stats.rx_packets;
94062306a36Sopenharmony_ci	    }
94162306a36Sopenharmony_ci	} else {
94262306a36Sopenharmony_ci	    ++dev->stats.rx_errors;
94362306a36Sopenharmony_ci	    ++dev->stats.rx_length_errors;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* advance to next */
94762306a36Sopenharmony_ci	if (++i >= N_RX_RING)
94862306a36Sopenharmony_ci	    i = 0;
94962306a36Sopenharmony_ci    }
95062306a36Sopenharmony_ci    mp->rx_empty = i;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci    i = mp->rx_fill;
95362306a36Sopenharmony_ci    for (;;) {
95462306a36Sopenharmony_ci	next = i + 1;
95562306a36Sopenharmony_ci	if (next >= N_RX_RING)
95662306a36Sopenharmony_ci	    next = 0;
95762306a36Sopenharmony_ci	if (next == mp->rx_empty)
95862306a36Sopenharmony_ci	    break;
95962306a36Sopenharmony_ci	cp = mp->rx_cmds + i;
96062306a36Sopenharmony_ci	skb = mp->rx_bufs[i];
96162306a36Sopenharmony_ci	if (!skb) {
96262306a36Sopenharmony_ci	    skb = netdev_alloc_skb(dev, RX_BUFLEN + 2);
96362306a36Sopenharmony_ci	    if (skb) {
96462306a36Sopenharmony_ci		skb_reserve(skb, 2);
96562306a36Sopenharmony_ci		mp->rx_bufs[i] = skb;
96662306a36Sopenharmony_ci	    }
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci	cp->req_count = cpu_to_le16(RX_BUFLEN);
96962306a36Sopenharmony_ci	data = skb? skb->data: dummy_buf;
97062306a36Sopenharmony_ci	cp->phy_addr = cpu_to_le32(virt_to_bus(data));
97162306a36Sopenharmony_ci	out_le16(&cp->xfer_status, 0);
97262306a36Sopenharmony_ci	out_le16(&cp->command, INPUT_LAST + INTR_ALWAYS);
97362306a36Sopenharmony_ci#if 0
97462306a36Sopenharmony_ci	if ((le32_to_cpu(rd->status) & ACTIVE) != 0) {
97562306a36Sopenharmony_ci	    out_le32(&rd->control, (PAUSE << 16) | PAUSE);
97662306a36Sopenharmony_ci	    while ((in_le32(&rd->status) & ACTIVE) != 0)
97762306a36Sopenharmony_ci		;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci#endif
98062306a36Sopenharmony_ci	i = next;
98162306a36Sopenharmony_ci    }
98262306a36Sopenharmony_ci    if (i != mp->rx_fill) {
98362306a36Sopenharmony_ci	out_le32(&rd->control, ((RUN|WAKE) << 16) | (RUN|WAKE));
98462306a36Sopenharmony_ci	mp->rx_fill = i;
98562306a36Sopenharmony_ci    }
98662306a36Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
98762306a36Sopenharmony_ci    return IRQ_HANDLED;
98862306a36Sopenharmony_ci}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic const struct of_device_id mace_match[] =
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	{
99362306a36Sopenharmony_ci	.name 		= "mace",
99462306a36Sopenharmony_ci	},
99562306a36Sopenharmony_ci	{},
99662306a36Sopenharmony_ci};
99762306a36Sopenharmony_ciMODULE_DEVICE_TABLE (of, mace_match);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic struct macio_driver mace_driver =
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	.driver = {
100262306a36Sopenharmony_ci		.name 		= "mace",
100362306a36Sopenharmony_ci		.owner		= THIS_MODULE,
100462306a36Sopenharmony_ci		.of_match_table	= mace_match,
100562306a36Sopenharmony_ci	},
100662306a36Sopenharmony_ci	.probe		= mace_probe,
100762306a36Sopenharmony_ci	.remove		= mace_remove,
100862306a36Sopenharmony_ci};
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int __init mace_init(void)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	return macio_register_driver(&mace_driver);
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic void __exit mace_cleanup(void)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	macio_unregister_driver(&mace_driver);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	kfree(dummy_buf);
102162306a36Sopenharmony_ci	dummy_buf = NULL;
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras");
102562306a36Sopenharmony_ciMODULE_DESCRIPTION("PowerMac MACE driver.");
102662306a36Sopenharmony_cimodule_param(port_aaui, int, 0);
102762306a36Sopenharmony_ciMODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)");
102862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cimodule_init(mace_init);
103162306a36Sopenharmony_cimodule_exit(mace_cleanup);
1032