1*61414f5eSMaciej W. Rozycki // SPDX-License-Identifier: GPL-2.0 2*61414f5eSMaciej W. Rozycki /* FDDI network adapter driver for DEC FDDIcontroller 700/700-C devices. 3*61414f5eSMaciej W. Rozycki * 4*61414f5eSMaciej W. Rozycki * Copyright (c) 2018 Maciej W. Rozycki 5*61414f5eSMaciej W. Rozycki * 6*61414f5eSMaciej W. Rozycki * This program is free software; you can redistribute it and/or 7*61414f5eSMaciej W. Rozycki * modify it under the terms of the GNU General Public License 8*61414f5eSMaciej W. Rozycki * as published by the Free Software Foundation; either version 9*61414f5eSMaciej W. Rozycki * 2 of the License, or (at your option) any later version. 10*61414f5eSMaciej W. Rozycki * 11*61414f5eSMaciej W. Rozycki * References: 12*61414f5eSMaciej W. Rozycki * 13*61414f5eSMaciej W. Rozycki * Dave Sawyer & Phil Weeks & Frank Itkowsky, 14*61414f5eSMaciej W. Rozycki * "DEC FDDIcontroller 700 Port Specification", 15*61414f5eSMaciej W. Rozycki * Revision 1.1, Digital Equipment Corporation 16*61414f5eSMaciej W. Rozycki */ 17*61414f5eSMaciej W. Rozycki 18*61414f5eSMaciej W. Rozycki /* ------------------------------------------------------------------------- */ 19*61414f5eSMaciej W. Rozycki /* FZA configurable parameters. */ 20*61414f5eSMaciej W. Rozycki 21*61414f5eSMaciej W. Rozycki /* The number of transmit ring descriptors; either 0 for 512 or 1 for 1024. */ 22*61414f5eSMaciej W. Rozycki #define FZA_RING_TX_MODE 0 23*61414f5eSMaciej W. Rozycki 24*61414f5eSMaciej W. Rozycki /* The number of receive ring descriptors; from 2 up to 256. */ 25*61414f5eSMaciej W. Rozycki #define FZA_RING_RX_SIZE 256 26*61414f5eSMaciej W. Rozycki 27*61414f5eSMaciej W. Rozycki /* End of FZA configurable parameters. No need to change anything below. */ 28*61414f5eSMaciej W. Rozycki /* ------------------------------------------------------------------------- */ 29*61414f5eSMaciej W. Rozycki 30*61414f5eSMaciej W. Rozycki #include <linux/delay.h> 31*61414f5eSMaciej W. Rozycki #include <linux/device.h> 32*61414f5eSMaciej W. Rozycki #include <linux/dma-mapping.h> 33*61414f5eSMaciej W. Rozycki #include <linux/init.h> 34*61414f5eSMaciej W. Rozycki #include <linux/interrupt.h> 35*61414f5eSMaciej W. Rozycki #include <linux/io.h> 36*61414f5eSMaciej W. Rozycki #include <linux/ioport.h> 37*61414f5eSMaciej W. Rozycki #include <linux/kernel.h> 38*61414f5eSMaciej W. Rozycki #include <linux/list.h> 39*61414f5eSMaciej W. Rozycki #include <linux/module.h> 40*61414f5eSMaciej W. Rozycki #include <linux/netdevice.h> 41*61414f5eSMaciej W. Rozycki #include <linux/fddidevice.h> 42*61414f5eSMaciej W. Rozycki #include <linux/sched.h> 43*61414f5eSMaciej W. Rozycki #include <linux/skbuff.h> 44*61414f5eSMaciej W. Rozycki #include <linux/spinlock.h> 45*61414f5eSMaciej W. Rozycki #include <linux/stat.h> 46*61414f5eSMaciej W. Rozycki #include <linux/tc.h> 47*61414f5eSMaciej W. Rozycki #include <linux/timer.h> 48*61414f5eSMaciej W. Rozycki #include <linux/types.h> 49*61414f5eSMaciej W. Rozycki #include <linux/wait.h> 50*61414f5eSMaciej W. Rozycki 51*61414f5eSMaciej W. Rozycki #include <asm/barrier.h> 52*61414f5eSMaciej W. Rozycki 53*61414f5eSMaciej W. Rozycki #include "defza.h" 54*61414f5eSMaciej W. Rozycki 55*61414f5eSMaciej W. Rozycki #define DRV_NAME "defza" 56*61414f5eSMaciej W. Rozycki #define DRV_VERSION "v.1.1.4" 57*61414f5eSMaciej W. Rozycki #define DRV_RELDATE "Oct 6 2018" 58*61414f5eSMaciej W. Rozycki 59*61414f5eSMaciej W. Rozycki static char version[] = 60*61414f5eSMaciej W. Rozycki DRV_NAME ": " DRV_VERSION " " DRV_RELDATE " Maciej W. Rozycki\n"; 61*61414f5eSMaciej W. Rozycki 62*61414f5eSMaciej W. Rozycki MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>"); 63*61414f5eSMaciej W. Rozycki MODULE_DESCRIPTION("DEC FDDIcontroller 700 (DEFZA-xx) driver"); 64*61414f5eSMaciej W. Rozycki MODULE_LICENSE("GPL"); 65*61414f5eSMaciej W. Rozycki 66*61414f5eSMaciej W. Rozycki static int loopback; 67*61414f5eSMaciej W. Rozycki module_param(loopback, int, 0644); 68*61414f5eSMaciej W. Rozycki 69*61414f5eSMaciej W. Rozycki /* Ring Purger Multicast */ 70*61414f5eSMaciej W. Rozycki static u8 hw_addr_purger[8] = { 0x09, 0x00, 0x2b, 0x02, 0x01, 0x05 }; 71*61414f5eSMaciej W. Rozycki /* Directed Beacon Multicast */ 72*61414f5eSMaciej W. Rozycki static u8 hw_addr_beacon[8] = { 0x01, 0x80, 0xc2, 0x00, 0x01, 0x00 }; 73*61414f5eSMaciej W. Rozycki 74*61414f5eSMaciej W. Rozycki /* Shorthands for MMIO accesses that we require to be strongly ordered 75*61414f5eSMaciej W. Rozycki * WRT preceding MMIO accesses. 76*61414f5eSMaciej W. Rozycki */ 77*61414f5eSMaciej W. Rozycki #define readw_o readw_relaxed 78*61414f5eSMaciej W. Rozycki #define readl_o readl_relaxed 79*61414f5eSMaciej W. Rozycki 80*61414f5eSMaciej W. Rozycki #define writew_o writew_relaxed 81*61414f5eSMaciej W. Rozycki #define writel_o writel_relaxed 82*61414f5eSMaciej W. Rozycki 83*61414f5eSMaciej W. Rozycki /* Shorthands for MMIO accesses that we are happy with being weakly ordered 84*61414f5eSMaciej W. Rozycki * WRT preceding MMIO accesses. 85*61414f5eSMaciej W. Rozycki */ 86*61414f5eSMaciej W. Rozycki #define readw_u readw_relaxed 87*61414f5eSMaciej W. Rozycki #define readl_u readl_relaxed 88*61414f5eSMaciej W. Rozycki #define readq_u readq_relaxed 89*61414f5eSMaciej W. Rozycki 90*61414f5eSMaciej W. Rozycki #define writew_u writew_relaxed 91*61414f5eSMaciej W. Rozycki #define writel_u writel_relaxed 92*61414f5eSMaciej W. Rozycki #define writeq_u writeq_relaxed 93*61414f5eSMaciej W. Rozycki 94*61414f5eSMaciej W. Rozycki static inline struct sk_buff *fza_alloc_skb_irq(struct net_device *dev, 95*61414f5eSMaciej W. Rozycki unsigned int length) 96*61414f5eSMaciej W. Rozycki { 97*61414f5eSMaciej W. Rozycki return __netdev_alloc_skb(dev, length, GFP_ATOMIC); 98*61414f5eSMaciej W. Rozycki } 99*61414f5eSMaciej W. Rozycki 100*61414f5eSMaciej W. Rozycki static inline struct sk_buff *fza_alloc_skb(struct net_device *dev, 101*61414f5eSMaciej W. Rozycki unsigned int length) 102*61414f5eSMaciej W. Rozycki { 103*61414f5eSMaciej W. Rozycki return __netdev_alloc_skb(dev, length, GFP_KERNEL); 104*61414f5eSMaciej W. Rozycki } 105*61414f5eSMaciej W. Rozycki 106*61414f5eSMaciej W. Rozycki static inline void fza_skb_align(struct sk_buff *skb, unsigned int v) 107*61414f5eSMaciej W. Rozycki { 108*61414f5eSMaciej W. Rozycki unsigned long x, y; 109*61414f5eSMaciej W. Rozycki 110*61414f5eSMaciej W. Rozycki x = (unsigned long)skb->data; 111*61414f5eSMaciej W. Rozycki y = ALIGN(x, v); 112*61414f5eSMaciej W. Rozycki 113*61414f5eSMaciej W. Rozycki skb_reserve(skb, y - x); 114*61414f5eSMaciej W. Rozycki } 115*61414f5eSMaciej W. Rozycki 116*61414f5eSMaciej W. Rozycki static inline void fza_reads(const void __iomem *from, void *to, 117*61414f5eSMaciej W. Rozycki unsigned long size) 118*61414f5eSMaciej W. Rozycki { 119*61414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 120*61414f5eSMaciej W. Rozycki const u64 __iomem *src = from; 121*61414f5eSMaciej W. Rozycki const u32 __iomem *src_trail; 122*61414f5eSMaciej W. Rozycki u64 *dst = to; 123*61414f5eSMaciej W. Rozycki u32 *dst_trail; 124*61414f5eSMaciej W. Rozycki 125*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 126*61414f5eSMaciej W. Rozycki *dst++ = readq_u(src++); 127*61414f5eSMaciej W. Rozycki if (size) { 128*61414f5eSMaciej W. Rozycki src_trail = (u32 __iomem *)src; 129*61414f5eSMaciej W. Rozycki dst_trail = (u32 *)dst; 130*61414f5eSMaciej W. Rozycki *dst_trail = readl_u(src_trail); 131*61414f5eSMaciej W. Rozycki } 132*61414f5eSMaciej W. Rozycki } else { 133*61414f5eSMaciej W. Rozycki const u32 __iomem *src = from; 134*61414f5eSMaciej W. Rozycki u32 *dst = to; 135*61414f5eSMaciej W. Rozycki 136*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 137*61414f5eSMaciej W. Rozycki *dst++ = readl_u(src++); 138*61414f5eSMaciej W. Rozycki } 139*61414f5eSMaciej W. Rozycki } 140*61414f5eSMaciej W. Rozycki 141*61414f5eSMaciej W. Rozycki static inline void fza_writes(const void *from, void __iomem *to, 142*61414f5eSMaciej W. Rozycki unsigned long size) 143*61414f5eSMaciej W. Rozycki { 144*61414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 145*61414f5eSMaciej W. Rozycki const u64 *src = from; 146*61414f5eSMaciej W. Rozycki const u32 *src_trail; 147*61414f5eSMaciej W. Rozycki u64 __iomem *dst = to; 148*61414f5eSMaciej W. Rozycki u32 __iomem *dst_trail; 149*61414f5eSMaciej W. Rozycki 150*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 151*61414f5eSMaciej W. Rozycki writeq_u(*src++, dst++); 152*61414f5eSMaciej W. Rozycki if (size) { 153*61414f5eSMaciej W. Rozycki src_trail = (u32 *)src; 154*61414f5eSMaciej W. Rozycki dst_trail = (u32 __iomem *)dst; 155*61414f5eSMaciej W. Rozycki writel_u(*src_trail, dst_trail); 156*61414f5eSMaciej W. Rozycki } 157*61414f5eSMaciej W. Rozycki } else { 158*61414f5eSMaciej W. Rozycki const u32 *src = from; 159*61414f5eSMaciej W. Rozycki u32 __iomem *dst = to; 160*61414f5eSMaciej W. Rozycki 161*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 162*61414f5eSMaciej W. Rozycki writel_u(*src++, dst++); 163*61414f5eSMaciej W. Rozycki } 164*61414f5eSMaciej W. Rozycki } 165*61414f5eSMaciej W. Rozycki 166*61414f5eSMaciej W. Rozycki static inline void fza_moves(const void __iomem *from, void __iomem *to, 167*61414f5eSMaciej W. Rozycki unsigned long size) 168*61414f5eSMaciej W. Rozycki { 169*61414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 170*61414f5eSMaciej W. Rozycki const u64 __iomem *src = from; 171*61414f5eSMaciej W. Rozycki const u32 __iomem *src_trail; 172*61414f5eSMaciej W. Rozycki u64 __iomem *dst = to; 173*61414f5eSMaciej W. Rozycki u32 __iomem *dst_trail; 174*61414f5eSMaciej W. Rozycki 175*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 176*61414f5eSMaciej W. Rozycki writeq_u(readq_u(src++), dst++); 177*61414f5eSMaciej W. Rozycki if (size) { 178*61414f5eSMaciej W. Rozycki src_trail = (u32 __iomem *)src; 179*61414f5eSMaciej W. Rozycki dst_trail = (u32 __iomem *)dst; 180*61414f5eSMaciej W. Rozycki writel_u(readl_u(src_trail), dst_trail); 181*61414f5eSMaciej W. Rozycki } 182*61414f5eSMaciej W. Rozycki } else { 183*61414f5eSMaciej W. Rozycki const u32 __iomem *src = from; 184*61414f5eSMaciej W. Rozycki u32 __iomem *dst = to; 185*61414f5eSMaciej W. Rozycki 186*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 187*61414f5eSMaciej W. Rozycki writel_u(readl_u(src++), dst++); 188*61414f5eSMaciej W. Rozycki } 189*61414f5eSMaciej W. Rozycki } 190*61414f5eSMaciej W. Rozycki 191*61414f5eSMaciej W. Rozycki static inline void fza_zeros(void __iomem *to, unsigned long size) 192*61414f5eSMaciej W. Rozycki { 193*61414f5eSMaciej W. Rozycki if (sizeof(unsigned long) == 8) { 194*61414f5eSMaciej W. Rozycki u64 __iomem *dst = to; 195*61414f5eSMaciej W. Rozycki u32 __iomem *dst_trail; 196*61414f5eSMaciej W. Rozycki 197*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size > 1; size -= 2) 198*61414f5eSMaciej W. Rozycki writeq_u(0, dst++); 199*61414f5eSMaciej W. Rozycki if (size) { 200*61414f5eSMaciej W. Rozycki dst_trail = (u32 __iomem *)dst; 201*61414f5eSMaciej W. Rozycki writel_u(0, dst_trail); 202*61414f5eSMaciej W. Rozycki } 203*61414f5eSMaciej W. Rozycki } else { 204*61414f5eSMaciej W. Rozycki u32 __iomem *dst = to; 205*61414f5eSMaciej W. Rozycki 206*61414f5eSMaciej W. Rozycki for (size = (size + 3) / 4; size; size--) 207*61414f5eSMaciej W. Rozycki writel_u(0, dst++); 208*61414f5eSMaciej W. Rozycki } 209*61414f5eSMaciej W. Rozycki } 210*61414f5eSMaciej W. Rozycki 211*61414f5eSMaciej W. Rozycki static inline void fza_regs_dump(struct fza_private *fp) 212*61414f5eSMaciej W. Rozycki { 213*61414f5eSMaciej W. Rozycki pr_debug("%s: iomem registers:\n", fp->name); 214*61414f5eSMaciej W. Rozycki pr_debug(" reset: 0x%04x\n", readw_o(&fp->regs->reset)); 215*61414f5eSMaciej W. Rozycki pr_debug(" interrupt event: 0x%04x\n", readw_u(&fp->regs->int_event)); 216*61414f5eSMaciej W. Rozycki pr_debug(" status: 0x%04x\n", readw_u(&fp->regs->status)); 217*61414f5eSMaciej W. Rozycki pr_debug(" interrupt mask: 0x%04x\n", readw_u(&fp->regs->int_mask)); 218*61414f5eSMaciej W. Rozycki pr_debug(" control A: 0x%04x\n", readw_u(&fp->regs->control_a)); 219*61414f5eSMaciej W. Rozycki pr_debug(" control B: 0x%04x\n", readw_u(&fp->regs->control_b)); 220*61414f5eSMaciej W. Rozycki } 221*61414f5eSMaciej W. Rozycki 222*61414f5eSMaciej W. Rozycki static inline void fza_do_reset(struct fza_private *fp) 223*61414f5eSMaciej W. Rozycki { 224*61414f5eSMaciej W. Rozycki /* Reset the board. */ 225*61414f5eSMaciej W. Rozycki writew_o(FZA_RESET_INIT, &fp->regs->reset); 226*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 227*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Read it back for a small delay. */ 228*61414f5eSMaciej W. Rozycki writew_o(FZA_RESET_CLR, &fp->regs->reset); 229*61414f5eSMaciej W. Rozycki 230*61414f5eSMaciej W. Rozycki /* Enable all interrupt events we handle. */ 231*61414f5eSMaciej W. Rozycki writew_o(fp->int_mask, &fp->regs->int_mask); 232*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 233*61414f5eSMaciej W. Rozycki } 234*61414f5eSMaciej W. Rozycki 235*61414f5eSMaciej W. Rozycki static inline void fza_do_shutdown(struct fza_private *fp) 236*61414f5eSMaciej W. Rozycki { 237*61414f5eSMaciej W. Rozycki /* Disable the driver mode. */ 238*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_B_IDLE, &fp->regs->control_b); 239*61414f5eSMaciej W. Rozycki 240*61414f5eSMaciej W. Rozycki /* And reset the board. */ 241*61414f5eSMaciej W. Rozycki writew_o(FZA_RESET_INIT, &fp->regs->reset); 242*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 243*61414f5eSMaciej W. Rozycki writew_o(FZA_RESET_CLR, &fp->regs->reset); 244*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 245*61414f5eSMaciej W. Rozycki } 246*61414f5eSMaciej W. Rozycki 247*61414f5eSMaciej W. Rozycki static int fza_reset(struct fza_private *fp) 248*61414f5eSMaciej W. Rozycki { 249*61414f5eSMaciej W. Rozycki unsigned long flags; 250*61414f5eSMaciej W. Rozycki uint status, state; 251*61414f5eSMaciej W. Rozycki long t; 252*61414f5eSMaciej W. Rozycki 253*61414f5eSMaciej W. Rozycki pr_info("%s: resetting the board...\n", fp->name); 254*61414f5eSMaciej W. Rozycki 255*61414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 256*61414f5eSMaciej W. Rozycki fp->state_chg_flag = 0; 257*61414f5eSMaciej W. Rozycki fza_do_reset(fp); 258*61414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 259*61414f5eSMaciej W. Rozycki 260*61414f5eSMaciej W. Rozycki /* DEC says RESET needs up to 30 seconds to complete. My DEFZA-AA 261*61414f5eSMaciej W. Rozycki * rev. C03 happily finishes in 9.7 seconds. :-) But we need to 262*61414f5eSMaciej W. Rozycki * be on the safe side... 263*61414f5eSMaciej W. Rozycki */ 264*61414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag, 265*61414f5eSMaciej W. Rozycki 45 * HZ); 266*61414f5eSMaciej W. Rozycki status = readw_u(&fp->regs->status); 267*61414f5eSMaciej W. Rozycki state = FZA_STATUS_GET_STATE(status); 268*61414f5eSMaciej W. Rozycki if (fp->state_chg_flag == 0) { 269*61414f5eSMaciej W. Rozycki pr_err("%s: RESET timed out!, state %x\n", fp->name, state); 270*61414f5eSMaciej W. Rozycki return -EIO; 271*61414f5eSMaciej W. Rozycki } 272*61414f5eSMaciej W. Rozycki if (state != FZA_STATE_UNINITIALIZED) { 273*61414f5eSMaciej W. Rozycki pr_err("%s: RESET failed!, state %x, failure ID %x\n", 274*61414f5eSMaciej W. Rozycki fp->name, state, FZA_STATUS_GET_TEST(status)); 275*61414f5eSMaciej W. Rozycki return -EIO; 276*61414f5eSMaciej W. Rozycki } 277*61414f5eSMaciej W. Rozycki pr_info("%s: OK\n", fp->name); 278*61414f5eSMaciej W. Rozycki pr_debug("%s: RESET: %lums elapsed\n", fp->name, 279*61414f5eSMaciej W. Rozycki (45 * HZ - t) * 1000 / HZ); 280*61414f5eSMaciej W. Rozycki 281*61414f5eSMaciej W. Rozycki return 0; 282*61414f5eSMaciej W. Rozycki } 283*61414f5eSMaciej W. Rozycki 284*61414f5eSMaciej W. Rozycki static struct fza_ring_cmd __iomem *fza_cmd_send(struct net_device *dev, 285*61414f5eSMaciej W. Rozycki int command) 286*61414f5eSMaciej W. Rozycki { 287*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 288*61414f5eSMaciej W. Rozycki struct fza_ring_cmd __iomem *ring = fp->ring_cmd + fp->ring_cmd_index; 289*61414f5eSMaciej W. Rozycki unsigned int old_mask, new_mask; 290*61414f5eSMaciej W. Rozycki union fza_cmd_buf __iomem *buf; 291*61414f5eSMaciej W. Rozycki struct netdev_hw_addr *ha; 292*61414f5eSMaciej W. Rozycki int i; 293*61414f5eSMaciej W. Rozycki 294*61414f5eSMaciej W. Rozycki old_mask = fp->int_mask; 295*61414f5eSMaciej W. Rozycki new_mask = old_mask & ~FZA_MASK_STATE_CHG; 296*61414f5eSMaciej W. Rozycki writew_u(new_mask, &fp->regs->int_mask); 297*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 298*61414f5eSMaciej W. Rozycki fp->int_mask = new_mask; 299*61414f5eSMaciej W. Rozycki 300*61414f5eSMaciej W. Rozycki buf = fp->mmio + readl_u(&ring->buffer); 301*61414f5eSMaciej W. Rozycki 302*61414f5eSMaciej W. Rozycki if ((readl_u(&ring->cmd_own) & FZA_RING_OWN_MASK) != 303*61414f5eSMaciej W. Rozycki FZA_RING_OWN_HOST) { 304*61414f5eSMaciej W. Rozycki pr_warn("%s: command buffer full, command: %u!\n", fp->name, 305*61414f5eSMaciej W. Rozycki command); 306*61414f5eSMaciej W. Rozycki return NULL; 307*61414f5eSMaciej W. Rozycki } 308*61414f5eSMaciej W. Rozycki 309*61414f5eSMaciej W. Rozycki switch (command) { 310*61414f5eSMaciej W. Rozycki case FZA_RING_CMD_INIT: 311*61414f5eSMaciej W. Rozycki writel_u(FZA_RING_TX_MODE, &buf->init.tx_mode); 312*61414f5eSMaciej W. Rozycki writel_u(FZA_RING_RX_SIZE, &buf->init.hst_rx_size); 313*61414f5eSMaciej W. Rozycki fza_zeros(&buf->init.counters, sizeof(buf->init.counters)); 314*61414f5eSMaciej W. Rozycki break; 315*61414f5eSMaciej W. Rozycki 316*61414f5eSMaciej W. Rozycki case FZA_RING_CMD_MODCAM: 317*61414f5eSMaciej W. Rozycki i = 0; 318*61414f5eSMaciej W. Rozycki fza_writes(&hw_addr_purger, &buf->cam.hw_addr[i++], 319*61414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 320*61414f5eSMaciej W. Rozycki fza_writes(&hw_addr_beacon, &buf->cam.hw_addr[i++], 321*61414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 322*61414f5eSMaciej W. Rozycki netdev_for_each_mc_addr(ha, dev) { 323*61414f5eSMaciej W. Rozycki if (i >= FZA_CMD_CAM_SIZE) 324*61414f5eSMaciej W. Rozycki break; 325*61414f5eSMaciej W. Rozycki fza_writes(ha->addr, &buf->cam.hw_addr[i++], 326*61414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 327*61414f5eSMaciej W. Rozycki } 328*61414f5eSMaciej W. Rozycki while (i < FZA_CMD_CAM_SIZE) 329*61414f5eSMaciej W. Rozycki fza_zeros(&buf->cam.hw_addr[i++], 330*61414f5eSMaciej W. Rozycki sizeof(*buf->cam.hw_addr)); 331*61414f5eSMaciej W. Rozycki break; 332*61414f5eSMaciej W. Rozycki 333*61414f5eSMaciej W. Rozycki case FZA_RING_CMD_PARAM: 334*61414f5eSMaciej W. Rozycki writel_u(loopback, &buf->param.loop_mode); 335*61414f5eSMaciej W. Rozycki writel_u(fp->t_max, &buf->param.t_max); 336*61414f5eSMaciej W. Rozycki writel_u(fp->t_req, &buf->param.t_req); 337*61414f5eSMaciej W. Rozycki writel_u(fp->tvx, &buf->param.tvx); 338*61414f5eSMaciej W. Rozycki writel_u(fp->lem_threshold, &buf->param.lem_threshold); 339*61414f5eSMaciej W. Rozycki fza_writes(&fp->station_id, &buf->param.station_id, 340*61414f5eSMaciej W. Rozycki sizeof(buf->param.station_id)); 341*61414f5eSMaciej W. Rozycki /* Convert to milliseconds due to buggy firmware. */ 342*61414f5eSMaciej W. Rozycki writel_u(fp->rtoken_timeout / 12500, 343*61414f5eSMaciej W. Rozycki &buf->param.rtoken_timeout); 344*61414f5eSMaciej W. Rozycki writel_u(fp->ring_purger, &buf->param.ring_purger); 345*61414f5eSMaciej W. Rozycki break; 346*61414f5eSMaciej W. Rozycki 347*61414f5eSMaciej W. Rozycki case FZA_RING_CMD_MODPROM: 348*61414f5eSMaciej W. Rozycki if (dev->flags & IFF_PROMISC) { 349*61414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.llc_prom); 350*61414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.smt_prom); 351*61414f5eSMaciej W. Rozycki } else { 352*61414f5eSMaciej W. Rozycki writel_u(0, &buf->modprom.llc_prom); 353*61414f5eSMaciej W. Rozycki writel_u(0, &buf->modprom.smt_prom); 354*61414f5eSMaciej W. Rozycki } 355*61414f5eSMaciej W. Rozycki if (dev->flags & IFF_ALLMULTI || 356*61414f5eSMaciej W. Rozycki netdev_mc_count(dev) > FZA_CMD_CAM_SIZE - 2) 357*61414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.llc_multi); 358*61414f5eSMaciej W. Rozycki else 359*61414f5eSMaciej W. Rozycki writel_u(0, &buf->modprom.llc_multi); 360*61414f5eSMaciej W. Rozycki writel_u(1, &buf->modprom.llc_bcast); 361*61414f5eSMaciej W. Rozycki break; 362*61414f5eSMaciej W. Rozycki } 363*61414f5eSMaciej W. Rozycki 364*61414f5eSMaciej W. Rozycki /* Trigger the command. */ 365*61414f5eSMaciej W. Rozycki writel_u(FZA_RING_OWN_FZA | command, &ring->cmd_own); 366*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_CMD_POLL, &fp->regs->control_a); 367*61414f5eSMaciej W. Rozycki 368*61414f5eSMaciej W. Rozycki fp->ring_cmd_index = (fp->ring_cmd_index + 1) % FZA_RING_CMD_SIZE; 369*61414f5eSMaciej W. Rozycki 370*61414f5eSMaciej W. Rozycki fp->int_mask = old_mask; 371*61414f5eSMaciej W. Rozycki writew_u(fp->int_mask, &fp->regs->int_mask); 372*61414f5eSMaciej W. Rozycki 373*61414f5eSMaciej W. Rozycki return ring; 374*61414f5eSMaciej W. Rozycki } 375*61414f5eSMaciej W. Rozycki 376*61414f5eSMaciej W. Rozycki static int fza_init_send(struct net_device *dev, 377*61414f5eSMaciej W. Rozycki struct fza_cmd_init *__iomem *init) 378*61414f5eSMaciej W. Rozycki { 379*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 380*61414f5eSMaciej W. Rozycki struct fza_ring_cmd __iomem *ring; 381*61414f5eSMaciej W. Rozycki unsigned long flags; 382*61414f5eSMaciej W. Rozycki u32 stat; 383*61414f5eSMaciej W. Rozycki long t; 384*61414f5eSMaciej W. Rozycki 385*61414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 386*61414f5eSMaciej W. Rozycki fp->cmd_done_flag = 0; 387*61414f5eSMaciej W. Rozycki ring = fza_cmd_send(dev, FZA_RING_CMD_INIT); 388*61414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 389*61414f5eSMaciej W. Rozycki if (!ring) 390*61414f5eSMaciej W. Rozycki /* This should never happen in the uninitialized state, 391*61414f5eSMaciej W. Rozycki * so do not try to recover and just consider it fatal. 392*61414f5eSMaciej W. Rozycki */ 393*61414f5eSMaciej W. Rozycki return -ENOBUFS; 394*61414f5eSMaciej W. Rozycki 395*61414f5eSMaciej W. Rozycki /* INIT may take quite a long time (160ms for my C03). */ 396*61414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); 397*61414f5eSMaciej W. Rozycki if (fp->cmd_done_flag == 0) { 398*61414f5eSMaciej W. Rozycki pr_err("%s: INIT command timed out!, state %x\n", fp->name, 399*61414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 400*61414f5eSMaciej W. Rozycki return -EIO; 401*61414f5eSMaciej W. Rozycki } 402*61414f5eSMaciej W. Rozycki stat = readl_u(&ring->stat); 403*61414f5eSMaciej W. Rozycki if (stat != FZA_RING_STAT_SUCCESS) { 404*61414f5eSMaciej W. Rozycki pr_err("%s: INIT command failed!, status %02x, state %x\n", 405*61414f5eSMaciej W. Rozycki fp->name, stat, 406*61414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 407*61414f5eSMaciej W. Rozycki return -EIO; 408*61414f5eSMaciej W. Rozycki } 409*61414f5eSMaciej W. Rozycki pr_debug("%s: INIT: %lums elapsed\n", fp->name, 410*61414f5eSMaciej W. Rozycki (3 * HZ - t) * 1000 / HZ); 411*61414f5eSMaciej W. Rozycki 412*61414f5eSMaciej W. Rozycki if (init) 413*61414f5eSMaciej W. Rozycki *init = fp->mmio + readl_u(&ring->buffer); 414*61414f5eSMaciej W. Rozycki return 0; 415*61414f5eSMaciej W. Rozycki } 416*61414f5eSMaciej W. Rozycki 417*61414f5eSMaciej W. Rozycki static void fza_rx_init(struct fza_private *fp) 418*61414f5eSMaciej W. Rozycki { 419*61414f5eSMaciej W. Rozycki int i; 420*61414f5eSMaciej W. Rozycki 421*61414f5eSMaciej W. Rozycki /* Fill the host receive descriptor ring. */ 422*61414f5eSMaciej W. Rozycki for (i = 0; i < FZA_RING_RX_SIZE; i++) { 423*61414f5eSMaciej W. Rozycki writel_o(0, &fp->ring_hst_rx[i].rmc); 424*61414f5eSMaciej W. Rozycki writel_o((fp->rx_dma[i] + 0x1000) >> 9, 425*61414f5eSMaciej W. Rozycki &fp->ring_hst_rx[i].buffer1); 426*61414f5eSMaciej W. Rozycki writel_o(fp->rx_dma[i] >> 9 | FZA_RING_OWN_FZA, 427*61414f5eSMaciej W. Rozycki &fp->ring_hst_rx[i].buf0_own); 428*61414f5eSMaciej W. Rozycki } 429*61414f5eSMaciej W. Rozycki } 430*61414f5eSMaciej W. Rozycki 431*61414f5eSMaciej W. Rozycki static void fza_set_rx_mode(struct net_device *dev) 432*61414f5eSMaciej W. Rozycki { 433*61414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_MODCAM); 434*61414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_MODPROM); 435*61414f5eSMaciej W. Rozycki } 436*61414f5eSMaciej W. Rozycki 437*61414f5eSMaciej W. Rozycki union fza_buffer_txp { 438*61414f5eSMaciej W. Rozycki struct fza_buffer_tx *data_ptr; 439*61414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *mmio_ptr; 440*61414f5eSMaciej W. Rozycki }; 441*61414f5eSMaciej W. Rozycki 442*61414f5eSMaciej W. Rozycki static int fza_do_xmit(union fza_buffer_txp ub, int len, 443*61414f5eSMaciej W. Rozycki struct net_device *dev, int smt) 444*61414f5eSMaciej W. Rozycki { 445*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 446*61414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *rmc_tx_ptr; 447*61414f5eSMaciej W. Rozycki int i, first, frag_len, left_len; 448*61414f5eSMaciej W. Rozycki u32 own, rmc; 449*61414f5eSMaciej W. Rozycki 450*61414f5eSMaciej W. Rozycki if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 451*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 452*61414f5eSMaciej W. Rozycki FZA_TX_BUFFER_SIZE) < len) 453*61414f5eSMaciej W. Rozycki return 1; 454*61414f5eSMaciej W. Rozycki 455*61414f5eSMaciej W. Rozycki first = fp->ring_rmc_tx_index; 456*61414f5eSMaciej W. Rozycki 457*61414f5eSMaciej W. Rozycki left_len = len; 458*61414f5eSMaciej W. Rozycki frag_len = FZA_TX_BUFFER_SIZE; 459*61414f5eSMaciej W. Rozycki /* First descriptor is relinquished last. */ 460*61414f5eSMaciej W. Rozycki own = FZA_RING_TX_OWN_HOST; 461*61414f5eSMaciej W. Rozycki /* First descriptor carries frame length; we don't use cut-through. */ 462*61414f5eSMaciej W. Rozycki rmc = FZA_RING_TX_SOP | FZA_RING_TX_VBC | len; 463*61414f5eSMaciej W. Rozycki do { 464*61414f5eSMaciej W. Rozycki i = fp->ring_rmc_tx_index; 465*61414f5eSMaciej W. Rozycki rmc_tx_ptr = &fp->buffer_tx[i]; 466*61414f5eSMaciej W. Rozycki 467*61414f5eSMaciej W. Rozycki if (left_len < FZA_TX_BUFFER_SIZE) 468*61414f5eSMaciej W. Rozycki frag_len = left_len; 469*61414f5eSMaciej W. Rozycki left_len -= frag_len; 470*61414f5eSMaciej W. Rozycki 471*61414f5eSMaciej W. Rozycki /* Length must be a multiple of 4 as only word writes are 472*61414f5eSMaciej W. Rozycki * permitted! 473*61414f5eSMaciej W. Rozycki */ 474*61414f5eSMaciej W. Rozycki frag_len = (frag_len + 3) & ~3; 475*61414f5eSMaciej W. Rozycki if (smt) 476*61414f5eSMaciej W. Rozycki fza_moves(ub.mmio_ptr, rmc_tx_ptr, frag_len); 477*61414f5eSMaciej W. Rozycki else 478*61414f5eSMaciej W. Rozycki fza_writes(ub.data_ptr, rmc_tx_ptr, frag_len); 479*61414f5eSMaciej W. Rozycki 480*61414f5eSMaciej W. Rozycki if (left_len == 0) 481*61414f5eSMaciej W. Rozycki rmc |= FZA_RING_TX_EOP; /* Mark last frag. */ 482*61414f5eSMaciej W. Rozycki 483*61414f5eSMaciej W. Rozycki writel_o(rmc, &fp->ring_rmc_tx[i].rmc); 484*61414f5eSMaciej W. Rozycki writel_o(own, &fp->ring_rmc_tx[i].own); 485*61414f5eSMaciej W. Rozycki 486*61414f5eSMaciej W. Rozycki ub.data_ptr++; 487*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index = (fp->ring_rmc_tx_index + 1) % 488*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size; 489*61414f5eSMaciej W. Rozycki 490*61414f5eSMaciej W. Rozycki /* Settings for intermediate frags. */ 491*61414f5eSMaciej W. Rozycki own = FZA_RING_TX_OWN_RMC; 492*61414f5eSMaciej W. Rozycki rmc = 0; 493*61414f5eSMaciej W. Rozycki } while (left_len > 0); 494*61414f5eSMaciej W. Rozycki 495*61414f5eSMaciej W. Rozycki if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 496*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 497*61414f5eSMaciej W. Rozycki FZA_TX_BUFFER_SIZE) < dev->mtu + dev->hard_header_len) { 498*61414f5eSMaciej W. Rozycki netif_stop_queue(dev); 499*61414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 500*61414f5eSMaciej W. Rozycki } 501*61414f5eSMaciej W. Rozycki 502*61414f5eSMaciej W. Rozycki writel_o(FZA_RING_TX_OWN_RMC, &fp->ring_rmc_tx[first].own); 503*61414f5eSMaciej W. Rozycki 504*61414f5eSMaciej W. Rozycki /* Go, go, go! */ 505*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_TX_POLL, &fp->regs->control_a); 506*61414f5eSMaciej W. Rozycki 507*61414f5eSMaciej W. Rozycki return 0; 508*61414f5eSMaciej W. Rozycki } 509*61414f5eSMaciej W. Rozycki 510*61414f5eSMaciej W. Rozycki static int fza_do_recv_smt(struct fza_buffer_tx *data_ptr, int len, 511*61414f5eSMaciej W. Rozycki u32 rmc, struct net_device *dev) 512*61414f5eSMaciej W. Rozycki { 513*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 514*61414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *smt_rx_ptr; 515*61414f5eSMaciej W. Rozycki u32 own; 516*61414f5eSMaciej W. Rozycki int i; 517*61414f5eSMaciej W. Rozycki 518*61414f5eSMaciej W. Rozycki i = fp->ring_smt_rx_index; 519*61414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_smt_rx[i].own); 520*61414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 521*61414f5eSMaciej W. Rozycki return 1; 522*61414f5eSMaciej W. Rozycki 523*61414f5eSMaciej W. Rozycki smt_rx_ptr = fp->mmio + readl_u(&fp->ring_smt_rx[i].buffer); 524*61414f5eSMaciej W. Rozycki 525*61414f5eSMaciej W. Rozycki /* Length must be a multiple of 4 as only word writes are permitted! */ 526*61414f5eSMaciej W. Rozycki fza_writes(data_ptr, smt_rx_ptr, (len + 3) & ~3); 527*61414f5eSMaciej W. Rozycki 528*61414f5eSMaciej W. Rozycki writel_o(rmc, &fp->ring_smt_rx[i].rmc); 529*61414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_rx[i].own); 530*61414f5eSMaciej W. Rozycki 531*61414f5eSMaciej W. Rozycki fp->ring_smt_rx_index = 532*61414f5eSMaciej W. Rozycki (fp->ring_smt_rx_index + 1) % fp->ring_smt_rx_size; 533*61414f5eSMaciej W. Rozycki 534*61414f5eSMaciej W. Rozycki /* Grab it! */ 535*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_SMT_RX_POLL, &fp->regs->control_a); 536*61414f5eSMaciej W. Rozycki 537*61414f5eSMaciej W. Rozycki return 0; 538*61414f5eSMaciej W. Rozycki } 539*61414f5eSMaciej W. Rozycki 540*61414f5eSMaciej W. Rozycki static void fza_tx(struct net_device *dev) 541*61414f5eSMaciej W. Rozycki { 542*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 543*61414f5eSMaciej W. Rozycki u32 own, rmc; 544*61414f5eSMaciej W. Rozycki int i; 545*61414f5eSMaciej W. Rozycki 546*61414f5eSMaciej W. Rozycki while (1) { 547*61414f5eSMaciej W. Rozycki i = fp->ring_rmc_txd_index; 548*61414f5eSMaciej W. Rozycki if (i == fp->ring_rmc_tx_index) 549*61414f5eSMaciej W. Rozycki break; 550*61414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_rmc_tx[i].own); 551*61414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) 552*61414f5eSMaciej W. Rozycki break; 553*61414f5eSMaciej W. Rozycki 554*61414f5eSMaciej W. Rozycki rmc = readl_u(&fp->ring_rmc_tx[i].rmc); 555*61414f5eSMaciej W. Rozycki /* Only process the first descriptor. */ 556*61414f5eSMaciej W. Rozycki if ((rmc & FZA_RING_TX_SOP) != 0) { 557*61414f5eSMaciej W. Rozycki if ((rmc & FZA_RING_TX_DCC_MASK) == 558*61414f5eSMaciej W. Rozycki FZA_RING_TX_DCC_SUCCESS) { 559*61414f5eSMaciej W. Rozycki int pkt_len = (rmc & FZA_RING_PBC_MASK) - 3; 560*61414f5eSMaciej W. Rozycki /* Omit PRH. */ 561*61414f5eSMaciej W. Rozycki 562*61414f5eSMaciej W. Rozycki fp->stats.tx_packets++; 563*61414f5eSMaciej W. Rozycki fp->stats.tx_bytes += pkt_len; 564*61414f5eSMaciej W. Rozycki } else { 565*61414f5eSMaciej W. Rozycki fp->stats.tx_errors++; 566*61414f5eSMaciej W. Rozycki switch (rmc & FZA_RING_TX_DCC_MASK) { 567*61414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_DTP_SOP: 568*61414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_DTP: 569*61414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_ABORT: 570*61414f5eSMaciej W. Rozycki fp->stats.tx_aborted_errors++; 571*61414f5eSMaciej W. Rozycki break; 572*61414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_UNDRRUN: 573*61414f5eSMaciej W. Rozycki fp->stats.tx_fifo_errors++; 574*61414f5eSMaciej W. Rozycki break; 575*61414f5eSMaciej W. Rozycki case FZA_RING_TX_DCC_PARITY: 576*61414f5eSMaciej W. Rozycki default: 577*61414f5eSMaciej W. Rozycki break; 578*61414f5eSMaciej W. Rozycki } 579*61414f5eSMaciej W. Rozycki } 580*61414f5eSMaciej W. Rozycki } 581*61414f5eSMaciej W. Rozycki 582*61414f5eSMaciej W. Rozycki fp->ring_rmc_txd_index = (fp->ring_rmc_txd_index + 1) % 583*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size; 584*61414f5eSMaciej W. Rozycki } 585*61414f5eSMaciej W. Rozycki 586*61414f5eSMaciej W. Rozycki if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 587*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 588*61414f5eSMaciej W. Rozycki FZA_TX_BUFFER_SIZE) >= dev->mtu + dev->hard_header_len) { 589*61414f5eSMaciej W. Rozycki if (fp->queue_active) { 590*61414f5eSMaciej W. Rozycki netif_wake_queue(dev); 591*61414f5eSMaciej W. Rozycki pr_debug("%s: queue woken\n", fp->name); 592*61414f5eSMaciej W. Rozycki } 593*61414f5eSMaciej W. Rozycki } 594*61414f5eSMaciej W. Rozycki } 595*61414f5eSMaciej W. Rozycki 596*61414f5eSMaciej W. Rozycki static inline int fza_rx_err(struct fza_private *fp, 597*61414f5eSMaciej W. Rozycki const u32 rmc, const u8 fc) 598*61414f5eSMaciej W. Rozycki { 599*61414f5eSMaciej W. Rozycki int len, min_len, max_len; 600*61414f5eSMaciej W. Rozycki 601*61414f5eSMaciej W. Rozycki len = rmc & FZA_RING_PBC_MASK; 602*61414f5eSMaciej W. Rozycki 603*61414f5eSMaciej W. Rozycki if (unlikely((rmc & FZA_RING_RX_BAD) != 0)) { 604*61414f5eSMaciej W. Rozycki fp->stats.rx_errors++; 605*61414f5eSMaciej W. Rozycki 606*61414f5eSMaciej W. Rozycki /* Check special status codes. */ 607*61414f5eSMaciej W. Rozycki if ((rmc & (FZA_RING_RX_CRC | FZA_RING_RX_RRR_MASK | 608*61414f5eSMaciej W. Rozycki FZA_RING_RX_DA_MASK | FZA_RING_RX_SA_MASK)) == 609*61414f5eSMaciej W. Rozycki (FZA_RING_RX_CRC | FZA_RING_RX_RRR_DADDR | 610*61414f5eSMaciej W. Rozycki FZA_RING_RX_DA_CAM | FZA_RING_RX_SA_ALIAS)) { 611*61414f5eSMaciej W. Rozycki if (len >= 8190) 612*61414f5eSMaciej W. Rozycki fp->stats.rx_length_errors++; 613*61414f5eSMaciej W. Rozycki return 1; 614*61414f5eSMaciej W. Rozycki } 615*61414f5eSMaciej W. Rozycki if ((rmc & (FZA_RING_RX_CRC | FZA_RING_RX_RRR_MASK | 616*61414f5eSMaciej W. Rozycki FZA_RING_RX_DA_MASK | FZA_RING_RX_SA_MASK)) == 617*61414f5eSMaciej W. Rozycki (FZA_RING_RX_CRC | FZA_RING_RX_RRR_DADDR | 618*61414f5eSMaciej W. Rozycki FZA_RING_RX_DA_CAM | FZA_RING_RX_SA_CAM)) { 619*61414f5eSMaciej W. Rozycki /* Halt the interface to trigger a reset. */ 620*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_HALT, &fp->regs->control_a); 621*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->control_a); /* Synchronize. */ 622*61414f5eSMaciej W. Rozycki return 1; 623*61414f5eSMaciej W. Rozycki } 624*61414f5eSMaciej W. Rozycki 625*61414f5eSMaciej W. Rozycki /* Check the MAC status. */ 626*61414f5eSMaciej W. Rozycki switch (rmc & FZA_RING_RX_RRR_MASK) { 627*61414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_OK: 628*61414f5eSMaciej W. Rozycki if ((rmc & FZA_RING_RX_CRC) != 0) 629*61414f5eSMaciej W. Rozycki fp->stats.rx_crc_errors++; 630*61414f5eSMaciej W. Rozycki else if ((rmc & FZA_RING_RX_FSC_MASK) == 0 || 631*61414f5eSMaciej W. Rozycki (rmc & FZA_RING_RX_FSB_ERR) != 0) 632*61414f5eSMaciej W. Rozycki fp->stats.rx_frame_errors++; 633*61414f5eSMaciej W. Rozycki return 1; 634*61414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_SADDR: 635*61414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_DADDR: 636*61414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_ABORT: 637*61414f5eSMaciej W. Rozycki /* Halt the interface to trigger a reset. */ 638*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_HALT, &fp->regs->control_a); 639*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->control_a); /* Synchronize. */ 640*61414f5eSMaciej W. Rozycki return 1; 641*61414f5eSMaciej W. Rozycki case FZA_RING_RX_RRR_LENGTH: 642*61414f5eSMaciej W. Rozycki fp->stats.rx_frame_errors++; 643*61414f5eSMaciej W. Rozycki return 1; 644*61414f5eSMaciej W. Rozycki default: 645*61414f5eSMaciej W. Rozycki return 1; 646*61414f5eSMaciej W. Rozycki } 647*61414f5eSMaciej W. Rozycki } 648*61414f5eSMaciej W. Rozycki 649*61414f5eSMaciej W. Rozycki /* Packet received successfully; validate the length. */ 650*61414f5eSMaciej W. Rozycki switch (fc & FDDI_FC_K_FORMAT_MASK) { 651*61414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_MANAGEMENT: 652*61414f5eSMaciej W. Rozycki if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_ASYNC) 653*61414f5eSMaciej W. Rozycki min_len = 37; 654*61414f5eSMaciej W. Rozycki else 655*61414f5eSMaciej W. Rozycki min_len = 17; 656*61414f5eSMaciej W. Rozycki break; 657*61414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_LLC: 658*61414f5eSMaciej W. Rozycki min_len = 20; 659*61414f5eSMaciej W. Rozycki break; 660*61414f5eSMaciej W. Rozycki default: 661*61414f5eSMaciej W. Rozycki min_len = 17; 662*61414f5eSMaciej W. Rozycki break; 663*61414f5eSMaciej W. Rozycki } 664*61414f5eSMaciej W. Rozycki max_len = 4495; 665*61414f5eSMaciej W. Rozycki if (len < min_len || len > max_len) { 666*61414f5eSMaciej W. Rozycki fp->stats.rx_errors++; 667*61414f5eSMaciej W. Rozycki fp->stats.rx_length_errors++; 668*61414f5eSMaciej W. Rozycki return 1; 669*61414f5eSMaciej W. Rozycki } 670*61414f5eSMaciej W. Rozycki 671*61414f5eSMaciej W. Rozycki return 0; 672*61414f5eSMaciej W. Rozycki } 673*61414f5eSMaciej W. Rozycki 674*61414f5eSMaciej W. Rozycki static void fza_rx(struct net_device *dev) 675*61414f5eSMaciej W. Rozycki { 676*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 677*61414f5eSMaciej W. Rozycki struct sk_buff *skb, *newskb; 678*61414f5eSMaciej W. Rozycki struct fza_fddihdr *frame; 679*61414f5eSMaciej W. Rozycki dma_addr_t dma, newdma; 680*61414f5eSMaciej W. Rozycki u32 own, rmc, buf; 681*61414f5eSMaciej W. Rozycki int i, len; 682*61414f5eSMaciej W. Rozycki u8 fc; 683*61414f5eSMaciej W. Rozycki 684*61414f5eSMaciej W. Rozycki while (1) { 685*61414f5eSMaciej W. Rozycki i = fp->ring_hst_rx_index; 686*61414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_hst_rx[i].buf0_own); 687*61414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 688*61414f5eSMaciej W. Rozycki break; 689*61414f5eSMaciej W. Rozycki 690*61414f5eSMaciej W. Rozycki rmc = readl_u(&fp->ring_hst_rx[i].rmc); 691*61414f5eSMaciej W. Rozycki skb = fp->rx_skbuff[i]; 692*61414f5eSMaciej W. Rozycki dma = fp->rx_dma[i]; 693*61414f5eSMaciej W. Rozycki 694*61414f5eSMaciej W. Rozycki /* The RMC doesn't count the preamble and the starting 695*61414f5eSMaciej W. Rozycki * delimiter. We fix it up here for a total of 3 octets. 696*61414f5eSMaciej W. Rozycki */ 697*61414f5eSMaciej W. Rozycki dma_rmb(); 698*61414f5eSMaciej W. Rozycki len = (rmc & FZA_RING_PBC_MASK) + 3; 699*61414f5eSMaciej W. Rozycki frame = (struct fza_fddihdr *)skb->data; 700*61414f5eSMaciej W. Rozycki 701*61414f5eSMaciej W. Rozycki /* We need to get at real FC. */ 702*61414f5eSMaciej W. Rozycki dma_sync_single_for_cpu(fp->bdev, 703*61414f5eSMaciej W. Rozycki dma + 704*61414f5eSMaciej W. Rozycki ((u8 *)&frame->hdr.fc - (u8 *)frame), 705*61414f5eSMaciej W. Rozycki sizeof(frame->hdr.fc), 706*61414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 707*61414f5eSMaciej W. Rozycki fc = frame->hdr.fc; 708*61414f5eSMaciej W. Rozycki 709*61414f5eSMaciej W. Rozycki if (fza_rx_err(fp, rmc, fc)) 710*61414f5eSMaciej W. Rozycki goto err_rx; 711*61414f5eSMaciej W. Rozycki 712*61414f5eSMaciej W. Rozycki /* We have to 512-byte-align RX buffers... */ 713*61414f5eSMaciej W. Rozycki newskb = fza_alloc_skb_irq(dev, FZA_RX_BUFFER_SIZE + 511); 714*61414f5eSMaciej W. Rozycki if (newskb) { 715*61414f5eSMaciej W. Rozycki fza_skb_align(newskb, 512); 716*61414f5eSMaciej W. Rozycki newdma = dma_map_single(fp->bdev, newskb->data, 717*61414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, 718*61414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 719*61414f5eSMaciej W. Rozycki if (dma_mapping_error(fp->bdev, newdma)) { 720*61414f5eSMaciej W. Rozycki dev_kfree_skb_irq(newskb); 721*61414f5eSMaciej W. Rozycki newskb = NULL; 722*61414f5eSMaciej W. Rozycki } 723*61414f5eSMaciej W. Rozycki } 724*61414f5eSMaciej W. Rozycki if (newskb) { 725*61414f5eSMaciej W. Rozycki int pkt_len = len - 7; /* Omit P, SD and FCS. */ 726*61414f5eSMaciej W. Rozycki int is_multi; 727*61414f5eSMaciej W. Rozycki int rx_stat; 728*61414f5eSMaciej W. Rozycki 729*61414f5eSMaciej W. Rozycki dma_unmap_single(fp->bdev, dma, FZA_RX_BUFFER_SIZE, 730*61414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 731*61414f5eSMaciej W. Rozycki 732*61414f5eSMaciej W. Rozycki /* Queue SMT frames to the SMT receive ring. */ 733*61414f5eSMaciej W. Rozycki if ((fc & (FDDI_FC_K_CLASS_MASK | 734*61414f5eSMaciej W. Rozycki FDDI_FC_K_FORMAT_MASK)) == 735*61414f5eSMaciej W. Rozycki (FDDI_FC_K_CLASS_ASYNC | 736*61414f5eSMaciej W. Rozycki FDDI_FC_K_FORMAT_MANAGEMENT) && 737*61414f5eSMaciej W. Rozycki (rmc & FZA_RING_RX_DA_MASK) != 738*61414f5eSMaciej W. Rozycki FZA_RING_RX_DA_PROM) { 739*61414f5eSMaciej W. Rozycki if (fza_do_recv_smt((struct fza_buffer_tx *) 740*61414f5eSMaciej W. Rozycki skb->data, len, rmc, 741*61414f5eSMaciej W. Rozycki dev)) { 742*61414f5eSMaciej W. Rozycki writel_o(FZA_CONTROL_A_SMT_RX_OVFL, 743*61414f5eSMaciej W. Rozycki &fp->regs->control_a); 744*61414f5eSMaciej W. Rozycki } 745*61414f5eSMaciej W. Rozycki } 746*61414f5eSMaciej W. Rozycki 747*61414f5eSMaciej W. Rozycki is_multi = ((frame->hdr.daddr[0] & 0x01) != 0); 748*61414f5eSMaciej W. Rozycki 749*61414f5eSMaciej W. Rozycki skb_reserve(skb, 3); /* Skip over P and SD. */ 750*61414f5eSMaciej W. Rozycki skb_put(skb, pkt_len); /* And cut off FCS. */ 751*61414f5eSMaciej W. Rozycki skb->protocol = fddi_type_trans(skb, dev); 752*61414f5eSMaciej W. Rozycki 753*61414f5eSMaciej W. Rozycki rx_stat = netif_rx(skb); 754*61414f5eSMaciej W. Rozycki if (rx_stat != NET_RX_DROP) { 755*61414f5eSMaciej W. Rozycki fp->stats.rx_packets++; 756*61414f5eSMaciej W. Rozycki fp->stats.rx_bytes += pkt_len; 757*61414f5eSMaciej W. Rozycki if (is_multi) 758*61414f5eSMaciej W. Rozycki fp->stats.multicast++; 759*61414f5eSMaciej W. Rozycki } else { 760*61414f5eSMaciej W. Rozycki fp->stats.rx_dropped++; 761*61414f5eSMaciej W. Rozycki } 762*61414f5eSMaciej W. Rozycki 763*61414f5eSMaciej W. Rozycki skb = newskb; 764*61414f5eSMaciej W. Rozycki dma = newdma; 765*61414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = skb; 766*61414f5eSMaciej W. Rozycki fp->rx_dma[i] = dma; 767*61414f5eSMaciej W. Rozycki } else { 768*61414f5eSMaciej W. Rozycki fp->stats.rx_dropped++; 769*61414f5eSMaciej W. Rozycki pr_notice("%s: memory squeeze, dropping packet\n", 770*61414f5eSMaciej W. Rozycki fp->name); 771*61414f5eSMaciej W. Rozycki } 772*61414f5eSMaciej W. Rozycki 773*61414f5eSMaciej W. Rozycki err_rx: 774*61414f5eSMaciej W. Rozycki writel_o(0, &fp->ring_hst_rx[i].rmc); 775*61414f5eSMaciej W. Rozycki buf = (dma + 0x1000) >> 9; 776*61414f5eSMaciej W. Rozycki writel_o(buf, &fp->ring_hst_rx[i].buffer1); 777*61414f5eSMaciej W. Rozycki buf = dma >> 9 | FZA_RING_OWN_FZA; 778*61414f5eSMaciej W. Rozycki writel_o(buf, &fp->ring_hst_rx[i].buf0_own); 779*61414f5eSMaciej W. Rozycki fp->ring_hst_rx_index = 780*61414f5eSMaciej W. Rozycki (fp->ring_hst_rx_index + 1) % fp->ring_hst_rx_size; 781*61414f5eSMaciej W. Rozycki } 782*61414f5eSMaciej W. Rozycki } 783*61414f5eSMaciej W. Rozycki 784*61414f5eSMaciej W. Rozycki static void fza_tx_smt(struct net_device *dev) 785*61414f5eSMaciej W. Rozycki { 786*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 787*61414f5eSMaciej W. Rozycki struct fza_buffer_tx __iomem *smt_tx_ptr, *skb_data_ptr; 788*61414f5eSMaciej W. Rozycki int i, len; 789*61414f5eSMaciej W. Rozycki u32 own; 790*61414f5eSMaciej W. Rozycki 791*61414f5eSMaciej W. Rozycki while (1) { 792*61414f5eSMaciej W. Rozycki i = fp->ring_smt_tx_index; 793*61414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_smt_tx[i].own); 794*61414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 795*61414f5eSMaciej W. Rozycki break; 796*61414f5eSMaciej W. Rozycki 797*61414f5eSMaciej W. Rozycki smt_tx_ptr = fp->mmio + readl_u(&fp->ring_smt_tx[i].buffer); 798*61414f5eSMaciej W. Rozycki len = readl_u(&fp->ring_smt_tx[i].rmc) & FZA_RING_PBC_MASK; 799*61414f5eSMaciej W. Rozycki 800*61414f5eSMaciej W. Rozycki /* Queue the frame to the RMC transmit ring. */ 801*61414f5eSMaciej W. Rozycki if (!netif_queue_stopped(dev)) 802*61414f5eSMaciej W. Rozycki fza_do_xmit((union fza_buffer_txp) 803*61414f5eSMaciej W. Rozycki { .mmio_ptr = smt_tx_ptr }, 804*61414f5eSMaciej W. Rozycki len, dev, 1); 805*61414f5eSMaciej W. Rozycki 806*61414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own); 807*61414f5eSMaciej W. Rozycki fp->ring_smt_tx_index = 808*61414f5eSMaciej W. Rozycki (fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size; 809*61414f5eSMaciej W. Rozycki } 810*61414f5eSMaciej W. Rozycki } 811*61414f5eSMaciej W. Rozycki 812*61414f5eSMaciej W. Rozycki static void fza_uns(struct net_device *dev) 813*61414f5eSMaciej W. Rozycki { 814*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 815*61414f5eSMaciej W. Rozycki u32 own; 816*61414f5eSMaciej W. Rozycki int i; 817*61414f5eSMaciej W. Rozycki 818*61414f5eSMaciej W. Rozycki while (1) { 819*61414f5eSMaciej W. Rozycki i = fp->ring_uns_index; 820*61414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_uns[i].own); 821*61414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 822*61414f5eSMaciej W. Rozycki break; 823*61414f5eSMaciej W. Rozycki 824*61414f5eSMaciej W. Rozycki if (readl_u(&fp->ring_uns[i].id) == FZA_RING_UNS_RX_OVER) { 825*61414f5eSMaciej W. Rozycki fp->stats.rx_errors++; 826*61414f5eSMaciej W. Rozycki fp->stats.rx_over_errors++; 827*61414f5eSMaciej W. Rozycki } 828*61414f5eSMaciej W. Rozycki 829*61414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_uns[i].own); 830*61414f5eSMaciej W. Rozycki fp->ring_uns_index = 831*61414f5eSMaciej W. Rozycki (fp->ring_uns_index + 1) % FZA_RING_UNS_SIZE; 832*61414f5eSMaciej W. Rozycki } 833*61414f5eSMaciej W. Rozycki } 834*61414f5eSMaciej W. Rozycki 835*61414f5eSMaciej W. Rozycki static void fza_tx_flush(struct net_device *dev) 836*61414f5eSMaciej W. Rozycki { 837*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 838*61414f5eSMaciej W. Rozycki u32 own; 839*61414f5eSMaciej W. Rozycki int i; 840*61414f5eSMaciej W. Rozycki 841*61414f5eSMaciej W. Rozycki /* Clean up the SMT TX ring. */ 842*61414f5eSMaciej W. Rozycki i = fp->ring_smt_tx_index; 843*61414f5eSMaciej W. Rozycki do { 844*61414f5eSMaciej W. Rozycki writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own); 845*61414f5eSMaciej W. Rozycki fp->ring_smt_tx_index = 846*61414f5eSMaciej W. Rozycki (fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size; 847*61414f5eSMaciej W. Rozycki 848*61414f5eSMaciej W. Rozycki } while (i != fp->ring_smt_tx_index); 849*61414f5eSMaciej W. Rozycki 850*61414f5eSMaciej W. Rozycki /* Clean up the RMC TX ring. */ 851*61414f5eSMaciej W. Rozycki i = fp->ring_rmc_tx_index; 852*61414f5eSMaciej W. Rozycki do { 853*61414f5eSMaciej W. Rozycki own = readl_o(&fp->ring_rmc_tx[i].own); 854*61414f5eSMaciej W. Rozycki if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) { 855*61414f5eSMaciej W. Rozycki u32 rmc = readl_u(&fp->ring_rmc_tx[i].rmc); 856*61414f5eSMaciej W. Rozycki 857*61414f5eSMaciej W. Rozycki writel_u(rmc | FZA_RING_TX_DTP, 858*61414f5eSMaciej W. Rozycki &fp->ring_rmc_tx[i].rmc); 859*61414f5eSMaciej W. Rozycki } 860*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index = 861*61414f5eSMaciej W. Rozycki (fp->ring_rmc_tx_index + 1) % fp->ring_rmc_tx_size; 862*61414f5eSMaciej W. Rozycki 863*61414f5eSMaciej W. Rozycki } while (i != fp->ring_rmc_tx_index); 864*61414f5eSMaciej W. Rozycki 865*61414f5eSMaciej W. Rozycki /* Done. */ 866*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_FLUSH_DONE, &fp->regs->control_a); 867*61414f5eSMaciej W. Rozycki } 868*61414f5eSMaciej W. Rozycki 869*61414f5eSMaciej W. Rozycki static irqreturn_t fza_interrupt(int irq, void *dev_id) 870*61414f5eSMaciej W. Rozycki { 871*61414f5eSMaciej W. Rozycki struct net_device *dev = dev_id; 872*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 873*61414f5eSMaciej W. Rozycki uint int_event; 874*61414f5eSMaciej W. Rozycki 875*61414f5eSMaciej W. Rozycki /* Get interrupt events. */ 876*61414f5eSMaciej W. Rozycki int_event = readw_o(&fp->regs->int_event) & fp->int_mask; 877*61414f5eSMaciej W. Rozycki if (int_event == 0) 878*61414f5eSMaciej W. Rozycki return IRQ_NONE; 879*61414f5eSMaciej W. Rozycki 880*61414f5eSMaciej W. Rozycki /* Clear the events. */ 881*61414f5eSMaciej W. Rozycki writew_u(int_event, &fp->regs->int_event); 882*61414f5eSMaciej W. Rozycki 883*61414f5eSMaciej W. Rozycki /* Now handle the events. The order matters. */ 884*61414f5eSMaciej W. Rozycki 885*61414f5eSMaciej W. Rozycki /* Command finished interrupt. */ 886*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_CMD_DONE) != 0) { 887*61414f5eSMaciej W. Rozycki fp->irq_count_cmd_done++; 888*61414f5eSMaciej W. Rozycki 889*61414f5eSMaciej W. Rozycki spin_lock(&fp->lock); 890*61414f5eSMaciej W. Rozycki fp->cmd_done_flag = 1; 891*61414f5eSMaciej W. Rozycki wake_up(&fp->cmd_done_wait); 892*61414f5eSMaciej W. Rozycki spin_unlock(&fp->lock); 893*61414f5eSMaciej W. Rozycki } 894*61414f5eSMaciej W. Rozycki 895*61414f5eSMaciej W. Rozycki /* Transmit finished interrupt. */ 896*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_TX_DONE) != 0) { 897*61414f5eSMaciej W. Rozycki fp->irq_count_tx_done++; 898*61414f5eSMaciej W. Rozycki fza_tx(dev); 899*61414f5eSMaciej W. Rozycki } 900*61414f5eSMaciej W. Rozycki 901*61414f5eSMaciej W. Rozycki /* Host receive interrupt. */ 902*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_RX_POLL) != 0) { 903*61414f5eSMaciej W. Rozycki fp->irq_count_rx_poll++; 904*61414f5eSMaciej W. Rozycki fza_rx(dev); 905*61414f5eSMaciej W. Rozycki } 906*61414f5eSMaciej W. Rozycki 907*61414f5eSMaciej W. Rozycki /* SMT transmit interrupt. */ 908*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_SMT_TX_POLL) != 0) { 909*61414f5eSMaciej W. Rozycki fp->irq_count_smt_tx_poll++; 910*61414f5eSMaciej W. Rozycki fza_tx_smt(dev); 911*61414f5eSMaciej W. Rozycki } 912*61414f5eSMaciej W. Rozycki 913*61414f5eSMaciej W. Rozycki /* Transmit ring flush request. */ 914*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_FLUSH_TX) != 0) { 915*61414f5eSMaciej W. Rozycki fp->irq_count_flush_tx++; 916*61414f5eSMaciej W. Rozycki fza_tx_flush(dev); 917*61414f5eSMaciej W. Rozycki } 918*61414f5eSMaciej W. Rozycki 919*61414f5eSMaciej W. Rozycki /* Link status change interrupt. */ 920*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_LINK_ST_CHG) != 0) { 921*61414f5eSMaciej W. Rozycki uint status; 922*61414f5eSMaciej W. Rozycki 923*61414f5eSMaciej W. Rozycki fp->irq_count_link_st_chg++; 924*61414f5eSMaciej W. Rozycki status = readw_u(&fp->regs->status); 925*61414f5eSMaciej W. Rozycki if (FZA_STATUS_GET_LINK(status) == FZA_LINK_ON) { 926*61414f5eSMaciej W. Rozycki netif_carrier_on(dev); 927*61414f5eSMaciej W. Rozycki pr_info("%s: link available\n", fp->name); 928*61414f5eSMaciej W. Rozycki } else { 929*61414f5eSMaciej W. Rozycki netif_carrier_off(dev); 930*61414f5eSMaciej W. Rozycki pr_info("%s: link unavailable\n", fp->name); 931*61414f5eSMaciej W. Rozycki } 932*61414f5eSMaciej W. Rozycki } 933*61414f5eSMaciej W. Rozycki 934*61414f5eSMaciej W. Rozycki /* Unsolicited event interrupt. */ 935*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_UNS_POLL) != 0) { 936*61414f5eSMaciej W. Rozycki fp->irq_count_uns_poll++; 937*61414f5eSMaciej W. Rozycki fza_uns(dev); 938*61414f5eSMaciej W. Rozycki } 939*61414f5eSMaciej W. Rozycki 940*61414f5eSMaciej W. Rozycki /* State change interrupt. */ 941*61414f5eSMaciej W. Rozycki if ((int_event & FZA_EVENT_STATE_CHG) != 0) { 942*61414f5eSMaciej W. Rozycki uint status, state; 943*61414f5eSMaciej W. Rozycki 944*61414f5eSMaciej W. Rozycki fp->irq_count_state_chg++; 945*61414f5eSMaciej W. Rozycki 946*61414f5eSMaciej W. Rozycki status = readw_u(&fp->regs->status); 947*61414f5eSMaciej W. Rozycki state = FZA_STATUS_GET_STATE(status); 948*61414f5eSMaciej W. Rozycki pr_debug("%s: state change: %x\n", fp->name, state); 949*61414f5eSMaciej W. Rozycki switch (state) { 950*61414f5eSMaciej W. Rozycki case FZA_STATE_RESET: 951*61414f5eSMaciej W. Rozycki break; 952*61414f5eSMaciej W. Rozycki 953*61414f5eSMaciej W. Rozycki case FZA_STATE_UNINITIALIZED: 954*61414f5eSMaciej W. Rozycki netif_carrier_off(dev); 955*61414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 956*61414f5eSMaciej W. Rozycki fp->ring_cmd_index = 0; 957*61414f5eSMaciej W. Rozycki fp->ring_uns_index = 0; 958*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_index = 0; 959*61414f5eSMaciej W. Rozycki fp->ring_rmc_txd_index = 0; 960*61414f5eSMaciej W. Rozycki fp->ring_hst_rx_index = 0; 961*61414f5eSMaciej W. Rozycki fp->ring_smt_tx_index = 0; 962*61414f5eSMaciej W. Rozycki fp->ring_smt_rx_index = 0; 963*61414f5eSMaciej W. Rozycki if (fp->state > state) { 964*61414f5eSMaciej W. Rozycki pr_info("%s: OK\n", fp->name); 965*61414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_INIT); 966*61414f5eSMaciej W. Rozycki } 967*61414f5eSMaciej W. Rozycki break; 968*61414f5eSMaciej W. Rozycki 969*61414f5eSMaciej W. Rozycki case FZA_STATE_INITIALIZED: 970*61414f5eSMaciej W. Rozycki if (fp->state > state) { 971*61414f5eSMaciej W. Rozycki fza_set_rx_mode(dev); 972*61414f5eSMaciej W. Rozycki fza_cmd_send(dev, FZA_RING_CMD_PARAM); 973*61414f5eSMaciej W. Rozycki } 974*61414f5eSMaciej W. Rozycki break; 975*61414f5eSMaciej W. Rozycki 976*61414f5eSMaciej W. Rozycki case FZA_STATE_RUNNING: 977*61414f5eSMaciej W. Rozycki case FZA_STATE_MAINTENANCE: 978*61414f5eSMaciej W. Rozycki fp->state = state; 979*61414f5eSMaciej W. Rozycki fza_rx_init(fp); 980*61414f5eSMaciej W. Rozycki fp->queue_active = 1; 981*61414f5eSMaciej W. Rozycki netif_wake_queue(dev); 982*61414f5eSMaciej W. Rozycki pr_debug("%s: queue woken\n", fp->name); 983*61414f5eSMaciej W. Rozycki break; 984*61414f5eSMaciej W. Rozycki 985*61414f5eSMaciej W. Rozycki case FZA_STATE_HALTED: 986*61414f5eSMaciej W. Rozycki fp->queue_active = 0; 987*61414f5eSMaciej W. Rozycki netif_stop_queue(dev); 988*61414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 989*61414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 990*61414f5eSMaciej W. Rozycki pr_warn("%s: halted, reason: %x\n", fp->name, 991*61414f5eSMaciej W. Rozycki FZA_STATUS_GET_HALT(status)); 992*61414f5eSMaciej W. Rozycki fza_regs_dump(fp); 993*61414f5eSMaciej W. Rozycki pr_info("%s: resetting the board...\n", fp->name); 994*61414f5eSMaciej W. Rozycki fza_do_reset(fp); 995*61414f5eSMaciej W. Rozycki fp->timer_state = 0; 996*61414f5eSMaciej W. Rozycki fp->reset_timer.expires = jiffies + 45 * HZ; 997*61414f5eSMaciej W. Rozycki add_timer(&fp->reset_timer); 998*61414f5eSMaciej W. Rozycki break; 999*61414f5eSMaciej W. Rozycki 1000*61414f5eSMaciej W. Rozycki default: 1001*61414f5eSMaciej W. Rozycki pr_warn("%s: undefined state: %x\n", fp->name, state); 1002*61414f5eSMaciej W. Rozycki break; 1003*61414f5eSMaciej W. Rozycki } 1004*61414f5eSMaciej W. Rozycki 1005*61414f5eSMaciej W. Rozycki spin_lock(&fp->lock); 1006*61414f5eSMaciej W. Rozycki fp->state_chg_flag = 1; 1007*61414f5eSMaciej W. Rozycki wake_up(&fp->state_chg_wait); 1008*61414f5eSMaciej W. Rozycki spin_unlock(&fp->lock); 1009*61414f5eSMaciej W. Rozycki } 1010*61414f5eSMaciej W. Rozycki 1011*61414f5eSMaciej W. Rozycki return IRQ_HANDLED; 1012*61414f5eSMaciej W. Rozycki } 1013*61414f5eSMaciej W. Rozycki 1014*61414f5eSMaciej W. Rozycki static void fza_reset_timer(struct timer_list *t) 1015*61414f5eSMaciej W. Rozycki { 1016*61414f5eSMaciej W. Rozycki struct fza_private *fp = from_timer(fp, t, reset_timer); 1017*61414f5eSMaciej W. Rozycki 1018*61414f5eSMaciej W. Rozycki if (!fp->timer_state) { 1019*61414f5eSMaciej W. Rozycki pr_err("%s: RESET timed out!\n", fp->name); 1020*61414f5eSMaciej W. Rozycki pr_info("%s: trying harder...\n", fp->name); 1021*61414f5eSMaciej W. Rozycki 1022*61414f5eSMaciej W. Rozycki /* Assert the board reset. */ 1023*61414f5eSMaciej W. Rozycki writew_o(FZA_RESET_INIT, &fp->regs->reset); 1024*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->reset); /* Synchronize. */ 1025*61414f5eSMaciej W. Rozycki 1026*61414f5eSMaciej W. Rozycki fp->timer_state = 1; 1027*61414f5eSMaciej W. Rozycki fp->reset_timer.expires = jiffies + HZ; 1028*61414f5eSMaciej W. Rozycki } else { 1029*61414f5eSMaciej W. Rozycki /* Clear the board reset. */ 1030*61414f5eSMaciej W. Rozycki writew_u(FZA_RESET_CLR, &fp->regs->reset); 1031*61414f5eSMaciej W. Rozycki 1032*61414f5eSMaciej W. Rozycki /* Enable all interrupt events we handle. */ 1033*61414f5eSMaciej W. Rozycki writew_o(fp->int_mask, &fp->regs->int_mask); 1034*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 1035*61414f5eSMaciej W. Rozycki 1036*61414f5eSMaciej W. Rozycki fp->timer_state = 0; 1037*61414f5eSMaciej W. Rozycki fp->reset_timer.expires = jiffies + 45 * HZ; 1038*61414f5eSMaciej W. Rozycki } 1039*61414f5eSMaciej W. Rozycki add_timer(&fp->reset_timer); 1040*61414f5eSMaciej W. Rozycki } 1041*61414f5eSMaciej W. Rozycki 1042*61414f5eSMaciej W. Rozycki static int fza_set_mac_address(struct net_device *dev, void *addr) 1043*61414f5eSMaciej W. Rozycki { 1044*61414f5eSMaciej W. Rozycki return -EOPNOTSUPP; 1045*61414f5eSMaciej W. Rozycki } 1046*61414f5eSMaciej W. Rozycki 1047*61414f5eSMaciej W. Rozycki static netdev_tx_t fza_start_xmit(struct sk_buff *skb, struct net_device *dev) 1048*61414f5eSMaciej W. Rozycki { 1049*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 1050*61414f5eSMaciej W. Rozycki unsigned int old_mask, new_mask; 1051*61414f5eSMaciej W. Rozycki int ret; 1052*61414f5eSMaciej W. Rozycki u8 fc; 1053*61414f5eSMaciej W. Rozycki 1054*61414f5eSMaciej W. Rozycki skb_push(skb, 3); /* Make room for PRH. */ 1055*61414f5eSMaciej W. Rozycki 1056*61414f5eSMaciej W. Rozycki /* Decode FC to set PRH. */ 1057*61414f5eSMaciej W. Rozycki fc = skb->data[3]; 1058*61414f5eSMaciej W. Rozycki skb->data[0] = 0; 1059*61414f5eSMaciej W. Rozycki skb->data[1] = 0; 1060*61414f5eSMaciej W. Rozycki skb->data[2] = FZA_PRH2_NORMAL; 1061*61414f5eSMaciej W. Rozycki if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_SYNC) 1062*61414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_FRAME_SYNC; 1063*61414f5eSMaciej W. Rozycki switch (fc & FDDI_FC_K_FORMAT_MASK) { 1064*61414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_MANAGEMENT: 1065*61414f5eSMaciej W. Rozycki if ((fc & FDDI_FC_K_CONTROL_MASK) == 0) { 1066*61414f5eSMaciej W. Rozycki /* Token. */ 1067*61414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_IMM; 1068*61414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_TKN_SEND_NONE; 1069*61414f5eSMaciej W. Rozycki } else { 1070*61414f5eSMaciej W. Rozycki /* SMT or MAC. */ 1071*61414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 1072*61414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_TKN_SEND_UNR; 1073*61414f5eSMaciej W. Rozycki } 1074*61414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_CRC_NORMAL; 1075*61414f5eSMaciej W. Rozycki break; 1076*61414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_LLC: 1077*61414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_FUTURE: 1078*61414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 1079*61414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_CRC_NORMAL | FZA_PRH1_TKN_SEND_UNR; 1080*61414f5eSMaciej W. Rozycki break; 1081*61414f5eSMaciej W. Rozycki case FDDI_FC_K_FORMAT_IMPLEMENTOR: 1082*61414f5eSMaciej W. Rozycki skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 1083*61414f5eSMaciej W. Rozycki skb->data[1] |= FZA_PRH1_TKN_SEND_ORIG; 1084*61414f5eSMaciej W. Rozycki break; 1085*61414f5eSMaciej W. Rozycki } 1086*61414f5eSMaciej W. Rozycki 1087*61414f5eSMaciej W. Rozycki /* SMT transmit interrupts may sneak frames into the RMC 1088*61414f5eSMaciej W. Rozycki * transmit ring. We disable them while queueing a frame 1089*61414f5eSMaciej W. Rozycki * to maintain consistency. 1090*61414f5eSMaciej W. Rozycki */ 1091*61414f5eSMaciej W. Rozycki old_mask = fp->int_mask; 1092*61414f5eSMaciej W. Rozycki new_mask = old_mask & ~FZA_MASK_SMT_TX_POLL; 1093*61414f5eSMaciej W. Rozycki writew_u(new_mask, &fp->regs->int_mask); 1094*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->int_mask); /* Synchronize. */ 1095*61414f5eSMaciej W. Rozycki fp->int_mask = new_mask; 1096*61414f5eSMaciej W. Rozycki ret = fza_do_xmit((union fza_buffer_txp) 1097*61414f5eSMaciej W. Rozycki { .data_ptr = (struct fza_buffer_tx *)skb->data }, 1098*61414f5eSMaciej W. Rozycki skb->len, dev, 0); 1099*61414f5eSMaciej W. Rozycki fp->int_mask = old_mask; 1100*61414f5eSMaciej W. Rozycki writew_u(fp->int_mask, &fp->regs->int_mask); 1101*61414f5eSMaciej W. Rozycki 1102*61414f5eSMaciej W. Rozycki if (ret) { 1103*61414f5eSMaciej W. Rozycki /* Probably an SMT packet filled the remaining space, 1104*61414f5eSMaciej W. Rozycki * so just stop the queue, but don't report it as an error. 1105*61414f5eSMaciej W. Rozycki */ 1106*61414f5eSMaciej W. Rozycki netif_stop_queue(dev); 1107*61414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 1108*61414f5eSMaciej W. Rozycki fp->stats.tx_dropped++; 1109*61414f5eSMaciej W. Rozycki } 1110*61414f5eSMaciej W. Rozycki 1111*61414f5eSMaciej W. Rozycki dev_kfree_skb(skb); 1112*61414f5eSMaciej W. Rozycki 1113*61414f5eSMaciej W. Rozycki return ret; 1114*61414f5eSMaciej W. Rozycki } 1115*61414f5eSMaciej W. Rozycki 1116*61414f5eSMaciej W. Rozycki static int fza_open(struct net_device *dev) 1117*61414f5eSMaciej W. Rozycki { 1118*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 1119*61414f5eSMaciej W. Rozycki struct fza_ring_cmd __iomem *ring; 1120*61414f5eSMaciej W. Rozycki struct sk_buff *skb; 1121*61414f5eSMaciej W. Rozycki unsigned long flags; 1122*61414f5eSMaciej W. Rozycki dma_addr_t dma; 1123*61414f5eSMaciej W. Rozycki int ret, i; 1124*61414f5eSMaciej W. Rozycki u32 stat; 1125*61414f5eSMaciej W. Rozycki long t; 1126*61414f5eSMaciej W. Rozycki 1127*61414f5eSMaciej W. Rozycki for (i = 0; i < FZA_RING_RX_SIZE; i++) { 1128*61414f5eSMaciej W. Rozycki /* We have to 512-byte-align RX buffers... */ 1129*61414f5eSMaciej W. Rozycki skb = fza_alloc_skb(dev, FZA_RX_BUFFER_SIZE + 511); 1130*61414f5eSMaciej W. Rozycki if (skb) { 1131*61414f5eSMaciej W. Rozycki fza_skb_align(skb, 512); 1132*61414f5eSMaciej W. Rozycki dma = dma_map_single(fp->bdev, skb->data, 1133*61414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, 1134*61414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 1135*61414f5eSMaciej W. Rozycki if (dma_mapping_error(fp->bdev, dma)) { 1136*61414f5eSMaciej W. Rozycki dev_kfree_skb(skb); 1137*61414f5eSMaciej W. Rozycki skb = NULL; 1138*61414f5eSMaciej W. Rozycki } 1139*61414f5eSMaciej W. Rozycki } 1140*61414f5eSMaciej W. Rozycki if (!skb) { 1141*61414f5eSMaciej W. Rozycki for (--i; i >= 0; i--) { 1142*61414f5eSMaciej W. Rozycki dma_unmap_single(fp->bdev, fp->rx_dma[i], 1143*61414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, 1144*61414f5eSMaciej W. Rozycki DMA_FROM_DEVICE); 1145*61414f5eSMaciej W. Rozycki dev_kfree_skb(fp->rx_skbuff[i]); 1146*61414f5eSMaciej W. Rozycki fp->rx_dma[i] = 0; 1147*61414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = NULL; 1148*61414f5eSMaciej W. Rozycki } 1149*61414f5eSMaciej W. Rozycki return -ENOMEM; 1150*61414f5eSMaciej W. Rozycki } 1151*61414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = skb; 1152*61414f5eSMaciej W. Rozycki fp->rx_dma[i] = dma; 1153*61414f5eSMaciej W. Rozycki } 1154*61414f5eSMaciej W. Rozycki 1155*61414f5eSMaciej W. Rozycki ret = fza_init_send(dev, NULL); 1156*61414f5eSMaciej W. Rozycki if (ret != 0) 1157*61414f5eSMaciej W. Rozycki return ret; 1158*61414f5eSMaciej W. Rozycki 1159*61414f5eSMaciej W. Rozycki /* Purger and Beacon multicasts need to be supplied before PARAM. */ 1160*61414f5eSMaciej W. Rozycki fza_set_rx_mode(dev); 1161*61414f5eSMaciej W. Rozycki 1162*61414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 1163*61414f5eSMaciej W. Rozycki fp->cmd_done_flag = 0; 1164*61414f5eSMaciej W. Rozycki ring = fza_cmd_send(dev, FZA_RING_CMD_PARAM); 1165*61414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 1166*61414f5eSMaciej W. Rozycki if (!ring) 1167*61414f5eSMaciej W. Rozycki return -ENOBUFS; 1168*61414f5eSMaciej W. Rozycki 1169*61414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); 1170*61414f5eSMaciej W. Rozycki if (fp->cmd_done_flag == 0) { 1171*61414f5eSMaciej W. Rozycki pr_err("%s: PARAM command timed out!, state %x\n", fp->name, 1172*61414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 1173*61414f5eSMaciej W. Rozycki return -EIO; 1174*61414f5eSMaciej W. Rozycki } 1175*61414f5eSMaciej W. Rozycki stat = readl_u(&ring->stat); 1176*61414f5eSMaciej W. Rozycki if (stat != FZA_RING_STAT_SUCCESS) { 1177*61414f5eSMaciej W. Rozycki pr_err("%s: PARAM command failed!, status %02x, state %x\n", 1178*61414f5eSMaciej W. Rozycki fp->name, stat, 1179*61414f5eSMaciej W. Rozycki FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 1180*61414f5eSMaciej W. Rozycki return -EIO; 1181*61414f5eSMaciej W. Rozycki } 1182*61414f5eSMaciej W. Rozycki pr_debug("%s: PARAM: %lums elapsed\n", fp->name, 1183*61414f5eSMaciej W. Rozycki (3 * HZ - t) * 1000 / HZ); 1184*61414f5eSMaciej W. Rozycki 1185*61414f5eSMaciej W. Rozycki return 0; 1186*61414f5eSMaciej W. Rozycki } 1187*61414f5eSMaciej W. Rozycki 1188*61414f5eSMaciej W. Rozycki static int fza_close(struct net_device *dev) 1189*61414f5eSMaciej W. Rozycki { 1190*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 1191*61414f5eSMaciej W. Rozycki unsigned long flags; 1192*61414f5eSMaciej W. Rozycki uint state; 1193*61414f5eSMaciej W. Rozycki long t; 1194*61414f5eSMaciej W. Rozycki int i; 1195*61414f5eSMaciej W. Rozycki 1196*61414f5eSMaciej W. Rozycki netif_stop_queue(dev); 1197*61414f5eSMaciej W. Rozycki pr_debug("%s: queue stopped\n", fp->name); 1198*61414f5eSMaciej W. Rozycki 1199*61414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 1200*61414f5eSMaciej W. Rozycki spin_lock_irqsave(&fp->lock, flags); 1201*61414f5eSMaciej W. Rozycki fp->state = FZA_STATE_UNINITIALIZED; 1202*61414f5eSMaciej W. Rozycki fp->state_chg_flag = 0; 1203*61414f5eSMaciej W. Rozycki /* Shut the interface down. */ 1204*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_A_SHUT, &fp->regs->control_a); 1205*61414f5eSMaciej W. Rozycki readw_o(&fp->regs->control_a); /* Synchronize. */ 1206*61414f5eSMaciej W. Rozycki spin_unlock_irqrestore(&fp->lock, flags); 1207*61414f5eSMaciej W. Rozycki 1208*61414f5eSMaciej W. Rozycki /* DEC says SHUT needs up to 10 seconds to complete. */ 1209*61414f5eSMaciej W. Rozycki t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag, 1210*61414f5eSMaciej W. Rozycki 15 * HZ); 1211*61414f5eSMaciej W. Rozycki state = FZA_STATUS_GET_STATE(readw_o(&fp->regs->status)); 1212*61414f5eSMaciej W. Rozycki if (fp->state_chg_flag == 0) { 1213*61414f5eSMaciej W. Rozycki pr_err("%s: SHUT timed out!, state %x\n", fp->name, state); 1214*61414f5eSMaciej W. Rozycki return -EIO; 1215*61414f5eSMaciej W. Rozycki } 1216*61414f5eSMaciej W. Rozycki if (state != FZA_STATE_UNINITIALIZED) { 1217*61414f5eSMaciej W. Rozycki pr_err("%s: SHUT failed!, state %x\n", fp->name, state); 1218*61414f5eSMaciej W. Rozycki return -EIO; 1219*61414f5eSMaciej W. Rozycki } 1220*61414f5eSMaciej W. Rozycki pr_debug("%s: SHUT: %lums elapsed\n", fp->name, 1221*61414f5eSMaciej W. Rozycki (15 * HZ - t) * 1000 / HZ); 1222*61414f5eSMaciej W. Rozycki 1223*61414f5eSMaciej W. Rozycki for (i = 0; i < FZA_RING_RX_SIZE; i++) 1224*61414f5eSMaciej W. Rozycki if (fp->rx_skbuff[i]) { 1225*61414f5eSMaciej W. Rozycki dma_unmap_single(fp->bdev, fp->rx_dma[i], 1226*61414f5eSMaciej W. Rozycki FZA_RX_BUFFER_SIZE, DMA_FROM_DEVICE); 1227*61414f5eSMaciej W. Rozycki dev_kfree_skb(fp->rx_skbuff[i]); 1228*61414f5eSMaciej W. Rozycki fp->rx_dma[i] = 0; 1229*61414f5eSMaciej W. Rozycki fp->rx_skbuff[i] = NULL; 1230*61414f5eSMaciej W. Rozycki } 1231*61414f5eSMaciej W. Rozycki 1232*61414f5eSMaciej W. Rozycki return 0; 1233*61414f5eSMaciej W. Rozycki } 1234*61414f5eSMaciej W. Rozycki 1235*61414f5eSMaciej W. Rozycki static struct net_device_stats *fza_get_stats(struct net_device *dev) 1236*61414f5eSMaciej W. Rozycki { 1237*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 1238*61414f5eSMaciej W. Rozycki 1239*61414f5eSMaciej W. Rozycki return &fp->stats; 1240*61414f5eSMaciej W. Rozycki } 1241*61414f5eSMaciej W. Rozycki 1242*61414f5eSMaciej W. Rozycki static int fza_probe(struct device *bdev) 1243*61414f5eSMaciej W. Rozycki { 1244*61414f5eSMaciej W. Rozycki static const struct net_device_ops netdev_ops = { 1245*61414f5eSMaciej W. Rozycki .ndo_open = fza_open, 1246*61414f5eSMaciej W. Rozycki .ndo_stop = fza_close, 1247*61414f5eSMaciej W. Rozycki .ndo_start_xmit = fza_start_xmit, 1248*61414f5eSMaciej W. Rozycki .ndo_set_rx_mode = fza_set_rx_mode, 1249*61414f5eSMaciej W. Rozycki .ndo_set_mac_address = fza_set_mac_address, 1250*61414f5eSMaciej W. Rozycki .ndo_get_stats = fza_get_stats, 1251*61414f5eSMaciej W. Rozycki }; 1252*61414f5eSMaciej W. Rozycki static int version_printed; 1253*61414f5eSMaciej W. Rozycki char rom_rev[4], fw_rev[4], rmc_rev[4]; 1254*61414f5eSMaciej W. Rozycki struct tc_dev *tdev = to_tc_dev(bdev); 1255*61414f5eSMaciej W. Rozycki struct fza_cmd_init __iomem *init; 1256*61414f5eSMaciej W. Rozycki resource_size_t start, len; 1257*61414f5eSMaciej W. Rozycki struct net_device *dev; 1258*61414f5eSMaciej W. Rozycki struct fza_private *fp; 1259*61414f5eSMaciej W. Rozycki uint smt_ver, pmd_type; 1260*61414f5eSMaciej W. Rozycki void __iomem *mmio; 1261*61414f5eSMaciej W. Rozycki uint hw_addr[2]; 1262*61414f5eSMaciej W. Rozycki int ret, i; 1263*61414f5eSMaciej W. Rozycki 1264*61414f5eSMaciej W. Rozycki if (!version_printed) { 1265*61414f5eSMaciej W. Rozycki pr_info("%s", version); 1266*61414f5eSMaciej W. Rozycki version_printed = 1; 1267*61414f5eSMaciej W. Rozycki } 1268*61414f5eSMaciej W. Rozycki 1269*61414f5eSMaciej W. Rozycki dev = alloc_fddidev(sizeof(*fp)); 1270*61414f5eSMaciej W. Rozycki if (!dev) 1271*61414f5eSMaciej W. Rozycki return -ENOMEM; 1272*61414f5eSMaciej W. Rozycki SET_NETDEV_DEV(dev, bdev); 1273*61414f5eSMaciej W. Rozycki 1274*61414f5eSMaciej W. Rozycki fp = netdev_priv(dev); 1275*61414f5eSMaciej W. Rozycki dev_set_drvdata(bdev, dev); 1276*61414f5eSMaciej W. Rozycki 1277*61414f5eSMaciej W. Rozycki fp->bdev = bdev; 1278*61414f5eSMaciej W. Rozycki fp->name = dev_name(bdev); 1279*61414f5eSMaciej W. Rozycki 1280*61414f5eSMaciej W. Rozycki /* Request the I/O MEM resource. */ 1281*61414f5eSMaciej W. Rozycki start = tdev->resource.start; 1282*61414f5eSMaciej W. Rozycki len = tdev->resource.end - start + 1; 1283*61414f5eSMaciej W. Rozycki if (!request_mem_region(start, len, dev_name(bdev))) { 1284*61414f5eSMaciej W. Rozycki pr_err("%s: cannot reserve MMIO region\n", fp->name); 1285*61414f5eSMaciej W. Rozycki ret = -EBUSY; 1286*61414f5eSMaciej W. Rozycki goto err_out_kfree; 1287*61414f5eSMaciej W. Rozycki } 1288*61414f5eSMaciej W. Rozycki 1289*61414f5eSMaciej W. Rozycki /* MMIO mapping setup. */ 1290*61414f5eSMaciej W. Rozycki mmio = ioremap_nocache(start, len); 1291*61414f5eSMaciej W. Rozycki if (!mmio) { 1292*61414f5eSMaciej W. Rozycki pr_err("%s: cannot map MMIO\n", fp->name); 1293*61414f5eSMaciej W. Rozycki ret = -ENOMEM; 1294*61414f5eSMaciej W. Rozycki goto err_out_resource; 1295*61414f5eSMaciej W. Rozycki } 1296*61414f5eSMaciej W. Rozycki 1297*61414f5eSMaciej W. Rozycki /* Initialize the new device structure. */ 1298*61414f5eSMaciej W. Rozycki switch (loopback) { 1299*61414f5eSMaciej W. Rozycki case FZA_LOOP_NORMAL: 1300*61414f5eSMaciej W. Rozycki case FZA_LOOP_INTERN: 1301*61414f5eSMaciej W. Rozycki case FZA_LOOP_EXTERN: 1302*61414f5eSMaciej W. Rozycki break; 1303*61414f5eSMaciej W. Rozycki default: 1304*61414f5eSMaciej W. Rozycki loopback = FZA_LOOP_NORMAL; 1305*61414f5eSMaciej W. Rozycki } 1306*61414f5eSMaciej W. Rozycki 1307*61414f5eSMaciej W. Rozycki fp->mmio = mmio; 1308*61414f5eSMaciej W. Rozycki dev->irq = tdev->interrupt; 1309*61414f5eSMaciej W. Rozycki 1310*61414f5eSMaciej W. Rozycki pr_info("%s: DEC FDDIcontroller 700 or 700-C at 0x%08llx, irq %d\n", 1311*61414f5eSMaciej W. Rozycki fp->name, (long long)tdev->resource.start, dev->irq); 1312*61414f5eSMaciej W. Rozycki pr_debug("%s: mapped at: 0x%p\n", fp->name, mmio); 1313*61414f5eSMaciej W. Rozycki 1314*61414f5eSMaciej W. Rozycki fp->regs = mmio + FZA_REG_BASE; 1315*61414f5eSMaciej W. Rozycki fp->ring_cmd = mmio + FZA_RING_CMD; 1316*61414f5eSMaciej W. Rozycki fp->ring_uns = mmio + FZA_RING_UNS; 1317*61414f5eSMaciej W. Rozycki 1318*61414f5eSMaciej W. Rozycki init_waitqueue_head(&fp->state_chg_wait); 1319*61414f5eSMaciej W. Rozycki init_waitqueue_head(&fp->cmd_done_wait); 1320*61414f5eSMaciej W. Rozycki spin_lock_init(&fp->lock); 1321*61414f5eSMaciej W. Rozycki fp->int_mask = FZA_MASK_NORMAL; 1322*61414f5eSMaciej W. Rozycki 1323*61414f5eSMaciej W. Rozycki timer_setup(&fp->reset_timer, fza_reset_timer, 0); 1324*61414f5eSMaciej W. Rozycki 1325*61414f5eSMaciej W. Rozycki /* Sanitize the board. */ 1326*61414f5eSMaciej W. Rozycki fza_regs_dump(fp); 1327*61414f5eSMaciej W. Rozycki fza_do_shutdown(fp); 1328*61414f5eSMaciej W. Rozycki 1329*61414f5eSMaciej W. Rozycki ret = request_irq(dev->irq, fza_interrupt, IRQF_SHARED, fp->name, dev); 1330*61414f5eSMaciej W. Rozycki if (ret != 0) { 1331*61414f5eSMaciej W. Rozycki pr_err("%s: unable to get IRQ %d!\n", fp->name, dev->irq); 1332*61414f5eSMaciej W. Rozycki goto err_out_map; 1333*61414f5eSMaciej W. Rozycki } 1334*61414f5eSMaciej W. Rozycki 1335*61414f5eSMaciej W. Rozycki /* Enable the driver mode. */ 1336*61414f5eSMaciej W. Rozycki writew_o(FZA_CONTROL_B_DRIVER, &fp->regs->control_b); 1337*61414f5eSMaciej W. Rozycki 1338*61414f5eSMaciej W. Rozycki /* For some reason transmit done interrupts can trigger during 1339*61414f5eSMaciej W. Rozycki * reset. This avoids a division error in the handler. 1340*61414f5eSMaciej W. Rozycki */ 1341*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size = FZA_RING_TX_SIZE; 1342*61414f5eSMaciej W. Rozycki 1343*61414f5eSMaciej W. Rozycki ret = fza_reset(fp); 1344*61414f5eSMaciej W. Rozycki if (ret != 0) 1345*61414f5eSMaciej W. Rozycki goto err_out_irq; 1346*61414f5eSMaciej W. Rozycki 1347*61414f5eSMaciej W. Rozycki ret = fza_init_send(dev, &init); 1348*61414f5eSMaciej W. Rozycki if (ret != 0) 1349*61414f5eSMaciej W. Rozycki goto err_out_irq; 1350*61414f5eSMaciej W. Rozycki 1351*61414f5eSMaciej W. Rozycki fza_reads(&init->hw_addr, &hw_addr, sizeof(hw_addr)); 1352*61414f5eSMaciej W. Rozycki memcpy(dev->dev_addr, &hw_addr, FDDI_K_ALEN); 1353*61414f5eSMaciej W. Rozycki 1354*61414f5eSMaciej W. Rozycki fza_reads(&init->rom_rev, &rom_rev, sizeof(rom_rev)); 1355*61414f5eSMaciej W. Rozycki fza_reads(&init->fw_rev, &fw_rev, sizeof(fw_rev)); 1356*61414f5eSMaciej W. Rozycki fza_reads(&init->rmc_rev, &rmc_rev, sizeof(rmc_rev)); 1357*61414f5eSMaciej W. Rozycki for (i = 3; i >= 0 && rom_rev[i] == ' '; i--) 1358*61414f5eSMaciej W. Rozycki rom_rev[i] = 0; 1359*61414f5eSMaciej W. Rozycki for (i = 3; i >= 0 && fw_rev[i] == ' '; i--) 1360*61414f5eSMaciej W. Rozycki fw_rev[i] = 0; 1361*61414f5eSMaciej W. Rozycki for (i = 3; i >= 0 && rmc_rev[i] == ' '; i--) 1362*61414f5eSMaciej W. Rozycki rmc_rev[i] = 0; 1363*61414f5eSMaciej W. Rozycki 1364*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx = mmio + readl_u(&init->rmc_tx); 1365*61414f5eSMaciej W. Rozycki fp->ring_rmc_tx_size = readl_u(&init->rmc_tx_size); 1366*61414f5eSMaciej W. Rozycki fp->ring_hst_rx = mmio + readl_u(&init->hst_rx); 1367*61414f5eSMaciej W. Rozycki fp->ring_hst_rx_size = readl_u(&init->hst_rx_size); 1368*61414f5eSMaciej W. Rozycki fp->ring_smt_tx = mmio + readl_u(&init->smt_tx); 1369*61414f5eSMaciej W. Rozycki fp->ring_smt_tx_size = readl_u(&init->smt_tx_size); 1370*61414f5eSMaciej W. Rozycki fp->ring_smt_rx = mmio + readl_u(&init->smt_rx); 1371*61414f5eSMaciej W. Rozycki fp->ring_smt_rx_size = readl_u(&init->smt_rx_size); 1372*61414f5eSMaciej W. Rozycki 1373*61414f5eSMaciej W. Rozycki fp->buffer_tx = mmio + FZA_TX_BUFFER_ADDR(readl_u(&init->rmc_tx)); 1374*61414f5eSMaciej W. Rozycki 1375*61414f5eSMaciej W. Rozycki fp->t_max = readl_u(&init->def_t_max); 1376*61414f5eSMaciej W. Rozycki fp->t_req = readl_u(&init->def_t_req); 1377*61414f5eSMaciej W. Rozycki fp->tvx = readl_u(&init->def_tvx); 1378*61414f5eSMaciej W. Rozycki fp->lem_threshold = readl_u(&init->lem_threshold); 1379*61414f5eSMaciej W. Rozycki fza_reads(&init->def_station_id, &fp->station_id, 1380*61414f5eSMaciej W. Rozycki sizeof(fp->station_id)); 1381*61414f5eSMaciej W. Rozycki fp->rtoken_timeout = readl_u(&init->rtoken_timeout); 1382*61414f5eSMaciej W. Rozycki fp->ring_purger = readl_u(&init->ring_purger); 1383*61414f5eSMaciej W. Rozycki 1384*61414f5eSMaciej W. Rozycki smt_ver = readl_u(&init->smt_ver); 1385*61414f5eSMaciej W. Rozycki pmd_type = readl_u(&init->pmd_type); 1386*61414f5eSMaciej W. Rozycki 1387*61414f5eSMaciej W. Rozycki pr_debug("%s: INIT parameters:\n", fp->name); 1388*61414f5eSMaciej W. Rozycki pr_debug(" tx_mode: %u\n", readl_u(&init->tx_mode)); 1389*61414f5eSMaciej W. Rozycki pr_debug(" hst_rx_size: %u\n", readl_u(&init->hst_rx_size)); 1390*61414f5eSMaciej W. Rozycki pr_debug(" rmc_rev: %.4s\n", rmc_rev); 1391*61414f5eSMaciej W. Rozycki pr_debug(" rom_rev: %.4s\n", rom_rev); 1392*61414f5eSMaciej W. Rozycki pr_debug(" fw_rev: %.4s\n", fw_rev); 1393*61414f5eSMaciej W. Rozycki pr_debug(" mop_type: %u\n", readl_u(&init->mop_type)); 1394*61414f5eSMaciej W. Rozycki pr_debug(" hst_rx: 0x%08x\n", readl_u(&init->hst_rx)); 1395*61414f5eSMaciej W. Rozycki pr_debug(" rmc_tx: 0x%08x\n", readl_u(&init->rmc_tx)); 1396*61414f5eSMaciej W. Rozycki pr_debug(" rmc_tx_size: %u\n", readl_u(&init->rmc_tx_size)); 1397*61414f5eSMaciej W. Rozycki pr_debug(" smt_tx: 0x%08x\n", readl_u(&init->smt_tx)); 1398*61414f5eSMaciej W. Rozycki pr_debug(" smt_tx_size: %u\n", readl_u(&init->smt_tx_size)); 1399*61414f5eSMaciej W. Rozycki pr_debug(" smt_rx: 0x%08x\n", readl_u(&init->smt_rx)); 1400*61414f5eSMaciej W. Rozycki pr_debug(" smt_rx_size: %u\n", readl_u(&init->smt_rx_size)); 1401*61414f5eSMaciej W. Rozycki /* TC systems are always LE, so don't bother swapping. */ 1402*61414f5eSMaciej W. Rozycki pr_debug(" hw_addr: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", 1403*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 0) & 0xff, 1404*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 8) & 0xff, 1405*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 16) & 0xff, 1406*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[0]) >> 24) & 0xff, 1407*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 0) & 0xff, 1408*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 8) & 0xff, 1409*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 16) & 0xff, 1410*61414f5eSMaciej W. Rozycki (readl_u(&init->hw_addr[1]) >> 24) & 0xff); 1411*61414f5eSMaciej W. Rozycki pr_debug(" def_t_req: %u\n", readl_u(&init->def_t_req)); 1412*61414f5eSMaciej W. Rozycki pr_debug(" def_tvx: %u\n", readl_u(&init->def_tvx)); 1413*61414f5eSMaciej W. Rozycki pr_debug(" def_t_max: %u\n", readl_u(&init->def_t_max)); 1414*61414f5eSMaciej W. Rozycki pr_debug(" lem_threshold: %u\n", readl_u(&init->lem_threshold)); 1415*61414f5eSMaciej W. Rozycki /* Don't bother swapping, see above. */ 1416*61414f5eSMaciej W. Rozycki pr_debug(" def_station_id: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", 1417*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 0) & 0xff, 1418*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 8) & 0xff, 1419*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 16) & 0xff, 1420*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[0]) >> 24) & 0xff, 1421*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 0) & 0xff, 1422*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 8) & 0xff, 1423*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 16) & 0xff, 1424*61414f5eSMaciej W. Rozycki (readl_u(&init->def_station_id[1]) >> 24) & 0xff); 1425*61414f5eSMaciej W. Rozycki pr_debug(" pmd_type_alt: %u\n", readl_u(&init->pmd_type_alt)); 1426*61414f5eSMaciej W. Rozycki pr_debug(" smt_ver: %u\n", readl_u(&init->smt_ver)); 1427*61414f5eSMaciej W. Rozycki pr_debug(" rtoken_timeout: %u\n", readl_u(&init->rtoken_timeout)); 1428*61414f5eSMaciej W. Rozycki pr_debug(" ring_purger: %u\n", readl_u(&init->ring_purger)); 1429*61414f5eSMaciej W. Rozycki pr_debug(" smt_ver_max: %u\n", readl_u(&init->smt_ver_max)); 1430*61414f5eSMaciej W. Rozycki pr_debug(" smt_ver_min: %u\n", readl_u(&init->smt_ver_min)); 1431*61414f5eSMaciej W. Rozycki pr_debug(" pmd_type: %u\n", readl_u(&init->pmd_type)); 1432*61414f5eSMaciej W. Rozycki 1433*61414f5eSMaciej W. Rozycki pr_info("%s: model %s, address %pMF\n", 1434*61414f5eSMaciej W. Rozycki fp->name, 1435*61414f5eSMaciej W. Rozycki pmd_type == FZA_PMD_TYPE_TW ? 1436*61414f5eSMaciej W. Rozycki "700-C (DEFZA-CA), ThinWire PMD selected" : 1437*61414f5eSMaciej W. Rozycki pmd_type == FZA_PMD_TYPE_STP ? 1438*61414f5eSMaciej W. Rozycki "700-C (DEFZA-CA), STP PMD selected" : 1439*61414f5eSMaciej W. Rozycki "700 (DEFZA-AA), MMF PMD", 1440*61414f5eSMaciej W. Rozycki dev->dev_addr); 1441*61414f5eSMaciej W. Rozycki pr_info("%s: ROM rev. %.4s, firmware rev. %.4s, RMC rev. %.4s, " 1442*61414f5eSMaciej W. Rozycki "SMT ver. %u\n", fp->name, rom_rev, fw_rev, rmc_rev, smt_ver); 1443*61414f5eSMaciej W. Rozycki 1444*61414f5eSMaciej W. Rozycki /* Now that we fetched initial parameters just shut the interface 1445*61414f5eSMaciej W. Rozycki * until opened. 1446*61414f5eSMaciej W. Rozycki */ 1447*61414f5eSMaciej W. Rozycki ret = fza_close(dev); 1448*61414f5eSMaciej W. Rozycki if (ret != 0) 1449*61414f5eSMaciej W. Rozycki goto err_out_irq; 1450*61414f5eSMaciej W. Rozycki 1451*61414f5eSMaciej W. Rozycki /* The FZA-specific entries in the device structure. */ 1452*61414f5eSMaciej W. Rozycki dev->netdev_ops = &netdev_ops; 1453*61414f5eSMaciej W. Rozycki 1454*61414f5eSMaciej W. Rozycki ret = register_netdev(dev); 1455*61414f5eSMaciej W. Rozycki if (ret != 0) 1456*61414f5eSMaciej W. Rozycki goto err_out_irq; 1457*61414f5eSMaciej W. Rozycki 1458*61414f5eSMaciej W. Rozycki pr_info("%s: registered as %s\n", fp->name, dev->name); 1459*61414f5eSMaciej W. Rozycki fp->name = (const char *)dev->name; 1460*61414f5eSMaciej W. Rozycki 1461*61414f5eSMaciej W. Rozycki get_device(bdev); 1462*61414f5eSMaciej W. Rozycki return 0; 1463*61414f5eSMaciej W. Rozycki 1464*61414f5eSMaciej W. Rozycki err_out_irq: 1465*61414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 1466*61414f5eSMaciej W. Rozycki fza_do_shutdown(fp); 1467*61414f5eSMaciej W. Rozycki free_irq(dev->irq, dev); 1468*61414f5eSMaciej W. Rozycki 1469*61414f5eSMaciej W. Rozycki err_out_map: 1470*61414f5eSMaciej W. Rozycki iounmap(mmio); 1471*61414f5eSMaciej W. Rozycki 1472*61414f5eSMaciej W. Rozycki err_out_resource: 1473*61414f5eSMaciej W. Rozycki release_mem_region(start, len); 1474*61414f5eSMaciej W. Rozycki 1475*61414f5eSMaciej W. Rozycki err_out_kfree: 1476*61414f5eSMaciej W. Rozycki free_netdev(dev); 1477*61414f5eSMaciej W. Rozycki 1478*61414f5eSMaciej W. Rozycki pr_err("%s: initialization failure, aborting!\n", fp->name); 1479*61414f5eSMaciej W. Rozycki return ret; 1480*61414f5eSMaciej W. Rozycki } 1481*61414f5eSMaciej W. Rozycki 1482*61414f5eSMaciej W. Rozycki static int fza_remove(struct device *bdev) 1483*61414f5eSMaciej W. Rozycki { 1484*61414f5eSMaciej W. Rozycki struct net_device *dev = dev_get_drvdata(bdev); 1485*61414f5eSMaciej W. Rozycki struct fza_private *fp = netdev_priv(dev); 1486*61414f5eSMaciej W. Rozycki struct tc_dev *tdev = to_tc_dev(bdev); 1487*61414f5eSMaciej W. Rozycki resource_size_t start, len; 1488*61414f5eSMaciej W. Rozycki 1489*61414f5eSMaciej W. Rozycki put_device(bdev); 1490*61414f5eSMaciej W. Rozycki 1491*61414f5eSMaciej W. Rozycki unregister_netdev(dev); 1492*61414f5eSMaciej W. Rozycki 1493*61414f5eSMaciej W. Rozycki del_timer_sync(&fp->reset_timer); 1494*61414f5eSMaciej W. Rozycki fza_do_shutdown(fp); 1495*61414f5eSMaciej W. Rozycki free_irq(dev->irq, dev); 1496*61414f5eSMaciej W. Rozycki 1497*61414f5eSMaciej W. Rozycki iounmap(fp->mmio); 1498*61414f5eSMaciej W. Rozycki 1499*61414f5eSMaciej W. Rozycki start = tdev->resource.start; 1500*61414f5eSMaciej W. Rozycki len = tdev->resource.end - start + 1; 1501*61414f5eSMaciej W. Rozycki release_mem_region(start, len); 1502*61414f5eSMaciej W. Rozycki 1503*61414f5eSMaciej W. Rozycki free_netdev(dev); 1504*61414f5eSMaciej W. Rozycki 1505*61414f5eSMaciej W. Rozycki return 0; 1506*61414f5eSMaciej W. Rozycki } 1507*61414f5eSMaciej W. Rozycki 1508*61414f5eSMaciej W. Rozycki static struct tc_device_id const fza_tc_table[] = { 1509*61414f5eSMaciej W. Rozycki { "DEC ", "PMAF-AA " }, 1510*61414f5eSMaciej W. Rozycki { } 1511*61414f5eSMaciej W. Rozycki }; 1512*61414f5eSMaciej W. Rozycki MODULE_DEVICE_TABLE(tc, fza_tc_table); 1513*61414f5eSMaciej W. Rozycki 1514*61414f5eSMaciej W. Rozycki static struct tc_driver fza_driver = { 1515*61414f5eSMaciej W. Rozycki .id_table = fza_tc_table, 1516*61414f5eSMaciej W. Rozycki .driver = { 1517*61414f5eSMaciej W. Rozycki .name = "defza", 1518*61414f5eSMaciej W. Rozycki .bus = &tc_bus_type, 1519*61414f5eSMaciej W. Rozycki .probe = fza_probe, 1520*61414f5eSMaciej W. Rozycki .remove = fza_remove, 1521*61414f5eSMaciej W. Rozycki }, 1522*61414f5eSMaciej W. Rozycki }; 1523*61414f5eSMaciej W. Rozycki 1524*61414f5eSMaciej W. Rozycki static int fza_init(void) 1525*61414f5eSMaciej W. Rozycki { 1526*61414f5eSMaciej W. Rozycki return tc_register_driver(&fza_driver); 1527*61414f5eSMaciej W. Rozycki } 1528*61414f5eSMaciej W. Rozycki 1529*61414f5eSMaciej W. Rozycki static void fza_exit(void) 1530*61414f5eSMaciej W. Rozycki { 1531*61414f5eSMaciej W. Rozycki tc_unregister_driver(&fza_driver); 1532*61414f5eSMaciej W. Rozycki } 1533*61414f5eSMaciej W. Rozycki 1534*61414f5eSMaciej W. Rozycki module_init(fza_init); 1535*61414f5eSMaciej W. Rozycki module_exit(fza_exit); 1536