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