18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Network device driver for the MACE ethernet controller on
48c2ecf20Sopenharmony_ci * Apple Powermacs.  Assumes it's under a DBDMA controller.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/string.h>
158c2ecf20Sopenharmony_ci#include <linux/timer.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
188c2ecf20Sopenharmony_ci#include <linux/crc32.h>
198c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
208c2ecf20Sopenharmony_ci#include <linux/bitrev.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
238c2ecf20Sopenharmony_ci#include <asm/prom.h>
248c2ecf20Sopenharmony_ci#include <asm/dbdma.h>
258c2ecf20Sopenharmony_ci#include <asm/io.h>
268c2ecf20Sopenharmony_ci#include <asm/macio.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "mace.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int port_aaui = -1;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define N_RX_RING	8
338c2ecf20Sopenharmony_ci#define N_TX_RING	6
348c2ecf20Sopenharmony_ci#define MAX_TX_ACTIVE	1
358c2ecf20Sopenharmony_ci#define NCMDS_TX	1	/* dma commands per element in tx ring */
368c2ecf20Sopenharmony_ci#define RX_BUFLEN	(ETH_FRAME_LEN + 8)
378c2ecf20Sopenharmony_ci#define TX_TIMEOUT	HZ	/* 1 second */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Chip rev needs workaround on HW & multicast addr change */
408c2ecf20Sopenharmony_ci#define BROKEN_ADDRCHG_REV	0x0941
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Bits in transmit DMA status */
438c2ecf20Sopenharmony_ci#define TX_DMA_ERR	0x80
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct mace_data {
468c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mace;
478c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *tx_dma;
488c2ecf20Sopenharmony_ci    int tx_dma_intr;
498c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *rx_dma;
508c2ecf20Sopenharmony_ci    int rx_dma_intr;
518c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *tx_cmds;	/* xmit dma command list */
528c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *rx_cmds;	/* recv dma command list */
538c2ecf20Sopenharmony_ci    struct sk_buff *rx_bufs[N_RX_RING];
548c2ecf20Sopenharmony_ci    int rx_fill;
558c2ecf20Sopenharmony_ci    int rx_empty;
568c2ecf20Sopenharmony_ci    struct sk_buff *tx_bufs[N_TX_RING];
578c2ecf20Sopenharmony_ci    int tx_fill;
588c2ecf20Sopenharmony_ci    int tx_empty;
598c2ecf20Sopenharmony_ci    unsigned char maccc;
608c2ecf20Sopenharmony_ci    unsigned char tx_fullup;
618c2ecf20Sopenharmony_ci    unsigned char tx_active;
628c2ecf20Sopenharmony_ci    unsigned char tx_bad_runt;
638c2ecf20Sopenharmony_ci    struct timer_list tx_timeout;
648c2ecf20Sopenharmony_ci    int timeout_active;
658c2ecf20Sopenharmony_ci    int port_aaui;
668c2ecf20Sopenharmony_ci    int chipid;
678c2ecf20Sopenharmony_ci    struct macio_dev *mdev;
688c2ecf20Sopenharmony_ci    spinlock_t lock;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci * Number of bytes of private data per MACE: allow enough for
738c2ecf20Sopenharmony_ci * the rx and tx dma commands plus a branch dma command each,
748c2ecf20Sopenharmony_ci * and another 16 bytes to allow us to align the dma command
758c2ecf20Sopenharmony_ci * buffers on a 16 byte boundary.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_ci#define PRIV_BYTES	(sizeof(struct mace_data) \
788c2ecf20Sopenharmony_ci	+ (N_RX_RING + NCMDS_TX * N_TX_RING + 3) * sizeof(struct dbdma_cmd))
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int mace_open(struct net_device *dev);
818c2ecf20Sopenharmony_cistatic int mace_close(struct net_device *dev);
828c2ecf20Sopenharmony_cistatic netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev);
838c2ecf20Sopenharmony_cistatic void mace_set_multicast(struct net_device *dev);
848c2ecf20Sopenharmony_cistatic void mace_reset(struct net_device *dev);
858c2ecf20Sopenharmony_cistatic int mace_set_address(struct net_device *dev, void *addr);
868c2ecf20Sopenharmony_cistatic irqreturn_t mace_interrupt(int irq, void *dev_id);
878c2ecf20Sopenharmony_cistatic irqreturn_t mace_txdma_intr(int irq, void *dev_id);
888c2ecf20Sopenharmony_cistatic irqreturn_t mace_rxdma_intr(int irq, void *dev_id);
898c2ecf20Sopenharmony_cistatic void mace_set_timeout(struct net_device *dev);
908c2ecf20Sopenharmony_cistatic void mace_tx_timeout(struct timer_list *t);
918c2ecf20Sopenharmony_cistatic inline void dbdma_reset(volatile struct dbdma_regs __iomem *dma);
928c2ecf20Sopenharmony_cistatic inline void mace_clean_rings(struct mace_data *mp);
938c2ecf20Sopenharmony_cistatic void __mace_set_address(struct net_device *dev, void *addr);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*
968c2ecf20Sopenharmony_ci * If we can't get a skbuff when we need it, we use this area for DMA.
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic unsigned char *dummy_buf;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic const struct net_device_ops mace_netdev_ops = {
1018c2ecf20Sopenharmony_ci	.ndo_open		= mace_open,
1028c2ecf20Sopenharmony_ci	.ndo_stop		= mace_close,
1038c2ecf20Sopenharmony_ci	.ndo_start_xmit		= mace_xmit_start,
1048c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= mace_set_multicast,
1058c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= mace_set_address,
1068c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int mace_probe(struct macio_dev *mdev, const struct of_device_id *match)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct device_node *mace = macio_get_of_node(mdev);
1128c2ecf20Sopenharmony_ci	struct net_device *dev;
1138c2ecf20Sopenharmony_ci	struct mace_data *mp;
1148c2ecf20Sopenharmony_ci	const unsigned char *addr;
1158c2ecf20Sopenharmony_ci	int j, rev, rc = -EBUSY;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) {
1188c2ecf20Sopenharmony_ci		printk(KERN_ERR "can't use MACE %pOF: need 3 addrs and 3 irqs\n",
1198c2ecf20Sopenharmony_ci		       mace);
1208c2ecf20Sopenharmony_ci		return -ENODEV;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	addr = of_get_property(mace, "mac-address", NULL);
1248c2ecf20Sopenharmony_ci	if (addr == NULL) {
1258c2ecf20Sopenharmony_ci		addr = of_get_property(mace, "local-mac-address", NULL);
1268c2ecf20Sopenharmony_ci		if (addr == NULL) {
1278c2ecf20Sopenharmony_ci			printk(KERN_ERR "Can't get mac-address for MACE %pOF\n",
1288c2ecf20Sopenharmony_ci			       mace);
1298c2ecf20Sopenharmony_ci			return -ENODEV;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/*
1348c2ecf20Sopenharmony_ci	 * lazy allocate the driver-wide dummy buffer. (Note that we
1358c2ecf20Sopenharmony_ci	 * never have more than one MACE in the system anyway)
1368c2ecf20Sopenharmony_ci	 */
1378c2ecf20Sopenharmony_ci	if (dummy_buf == NULL) {
1388c2ecf20Sopenharmony_ci		dummy_buf = kmalloc(RX_BUFLEN+2, GFP_KERNEL);
1398c2ecf20Sopenharmony_ci		if (dummy_buf == NULL)
1408c2ecf20Sopenharmony_ci			return -ENOMEM;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (macio_request_resources(mdev, "mace")) {
1448c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't request IO resources !\n");
1458c2ecf20Sopenharmony_ci		return -EBUSY;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	dev = alloc_etherdev(PRIV_BYTES);
1498c2ecf20Sopenharmony_ci	if (!dev) {
1508c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1518c2ecf20Sopenharmony_ci		goto err_release;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &mdev->ofdev.dev);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	mp = netdev_priv(dev);
1568c2ecf20Sopenharmony_ci	mp->mdev = mdev;
1578c2ecf20Sopenharmony_ci	macio_set_drvdata(mdev, dev);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	dev->base_addr = macio_resource_start(mdev, 0);
1608c2ecf20Sopenharmony_ci	mp->mace = ioremap(dev->base_addr, 0x1000);
1618c2ecf20Sopenharmony_ci	if (mp->mace == NULL) {
1628c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't map IO resources !\n");
1638c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1648c2ecf20Sopenharmony_ci		goto err_free;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci	dev->irq = macio_irq(mdev, 0);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	rev = addr[0] == 0 && addr[1] == 0xA0;
1698c2ecf20Sopenharmony_ci	for (j = 0; j < 6; ++j) {
1708c2ecf20Sopenharmony_ci		dev->dev_addr[j] = rev ? bitrev8(addr[j]): addr[j];
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) |
1738c2ecf20Sopenharmony_ci			in_8(&mp->mace->chipid_lo);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	mp = netdev_priv(dev);
1778c2ecf20Sopenharmony_ci	mp->maccc = ENXMT | ENRCV;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	mp->tx_dma = ioremap(macio_resource_start(mdev, 1), 0x1000);
1808c2ecf20Sopenharmony_ci	if (mp->tx_dma == NULL) {
1818c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't map TX DMA resources !\n");
1828c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1838c2ecf20Sopenharmony_ci		goto err_unmap_io;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	mp->tx_dma_intr = macio_irq(mdev, 1);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	mp->rx_dma = ioremap(macio_resource_start(mdev, 2), 0x1000);
1888c2ecf20Sopenharmony_ci	if (mp->rx_dma == NULL) {
1898c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't map RX DMA resources !\n");
1908c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1918c2ecf20Sopenharmony_ci		goto err_unmap_tx_dma;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci	mp->rx_dma_intr = macio_irq(mdev, 2);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	mp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(mp + 1);
1968c2ecf20Sopenharmony_ci	mp->rx_cmds = mp->tx_cmds + NCMDS_TX * N_TX_RING + 1;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	memset((char *) mp->tx_cmds, 0,
1998c2ecf20Sopenharmony_ci	       (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
2008c2ecf20Sopenharmony_ci	timer_setup(&mp->tx_timeout, mace_tx_timeout, 0);
2018c2ecf20Sopenharmony_ci	spin_lock_init(&mp->lock);
2028c2ecf20Sopenharmony_ci	mp->timeout_active = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (port_aaui >= 0)
2058c2ecf20Sopenharmony_ci		mp->port_aaui = port_aaui;
2068c2ecf20Sopenharmony_ci	else {
2078c2ecf20Sopenharmony_ci		/* Apple Network Server uses the AAUI port */
2088c2ecf20Sopenharmony_ci		if (of_machine_is_compatible("AAPL,ShinerESB"))
2098c2ecf20Sopenharmony_ci			mp->port_aaui = 1;
2108c2ecf20Sopenharmony_ci		else {
2118c2ecf20Sopenharmony_ci#ifdef CONFIG_MACE_AAUI_PORT
2128c2ecf20Sopenharmony_ci			mp->port_aaui = 1;
2138c2ecf20Sopenharmony_ci#else
2148c2ecf20Sopenharmony_ci			mp->port_aaui = 0;
2158c2ecf20Sopenharmony_ci#endif
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	dev->netdev_ops = &mace_netdev_ops;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/*
2228c2ecf20Sopenharmony_ci	 * Most of what is below could be moved to mace_open()
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	mace_reset(dev);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	rc = request_irq(dev->irq, mace_interrupt, 0, "MACE", dev);
2278c2ecf20Sopenharmony_ci	if (rc) {
2288c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq);
2298c2ecf20Sopenharmony_ci		goto err_unmap_rx_dma;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	rc = request_irq(mp->tx_dma_intr, mace_txdma_intr, 0, "MACE-txdma", dev);
2328c2ecf20Sopenharmony_ci	if (rc) {
2338c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't get irq %d\n", mp->tx_dma_intr);
2348c2ecf20Sopenharmony_ci		goto err_free_irq;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci	rc = request_irq(mp->rx_dma_intr, mace_rxdma_intr, 0, "MACE-rxdma", dev);
2378c2ecf20Sopenharmony_ci	if (rc) {
2388c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: can't get irq %d\n", mp->rx_dma_intr);
2398c2ecf20Sopenharmony_ci		goto err_free_tx_irq;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	rc = register_netdev(dev);
2438c2ecf20Sopenharmony_ci	if (rc) {
2448c2ecf20Sopenharmony_ci		printk(KERN_ERR "MACE: Cannot register net device, aborting.\n");
2458c2ecf20Sopenharmony_ci		goto err_free_rx_irq;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: MACE at %pM, chip revision %d.%d\n",
2498c2ecf20Sopenharmony_ci	       dev->name, dev->dev_addr,
2508c2ecf20Sopenharmony_ci	       mp->chipid >> 8, mp->chipid & 0xff);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return 0;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci err_free_rx_irq:
2558c2ecf20Sopenharmony_ci	free_irq(macio_irq(mdev, 2), dev);
2568c2ecf20Sopenharmony_ci err_free_tx_irq:
2578c2ecf20Sopenharmony_ci	free_irq(macio_irq(mdev, 1), dev);
2588c2ecf20Sopenharmony_ci err_free_irq:
2598c2ecf20Sopenharmony_ci	free_irq(macio_irq(mdev, 0), dev);
2608c2ecf20Sopenharmony_ci err_unmap_rx_dma:
2618c2ecf20Sopenharmony_ci	iounmap(mp->rx_dma);
2628c2ecf20Sopenharmony_ci err_unmap_tx_dma:
2638c2ecf20Sopenharmony_ci	iounmap(mp->tx_dma);
2648c2ecf20Sopenharmony_ci err_unmap_io:
2658c2ecf20Sopenharmony_ci	iounmap(mp->mace);
2668c2ecf20Sopenharmony_ci err_free:
2678c2ecf20Sopenharmony_ci	free_netdev(dev);
2688c2ecf20Sopenharmony_ci err_release:
2698c2ecf20Sopenharmony_ci	macio_release_resources(mdev);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return rc;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int mace_remove(struct macio_dev *mdev)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct net_device *dev = macio_get_drvdata(mdev);
2778c2ecf20Sopenharmony_ci	struct mace_data *mp;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	BUG_ON(dev == NULL);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	macio_set_drvdata(mdev, NULL);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	mp = netdev_priv(dev);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	unregister_netdev(dev);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
2888c2ecf20Sopenharmony_ci	free_irq(mp->tx_dma_intr, dev);
2898c2ecf20Sopenharmony_ci	free_irq(mp->rx_dma_intr, dev);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	iounmap(mp->rx_dma);
2928c2ecf20Sopenharmony_ci	iounmap(mp->tx_dma);
2938c2ecf20Sopenharmony_ci	iounmap(mp->mace);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	free_netdev(dev);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	macio_release_resources(mdev);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return 0;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void dbdma_reset(volatile struct dbdma_regs __iomem *dma)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci    int i;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci    out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci    /*
3098c2ecf20Sopenharmony_ci     * Yes this looks peculiar, but apparently it needs to be this
3108c2ecf20Sopenharmony_ci     * way on some machines.
3118c2ecf20Sopenharmony_ci     */
3128c2ecf20Sopenharmony_ci    for (i = 200; i > 0; --i)
3138c2ecf20Sopenharmony_ci	if (le32_to_cpu(dma->control) & RUN)
3148c2ecf20Sopenharmony_ci	    udelay(1);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void mace_reset(struct net_device *dev)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
3208c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
3218c2ecf20Sopenharmony_ci    int i;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci    /* soft-reset the chip */
3248c2ecf20Sopenharmony_ci    i = 200;
3258c2ecf20Sopenharmony_ci    while (--i) {
3268c2ecf20Sopenharmony_ci	out_8(&mb->biucc, SWRST);
3278c2ecf20Sopenharmony_ci	if (in_8(&mb->biucc) & SWRST) {
3288c2ecf20Sopenharmony_ci	    udelay(10);
3298c2ecf20Sopenharmony_ci	    continue;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	break;
3328c2ecf20Sopenharmony_ci    }
3338c2ecf20Sopenharmony_ci    if (!i) {
3348c2ecf20Sopenharmony_ci	printk(KERN_ERR "mace: cannot reset chip!\n");
3358c2ecf20Sopenharmony_ci	return;
3368c2ecf20Sopenharmony_ci    }
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci    out_8(&mb->imr, 0xff);	/* disable all intrs for now */
3398c2ecf20Sopenharmony_ci    i = in_8(&mb->ir);
3408c2ecf20Sopenharmony_ci    out_8(&mb->maccc, 0);	/* turn off tx, rx */
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci    out_8(&mb->biucc, XMTSP_64);
3438c2ecf20Sopenharmony_ci    out_8(&mb->utr, RTRD);
3448c2ecf20Sopenharmony_ci    out_8(&mb->fifocc, RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST);
3458c2ecf20Sopenharmony_ci    out_8(&mb->xmtfc, AUTO_PAD_XMIT); /* auto-pad short frames */
3468c2ecf20Sopenharmony_ci    out_8(&mb->rcvfc, 0);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci    /* load up the hardware address */
3498c2ecf20Sopenharmony_ci    __mace_set_address(dev, dev->dev_addr);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci    /* clear the multicast filter */
3528c2ecf20Sopenharmony_ci    if (mp->chipid == BROKEN_ADDRCHG_REV)
3538c2ecf20Sopenharmony_ci	out_8(&mb->iac, LOGADDR);
3548c2ecf20Sopenharmony_ci    else {
3558c2ecf20Sopenharmony_ci	out_8(&mb->iac, ADDRCHG | LOGADDR);
3568c2ecf20Sopenharmony_ci	while ((in_8(&mb->iac) & ADDRCHG) != 0)
3578c2ecf20Sopenharmony_ci		;
3588c2ecf20Sopenharmony_ci    }
3598c2ecf20Sopenharmony_ci    for (i = 0; i < 8; ++i)
3608c2ecf20Sopenharmony_ci	out_8(&mb->ladrf, 0);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci    /* done changing address */
3638c2ecf20Sopenharmony_ci    if (mp->chipid != BROKEN_ADDRCHG_REV)
3648c2ecf20Sopenharmony_ci	out_8(&mb->iac, 0);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci    if (mp->port_aaui)
3678c2ecf20Sopenharmony_ci    	out_8(&mb->plscc, PORTSEL_AUI + ENPLSIO);
3688c2ecf20Sopenharmony_ci    else
3698c2ecf20Sopenharmony_ci    	out_8(&mb->plscc, PORTSEL_GPSI + ENPLSIO);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void __mace_set_address(struct net_device *dev, void *addr)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
3758c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
3768c2ecf20Sopenharmony_ci    unsigned char *p = addr;
3778c2ecf20Sopenharmony_ci    int i;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci    /* load up the hardware address */
3808c2ecf20Sopenharmony_ci    if (mp->chipid == BROKEN_ADDRCHG_REV)
3818c2ecf20Sopenharmony_ci    	out_8(&mb->iac, PHYADDR);
3828c2ecf20Sopenharmony_ci    else {
3838c2ecf20Sopenharmony_ci    	out_8(&mb->iac, ADDRCHG | PHYADDR);
3848c2ecf20Sopenharmony_ci	while ((in_8(&mb->iac) & ADDRCHG) != 0)
3858c2ecf20Sopenharmony_ci	    ;
3868c2ecf20Sopenharmony_ci    }
3878c2ecf20Sopenharmony_ci    for (i = 0; i < 6; ++i)
3888c2ecf20Sopenharmony_ci	out_8(&mb->padr, dev->dev_addr[i] = p[i]);
3898c2ecf20Sopenharmony_ci    if (mp->chipid != BROKEN_ADDRCHG_REV)
3908c2ecf20Sopenharmony_ci        out_8(&mb->iac, 0);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int mace_set_address(struct net_device *dev, void *addr)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
3968c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
3978c2ecf20Sopenharmony_ci    unsigned long flags;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci    __mace_set_address(dev, addr);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci    /* note: setting ADDRCHG clears ENRCV */
4048c2ecf20Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
4078c2ecf20Sopenharmony_ci    return 0;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic inline void mace_clean_rings(struct mace_data *mp)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci    int i;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci    /* free some skb's */
4158c2ecf20Sopenharmony_ci    for (i = 0; i < N_RX_RING; ++i) {
4168c2ecf20Sopenharmony_ci	if (mp->rx_bufs[i] != NULL) {
4178c2ecf20Sopenharmony_ci	    dev_kfree_skb(mp->rx_bufs[i]);
4188c2ecf20Sopenharmony_ci	    mp->rx_bufs[i] = NULL;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci    }
4218c2ecf20Sopenharmony_ci    for (i = mp->tx_empty; i != mp->tx_fill; ) {
4228c2ecf20Sopenharmony_ci	dev_kfree_skb(mp->tx_bufs[i]);
4238c2ecf20Sopenharmony_ci	if (++i >= N_TX_RING)
4248c2ecf20Sopenharmony_ci	    i = 0;
4258c2ecf20Sopenharmony_ci    }
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic int mace_open(struct net_device *dev)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
4318c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
4328c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
4338c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
4348c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *cp;
4358c2ecf20Sopenharmony_ci    int i;
4368c2ecf20Sopenharmony_ci    struct sk_buff *skb;
4378c2ecf20Sopenharmony_ci    unsigned char *data;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci    /* reset the chip */
4408c2ecf20Sopenharmony_ci    mace_reset(dev);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci    /* initialize list of sk_buffs for receiving and set up recv dma */
4438c2ecf20Sopenharmony_ci    mace_clean_rings(mp);
4448c2ecf20Sopenharmony_ci    memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd));
4458c2ecf20Sopenharmony_ci    cp = mp->rx_cmds;
4468c2ecf20Sopenharmony_ci    for (i = 0; i < N_RX_RING - 1; ++i) {
4478c2ecf20Sopenharmony_ci	skb = netdev_alloc_skb(dev, RX_BUFLEN + 2);
4488c2ecf20Sopenharmony_ci	if (!skb) {
4498c2ecf20Sopenharmony_ci	    data = dummy_buf;
4508c2ecf20Sopenharmony_ci	} else {
4518c2ecf20Sopenharmony_ci	    skb_reserve(skb, 2);	/* so IP header lands on 4-byte bdry */
4528c2ecf20Sopenharmony_ci	    data = skb->data;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci	mp->rx_bufs[i] = skb;
4558c2ecf20Sopenharmony_ci	cp->req_count = cpu_to_le16(RX_BUFLEN);
4568c2ecf20Sopenharmony_ci	cp->command = cpu_to_le16(INPUT_LAST + INTR_ALWAYS);
4578c2ecf20Sopenharmony_ci	cp->phy_addr = cpu_to_le32(virt_to_bus(data));
4588c2ecf20Sopenharmony_ci	cp->xfer_status = 0;
4598c2ecf20Sopenharmony_ci	++cp;
4608c2ecf20Sopenharmony_ci    }
4618c2ecf20Sopenharmony_ci    mp->rx_bufs[i] = NULL;
4628c2ecf20Sopenharmony_ci    cp->command = cpu_to_le16(DBDMA_STOP);
4638c2ecf20Sopenharmony_ci    mp->rx_fill = i;
4648c2ecf20Sopenharmony_ci    mp->rx_empty = 0;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci    /* Put a branch back to the beginning of the receive command list */
4678c2ecf20Sopenharmony_ci    ++cp;
4688c2ecf20Sopenharmony_ci    cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
4698c2ecf20Sopenharmony_ci    cp->cmd_dep = cpu_to_le32(virt_to_bus(mp->rx_cmds));
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci    /* start rx dma */
4728c2ecf20Sopenharmony_ci    out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
4738c2ecf20Sopenharmony_ci    out_le32(&rd->cmdptr, virt_to_bus(mp->rx_cmds));
4748c2ecf20Sopenharmony_ci    out_le32(&rd->control, (RUN << 16) | RUN);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci    /* put a branch at the end of the tx command list */
4778c2ecf20Sopenharmony_ci    cp = mp->tx_cmds + NCMDS_TX * N_TX_RING;
4788c2ecf20Sopenharmony_ci    cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS);
4798c2ecf20Sopenharmony_ci    cp->cmd_dep = cpu_to_le32(virt_to_bus(mp->tx_cmds));
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci    /* reset tx dma */
4828c2ecf20Sopenharmony_ci    out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
4838c2ecf20Sopenharmony_ci    out_le32(&td->cmdptr, virt_to_bus(mp->tx_cmds));
4848c2ecf20Sopenharmony_ci    mp->tx_fill = 0;
4858c2ecf20Sopenharmony_ci    mp->tx_empty = 0;
4868c2ecf20Sopenharmony_ci    mp->tx_fullup = 0;
4878c2ecf20Sopenharmony_ci    mp->tx_active = 0;
4888c2ecf20Sopenharmony_ci    mp->tx_bad_runt = 0;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci    /* turn it on! */
4918c2ecf20Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
4928c2ecf20Sopenharmony_ci    /* enable all interrupts except receive interrupts */
4938c2ecf20Sopenharmony_ci    out_8(&mb->imr, RCVINT);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci    return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int mace_close(struct net_device *dev)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
5018c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
5028c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
5038c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci    /* disable rx and tx */
5068c2ecf20Sopenharmony_ci    out_8(&mb->maccc, 0);
5078c2ecf20Sopenharmony_ci    out_8(&mb->imr, 0xff);		/* disable all intrs */
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci    /* disable rx and tx dma */
5108c2ecf20Sopenharmony_ci    rd->control = cpu_to_le32((RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
5118c2ecf20Sopenharmony_ci    td->control = cpu_to_le32((RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci    mace_clean_rings(mp);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci    return 0;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic inline void mace_set_timeout(struct net_device *dev)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci    if (mp->timeout_active)
5238c2ecf20Sopenharmony_ci	del_timer(&mp->tx_timeout);
5248c2ecf20Sopenharmony_ci    mp->tx_timeout.expires = jiffies + TX_TIMEOUT;
5258c2ecf20Sopenharmony_ci    add_timer(&mp->tx_timeout);
5268c2ecf20Sopenharmony_ci    mp->timeout_active = 1;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
5328c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
5338c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *cp, *np;
5348c2ecf20Sopenharmony_ci    unsigned long flags;
5358c2ecf20Sopenharmony_ci    int fill, next, len;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci    /* see if there's a free slot in the tx ring */
5388c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
5398c2ecf20Sopenharmony_ci    fill = mp->tx_fill;
5408c2ecf20Sopenharmony_ci    next = fill + 1;
5418c2ecf20Sopenharmony_ci    if (next >= N_TX_RING)
5428c2ecf20Sopenharmony_ci	next = 0;
5438c2ecf20Sopenharmony_ci    if (next == mp->tx_empty) {
5448c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
5458c2ecf20Sopenharmony_ci	mp->tx_fullup = 1;
5468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mp->lock, flags);
5478c2ecf20Sopenharmony_ci	return NETDEV_TX_BUSY;		/* can't take it at the moment */
5488c2ecf20Sopenharmony_ci    }
5498c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci    /* partially fill in the dma command block */
5528c2ecf20Sopenharmony_ci    len = skb->len;
5538c2ecf20Sopenharmony_ci    if (len > ETH_FRAME_LEN) {
5548c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "mace: xmit frame too long (%d)\n", len);
5558c2ecf20Sopenharmony_ci	len = ETH_FRAME_LEN;
5568c2ecf20Sopenharmony_ci    }
5578c2ecf20Sopenharmony_ci    mp->tx_bufs[fill] = skb;
5588c2ecf20Sopenharmony_ci    cp = mp->tx_cmds + NCMDS_TX * fill;
5598c2ecf20Sopenharmony_ci    cp->req_count = cpu_to_le16(len);
5608c2ecf20Sopenharmony_ci    cp->phy_addr = cpu_to_le32(virt_to_bus(skb->data));
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci    np = mp->tx_cmds + NCMDS_TX * next;
5638c2ecf20Sopenharmony_ci    out_le16(&np->command, DBDMA_STOP);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci    /* poke the tx dma channel */
5668c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
5678c2ecf20Sopenharmony_ci    mp->tx_fill = next;
5688c2ecf20Sopenharmony_ci    if (!mp->tx_bad_runt && mp->tx_active < MAX_TX_ACTIVE) {
5698c2ecf20Sopenharmony_ci	out_le16(&cp->xfer_status, 0);
5708c2ecf20Sopenharmony_ci	out_le16(&cp->command, OUTPUT_LAST);
5718c2ecf20Sopenharmony_ci	out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
5728c2ecf20Sopenharmony_ci	++mp->tx_active;
5738c2ecf20Sopenharmony_ci	mace_set_timeout(dev);
5748c2ecf20Sopenharmony_ci    }
5758c2ecf20Sopenharmony_ci    if (++next >= N_TX_RING)
5768c2ecf20Sopenharmony_ci	next = 0;
5778c2ecf20Sopenharmony_ci    if (next == mp->tx_empty)
5788c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
5798c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci    return NETDEV_TX_OK;
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic void mace_set_multicast(struct net_device *dev)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
5878c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
5888c2ecf20Sopenharmony_ci    int i;
5898c2ecf20Sopenharmony_ci    u32 crc;
5908c2ecf20Sopenharmony_ci    unsigned long flags;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
5938c2ecf20Sopenharmony_ci    mp->maccc &= ~PROM;
5948c2ecf20Sopenharmony_ci    if (dev->flags & IFF_PROMISC) {
5958c2ecf20Sopenharmony_ci	mp->maccc |= PROM;
5968c2ecf20Sopenharmony_ci    } else {
5978c2ecf20Sopenharmony_ci	unsigned char multicast_filter[8];
5988c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI) {
6018c2ecf20Sopenharmony_ci	    for (i = 0; i < 8; i++)
6028c2ecf20Sopenharmony_ci		multicast_filter[i] = 0xff;
6038c2ecf20Sopenharmony_ci	} else {
6048c2ecf20Sopenharmony_ci	    for (i = 0; i < 8; i++)
6058c2ecf20Sopenharmony_ci		multicast_filter[i] = 0;
6068c2ecf20Sopenharmony_ci	    netdev_for_each_mc_addr(ha, dev) {
6078c2ecf20Sopenharmony_ci	        crc = ether_crc_le(6, ha->addr);
6088c2ecf20Sopenharmony_ci		i = crc >> 26;	/* bit number in multicast_filter */
6098c2ecf20Sopenharmony_ci		multicast_filter[i >> 3] |= 1 << (i & 7);
6108c2ecf20Sopenharmony_ci	    }
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci#if 0
6138c2ecf20Sopenharmony_ci	printk("Multicast filter :");
6148c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
6158c2ecf20Sopenharmony_ci	    printk("%02x ", multicast_filter[i]);
6168c2ecf20Sopenharmony_ci	printk("\n");
6178c2ecf20Sopenharmony_ci#endif
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (mp->chipid == BROKEN_ADDRCHG_REV)
6208c2ecf20Sopenharmony_ci	    out_8(&mb->iac, LOGADDR);
6218c2ecf20Sopenharmony_ci	else {
6228c2ecf20Sopenharmony_ci	    out_8(&mb->iac, ADDRCHG | LOGADDR);
6238c2ecf20Sopenharmony_ci	    while ((in_8(&mb->iac) & ADDRCHG) != 0)
6248c2ecf20Sopenharmony_ci		;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci	for (i = 0; i < 8; ++i)
6278c2ecf20Sopenharmony_ci	    out_8(&mb->ladrf, multicast_filter[i]);
6288c2ecf20Sopenharmony_ci	if (mp->chipid != BROKEN_ADDRCHG_REV)
6298c2ecf20Sopenharmony_ci	    out_8(&mb->iac, 0);
6308c2ecf20Sopenharmony_ci    }
6318c2ecf20Sopenharmony_ci    /* reset maccc */
6328c2ecf20Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
6338c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic void mace_handle_misc_intrs(struct mace_data *mp, int intr, struct net_device *dev)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
6398c2ecf20Sopenharmony_ci    static int mace_babbles, mace_jabbers;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci    if (intr & MPCO)
6428c2ecf20Sopenharmony_ci	dev->stats.rx_missed_errors += 256;
6438c2ecf20Sopenharmony_ci    dev->stats.rx_missed_errors += in_8(&mb->mpc);   /* reading clears it */
6448c2ecf20Sopenharmony_ci    if (intr & RNTPCO)
6458c2ecf20Sopenharmony_ci	dev->stats.rx_length_errors += 256;
6468c2ecf20Sopenharmony_ci    dev->stats.rx_length_errors += in_8(&mb->rntpc); /* reading clears it */
6478c2ecf20Sopenharmony_ci    if (intr & CERR)
6488c2ecf20Sopenharmony_ci	++dev->stats.tx_heartbeat_errors;
6498c2ecf20Sopenharmony_ci    if (intr & BABBLE)
6508c2ecf20Sopenharmony_ci	if (mace_babbles++ < 4)
6518c2ecf20Sopenharmony_ci	    printk(KERN_DEBUG "mace: babbling transmitter\n");
6528c2ecf20Sopenharmony_ci    if (intr & JABBER)
6538c2ecf20Sopenharmony_ci	if (mace_jabbers++ < 4)
6548c2ecf20Sopenharmony_ci	    printk(KERN_DEBUG "mace: jabbering transceiver\n");
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic irqreturn_t mace_interrupt(int irq, void *dev_id)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci    struct net_device *dev = (struct net_device *) dev_id;
6608c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
6618c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
6628c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
6638c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *cp;
6648c2ecf20Sopenharmony_ci    int intr, fs, i, stat, x;
6658c2ecf20Sopenharmony_ci    int xcount, dstat;
6668c2ecf20Sopenharmony_ci    unsigned long flags;
6678c2ecf20Sopenharmony_ci    /* static int mace_last_fs, mace_last_xcount; */
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
6708c2ecf20Sopenharmony_ci    intr = in_8(&mb->ir);		/* read interrupt register */
6718c2ecf20Sopenharmony_ci    in_8(&mb->xmtrc);			/* get retries */
6728c2ecf20Sopenharmony_ci    mace_handle_misc_intrs(mp, intr, dev);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci    i = mp->tx_empty;
6758c2ecf20Sopenharmony_ci    while (in_8(&mb->pr) & XMTSV) {
6768c2ecf20Sopenharmony_ci	del_timer(&mp->tx_timeout);
6778c2ecf20Sopenharmony_ci	mp->timeout_active = 0;
6788c2ecf20Sopenharmony_ci	/*
6798c2ecf20Sopenharmony_ci	 * Clear any interrupt indication associated with this status
6808c2ecf20Sopenharmony_ci	 * word.  This appears to unlatch any error indication from
6818c2ecf20Sopenharmony_ci	 * the DMA controller.
6828c2ecf20Sopenharmony_ci	 */
6838c2ecf20Sopenharmony_ci	intr = in_8(&mb->ir);
6848c2ecf20Sopenharmony_ci	if (intr != 0)
6858c2ecf20Sopenharmony_ci	    mace_handle_misc_intrs(mp, intr, dev);
6868c2ecf20Sopenharmony_ci	if (mp->tx_bad_runt) {
6878c2ecf20Sopenharmony_ci	    fs = in_8(&mb->xmtfs);
6888c2ecf20Sopenharmony_ci	    mp->tx_bad_runt = 0;
6898c2ecf20Sopenharmony_ci	    out_8(&mb->xmtfc, AUTO_PAD_XMIT);
6908c2ecf20Sopenharmony_ci	    continue;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci	dstat = le32_to_cpu(td->status);
6938c2ecf20Sopenharmony_ci	/* stop DMA controller */
6948c2ecf20Sopenharmony_ci	out_le32(&td->control, RUN << 16);
6958c2ecf20Sopenharmony_ci	/*
6968c2ecf20Sopenharmony_ci	 * xcount is the number of complete frames which have been
6978c2ecf20Sopenharmony_ci	 * written to the fifo but for which status has not been read.
6988c2ecf20Sopenharmony_ci	 */
6998c2ecf20Sopenharmony_ci	xcount = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK;
7008c2ecf20Sopenharmony_ci	if (xcount == 0 || (dstat & DEAD)) {
7018c2ecf20Sopenharmony_ci	    /*
7028c2ecf20Sopenharmony_ci	     * If a packet was aborted before the DMA controller has
7038c2ecf20Sopenharmony_ci	     * finished transferring it, it seems that there are 2 bytes
7048c2ecf20Sopenharmony_ci	     * which are stuck in some buffer somewhere.  These will get
7058c2ecf20Sopenharmony_ci	     * transmitted as soon as we read the frame status (which
7068c2ecf20Sopenharmony_ci	     * reenables the transmit data transfer request).  Turning
7078c2ecf20Sopenharmony_ci	     * off the DMA controller and/or resetting the MACE doesn't
7088c2ecf20Sopenharmony_ci	     * help.  So we disable auto-padding and FCS transmission
7098c2ecf20Sopenharmony_ci	     * so the two bytes will only be a runt packet which should
7108c2ecf20Sopenharmony_ci	     * be ignored by other stations.
7118c2ecf20Sopenharmony_ci	     */
7128c2ecf20Sopenharmony_ci	    out_8(&mb->xmtfc, DXMTFCS);
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci	fs = in_8(&mb->xmtfs);
7158c2ecf20Sopenharmony_ci	if ((fs & XMTSV) == 0) {
7168c2ecf20Sopenharmony_ci	    printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n",
7178c2ecf20Sopenharmony_ci		   fs, xcount, dstat);
7188c2ecf20Sopenharmony_ci	    mace_reset(dev);
7198c2ecf20Sopenharmony_ci		/*
7208c2ecf20Sopenharmony_ci		 * XXX mace likes to hang the machine after a xmtfs error.
7218c2ecf20Sopenharmony_ci		 * This is hard to reproduce, resetting *may* help
7228c2ecf20Sopenharmony_ci		 */
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci	cp = mp->tx_cmds + NCMDS_TX * i;
7258c2ecf20Sopenharmony_ci	stat = le16_to_cpu(cp->xfer_status);
7268c2ecf20Sopenharmony_ci	if ((fs & (UFLO|LCOL|LCAR|RTRY)) || (dstat & DEAD) || xcount == 0) {
7278c2ecf20Sopenharmony_ci	    /*
7288c2ecf20Sopenharmony_ci	     * Check whether there were in fact 2 bytes written to
7298c2ecf20Sopenharmony_ci	     * the transmit FIFO.
7308c2ecf20Sopenharmony_ci	     */
7318c2ecf20Sopenharmony_ci	    udelay(1);
7328c2ecf20Sopenharmony_ci	    x = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK;
7338c2ecf20Sopenharmony_ci	    if (x != 0) {
7348c2ecf20Sopenharmony_ci		/* there were two bytes with an end-of-packet indication */
7358c2ecf20Sopenharmony_ci		mp->tx_bad_runt = 1;
7368c2ecf20Sopenharmony_ci		mace_set_timeout(dev);
7378c2ecf20Sopenharmony_ci	    } else {
7388c2ecf20Sopenharmony_ci		/*
7398c2ecf20Sopenharmony_ci		 * Either there weren't the two bytes buffered up, or they
7408c2ecf20Sopenharmony_ci		 * didn't have an end-of-packet indication.
7418c2ecf20Sopenharmony_ci		 * We flush the transmit FIFO just in case (by setting the
7428c2ecf20Sopenharmony_ci		 * XMTFWU bit with the transmitter disabled).
7438c2ecf20Sopenharmony_ci		 */
7448c2ecf20Sopenharmony_ci		out_8(&mb->maccc, in_8(&mb->maccc) & ~ENXMT);
7458c2ecf20Sopenharmony_ci		out_8(&mb->fifocc, in_8(&mb->fifocc) | XMTFWU);
7468c2ecf20Sopenharmony_ci		udelay(1);
7478c2ecf20Sopenharmony_ci		out_8(&mb->maccc, in_8(&mb->maccc) | ENXMT);
7488c2ecf20Sopenharmony_ci		out_8(&mb->xmtfc, AUTO_PAD_XMIT);
7498c2ecf20Sopenharmony_ci	    }
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci	/* dma should have finished */
7528c2ecf20Sopenharmony_ci	if (i == mp->tx_fill) {
7538c2ecf20Sopenharmony_ci	    printk(KERN_DEBUG "mace: tx ring ran out? (fs=%x xc=%d ds=%x)\n",
7548c2ecf20Sopenharmony_ci		   fs, xcount, dstat);
7558c2ecf20Sopenharmony_ci	    continue;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci	/* Update stats */
7588c2ecf20Sopenharmony_ci	if (fs & (UFLO|LCOL|LCAR|RTRY)) {
7598c2ecf20Sopenharmony_ci	    ++dev->stats.tx_errors;
7608c2ecf20Sopenharmony_ci	    if (fs & LCAR)
7618c2ecf20Sopenharmony_ci		++dev->stats.tx_carrier_errors;
7628c2ecf20Sopenharmony_ci	    if (fs & (UFLO|LCOL|RTRY))
7638c2ecf20Sopenharmony_ci		++dev->stats.tx_aborted_errors;
7648c2ecf20Sopenharmony_ci	} else {
7658c2ecf20Sopenharmony_ci	    dev->stats.tx_bytes += mp->tx_bufs[i]->len;
7668c2ecf20Sopenharmony_ci	    ++dev->stats.tx_packets;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci	dev_consume_skb_irq(mp->tx_bufs[i]);
7698c2ecf20Sopenharmony_ci	--mp->tx_active;
7708c2ecf20Sopenharmony_ci	if (++i >= N_TX_RING)
7718c2ecf20Sopenharmony_ci	    i = 0;
7728c2ecf20Sopenharmony_ci#if 0
7738c2ecf20Sopenharmony_ci	mace_last_fs = fs;
7748c2ecf20Sopenharmony_ci	mace_last_xcount = xcount;
7758c2ecf20Sopenharmony_ci#endif
7768c2ecf20Sopenharmony_ci    }
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci    if (i != mp->tx_empty) {
7798c2ecf20Sopenharmony_ci	mp->tx_fullup = 0;
7808c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
7818c2ecf20Sopenharmony_ci    }
7828c2ecf20Sopenharmony_ci    mp->tx_empty = i;
7838c2ecf20Sopenharmony_ci    i += mp->tx_active;
7848c2ecf20Sopenharmony_ci    if (i >= N_TX_RING)
7858c2ecf20Sopenharmony_ci	i -= N_TX_RING;
7868c2ecf20Sopenharmony_ci    if (!mp->tx_bad_runt && i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE) {
7878c2ecf20Sopenharmony_ci	do {
7888c2ecf20Sopenharmony_ci	    /* set up the next one */
7898c2ecf20Sopenharmony_ci	    cp = mp->tx_cmds + NCMDS_TX * i;
7908c2ecf20Sopenharmony_ci	    out_le16(&cp->xfer_status, 0);
7918c2ecf20Sopenharmony_ci	    out_le16(&cp->command, OUTPUT_LAST);
7928c2ecf20Sopenharmony_ci	    ++mp->tx_active;
7938c2ecf20Sopenharmony_ci	    if (++i >= N_TX_RING)
7948c2ecf20Sopenharmony_ci		i = 0;
7958c2ecf20Sopenharmony_ci	} while (i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE);
7968c2ecf20Sopenharmony_ci	out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
7978c2ecf20Sopenharmony_ci	mace_set_timeout(dev);
7988c2ecf20Sopenharmony_ci    }
7998c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
8008c2ecf20Sopenharmony_ci    return IRQ_HANDLED;
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cistatic void mace_tx_timeout(struct timer_list *t)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci    struct mace_data *mp = from_timer(mp, t, tx_timeout);
8068c2ecf20Sopenharmony_ci    struct net_device *dev = macio_get_drvdata(mp->mdev);
8078c2ecf20Sopenharmony_ci    volatile struct mace __iomem *mb = mp->mace;
8088c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *td = mp->tx_dma;
8098c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
8108c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *cp;
8118c2ecf20Sopenharmony_ci    unsigned long flags;
8128c2ecf20Sopenharmony_ci    int i;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
8158c2ecf20Sopenharmony_ci    mp->timeout_active = 0;
8168c2ecf20Sopenharmony_ci    if (mp->tx_active == 0 && !mp->tx_bad_runt)
8178c2ecf20Sopenharmony_ci	goto out;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci    /* update various counters */
8208c2ecf20Sopenharmony_ci    mace_handle_misc_intrs(mp, in_8(&mb->ir), dev);
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci    cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci    /* turn off both tx and rx and reset the chip */
8258c2ecf20Sopenharmony_ci    out_8(&mb->maccc, 0);
8268c2ecf20Sopenharmony_ci    printk(KERN_ERR "mace: transmit timeout - resetting\n");
8278c2ecf20Sopenharmony_ci    dbdma_reset(td);
8288c2ecf20Sopenharmony_ci    mace_reset(dev);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci    /* restart rx dma */
8318c2ecf20Sopenharmony_ci    cp = bus_to_virt(le32_to_cpu(rd->cmdptr));
8328c2ecf20Sopenharmony_ci    dbdma_reset(rd);
8338c2ecf20Sopenharmony_ci    out_le16(&cp->xfer_status, 0);
8348c2ecf20Sopenharmony_ci    out_le32(&rd->cmdptr, virt_to_bus(cp));
8358c2ecf20Sopenharmony_ci    out_le32(&rd->control, (RUN << 16) | RUN);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci    /* fix up the transmit side */
8388c2ecf20Sopenharmony_ci    i = mp->tx_empty;
8398c2ecf20Sopenharmony_ci    mp->tx_active = 0;
8408c2ecf20Sopenharmony_ci    ++dev->stats.tx_errors;
8418c2ecf20Sopenharmony_ci    if (mp->tx_bad_runt) {
8428c2ecf20Sopenharmony_ci	mp->tx_bad_runt = 0;
8438c2ecf20Sopenharmony_ci    } else if (i != mp->tx_fill) {
8448c2ecf20Sopenharmony_ci	dev_kfree_skb_irq(mp->tx_bufs[i]);
8458c2ecf20Sopenharmony_ci	if (++i >= N_TX_RING)
8468c2ecf20Sopenharmony_ci	    i = 0;
8478c2ecf20Sopenharmony_ci	mp->tx_empty = i;
8488c2ecf20Sopenharmony_ci    }
8498c2ecf20Sopenharmony_ci    mp->tx_fullup = 0;
8508c2ecf20Sopenharmony_ci    netif_wake_queue(dev);
8518c2ecf20Sopenharmony_ci    if (i != mp->tx_fill) {
8528c2ecf20Sopenharmony_ci	cp = mp->tx_cmds + NCMDS_TX * i;
8538c2ecf20Sopenharmony_ci	out_le16(&cp->xfer_status, 0);
8548c2ecf20Sopenharmony_ci	out_le16(&cp->command, OUTPUT_LAST);
8558c2ecf20Sopenharmony_ci	out_le32(&td->cmdptr, virt_to_bus(cp));
8568c2ecf20Sopenharmony_ci	out_le32(&td->control, (RUN << 16) | RUN);
8578c2ecf20Sopenharmony_ci	++mp->tx_active;
8588c2ecf20Sopenharmony_ci	mace_set_timeout(dev);
8598c2ecf20Sopenharmony_ci    }
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci    /* turn it back on */
8628c2ecf20Sopenharmony_ci    out_8(&mb->imr, RCVINT);
8638c2ecf20Sopenharmony_ci    out_8(&mb->maccc, mp->maccc);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ciout:
8668c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cistatic irqreturn_t mace_txdma_intr(int irq, void *dev_id)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic irqreturn_t mace_rxdma_intr(int irq, void *dev_id)
8758c2ecf20Sopenharmony_ci{
8768c2ecf20Sopenharmony_ci    struct net_device *dev = (struct net_device *) dev_id;
8778c2ecf20Sopenharmony_ci    struct mace_data *mp = netdev_priv(dev);
8788c2ecf20Sopenharmony_ci    volatile struct dbdma_regs __iomem *rd = mp->rx_dma;
8798c2ecf20Sopenharmony_ci    volatile struct dbdma_cmd *cp, *np;
8808c2ecf20Sopenharmony_ci    int i, nb, stat, next;
8818c2ecf20Sopenharmony_ci    struct sk_buff *skb;
8828c2ecf20Sopenharmony_ci    unsigned frame_status;
8838c2ecf20Sopenharmony_ci    static int mace_lost_status;
8848c2ecf20Sopenharmony_ci    unsigned char *data;
8858c2ecf20Sopenharmony_ci    unsigned long flags;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci    spin_lock_irqsave(&mp->lock, flags);
8888c2ecf20Sopenharmony_ci    for (i = mp->rx_empty; i != mp->rx_fill; ) {
8898c2ecf20Sopenharmony_ci	cp = mp->rx_cmds + i;
8908c2ecf20Sopenharmony_ci	stat = le16_to_cpu(cp->xfer_status);
8918c2ecf20Sopenharmony_ci	if ((stat & ACTIVE) == 0) {
8928c2ecf20Sopenharmony_ci	    next = i + 1;
8938c2ecf20Sopenharmony_ci	    if (next >= N_RX_RING)
8948c2ecf20Sopenharmony_ci		next = 0;
8958c2ecf20Sopenharmony_ci	    np = mp->rx_cmds + next;
8968c2ecf20Sopenharmony_ci	    if (next != mp->rx_fill &&
8978c2ecf20Sopenharmony_ci		(le16_to_cpu(np->xfer_status) & ACTIVE) != 0) {
8988c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mace: lost a status word\n");
8998c2ecf20Sopenharmony_ci		++mace_lost_status;
9008c2ecf20Sopenharmony_ci	    } else
9018c2ecf20Sopenharmony_ci		break;
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci	nb = le16_to_cpu(cp->req_count) - le16_to_cpu(cp->res_count);
9048c2ecf20Sopenharmony_ci	out_le16(&cp->command, DBDMA_STOP);
9058c2ecf20Sopenharmony_ci	/* got a packet, have a look at it */
9068c2ecf20Sopenharmony_ci	skb = mp->rx_bufs[i];
9078c2ecf20Sopenharmony_ci	if (!skb) {
9088c2ecf20Sopenharmony_ci	    ++dev->stats.rx_dropped;
9098c2ecf20Sopenharmony_ci	} else if (nb > 8) {
9108c2ecf20Sopenharmony_ci	    data = skb->data;
9118c2ecf20Sopenharmony_ci	    frame_status = (data[nb-3] << 8) + data[nb-4];
9128c2ecf20Sopenharmony_ci	    if (frame_status & (RS_OFLO|RS_CLSN|RS_FRAMERR|RS_FCSERR)) {
9138c2ecf20Sopenharmony_ci		++dev->stats.rx_errors;
9148c2ecf20Sopenharmony_ci		if (frame_status & RS_OFLO)
9158c2ecf20Sopenharmony_ci		    ++dev->stats.rx_over_errors;
9168c2ecf20Sopenharmony_ci		if (frame_status & RS_FRAMERR)
9178c2ecf20Sopenharmony_ci		    ++dev->stats.rx_frame_errors;
9188c2ecf20Sopenharmony_ci		if (frame_status & RS_FCSERR)
9198c2ecf20Sopenharmony_ci		    ++dev->stats.rx_crc_errors;
9208c2ecf20Sopenharmony_ci	    } else {
9218c2ecf20Sopenharmony_ci		/* Mace feature AUTO_STRIP_RCV is on by default, dropping the
9228c2ecf20Sopenharmony_ci		 * FCS on frames with 802.3 headers. This means that Ethernet
9238c2ecf20Sopenharmony_ci		 * frames have 8 extra octets at the end, while 802.3 frames
9248c2ecf20Sopenharmony_ci		 * have only 4. We need to correctly account for this. */
9258c2ecf20Sopenharmony_ci		if (*(unsigned short *)(data+12) < 1536) /* 802.3 header */
9268c2ecf20Sopenharmony_ci		    nb -= 4;
9278c2ecf20Sopenharmony_ci		else	/* Ethernet header; mace includes FCS */
9288c2ecf20Sopenharmony_ci		    nb -= 8;
9298c2ecf20Sopenharmony_ci		skb_put(skb, nb);
9308c2ecf20Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
9318c2ecf20Sopenharmony_ci		dev->stats.rx_bytes += skb->len;
9328c2ecf20Sopenharmony_ci		netif_rx(skb);
9338c2ecf20Sopenharmony_ci		mp->rx_bufs[i] = NULL;
9348c2ecf20Sopenharmony_ci		++dev->stats.rx_packets;
9358c2ecf20Sopenharmony_ci	    }
9368c2ecf20Sopenharmony_ci	} else {
9378c2ecf20Sopenharmony_ci	    ++dev->stats.rx_errors;
9388c2ecf20Sopenharmony_ci	    ++dev->stats.rx_length_errors;
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	/* advance to next */
9428c2ecf20Sopenharmony_ci	if (++i >= N_RX_RING)
9438c2ecf20Sopenharmony_ci	    i = 0;
9448c2ecf20Sopenharmony_ci    }
9458c2ecf20Sopenharmony_ci    mp->rx_empty = i;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci    i = mp->rx_fill;
9488c2ecf20Sopenharmony_ci    for (;;) {
9498c2ecf20Sopenharmony_ci	next = i + 1;
9508c2ecf20Sopenharmony_ci	if (next >= N_RX_RING)
9518c2ecf20Sopenharmony_ci	    next = 0;
9528c2ecf20Sopenharmony_ci	if (next == mp->rx_empty)
9538c2ecf20Sopenharmony_ci	    break;
9548c2ecf20Sopenharmony_ci	cp = mp->rx_cmds + i;
9558c2ecf20Sopenharmony_ci	skb = mp->rx_bufs[i];
9568c2ecf20Sopenharmony_ci	if (!skb) {
9578c2ecf20Sopenharmony_ci	    skb = netdev_alloc_skb(dev, RX_BUFLEN + 2);
9588c2ecf20Sopenharmony_ci	    if (skb) {
9598c2ecf20Sopenharmony_ci		skb_reserve(skb, 2);
9608c2ecf20Sopenharmony_ci		mp->rx_bufs[i] = skb;
9618c2ecf20Sopenharmony_ci	    }
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci	cp->req_count = cpu_to_le16(RX_BUFLEN);
9648c2ecf20Sopenharmony_ci	data = skb? skb->data: dummy_buf;
9658c2ecf20Sopenharmony_ci	cp->phy_addr = cpu_to_le32(virt_to_bus(data));
9668c2ecf20Sopenharmony_ci	out_le16(&cp->xfer_status, 0);
9678c2ecf20Sopenharmony_ci	out_le16(&cp->command, INPUT_LAST + INTR_ALWAYS);
9688c2ecf20Sopenharmony_ci#if 0
9698c2ecf20Sopenharmony_ci	if ((le32_to_cpu(rd->status) & ACTIVE) != 0) {
9708c2ecf20Sopenharmony_ci	    out_le32(&rd->control, (PAUSE << 16) | PAUSE);
9718c2ecf20Sopenharmony_ci	    while ((in_le32(&rd->status) & ACTIVE) != 0)
9728c2ecf20Sopenharmony_ci		;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci#endif
9758c2ecf20Sopenharmony_ci	i = next;
9768c2ecf20Sopenharmony_ci    }
9778c2ecf20Sopenharmony_ci    if (i != mp->rx_fill) {
9788c2ecf20Sopenharmony_ci	out_le32(&rd->control, ((RUN|WAKE) << 16) | (RUN|WAKE));
9798c2ecf20Sopenharmony_ci	mp->rx_fill = i;
9808c2ecf20Sopenharmony_ci    }
9818c2ecf20Sopenharmony_ci    spin_unlock_irqrestore(&mp->lock, flags);
9828c2ecf20Sopenharmony_ci    return IRQ_HANDLED;
9838c2ecf20Sopenharmony_ci}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_cistatic const struct of_device_id mace_match[] =
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	{
9888c2ecf20Sopenharmony_ci	.name 		= "mace",
9898c2ecf20Sopenharmony_ci	},
9908c2ecf20Sopenharmony_ci	{},
9918c2ecf20Sopenharmony_ci};
9928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (of, mace_match);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic struct macio_driver mace_driver =
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	.driver = {
9978c2ecf20Sopenharmony_ci		.name 		= "mace",
9988c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
9998c2ecf20Sopenharmony_ci		.of_match_table	= mace_match,
10008c2ecf20Sopenharmony_ci	},
10018c2ecf20Sopenharmony_ci	.probe		= mace_probe,
10028c2ecf20Sopenharmony_ci	.remove		= mace_remove,
10038c2ecf20Sopenharmony_ci};
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic int __init mace_init(void)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	return macio_register_driver(&mace_driver);
10098c2ecf20Sopenharmony_ci}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_cistatic void __exit mace_cleanup(void)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	macio_unregister_driver(&mace_driver);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	kfree(dummy_buf);
10168c2ecf20Sopenharmony_ci	dummy_buf = NULL;
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras");
10208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PowerMac MACE driver.");
10218c2ecf20Sopenharmony_cimodule_param(port_aaui, int, 0);
10228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)");
10238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cimodule_init(mace_init);
10268c2ecf20Sopenharmony_cimodule_exit(mace_cleanup);
1027