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