196ed82ccSMaciej W. Rozycki // SPDX-License-Identifier: GPL-2.0+ 261414f5eSMaciej W. Rozycki /* FDDI network adapter driver for DEC FDDIcontroller 700/700-C devices. 361414f5eSMaciej W. Rozycki * 461414f5eSMaciej W. Rozycki * Copyright (c) 2018 Maciej W. Rozycki 561414f5eSMaciej W. Rozycki * 661414f5eSMaciej W. Rozycki * This program is free software; you can redistribute it and/or 761414f5eSMaciej W. Rozycki * modify it under the terms of the GNU General Public License 861414f5eSMaciej W. Rozycki * as published by the Free Software Foundation; either version 961414f5eSMaciej W. Rozycki * 2 of the License, or (at your option) any later version. 1061414f5eSMaciej W. Rozycki * 1161414f5eSMaciej W. Rozycki * References: 1261414f5eSMaciej W. Rozycki * 1361414f5eSMaciej W. Rozycki * Dave Sawyer & Phil Weeks & Frank Itkowsky, 1461414f5eSMaciej W. Rozycki * "DEC FDDIcontroller 700 Port Specification", 1561414f5eSMaciej W. Rozycki * Revision 1.1, Digital Equipment Corporation 1661414f5eSMaciej W. Rozycki */ 1761414f5eSMaciej W. Rozycki 1861414f5eSMaciej W. Rozycki /* ------------------------------------------------------------------------- */ 1961414f5eSMaciej W. Rozycki /* FZA configurable parameters. */ 2061414f5eSMaciej W. Rozycki 2161414f5eSMaciej W. Rozycki /* The number of transmit ring descriptors; either 0 for 512 or 1 for 1024. */ 2261414f5eSMaciej W. Rozycki #define FZA_RING_TX_MODE 0 2361414f5eSMaciej W. Rozycki 2461414f5eSMaciej W. Rozycki /* The number of receive ring descriptors; from 2 up to 256. */ 2561414f5eSMaciej W. Rozycki #define FZA_RING_RX_SIZE 256 2661414f5eSMaciej W. Rozycki 2761414f5eSMaciej W. Rozycki /* End of FZA configurable parameters. No need to change anything below. */ 2861414f5eSMaciej W. Rozycki /* ------------------------------------------------------------------------- */ 2961414f5eSMaciej W. Rozycki 3061414f5eSMaciej W. Rozycki #include <linux/delay.h> 3161414f5eSMaciej W. Rozycki #include <linux/device.h> 3261414f5eSMaciej W. Rozycki #include <linux/dma-mapping.h> 3361414f5eSMaciej W. Rozycki #include <linux/init.h> 3461414f5eSMaciej W. Rozycki #include <linux/interrupt.h> 3561414f5eSMaciej W. Rozycki #include <linux/io.h> 36*262e4c38SPaul Burton #include <linux/io-64-nonatomic-lo-hi.h> 3761414f5eSMaciej W. Rozycki #include <linux/ioport.h> 3861414f5eSMaciej W. Rozycki #include <linux/kernel.h> 3961414f5eSMaciej W. Rozycki #include <linux/list.h> 4061414f5eSMaciej W. Rozycki #include <linux/module.h> 4161414f5eSMaciej W. Rozycki #include <linux/netdevice.h> 4261414f5eSMaciej W. Rozycki #include <linux/fddidevice.h> 4361414f5eSMaciej W. Rozycki #include <linux/sched.h> 4461414f5eSMaciej W. Rozycki #include <linux/skbuff.h> 4561414f5eSMaciej W. Rozycki #include <linux/spinlock.h> 4661414f5eSMaciej W. Rozycki #include <linux/stat.h> 4761414f5eSMaciej W. Rozycki #include <linux/tc.h> 4861414f5eSMaciej W. Rozycki #include <linux/timer.h> 4961414f5eSMaciej W. Rozycki #include <linux/types.h> 5061414f5eSMaciej W. Rozycki #include <linux/wait.h> 5161414f5eSMaciej W. Rozycki 5261414f5eSMaciej W. Rozycki #include <asm/barrier.h> 5361414f5eSMaciej W. Rozycki 5461414f5eSMaciej W. Rozycki #include "defza.h" 5561414f5eSMaciej W. Rozycki 5661414f5eSMaciej W. Rozycki #define DRV_NAME "defza" 5761414f5eSMaciej W. Rozycki #define DRV_VERSION "v.1.1.4" 5861414f5eSMaciej W. Rozycki #define DRV_RELDATE "Oct 6 2018" 5961414f5eSMaciej W. Rozycki 608f5365ebSMaciej W. Rozycki static const char version[] = 6161414f5eSMaciej W. Rozycki DRV_NAME ": " DRV_VERSION " " DRV_RELDATE " Maciej W. Rozycki\n"; 6261414f5eSMaciej W. Rozycki 6361414f5eSMaciej W. Rozycki MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>"); 6461414f5eSMaciej W. Rozycki MODULE_DESCRIPTION("DEC FDDIcontroller 700 (DEFZA-xx) driver"); 6561414f5eSMaciej W. Rozycki MODULE_LICENSE("GPL"); 6661414f5eSMaciej W. Rozycki 6761414f5eSMaciej W. Rozycki static int loopback; 6861414f5eSMaciej W. Rozycki module_param(loopback, int, 0644); 6961414f5eSMaciej W. Rozycki 7061414f5eSMaciej W. Rozycki /* Ring Purger Multicast */ 7161414f5eSMaciej W. Rozycki static u8 hw_addr_purger[8] = { 0x09, 0x00, 0x2b, 0x02, 0x01, 0x05 }; 7261414f5eSMaciej W. Rozycki /* Directed Beacon Multicast */ 7361414f5eSMaciej W. Rozycki static u8 hw_addr_beacon[8] = { 0x01, 0x80, 0xc2, 0x00, 0x01, 0x00 }; 7461414f5eSMaciej W. Rozycki 7561414f5eSMaciej W. Rozycki /* Shorthands for MMIO accesses that we require to be strongly ordered 7661414f5eSMaciej W. Rozycki * WRT preceding MMIO accesses. 7761414f5eSMaciej W. Rozycki */ 7861414f5eSMaciej W. Rozycki #define readw_o readw_relaxed 7961414f5eSMaciej W. Rozycki #define readl_o readl_relaxed 8061414f5eSMaciej W. Rozycki 8161414f5eSMaciej W. Rozycki #define writew_o writew_relaxed 8261414f5eSMaciej W. Rozycki #define writel_o writel_relaxed 8361414f5eSMaciej W. Rozycki 8461414f5eSMaciej W. Rozycki /* Shorthands for MMIO accesses that we are happy with being weakly ordered 8561414f5eSMaciej W. Rozycki * WRT preceding MMIO accesses. 8661414f5eSMaciej W. Rozycki */ 8761414f5eSMaciej W. Rozycki #define readw_u readw_relaxed 8861414f5eSMaciej W. Rozycki #define readl_u readl_relaxed 8961414f5eSMaciej W. Rozycki #define readq_u readq_relaxed 9061414f5eSMaciej W. Rozycki 9161414f5eSMaciej W. Rozycki #define writew_u writew_relaxed 9261414f5eSMaciej W. Rozycki #define writel_u writel_relaxed 9361414f5eSMaciej W. Rozycki #define writeq_u writeq_relaxed 9461414f5eSMaciej W. Rozycki 9561414f5eSMaciej W. Rozycki static inline struct sk_buff *fza_alloc_skb_irq(struct net_device *dev, 9661414f5eSMaciej W. Rozycki unsigned int length) 9761414f5eSMaciej W. Rozycki { 9861414f5eSMaciej W. Rozycki return __netdev_alloc_skb(dev, length, GFP_ATOMIC); 9961414f5eSMaciej W. Rozycki } 10061414f5eSMaciej W. Rozycki 10161414f5eSMaciej W. Rozycki static inline struct sk_buff *fza_alloc_skb(struct net_device *dev, 10261414f5eSMaciej W. Rozycki unsigned int length) 10361414f5eSMaciej W. Rozycki { 10461414f5eSMaciej W. Rozycki return __netdev_alloc_skb(dev, length, GFP_KERNEL); 10561414f5eSMaciej W. Rozycki } 10661414f5eSMaciej W. Rozycki 10761414f5eSMaciej W. Rozycki static inline void fza_skb_align(struct sk_buff *skb, unsigned int v) 10861414f5eSMaciej W. Rozycki { 10961414f5eSMaciej W. Rozycki unsigned long x, y; 11061414f5eSMaciej W. Rozycki 11161414f5eSMaciej W. Rozycki x = (unsigned long)skb->data; 11261414f5eSMaciej W. Rozycki y = ALIGN(x, v); 11361414f5eSMaciej W. Rozycki 11461414f5eSMaciej W. Rozycki skb_reserve(skb, y - x); 11561414f5eSMaciej W. Rozycki } 11661414f5eSMaciej W. Rozycki 11761414f5eSMaciej W. Rozycki static inline void fza_reads(const void __iomem *from, void *to, 11861414f5eSMaciej W. Rozycki unsigned long size) 11961414f5eSMaciej W. Rozycki { 12061414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 12161414f5eSMaciej W. Rozycki const u64 __iomem *src = from; 12261414f5eSMaciej W. Rozycki const u32 __iomem *src_trail; 12361414f5eSMaciej W. Rozycki u64 *dst = to; 12461414f5eSMaciej W. Rozycki u32 *dst_trail; 12561414f5eSMaciej W. Rozycki 12661414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 12761414f5eSMaciej W. Rozycki *dst++ = readq_u(src++); 12861414f5eSMaciej W. Rozycki if (size) { 12961414f5eSMaciej W. Rozycki src_trail = (u32 __iomem *)src; 13061414f5eSMaciej W. Rozycki dst_trail = (u32 *)dst; 13161414f5eSMaciej W. Rozycki *dst_trail = readl_u(src_trail); 13261414f5eSMaciej W. Rozycki } 13361414f5eSMaciej W. Rozycki } else { 13461414f5eSMaciej W. Rozycki const u32 __iomem *src = from; 13561414f5eSMaciej W. Rozycki u32 *dst = to; 13661414f5eSMaciej W. Rozycki 13761414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 13861414f5eSMaciej W. Rozycki *dst++ = readl_u(src++); 13961414f5eSMaciej W. Rozycki } 14061414f5eSMaciej W. Rozycki } 14161414f5eSMaciej W. Rozycki 14261414f5eSMaciej W. Rozycki static inline void fza_writes(const void *from, void __iomem *to, 14361414f5eSMaciej W. Rozycki unsigned long size) 14461414f5eSMaciej W. Rozycki { 14561414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 14661414f5eSMaciej W. Rozycki const u64 *src = from; 14761414f5eSMaciej W. Rozycki const u32 *src_trail; 14861414f5eSMaciej W. Rozycki u64 __iomem *dst = to; 14961414f5eSMaciej W. Rozycki u32 __iomem *dst_trail; 15061414f5eSMaciej W. Rozycki 15161414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 15261414f5eSMaciej W. Rozycki writeq_u(*src++, dst++); 15361414f5eSMaciej W. Rozycki if (size) { 15461414f5eSMaciej W. Rozycki src_trail = (u32 *)src; 15561414f5eSMaciej W. Rozycki dst_trail = (u32 __iomem *)dst; 15661414f5eSMaciej W. Rozycki writel_u(*src_trail, dst_trail); 15761414f5eSMaciej W. Rozycki } 15861414f5eSMaciej W. Rozycki } else { 15961414f5eSMaciej W. Rozycki const u32 *src = from; 16061414f5eSMaciej W. Rozycki u32 __iomem *dst = to; 16161414f5eSMaciej W. Rozycki 16261414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 16361414f5eSMaciej W. Rozycki writel_u(*src++, dst++); 16461414f5eSMaciej W. Rozycki } 16561414f5eSMaciej W. Rozycki } 16661414f5eSMaciej W. Rozycki 16761414f5eSMaciej W. Rozycki static inline void fza_moves(const void __iomem *from, void __iomem *to, 16861414f5eSMaciej W. Rozycki unsigned long size) 16961414f5eSMaciej W. Rozycki { 17061414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 17161414f5eSMaciej W. Rozycki const u64 __iomem *src = from; 17261414f5eSMaciej W. Rozycki const u32 __iomem *src_trail; 17361414f5eSMaciej W. Rozycki u64 __iomem *dst = to; 17461414f5eSMaciej W. Rozycki u32 __iomem *dst_trail; 17561414f5eSMaciej W. Rozycki 17661414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 17761414f5eSMaciej W. Rozycki writeq_u(readq_u(src++), dst++); 17861414f5eSMaciej W. Rozycki if (size) { 17961414f5eSMaciej W. Rozycki src_trail = (u32 __iomem *)src; 18061414f5eSMaciej W. Rozycki dst_trail = (u32 __iomem *)dst; 18161414f5eSMaciej W. Rozycki writel_u(readl_u(src_trail), dst_trail); 18261414f5eSMaciej W. Rozycki } 18361414f5eSMaciej W. Rozycki } else { 18461414f5eSMaciej W. Rozycki const u32 __iomem *src = from; 18561414f5eSMaciej W. Rozycki u32 __iomem *dst = to; 18661414f5eSMaciej W. Rozycki 18761414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 18861414f5eSMaciej W. Rozycki writel_u(readl_u(src++), dst++); 18961414f5eSMaciej W. Rozycki } 19061414f5eSMaciej W. Rozycki } 19161414f5eSMaciej W. Rozycki 19261414f5eSMaciej W. Rozycki static inline void fza_zeros(void __iomem *to, unsigned long size) 19361414f5eSMaciej W. Rozycki { 19461414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 19561414f5eSMaciej W. Rozycki u64 __iomem *dst = to; 19661414f5eSMaciej W. Rozycki u32 __iomem *dst_trail; 19761414f5eSMaciej W. Rozycki 19861414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 19961414f5eSMaciej W. Rozycki writeq_u(0, dst++); 20061414f5eSMaciej W. Rozycki if (size) { 20161414f5eSMaciej W. Rozycki dst_trail = (u32 __iomem *)dst; 20261414f5eSMaciej W. Rozycki writel_u(0, dst_trail); 20361414f5eSMaciej W. Rozycki } 20461414f5eSMaciej W. Rozycki } else { 20561414f5eSMaciej W. Rozycki u32 __iomem *dst = to; 20661414f5eSMaciej W. Rozycki 20761414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 20861414f5eSMaciej W. Rozycki writel_u(0, dst++); 20961414f5eSMaciej W. Rozycki } 21061414f5eSMaciej W. Rozycki } 21161414f5eSMaciej W. Rozycki 21261414f5eSMaciej W. Rozycki static inline void fza_regs_dump(struct fza_private *fp) 21361414f5eSMaciej W. Rozycki { 21461414f5eSMaciej W. Rozycki pr_debug("%s: iomem registers:\n", fp->name); 21561414f5eSMaciej W. Rozycki pr_debug(" reset: 0x%04x\n", readw_o(&fp->regs->reset)); 21661414f5eSMaciej W. Rozycki pr_debug(" interrupt event: 0x%04x\n", readw_u(&fp->regs->int_event)); 21761414f5eSMaciej W. Rozycki pr_debug(" status: 0x%04x\n", readw_u(&fp->regs->status)); 21861414f5eSMaciej W. Rozycki pr_debug(" interrupt mask: 0x%04x\n", readw_u(&fp->regs->int_mask)); 21961414f5eSMaciej W. Rozycki pr_debug(" control A: 0x%04x\n", readw_u(&fp->regs->control_a)); 22061414f5eSMaciej W. Rozycki pr_debug(" control B: 0x%04x\n", readw_u(&fp->regs->control_b)); 22161414f5eSMaciej W. Rozycki } 22261414f5eSMaciej W. Rozycki 22361414f5eSMaciej W. Rozycki static inline void fza_do_reset(struct fza_private *fp) 22461414f5eSMaciej W. Rozycki { 22561414f5eSMaciej W. Rozycki /* Reset the board. */ 22661414f5eSMaciej W. Rozycki writew_o(FZA_RESET_INIT, &fp->regs->reset); 22761414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 22861414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Read it back for a small delay. */ 22961414f5eSMaciej W. Rozycki writew_o(FZA_RESET_CLR, &fp->regs->reset); 23061414f5eSMaciej W. Rozycki 23161414f5eSMaciej W. Rozycki /* Enable all interrupt events we handle. */ 23261414f5eSMaciej W. Rozycki writew_o(fp->int_mask, &fp->regs->int_mask); 23361414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 23461414f5eSMaciej W. Rozycki } 23561414f5eSMaciej W. Rozycki 23661414f5eSMaciej W. Rozycki static inline void fza_do_shutdown(struct fza_private *fp) 23761414f5eSMaciej W. Rozycki { 23861414f5eSMaciej W. Rozycki /* Disable the driver mode. */ 23961414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_B_IDLE, &fp->regs->control_b); 24061414f5eSMaciej W. Rozycki 24161414f5eSMaciej W. Rozycki /* And reset the board. */ 24261414f5eSMaciej W. Rozycki writew_o(FZA_RESET_INIT, &fp->regs->reset); 24361414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 24461414f5eSMaciej W. Rozycki writew_o(FZA_RESET_CLR, &fp->regs->reset); 24561414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 24661414f5eSMaciej W. Rozycki } 24761414f5eSMaciej W. Rozycki 24861414f5eSMaciej W. Rozycki static int fza_reset(struct fza_private *fp) 24961414f5eSMaciej W. Rozycki { 25061414f5eSMaciej W. Rozycki unsigned long flags; 25161414f5eSMaciej W. Rozycki uint status, state; 25261414f5eSMaciej W. Rozycki long t; 25361414f5eSMaciej W. Rozycki 25461414f5eSMaciej W. Rozycki pr_info("%s: resetting the board...\n", fp->name); 25561414f5eSMaciej W. Rozycki 25661414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 25761414f5eSMaciej W. Rozycki fp->state_chg_flag = 0; 25861414f5eSMaciej W. Rozycki fza_do_reset(fp); 25961414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 26061414f5eSMaciej W. Rozycki 26161414f5eSMaciej W. Rozycki /* DEC says RESET needs up to 30 seconds to complete. My DEFZA-AA 26261414f5eSMaciej W. Rozycki * rev. C03 happily finishes in 9.7 seconds. :-) But we need to 26361414f5eSMaciej W. Rozycki * be on the safe side... 26461414f5eSMaciej W. Rozycki */ 26561414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag, 26661414f5eSMaciej W. Rozycki 45 * HZ); 26761414f5eSMaciej W. Rozycki status = readw_u(&fp->regs->status); 26861414f5eSMaciej W. Rozycki state = FZA_STATUS_GET_STATE(status); 26961414f5eSMaciej W. Rozycki if (fp->state_chg_flag == 0) { 27061414f5eSMaciej W. Rozycki pr_err("%s: RESET timed out!, state %x\n", fp->name, state); 27161414f5eSMaciej W. Rozycki return -EIO; 27261414f5eSMaciej W. Rozycki } 27361414f5eSMaciej W. Rozycki if (state != FZA_STATE_UNINITIALIZED) { 27461414f5eSMaciej W. Rozycki pr_err("%s: RESET failed!, state %x, failure ID %x\n", 27561414f5eSMaciej W. Rozycki fp->name, state, FZA_STATUS_GET_TEST(status)); 27661414f5eSMaciej W. Rozycki return -EIO; 27761414f5eSMaciej W. Rozycki } 27861414f5eSMaciej W. Rozycki pr_info("%s: OK\n", fp->name); 27961414f5eSMaciej W. Rozycki pr_debug("%s: RESET: %lums elapsed\n", fp->name, 28061414f5eSMaciej W. Rozycki (45 * HZ - t) * 1000 / HZ); 28161414f5eSMaciej W. Rozycki 28261414f5eSMaciej W. Rozycki return 0; 28361414f5eSMaciej W. Rozycki } 28461414f5eSMaciej W. Rozycki 28561414f5eSMaciej W. Rozycki static struct fza_ring_cmd __iomem *fza_cmd_send(struct net_device *dev, 28661414f5eSMaciej W. Rozycki int command) 28761414f5eSMaciej W. Rozycki { 28861414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 28961414f5eSMaciej W. Rozycki struct fza_ring_cmd __iomem *ring = fp->ring_cmd + fp->ring_cmd_index; 29061414f5eSMaciej W. Rozycki unsigned int old_mask, new_mask; 29161414f5eSMaciej W. Rozycki union fza_cmd_buf __iomem *buf; 29261414f5eSMaciej W. Rozycki struct netdev_hw_addr *ha; 29361414f5eSMaciej W. Rozycki int i; 29461414f5eSMaciej W. Rozycki 29561414f5eSMaciej W. Rozycki old_mask = fp->int_mask; 29661414f5eSMaciej W. Rozycki new_mask = old_mask & ~FZA_MASK_STATE_CHG; 29761414f5eSMaciej W. Rozycki writew_u(new_mask, &fp->regs->int_mask); 29861414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 29961414f5eSMaciej W. Rozycki fp->int_mask = new_mask; 30061414f5eSMaciej W. Rozycki 30161414f5eSMaciej W. Rozycki buf = fp->mmio + readl_u(&ring->buffer); 30261414f5eSMaciej W. Rozycki 30361414f5eSMaciej W. Rozycki if ((readl_u(&ring->cmd_own) & FZA_RING_OWN_MASK) != 30461414f5eSMaciej W. Rozycki FZA_RING_OWN_HOST) { 30561414f5eSMaciej W. Rozycki pr_warn("%s: command buffer full, command: %u!\n", fp->name, 30661414f5eSMaciej W. Rozycki command); 30761414f5eSMaciej W. Rozycki return NULL; 30861414f5eSMaciej W. Rozycki } 30961414f5eSMaciej W. Rozycki 31061414f5eSMaciej W. Rozycki switch (command) { 31161414f5eSMaciej W. Rozycki case FZA_RING_CMD_INIT: 31261414f5eSMaciej W. Rozycki writel_u(FZA_RING_TX_MODE, &buf->init.tx_mode); 31361414f5eSMaciej W. Rozycki writel_u(FZA_RING_RX_SIZE, &buf->init.hst_rx_size); 31461414f5eSMaciej W. Rozycki fza_zeros(&buf->init.counters, sizeof(buf->init.counters)); 31561414f5eSMaciej W. Rozycki break; 31661414f5eSMaciej W. Rozycki 31761414f5eSMaciej W. Rozycki case FZA_RING_CMD_MODCAM: 31861414f5eSMaciej W. Rozycki i = 0; 31961414f5eSMaciej W. Rozycki fza_writes(&hw_addr_purger, &buf->cam.hw_addr[i++], 32061414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 32161414f5eSMaciej W. Rozycki fza_writes(&hw_addr_beacon, &buf->cam.hw_addr[i++], 32261414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 32361414f5eSMaciej W. Rozycki netdev_for_each_mc_addr(ha, dev) { 32461414f5eSMaciej W. Rozycki if (i >= FZA_CMD_CAM_SIZE) 32561414f5eSMaciej W. Rozycki break; 32661414f5eSMaciej W. Rozycki fza_writes(ha->addr, &buf->cam.hw_addr[i++], 32761414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 32861414f5eSMaciej W. Rozycki } 32961414f5eSMaciej W. Rozycki while (i < FZA_CMD_CAM_SIZE) 33061414f5eSMaciej W. Rozycki fza_zeros(&buf->cam.hw_addr[i++], 33161414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 33261414f5eSMaciej W. Rozycki break; 33361414f5eSMaciej W. Rozycki 33461414f5eSMaciej W. Rozycki case FZA_RING_CMD_PARAM: 33561414f5eSMaciej W. Rozycki writel_u(loopback, &buf->param.loop_mode); 33661414f5eSMaciej W. Rozycki writel_u(fp->t_max, &buf->param.t_max); 33761414f5eSMaciej W. Rozycki writel_u(fp->t_req, &buf->param.t_req); 33861414f5eSMaciej W. Rozycki writel_u(fp->tvx, &buf->param.tvx); 33961414f5eSMaciej W. Rozycki writel_u(fp->lem_threshold, &buf->param.lem_threshold); 34061414f5eSMaciej W. Rozycki fza_writes(&fp->station_id, &buf->param.station_id, 34161414f5eSMaciej W. Rozycki sizeof(buf->param.station_id)); 34261414f5eSMaciej W. Rozycki /* Convert to milliseconds due to buggy firmware. */ 34361414f5eSMaciej W. Rozycki writel_u(fp->rtoken_timeout / 12500, 34461414f5eSMaciej W. Rozycki &buf->param.rtoken_timeout); 34561414f5eSMaciej W. Rozycki writel_u(fp->ring_purger, &buf->param.ring_purger); 34661414f5eSMaciej W. Rozycki break; 34761414f5eSMaciej W. Rozycki 34861414f5eSMaciej W. Rozycki case FZA_RING_CMD_MODPROM: 34961414f5eSMaciej W. Rozycki if (dev->flags & IFF_PROMISC) { 35061414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.llc_prom); 35161414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.smt_prom); 35261414f5eSMaciej W. Rozycki } else { 35361414f5eSMaciej W. Rozycki writel_u(0, &buf->modprom.llc_prom); 35461414f5eSMaciej W. Rozycki writel_u(0, &buf->modprom.smt_prom); 35561414f5eSMaciej W. Rozycki } 35661414f5eSMaciej W. Rozycki if (dev->flags & IFF_ALLMULTI || 35761414f5eSMaciej W. Rozycki netdev_mc_count(dev) > FZA_CMD_CAM_SIZE - 2) 35861414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.llc_multi); 35961414f5eSMaciej W. Rozycki else 36061414f5eSMaciej W. Rozycki writel_u(0, &buf->modprom.llc_multi); 36161414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.llc_bcast); 36261414f5eSMaciej W. Rozycki break; 36361414f5eSMaciej W. Rozycki } 36461414f5eSMaciej W. Rozycki 36561414f5eSMaciej W. Rozycki /* Trigger the command. */ 36661414f5eSMaciej W. Rozycki writel_u(FZA_RING_OWN_FZA | command, &ring->cmd_own); 36761414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_CMD_POLL, &fp->regs->control_a); 36861414f5eSMaciej W. Rozycki 36961414f5eSMaciej W. Rozycki fp->ring_cmd_index = (fp->ring_cmd_index + 1) % FZA_RING_CMD_SIZE; 37061414f5eSMaciej W. Rozycki 37161414f5eSMaciej W. Rozycki fp->int_mask = old_mask; 37261414f5eSMaciej W. Rozycki writew_u(fp->int_mask, &fp->regs->int_mask); 37361414f5eSMaciej W. Rozycki 37461414f5eSMaciej W. Rozycki return ring; 37561414f5eSMaciej W. Rozycki } 37661414f5eSMaciej W. Rozycki 37761414f5eSMaciej W. Rozycki static int fza_init_send(struct net_device *dev, 37861414f5eSMaciej W. Rozycki struct fza_cmd_init *__iomem *init) 37961414f5eSMaciej W. Rozycki { 38061414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 38161414f5eSMaciej W. Rozycki struct fza_ring_cmd __iomem *ring; 38261414f5eSMaciej W. Rozycki unsigned long flags; 38361414f5eSMaciej W. Rozycki u32 stat; 38461414f5eSMaciej W. Rozycki long t; 38561414f5eSMaciej W. Rozycki 38661414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 38761414f5eSMaciej W. Rozycki fp->cmd_done_flag = 0; 38861414f5eSMaciej W. Rozycki ring = fza_cmd_send(dev, FZA_RING_CMD_INIT); 38961414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 39061414f5eSMaciej W. Rozycki if (!ring) 39161414f5eSMaciej W. Rozycki /* This should never happen in the uninitialized state, 39261414f5eSMaciej W. Rozycki * so do not try to recover and just consider it fatal. 39361414f5eSMaciej W. Rozycki */ 39461414f5eSMaciej W. Rozycki return -ENOBUFS; 39561414f5eSMaciej W. Rozycki 39661414f5eSMaciej W. Rozycki /* INIT may take quite a long time (160ms for my C03). */ 39761414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); 39861414f5eSMaciej W. Rozycki if (fp->cmd_done_flag == 0) { 39961414f5eSMaciej W. Rozycki pr_err("%s: INIT command timed out!, state %x\n", fp->name, 40061414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 40161414f5eSMaciej W. Rozycki return -EIO; 40261414f5eSMaciej W. Rozycki } 40361414f5eSMaciej W. Rozycki stat = readl_u(&ring->stat); 40461414f5eSMaciej W. Rozycki if (stat != FZA_RING_STAT_SUCCESS) { 40561414f5eSMaciej W. Rozycki pr_err("%s: INIT command failed!, status %02x, state %x\n", 40661414f5eSMaciej W. Rozycki fp->name, stat, 40761414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 40861414f5eSMaciej W. Rozycki return -EIO; 40961414f5eSMaciej W. Rozycki } 41061414f5eSMaciej W. Rozycki pr_debug("%s: INIT: %lums elapsed\n", fp->name, 41161414f5eSMaciej W. Rozycki (3 * HZ - t) * 1000 / HZ); 41261414f5eSMaciej W. Rozycki 41361414f5eSMaciej W. Rozycki if (init) 41461414f5eSMaciej W. Rozycki *init = fp->mmio + readl_u(&ring->buffer); 41561414f5eSMaciej W. Rozycki return 0; 41661414f5eSMaciej W. Rozycki } 41761414f5eSMaciej W. Rozycki 41861414f5eSMaciej W. Rozycki static void fza_rx_init(struct fza_private *fp) 41961414f5eSMaciej W. Rozycki { 42061414f5eSMaciej W. Rozycki int i; 42161414f5eSMaciej W. Rozycki 42261414f5eSMaciej W. Rozycki /* Fill the host receive descriptor ring. */ 42361414f5eSMaciej W. Rozycki for (i = 0; i < FZA_RING_RX_SIZE; i++) { 42461414f5eSMaciej W. Rozycki writel_o(0, &fp->ring_hst_rx[i].rmc); 42561414f5eSMaciej W. Rozycki writel_o((fp->rx_dma[i] + 0x1000) >> 9, 42661414f5eSMaciej W. Rozycki &fp->ring_hst_rx[i].buffer1); 42761414f5eSMaciej W. Rozycki writel_o(fp->rx_dma[i] >> 9 | FZA_RING_OWN_FZA, 42861414f5eSMaciej W. Rozycki &fp->ring_hst_rx[i].buf0_own); 42961414f5eSMaciej W. Rozycki } 43061414f5eSMaciej W. Rozycki } 43161414f5eSMaciej W. Rozycki 43261414f5eSMaciej W. Rozycki static void fza_set_rx_mode(struct net_device *dev) 43361414f5eSMaciej W. Rozycki { 43461414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_MODCAM); 43561414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_MODPROM); 43661414f5eSMaciej W. Rozycki } 43761414f5eSMaciej W. Rozycki 43861414f5eSMaciej W. Rozycki union fza_buffer_txp { 43961414f5eSMaciej W. Rozycki struct fza_buffer_tx *data_ptr; 44061414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *mmio_ptr; 44161414f5eSMaciej W. Rozycki }; 44261414f5eSMaciej W. Rozycki 44361414f5eSMaciej W. Rozycki static int fza_do_xmit(union fza_buffer_txp ub, int len, 44461414f5eSMaciej W. Rozycki struct net_device *dev, int smt) 44561414f5eSMaciej W. Rozycki { 44661414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 44761414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *rmc_tx_ptr; 44861414f5eSMaciej W. Rozycki int i, first, frag_len, left_len; 44961414f5eSMaciej W. Rozycki u32 own, rmc; 45061414f5eSMaciej W. Rozycki 45161414f5eSMaciej W. Rozycki if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 45261414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 45361414f5eSMaciej W. Rozycki FZA_TX_BUFFER_SIZE) < len) 45461414f5eSMaciej W. Rozycki return 1; 45561414f5eSMaciej W. Rozycki 45661414f5eSMaciej W. Rozycki first = fp->ring_rmc_tx_index; 45761414f5eSMaciej W. Rozycki 45861414f5eSMaciej W. Rozycki left_len = len; 45961414f5eSMaciej W. Rozycki frag_len = FZA_TX_BUFFER_SIZE; 46061414f5eSMaciej W. Rozycki /* First descriptor is relinquished last. */ 46161414f5eSMaciej W. Rozycki own = FZA_RING_TX_OWN_HOST; 46261414f5eSMaciej W. Rozycki /* First descriptor carries frame length; we don't use cut-through. */ 46361414f5eSMaciej W. Rozycki rmc = FZA_RING_TX_SOP | FZA_RING_TX_VBC | len; 46461414f5eSMaciej W. Rozycki do { 46561414f5eSMaciej W. Rozycki i = fp->ring_rmc_tx_index; 46661414f5eSMaciej W. Rozycki rmc_tx_ptr = &fp->buffer_tx[i]; 46761414f5eSMaciej W. Rozycki 46861414f5eSMaciej W. Rozycki if (left_len < FZA_TX_BUFFER_SIZE) 46961414f5eSMaciej W. Rozycki frag_len = left_len; 47061414f5eSMaciej W. Rozycki left_len -= frag_len; 47161414f5eSMaciej W. Rozycki 47261414f5eSMaciej W. Rozycki /* Length must be a multiple of 4 as only word writes are 47361414f5eSMaciej W. Rozycki * permitted! 47461414f5eSMaciej W. Rozycki */ 47561414f5eSMaciej W. Rozycki frag_len = (frag_len + 3) & ~3; 47661414f5eSMaciej W. Rozycki if (smt) 47761414f5eSMaciej W. Rozycki fza_moves(ub.mmio_ptr, rmc_tx_ptr, frag_len); 47861414f5eSMaciej W. Rozycki else 47961414f5eSMaciej W. Rozycki fza_writes(ub.data_ptr, rmc_tx_ptr, frag_len); 48061414f5eSMaciej W. Rozycki 48161414f5eSMaciej W. Rozycki if (left_len == 0) 48261414f5eSMaciej W. Rozycki rmc |= FZA_RING_TX_EOP; /* Mark last frag. */ 48361414f5eSMaciej W. Rozycki 48461414f5eSMaciej W. Rozycki writel_o(rmc, &fp->ring_rmc_tx[i].rmc); 48561414f5eSMaciej W. Rozycki writel_o(own, &fp->ring_rmc_tx[i].own); 48661414f5eSMaciej W. Rozycki 48761414f5eSMaciej W. Rozycki ub.data_ptr++; 48861414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index = (fp->ring_rmc_tx_index + 1) % 48961414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size; 49061414f5eSMaciej W. Rozycki 49161414f5eSMaciej W. Rozycki /* Settings for intermediate frags. */ 49261414f5eSMaciej W. Rozycki own = FZA_RING_TX_OWN_RMC; 49361414f5eSMaciej W. Rozycki rmc = 0; 49461414f5eSMaciej W. Rozycki } while (left_len > 0); 49561414f5eSMaciej W. Rozycki 49661414f5eSMaciej W. Rozycki if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 49761414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 49861414f5eSMaciej W. Rozycki FZA_TX_BUFFER_SIZE) < dev->mtu + dev->hard_header_len) { 49961414f5eSMaciej W. Rozycki netif_stop_queue(dev); 50061414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 50161414f5eSMaciej W. Rozycki } 50261414f5eSMaciej W. Rozycki 50361414f5eSMaciej W. Rozycki writel_o(FZA_RING_TX_OWN_RMC, &fp->ring_rmc_tx[first].own); 50461414f5eSMaciej W. Rozycki 50561414f5eSMaciej W. Rozycki /* Go, go, go! */ 50661414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_TX_POLL, &fp->regs->control_a); 50761414f5eSMaciej W. Rozycki 50861414f5eSMaciej W. Rozycki return 0; 50961414f5eSMaciej W. Rozycki } 51061414f5eSMaciej W. Rozycki 51161414f5eSMaciej W. Rozycki static int fza_do_recv_smt(struct fza_buffer_tx *data_ptr, int len, 51261414f5eSMaciej W. Rozycki u32 rmc, struct net_device *dev) 51361414f5eSMaciej W. Rozycki { 51461414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 51561414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *smt_rx_ptr; 51661414f5eSMaciej W. Rozycki u32 own; 51761414f5eSMaciej W. Rozycki int i; 51861414f5eSMaciej W. Rozycki 51961414f5eSMaciej W. Rozycki i = fp->ring_smt_rx_index; 52061414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_smt_rx[i].own); 52161414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 52261414f5eSMaciej W. Rozycki return 1; 52361414f5eSMaciej W. Rozycki 52461414f5eSMaciej W. Rozycki smt_rx_ptr = fp->mmio + readl_u(&fp->ring_smt_rx[i].buffer); 52561414f5eSMaciej W. Rozycki 52661414f5eSMaciej W. Rozycki /* Length must be a multiple of 4 as only word writes are permitted! */ 52761414f5eSMaciej W. Rozycki fza_writes(data_ptr, smt_rx_ptr, (len + 3) & ~3); 52861414f5eSMaciej W. Rozycki 52961414f5eSMaciej W. Rozycki writel_o(rmc, &fp->ring_smt_rx[i].rmc); 53061414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_rx[i].own); 53161414f5eSMaciej W. Rozycki 53261414f5eSMaciej W. Rozycki fp->ring_smt_rx_index = 53361414f5eSMaciej W. Rozycki (fp->ring_smt_rx_index + 1) % fp->ring_smt_rx_size; 53461414f5eSMaciej W. Rozycki 53561414f5eSMaciej W. Rozycki /* Grab it! */ 53661414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_SMT_RX_POLL, &fp->regs->control_a); 53761414f5eSMaciej W. Rozycki 53861414f5eSMaciej W. Rozycki return 0; 53961414f5eSMaciej W. Rozycki } 54061414f5eSMaciej W. Rozycki 54161414f5eSMaciej W. Rozycki static void fza_tx(struct net_device *dev) 54261414f5eSMaciej W. Rozycki { 54361414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 54461414f5eSMaciej W. Rozycki u32 own, rmc; 54561414f5eSMaciej W. Rozycki int i; 54661414f5eSMaciej W. Rozycki 54761414f5eSMaciej W. Rozycki while (1) { 54861414f5eSMaciej W. Rozycki i = fp->ring_rmc_txd_index; 54961414f5eSMaciej W. Rozycki if (i == fp->ring_rmc_tx_index) 55061414f5eSMaciej W. Rozycki break; 55161414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_rmc_tx[i].own); 55261414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) 55361414f5eSMaciej W. Rozycki break; 55461414f5eSMaciej W. Rozycki 55561414f5eSMaciej W. Rozycki rmc = readl_u(&fp->ring_rmc_tx[i].rmc); 55661414f5eSMaciej W. Rozycki /* Only process the first descriptor. */ 55761414f5eSMaciej W. Rozycki if ((rmc & FZA_RING_TX_SOP) != 0) { 55861414f5eSMaciej W. Rozycki if ((rmc & FZA_RING_TX_DCC_MASK) == 55961414f5eSMaciej W. Rozycki FZA_RING_TX_DCC_SUCCESS) { 56061414f5eSMaciej W. Rozycki int pkt_len = (rmc & FZA_RING_PBC_MASK) - 3; 56161414f5eSMaciej W. Rozycki /* Omit PRH. */ 56261414f5eSMaciej W. Rozycki 56361414f5eSMaciej W. Rozycki fp->stats.tx_packets++; 56461414f5eSMaciej W. Rozycki fp->stats.tx_bytes += pkt_len; 56561414f5eSMaciej W. Rozycki } else { 56661414f5eSMaciej W. Rozycki fp->stats.tx_errors++; 56761414f5eSMaciej W. Rozycki switch (rmc & FZA_RING_TX_DCC_MASK) { 56861414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_DTP_SOP: 56961414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_DTP: 57061414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_ABORT: 57161414f5eSMaciej W. Rozycki fp->stats.tx_aborted_errors++; 57261414f5eSMaciej W. Rozycki break; 57361414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_UNDRRUN: 57461414f5eSMaciej W. Rozycki fp->stats.tx_fifo_errors++; 57561414f5eSMaciej W. Rozycki break; 57661414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_PARITY: 57761414f5eSMaciej W. Rozycki default: 57861414f5eSMaciej W. Rozycki break; 57961414f5eSMaciej W. Rozycki } 58061414f5eSMaciej W. Rozycki } 58161414f5eSMaciej W. Rozycki } 58261414f5eSMaciej W. Rozycki 58361414f5eSMaciej W. Rozycki fp->ring_rmc_txd_index = (fp->ring_rmc_txd_index + 1) % 58461414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size; 58561414f5eSMaciej W. Rozycki } 58661414f5eSMaciej W. Rozycki 58761414f5eSMaciej W. Rozycki if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 58861414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 58961414f5eSMaciej W. Rozycki FZA_TX_BUFFER_SIZE) >= dev->mtu + dev->hard_header_len) { 59061414f5eSMaciej W. Rozycki if (fp->queue_active) { 59161414f5eSMaciej W. Rozycki netif_wake_queue(dev); 59261414f5eSMaciej W. Rozycki pr_debug("%s: queue woken\n", fp->name); 59361414f5eSMaciej W. Rozycki } 59461414f5eSMaciej W. Rozycki } 59561414f5eSMaciej W. Rozycki } 59661414f5eSMaciej W. Rozycki 59761414f5eSMaciej W. Rozycki static inline int fza_rx_err(struct fza_private *fp, 59861414f5eSMaciej W. Rozycki const u32 rmc, const u8 fc) 59961414f5eSMaciej W. Rozycki { 60061414f5eSMaciej W. Rozycki int len, min_len, max_len; 60161414f5eSMaciej W. Rozycki 60261414f5eSMaciej W. Rozycki len = rmc & FZA_RING_PBC_MASK; 60361414f5eSMaciej W. Rozycki 60461414f5eSMaciej W. Rozycki if (unlikely((rmc & FZA_RING_RX_BAD) != 0)) { 60561414f5eSMaciej W. Rozycki fp->stats.rx_errors++; 60661414f5eSMaciej W. Rozycki 60761414f5eSMaciej W. Rozycki /* Check special status codes. */ 60861414f5eSMaciej W. Rozycki if ((rmc & (FZA_RING_RX_CRC | FZA_RING_RX_RRR_MASK | 60961414f5eSMaciej W. Rozycki FZA_RING_RX_DA_MASK | FZA_RING_RX_SA_MASK)) == 61061414f5eSMaciej W. Rozycki (FZA_RING_RX_CRC | FZA_RING_RX_RRR_DADDR | 61161414f5eSMaciej W. Rozycki FZA_RING_RX_DA_CAM | FZA_RING_RX_SA_ALIAS)) { 61261414f5eSMaciej W. Rozycki if (len >= 8190) 61361414f5eSMaciej W. Rozycki fp->stats.rx_length_errors++; 61461414f5eSMaciej W. Rozycki return 1; 61561414f5eSMaciej W. Rozycki } 61661414f5eSMaciej W. Rozycki if ((rmc & (FZA_RING_RX_CRC | FZA_RING_RX_RRR_MASK | 61761414f5eSMaciej W. Rozycki FZA_RING_RX_DA_MASK | FZA_RING_RX_SA_MASK)) == 61861414f5eSMaciej W. Rozycki (FZA_RING_RX_CRC | FZA_RING_RX_RRR_DADDR | 61961414f5eSMaciej W. Rozycki FZA_RING_RX_DA_CAM | FZA_RING_RX_SA_CAM)) { 62061414f5eSMaciej W. Rozycki /* Halt the interface to trigger a reset. */ 62161414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_HALT, &fp->regs->control_a); 62261414f5eSMaciej W. Rozycki readw_o(&fp->regs->control_a); /* Synchronize. */ 62361414f5eSMaciej W. Rozycki return 1; 62461414f5eSMaciej W. Rozycki } 62561414f5eSMaciej W. Rozycki 62661414f5eSMaciej W. Rozycki /* Check the MAC status. */ 62761414f5eSMaciej W. Rozycki switch (rmc & FZA_RING_RX_RRR_MASK) { 62861414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_OK: 62961414f5eSMaciej W. Rozycki if ((rmc & FZA_RING_RX_CRC) != 0) 63061414f5eSMaciej W. Rozycki fp->stats.rx_crc_errors++; 63161414f5eSMaciej W. Rozycki else if ((rmc & FZA_RING_RX_FSC_MASK) == 0 || 63261414f5eSMaciej W. Rozycki (rmc & FZA_RING_RX_FSB_ERR) != 0) 63361414f5eSMaciej W. Rozycki fp->stats.rx_frame_errors++; 63461414f5eSMaciej W. Rozycki return 1; 63561414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_SADDR: 63661414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_DADDR: 63761414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_ABORT: 63861414f5eSMaciej W. Rozycki /* Halt the interface to trigger a reset. */ 63961414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_HALT, &fp->regs->control_a); 64061414f5eSMaciej W. Rozycki readw_o(&fp->regs->control_a); /* Synchronize. */ 64161414f5eSMaciej W. Rozycki return 1; 64261414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_LENGTH: 64361414f5eSMaciej W. Rozycki fp->stats.rx_frame_errors++; 64461414f5eSMaciej W. Rozycki return 1; 64561414f5eSMaciej W. Rozycki default: 64661414f5eSMaciej W. Rozycki return 1; 64761414f5eSMaciej W. Rozycki } 64861414f5eSMaciej W. Rozycki } 64961414f5eSMaciej W. Rozycki 65061414f5eSMaciej W. Rozycki /* Packet received successfully; validate the length. */ 65161414f5eSMaciej W. Rozycki switch (fc & FDDI_FC_K_FORMAT_MASK) { 65261414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_MANAGEMENT: 65361414f5eSMaciej W. Rozycki if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_ASYNC) 65461414f5eSMaciej W. Rozycki min_len = 37; 65561414f5eSMaciej W. Rozycki else 65661414f5eSMaciej W. Rozycki min_len = 17; 65761414f5eSMaciej W. Rozycki break; 65861414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_LLC: 65961414f5eSMaciej W. Rozycki min_len = 20; 66061414f5eSMaciej W. Rozycki break; 66161414f5eSMaciej W. Rozycki default: 66261414f5eSMaciej W. Rozycki min_len = 17; 66361414f5eSMaciej W. Rozycki break; 66461414f5eSMaciej W. Rozycki } 66561414f5eSMaciej W. Rozycki max_len = 4495; 66661414f5eSMaciej W. Rozycki if (len < min_len || len > max_len) { 66761414f5eSMaciej W. Rozycki fp->stats.rx_errors++; 66861414f5eSMaciej W. Rozycki fp->stats.rx_length_errors++; 66961414f5eSMaciej W. Rozycki return 1; 67061414f5eSMaciej W. Rozycki } 67161414f5eSMaciej W. Rozycki 67261414f5eSMaciej W. Rozycki return 0; 67361414f5eSMaciej W. Rozycki } 67461414f5eSMaciej W. Rozycki 67561414f5eSMaciej W. Rozycki static void fza_rx(struct net_device *dev) 67661414f5eSMaciej W. Rozycki { 67761414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 67861414f5eSMaciej W. Rozycki struct sk_buff *skb, *newskb; 67961414f5eSMaciej W. Rozycki struct fza_fddihdr *frame; 68061414f5eSMaciej W. Rozycki dma_addr_t dma, newdma; 68161414f5eSMaciej W. Rozycki u32 own, rmc, buf; 68261414f5eSMaciej W. Rozycki int i, len; 68361414f5eSMaciej W. Rozycki u8 fc; 68461414f5eSMaciej W. Rozycki 68561414f5eSMaciej W. Rozycki while (1) { 68661414f5eSMaciej W. Rozycki i = fp->ring_hst_rx_index; 68761414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_hst_rx[i].buf0_own); 68861414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 68961414f5eSMaciej W. Rozycki break; 69061414f5eSMaciej W. Rozycki 69161414f5eSMaciej W. Rozycki rmc = readl_u(&fp->ring_hst_rx[i].rmc); 69261414f5eSMaciej W. Rozycki skb = fp->rx_skbuff[i]; 69361414f5eSMaciej W. Rozycki dma = fp->rx_dma[i]; 69461414f5eSMaciej W. Rozycki 69561414f5eSMaciej W. Rozycki /* The RMC doesn't count the preamble and the starting 69661414f5eSMaciej W. Rozycki * delimiter. We fix it up here for a total of 3 octets. 69761414f5eSMaciej W. Rozycki */ 69861414f5eSMaciej W. Rozycki dma_rmb(); 69961414f5eSMaciej W. Rozycki len = (rmc & FZA_RING_PBC_MASK) + 3; 70061414f5eSMaciej W. Rozycki frame = (struct fza_fddihdr *)skb->data; 70161414f5eSMaciej W. Rozycki 70261414f5eSMaciej W. Rozycki /* We need to get at real FC. */ 70361414f5eSMaciej W. Rozycki dma_sync_single_for_cpu(fp->bdev, 70461414f5eSMaciej W. Rozycki dma + 70561414f5eSMaciej W. Rozycki ((u8 *)&frame->hdr.fc - (u8 *)frame), 70661414f5eSMaciej W. Rozycki sizeof(frame->hdr.fc), 70761414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 70861414f5eSMaciej W. Rozycki fc = frame->hdr.fc; 70961414f5eSMaciej W. Rozycki 71061414f5eSMaciej W. Rozycki if (fza_rx_err(fp, rmc, fc)) 71161414f5eSMaciej W. Rozycki goto err_rx; 71261414f5eSMaciej W. Rozycki 71361414f5eSMaciej W. Rozycki /* We have to 512-byte-align RX buffers... */ 71461414f5eSMaciej W. Rozycki newskb = fza_alloc_skb_irq(dev, FZA_RX_BUFFER_SIZE + 511); 71561414f5eSMaciej W. Rozycki if (newskb) { 71661414f5eSMaciej W. Rozycki fza_skb_align(newskb, 512); 71761414f5eSMaciej W. Rozycki newdma = dma_map_single(fp->bdev, newskb->data, 71861414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, 71961414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 72061414f5eSMaciej W. Rozycki if (dma_mapping_error(fp->bdev, newdma)) { 72161414f5eSMaciej W. Rozycki dev_kfree_skb_irq(newskb); 72261414f5eSMaciej W. Rozycki newskb = NULL; 72361414f5eSMaciej W. Rozycki } 72461414f5eSMaciej W. Rozycki } 72561414f5eSMaciej W. Rozycki if (newskb) { 72661414f5eSMaciej W. Rozycki int pkt_len = len - 7; /* Omit P, SD and FCS. */ 72761414f5eSMaciej W. Rozycki int is_multi; 72861414f5eSMaciej W. Rozycki int rx_stat; 72961414f5eSMaciej W. Rozycki 73061414f5eSMaciej W. Rozycki dma_unmap_single(fp->bdev, dma, FZA_RX_BUFFER_SIZE, 73161414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 73261414f5eSMaciej W. Rozycki 73361414f5eSMaciej W. Rozycki /* Queue SMT frames to the SMT receive ring. */ 73461414f5eSMaciej W. Rozycki if ((fc & (FDDI_FC_K_CLASS_MASK | 73561414f5eSMaciej W. Rozycki FDDI_FC_K_FORMAT_MASK)) == 73661414f5eSMaciej W. Rozycki (FDDI_FC_K_CLASS_ASYNC | 73761414f5eSMaciej W. Rozycki FDDI_FC_K_FORMAT_MANAGEMENT) && 73861414f5eSMaciej W. Rozycki (rmc & FZA_RING_RX_DA_MASK) != 73961414f5eSMaciej W. Rozycki FZA_RING_RX_DA_PROM) { 74061414f5eSMaciej W. Rozycki if (fza_do_recv_smt((struct fza_buffer_tx *) 74161414f5eSMaciej W. Rozycki skb->data, len, rmc, 74261414f5eSMaciej W. Rozycki dev)) { 74361414f5eSMaciej W. Rozycki writel_o(FZA_CONTROL_A_SMT_RX_OVFL, 74461414f5eSMaciej W. Rozycki &fp->regs->control_a); 74561414f5eSMaciej W. Rozycki } 74661414f5eSMaciej W. Rozycki } 74761414f5eSMaciej W. Rozycki 74861414f5eSMaciej W. Rozycki is_multi = ((frame->hdr.daddr[0] & 0x01) != 0); 74961414f5eSMaciej W. Rozycki 75061414f5eSMaciej W. Rozycki skb_reserve(skb, 3); /* Skip over P and SD. */ 75161414f5eSMaciej W. Rozycki skb_put(skb, pkt_len); /* And cut off FCS. */ 75261414f5eSMaciej W. Rozycki skb->protocol = fddi_type_trans(skb, dev); 75361414f5eSMaciej W. Rozycki 75461414f5eSMaciej W. Rozycki rx_stat = netif_rx(skb); 75561414f5eSMaciej W. Rozycki if (rx_stat != NET_RX_DROP) { 75661414f5eSMaciej W. Rozycki fp->stats.rx_packets++; 75761414f5eSMaciej W. Rozycki fp->stats.rx_bytes += pkt_len; 75861414f5eSMaciej W. Rozycki if (is_multi) 75961414f5eSMaciej W. Rozycki fp->stats.multicast++; 76061414f5eSMaciej W. Rozycki } else { 76161414f5eSMaciej W. Rozycki fp->stats.rx_dropped++; 76261414f5eSMaciej W. Rozycki } 76361414f5eSMaciej W. Rozycki 76461414f5eSMaciej W. Rozycki skb = newskb; 76561414f5eSMaciej W. Rozycki dma = newdma; 76661414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = skb; 76761414f5eSMaciej W. Rozycki fp->rx_dma[i] = dma; 76861414f5eSMaciej W. Rozycki } else { 76961414f5eSMaciej W. Rozycki fp->stats.rx_dropped++; 77061414f5eSMaciej W. Rozycki pr_notice("%s: memory squeeze, dropping packet\n", 77161414f5eSMaciej W. Rozycki fp->name); 77261414f5eSMaciej W. Rozycki } 77361414f5eSMaciej W. Rozycki 77461414f5eSMaciej W. Rozycki err_rx: 77561414f5eSMaciej W. Rozycki writel_o(0, &fp->ring_hst_rx[i].rmc); 77661414f5eSMaciej W. Rozycki buf = (dma + 0x1000) >> 9; 77761414f5eSMaciej W. Rozycki writel_o(buf, &fp->ring_hst_rx[i].buffer1); 77861414f5eSMaciej W. Rozycki buf = dma >> 9 | FZA_RING_OWN_FZA; 77961414f5eSMaciej W. Rozycki writel_o(buf, &fp->ring_hst_rx[i].buf0_own); 78061414f5eSMaciej W. Rozycki fp->ring_hst_rx_index = 78161414f5eSMaciej W. Rozycki (fp->ring_hst_rx_index + 1) % fp->ring_hst_rx_size; 78261414f5eSMaciej W. Rozycki } 78361414f5eSMaciej W. Rozycki } 78461414f5eSMaciej W. Rozycki 78561414f5eSMaciej W. Rozycki static void fza_tx_smt(struct net_device *dev) 78661414f5eSMaciej W. Rozycki { 78761414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 78804453b6bSMaciej W. Rozycki struct fza_buffer_tx __iomem *smt_tx_ptr; 78961414f5eSMaciej W. Rozycki int i, len; 79061414f5eSMaciej W. Rozycki u32 own; 79161414f5eSMaciej W. Rozycki 79261414f5eSMaciej W. Rozycki while (1) { 79361414f5eSMaciej W. Rozycki i = fp->ring_smt_tx_index; 79461414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_smt_tx[i].own); 79561414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 79661414f5eSMaciej W. Rozycki break; 79761414f5eSMaciej W. Rozycki 79861414f5eSMaciej W. Rozycki smt_tx_ptr = fp->mmio + readl_u(&fp->ring_smt_tx[i].buffer); 79961414f5eSMaciej W. Rozycki len = readl_u(&fp->ring_smt_tx[i].rmc) & FZA_RING_PBC_MASK; 80061414f5eSMaciej W. Rozycki 8019f9a742dSMaciej W. Rozycki if (!netif_queue_stopped(dev)) { 8029f9a742dSMaciej W. Rozycki if (dev_nit_active(dev)) { 80304453b6bSMaciej W. Rozycki struct fza_buffer_tx *skb_data_ptr; 8049f9a742dSMaciej W. Rozycki struct sk_buff *skb; 8059f9a742dSMaciej W. Rozycki 8069f9a742dSMaciej W. Rozycki /* Length must be a multiple of 4 as only word 8079f9a742dSMaciej W. Rozycki * reads are permitted! 8089f9a742dSMaciej W. Rozycki */ 8099f9a742dSMaciej W. Rozycki skb = fza_alloc_skb_irq(dev, (len + 3) & ~3); 8109f9a742dSMaciej W. Rozycki if (!skb) 8119f9a742dSMaciej W. Rozycki goto err_no_skb; /* Drop. */ 8129f9a742dSMaciej W. Rozycki 8139f9a742dSMaciej W. Rozycki skb_data_ptr = (struct fza_buffer_tx *) 8149f9a742dSMaciej W. Rozycki skb->data; 8159f9a742dSMaciej W. Rozycki 8169f9a742dSMaciej W. Rozycki fza_reads(smt_tx_ptr, skb_data_ptr, 8179f9a742dSMaciej W. Rozycki (len + 3) & ~3); 8189f9a742dSMaciej W. Rozycki skb->dev = dev; 8199f9a742dSMaciej W. Rozycki skb_reserve(skb, 3); /* Skip over PRH. */ 8209f9a742dSMaciej W. Rozycki skb_put(skb, len - 3); 8219f9a742dSMaciej W. Rozycki skb_reset_network_header(skb); 8229f9a742dSMaciej W. Rozycki 8239f9a742dSMaciej W. Rozycki dev_queue_xmit_nit(skb, dev); 8249f9a742dSMaciej W. Rozycki 8259f9a742dSMaciej W. Rozycki dev_kfree_skb_irq(skb); 8269f9a742dSMaciej W. Rozycki 8279f9a742dSMaciej W. Rozycki err_no_skb: 8289f9a742dSMaciej W. Rozycki ; 8299f9a742dSMaciej W. Rozycki } 8309f9a742dSMaciej W. Rozycki 83161414f5eSMaciej W. Rozycki /* Queue the frame to the RMC transmit ring. */ 83261414f5eSMaciej W. Rozycki fza_do_xmit((union fza_buffer_txp) 83361414f5eSMaciej W. Rozycki { .mmio_ptr = smt_tx_ptr }, 83461414f5eSMaciej W. Rozycki len, dev, 1); 8359f9a742dSMaciej W. Rozycki } 83661414f5eSMaciej W. Rozycki 83761414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own); 83861414f5eSMaciej W. Rozycki fp->ring_smt_tx_index = 83961414f5eSMaciej W. Rozycki (fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size; 84061414f5eSMaciej W. Rozycki } 84161414f5eSMaciej W. Rozycki } 84261414f5eSMaciej W. Rozycki 84361414f5eSMaciej W. Rozycki static void fza_uns(struct net_device *dev) 84461414f5eSMaciej W. Rozycki { 84561414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 84661414f5eSMaciej W. Rozycki u32 own; 84761414f5eSMaciej W. Rozycki int i; 84861414f5eSMaciej W. Rozycki 84961414f5eSMaciej W. Rozycki while (1) { 85061414f5eSMaciej W. Rozycki i = fp->ring_uns_index; 85161414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_uns[i].own); 85261414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 85361414f5eSMaciej W. Rozycki break; 85461414f5eSMaciej W. Rozycki 85561414f5eSMaciej W. Rozycki if (readl_u(&fp->ring_uns[i].id) == FZA_RING_UNS_RX_OVER) { 85661414f5eSMaciej W. Rozycki fp->stats.rx_errors++; 85761414f5eSMaciej W. Rozycki fp->stats.rx_over_errors++; 85861414f5eSMaciej W. Rozycki } 85961414f5eSMaciej W. Rozycki 86061414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_uns[i].own); 86161414f5eSMaciej W. Rozycki fp->ring_uns_index = 86261414f5eSMaciej W. Rozycki (fp->ring_uns_index + 1) % FZA_RING_UNS_SIZE; 86361414f5eSMaciej W. Rozycki } 86461414f5eSMaciej W. Rozycki } 86561414f5eSMaciej W. Rozycki 86661414f5eSMaciej W. Rozycki static void fza_tx_flush(struct net_device *dev) 86761414f5eSMaciej W. Rozycki { 86861414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 86961414f5eSMaciej W. Rozycki u32 own; 87061414f5eSMaciej W. Rozycki int i; 87161414f5eSMaciej W. Rozycki 87261414f5eSMaciej W. Rozycki /* Clean up the SMT TX ring. */ 87361414f5eSMaciej W. Rozycki i = fp->ring_smt_tx_index; 87461414f5eSMaciej W. Rozycki do { 87561414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own); 87661414f5eSMaciej W. Rozycki fp->ring_smt_tx_index = 87761414f5eSMaciej W. Rozycki (fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size; 87861414f5eSMaciej W. Rozycki 87961414f5eSMaciej W. Rozycki } while (i != fp->ring_smt_tx_index); 88061414f5eSMaciej W. Rozycki 88161414f5eSMaciej W. Rozycki /* Clean up the RMC TX ring. */ 88261414f5eSMaciej W. Rozycki i = fp->ring_rmc_tx_index; 88361414f5eSMaciej W. Rozycki do { 88461414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_rmc_tx[i].own); 88561414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) { 88661414f5eSMaciej W. Rozycki u32 rmc = readl_u(&fp->ring_rmc_tx[i].rmc); 88761414f5eSMaciej W. Rozycki 88861414f5eSMaciej W. Rozycki writel_u(rmc | FZA_RING_TX_DTP, 88961414f5eSMaciej W. Rozycki &fp->ring_rmc_tx[i].rmc); 89061414f5eSMaciej W. Rozycki } 89161414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index = 89261414f5eSMaciej W. Rozycki (fp->ring_rmc_tx_index + 1) % fp->ring_rmc_tx_size; 89361414f5eSMaciej W. Rozycki 89461414f5eSMaciej W. Rozycki } while (i != fp->ring_rmc_tx_index); 89561414f5eSMaciej W. Rozycki 89661414f5eSMaciej W. Rozycki /* Done. */ 89761414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_FLUSH_DONE, &fp->regs->control_a); 89861414f5eSMaciej W. Rozycki } 89961414f5eSMaciej W. Rozycki 90061414f5eSMaciej W. Rozycki static irqreturn_t fza_interrupt(int irq, void *dev_id) 90161414f5eSMaciej W. Rozycki { 90261414f5eSMaciej W. Rozycki struct net_device *dev = dev_id; 90361414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 90461414f5eSMaciej W. Rozycki uint int_event; 90561414f5eSMaciej W. Rozycki 90661414f5eSMaciej W. Rozycki /* Get interrupt events. */ 90761414f5eSMaciej W. Rozycki int_event = readw_o(&fp->regs->int_event) & fp->int_mask; 90861414f5eSMaciej W. Rozycki if (int_event == 0) 90961414f5eSMaciej W. Rozycki return IRQ_NONE; 91061414f5eSMaciej W. Rozycki 91161414f5eSMaciej W. Rozycki /* Clear the events. */ 91261414f5eSMaciej W. Rozycki writew_u(int_event, &fp->regs->int_event); 91361414f5eSMaciej W. Rozycki 91461414f5eSMaciej W. Rozycki /* Now handle the events. The order matters. */ 91561414f5eSMaciej W. Rozycki 91661414f5eSMaciej W. Rozycki /* Command finished interrupt. */ 91761414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_CMD_DONE) != 0) { 91861414f5eSMaciej W. Rozycki fp->irq_count_cmd_done++; 91961414f5eSMaciej W. Rozycki 92061414f5eSMaciej W. Rozycki spin_lock(&fp->lock); 92161414f5eSMaciej W. Rozycki fp->cmd_done_flag = 1; 92261414f5eSMaciej W. Rozycki wake_up(&fp->cmd_done_wait); 92361414f5eSMaciej W. Rozycki spin_unlock(&fp->lock); 92461414f5eSMaciej W. Rozycki } 92561414f5eSMaciej W. Rozycki 92661414f5eSMaciej W. Rozycki /* Transmit finished interrupt. */ 92761414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_TX_DONE) != 0) { 92861414f5eSMaciej W. Rozycki fp->irq_count_tx_done++; 92961414f5eSMaciej W. Rozycki fza_tx(dev); 93061414f5eSMaciej W. Rozycki } 93161414f5eSMaciej W. Rozycki 93261414f5eSMaciej W. Rozycki /* Host receive interrupt. */ 93361414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_RX_POLL) != 0) { 93461414f5eSMaciej W. Rozycki fp->irq_count_rx_poll++; 93561414f5eSMaciej W. Rozycki fza_rx(dev); 93661414f5eSMaciej W. Rozycki } 93761414f5eSMaciej W. Rozycki 93861414f5eSMaciej W. Rozycki /* SMT transmit interrupt. */ 93961414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_SMT_TX_POLL) != 0) { 94061414f5eSMaciej W. Rozycki fp->irq_count_smt_tx_poll++; 94161414f5eSMaciej W. Rozycki fza_tx_smt(dev); 94261414f5eSMaciej W. Rozycki } 94361414f5eSMaciej W. Rozycki 94461414f5eSMaciej W. Rozycki /* Transmit ring flush request. */ 94561414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_FLUSH_TX) != 0) { 94661414f5eSMaciej W. Rozycki fp->irq_count_flush_tx++; 94761414f5eSMaciej W. Rozycki fza_tx_flush(dev); 94861414f5eSMaciej W. Rozycki } 94961414f5eSMaciej W. Rozycki 95061414f5eSMaciej W. Rozycki /* Link status change interrupt. */ 95161414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_LINK_ST_CHG) != 0) { 95261414f5eSMaciej W. Rozycki uint status; 95361414f5eSMaciej W. Rozycki 95461414f5eSMaciej W. Rozycki fp->irq_count_link_st_chg++; 95561414f5eSMaciej W. Rozycki status = readw_u(&fp->regs->status); 95661414f5eSMaciej W. Rozycki if (FZA_STATUS_GET_LINK(status) == FZA_LINK_ON) { 95761414f5eSMaciej W. Rozycki netif_carrier_on(dev); 95861414f5eSMaciej W. Rozycki pr_info("%s: link available\n", fp->name); 95961414f5eSMaciej W. Rozycki } else { 96061414f5eSMaciej W. Rozycki netif_carrier_off(dev); 96161414f5eSMaciej W. Rozycki pr_info("%s: link unavailable\n", fp->name); 96261414f5eSMaciej W. Rozycki } 96361414f5eSMaciej W. Rozycki } 96461414f5eSMaciej W. Rozycki 96561414f5eSMaciej W. Rozycki /* Unsolicited event interrupt. */ 96661414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_UNS_POLL) != 0) { 96761414f5eSMaciej W. Rozycki fp->irq_count_uns_poll++; 96861414f5eSMaciej W. Rozycki fza_uns(dev); 96961414f5eSMaciej W. Rozycki } 97061414f5eSMaciej W. Rozycki 97161414f5eSMaciej W. Rozycki /* State change interrupt. */ 97261414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_STATE_CHG) != 0) { 97361414f5eSMaciej W. Rozycki uint status, state; 97461414f5eSMaciej W. Rozycki 97561414f5eSMaciej W. Rozycki fp->irq_count_state_chg++; 97661414f5eSMaciej W. Rozycki 97761414f5eSMaciej W. Rozycki status = readw_u(&fp->regs->status); 97861414f5eSMaciej W. Rozycki state = FZA_STATUS_GET_STATE(status); 97961414f5eSMaciej W. Rozycki pr_debug("%s: state change: %x\n", fp->name, state); 98061414f5eSMaciej W. Rozycki switch (state) { 98161414f5eSMaciej W. Rozycki case FZA_STATE_RESET: 98261414f5eSMaciej W. Rozycki break; 98361414f5eSMaciej W. Rozycki 98461414f5eSMaciej W. Rozycki case FZA_STATE_UNINITIALIZED: 98561414f5eSMaciej W. Rozycki netif_carrier_off(dev); 98661414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 98761414f5eSMaciej W. Rozycki fp->ring_cmd_index = 0; 98861414f5eSMaciej W. Rozycki fp->ring_uns_index = 0; 98961414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index = 0; 99061414f5eSMaciej W. Rozycki fp->ring_rmc_txd_index = 0; 99161414f5eSMaciej W. Rozycki fp->ring_hst_rx_index = 0; 99261414f5eSMaciej W. Rozycki fp->ring_smt_tx_index = 0; 99361414f5eSMaciej W. Rozycki fp->ring_smt_rx_index = 0; 99461414f5eSMaciej W. Rozycki if (fp->state > state) { 99561414f5eSMaciej W. Rozycki pr_info("%s: OK\n", fp->name); 99661414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_INIT); 99761414f5eSMaciej W. Rozycki } 99861414f5eSMaciej W. Rozycki break; 99961414f5eSMaciej W. Rozycki 100061414f5eSMaciej W. Rozycki case FZA_STATE_INITIALIZED: 100161414f5eSMaciej W. Rozycki if (fp->state > state) { 100261414f5eSMaciej W. Rozycki fza_set_rx_mode(dev); 100361414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_PARAM); 100461414f5eSMaciej W. Rozycki } 100561414f5eSMaciej W. Rozycki break; 100661414f5eSMaciej W. Rozycki 100761414f5eSMaciej W. Rozycki case FZA_STATE_RUNNING: 100861414f5eSMaciej W. Rozycki case FZA_STATE_MAINTENANCE: 100961414f5eSMaciej W. Rozycki fp->state = state; 101061414f5eSMaciej W. Rozycki fza_rx_init(fp); 101161414f5eSMaciej W. Rozycki fp->queue_active = 1; 101261414f5eSMaciej W. Rozycki netif_wake_queue(dev); 101361414f5eSMaciej W. Rozycki pr_debug("%s: queue woken\n", fp->name); 101461414f5eSMaciej W. Rozycki break; 101561414f5eSMaciej W. Rozycki 101661414f5eSMaciej W. Rozycki case FZA_STATE_HALTED: 101761414f5eSMaciej W. Rozycki fp->queue_active = 0; 101861414f5eSMaciej W. Rozycki netif_stop_queue(dev); 101961414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 102061414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 102161414f5eSMaciej W. Rozycki pr_warn("%s: halted, reason: %x\n", fp->name, 102261414f5eSMaciej W. Rozycki FZA_STATUS_GET_HALT(status)); 102361414f5eSMaciej W. Rozycki fza_regs_dump(fp); 102461414f5eSMaciej W. Rozycki pr_info("%s: resetting the board...\n", fp->name); 102561414f5eSMaciej W. Rozycki fza_do_reset(fp); 102661414f5eSMaciej W. Rozycki fp->timer_state = 0; 102761414f5eSMaciej W. Rozycki fp->reset_timer.expires = jiffies + 45 * HZ; 102861414f5eSMaciej W. Rozycki add_timer(&fp->reset_timer); 102961414f5eSMaciej W. Rozycki break; 103061414f5eSMaciej W. Rozycki 103161414f5eSMaciej W. Rozycki default: 103261414f5eSMaciej W. Rozycki pr_warn("%s: undefined state: %x\n", fp->name, state); 103361414f5eSMaciej W. Rozycki break; 103461414f5eSMaciej W. Rozycki } 103561414f5eSMaciej W. Rozycki 103661414f5eSMaciej W. Rozycki spin_lock(&fp->lock); 103761414f5eSMaciej W. Rozycki fp->state_chg_flag = 1; 103861414f5eSMaciej W. Rozycki wake_up(&fp->state_chg_wait); 103961414f5eSMaciej W. Rozycki spin_unlock(&fp->lock); 104061414f5eSMaciej W. Rozycki } 104161414f5eSMaciej W. Rozycki 104261414f5eSMaciej W. Rozycki return IRQ_HANDLED; 104361414f5eSMaciej W. Rozycki } 104461414f5eSMaciej W. Rozycki 104561414f5eSMaciej W. Rozycki static void fza_reset_timer(struct timer_list *t) 104661414f5eSMaciej W. Rozycki { 104761414f5eSMaciej W. Rozycki struct fza_private *fp = from_timer(fp, t, reset_timer); 104861414f5eSMaciej W. Rozycki 104961414f5eSMaciej W. Rozycki if (!fp->timer_state) { 105061414f5eSMaciej W. Rozycki pr_err("%s: RESET timed out!\n", fp->name); 105161414f5eSMaciej W. Rozycki pr_info("%s: trying harder...\n", fp->name); 105261414f5eSMaciej W. Rozycki 105361414f5eSMaciej W. Rozycki /* Assert the board reset. */ 105461414f5eSMaciej W. Rozycki writew_o(FZA_RESET_INIT, &fp->regs->reset); 105561414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 105661414f5eSMaciej W. Rozycki 105761414f5eSMaciej W. Rozycki fp->timer_state = 1; 105861414f5eSMaciej W. Rozycki fp->reset_timer.expires = jiffies + HZ; 105961414f5eSMaciej W. Rozycki } else { 106061414f5eSMaciej W. Rozycki /* Clear the board reset. */ 106161414f5eSMaciej W. Rozycki writew_u(FZA_RESET_CLR, &fp->regs->reset); 106261414f5eSMaciej W. Rozycki 106361414f5eSMaciej W. Rozycki /* Enable all interrupt events we handle. */ 106461414f5eSMaciej W. Rozycki writew_o(fp->int_mask, &fp->regs->int_mask); 106561414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 106661414f5eSMaciej W. Rozycki 106761414f5eSMaciej W. Rozycki fp->timer_state = 0; 106861414f5eSMaciej W. Rozycki fp->reset_timer.expires = jiffies + 45 * HZ; 106961414f5eSMaciej W. Rozycki } 107061414f5eSMaciej W. Rozycki add_timer(&fp->reset_timer); 107161414f5eSMaciej W. Rozycki } 107261414f5eSMaciej W. Rozycki 107361414f5eSMaciej W. Rozycki static int fza_set_mac_address(struct net_device *dev, void *addr) 107461414f5eSMaciej W. Rozycki { 107561414f5eSMaciej W. Rozycki return -EOPNOTSUPP; 107661414f5eSMaciej W. Rozycki } 107761414f5eSMaciej W. Rozycki 107861414f5eSMaciej W. Rozycki static netdev_tx_t fza_start_xmit(struct sk_buff *skb, struct net_device *dev) 107961414f5eSMaciej W. Rozycki { 108061414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 108161414f5eSMaciej W. Rozycki unsigned int old_mask, new_mask; 108261414f5eSMaciej W. Rozycki int ret; 108361414f5eSMaciej W. Rozycki u8 fc; 108461414f5eSMaciej W. Rozycki 108561414f5eSMaciej W. Rozycki skb_push(skb, 3); /* Make room for PRH. */ 108661414f5eSMaciej W. Rozycki 108761414f5eSMaciej W. Rozycki /* Decode FC to set PRH. */ 108861414f5eSMaciej W. Rozycki fc = skb->data[3]; 108961414f5eSMaciej W. Rozycki skb->data[0] = 0; 109061414f5eSMaciej W. Rozycki skb->data[1] = 0; 109161414f5eSMaciej W. Rozycki skb->data[2] = FZA_PRH2_NORMAL; 109261414f5eSMaciej W. Rozycki if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_SYNC) 109361414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_FRAME_SYNC; 109461414f5eSMaciej W. Rozycki switch (fc & FDDI_FC_K_FORMAT_MASK) { 109561414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_MANAGEMENT: 109661414f5eSMaciej W. Rozycki if ((fc & FDDI_FC_K_CONTROL_MASK) == 0) { 109761414f5eSMaciej W. Rozycki /* Token. */ 109861414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_IMM; 109961414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_TKN_SEND_NONE; 110061414f5eSMaciej W. Rozycki } else { 110161414f5eSMaciej W. Rozycki /* SMT or MAC. */ 110261414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 110361414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_TKN_SEND_UNR; 110461414f5eSMaciej W. Rozycki } 110561414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_CRC_NORMAL; 110661414f5eSMaciej W. Rozycki break; 110761414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_LLC: 110861414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_FUTURE: 110961414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 111061414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_CRC_NORMAL | FZA_PRH1_TKN_SEND_UNR; 111161414f5eSMaciej W. Rozycki break; 111261414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_IMPLEMENTOR: 111361414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 111461414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_TKN_SEND_ORIG; 111561414f5eSMaciej W. Rozycki break; 111661414f5eSMaciej W. Rozycki } 111761414f5eSMaciej W. Rozycki 111861414f5eSMaciej W. Rozycki /* SMT transmit interrupts may sneak frames into the RMC 111961414f5eSMaciej W. Rozycki * transmit ring. We disable them while queueing a frame 112061414f5eSMaciej W. Rozycki * to maintain consistency. 112161414f5eSMaciej W. Rozycki */ 112261414f5eSMaciej W. Rozycki old_mask = fp->int_mask; 112361414f5eSMaciej W. Rozycki new_mask = old_mask & ~FZA_MASK_SMT_TX_POLL; 112461414f5eSMaciej W. Rozycki writew_u(new_mask, &fp->regs->int_mask); 112561414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 112661414f5eSMaciej W. Rozycki fp->int_mask = new_mask; 112761414f5eSMaciej W. Rozycki ret = fza_do_xmit((union fza_buffer_txp) 112861414f5eSMaciej W. Rozycki { .data_ptr = (struct fza_buffer_tx *)skb->data }, 112961414f5eSMaciej W. Rozycki skb->len, dev, 0); 113061414f5eSMaciej W. Rozycki fp->int_mask = old_mask; 113161414f5eSMaciej W. Rozycki writew_u(fp->int_mask, &fp->regs->int_mask); 113261414f5eSMaciej W. Rozycki 113361414f5eSMaciej W. Rozycki if (ret) { 113461414f5eSMaciej W. Rozycki /* Probably an SMT packet filled the remaining space, 113561414f5eSMaciej W. Rozycki * so just stop the queue, but don't report it as an error. 113661414f5eSMaciej W. Rozycki */ 113761414f5eSMaciej W. Rozycki netif_stop_queue(dev); 113861414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 113961414f5eSMaciej W. Rozycki fp->stats.tx_dropped++; 114061414f5eSMaciej W. Rozycki } 114161414f5eSMaciej W. Rozycki 114261414f5eSMaciej W. Rozycki dev_kfree_skb(skb); 114361414f5eSMaciej W. Rozycki 114461414f5eSMaciej W. Rozycki return ret; 114561414f5eSMaciej W. Rozycki } 114661414f5eSMaciej W. Rozycki 114761414f5eSMaciej W. Rozycki static int fza_open(struct net_device *dev) 114861414f5eSMaciej W. Rozycki { 114961414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 115061414f5eSMaciej W. Rozycki struct fza_ring_cmd __iomem *ring; 115161414f5eSMaciej W. Rozycki struct sk_buff *skb; 115261414f5eSMaciej W. Rozycki unsigned long flags; 115361414f5eSMaciej W. Rozycki dma_addr_t dma; 115461414f5eSMaciej W. Rozycki int ret, i; 115561414f5eSMaciej W. Rozycki u32 stat; 115661414f5eSMaciej W. Rozycki long t; 115761414f5eSMaciej W. Rozycki 115861414f5eSMaciej W. Rozycki for (i = 0; i < FZA_RING_RX_SIZE; i++) { 115961414f5eSMaciej W. Rozycki /* We have to 512-byte-align RX buffers... */ 116061414f5eSMaciej W. Rozycki skb = fza_alloc_skb(dev, FZA_RX_BUFFER_SIZE + 511); 116161414f5eSMaciej W. Rozycki if (skb) { 116261414f5eSMaciej W. Rozycki fza_skb_align(skb, 512); 116361414f5eSMaciej W. Rozycki dma = dma_map_single(fp->bdev, skb->data, 116461414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, 116561414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 116661414f5eSMaciej W. Rozycki if (dma_mapping_error(fp->bdev, dma)) { 116761414f5eSMaciej W. Rozycki dev_kfree_skb(skb); 116861414f5eSMaciej W. Rozycki skb = NULL; 116961414f5eSMaciej W. Rozycki } 117061414f5eSMaciej W. Rozycki } 117161414f5eSMaciej W. Rozycki if (!skb) { 117261414f5eSMaciej W. Rozycki for (--i; i >= 0; i--) { 117361414f5eSMaciej W. Rozycki dma_unmap_single(fp->bdev, fp->rx_dma[i], 117461414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, 117561414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 117661414f5eSMaciej W. Rozycki dev_kfree_skb(fp->rx_skbuff[i]); 117761414f5eSMaciej W. Rozycki fp->rx_dma[i] = 0; 117861414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = NULL; 117961414f5eSMaciej W. Rozycki } 118061414f5eSMaciej W. Rozycki return -ENOMEM; 118161414f5eSMaciej W. Rozycki } 118261414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = skb; 118361414f5eSMaciej W. Rozycki fp->rx_dma[i] = dma; 118461414f5eSMaciej W. Rozycki } 118561414f5eSMaciej W. Rozycki 118661414f5eSMaciej W. Rozycki ret = fza_init_send(dev, NULL); 118761414f5eSMaciej W. Rozycki if (ret != 0) 118861414f5eSMaciej W. Rozycki return ret; 118961414f5eSMaciej W. Rozycki 119061414f5eSMaciej W. Rozycki /* Purger and Beacon multicasts need to be supplied before PARAM. */ 119161414f5eSMaciej W. Rozycki fza_set_rx_mode(dev); 119261414f5eSMaciej W. Rozycki 119361414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 119461414f5eSMaciej W. Rozycki fp->cmd_done_flag = 0; 119561414f5eSMaciej W. Rozycki ring = fza_cmd_send(dev, FZA_RING_CMD_PARAM); 119661414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 119761414f5eSMaciej W. Rozycki if (!ring) 119861414f5eSMaciej W. Rozycki return -ENOBUFS; 119961414f5eSMaciej W. Rozycki 120061414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); 120161414f5eSMaciej W. Rozycki if (fp->cmd_done_flag == 0) { 120261414f5eSMaciej W. Rozycki pr_err("%s: PARAM command timed out!, state %x\n", fp->name, 120361414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 120461414f5eSMaciej W. Rozycki return -EIO; 120561414f5eSMaciej W. Rozycki } 120661414f5eSMaciej W. Rozycki stat = readl_u(&ring->stat); 120761414f5eSMaciej W. Rozycki if (stat != FZA_RING_STAT_SUCCESS) { 120861414f5eSMaciej W. Rozycki pr_err("%s: PARAM command failed!, status %02x, state %x\n", 120961414f5eSMaciej W. Rozycki fp->name, stat, 121061414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 121161414f5eSMaciej W. Rozycki return -EIO; 121261414f5eSMaciej W. Rozycki } 121361414f5eSMaciej W. Rozycki pr_debug("%s: PARAM: %lums elapsed\n", fp->name, 121461414f5eSMaciej W. Rozycki (3 * HZ - t) * 1000 / HZ); 121561414f5eSMaciej W. Rozycki 121661414f5eSMaciej W. Rozycki return 0; 121761414f5eSMaciej W. Rozycki } 121861414f5eSMaciej W. Rozycki 121961414f5eSMaciej W. Rozycki static int fza_close(struct net_device *dev) 122061414f5eSMaciej W. Rozycki { 122161414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 122261414f5eSMaciej W. Rozycki unsigned long flags; 122361414f5eSMaciej W. Rozycki uint state; 122461414f5eSMaciej W. Rozycki long t; 122561414f5eSMaciej W. Rozycki int i; 122661414f5eSMaciej W. Rozycki 122761414f5eSMaciej W. Rozycki netif_stop_queue(dev); 122861414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 122961414f5eSMaciej W. Rozycki 123061414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 123161414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 123261414f5eSMaciej W. Rozycki fp->state = FZA_STATE_UNINITIALIZED; 123361414f5eSMaciej W. Rozycki fp->state_chg_flag = 0; 123461414f5eSMaciej W. Rozycki /* Shut the interface down. */ 123561414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_SHUT, &fp->regs->control_a); 123661414f5eSMaciej W. Rozycki readw_o(&fp->regs->control_a); /* Synchronize. */ 123761414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 123861414f5eSMaciej W. Rozycki 123961414f5eSMaciej W. Rozycki /* DEC says SHUT needs up to 10 seconds to complete. */ 124061414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag, 124161414f5eSMaciej W. Rozycki 15 * HZ); 124261414f5eSMaciej W. Rozycki state = FZA_STATUS_GET_STATE(readw_o(&fp->regs->status)); 124361414f5eSMaciej W. Rozycki if (fp->state_chg_flag == 0) { 124461414f5eSMaciej W. Rozycki pr_err("%s: SHUT timed out!, state %x\n", fp->name, state); 124561414f5eSMaciej W. Rozycki return -EIO; 124661414f5eSMaciej W. Rozycki } 124761414f5eSMaciej W. Rozycki if (state != FZA_STATE_UNINITIALIZED) { 124861414f5eSMaciej W. Rozycki pr_err("%s: SHUT failed!, state %x\n", fp->name, state); 124961414f5eSMaciej W. Rozycki return -EIO; 125061414f5eSMaciej W. Rozycki } 125161414f5eSMaciej W. Rozycki pr_debug("%s: SHUT: %lums elapsed\n", fp->name, 125261414f5eSMaciej W. Rozycki (15 * HZ - t) * 1000 / HZ); 125361414f5eSMaciej W. Rozycki 125461414f5eSMaciej W. Rozycki for (i = 0; i < FZA_RING_RX_SIZE; i++) 125561414f5eSMaciej W. Rozycki if (fp->rx_skbuff[i]) { 125661414f5eSMaciej W. Rozycki dma_unmap_single(fp->bdev, fp->rx_dma[i], 125761414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, DMA_FROM_DEVICE); 125861414f5eSMaciej W. Rozycki dev_kfree_skb(fp->rx_skbuff[i]); 125961414f5eSMaciej W. Rozycki fp->rx_dma[i] = 0; 126061414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = NULL; 126161414f5eSMaciej W. Rozycki } 126261414f5eSMaciej W. Rozycki 126361414f5eSMaciej W. Rozycki return 0; 126461414f5eSMaciej W. Rozycki } 126561414f5eSMaciej W. Rozycki 126661414f5eSMaciej W. Rozycki static struct net_device_stats *fza_get_stats(struct net_device *dev) 126761414f5eSMaciej W. Rozycki { 126861414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 126961414f5eSMaciej W. Rozycki 127061414f5eSMaciej W. Rozycki return &fp->stats; 127161414f5eSMaciej W. Rozycki } 127261414f5eSMaciej W. Rozycki 127361414f5eSMaciej W. Rozycki static int fza_probe(struct device *bdev) 127461414f5eSMaciej W. Rozycki { 127561414f5eSMaciej W. Rozycki static const struct net_device_ops netdev_ops = { 127661414f5eSMaciej W. Rozycki .ndo_open = fza_open, 127761414f5eSMaciej W. Rozycki .ndo_stop = fza_close, 127861414f5eSMaciej W. Rozycki .ndo_start_xmit = fza_start_xmit, 127961414f5eSMaciej W. Rozycki .ndo_set_rx_mode = fza_set_rx_mode, 128061414f5eSMaciej W. Rozycki .ndo_set_mac_address = fza_set_mac_address, 128161414f5eSMaciej W. Rozycki .ndo_get_stats = fza_get_stats, 128261414f5eSMaciej W. Rozycki }; 128361414f5eSMaciej W. Rozycki static int version_printed; 128461414f5eSMaciej W. Rozycki char rom_rev[4], fw_rev[4], rmc_rev[4]; 128561414f5eSMaciej W. Rozycki struct tc_dev *tdev = to_tc_dev(bdev); 128661414f5eSMaciej W. Rozycki struct fza_cmd_init __iomem *init; 128761414f5eSMaciej W. Rozycki resource_size_t start, len; 128861414f5eSMaciej W. Rozycki struct net_device *dev; 128961414f5eSMaciej W. Rozycki struct fza_private *fp; 129061414f5eSMaciej W. Rozycki uint smt_ver, pmd_type; 129161414f5eSMaciej W. Rozycki void __iomem *mmio; 129261414f5eSMaciej W. Rozycki uint hw_addr[2]; 129361414f5eSMaciej W. Rozycki int ret, i; 129461414f5eSMaciej W. Rozycki 129561414f5eSMaciej W. Rozycki if (!version_printed) { 129661414f5eSMaciej W. Rozycki pr_info("%s", version); 129761414f5eSMaciej W. Rozycki version_printed = 1; 129861414f5eSMaciej W. Rozycki } 129961414f5eSMaciej W. Rozycki 130061414f5eSMaciej W. Rozycki dev = alloc_fddidev(sizeof(*fp)); 130161414f5eSMaciej W. Rozycki if (!dev) 130261414f5eSMaciej W. Rozycki return -ENOMEM; 130361414f5eSMaciej W. Rozycki SET_NETDEV_DEV(dev, bdev); 130461414f5eSMaciej W. Rozycki 130561414f5eSMaciej W. Rozycki fp = netdev_priv(dev); 130661414f5eSMaciej W. Rozycki dev_set_drvdata(bdev, dev); 130761414f5eSMaciej W. Rozycki 130861414f5eSMaciej W. Rozycki fp->bdev = bdev; 130961414f5eSMaciej W. Rozycki fp->name = dev_name(bdev); 131061414f5eSMaciej W. Rozycki 131161414f5eSMaciej W. Rozycki /* Request the I/O MEM resource. */ 131261414f5eSMaciej W. Rozycki start = tdev->resource.start; 131361414f5eSMaciej W. Rozycki len = tdev->resource.end - start + 1; 131461414f5eSMaciej W. Rozycki if (!request_mem_region(start, len, dev_name(bdev))) { 131561414f5eSMaciej W. Rozycki pr_err("%s: cannot reserve MMIO region\n", fp->name); 131661414f5eSMaciej W. Rozycki ret = -EBUSY; 131761414f5eSMaciej W. Rozycki goto err_out_kfree; 131861414f5eSMaciej W. Rozycki } 131961414f5eSMaciej W. Rozycki 132061414f5eSMaciej W. Rozycki /* MMIO mapping setup. */ 132161414f5eSMaciej W. Rozycki mmio = ioremap_nocache(start, len); 132261414f5eSMaciej W. Rozycki if (!mmio) { 132361414f5eSMaciej W. Rozycki pr_err("%s: cannot map MMIO\n", fp->name); 132461414f5eSMaciej W. Rozycki ret = -ENOMEM; 132561414f5eSMaciej W. Rozycki goto err_out_resource; 132661414f5eSMaciej W. Rozycki } 132761414f5eSMaciej W. Rozycki 132861414f5eSMaciej W. Rozycki /* Initialize the new device structure. */ 132961414f5eSMaciej W. Rozycki switch (loopback) { 133061414f5eSMaciej W. Rozycki case FZA_LOOP_NORMAL: 133161414f5eSMaciej W. Rozycki case FZA_LOOP_INTERN: 133261414f5eSMaciej W. Rozycki case FZA_LOOP_EXTERN: 133361414f5eSMaciej W. Rozycki break; 133461414f5eSMaciej W. Rozycki default: 133561414f5eSMaciej W. Rozycki loopback = FZA_LOOP_NORMAL; 133661414f5eSMaciej W. Rozycki } 133761414f5eSMaciej W. Rozycki 133861414f5eSMaciej W. Rozycki fp->mmio = mmio; 133961414f5eSMaciej W. Rozycki dev->irq = tdev->interrupt; 134061414f5eSMaciej W. Rozycki 134161414f5eSMaciej W. Rozycki pr_info("%s: DEC FDDIcontroller 700 or 700-C at 0x%08llx, irq %d\n", 134261414f5eSMaciej W. Rozycki fp->name, (long long)tdev->resource.start, dev->irq); 134361414f5eSMaciej W. Rozycki pr_debug("%s: mapped at: 0x%p\n", fp->name, mmio); 134461414f5eSMaciej W. Rozycki 134561414f5eSMaciej W. Rozycki fp->regs = mmio + FZA_REG_BASE; 134661414f5eSMaciej W. Rozycki fp->ring_cmd = mmio + FZA_RING_CMD; 134761414f5eSMaciej W. Rozycki fp->ring_uns = mmio + FZA_RING_UNS; 134861414f5eSMaciej W. Rozycki 134961414f5eSMaciej W. Rozycki init_waitqueue_head(&fp->state_chg_wait); 135061414f5eSMaciej W. Rozycki init_waitqueue_head(&fp->cmd_done_wait); 135161414f5eSMaciej W. Rozycki spin_lock_init(&fp->lock); 135261414f5eSMaciej W. Rozycki fp->int_mask = FZA_MASK_NORMAL; 135361414f5eSMaciej W. Rozycki 135461414f5eSMaciej W. Rozycki timer_setup(&fp->reset_timer, fza_reset_timer, 0); 135561414f5eSMaciej W. Rozycki 135661414f5eSMaciej W. Rozycki /* Sanitize the board. */ 135761414f5eSMaciej W. Rozycki fza_regs_dump(fp); 135861414f5eSMaciej W. Rozycki fza_do_shutdown(fp); 135961414f5eSMaciej W. Rozycki 136061414f5eSMaciej W. Rozycki ret = request_irq(dev->irq, fza_interrupt, IRQF_SHARED, fp->name, dev); 136161414f5eSMaciej W. Rozycki if (ret != 0) { 136261414f5eSMaciej W. Rozycki pr_err("%s: unable to get IRQ %d!\n", fp->name, dev->irq); 136361414f5eSMaciej W. Rozycki goto err_out_map; 136461414f5eSMaciej W. Rozycki } 136561414f5eSMaciej W. Rozycki 136661414f5eSMaciej W. Rozycki /* Enable the driver mode. */ 136761414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_B_DRIVER, &fp->regs->control_b); 136861414f5eSMaciej W. Rozycki 136961414f5eSMaciej W. Rozycki /* For some reason transmit done interrupts can trigger during 137061414f5eSMaciej W. Rozycki * reset. This avoids a division error in the handler. 137161414f5eSMaciej W. Rozycki */ 137261414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size = FZA_RING_TX_SIZE; 137361414f5eSMaciej W. Rozycki 137461414f5eSMaciej W. Rozycki ret = fza_reset(fp); 137561414f5eSMaciej W. Rozycki if (ret != 0) 137661414f5eSMaciej W. Rozycki goto err_out_irq; 137761414f5eSMaciej W. Rozycki 137861414f5eSMaciej W. Rozycki ret = fza_init_send(dev, &init); 137961414f5eSMaciej W. Rozycki if (ret != 0) 138061414f5eSMaciej W. Rozycki goto err_out_irq; 138161414f5eSMaciej W. Rozycki 138261414f5eSMaciej W. Rozycki fza_reads(&init->hw_addr, &hw_addr, sizeof(hw_addr)); 138361414f5eSMaciej W. Rozycki memcpy(dev->dev_addr, &hw_addr, FDDI_K_ALEN); 138461414f5eSMaciej W. Rozycki 138561414f5eSMaciej W. Rozycki fza_reads(&init->rom_rev, &rom_rev, sizeof(rom_rev)); 138661414f5eSMaciej W. Rozycki fza_reads(&init->fw_rev, &fw_rev, sizeof(fw_rev)); 138761414f5eSMaciej W. Rozycki fza_reads(&init->rmc_rev, &rmc_rev, sizeof(rmc_rev)); 138861414f5eSMaciej W. Rozycki for (i = 3; i >= 0 && rom_rev[i] == ' '; i--) 138961414f5eSMaciej W. Rozycki rom_rev[i] = 0; 139061414f5eSMaciej W. Rozycki for (i = 3; i >= 0 && fw_rev[i] == ' '; i--) 139161414f5eSMaciej W. Rozycki fw_rev[i] = 0; 139261414f5eSMaciej W. Rozycki for (i = 3; i >= 0 && rmc_rev[i] == ' '; i--) 139361414f5eSMaciej W. Rozycki rmc_rev[i] = 0; 139461414f5eSMaciej W. Rozycki 139561414f5eSMaciej W. Rozycki fp->ring_rmc_tx = mmio + readl_u(&init->rmc_tx); 139661414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size = readl_u(&init->rmc_tx_size); 139761414f5eSMaciej W. Rozycki fp->ring_hst_rx = mmio + readl_u(&init->hst_rx); 139861414f5eSMaciej W. Rozycki fp->ring_hst_rx_size = readl_u(&init->hst_rx_size); 139961414f5eSMaciej W. Rozycki fp->ring_smt_tx = mmio + readl_u(&init->smt_tx); 140061414f5eSMaciej W. Rozycki fp->ring_smt_tx_size = readl_u(&init->smt_tx_size); 140161414f5eSMaciej W. Rozycki fp->ring_smt_rx = mmio + readl_u(&init->smt_rx); 140261414f5eSMaciej W. Rozycki fp->ring_smt_rx_size = readl_u(&init->smt_rx_size); 140361414f5eSMaciej W. Rozycki 140461414f5eSMaciej W. Rozycki fp->buffer_tx = mmio + FZA_TX_BUFFER_ADDR(readl_u(&init->rmc_tx)); 140561414f5eSMaciej W. Rozycki 140661414f5eSMaciej W. Rozycki fp->t_max = readl_u(&init->def_t_max); 140761414f5eSMaciej W. Rozycki fp->t_req = readl_u(&init->def_t_req); 140861414f5eSMaciej W. Rozycki fp->tvx = readl_u(&init->def_tvx); 140961414f5eSMaciej W. Rozycki fp->lem_threshold = readl_u(&init->lem_threshold); 141061414f5eSMaciej W. Rozycki fza_reads(&init->def_station_id, &fp->station_id, 141161414f5eSMaciej W. Rozycki sizeof(fp->station_id)); 141261414f5eSMaciej W. Rozycki fp->rtoken_timeout = readl_u(&init->rtoken_timeout); 141361414f5eSMaciej W. Rozycki fp->ring_purger = readl_u(&init->ring_purger); 141461414f5eSMaciej W. Rozycki 141561414f5eSMaciej W. Rozycki smt_ver = readl_u(&init->smt_ver); 141661414f5eSMaciej W. Rozycki pmd_type = readl_u(&init->pmd_type); 141761414f5eSMaciej W. Rozycki 141861414f5eSMaciej W. Rozycki pr_debug("%s: INIT parameters:\n", fp->name); 141961414f5eSMaciej W. Rozycki pr_debug(" tx_mode: %u\n", readl_u(&init->tx_mode)); 142061414f5eSMaciej W. Rozycki pr_debug(" hst_rx_size: %u\n", readl_u(&init->hst_rx_size)); 142161414f5eSMaciej W. Rozycki pr_debug(" rmc_rev: %.4s\n", rmc_rev); 142261414f5eSMaciej W. Rozycki pr_debug(" rom_rev: %.4s\n", rom_rev); 142361414f5eSMaciej W. Rozycki pr_debug(" fw_rev: %.4s\n", fw_rev); 142461414f5eSMaciej W. Rozycki pr_debug(" mop_type: %u\n", readl_u(&init->mop_type)); 142561414f5eSMaciej W. Rozycki pr_debug(" hst_rx: 0x%08x\n", readl_u(&init->hst_rx)); 142661414f5eSMaciej W. Rozycki pr_debug(" rmc_tx: 0x%08x\n", readl_u(&init->rmc_tx)); 142761414f5eSMaciej W. Rozycki pr_debug(" rmc_tx_size: %u\n", readl_u(&init->rmc_tx_size)); 142861414f5eSMaciej W. Rozycki pr_debug(" smt_tx: 0x%08x\n", readl_u(&init->smt_tx)); 142961414f5eSMaciej W. Rozycki pr_debug(" smt_tx_size: %u\n", readl_u(&init->smt_tx_size)); 143061414f5eSMaciej W. Rozycki pr_debug(" smt_rx: 0x%08x\n", readl_u(&init->smt_rx)); 143161414f5eSMaciej W. Rozycki pr_debug(" smt_rx_size: %u\n", readl_u(&init->smt_rx_size)); 143261414f5eSMaciej W. Rozycki /* TC systems are always LE, so don't bother swapping. */ 143361414f5eSMaciej W. Rozycki pr_debug(" hw_addr: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", 143461414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 0) & 0xff, 143561414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 8) & 0xff, 143661414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 16) & 0xff, 143761414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 24) & 0xff, 143861414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 0) & 0xff, 143961414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 8) & 0xff, 144061414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 16) & 0xff, 144161414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 24) & 0xff); 144261414f5eSMaciej W. Rozycki pr_debug(" def_t_req: %u\n", readl_u(&init->def_t_req)); 144361414f5eSMaciej W. Rozycki pr_debug(" def_tvx: %u\n", readl_u(&init->def_tvx)); 144461414f5eSMaciej W. Rozycki pr_debug(" def_t_max: %u\n", readl_u(&init->def_t_max)); 144561414f5eSMaciej W. Rozycki pr_debug(" lem_threshold: %u\n", readl_u(&init->lem_threshold)); 144661414f5eSMaciej W. Rozycki /* Don't bother swapping, see above. */ 144761414f5eSMaciej W. Rozycki pr_debug(" def_station_id: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", 144861414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 0) & 0xff, 144961414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 8) & 0xff, 145061414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 16) & 0xff, 145161414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 24) & 0xff, 145261414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 0) & 0xff, 145361414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 8) & 0xff, 145461414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 16) & 0xff, 145561414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 24) & 0xff); 145661414f5eSMaciej W. Rozycki pr_debug(" pmd_type_alt: %u\n", readl_u(&init->pmd_type_alt)); 145761414f5eSMaciej W. Rozycki pr_debug(" smt_ver: %u\n", readl_u(&init->smt_ver)); 145861414f5eSMaciej W. Rozycki pr_debug(" rtoken_timeout: %u\n", readl_u(&init->rtoken_timeout)); 145961414f5eSMaciej W. Rozycki pr_debug(" ring_purger: %u\n", readl_u(&init->ring_purger)); 146061414f5eSMaciej W. Rozycki pr_debug(" smt_ver_max: %u\n", readl_u(&init->smt_ver_max)); 146161414f5eSMaciej W. Rozycki pr_debug(" smt_ver_min: %u\n", readl_u(&init->smt_ver_min)); 146261414f5eSMaciej W. Rozycki pr_debug(" pmd_type: %u\n", readl_u(&init->pmd_type)); 146361414f5eSMaciej W. Rozycki 146461414f5eSMaciej W. Rozycki pr_info("%s: model %s, address %pMF\n", 146561414f5eSMaciej W. Rozycki fp->name, 146661414f5eSMaciej W. Rozycki pmd_type == FZA_PMD_TYPE_TW ? 146761414f5eSMaciej W. Rozycki "700-C (DEFZA-CA), ThinWire PMD selected" : 146861414f5eSMaciej W. Rozycki pmd_type == FZA_PMD_TYPE_STP ? 146961414f5eSMaciej W. Rozycki "700-C (DEFZA-CA), STP PMD selected" : 147061414f5eSMaciej W. Rozycki "700 (DEFZA-AA), MMF PMD", 147161414f5eSMaciej W. Rozycki dev->dev_addr); 147261414f5eSMaciej W. Rozycki pr_info("%s: ROM rev. %.4s, firmware rev. %.4s, RMC rev. %.4s, " 147361414f5eSMaciej W. Rozycki "SMT ver. %u\n", fp->name, rom_rev, fw_rev, rmc_rev, smt_ver); 147461414f5eSMaciej W. Rozycki 147561414f5eSMaciej W. Rozycki /* Now that we fetched initial parameters just shut the interface 147661414f5eSMaciej W. Rozycki * until opened. 147761414f5eSMaciej W. Rozycki */ 147861414f5eSMaciej W. Rozycki ret = fza_close(dev); 147961414f5eSMaciej W. Rozycki if (ret != 0) 148061414f5eSMaciej W. Rozycki goto err_out_irq; 148161414f5eSMaciej W. Rozycki 148261414f5eSMaciej W. Rozycki /* The FZA-specific entries in the device structure. */ 148361414f5eSMaciej W. Rozycki dev->netdev_ops = &netdev_ops; 148461414f5eSMaciej W. Rozycki 148561414f5eSMaciej W. Rozycki ret = register_netdev(dev); 148661414f5eSMaciej W. Rozycki if (ret != 0) 148761414f5eSMaciej W. Rozycki goto err_out_irq; 148861414f5eSMaciej W. Rozycki 148961414f5eSMaciej W. Rozycki pr_info("%s: registered as %s\n", fp->name, dev->name); 149061414f5eSMaciej W. Rozycki fp->name = (const char *)dev->name; 149161414f5eSMaciej W. Rozycki 149261414f5eSMaciej W. Rozycki get_device(bdev); 149361414f5eSMaciej W. Rozycki return 0; 149461414f5eSMaciej W. Rozycki 149561414f5eSMaciej W. Rozycki err_out_irq: 149661414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 149761414f5eSMaciej W. Rozycki fza_do_shutdown(fp); 149861414f5eSMaciej W. Rozycki free_irq(dev->irq, dev); 149961414f5eSMaciej W. Rozycki 150061414f5eSMaciej W. Rozycki err_out_map: 150161414f5eSMaciej W. Rozycki iounmap(mmio); 150261414f5eSMaciej W. Rozycki 150361414f5eSMaciej W. Rozycki err_out_resource: 150461414f5eSMaciej W. Rozycki release_mem_region(start, len); 150561414f5eSMaciej W. Rozycki 150661414f5eSMaciej W. Rozycki err_out_kfree: 150761414f5eSMaciej W. Rozycki free_netdev(dev); 150861414f5eSMaciej W. Rozycki 150961414f5eSMaciej W. Rozycki pr_err("%s: initialization failure, aborting!\n", fp->name); 151061414f5eSMaciej W. Rozycki return ret; 151161414f5eSMaciej W. Rozycki } 151261414f5eSMaciej W. Rozycki 151361414f5eSMaciej W. Rozycki static int fza_remove(struct device *bdev) 151461414f5eSMaciej W. Rozycki { 151561414f5eSMaciej W. Rozycki struct net_device *dev = dev_get_drvdata(bdev); 151661414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 151761414f5eSMaciej W. Rozycki struct tc_dev *tdev = to_tc_dev(bdev); 151861414f5eSMaciej W. Rozycki resource_size_t start, len; 151961414f5eSMaciej W. Rozycki 152061414f5eSMaciej W. Rozycki put_device(bdev); 152161414f5eSMaciej W. Rozycki 152261414f5eSMaciej W. Rozycki unregister_netdev(dev); 152361414f5eSMaciej W. Rozycki 152461414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 152561414f5eSMaciej W. Rozycki fza_do_shutdown(fp); 152661414f5eSMaciej W. Rozycki free_irq(dev->irq, dev); 152761414f5eSMaciej W. Rozycki 152861414f5eSMaciej W. Rozycki iounmap(fp->mmio); 152961414f5eSMaciej W. Rozycki 153061414f5eSMaciej W. Rozycki start = tdev->resource.start; 153161414f5eSMaciej W. Rozycki len = tdev->resource.end - start + 1; 153261414f5eSMaciej W. Rozycki release_mem_region(start, len); 153361414f5eSMaciej W. Rozycki 153461414f5eSMaciej W. Rozycki free_netdev(dev); 153561414f5eSMaciej W. Rozycki 153661414f5eSMaciej W. Rozycki return 0; 153761414f5eSMaciej W. Rozycki } 153861414f5eSMaciej W. Rozycki 153961414f5eSMaciej W. Rozycki static struct tc_device_id const fza_tc_table[] = { 154061414f5eSMaciej W. Rozycki { "DEC ", "PMAF-AA " }, 154161414f5eSMaciej W. Rozycki { } 154261414f5eSMaciej W. Rozycki }; 154361414f5eSMaciej W. Rozycki MODULE_DEVICE_TABLE(tc, fza_tc_table); 154461414f5eSMaciej W. Rozycki 154561414f5eSMaciej W. Rozycki static struct tc_driver fza_driver = { 154661414f5eSMaciej W. Rozycki .id_table = fza_tc_table, 154761414f5eSMaciej W. Rozycki .driver = { 154861414f5eSMaciej W. Rozycki .name = "defza", 154961414f5eSMaciej W. Rozycki .bus = &tc_bus_type, 155061414f5eSMaciej W. Rozycki .probe = fza_probe, 155161414f5eSMaciej W. Rozycki .remove = fza_remove, 155261414f5eSMaciej W. Rozycki }, 155361414f5eSMaciej W. Rozycki }; 155461414f5eSMaciej W. Rozycki 155561414f5eSMaciej W. Rozycki static int fza_init(void) 155661414f5eSMaciej W. Rozycki { 155761414f5eSMaciej W. Rozycki return tc_register_driver(&fza_driver); 155861414f5eSMaciej W. Rozycki } 155961414f5eSMaciej W. Rozycki 156061414f5eSMaciej W. Rozycki static void fza_exit(void) 156161414f5eSMaciej W. Rozycki { 156261414f5eSMaciej W. Rozycki tc_unregister_driver(&fza_driver); 156361414f5eSMaciej W. Rozycki } 156461414f5eSMaciej W. Rozycki 156561414f5eSMaciej W. Rozycki module_init(fza_init); 156661414f5eSMaciej W. Rozycki module_exit(fza_exit); 1567