From a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 Mon Sep 17 00:00:00 2001 From: Jinke Han Date: Tue, 8 Sep 2026 15:37:42 +0800 Subject: x86/kprobes: Fix crash when probing CS CALL instructions When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) Signed-off-by: Jinke Han Signed-off-by: Ingo Molnar Reviewed-by: Masami Hiramatsu (Google) Acked-by: Yafang Shao Acked-by: Borislav Petkov Cc: Peter Zijlstra Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000 --- drivers/net/ethernet/apple/Kconfig | 63 ++ drivers/net/ethernet/apple/Makefile | 8 + drivers/net/ethernet/apple/bmac.c | 1612 ++++++++++++++++++++++++++++++++++ drivers/net/ethernet/apple/bmac.h | 160 ++++ drivers/net/ethernet/apple/mace.c | 1029 ++++++++++++++++++++++ drivers/net/ethernet/apple/mace.h | 169 ++++ drivers/net/ethernet/apple/macmace.c | 768 ++++++++++++++++ 7 files changed, 3809 insertions(+) create mode 100644 drivers/net/ethernet/apple/Kconfig create mode 100644 drivers/net/ethernet/apple/Makefile create mode 100644 drivers/net/ethernet/apple/bmac.c create mode 100644 drivers/net/ethernet/apple/bmac.h create mode 100644 drivers/net/ethernet/apple/mace.c create mode 100644 drivers/net/ethernet/apple/mace.h create mode 100644 drivers/net/ethernet/apple/macmace.c (limited to 'drivers/net/ethernet/apple') diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig new file mode 100644 index 000000000..a4176d2ec --- /dev/null +++ b/drivers/net/ethernet/apple/Kconfig @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Apple device configuration +# + +config NET_VENDOR_APPLE + bool "Apple devices" + default y + depends on (PPC_PMAC && PPC32) || MAC + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + questions about Apple devices. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_APPLE + +config MACE + tristate "MACE (Power Mac ethernet) support" + depends on PPC_PMAC && PPC32 + select CRC32 + help + Power Macintoshes and clones with Ethernet built-in on the + motherboard will usually use a MACE (Medium Access Control for + Ethernet) interface. Say Y to include support for the MACE chip. + + To compile this driver as a module, choose M here: the module + will be called mace. + +config MACE_AAUI_PORT + bool "Use AAUI port instead of TP by default" + depends on MACE + help + Some Apple machines (notably the Apple Network Server) which use the + MACE ethernet chip have an Apple AUI port (small 15-pin connector), + instead of an 8-pin RJ45 connector for twisted-pair ethernet. Say + Y here if you have such a machine. If unsure, say N. + The driver will default to AAUI on ANS anyway, and if you use it as + a module, you can provide the port_aaui=0|1 to force the driver. + +config BMAC + tristate "BMAC (G3 ethernet) support" + depends on PPC_PMAC && PPC32 + select CRC32 + help + Say Y for support of BMAC Ethernet interfaces. These are used on G3 + computers. + + To compile this driver as a module, choose M here: the module + will be called bmac. + +config MACMACE + tristate "Macintosh (AV) onboard MACE ethernet" + depends on MAC + select CRC32 + help + Support for the onboard AMD 79C940 MACE Ethernet controller used in + the 660AV and 840AV Macintosh. If you have one of these Macintoshes + say Y here. + +endif # NET_VENDOR_APPLE diff --git a/drivers/net/ethernet/apple/Makefile b/drivers/net/ethernet/apple/Makefile new file mode 100644 index 000000000..322457027 --- /dev/null +++ b/drivers/net/ethernet/apple/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Apple network device drivers. +# + +obj-$(CONFIG_MACE) += mace.o +obj-$(CONFIG_BMAC) += bmac.o +obj-$(CONFIG_MACMACE) += macmace.o diff --git a/drivers/net/ethernet/apple/bmac.c b/drivers/net/ethernet/apple/bmac.c new file mode 100644 index 000000000..b3bf8d6f8 --- /dev/null +++ b/drivers/net/ethernet/apple/bmac.c @@ -0,0 +1,1612 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Network device driver for the BMAC ethernet controller on + * Apple Powermacs. Assumes it's under a DBDMA controller. + * + * Copyright (C) 1998 Randy Gobbel. + * + * May 1999, Al Viro: proper release of /proc/net/bmac entry, switched to + * dynamic procfs inode. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bmac.h" + +#define trunc_page(x) ((void *)(((unsigned long)(x)) & ~((unsigned long)(PAGE_SIZE - 1)))) +#define round_page(x) trunc_page(((unsigned long)(x)) + ((unsigned long)(PAGE_SIZE - 1))) + +/* switch to use multicast code lifted from sunhme driver */ +#define SUNHME_MULTICAST + +#define N_RX_RING 64 +#define N_TX_RING 32 +#define MAX_TX_ACTIVE 1 +#define ETHERCRC 4 +#define ETHERMINPACKET 64 +#define ETHERMTU 1500 +#define RX_BUFLEN (ETHERMTU + 14 + ETHERCRC + 2) +#define TX_TIMEOUT HZ /* 1 second */ + +/* Bits in transmit DMA status */ +#define TX_DMA_ERR 0x80 + +#define XXDEBUG(args) + +struct bmac_data { + /* volatile struct bmac *bmac; */ + struct sk_buff_head *queue; + volatile struct dbdma_regs __iomem *tx_dma; + int tx_dma_intr; + volatile struct dbdma_regs __iomem *rx_dma; + int rx_dma_intr; + volatile struct dbdma_cmd *tx_cmds; /* xmit dma command list */ + volatile struct dbdma_cmd *rx_cmds; /* recv dma command list */ + struct macio_dev *mdev; + int is_bmac_plus; + struct sk_buff *rx_bufs[N_RX_RING]; + int rx_fill; + int rx_empty; + struct sk_buff *tx_bufs[N_TX_RING]; + int tx_fill; + int tx_empty; + unsigned char tx_fullup; + struct timer_list tx_timeout; + int timeout_active; + int sleeping; + int opened; + unsigned short hash_use_count[64]; + unsigned short hash_table_mask[4]; + spinlock_t lock; +}; + +#if 0 /* Move that to ethtool */ + +typedef struct bmac_reg_entry { + char *name; + unsigned short reg_offset; +} bmac_reg_entry_t; + +#define N_REG_ENTRIES 31 + +static bmac_reg_entry_t reg_entries[N_REG_ENTRIES] = { + {"MEMADD", MEMADD}, + {"MEMDATAHI", MEMDATAHI}, + {"MEMDATALO", MEMDATALO}, + {"TXPNTR", TXPNTR}, + {"RXPNTR", RXPNTR}, + {"IPG1", IPG1}, + {"IPG2", IPG2}, + {"ALIMIT", ALIMIT}, + {"SLOT", SLOT}, + {"PALEN", PALEN}, + {"PAPAT", PAPAT}, + {"TXSFD", TXSFD}, + {"JAM", JAM}, + {"TXCFG", TXCFG}, + {"TXMAX", TXMAX}, + {"TXMIN", TXMIN}, + {"PAREG", PAREG}, + {"DCNT", DCNT}, + {"NCCNT", NCCNT}, + {"NTCNT", NTCNT}, + {"EXCNT", EXCNT}, + {"LTCNT", LTCNT}, + {"TXSM", TXSM}, + {"RXCFG", RXCFG}, + {"RXMAX", RXMAX}, + {"RXMIN", RXMIN}, + {"FRCNT", FRCNT}, + {"AECNT", AECNT}, + {"FECNT", FECNT}, + {"RXSM", RXSM}, + {"RXCV", RXCV} +}; + +#endif + +static unsigned char *bmac_emergency_rxbuf; + +/* + * Number of bytes of private data per BMAC: allow enough for + * the rx and tx dma commands plus a branch dma command each, + * and another 16 bytes to allow us to align the dma command + * buffers on a 16 byte boundary. + */ +#define PRIV_BYTES (sizeof(struct bmac_data) \ + + (N_RX_RING + N_TX_RING + 4) * sizeof(struct dbdma_cmd) \ + + sizeof(struct sk_buff_head)) + +static int bmac_open(struct net_device *dev); +static int bmac_close(struct net_device *dev); +static int bmac_transmit_packet(struct sk_buff *skb, struct net_device *dev); +static void bmac_set_multicast(struct net_device *dev); +static void bmac_reset_and_enable(struct net_device *dev); +static void bmac_start_chip(struct net_device *dev); +static void bmac_init_chip(struct net_device *dev); +static void bmac_init_registers(struct net_device *dev); +static void bmac_enable_and_reset_chip(struct net_device *dev); +static int bmac_set_address(struct net_device *dev, void *addr); +static irqreturn_t bmac_misc_intr(int irq, void *dev_id); +static irqreturn_t bmac_txdma_intr(int irq, void *dev_id); +static irqreturn_t bmac_rxdma_intr(int irq, void *dev_id); +static void bmac_set_timeout(struct net_device *dev); +static void bmac_tx_timeout(struct timer_list *t); +static netdev_tx_t bmac_output(struct sk_buff *skb, struct net_device *dev); +static void bmac_start(struct net_device *dev); + +#define DBDMA_SET(x) ( ((x) | (x) << 16) ) +#define DBDMA_CLEAR(x) ( (x) << 16) + +static inline void +dbdma_st32(volatile __u32 __iomem *a, unsigned long x) +{ + __asm__ volatile( "stwbrx %0,0,%1" : : "r" (x), "r" (a) : "memory"); +} + +static inline unsigned long +dbdma_ld32(volatile __u32 __iomem *a) +{ + __u32 swap; + __asm__ volatile ("lwbrx %0,0,%1" : "=r" (swap) : "r" (a)); + return swap; +} + +static void +dbdma_continue(volatile struct dbdma_regs __iomem *dmap) +{ + dbdma_st32(&dmap->control, + DBDMA_SET(RUN|WAKE) | DBDMA_CLEAR(PAUSE|DEAD)); + eieio(); +} + +static void +dbdma_reset(volatile struct dbdma_regs __iomem *dmap) +{ + dbdma_st32(&dmap->control, + DBDMA_CLEAR(ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)); + eieio(); + while (dbdma_ld32(&dmap->status) & RUN) + eieio(); +} + +static void +dbdma_setcmd(volatile struct dbdma_cmd *cp, + unsigned short cmd, unsigned count, unsigned long addr, + unsigned long cmd_dep) +{ + out_le16(&cp->command, cmd); + out_le16(&cp->req_count, count); + out_le32(&cp->phy_addr, addr); + out_le32(&cp->cmd_dep, cmd_dep); + out_le16(&cp->xfer_status, 0); + out_le16(&cp->res_count, 0); +} + +static inline +void bmwrite(struct net_device *dev, unsigned long reg_offset, unsigned data ) +{ + out_le16((void __iomem *)dev->base_addr + reg_offset, data); +} + + +static inline +unsigned short bmread(struct net_device *dev, unsigned long reg_offset ) +{ + return in_le16((void __iomem *)dev->base_addr + reg_offset); +} + +static void +bmac_enable_and_reset_chip(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + volatile struct dbdma_regs __iomem *td = bp->tx_dma; + + if (rd) + dbdma_reset(rd); + if (td) + dbdma_reset(td); + + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, macio_get_of_node(bp->mdev), 0, 1); +} + +#define MIFDELAY udelay(10) + +static unsigned int +bmac_mif_readbits(struct net_device *dev, int nb) +{ + unsigned int val = 0; + + while (--nb >= 0) { + bmwrite(dev, MIFCSR, 0); + MIFDELAY; + if (bmread(dev, MIFCSR) & 8) + val |= 1 << nb; + bmwrite(dev, MIFCSR, 1); + MIFDELAY; + } + bmwrite(dev, MIFCSR, 0); + MIFDELAY; + bmwrite(dev, MIFCSR, 1); + MIFDELAY; + return val; +} + +static void +bmac_mif_writebits(struct net_device *dev, unsigned int val, int nb) +{ + int b; + + while (--nb >= 0) { + b = (val & (1 << nb))? 6: 4; + bmwrite(dev, MIFCSR, b); + MIFDELAY; + bmwrite(dev, MIFCSR, b|1); + MIFDELAY; + } +} + +static unsigned int +bmac_mif_read(struct net_device *dev, unsigned int addr) +{ + unsigned int val; + + bmwrite(dev, MIFCSR, 4); + MIFDELAY; + bmac_mif_writebits(dev, ~0U, 32); + bmac_mif_writebits(dev, 6, 4); + bmac_mif_writebits(dev, addr, 10); + bmwrite(dev, MIFCSR, 2); + MIFDELAY; + bmwrite(dev, MIFCSR, 1); + MIFDELAY; + val = bmac_mif_readbits(dev, 17); + bmwrite(dev, MIFCSR, 4); + MIFDELAY; + return val; +} + +static void +bmac_mif_write(struct net_device *dev, unsigned int addr, unsigned int val) +{ + bmwrite(dev, MIFCSR, 4); + MIFDELAY; + bmac_mif_writebits(dev, ~0U, 32); + bmac_mif_writebits(dev, 5, 4); + bmac_mif_writebits(dev, addr, 10); + bmac_mif_writebits(dev, 2, 2); + bmac_mif_writebits(dev, val, 16); + bmac_mif_writebits(dev, 3, 2); +} + +static void +bmac_init_registers(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + volatile unsigned short regValue; + const unsigned short *pWord16; + int i; + + /* XXDEBUG(("bmac: enter init_registers\n")); */ + + bmwrite(dev, RXRST, RxResetValue); + bmwrite(dev, TXRST, TxResetBit); + + i = 100; + do { + --i; + udelay(10000); + regValue = bmread(dev, TXRST); /* wait for reset to clear..acknowledge */ + } while ((regValue & TxResetBit) && i > 0); + + if (!bp->is_bmac_plus) { + regValue = bmread(dev, XCVRIF); + regValue |= ClkBit | SerialMode | COLActiveLow; + bmwrite(dev, XCVRIF, regValue); + udelay(10000); + } + + bmwrite(dev, RSEED, (unsigned short)0x1968); + + regValue = bmread(dev, XIFC); + regValue |= TxOutputEnable; + bmwrite(dev, XIFC, regValue); + + bmread(dev, PAREG); + + /* set collision counters to 0 */ + bmwrite(dev, NCCNT, 0); + bmwrite(dev, NTCNT, 0); + bmwrite(dev, EXCNT, 0); + bmwrite(dev, LTCNT, 0); + + /* set rx counters to 0 */ + bmwrite(dev, FRCNT, 0); + bmwrite(dev, LECNT, 0); + bmwrite(dev, AECNT, 0); + bmwrite(dev, FECNT, 0); + bmwrite(dev, RXCV, 0); + + /* set tx fifo information */ + bmwrite(dev, TXTH, 4); /* 4 octets before tx starts */ + + bmwrite(dev, TXFIFOCSR, 0); /* first disable txFIFO */ + bmwrite(dev, TXFIFOCSR, TxFIFOEnable ); + + /* set rx fifo information */ + bmwrite(dev, RXFIFOCSR, 0); /* first disable rxFIFO */ + bmwrite(dev, RXFIFOCSR, RxFIFOEnable ); + + //bmwrite(dev, TXCFG, TxMACEnable); /* TxNeverGiveUp maybe later */ + bmread(dev, STATUS); /* read it just to clear it */ + + /* zero out the chip Hash Filter registers */ + for (i=0; i<4; i++) bp->hash_table_mask[i] = 0; + bmwrite(dev, BHASH3, bp->hash_table_mask[0]); /* bits 15 - 0 */ + bmwrite(dev, BHASH2, bp->hash_table_mask[1]); /* bits 31 - 16 */ + bmwrite(dev, BHASH1, bp->hash_table_mask[2]); /* bits 47 - 32 */ + bmwrite(dev, BHASH0, bp->hash_table_mask[3]); /* bits 63 - 48 */ + + pWord16 = (const unsigned short *)dev->dev_addr; + bmwrite(dev, MADD0, *pWord16++); + bmwrite(dev, MADD1, *pWord16++); + bmwrite(dev, MADD2, *pWord16); + + bmwrite(dev, RXCFG, RxCRCNoStrip | RxHashFilterEnable | RxRejectOwnPackets); + + bmwrite(dev, INTDISABLE, EnableNormal); +} + +#if 0 +static void +bmac_disable_interrupts(struct net_device *dev) +{ + bmwrite(dev, INTDISABLE, DisableAll); +} + +static void +bmac_enable_interrupts(struct net_device *dev) +{ + bmwrite(dev, INTDISABLE, EnableNormal); +} +#endif + + +static void +bmac_start_chip(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + unsigned short oldConfig; + + /* enable rx dma channel */ + dbdma_continue(rd); + + oldConfig = bmread(dev, TXCFG); + bmwrite(dev, TXCFG, oldConfig | TxMACEnable ); + + /* turn on rx plus any other bits already on (promiscuous possibly) */ + oldConfig = bmread(dev, RXCFG); + bmwrite(dev, RXCFG, oldConfig | RxMACEnable ); + udelay(20000); +} + +static void +bmac_init_phy(struct net_device *dev) +{ + unsigned int addr; + struct bmac_data *bp = netdev_priv(dev); + + printk(KERN_DEBUG "phy registers:"); + for (addr = 0; addr < 32; ++addr) { + if ((addr & 7) == 0) + printk(KERN_DEBUG); + printk(KERN_CONT " %.4x", bmac_mif_read(dev, addr)); + } + printk(KERN_CONT "\n"); + + if (bp->is_bmac_plus) { + unsigned int capable, ctrl; + + ctrl = bmac_mif_read(dev, 0); + capable = ((bmac_mif_read(dev, 1) & 0xf800) >> 6) | 1; + if (bmac_mif_read(dev, 4) != capable || + (ctrl & 0x1000) == 0) { + bmac_mif_write(dev, 4, capable); + bmac_mif_write(dev, 0, 0x1200); + } else + bmac_mif_write(dev, 0, 0x1000); + } +} + +static void bmac_init_chip(struct net_device *dev) +{ + bmac_init_phy(dev); + bmac_init_registers(dev); +} + +#ifdef CONFIG_PM +static int bmac_suspend(struct macio_dev *mdev, pm_message_t state) +{ + struct net_device* dev = macio_get_drvdata(mdev); + struct bmac_data *bp = netdev_priv(dev); + unsigned long flags; + unsigned short config; + int i; + + netif_device_detach(dev); + /* prolly should wait for dma to finish & turn off the chip */ + spin_lock_irqsave(&bp->lock, flags); + if (bp->timeout_active) { + timer_delete(&bp->tx_timeout); + bp->timeout_active = 0; + } + disable_irq(dev->irq); + disable_irq(bp->tx_dma_intr); + disable_irq(bp->rx_dma_intr); + bp->sleeping = 1; + spin_unlock_irqrestore(&bp->lock, flags); + if (bp->opened) { + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + volatile struct dbdma_regs __iomem *td = bp->tx_dma; + + config = bmread(dev, RXCFG); + bmwrite(dev, RXCFG, (config & ~RxMACEnable)); + config = bmread(dev, TXCFG); + bmwrite(dev, TXCFG, (config & ~TxMACEnable)); + bmwrite(dev, INTDISABLE, DisableAll); /* disable all intrs */ + /* disable rx and tx dma */ + rd->control = cpu_to_le32(DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE)); /* clear run bit */ + td->control = cpu_to_le32(DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE)); /* clear run bit */ + /* free some skb's */ + for (i=0; irx_bufs[i] != NULL) { + dev_kfree_skb(bp->rx_bufs[i]); + bp->rx_bufs[i] = NULL; + } + } + for (i = 0; itx_bufs[i] != NULL) { + dev_kfree_skb(bp->tx_bufs[i]); + bp->tx_bufs[i] = NULL; + } + } + } + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, macio_get_of_node(bp->mdev), 0, 0); + return 0; +} + +static int bmac_resume(struct macio_dev *mdev) +{ + struct net_device* dev = macio_get_drvdata(mdev); + struct bmac_data *bp = netdev_priv(dev); + + /* see if this is enough */ + if (bp->opened) + bmac_reset_and_enable(dev); + + enable_irq(dev->irq); + enable_irq(bp->tx_dma_intr); + enable_irq(bp->rx_dma_intr); + netif_device_attach(dev); + + return 0; +} +#endif /* CONFIG_PM */ + +static int bmac_set_address(struct net_device *dev, void *addr) +{ + struct bmac_data *bp = netdev_priv(dev); + const unsigned short *pWord16; + unsigned long flags; + + XXDEBUG(("bmac: enter set_address\n")); + spin_lock_irqsave(&bp->lock, flags); + + eth_hw_addr_set(dev, addr); + + /* load up the hardware address */ + pWord16 = (const unsigned short *)dev->dev_addr; + bmwrite(dev, MADD0, *pWord16++); + bmwrite(dev, MADD1, *pWord16++); + bmwrite(dev, MADD2, *pWord16); + + spin_unlock_irqrestore(&bp->lock, flags); + XXDEBUG(("bmac: exit set_address\n")); + return 0; +} + +static inline void bmac_set_timeout(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + if (bp->timeout_active) + timer_delete(&bp->tx_timeout); + bp->tx_timeout.expires = jiffies + TX_TIMEOUT; + add_timer(&bp->tx_timeout); + bp->timeout_active = 1; + spin_unlock_irqrestore(&bp->lock, flags); +} + +static void +bmac_construct_xmt(struct sk_buff *skb, volatile struct dbdma_cmd *cp) +{ + void *vaddr; + unsigned long baddr; + unsigned long len; + + len = skb->len; + vaddr = skb->data; + baddr = virt_to_bus(vaddr); + + dbdma_setcmd(cp, (OUTPUT_LAST | INTR_ALWAYS | WAIT_IFCLR), len, baddr, 0); +} + +static void +bmac_construct_rxbuff(struct sk_buff *skb, volatile struct dbdma_cmd *cp) +{ + unsigned char *addr = skb? skb->data: bmac_emergency_rxbuf; + + dbdma_setcmd(cp, (INPUT_LAST | INTR_ALWAYS), RX_BUFLEN, + virt_to_bus(addr), 0); +} + +static void +bmac_init_tx_ring(struct bmac_data *bp) +{ + volatile struct dbdma_regs __iomem *td = bp->tx_dma; + + memset((char *)bp->tx_cmds, 0, (N_TX_RING+1) * sizeof(struct dbdma_cmd)); + + bp->tx_empty = 0; + bp->tx_fill = 0; + bp->tx_fullup = 0; + + /* put a branch at the end of the tx command list */ + dbdma_setcmd(&bp->tx_cmds[N_TX_RING], + (DBDMA_NOP | BR_ALWAYS), 0, 0, virt_to_bus(bp->tx_cmds)); + + /* reset tx dma */ + dbdma_reset(td); + out_le32(&td->wait_sel, 0x00200020); + out_le32(&td->cmdptr, virt_to_bus(bp->tx_cmds)); +} + +static int +bmac_init_rx_ring(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + int i; + struct sk_buff *skb; + + /* initialize list of sk_buffs for receiving and set up recv dma */ + memset((char *)bp->rx_cmds, 0, + (N_RX_RING + 1) * sizeof(struct dbdma_cmd)); + for (i = 0; i < N_RX_RING; i++) { + if ((skb = bp->rx_bufs[i]) == NULL) { + bp->rx_bufs[i] = skb = netdev_alloc_skb(dev, RX_BUFLEN + 2); + if (skb != NULL) + skb_reserve(skb, 2); + } + bmac_construct_rxbuff(skb, &bp->rx_cmds[i]); + } + + bp->rx_empty = 0; + bp->rx_fill = i; + + /* Put a branch back to the beginning of the receive command list */ + dbdma_setcmd(&bp->rx_cmds[N_RX_RING], + (DBDMA_NOP | BR_ALWAYS), 0, 0, virt_to_bus(bp->rx_cmds)); + + /* start rx dma */ + dbdma_reset(rd); + out_le32(&rd->cmdptr, virt_to_bus(bp->rx_cmds)); + + return 1; +} + + +static int bmac_transmit_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *td = bp->tx_dma; + int i; + + /* see if there's a free slot in the tx ring */ + /* XXDEBUG(("bmac_xmit_start: empty=%d fill=%d\n", */ + /* bp->tx_empty, bp->tx_fill)); */ + i = bp->tx_fill + 1; + if (i >= N_TX_RING) + i = 0; + if (i == bp->tx_empty) { + netif_stop_queue(dev); + bp->tx_fullup = 1; + XXDEBUG(("bmac_transmit_packet: tx ring full\n")); + return -1; /* can't take it at the moment */ + } + + dbdma_setcmd(&bp->tx_cmds[i], DBDMA_STOP, 0, 0, 0); + + bmac_construct_xmt(skb, &bp->tx_cmds[bp->tx_fill]); + + bp->tx_bufs[bp->tx_fill] = skb; + bp->tx_fill = i; + + dev->stats.tx_bytes += skb->len; + + dbdma_continue(td); + + return 0; +} + +static int rxintcount; + +static irqreturn_t bmac_rxdma_intr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + volatile struct dbdma_cmd *cp; + int i, nb, stat; + struct sk_buff *skb; + unsigned int residual; + int last; + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + + if (++rxintcount < 10) { + XXDEBUG(("bmac_rxdma_intr\n")); + } + + last = -1; + i = bp->rx_empty; + + while (1) { + cp = &bp->rx_cmds[i]; + stat = le16_to_cpu(cp->xfer_status); + residual = le16_to_cpu(cp->res_count); + if ((stat & ACTIVE) == 0) + break; + nb = RX_BUFLEN - residual - 2; + if (nb < (ETHERMINPACKET - ETHERCRC)) { + skb = NULL; + dev->stats.rx_length_errors++; + dev->stats.rx_errors++; + } else { + skb = bp->rx_bufs[i]; + bp->rx_bufs[i] = NULL; + } + if (skb != NULL) { + nb -= ETHERCRC; + skb_put(skb, nb); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + ++dev->stats.rx_packets; + dev->stats.rx_bytes += nb; + } else { + ++dev->stats.rx_dropped; + } + if ((skb = bp->rx_bufs[i]) == NULL) { + bp->rx_bufs[i] = skb = netdev_alloc_skb(dev, RX_BUFLEN + 2); + if (skb != NULL) + skb_reserve(bp->rx_bufs[i], 2); + } + bmac_construct_rxbuff(skb, &bp->rx_cmds[i]); + cp->res_count = cpu_to_le16(0); + cp->xfer_status = cpu_to_le16(0); + last = i; + if (++i >= N_RX_RING) i = 0; + } + + if (last != -1) { + bp->rx_fill = last; + bp->rx_empty = i; + } + + dbdma_continue(rd); + spin_unlock_irqrestore(&bp->lock, flags); + + if (rxintcount < 10) { + XXDEBUG(("bmac_rxdma_intr done\n")); + } + return IRQ_HANDLED; +} + +static int txintcount; + +static irqreturn_t bmac_txdma_intr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_cmd *cp; + int stat; + unsigned long flags; + + spin_lock_irqsave(&bp->lock, flags); + + if (txintcount++ < 10) { + XXDEBUG(("bmac_txdma_intr\n")); + } + + /* timer_delete(&bp->tx_timeout); */ + /* bp->timeout_active = 0; */ + + while (1) { + cp = &bp->tx_cmds[bp->tx_empty]; + stat = le16_to_cpu(cp->xfer_status); + if (txintcount < 10) { + XXDEBUG(("bmac_txdma_xfer_stat=%#0x\n", stat)); + } + if (!(stat & ACTIVE)) { + /* + * status field might not have been filled by DBDMA + */ + if (cp == bus_to_virt(in_le32(&bp->tx_dma->cmdptr))) + break; + } + + if (bp->tx_bufs[bp->tx_empty]) { + ++dev->stats.tx_packets; + dev_consume_skb_irq(bp->tx_bufs[bp->tx_empty]); + } + bp->tx_bufs[bp->tx_empty] = NULL; + bp->tx_fullup = 0; + netif_wake_queue(dev); + if (++bp->tx_empty >= N_TX_RING) + bp->tx_empty = 0; + if (bp->tx_empty == bp->tx_fill) + break; + } + + spin_unlock_irqrestore(&bp->lock, flags); + + if (txintcount < 10) { + XXDEBUG(("bmac_txdma_intr done->bmac_start\n")); + } + + bmac_start(dev); + return IRQ_HANDLED; +} + +#ifndef SUNHME_MULTICAST +/* + * Add requested mcast addr to BMac's hash table filter. + * + */ + +static void +bmac_addhash(struct bmac_data *bp, unsigned char *addr) +{ + unsigned int crc; + unsigned short mask; + + if (!(*addr)) return; + crc = crc32(~0, addr, ETH_ALEN) >> 26; + if (bp->hash_use_count[crc]++) return; /* This bit is already set */ + mask = crc % 16; + mask = (unsigned char)1 << mask; + bp->hash_use_count[crc/16] |= mask; +} + +static void +bmac_removehash(struct bmac_data *bp, unsigned char *addr) +{ + unsigned int crc; + unsigned char mask; + + /* Now, delete the address from the filter copy, as indicated */ + crc = crc32(~0, addr, ETH_ALEN) >> 26; + if (bp->hash_use_count[crc] == 0) return; /* That bit wasn't in use! */ + if (--bp->hash_use_count[crc]) return; /* That bit is still in use */ + mask = crc % 16; + mask = ((unsigned char)1 << mask) ^ 0xffff; /* To turn off bit */ + bp->hash_table_mask[crc/16] &= mask; +} + +/* + * Sync the adapter with the software copy of the multicast mask + * (logical address filter). + */ + +static void +bmac_rx_off(struct net_device *dev) +{ + unsigned short rx_cfg; + + rx_cfg = bmread(dev, RXCFG); + rx_cfg &= ~RxMACEnable; + bmwrite(dev, RXCFG, rx_cfg); + do { + rx_cfg = bmread(dev, RXCFG); + } while (rx_cfg & RxMACEnable); +} + +unsigned short +bmac_rx_on(struct net_device *dev, int hash_enable, int promisc_enable) +{ + unsigned short rx_cfg; + + rx_cfg = bmread(dev, RXCFG); + rx_cfg |= RxMACEnable; + if (hash_enable) rx_cfg |= RxHashFilterEnable; + else rx_cfg &= ~RxHashFilterEnable; + if (promisc_enable) rx_cfg |= RxPromiscEnable; + else rx_cfg &= ~RxPromiscEnable; + bmwrite(dev, RXRST, RxResetValue); + bmwrite(dev, RXFIFOCSR, 0); /* first disable rxFIFO */ + bmwrite(dev, RXFIFOCSR, RxFIFOEnable ); + bmwrite(dev, RXCFG, rx_cfg ); + return rx_cfg; +} + +static void +bmac_update_hash_table_mask(struct net_device *dev, struct bmac_data *bp) +{ + bmwrite(dev, BHASH3, bp->hash_table_mask[0]); /* bits 15 - 0 */ + bmwrite(dev, BHASH2, bp->hash_table_mask[1]); /* bits 31 - 16 */ + bmwrite(dev, BHASH1, bp->hash_table_mask[2]); /* bits 47 - 32 */ + bmwrite(dev, BHASH0, bp->hash_table_mask[3]); /* bits 63 - 48 */ +} + +#if 0 +static void +bmac_add_multi(struct net_device *dev, + struct bmac_data *bp, unsigned char *addr) +{ + /* XXDEBUG(("bmac: enter bmac_add_multi\n")); */ + bmac_addhash(bp, addr); + bmac_rx_off(dev); + bmac_update_hash_table_mask(dev, bp); + bmac_rx_on(dev, 1, (dev->flags & IFF_PROMISC)? 1 : 0); + /* XXDEBUG(("bmac: exit bmac_add_multi\n")); */ +} + +static void +bmac_remove_multi(struct net_device *dev, + struct bmac_data *bp, unsigned char *addr) +{ + bmac_removehash(bp, addr); + bmac_rx_off(dev); + bmac_update_hash_table_mask(dev, bp); + bmac_rx_on(dev, 1, (dev->flags & IFF_PROMISC)? 1 : 0); +} +#endif + +/* Set or clear the multicast filter for this adaptor. + num_addrs == -1 Promiscuous mode, receive all packets + num_addrs == 0 Normal mode, clear multicast list + num_addrs > 0 Multicast mode, receive normal and MC packets, and do + best-effort filtering. + */ +static void bmac_set_multicast(struct net_device *dev) +{ + struct netdev_hw_addr *ha; + struct bmac_data *bp = netdev_priv(dev); + int num_addrs = netdev_mc_count(dev); + unsigned short rx_cfg; + int i; + + if (bp->sleeping) + return; + + XXDEBUG(("bmac: enter bmac_set_multicast, n_addrs=%d\n", num_addrs)); + + if((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > 64)) { + for (i=0; i<4; i++) bp->hash_table_mask[i] = 0xffff; + bmac_update_hash_table_mask(dev, bp); + rx_cfg = bmac_rx_on(dev, 1, 0); + XXDEBUG(("bmac: all multi, rx_cfg=%#08x\n")); + } else if ((dev->flags & IFF_PROMISC) || (num_addrs < 0)) { + rx_cfg = bmread(dev, RXCFG); + rx_cfg |= RxPromiscEnable; + bmwrite(dev, RXCFG, rx_cfg); + rx_cfg = bmac_rx_on(dev, 0, 1); + XXDEBUG(("bmac: promisc mode enabled, rx_cfg=%#08x\n", rx_cfg)); + } else { + for (i=0; i<4; i++) bp->hash_table_mask[i] = 0; + for (i=0; i<64; i++) bp->hash_use_count[i] = 0; + if (num_addrs == 0) { + rx_cfg = bmac_rx_on(dev, 0, 0); + XXDEBUG(("bmac: multi disabled, rx_cfg=%#08x\n", rx_cfg)); + } else { + netdev_for_each_mc_addr(ha, dev) + bmac_addhash(bp, ha->addr); + bmac_update_hash_table_mask(dev, bp); + rx_cfg = bmac_rx_on(dev, 1, 0); + XXDEBUG(("bmac: multi enabled, rx_cfg=%#08x\n", rx_cfg)); + } + } + /* XXDEBUG(("bmac: exit bmac_set_multicast\n")); */ +} +#else /* ifdef SUNHME_MULTICAST */ + +/* The version of set_multicast below was lifted from sunhme.c */ + +static void bmac_set_multicast(struct net_device *dev) +{ + struct netdev_hw_addr *ha; + unsigned short rx_cfg; + u32 crc; + + if((dev->flags & IFF_ALLMULTI) || (netdev_mc_count(dev) > 64)) { + bmwrite(dev, BHASH0, 0xffff); + bmwrite(dev, BHASH1, 0xffff); + bmwrite(dev, BHASH2, 0xffff); + bmwrite(dev, BHASH3, 0xffff); + } else if(dev->flags & IFF_PROMISC) { + rx_cfg = bmread(dev, RXCFG); + rx_cfg |= RxPromiscEnable; + bmwrite(dev, RXCFG, rx_cfg); + } else { + u16 hash_table[4] = { 0 }; + + rx_cfg = bmread(dev, RXCFG); + rx_cfg &= ~RxPromiscEnable; + bmwrite(dev, RXCFG, rx_cfg); + + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(6, ha->addr); + crc >>= 26; + hash_table[crc >> 4] |= 1 << (crc & 0xf); + } + bmwrite(dev, BHASH0, hash_table[0]); + bmwrite(dev, BHASH1, hash_table[1]); + bmwrite(dev, BHASH2, hash_table[2]); + bmwrite(dev, BHASH3, hash_table[3]); + } +} +#endif /* SUNHME_MULTICAST */ + +static int miscintcount; + +static irqreturn_t bmac_misc_intr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + unsigned int status = bmread(dev, STATUS); + if (miscintcount++ < 10) { + XXDEBUG(("bmac_misc_intr\n")); + } + /* XXDEBUG(("bmac_misc_intr, status=%#08x\n", status)); */ + /* bmac_txdma_intr_inner(irq, dev_id); */ + /* if (status & FrameReceived) dev->stats.rx_dropped++; */ + if (status & RxErrorMask) dev->stats.rx_errors++; + if (status & RxCRCCntExp) dev->stats.rx_crc_errors++; + if (status & RxLenCntExp) dev->stats.rx_length_errors++; + if (status & RxOverFlow) dev->stats.rx_over_errors++; + if (status & RxAlignCntExp) dev->stats.rx_frame_errors++; + + /* if (status & FrameSent) dev->stats.tx_dropped++; */ + if (status & TxErrorMask) dev->stats.tx_errors++; + if (status & TxUnderrun) dev->stats.tx_fifo_errors++; + if (status & TxNormalCollExp) dev->stats.collisions++; + return IRQ_HANDLED; +} + +/* + * Procedure for reading EEPROM + */ +#define SROMAddressLength 5 +#define DataInOn 0x0008 +#define DataInOff 0x0000 +#define Clk 0x0002 +#define ChipSelect 0x0001 +#define SDIShiftCount 3 +#define SD0ShiftCount 2 +#define DelayValue 1000 /* number of microseconds */ +#define SROMStartOffset 10 /* this is in words */ +#define SROMReadCount 3 /* number of words to read from SROM */ +#define SROMAddressBits 6 +#define EnetAddressOffset 20 + +static unsigned char +bmac_clock_out_bit(struct net_device *dev) +{ + unsigned short data; + unsigned short val; + + bmwrite(dev, SROMCSR, ChipSelect | Clk); + udelay(DelayValue); + + data = bmread(dev, SROMCSR); + udelay(DelayValue); + val = (data >> SD0ShiftCount) & 1; + + bmwrite(dev, SROMCSR, ChipSelect); + udelay(DelayValue); + + return val; +} + +static void +bmac_clock_in_bit(struct net_device *dev, unsigned int val) +{ + unsigned short data; + + if (val != 0 && val != 1) return; + + data = (val << SDIShiftCount); + bmwrite(dev, SROMCSR, data | ChipSelect ); + udelay(DelayValue); + + bmwrite(dev, SROMCSR, data | ChipSelect | Clk ); + udelay(DelayValue); + + bmwrite(dev, SROMCSR, data | ChipSelect); + udelay(DelayValue); +} + +static void +reset_and_select_srom(struct net_device *dev) +{ + /* first reset */ + bmwrite(dev, SROMCSR, 0); + udelay(DelayValue); + + /* send it the read command (110) */ + bmac_clock_in_bit(dev, 1); + bmac_clock_in_bit(dev, 1); + bmac_clock_in_bit(dev, 0); +} + +static unsigned short +read_srom(struct net_device *dev, unsigned int addr, unsigned int addr_len) +{ + unsigned short data, val; + int i; + + /* send out the address we want to read from */ + for (i = 0; i < addr_len; i++) { + val = addr >> (addr_len-i-1); + bmac_clock_in_bit(dev, val & 1); + } + + /* Now read in the 16-bit data */ + data = 0; + for (i = 0; i < 16; i++) { + val = bmac_clock_out_bit(dev); + data <<= 1; + data |= val; + } + bmwrite(dev, SROMCSR, 0); + + return data; +} + +/* + * It looks like Cogent and SMC use different methods for calculating + * checksums. What a pain.. + */ + +static int +bmac_verify_checksum(struct net_device *dev) +{ + unsigned short data, storedCS; + + reset_and_select_srom(dev); + data = read_srom(dev, 3, SROMAddressBits); + storedCS = ((data >> 8) & 0x0ff) | ((data << 8) & 0xff00); + + return 0; +} + + +static void +bmac_get_station_address(struct net_device *dev, unsigned char *ea) +{ + int i; + unsigned short data; + + for (i = 0; i < 3; i++) + { + reset_and_select_srom(dev); + data = read_srom(dev, i + EnetAddressOffset/2, SROMAddressBits); + ea[2*i] = bitrev8(data & 0x0ff); + ea[2*i+1] = bitrev8((data >> 8) & 0x0ff); + } +} + +static void bmac_reset_and_enable(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + unsigned long flags; + struct sk_buff *skb; + unsigned char *data; + + spin_lock_irqsave(&bp->lock, flags); + bmac_enable_and_reset_chip(dev); + bmac_init_tx_ring(bp); + bmac_init_rx_ring(dev); + bmac_init_chip(dev); + bmac_start_chip(dev); + bmwrite(dev, INTDISABLE, EnableNormal); + bp->sleeping = 0; + + /* + * It seems that the bmac can't receive until it's transmitted + * a packet. So we give it a dummy packet to transmit. + */ + skb = netdev_alloc_skb(dev, ETHERMINPACKET); + if (skb != NULL) { + data = skb_put_zero(skb, ETHERMINPACKET); + memcpy(data, dev->dev_addr, ETH_ALEN); + memcpy(data + ETH_ALEN, dev->dev_addr, ETH_ALEN); + bmac_transmit_packet(skb, dev); + } + spin_unlock_irqrestore(&bp->lock, flags); +} + +static const struct ethtool_ops bmac_ethtool_ops = { + .get_link = ethtool_op_get_link, +}; + +static const struct net_device_ops bmac_netdev_ops = { + .ndo_open = bmac_open, + .ndo_stop = bmac_close, + .ndo_start_xmit = bmac_output, + .ndo_set_rx_mode = bmac_set_multicast, + .ndo_set_mac_address = bmac_set_address, + .ndo_validate_addr = eth_validate_addr, +}; + +static int bmac_probe(struct macio_dev *mdev, const struct of_device_id *match) +{ + int j, rev, ret; + struct bmac_data *bp; + const unsigned char *prop_addr; + unsigned char addr[6]; + u8 macaddr[6]; + struct net_device *dev; + int is_bmac_plus = ((int)match->data) != 0; + + if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) { + printk(KERN_ERR "BMAC: can't use, need 3 addrs and 3 intrs\n"); + return -ENODEV; + } + prop_addr = of_get_property(macio_get_of_node(mdev), + "mac-address", NULL); + if (prop_addr == NULL) { + prop_addr = of_get_property(macio_get_of_node(mdev), + "local-mac-address", NULL); + if (prop_addr == NULL) { + printk(KERN_ERR "BMAC: Can't get mac-address\n"); + return -ENODEV; + } + } + memcpy(addr, prop_addr, sizeof(addr)); + + dev = alloc_etherdev(PRIV_BYTES); + if (!dev) + return -ENOMEM; + + bp = netdev_priv(dev); + SET_NETDEV_DEV(dev, &mdev->ofdev.dev); + macio_set_drvdata(mdev, dev); + + bp->mdev = mdev; + spin_lock_init(&bp->lock); + + if (macio_request_resources(mdev, "bmac")) { + printk(KERN_ERR "BMAC: can't request IO resource !\n"); + goto out_free; + } + + dev->base_addr = (unsigned long) + ioremap(macio_resource_start(mdev, 0), macio_resource_len(mdev, 0)); + if (dev->base_addr == 0) + goto out_release; + + dev->irq = macio_irq(mdev, 0); + + bmac_enable_and_reset_chip(dev); + bmwrite(dev, INTDISABLE, DisableAll); + + rev = addr[0] == 0 && addr[1] == 0xA0; + for (j = 0; j < 6; ++j) + macaddr[j] = rev ? bitrev8(addr[j]): addr[j]; + + eth_hw_addr_set(dev, macaddr); + + /* Enable chip without interrupts for now */ + bmac_enable_and_reset_chip(dev); + bmwrite(dev, INTDISABLE, DisableAll); + + dev->netdev_ops = &bmac_netdev_ops; + dev->ethtool_ops = &bmac_ethtool_ops; + + bmac_get_station_address(dev, addr); + if (bmac_verify_checksum(dev) != 0) + goto err_out_iounmap; + + bp->is_bmac_plus = is_bmac_plus; + bp->tx_dma = ioremap(macio_resource_start(mdev, 1), macio_resource_len(mdev, 1)); + if (!bp->tx_dma) + goto err_out_iounmap; + bp->tx_dma_intr = macio_irq(mdev, 1); + bp->rx_dma = ioremap(macio_resource_start(mdev, 2), macio_resource_len(mdev, 2)); + if (!bp->rx_dma) + goto err_out_iounmap_tx; + bp->rx_dma_intr = macio_irq(mdev, 2); + + bp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(bp + 1); + bp->rx_cmds = bp->tx_cmds + N_TX_RING + 1; + + bp->queue = (struct sk_buff_head *)(bp->rx_cmds + N_RX_RING + 1); + skb_queue_head_init(bp->queue); + + timer_setup(&bp->tx_timeout, bmac_tx_timeout, 0); + + ret = request_irq(dev->irq, bmac_misc_intr, IRQF_NO_AUTOEN, "BMAC-misc", dev); + if (ret) { + printk(KERN_ERR "BMAC: can't get irq %d\n", dev->irq); + goto err_out_iounmap_rx; + } + ret = request_irq(bp->tx_dma_intr, bmac_txdma_intr, 0, "BMAC-txdma", dev); + if (ret) { + printk(KERN_ERR "BMAC: can't get irq %d\n", bp->tx_dma_intr); + goto err_out_irq0; + } + ret = request_irq(bp->rx_dma_intr, bmac_rxdma_intr, 0, "BMAC-rxdma", dev); + if (ret) { + printk(KERN_ERR "BMAC: can't get irq %d\n", bp->rx_dma_intr); + goto err_out_irq1; + } + + /* Mask chip interrupts and disable chip, will be + * re-enabled on open() + */ + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, macio_get_of_node(bp->mdev), 0, 0); + + if (register_netdev(dev) != 0) { + printk(KERN_ERR "BMAC: Ethernet registration failed\n"); + goto err_out_irq2; + } + + printk(KERN_INFO "%s: BMAC%s at %pM", + dev->name, (is_bmac_plus ? "+" : ""), dev->dev_addr); + XXDEBUG((", base_addr=%#0lx", dev->base_addr)); + printk("\n"); + + return 0; + +err_out_irq2: + free_irq(bp->rx_dma_intr, dev); +err_out_irq1: + free_irq(bp->tx_dma_intr, dev); +err_out_irq0: + free_irq(dev->irq, dev); +err_out_iounmap_rx: + iounmap(bp->rx_dma); +err_out_iounmap_tx: + iounmap(bp->tx_dma); +err_out_iounmap: + iounmap((void __iomem *)dev->base_addr); +out_release: + macio_release_resources(mdev); +out_free: + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, macio_get_of_node(bp->mdev), 0, 0); + free_netdev(dev); + + return -ENODEV; +} + +static int bmac_open(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + /* XXDEBUG(("bmac: enter open\n")); */ + /* reset the chip */ + bp->opened = 1; + bmac_reset_and_enable(dev); + enable_irq(dev->irq); + return 0; +} + +static int bmac_close(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + volatile struct dbdma_regs __iomem *td = bp->tx_dma; + unsigned short config; + int i; + + bp->sleeping = 1; + + /* disable rx and tx */ + config = bmread(dev, RXCFG); + bmwrite(dev, RXCFG, (config & ~RxMACEnable)); + + config = bmread(dev, TXCFG); + bmwrite(dev, TXCFG, (config & ~TxMACEnable)); + + bmwrite(dev, INTDISABLE, DisableAll); /* disable all intrs */ + + /* disable rx and tx dma */ + rd->control = cpu_to_le32(DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE)); /* clear run bit */ + td->control = cpu_to_le32(DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE)); /* clear run bit */ + + /* free some skb's */ + XXDEBUG(("bmac: free rx bufs\n")); + for (i=0; irx_bufs[i] != NULL) { + dev_kfree_skb(bp->rx_bufs[i]); + bp->rx_bufs[i] = NULL; + } + } + XXDEBUG(("bmac: free tx bufs\n")); + for (i = 0; itx_bufs[i] != NULL) { + dev_kfree_skb(bp->tx_bufs[i]); + bp->tx_bufs[i] = NULL; + } + } + XXDEBUG(("bmac: all bufs freed\n")); + + bp->opened = 0; + disable_irq(dev->irq); + pmac_call_feature(PMAC_FTR_BMAC_ENABLE, macio_get_of_node(bp->mdev), 0, 0); + + return 0; +} + +static void +bmac_start(struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + int i; + struct sk_buff *skb; + unsigned long flags; + + if (bp->sleeping) + return; + + spin_lock_irqsave(&bp->lock, flags); + while (1) { + i = bp->tx_fill + 1; + if (i >= N_TX_RING) + i = 0; + if (i == bp->tx_empty) + break; + skb = skb_dequeue(bp->queue); + if (skb == NULL) + break; + bmac_transmit_packet(skb, dev); + } + spin_unlock_irqrestore(&bp->lock, flags); +} + +static netdev_tx_t +bmac_output(struct sk_buff *skb, struct net_device *dev) +{ + struct bmac_data *bp = netdev_priv(dev); + skb_queue_tail(bp->queue, skb); + bmac_start(dev); + return NETDEV_TX_OK; +} + +static void bmac_tx_timeout(struct timer_list *t) +{ + struct bmac_data *bp = timer_container_of(bp, t, tx_timeout); + struct net_device *dev = macio_get_drvdata(bp->mdev); + volatile struct dbdma_regs __iomem *td = bp->tx_dma; + volatile struct dbdma_regs __iomem *rd = bp->rx_dma; + volatile struct dbdma_cmd *cp; + unsigned long flags; + unsigned short config, oldConfig; + int i; + + XXDEBUG(("bmac: tx_timeout called\n")); + spin_lock_irqsave(&bp->lock, flags); + bp->timeout_active = 0; + + /* update various counters */ +/* bmac_handle_misc_intrs(bp, 0); */ + + cp = &bp->tx_cmds[bp->tx_empty]; +/* XXDEBUG((KERN_DEBUG "bmac: tx dmastat=%x %x runt=%d pr=%x fs=%x fc=%x\n", */ +/* le32_to_cpu(td->status), le16_to_cpu(cp->xfer_status), bp->tx_bad_runt, */ +/* mb->pr, mb->xmtfs, mb->fifofc)); */ + + /* turn off both tx and rx and reset the chip */ + config = bmread(dev, RXCFG); + bmwrite(dev, RXCFG, (config & ~RxMACEnable)); + config = bmread(dev, TXCFG); + bmwrite(dev, TXCFG, (config & ~TxMACEnable)); + out_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD)); + printk(KERN_ERR "bmac: transmit timeout - resetting\n"); + bmac_enable_and_reset_chip(dev); + + /* restart rx dma */ + cp = bus_to_virt(le32_to_cpu(rd->cmdptr)); + out_le32(&rd->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD)); + out_le16(&cp->xfer_status, 0); + out_le32(&rd->cmdptr, virt_to_bus(cp)); + out_le32(&rd->control, DBDMA_SET(RUN|WAKE)); + + /* fix up the transmit side */ + XXDEBUG((KERN_DEBUG "bmac: tx empty=%d fill=%d fullup=%d\n", + bp->tx_empty, bp->tx_fill, bp->tx_fullup)); + i = bp->tx_empty; + ++dev->stats.tx_errors; + if (i != bp->tx_fill) { + dev_kfree_skb_irq(bp->tx_bufs[i]); + bp->tx_bufs[i] = NULL; + if (++i >= N_TX_RING) i = 0; + bp->tx_empty = i; + } + bp->tx_fullup = 0; + netif_wake_queue(dev); + if (i != bp->tx_fill) { + cp = &bp->tx_cmds[i]; + out_le16(&cp->xfer_status, 0); + out_le16(&cp->command, OUTPUT_LAST); + out_le32(&td->cmdptr, virt_to_bus(cp)); + out_le32(&td->control, DBDMA_SET(RUN)); + /* bmac_set_timeout(dev); */ + XXDEBUG((KERN_DEBUG "bmac: starting %d\n", i)); + } + + /* turn it back on */ + oldConfig = bmread(dev, RXCFG); + bmwrite(dev, RXCFG, oldConfig | RxMACEnable ); + oldConfig = bmread(dev, TXCFG); + bmwrite(dev, TXCFG, oldConfig | TxMACEnable ); + + spin_unlock_irqrestore(&bp->lock, flags); +} + +#if 0 +static void dump_dbdma(volatile struct dbdma_cmd *cp,int count) +{ + int i,*ip; + + for (i=0;i< count;i++) { + ip = (int*)(cp+i); + + printk("dbdma req 0x%x addr 0x%x baddr 0x%x xfer/res 0x%x\n", + le32_to_cpup(ip+0), + le32_to_cpup(ip+1), + le32_to_cpup(ip+2), + le32_to_cpup(ip+3)); + } + +} +#endif + +#if 0 +static int +bmac_proc_info(char *buffer, char **start, off_t offset, int length) +{ + int len = 0; + off_t pos = 0; + off_t begin = 0; + int i; + + if (bmac_devs == NULL) + return -ENOSYS; + + len += sprintf(buffer, "BMAC counters & registers\n"); + + for (i = 0; i offset+length) break; + } + + *start = buffer + (offset - begin); + len -= (offset - begin); + + if (len > length) len = length; + + return len; +} +#endif + +static void bmac_remove(struct macio_dev *mdev) +{ + struct net_device *dev = macio_get_drvdata(mdev); + struct bmac_data *bp = netdev_priv(dev); + + unregister_netdev(dev); + + free_irq(dev->irq, dev); + free_irq(bp->tx_dma_intr, dev); + free_irq(bp->rx_dma_intr, dev); + + iounmap((void __iomem *)dev->base_addr); + iounmap(bp->tx_dma); + iounmap(bp->rx_dma); + + macio_release_resources(mdev); + + free_netdev(dev); +} + +static const struct of_device_id bmac_match[] = +{ + { + .name = "bmac", + .data = (void *)0, + }, + { + .type = "network", + .compatible = "bmac+", + .data = (void *)1, + }, + {}, +}; +MODULE_DEVICE_TABLE (of, bmac_match); + +static struct macio_driver bmac_driver = +{ + .driver = { + .name = "bmac", + .owner = THIS_MODULE, + .of_match_table = bmac_match, + }, + .probe = bmac_probe, + .remove = bmac_remove, +#ifdef CONFIG_PM + .suspend = bmac_suspend, + .resume = bmac_resume, +#endif +}; + + +static int __init bmac_init(void) +{ + if (bmac_emergency_rxbuf == NULL) { + bmac_emergency_rxbuf = kmalloc(RX_BUFLEN, GFP_KERNEL); + if (bmac_emergency_rxbuf == NULL) + return -ENOMEM; + } + + return macio_register_driver(&bmac_driver); +} + +static void __exit bmac_exit(void) +{ + macio_unregister_driver(&bmac_driver); + + kfree(bmac_emergency_rxbuf); + bmac_emergency_rxbuf = NULL; +} + +MODULE_AUTHOR("Randy Gobbel/Paul Mackerras"); +MODULE_DESCRIPTION("PowerMac BMAC ethernet driver."); +MODULE_LICENSE("GPL"); + +module_init(bmac_init); +module_exit(bmac_exit); diff --git a/drivers/net/ethernet/apple/bmac.h b/drivers/net/ethernet/apple/bmac.h new file mode 100644 index 000000000..f8826c4ce --- /dev/null +++ b/drivers/net/ethernet/apple/bmac.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * mace.h - definitions for the registers in the "Big Mac" + * Ethernet controller found in PowerMac G3 models. + * + * Copyright (C) 1998 Randy Gobbel. + */ + +/* The "Big MAC" appears to have some parts in common with the Sun "Happy Meal" + * (HME) controller. See sunhme.h + */ + + +/* register offsets */ + +/* global status and control */ +#define XIFC 0x000 /* low-level interface control */ +# define TxOutputEnable 0x0001 /* output driver enable */ +# define XIFLoopback 0x0002 /* Loopback-mode XIF enable */ +# define MIILoopback 0x0004 /* Loopback-mode MII enable */ +# define MIILoopbackBits 0x0006 +# define MIIBuffDisable 0x0008 /* MII receive buffer disable */ +# define SQETestEnable 0x0010 /* SQE test enable */ +# define SQETimeWindow 0x03e0 /* SQE time window */ +# define XIFLanceMode 0x0010 /* Lance mode enable */ +# define XIFLanceIPG0 0x03e0 /* Lance mode IPG0 */ +#define TXFIFOCSR 0x100 /* transmit FIFO control */ +# define TxFIFOEnable 0x0001 +#define TXTH 0x110 /* transmit threshold */ +# define TxThreshold 0x0004 +#define RXFIFOCSR 0x120 /* receive FIFO control */ +# define RxFIFOEnable 0x0001 +#define MEMADD 0x130 /* memory address, unknown function */ +#define MEMDATAHI 0x140 /* memory data high, presently unused in driver */ +#define MEMDATALO 0x150 /* memory data low, presently unused in driver */ +#define XCVRIF 0x160 /* transceiver interface control */ +# define COLActiveLow 0x0002 +# define SerialMode 0x0004 +# define ClkBit 0x0008 +# define LinkStatus 0x0100 +#define CHIPID 0x170 /* chip ID */ +#define MIFCSR 0x180 /* ??? */ +#define SROMCSR 0x190 /* SROM control */ +# define ChipSelect 0x0001 +# define Clk 0x0002 +#define TXPNTR 0x1a0 /* transmit pointer */ +#define RXPNTR 0x1b0 /* receive pointer */ +#define STATUS 0x200 /* status--reading this clears it */ +#define INTDISABLE 0x210 /* interrupt enable/disable control */ +/* bits below are the same in both STATUS and INTDISABLE registers */ +# define FrameReceived 0x00000001 /* Received a frame */ +# define RxFrameCntExp 0x00000002 /* Receive frame counter expired */ +# define RxAlignCntExp 0x00000004 /* Align-error counter expired */ +# define RxCRCCntExp 0x00000008 /* CRC-error counter expired */ +# define RxLenCntExp 0x00000010 /* Length-error counter expired */ +# define RxOverFlow 0x00000020 /* Receive FIFO overflow */ +# define RxCodeViolation 0x00000040 /* Code-violation counter expired */ +# define SQETestError 0x00000080 /* Test error in XIF for SQE */ +# define FrameSent 0x00000100 /* Transmitted a frame */ +# define TxUnderrun 0x00000200 /* Transmit FIFO underrun */ +# define TxMaxSizeError 0x00000400 /* Max-packet size error */ +# define TxNormalCollExp 0x00000800 /* Normal-collision counter expired */ +# define TxExcessCollExp 0x00001000 /* Excess-collision counter expired */ +# define TxLateCollExp 0x00002000 /* Late-collision counter expired */ +# define TxNetworkCollExp 0x00004000 /* First-collision counter expired */ +# define TxDeferTimerExp 0x00008000 /* Defer-timer expired */ +# define RxFIFOToHost 0x00010000 /* Data moved from FIFO to host */ +# define RxNoDescriptors 0x00020000 /* No more receive descriptors */ +# define RxDMAError 0x00040000 /* Error during receive DMA */ +# define RxDMALateErr 0x00080000 /* Receive DMA, data late */ +# define RxParityErr 0x00100000 /* Parity error during receive DMA */ +# define RxTagError 0x00200000 /* Tag error during receive DMA */ +# define TxEOPError 0x00400000 /* Tx descriptor did not have EOP set */ +# define MIFIntrEvent 0x00800000 /* MIF is signaling an interrupt */ +# define TxHostToFIFO 0x01000000 /* Data moved from host to FIFO */ +# define TxFIFOAllSent 0x02000000 /* Transmitted all packets in FIFO */ +# define TxDMAError 0x04000000 /* Error during transmit DMA */ +# define TxDMALateError 0x08000000 /* Late error during transmit DMA */ +# define TxParityError 0x10000000 /* Parity error during transmit DMA */ +# define TxTagError 0x20000000 /* Tag error during transmit DMA */ +# define PIOError 0x40000000 /* PIO access got an error */ +# define PIOParityError 0x80000000 /* PIO access got a parity error */ +# define DisableAll 0xffffffff +# define EnableAll 0x00000000 +/* # define NormalIntEvents ~(FrameReceived | FrameSent | TxUnderrun) */ +# define EnableNormal ~(FrameReceived | FrameSent) +# define EnableErrors (FrameReceived | FrameSent) +# define RxErrorMask (RxFrameCntExp | RxAlignCntExp | RxCRCCntExp | \ + RxLenCntExp | RxOverFlow | RxCodeViolation) +# define TxErrorMask (TxUnderrun | TxMaxSizeError | TxExcessCollExp | \ + TxLateCollExp | TxNetworkCollExp | TxDeferTimerExp) + +/* transmit control */ +#define TXRST 0x420 /* transmit reset */ +# define TxResetBit 0x0001 +#define TXCFG 0x430 /* transmit configuration control*/ +# define TxMACEnable 0x0001 /* output driver enable */ +# define TxSlowMode 0x0020 /* enable slow mode */ +# define TxIgnoreColl 0x0040 /* ignore transmit collisions */ +# define TxNoFCS 0x0080 /* do not emit FCS */ +# define TxNoBackoff 0x0100 /* no backoff in case of collisions */ +# define TxFullDuplex 0x0200 /* enable full-duplex */ +# define TxNeverGiveUp 0x0400 /* don't give up on transmits */ +#define IPG1 0x440 /* Inter-packet gap 1 */ +#define IPG2 0x450 /* Inter-packet gap 2 */ +#define ALIMIT 0x460 /* Transmit attempt limit */ +#define SLOT 0x470 /* Transmit slot time */ +#define PALEN 0x480 /* Size of transmit preamble */ +#define PAPAT 0x490 /* Pattern for transmit preamble */ +#define TXSFD 0x4a0 /* Transmit frame delimiter */ +#define JAM 0x4b0 /* Jam size */ +#define TXMAX 0x4c0 /* Transmit max pkt size */ +#define TXMIN 0x4d0 /* Transmit min pkt size */ +#define PAREG 0x4e0 /* Count of transmit peak attempts */ +#define DCNT 0x4f0 /* Transmit defer timer */ +#define NCCNT 0x500 /* Transmit normal-collision counter */ +#define NTCNT 0x510 /* Transmit first-collision counter */ +#define EXCNT 0x520 /* Transmit excess-collision counter */ +#define LTCNT 0x530 /* Transmit late-collision counter */ +#define RSEED 0x540 /* Transmit random number seed */ +#define TXSM 0x550 /* Transmit state machine */ + +/* receive control */ +#define RXRST 0x620 /* receive reset */ +# define RxResetValue 0x0000 +#define RXCFG 0x630 /* receive configuration control */ +# define RxMACEnable 0x0001 /* receiver overall enable */ +# define RxCFGReserved 0x0004 +# define RxPadStripEnab 0x0020 /* enable pad byte stripping */ +# define RxPromiscEnable 0x0040 /* turn on promiscuous mode */ +# define RxNoErrCheck 0x0080 /* disable receive error checking */ +# define RxCRCNoStrip 0x0100 /* disable auto-CRC-stripping */ +# define RxRejectOwnPackets 0x0200 /* don't receive our own packets */ +# define RxGrpPromisck 0x0400 /* enable group promiscuous mode */ +# define RxHashFilterEnable 0x0800 /* enable hash filter */ +# define RxAddrFilterEnable 0x1000 /* enable address filter */ +#define RXMAX 0x640 /* Max receive packet size */ +#define RXMIN 0x650 /* Min receive packet size */ +#define MADD2 0x660 /* our enet address, high part */ +#define MADD1 0x670 /* our enet address, middle part */ +#define MADD0 0x680 /* our enet address, low part */ +#define FRCNT 0x690 /* receive frame counter */ +#define LECNT 0x6a0 /* Receive excess length error counter */ +#define AECNT 0x6b0 /* Receive misaligned error counter */ +#define FECNT 0x6c0 /* Receive CRC error counter */ +#define RXSM 0x6d0 /* Receive state machine */ +#define RXCV 0x6e0 /* Receive code violation */ + +#define BHASH3 0x700 /* multicast hash register */ +#define BHASH2 0x710 /* multicast hash register */ +#define BHASH1 0x720 /* multicast hash register */ +#define BHASH0 0x730 /* multicast hash register */ + +#define AFR2 0x740 /* address filtering setup? */ +#define AFR1 0x750 /* address filtering setup? */ +#define AFR0 0x760 /* address filtering setup? */ +#define AFCR 0x770 /* address filter compare register? */ +# define EnableAllCompares 0x0fff + +/* bits in XIFC */ diff --git a/drivers/net/ethernet/apple/mace.c b/drivers/net/ethernet/apple/mace.c new file mode 100644 index 000000000..af26905e4 --- /dev/null +++ b/drivers/net/ethernet/apple/mace.c @@ -0,0 +1,1029 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Network device driver for the MACE ethernet controller on + * Apple Powermacs. Assumes it's under a DBDMA controller. + * + * Copyright (C) 1996 Paul Mackerras. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mace.h" + +static int port_aaui = -1; + +#define N_RX_RING 8 +#define N_TX_RING 6 +#define MAX_TX_ACTIVE 1 +#define NCMDS_TX 1 /* dma commands per element in tx ring */ +#define RX_BUFLEN (ETH_FRAME_LEN + 8) +#define TX_TIMEOUT HZ /* 1 second */ + +/* Chip rev needs workaround on HW & multicast addr change */ +#define BROKEN_ADDRCHG_REV 0x0941 + +/* Bits in transmit DMA status */ +#define TX_DMA_ERR 0x80 + +struct mace_data { + volatile struct mace __iomem *mace; + volatile struct dbdma_regs __iomem *tx_dma; + int tx_dma_intr; + volatile struct dbdma_regs __iomem *rx_dma; + int rx_dma_intr; + volatile struct dbdma_cmd *tx_cmds; /* xmit dma command list */ + volatile struct dbdma_cmd *rx_cmds; /* recv dma command list */ + struct sk_buff *rx_bufs[N_RX_RING]; + int rx_fill; + int rx_empty; + struct sk_buff *tx_bufs[N_TX_RING]; + int tx_fill; + int tx_empty; + unsigned char maccc; + unsigned char tx_fullup; + unsigned char tx_active; + unsigned char tx_bad_runt; + struct timer_list tx_timeout; + int timeout_active; + int port_aaui; + int chipid; + struct macio_dev *mdev; + spinlock_t lock; +}; + +/* + * Number of bytes of private data per MACE: allow enough for + * the rx and tx dma commands plus a branch dma command each, + * and another 16 bytes to allow us to align the dma command + * buffers on a 16 byte boundary. + */ +#define PRIV_BYTES (sizeof(struct mace_data) \ + + (N_RX_RING + NCMDS_TX * N_TX_RING + 3) * sizeof(struct dbdma_cmd)) + +static int mace_open(struct net_device *dev); +static int mace_close(struct net_device *dev); +static netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev); +static void mace_set_multicast(struct net_device *dev); +static void mace_reset(struct net_device *dev); +static int mace_set_address(struct net_device *dev, void *addr); +static irqreturn_t mace_interrupt(int irq, void *dev_id); +static irqreturn_t mace_txdma_intr(int irq, void *dev_id); +static irqreturn_t mace_rxdma_intr(int irq, void *dev_id); +static void mace_set_timeout(struct net_device *dev); +static void mace_tx_timeout(struct timer_list *t); +static inline void dbdma_reset(volatile struct dbdma_regs __iomem *dma); +static inline void mace_clean_rings(struct mace_data *mp); +static void __mace_set_address(struct net_device *dev, const void *addr); + +/* + * If we can't get a skbuff when we need it, we use this area for DMA. + */ +static unsigned char *dummy_buf; + +static const struct net_device_ops mace_netdev_ops = { + .ndo_open = mace_open, + .ndo_stop = mace_close, + .ndo_start_xmit = mace_xmit_start, + .ndo_set_rx_mode = mace_set_multicast, + .ndo_set_mac_address = mace_set_address, + .ndo_validate_addr = eth_validate_addr, +}; + +static int mace_probe(struct macio_dev *mdev, const struct of_device_id *match) +{ + struct device_node *mace = macio_get_of_node(mdev); + struct net_device *dev; + struct mace_data *mp; + const unsigned char *addr; + u8 macaddr[ETH_ALEN]; + int j, rev, rc = -EBUSY; + + if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) { + printk(KERN_ERR "can't use MACE %pOF: need 3 addrs and 3 irqs\n", + mace); + return -ENODEV; + } + + addr = of_get_property(mace, "mac-address", NULL); + if (addr == NULL) { + addr = of_get_property(mace, "local-mac-address", NULL); + if (addr == NULL) { + printk(KERN_ERR "Can't get mac-address for MACE %pOF\n", + mace); + return -ENODEV; + } + } + + /* + * lazy allocate the driver-wide dummy buffer. (Note that we + * never have more than one MACE in the system anyway) + */ + if (dummy_buf == NULL) { + dummy_buf = kmalloc(RX_BUFLEN+2, GFP_KERNEL); + if (dummy_buf == NULL) + return -ENOMEM; + } + + if (macio_request_resources(mdev, "mace")) { + printk(KERN_ERR "MACE: can't request IO resources !\n"); + return -EBUSY; + } + + dev = alloc_etherdev(PRIV_BYTES); + if (!dev) { + rc = -ENOMEM; + goto err_release; + } + SET_NETDEV_DEV(dev, &mdev->ofdev.dev); + + mp = netdev_priv(dev); + mp->mdev = mdev; + macio_set_drvdata(mdev, dev); + + dev->base_addr = macio_resource_start(mdev, 0); + mp->mace = ioremap(dev->base_addr, 0x1000); + if (mp->mace == NULL) { + printk(KERN_ERR "MACE: can't map IO resources !\n"); + rc = -ENOMEM; + goto err_free; + } + dev->irq = macio_irq(mdev, 0); + + rev = addr[0] == 0 && addr[1] == 0xA0; + for (j = 0; j < 6; ++j) { + macaddr[j] = rev ? bitrev8(addr[j]): addr[j]; + } + eth_hw_addr_set(dev, macaddr); + mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) | + in_8(&mp->mace->chipid_lo); + + + mp = netdev_priv(dev); + mp->maccc = ENXMT | ENRCV; + + mp->tx_dma = ioremap(macio_resource_start(mdev, 1), 0x1000); + if (mp->tx_dma == NULL) { + printk(KERN_ERR "MACE: can't map TX DMA resources !\n"); + rc = -ENOMEM; + goto err_unmap_io; + } + mp->tx_dma_intr = macio_irq(mdev, 1); + + mp->rx_dma = ioremap(macio_resource_start(mdev, 2), 0x1000); + if (mp->rx_dma == NULL) { + printk(KERN_ERR "MACE: can't map RX DMA resources !\n"); + rc = -ENOMEM; + goto err_unmap_tx_dma; + } + mp->rx_dma_intr = macio_irq(mdev, 2); + + mp->tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(mp + 1); + mp->rx_cmds = mp->tx_cmds + NCMDS_TX * N_TX_RING + 1; + + memset((char *) mp->tx_cmds, 0, + (NCMDS_TX*N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd)); + timer_setup(&mp->tx_timeout, mace_tx_timeout, 0); + spin_lock_init(&mp->lock); + mp->timeout_active = 0; + + if (port_aaui >= 0) + mp->port_aaui = port_aaui; + else { + /* Apple Network Server uses the AAUI port */ + if (of_machine_is_compatible("AAPL,ShinerESB")) + mp->port_aaui = 1; + else { +#ifdef CONFIG_MACE_AAUI_PORT + mp->port_aaui = 1; +#else + mp->port_aaui = 0; +#endif + } + } + + dev->netdev_ops = &mace_netdev_ops; + + /* + * Most of what is below could be moved to mace_open() + */ + mace_reset(dev); + + rc = request_irq(dev->irq, mace_interrupt, 0, "MACE", dev); + if (rc) { + printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq); + goto err_unmap_rx_dma; + } + rc = request_irq(mp->tx_dma_intr, mace_txdma_intr, 0, "MACE-txdma", dev); + if (rc) { + printk(KERN_ERR "MACE: can't get irq %d\n", mp->tx_dma_intr); + goto err_free_irq; + } + rc = request_irq(mp->rx_dma_intr, mace_rxdma_intr, 0, "MACE-rxdma", dev); + if (rc) { + printk(KERN_ERR "MACE: can't get irq %d\n", mp->rx_dma_intr); + goto err_free_tx_irq; + } + + rc = register_netdev(dev); + if (rc) { + printk(KERN_ERR "MACE: Cannot register net device, aborting.\n"); + goto err_free_rx_irq; + } + + printk(KERN_INFO "%s: MACE at %pM, chip revision %d.%d\n", + dev->name, dev->dev_addr, + mp->chipid >> 8, mp->chipid & 0xff); + + return 0; + + err_free_rx_irq: + free_irq(macio_irq(mdev, 2), dev); + err_free_tx_irq: + free_irq(macio_irq(mdev, 1), dev); + err_free_irq: + free_irq(macio_irq(mdev, 0), dev); + err_unmap_rx_dma: + iounmap(mp->rx_dma); + err_unmap_tx_dma: + iounmap(mp->tx_dma); + err_unmap_io: + iounmap(mp->mace); + err_free: + free_netdev(dev); + err_release: + macio_release_resources(mdev); + + return rc; +} + +static void mace_remove(struct macio_dev *mdev) +{ + struct net_device *dev = macio_get_drvdata(mdev); + struct mace_data *mp; + + BUG_ON(dev == NULL); + + macio_set_drvdata(mdev, NULL); + + mp = netdev_priv(dev); + + unregister_netdev(dev); + + free_irq(dev->irq, dev); + free_irq(mp->tx_dma_intr, dev); + free_irq(mp->rx_dma_intr, dev); + + iounmap(mp->rx_dma); + iounmap(mp->tx_dma); + iounmap(mp->mace); + + free_netdev(dev); + + macio_release_resources(mdev); +} + +static void dbdma_reset(volatile struct dbdma_regs __iomem *dma) +{ + int i; + + out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16); + + /* + * Yes this looks peculiar, but apparently it needs to be this + * way on some machines. + */ + for (i = 200; i > 0; --i) + if (le32_to_cpu(dma->control) & RUN) + udelay(1); +} + +static void mace_reset(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + int i; + + /* soft-reset the chip */ + i = 200; + while (--i) { + out_8(&mb->biucc, SWRST); + if (in_8(&mb->biucc) & SWRST) { + udelay(10); + continue; + } + break; + } + if (!i) { + printk(KERN_ERR "mace: cannot reset chip!\n"); + return; + } + + out_8(&mb->imr, 0xff); /* disable all intrs for now */ + i = in_8(&mb->ir); + out_8(&mb->maccc, 0); /* turn off tx, rx */ + + out_8(&mb->biucc, XMTSP_64); + out_8(&mb->utr, RTRD); + out_8(&mb->fifocc, RCVFW_32 | XMTFW_16 | XMTFWU | RCVFWU | XMTBRST); + out_8(&mb->xmtfc, AUTO_PAD_XMIT); /* auto-pad short frames */ + out_8(&mb->rcvfc, 0); + + /* load up the hardware address */ + __mace_set_address(dev, dev->dev_addr); + + /* clear the multicast filter */ + if (mp->chipid == BROKEN_ADDRCHG_REV) + out_8(&mb->iac, LOGADDR); + else { + out_8(&mb->iac, ADDRCHG | LOGADDR); + while ((in_8(&mb->iac) & ADDRCHG) != 0) + ; + } + for (i = 0; i < 8; ++i) + out_8(&mb->ladrf, 0); + + /* done changing address */ + if (mp->chipid != BROKEN_ADDRCHG_REV) + out_8(&mb->iac, 0); + + if (mp->port_aaui) + out_8(&mb->plscc, PORTSEL_AUI + ENPLSIO); + else + out_8(&mb->plscc, PORTSEL_GPSI + ENPLSIO); +} + +static void __mace_set_address(struct net_device *dev, const void *addr) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + const unsigned char *p = addr; + u8 macaddr[ETH_ALEN]; + int i; + + /* load up the hardware address */ + if (mp->chipid == BROKEN_ADDRCHG_REV) + out_8(&mb->iac, PHYADDR); + else { + out_8(&mb->iac, ADDRCHG | PHYADDR); + while ((in_8(&mb->iac) & ADDRCHG) != 0) + ; + } + for (i = 0; i < 6; ++i) + out_8(&mb->padr, macaddr[i] = p[i]); + + eth_hw_addr_set(dev, macaddr); + + if (mp->chipid != BROKEN_ADDRCHG_REV) + out_8(&mb->iac, 0); +} + +static int mace_set_address(struct net_device *dev, void *addr) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + unsigned long flags; + + spin_lock_irqsave(&mp->lock, flags); + + __mace_set_address(dev, addr); + + /* note: setting ADDRCHG clears ENRCV */ + out_8(&mb->maccc, mp->maccc); + + spin_unlock_irqrestore(&mp->lock, flags); + return 0; +} + +static inline void mace_clean_rings(struct mace_data *mp) +{ + int i; + + /* free some skb's */ + for (i = 0; i < N_RX_RING; ++i) { + if (mp->rx_bufs[i] != NULL) { + dev_kfree_skb(mp->rx_bufs[i]); + mp->rx_bufs[i] = NULL; + } + } + for (i = mp->tx_empty; i != mp->tx_fill; ) { + dev_kfree_skb(mp->tx_bufs[i]); + if (++i >= N_TX_RING) + i = 0; + } +} + +static int mace_open(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + volatile struct dbdma_regs __iomem *rd = mp->rx_dma; + volatile struct dbdma_regs __iomem *td = mp->tx_dma; + volatile struct dbdma_cmd *cp; + int i; + struct sk_buff *skb; + unsigned char *data; + + /* reset the chip */ + mace_reset(dev); + + /* initialize list of sk_buffs for receiving and set up recv dma */ + mace_clean_rings(mp); + memset((char *)mp->rx_cmds, 0, N_RX_RING * sizeof(struct dbdma_cmd)); + cp = mp->rx_cmds; + for (i = 0; i < N_RX_RING - 1; ++i) { + skb = netdev_alloc_skb(dev, RX_BUFLEN + 2); + if (!skb) { + data = dummy_buf; + } else { + skb_reserve(skb, 2); /* so IP header lands on 4-byte bdry */ + data = skb->data; + } + mp->rx_bufs[i] = skb; + cp->req_count = cpu_to_le16(RX_BUFLEN); + cp->command = cpu_to_le16(INPUT_LAST + INTR_ALWAYS); + cp->phy_addr = cpu_to_le32(virt_to_bus(data)); + cp->xfer_status = 0; + ++cp; + } + mp->rx_bufs[i] = NULL; + cp->command = cpu_to_le16(DBDMA_STOP); + mp->rx_fill = i; + mp->rx_empty = 0; + + /* Put a branch back to the beginning of the receive command list */ + ++cp; + cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS); + cp->cmd_dep = cpu_to_le32(virt_to_bus(mp->rx_cmds)); + + /* start rx dma */ + out_le32(&rd->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ + out_le32(&rd->cmdptr, virt_to_bus(mp->rx_cmds)); + out_le32(&rd->control, (RUN << 16) | RUN); + + /* put a branch at the end of the tx command list */ + cp = mp->tx_cmds + NCMDS_TX * N_TX_RING; + cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS); + cp->cmd_dep = cpu_to_le32(virt_to_bus(mp->tx_cmds)); + + /* reset tx dma */ + out_le32(&td->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + out_le32(&td->cmdptr, virt_to_bus(mp->tx_cmds)); + mp->tx_fill = 0; + mp->tx_empty = 0; + mp->tx_fullup = 0; + mp->tx_active = 0; + mp->tx_bad_runt = 0; + + /* turn it on! */ + out_8(&mb->maccc, mp->maccc); + /* enable all interrupts except receive interrupts */ + out_8(&mb->imr, RCVINT); + + return 0; +} + +static int mace_close(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + volatile struct dbdma_regs __iomem *rd = mp->rx_dma; + volatile struct dbdma_regs __iomem *td = mp->tx_dma; + + /* disable rx and tx */ + out_8(&mb->maccc, 0); + out_8(&mb->imr, 0xff); /* disable all intrs */ + + /* disable rx and tx dma */ + rd->control = cpu_to_le32((RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ + td->control = cpu_to_le32((RUN|PAUSE|FLUSH|WAKE) << 16); /* clear run bit */ + + mace_clean_rings(mp); + + return 0; +} + +static inline void mace_set_timeout(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + + if (mp->timeout_active) + timer_delete(&mp->tx_timeout); + mp->tx_timeout.expires = jiffies + TX_TIMEOUT; + add_timer(&mp->tx_timeout); + mp->timeout_active = 1; +} + +static netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *td = mp->tx_dma; + volatile struct dbdma_cmd *cp, *np; + unsigned long flags; + int fill, next, len; + + /* see if there's a free slot in the tx ring */ + spin_lock_irqsave(&mp->lock, flags); + fill = mp->tx_fill; + next = fill + 1; + if (next >= N_TX_RING) + next = 0; + if (next == mp->tx_empty) { + netif_stop_queue(dev); + mp->tx_fullup = 1; + spin_unlock_irqrestore(&mp->lock, flags); + return NETDEV_TX_BUSY; /* can't take it at the moment */ + } + spin_unlock_irqrestore(&mp->lock, flags); + + /* partially fill in the dma command block */ + len = skb->len; + if (len > ETH_FRAME_LEN) { + printk(KERN_DEBUG "mace: xmit frame too long (%d)\n", len); + len = ETH_FRAME_LEN; + } + mp->tx_bufs[fill] = skb; + cp = mp->tx_cmds + NCMDS_TX * fill; + cp->req_count = cpu_to_le16(len); + cp->phy_addr = cpu_to_le32(virt_to_bus(skb->data)); + + np = mp->tx_cmds + NCMDS_TX * next; + out_le16(&np->command, DBDMA_STOP); + + /* poke the tx dma channel */ + spin_lock_irqsave(&mp->lock, flags); + mp->tx_fill = next; + if (!mp->tx_bad_runt && mp->tx_active < MAX_TX_ACTIVE) { + out_le16(&cp->xfer_status, 0); + out_le16(&cp->command, OUTPUT_LAST); + out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + ++mp->tx_active; + mace_set_timeout(dev); + } + if (++next >= N_TX_RING) + next = 0; + if (next == mp->tx_empty) + netif_stop_queue(dev); + spin_unlock_irqrestore(&mp->lock, flags); + + return NETDEV_TX_OK; +} + +static void mace_set_multicast(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + int i; + u32 crc; + unsigned long flags; + + spin_lock_irqsave(&mp->lock, flags); + mp->maccc &= ~PROM; + if (dev->flags & IFF_PROMISC) { + mp->maccc |= PROM; + } else { + unsigned char multicast_filter[8]; + struct netdev_hw_addr *ha; + + if (dev->flags & IFF_ALLMULTI) { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0xff; + } else { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0; + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(6, ha->addr); + i = crc >> 26; /* bit number in multicast_filter */ + multicast_filter[i >> 3] |= 1 << (i & 7); + } + } +#if 0 + printk("Multicast filter :"); + for (i = 0; i < 8; i++) + printk("%02x ", multicast_filter[i]); + printk("\n"); +#endif + + if (mp->chipid == BROKEN_ADDRCHG_REV) + out_8(&mb->iac, LOGADDR); + else { + out_8(&mb->iac, ADDRCHG | LOGADDR); + while ((in_8(&mb->iac) & ADDRCHG) != 0) + ; + } + for (i = 0; i < 8; ++i) + out_8(&mb->ladrf, multicast_filter[i]); + if (mp->chipid != BROKEN_ADDRCHG_REV) + out_8(&mb->iac, 0); + } + /* reset maccc */ + out_8(&mb->maccc, mp->maccc); + spin_unlock_irqrestore(&mp->lock, flags); +} + +static void mace_handle_misc_intrs(struct mace_data *mp, int intr, struct net_device *dev) +{ + volatile struct mace __iomem *mb = mp->mace; + static int mace_babbles, mace_jabbers; + + if (intr & MPCO) + dev->stats.rx_missed_errors += 256; + dev->stats.rx_missed_errors += in_8(&mb->mpc); /* reading clears it */ + if (intr & RNTPCO) + dev->stats.rx_length_errors += 256; + dev->stats.rx_length_errors += in_8(&mb->rntpc); /* reading clears it */ + if (intr & CERR) + ++dev->stats.tx_heartbeat_errors; + if (intr & BABBLE) + if (mace_babbles++ < 4) + printk(KERN_DEBUG "mace: babbling transmitter\n"); + if (intr & JABBER) + if (mace_jabbers++ < 4) + printk(KERN_DEBUG "mace: jabbering transceiver\n"); +} + +static irqreturn_t mace_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace_data *mp = netdev_priv(dev); + volatile struct mace __iomem *mb = mp->mace; + volatile struct dbdma_regs __iomem *td = mp->tx_dma; + volatile struct dbdma_cmd *cp; + int intr, fs, i, stat, x; + int xcount, dstat; + unsigned long flags; + /* static int mace_last_fs, mace_last_xcount; */ + + spin_lock_irqsave(&mp->lock, flags); + intr = in_8(&mb->ir); /* read interrupt register */ + in_8(&mb->xmtrc); /* get retries */ + mace_handle_misc_intrs(mp, intr, dev); + + i = mp->tx_empty; + while (in_8(&mb->pr) & XMTSV) { + timer_delete(&mp->tx_timeout); + mp->timeout_active = 0; + /* + * Clear any interrupt indication associated with this status + * word. This appears to unlatch any error indication from + * the DMA controller. + */ + intr = in_8(&mb->ir); + if (intr != 0) + mace_handle_misc_intrs(mp, intr, dev); + if (mp->tx_bad_runt) { + fs = in_8(&mb->xmtfs); + mp->tx_bad_runt = 0; + out_8(&mb->xmtfc, AUTO_PAD_XMIT); + continue; + } + dstat = le32_to_cpu(td->status); + /* stop DMA controller */ + out_le32(&td->control, RUN << 16); + /* + * xcount is the number of complete frames which have been + * written to the fifo but for which status has not been read. + */ + xcount = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK; + if (xcount == 0 || (dstat & DEAD)) { + /* + * If a packet was aborted before the DMA controller has + * finished transferring it, it seems that there are 2 bytes + * which are stuck in some buffer somewhere. These will get + * transmitted as soon as we read the frame status (which + * reenables the transmit data transfer request). Turning + * off the DMA controller and/or resetting the MACE doesn't + * help. So we disable auto-padding and FCS transmission + * so the two bytes will only be a runt packet which should + * be ignored by other stations. + */ + out_8(&mb->xmtfc, DXMTFCS); + } + fs = in_8(&mb->xmtfs); + if ((fs & XMTSV) == 0) { + printk(KERN_ERR "mace: xmtfs not valid! (fs=%x xc=%d ds=%x)\n", + fs, xcount, dstat); + mace_reset(dev); + /* + * XXX mace likes to hang the machine after a xmtfs error. + * This is hard to reproduce, resetting *may* help + */ + } + cp = mp->tx_cmds + NCMDS_TX * i; + stat = le16_to_cpu(cp->xfer_status); + if ((fs & (UFLO|LCOL|LCAR|RTRY)) || (dstat & DEAD) || xcount == 0) { + /* + * Check whether there were in fact 2 bytes written to + * the transmit FIFO. + */ + udelay(1); + x = (in_8(&mb->fifofc) >> XMTFC_SH) & XMTFC_MASK; + if (x != 0) { + /* there were two bytes with an end-of-packet indication */ + mp->tx_bad_runt = 1; + mace_set_timeout(dev); + } else { + /* + * Either there weren't the two bytes buffered up, or they + * didn't have an end-of-packet indication. + * We flush the transmit FIFO just in case (by setting the + * XMTFWU bit with the transmitter disabled). + */ + out_8(&mb->maccc, in_8(&mb->maccc) & ~ENXMT); + out_8(&mb->fifocc, in_8(&mb->fifocc) | XMTFWU); + udelay(1); + out_8(&mb->maccc, in_8(&mb->maccc) | ENXMT); + out_8(&mb->xmtfc, AUTO_PAD_XMIT); + } + } + /* dma should have finished */ + if (i == mp->tx_fill) { + printk(KERN_DEBUG "mace: tx ring ran out? (fs=%x xc=%d ds=%x)\n", + fs, xcount, dstat); + continue; + } + /* Update stats */ + if (fs & (UFLO|LCOL|LCAR|RTRY)) { + ++dev->stats.tx_errors; + if (fs & LCAR) + ++dev->stats.tx_carrier_errors; + if (fs & (UFLO|LCOL|RTRY)) + ++dev->stats.tx_aborted_errors; + } else { + dev->stats.tx_bytes += mp->tx_bufs[i]->len; + ++dev->stats.tx_packets; + } + dev_consume_skb_irq(mp->tx_bufs[i]); + --mp->tx_active; + if (++i >= N_TX_RING) + i = 0; +#if 0 + mace_last_fs = fs; + mace_last_xcount = xcount; +#endif + } + + if (i != mp->tx_empty) { + mp->tx_fullup = 0; + netif_wake_queue(dev); + } + mp->tx_empty = i; + i += mp->tx_active; + if (i >= N_TX_RING) + i -= N_TX_RING; + if (!mp->tx_bad_runt && i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE) { + do { + /* set up the next one */ + cp = mp->tx_cmds + NCMDS_TX * i; + out_le16(&cp->xfer_status, 0); + out_le16(&cp->command, OUTPUT_LAST); + ++mp->tx_active; + if (++i >= N_TX_RING) + i = 0; + } while (i != mp->tx_fill && mp->tx_active < MAX_TX_ACTIVE); + out_le32(&td->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); + mace_set_timeout(dev); + } + spin_unlock_irqrestore(&mp->lock, flags); + return IRQ_HANDLED; +} + +static void mace_tx_timeout(struct timer_list *t) +{ + struct mace_data *mp = timer_container_of(mp, t, tx_timeout); + struct net_device *dev = macio_get_drvdata(mp->mdev); + volatile struct mace __iomem *mb = mp->mace; + volatile struct dbdma_regs __iomem *td = mp->tx_dma; + volatile struct dbdma_regs __iomem *rd = mp->rx_dma; + volatile struct dbdma_cmd *cp; + unsigned long flags; + int i; + + spin_lock_irqsave(&mp->lock, flags); + mp->timeout_active = 0; + if (mp->tx_active == 0 && !mp->tx_bad_runt) + goto out; + + /* update various counters */ + mace_handle_misc_intrs(mp, in_8(&mb->ir), dev); + + cp = mp->tx_cmds + NCMDS_TX * mp->tx_empty; + + /* turn off both tx and rx and reset the chip */ + out_8(&mb->maccc, 0); + printk(KERN_ERR "mace: transmit timeout - resetting\n"); + dbdma_reset(td); + mace_reset(dev); + + /* restart rx dma */ + cp = bus_to_virt(le32_to_cpu(rd->cmdptr)); + dbdma_reset(rd); + out_le16(&cp->xfer_status, 0); + out_le32(&rd->cmdptr, virt_to_bus(cp)); + out_le32(&rd->control, (RUN << 16) | RUN); + + /* fix up the transmit side */ + i = mp->tx_empty; + mp->tx_active = 0; + ++dev->stats.tx_errors; + if (mp->tx_bad_runt) { + mp->tx_bad_runt = 0; + } else if (i != mp->tx_fill) { + dev_kfree_skb_irq(mp->tx_bufs[i]); + if (++i >= N_TX_RING) + i = 0; + mp->tx_empty = i; + } + mp->tx_fullup = 0; + netif_wake_queue(dev); + if (i != mp->tx_fill) { + cp = mp->tx_cmds + NCMDS_TX * i; + out_le16(&cp->xfer_status, 0); + out_le16(&cp->command, OUTPUT_LAST); + out_le32(&td->cmdptr, virt_to_bus(cp)); + out_le32(&td->control, (RUN << 16) | RUN); + ++mp->tx_active; + mace_set_timeout(dev); + } + + /* turn it back on */ + out_8(&mb->imr, RCVINT); + out_8(&mb->maccc, mp->maccc); + +out: + spin_unlock_irqrestore(&mp->lock, flags); +} + +static irqreturn_t mace_txdma_intr(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static irqreturn_t mace_rxdma_intr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace_data *mp = netdev_priv(dev); + volatile struct dbdma_regs __iomem *rd = mp->rx_dma; + volatile struct dbdma_cmd *cp, *np; + int i, nb, stat, next; + struct sk_buff *skb; + unsigned frame_status; + static int mace_lost_status; + unsigned char *data; + unsigned long flags; + + spin_lock_irqsave(&mp->lock, flags); + for (i = mp->rx_empty; i != mp->rx_fill; ) { + cp = mp->rx_cmds + i; + stat = le16_to_cpu(cp->xfer_status); + if ((stat & ACTIVE) == 0) { + next = i + 1; + if (next >= N_RX_RING) + next = 0; + np = mp->rx_cmds + next; + if (next != mp->rx_fill && + (le16_to_cpu(np->xfer_status) & ACTIVE) != 0) { + printk(KERN_DEBUG "mace: lost a status word\n"); + ++mace_lost_status; + } else + break; + } + nb = le16_to_cpu(cp->req_count) - le16_to_cpu(cp->res_count); + out_le16(&cp->command, DBDMA_STOP); + /* got a packet, have a look at it */ + skb = mp->rx_bufs[i]; + if (!skb) { + ++dev->stats.rx_dropped; + } else if (nb > 8) { + data = skb->data; + frame_status = (data[nb-3] << 8) + data[nb-4]; + if (frame_status & (RS_OFLO|RS_CLSN|RS_FRAMERR|RS_FCSERR)) { + ++dev->stats.rx_errors; + if (frame_status & RS_OFLO) + ++dev->stats.rx_over_errors; + if (frame_status & RS_FRAMERR) + ++dev->stats.rx_frame_errors; + if (frame_status & RS_FCSERR) + ++dev->stats.rx_crc_errors; + } else { + /* Mace feature AUTO_STRIP_RCV is on by default, dropping the + * FCS on frames with 802.3 headers. This means that Ethernet + * frames have 8 extra octets at the end, while 802.3 frames + * have only 4. We need to correctly account for this. */ + if (*(unsigned short *)(data+12) < 1536) /* 802.3 header */ + nb -= 4; + else /* Ethernet header; mace includes FCS */ + nb -= 8; + skb_put(skb, nb); + skb->protocol = eth_type_trans(skb, dev); + dev->stats.rx_bytes += skb->len; + netif_rx(skb); + mp->rx_bufs[i] = NULL; + ++dev->stats.rx_packets; + } + } else { + ++dev->stats.rx_errors; + ++dev->stats.rx_length_errors; + } + + /* advance to next */ + if (++i >= N_RX_RING) + i = 0; + } + mp->rx_empty = i; + + i = mp->rx_fill; + for (;;) { + next = i + 1; + if (next >= N_RX_RING) + next = 0; + if (next == mp->rx_empty) + break; + cp = mp->rx_cmds + i; + skb = mp->rx_bufs[i]; + if (!skb) { + skb = netdev_alloc_skb(dev, RX_BUFLEN + 2); + if (skb) { + skb_reserve(skb, 2); + mp->rx_bufs[i] = skb; + } + } + cp->req_count = cpu_to_le16(RX_BUFLEN); + data = skb? skb->data: dummy_buf; + cp->phy_addr = cpu_to_le32(virt_to_bus(data)); + out_le16(&cp->xfer_status, 0); + out_le16(&cp->command, INPUT_LAST + INTR_ALWAYS); +#if 0 + if ((le32_to_cpu(rd->status) & ACTIVE) != 0) { + out_le32(&rd->control, (PAUSE << 16) | PAUSE); + while ((in_le32(&rd->status) & ACTIVE) != 0) + ; + } +#endif + i = next; + } + if (i != mp->rx_fill) { + out_le32(&rd->control, ((RUN|WAKE) << 16) | (RUN|WAKE)); + mp->rx_fill = i; + } + spin_unlock_irqrestore(&mp->lock, flags); + return IRQ_HANDLED; +} + +static const struct of_device_id mace_match[] = +{ + { + .name = "mace", + }, + {}, +}; +MODULE_DEVICE_TABLE (of, mace_match); + +static struct macio_driver mace_driver = +{ + .driver = { + .name = "mace", + .owner = THIS_MODULE, + .of_match_table = mace_match, + }, + .probe = mace_probe, + .remove = mace_remove, +}; + + +static int __init mace_init(void) +{ + return macio_register_driver(&mace_driver); +} + +static void __exit mace_cleanup(void) +{ + macio_unregister_driver(&mace_driver); + + kfree(dummy_buf); + dummy_buf = NULL; +} + +MODULE_AUTHOR("Paul Mackerras"); +MODULE_DESCRIPTION("PowerMac MACE driver."); +module_param(port_aaui, int, 0); +MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)"); +MODULE_LICENSE("GPL"); + +module_init(mace_init); +module_exit(mace_cleanup); diff --git a/drivers/net/ethernet/apple/mace.h b/drivers/net/ethernet/apple/mace.h new file mode 100644 index 000000000..697f71cf1 --- /dev/null +++ b/drivers/net/ethernet/apple/mace.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * mace.h - definitions for the registers in the Am79C940 MACE + * (Medium Access Control for Ethernet) controller. + * + * Copyright (C) 1996 Paul Mackerras. + */ + +#define REG(x) volatile unsigned char x; char x ## _pad[15] + +struct mace { + REG(rcvfifo); /* receive FIFO */ + REG(xmtfifo); /* transmit FIFO */ + REG(xmtfc); /* transmit frame control */ + REG(xmtfs); /* transmit frame status */ + REG(xmtrc); /* transmit retry count */ + REG(rcvfc); /* receive frame control */ + REG(rcvfs); /* receive frame status (4 bytes) */ + REG(fifofc); /* FIFO frame count */ + REG(ir); /* interrupt register */ + REG(imr); /* interrupt mask register */ + REG(pr); /* poll register */ + REG(biucc); /* bus interface unit config control */ + REG(fifocc); /* FIFO configuration control */ + REG(maccc); /* medium access control config control */ + REG(plscc); /* phys layer signalling config control */ + REG(phycc); /* physical configuration control */ + REG(chipid_lo); /* chip ID, lsb */ + REG(chipid_hi); /* chip ID, msb */ + REG(iac); /* internal address config */ + REG(reg19); + REG(ladrf); /* logical address filter (8 bytes) */ + REG(padr); /* physical address (6 bytes) */ + REG(reg22); + REG(reg23); + REG(mpc); /* missed packet count (clears when read) */ + REG(reg25); + REG(rntpc); /* runt packet count (clears when read) */ + REG(rcvcc); /* recv collision count (clears when read) */ + REG(reg28); + REG(utr); /* user test reg */ + REG(reg30); + REG(reg31); +}; + +/* Bits in XMTFC */ +#define DRTRY 0x80 /* don't retry transmission after collision */ +#define DXMTFCS 0x08 /* don't append FCS to transmitted frame */ +#define AUTO_PAD_XMIT 0x01 /* auto-pad short packets on transmission */ + +/* Bits in XMTFS: only valid when XMTSV is set in PR and XMTFS */ +#define XMTSV 0x80 /* transmit status (i.e. XMTFS) valid */ +#define UFLO 0x40 /* underflow - xmit fifo ran dry */ +#define LCOL 0x20 /* late collision (transmission aborted) */ +#define MORE 0x10 /* 2 or more retries needed to xmit frame */ +#define ONE 0x08 /* 1 retry needed to xmit frame */ +#define DEFER 0x04 /* MACE had to defer xmission (enet busy) */ +#define LCAR 0x02 /* loss of carrier (transmission aborted) */ +#define RTRY 0x01 /* too many retries (transmission aborted) */ + +/* Bits in XMTRC: only valid when XMTSV is set in PR (and XMTFS) */ +#define EXDEF 0x80 /* had to defer for excessive time */ +#define RETRY_MASK 0x0f /* number of retries (0 - 15) */ + +/* Bits in RCVFC */ +#define LLRCV 0x08 /* low latency receive: early DMA request */ +#define M_RBAR 0x04 /* sets function of EAM/R pin */ +#define AUTO_STRIP_RCV 0x01 /* auto-strip short LLC frames on recv */ + +/* + * Bits in RCVFS. After a frame is received, four bytes of status + * are automatically read from this register and appended to the frame + * data in memory. These are: + * Byte 0 and 1: message byte count and frame status + * Byte 2: runt packet count + * Byte 3: receive collision count + */ +#define RS_OFLO 0x8000 /* receive FIFO overflowed */ +#define RS_CLSN 0x4000 /* received frame suffered (late) collision */ +#define RS_FRAMERR 0x2000 /* framing error flag */ +#define RS_FCSERR 0x1000 /* frame had FCS error */ +#define RS_COUNT 0x0fff /* mask for byte count field */ + +/* Bits (fields) in FIFOFC */ +#define RCVFC_SH 4 /* receive frame count in FIFO */ +#define RCVFC_MASK 0x0f +#define XMTFC_SH 0 /* transmit frame count in FIFO */ +#define XMTFC_MASK 0x0f + +/* + * Bits in IR and IMR. The IR clears itself when read. + * Setting a bit in the IMR will disable the corresponding interrupt. + */ +#define JABBER 0x80 /* jabber error - 10baseT xmission too long */ +#define BABBLE 0x40 /* babble - xmitter xmitting for too long */ +#define CERR 0x20 /* collision err - no SQE test (heartbeat) */ +#define RCVCCO 0x10 /* RCVCC overflow */ +#define RNTPCO 0x08 /* RNTPC overflow */ +#define MPCO 0x04 /* MPC overflow */ +#define RCVINT 0x02 /* receive interrupt */ +#define XMTINT 0x01 /* transmitter interrupt */ + +/* Bits in PR */ +#define XMTSV 0x80 /* XMTFS valid (same as in XMTFS) */ +#define TDTREQ 0x40 /* set when xmit fifo is requesting data */ +#define RDTREQ 0x20 /* set when recv fifo requests data xfer */ + +/* Bits in BIUCC */ +#define BSWP 0x40 /* byte swap, i.e. big-endian bus */ +#define XMTSP_4 0x00 /* start xmitting when 4 bytes in FIFO */ +#define XMTSP_16 0x10 /* start xmitting when 16 bytes in FIFO */ +#define XMTSP_64 0x20 /* start xmitting when 64 bytes in FIFO */ +#define XMTSP_112 0x30 /* start xmitting when 112 bytes in FIFO */ +#define SWRST 0x01 /* software reset */ + +/* Bits in FIFOCC */ +#define XMTFW_8 0x00 /* xmit fifo watermark = 8 words free */ +#define XMTFW_16 0x40 /* 16 words free */ +#define XMTFW_32 0x80 /* 32 words free */ +#define RCVFW_16 0x00 /* recv fifo watermark = 16 bytes avail */ +#define RCVFW_32 0x10 /* 32 bytes avail */ +#define RCVFW_64 0x20 /* 64 bytes avail */ +#define XMTFWU 0x08 /* xmit fifo watermark update enable */ +#define RCVFWU 0x04 /* recv fifo watermark update enable */ +#define XMTBRST 0x02 /* enable transmit burst mode */ +#define RCVBRST 0x01 /* enable receive burst mode */ + +/* Bits in MACCC */ +#define PROM 0x80 /* promiscuous mode */ +#define DXMT2PD 0x40 /* disable xmit two-part deferral algorithm */ +#define EMBA 0x20 /* enable modified backoff algorithm */ +#define DRCVPA 0x08 /* disable receiving physical address */ +#define DRCVBC 0x04 /* disable receiving broadcasts */ +#define ENXMT 0x02 /* enable transmitter */ +#define ENRCV 0x01 /* enable receiver */ + +/* Bits in PLSCC */ +#define XMTSEL 0x08 /* select DO+/DO- state when idle */ +#define PORTSEL_AUI 0x00 /* select AUI port */ +#define PORTSEL_10T 0x02 /* select 10Base-T port */ +#define PORTSEL_DAI 0x04 /* select DAI port */ +#define PORTSEL_GPSI 0x06 /* select GPSI port */ +#define ENPLSIO 0x01 /* enable optional PLS I/O pins */ + +/* Bits in PHYCC */ +#define LNKFL 0x80 /* reports 10Base-T link failure */ +#define DLNKTST 0x40 /* disable 10Base-T link test */ +#define REVPOL 0x20 /* 10Base-T receiver polarity reversed */ +#define DAPC 0x10 /* disable auto receiver polarity correction */ +#define LRT 0x08 /* low receive threshold for long links */ +#define ASEL 0x04 /* auto-select AUI or 10Base-T port */ +#define RWAKE 0x02 /* remote wake function */ +#define AWAKE 0x01 /* auto wake function */ + +/* Bits in IAC */ +#define ADDRCHG 0x80 /* request address change */ +#define PHYADDR 0x04 /* access physical address */ +#define LOGADDR 0x02 /* access multicast filter */ + +/* Bits in UTR */ +#define RTRE 0x80 /* reserved test register enable. DON'T SET. */ +#define RTRD 0x40 /* reserved test register disable. Sticky */ +#define RPAC 0x20 /* accept runt packets */ +#define FCOLL 0x10 /* force collision */ +#define RCVFCSE 0x08 /* receive FCS enable */ +#define LOOP_NONE 0x00 /* no loopback */ +#define LOOP_EXT 0x02 /* external loopback */ +#define LOOP_INT 0x04 /* internal loopback, excludes MENDEC */ +#define LOOP_MENDEC 0x06 /* internal loopback, includes MENDEC */ diff --git a/drivers/net/ethernet/apple/macmace.c b/drivers/net/ethernet/apple/macmace.c new file mode 100644 index 000000000..8989506e6 --- /dev/null +++ b/drivers/net/ethernet/apple/macmace.c @@ -0,0 +1,768 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the Macintosh 68K onboard MACE controller with PSC + * driven DMA. The MACE driver code is derived from mace.c. The + * Mac68k theory of operation is courtesy of the MacBSD wizards. + * + * Copyright (C) 1996 Paul Mackerras. + * Copyright (C) 1998 Alan Cox + * + * Modified heavily by Joshua M. Thompson based on Dave Huang's NetBSD driver + * + * Copyright (C) 2007 Finn Thain + * + * Converted to DMA API, converted to unified driver model, + * sync'd some routines with mace.c and fixed various bugs. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mace.h" + +static char mac_mace_string[] = "macmace"; + +#define N_TX_BUFF_ORDER 0 +#define N_TX_RING (1 << N_TX_BUFF_ORDER) +#define N_RX_BUFF_ORDER 3 +#define N_RX_RING (1 << N_RX_BUFF_ORDER) + +#define TX_TIMEOUT HZ + +#define MACE_BUFF_SIZE 0x800 + +/* Chip rev needs workaround on HW & multicast addr change */ +#define BROKEN_ADDRCHG_REV 0x0941 + +/* The MACE is simply wired down on a Mac68K box */ + +#define MACE_BASE (void *)(0x50F1C000) +#define MACE_PROM (void *)(0x50F08001) + +struct mace_data { + volatile struct mace *mace; + unsigned char *tx_ring; + dma_addr_t tx_ring_phys; + unsigned char *rx_ring; + dma_addr_t rx_ring_phys; + int dma_intr; + int rx_slot, rx_tail; + int tx_slot, tx_sloti, tx_count; + int chipid; + struct device *device; +}; + +struct mace_frame { + u8 rcvcnt; + u8 pad1; + u8 rcvsts; + u8 pad2; + u8 rntpc; + u8 pad3; + u8 rcvcc; + u8 pad4; + u32 pad5; + u32 pad6; + DECLARE_FLEX_ARRAY(u8, data); + /* And frame continues.. */ +}; + +#define PRIV_BYTES sizeof(struct mace_data) + +static int mace_open(struct net_device *dev); +static int mace_close(struct net_device *dev); +static netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev); +static void mace_set_multicast(struct net_device *dev); +static int mace_set_address(struct net_device *dev, void *addr); +static void mace_reset(struct net_device *dev); +static irqreturn_t mace_interrupt(int irq, void *dev_id); +static irqreturn_t mace_dma_intr(int irq, void *dev_id); +static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue); +static void __mace_set_address(struct net_device *dev, const void *addr); + +/* + * Load a receive DMA channel with a base address and ring length + */ + +static void mace_load_rxdma_base(struct net_device *dev, int set) +{ + struct mace_data *mp = netdev_priv(dev); + + psc_write_word(PSC_ENETRD_CMD + set, 0x0100); + psc_write_long(PSC_ENETRD_ADDR + set, (u32) mp->rx_ring_phys); + psc_write_long(PSC_ENETRD_LEN + set, N_RX_RING); + psc_write_word(PSC_ENETRD_CMD + set, 0x9800); + mp->rx_tail = 0; +} + +/* + * Reset the receive DMA subsystem + */ + +static void mace_rxdma_reset(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mace = mp->mace; + u8 maccc = mace->maccc; + + mace->maccc = maccc & ~ENRCV; + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + mace_load_rxdma_base(dev, 0x00); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + mace_load_rxdma_base(dev, 0x10); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + mace->maccc = maccc; + mp->rx_slot = 0; + + psc_write_word(PSC_ENETRD_CMD + PSC_SET0, 0x9800); + psc_write_word(PSC_ENETRD_CMD + PSC_SET1, 0x9800); +} + +/* + * Reset the transmit DMA subsystem + */ + +static void mace_txdma_reset(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mace = mp->mace; + u8 maccc; + + psc_write_word(PSC_ENETWR_CTL, 0x8800); + + maccc = mace->maccc; + mace->maccc = maccc & ~ENXMT; + + mp->tx_slot = mp->tx_sloti = 0; + mp->tx_count = N_TX_RING; + + psc_write_word(PSC_ENETWR_CTL, 0x0400); + mace->maccc = maccc; +} + +/* + * Disable DMA + */ + +static void mace_dma_off(struct net_device *dev) +{ + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_write_word(PSC_ENETRD_CTL, 0x1000); + psc_write_word(PSC_ENETRD_CMD + PSC_SET0, 0x1100); + psc_write_word(PSC_ENETRD_CMD + PSC_SET1, 0x1100); + + psc_write_word(PSC_ENETWR_CTL, 0x8800); + psc_write_word(PSC_ENETWR_CTL, 0x1000); + psc_write_word(PSC_ENETWR_CMD + PSC_SET0, 0x1100); + psc_write_word(PSC_ENETWR_CMD + PSC_SET1, 0x1100); +} + +static const struct net_device_ops mace_netdev_ops = { + .ndo_open = mace_open, + .ndo_stop = mace_close, + .ndo_start_xmit = mace_xmit_start, + .ndo_tx_timeout = mace_tx_timeout, + .ndo_set_rx_mode = mace_set_multicast, + .ndo_set_mac_address = mace_set_address, + .ndo_validate_addr = eth_validate_addr, +}; + +/* + * Not really much of a probe. The hardware table tells us if this + * model of Macintrash has a MACE (AV macintoshes) + */ + +static int mace_probe(struct platform_device *pdev) +{ + int j; + struct mace_data *mp; + unsigned char *addr; + struct net_device *dev; + unsigned char checksum = 0; + u8 macaddr[ETH_ALEN]; + int err; + + dev = alloc_etherdev(PRIV_BYTES); + if (!dev) + return -ENOMEM; + + mp = netdev_priv(dev); + + mp->device = &pdev->dev; + platform_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + dev->base_addr = (u32)MACE_BASE; + mp->mace = MACE_BASE; + + dev->irq = IRQ_MAC_MACE; + mp->dma_intr = IRQ_MAC_MACE_DMA; + + mp->chipid = mp->mace->chipid_hi << 8 | mp->mace->chipid_lo; + + /* + * The PROM contains 8 bytes which total 0xFF when XOR'd + * together. Due to the usual peculiar apple brain damage + * the bytes are spaced out in a strange boundary and the + * bits are reversed. + */ + + addr = MACE_PROM; + + for (j = 0; j < 6; ++j) { + u8 v = bitrev8(addr[j<<4]); + checksum ^= v; + macaddr[j] = v; + } + eth_hw_addr_set(dev, macaddr); + for (; j < 8; ++j) { + checksum ^= bitrev8(addr[j<<4]); + } + + if (checksum != 0xFF) { + free_netdev(dev); + return -ENODEV; + } + + dev->netdev_ops = &mace_netdev_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + pr_info("Onboard MACE, hardware address %pM, chip revision 0x%04X\n", + dev->dev_addr, mp->chipid); + + err = register_netdev(dev); + if (!err) + return 0; + + free_netdev(dev); + return err; +} + +/* + * Reset the chip. + */ + +static void mace_reset(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + int i; + + /* soft-reset the chip */ + i = 200; + while (--i) { + mb->biucc = SWRST; + if (mb->biucc & SWRST) { + udelay(10); + continue; + } + break; + } + if (!i) { + printk(KERN_ERR "macmace: cannot reset chip!\n"); + return; + } + + mb->maccc = 0; /* turn off tx, rx */ + mb->imr = 0xFF; /* disable all intrs for now */ + i = mb->ir; + + mb->biucc = XMTSP_64; + mb->utr = RTRD; + mb->fifocc = XMTFW_8 | RCVFW_64 | XMTFWU | RCVFWU; + + mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */ + mb->rcvfc = 0; + + /* load up the hardware address */ + __mace_set_address(dev, dev->dev_addr); + + /* clear the multicast filter */ + if (mp->chipid == BROKEN_ADDRCHG_REV) + mb->iac = LOGADDR; + else { + mb->iac = ADDRCHG | LOGADDR; + while ((mb->iac & ADDRCHG) != 0) + ; + } + for (i = 0; i < 8; ++i) + mb->ladrf = 0; + + /* done changing address */ + if (mp->chipid != BROKEN_ADDRCHG_REV) + mb->iac = 0; + + mb->plscc = PORTSEL_AUI; +} + +/* + * Load the address on a mace controller. + */ + +static void __mace_set_address(struct net_device *dev, const void *addr) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + const unsigned char *p = addr; + u8 macaddr[ETH_ALEN]; + int i; + + /* load up the hardware address */ + if (mp->chipid == BROKEN_ADDRCHG_REV) + mb->iac = PHYADDR; + else { + mb->iac = ADDRCHG | PHYADDR; + while ((mb->iac & ADDRCHG) != 0) + ; + } + for (i = 0; i < 6; ++i) + mb->padr = macaddr[i] = p[i]; + eth_hw_addr_set(dev, macaddr); + if (mp->chipid != BROKEN_ADDRCHG_REV) + mb->iac = 0; +} + +static int mace_set_address(struct net_device *dev, void *addr) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + unsigned long flags; + u8 maccc; + + local_irq_save(flags); + + maccc = mb->maccc; + + __mace_set_address(dev, addr); + + mb->maccc = maccc; + + local_irq_restore(flags); + + return 0; +} + +/* + * Open the Macintosh MACE. Most of this is playing with the DMA + * engine. The ethernet chip is quite friendly. + */ + +static int mace_open(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + + /* reset the chip */ + mace_reset(dev); + + if (request_irq(dev->irq, mace_interrupt, 0, dev->name, dev)) { + printk(KERN_ERR "%s: can't get irq %d\n", dev->name, dev->irq); + return -EAGAIN; + } + if (request_irq(mp->dma_intr, mace_dma_intr, 0, dev->name, dev)) { + printk(KERN_ERR "%s: can't get irq %d\n", dev->name, mp->dma_intr); + free_irq(dev->irq, dev); + return -EAGAIN; + } + + /* Allocate the DMA ring buffers */ + + mp->tx_ring = dma_alloc_coherent(mp->device, + N_TX_RING * MACE_BUFF_SIZE, + &mp->tx_ring_phys, GFP_KERNEL); + if (mp->tx_ring == NULL) + goto out1; + + mp->rx_ring = dma_alloc_coherent(mp->device, + N_RX_RING * MACE_BUFF_SIZE, + &mp->rx_ring_phys, GFP_KERNEL); + if (mp->rx_ring == NULL) + goto out2; + + mace_dma_off(dev); + + /* Not sure what these do */ + + psc_write_word(PSC_ENETWR_CTL, 0x9000); + psc_write_word(PSC_ENETRD_CTL, 0x9000); + psc_write_word(PSC_ENETWR_CTL, 0x0400); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + mace_rxdma_reset(dev); + mace_txdma_reset(dev); + + /* turn it on! */ + mb->maccc = ENXMT | ENRCV; + /* enable all interrupts except receive interrupts */ + mb->imr = RCVINT; + return 0; + +out2: + dma_free_coherent(mp->device, N_TX_RING * MACE_BUFF_SIZE, + mp->tx_ring, mp->tx_ring_phys); +out1: + free_irq(dev->irq, dev); + free_irq(mp->dma_intr, dev); + return -ENOMEM; +} + +/* + * Shut down the mace and its interrupt channel + */ + +static int mace_close(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + + mb->maccc = 0; /* disable rx and tx */ + mb->imr = 0xFF; /* disable all irqs */ + mace_dma_off(dev); /* disable rx and tx dma */ + + return 0; +} + +/* + * Transmit a frame + */ + +static netdev_tx_t mace_xmit_start(struct sk_buff *skb, struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + unsigned long flags; + + /* Stop the queue since there's only the one buffer */ + + local_irq_save(flags); + netif_stop_queue(dev); + if (!mp->tx_count) { + printk(KERN_ERR "macmace: tx queue running but no free buffers.\n"); + local_irq_restore(flags); + return NETDEV_TX_BUSY; + } + mp->tx_count--; + local_irq_restore(flags); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + /* We need to copy into our xmit buffer to take care of alignment and caching issues */ + skb_copy_from_linear_data(skb, mp->tx_ring, skb->len); + + /* load the Tx DMA and fire it off */ + + psc_write_long(PSC_ENETWR_ADDR + mp->tx_slot, (u32) mp->tx_ring_phys); + psc_write_long(PSC_ENETWR_LEN + mp->tx_slot, skb->len); + psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x9800); + + mp->tx_slot ^= 0x10; + + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static void mace_set_multicast(struct net_device *dev) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + int i; + u32 crc; + u8 maccc; + unsigned long flags; + + local_irq_save(flags); + maccc = mb->maccc; + mb->maccc &= ~PROM; + + if (dev->flags & IFF_PROMISC) { + mb->maccc |= PROM; + } else { + unsigned char multicast_filter[8]; + struct netdev_hw_addr *ha; + + if (dev->flags & IFF_ALLMULTI) { + for (i = 0; i < 8; i++) { + multicast_filter[i] = 0xFF; + } + } else { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0; + netdev_for_each_mc_addr(ha, dev) { + crc = ether_crc_le(6, ha->addr); + /* bit number in multicast_filter */ + i = crc >> 26; + multicast_filter[i >> 3] |= 1 << (i & 7); + } + } + + if (mp->chipid == BROKEN_ADDRCHG_REV) + mb->iac = LOGADDR; + else { + mb->iac = ADDRCHG | LOGADDR; + while ((mb->iac & ADDRCHG) != 0) + ; + } + for (i = 0; i < 8; ++i) + mb->ladrf = multicast_filter[i]; + if (mp->chipid != BROKEN_ADDRCHG_REV) + mb->iac = 0; + } + + mb->maccc = maccc; + local_irq_restore(flags); +} + +static void mace_handle_misc_intrs(struct net_device *dev, int intr) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + static int mace_babbles, mace_jabbers; + + if (intr & MPCO) + dev->stats.rx_missed_errors += 256; + dev->stats.rx_missed_errors += mb->mpc; /* reading clears it */ + if (intr & RNTPCO) + dev->stats.rx_length_errors += 256; + dev->stats.rx_length_errors += mb->rntpc; /* reading clears it */ + if (intr & CERR) + ++dev->stats.tx_heartbeat_errors; + if (intr & BABBLE) + if (mace_babbles++ < 4) + printk(KERN_DEBUG "macmace: babbling transmitter\n"); + if (intr & JABBER) + if (mace_jabbers++ < 4) + printk(KERN_DEBUG "macmace: jabbering transceiver\n"); +} + +static irqreturn_t mace_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + int intr, fs; + unsigned long flags; + + /* don't want the dma interrupt handler to fire */ + local_irq_save(flags); + + intr = mb->ir; /* read interrupt register */ + mace_handle_misc_intrs(dev, intr); + + if (intr & XMTINT) { + fs = mb->xmtfs; + if ((fs & XMTSV) == 0) { + printk(KERN_ERR "macmace: xmtfs not valid! (fs=%x)\n", fs); + mace_reset(dev); + /* + * XXX mace likes to hang the machine after a xmtfs error. + * This is hard to reproduce, resetting *may* help + */ + } + /* dma should have finished */ + if (!mp->tx_count) { + printk(KERN_DEBUG "macmace: tx ring ran out? (fs=%x)\n", fs); + } + /* Update stats */ + if (fs & (UFLO|LCOL|LCAR|RTRY)) { + ++dev->stats.tx_errors; + if (fs & LCAR) + ++dev->stats.tx_carrier_errors; + else if (fs & (UFLO|LCOL|RTRY)) { + ++dev->stats.tx_aborted_errors; + if (mb->xmtfs & UFLO) { + dev->stats.tx_fifo_errors++; + mace_txdma_reset(dev); + } + } + } + } + + if (mp->tx_count) + netif_wake_queue(dev); + + local_irq_restore(flags); + + return IRQ_HANDLED; +} + +static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct mace_data *mp = netdev_priv(dev); + volatile struct mace *mb = mp->mace; + unsigned long flags; + + local_irq_save(flags); + + /* turn off both tx and rx and reset the chip */ + mb->maccc = 0; + printk(KERN_ERR "macmace: transmit timeout - resetting\n"); + mace_txdma_reset(dev); + mace_reset(dev); + + /* restart rx dma */ + mace_rxdma_reset(dev); + + mp->tx_count = N_TX_RING; + netif_wake_queue(dev); + + /* turn it on! */ + mb->maccc = ENXMT | ENRCV; + /* enable all interrupts except receive interrupts */ + mb->imr = RCVINT; + + local_irq_restore(flags); +} + +/* + * Handle a newly arrived frame + */ + +static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf) +{ + struct sk_buff *skb; + unsigned int frame_status = mf->rcvsts; + + if (frame_status & (RS_OFLO | RS_CLSN | RS_FRAMERR | RS_FCSERR)) { + dev->stats.rx_errors++; + if (frame_status & RS_OFLO) + dev->stats.rx_fifo_errors++; + if (frame_status & RS_CLSN) + dev->stats.collisions++; + if (frame_status & RS_FRAMERR) + dev->stats.rx_frame_errors++; + if (frame_status & RS_FCSERR) + dev->stats.rx_crc_errors++; + } else { + unsigned int frame_length = mf->rcvcnt + ((frame_status & 0x0F) << 8 ); + + skb = netdev_alloc_skb(dev, frame_length + 2); + if (!skb) { + dev->stats.rx_dropped++; + return; + } + skb_reserve(skb, 2); + skb_put_data(skb, mf->data, frame_length); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += frame_length; + } +} + +/* + * The PSC has passed us a DMA interrupt event. + */ + +static irqreturn_t mace_dma_intr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace_data *mp = netdev_priv(dev); + int left, head; + u16 status; + u32 baka; + + /* Not sure what this does */ + + while ((baka = psc_read_long(PSC_MYSTERY)) != psc_read_long(PSC_MYSTERY)); + if (!(baka & 0x60000000)) return IRQ_NONE; + + /* + * Process the read queue + */ + + status = psc_read_word(PSC_ENETRD_CTL); + + if (status & 0x2000) { + mace_rxdma_reset(dev); + } else if (status & 0x0100) { + psc_write_word(PSC_ENETRD_CMD + mp->rx_slot, 0x1100); + + left = psc_read_long(PSC_ENETRD_LEN + mp->rx_slot); + head = N_RX_RING - left; + + /* Loop through the ring buffer and process new packages */ + + while (mp->rx_tail < head) { + mace_dma_rx_frame(dev, (struct mace_frame*) (mp->rx_ring + + (mp->rx_tail * MACE_BUFF_SIZE))); + mp->rx_tail++; + } + + /* If we're out of buffers in this ring then switch to */ + /* the other set, otherwise just reactivate this one. */ + + if (!left) { + mace_load_rxdma_base(dev, mp->rx_slot); + mp->rx_slot ^= 0x10; + } else { + psc_write_word(PSC_ENETRD_CMD + mp->rx_slot, 0x9800); + } + } + + /* + * Process the write queue + */ + + status = psc_read_word(PSC_ENETWR_CTL); + + if (status & 0x2000) { + mace_txdma_reset(dev); + } else if (status & 0x0100) { + psc_write_word(PSC_ENETWR_CMD + mp->tx_sloti, 0x0100); + mp->tx_sloti ^= 0x10; + mp->tx_count++; + } + return IRQ_HANDLED; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Macintosh MACE ethernet driver"); +MODULE_ALIAS("platform:macmace"); + +static void mac_mace_device_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct mace_data *mp = netdev_priv(dev); + + unregister_netdev(dev); + + free_irq(dev->irq, dev); + free_irq(IRQ_MAC_MACE_DMA, dev); + + dma_free_coherent(mp->device, N_RX_RING * MACE_BUFF_SIZE, + mp->rx_ring, mp->rx_ring_phys); + dma_free_coherent(mp->device, N_TX_RING * MACE_BUFF_SIZE, + mp->tx_ring, mp->tx_ring_phys); + + free_netdev(dev); +} + +static struct platform_driver mac_mace_driver = { + .probe = mace_probe, + .remove = mac_mace_device_remove, + .driver = { + .name = mac_mace_string, + }, +}; + +module_platform_driver(mac_mace_driver); -- cgit v1.3.1