18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* FDDI network adapter driver for DEC FDDIcontroller 700/700-C devices. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2018 Maciej W. Rozycki 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 78c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License 88c2ecf20Sopenharmony_ci * as published by the Free Software Foundation; either version 98c2ecf20Sopenharmony_ci * 2 of the License, or (at your option) any later version. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * References: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Dave Sawyer & Phil Weeks & Frank Itkowsky, 148c2ecf20Sopenharmony_ci * "DEC FDDIcontroller 700 Port Specification", 158c2ecf20Sopenharmony_ci * Revision 1.1, Digital Equipment Corporation 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 198c2ecf20Sopenharmony_ci/* FZA configurable parameters. */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* The number of transmit ring descriptors; either 0 for 512 or 1 for 1024. */ 228c2ecf20Sopenharmony_ci#define FZA_RING_TX_MODE 0 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* The number of receive ring descriptors; from 2 up to 256. */ 258c2ecf20Sopenharmony_ci#define FZA_RING_RX_SIZE 256 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* End of FZA configurable parameters. No need to change anything below. */ 288c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/device.h> 328c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 358c2ecf20Sopenharmony_ci#include <linux/io.h> 368c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 378c2ecf20Sopenharmony_ci#include <linux/ioport.h> 388c2ecf20Sopenharmony_ci#include <linux/kernel.h> 398c2ecf20Sopenharmony_ci#include <linux/list.h> 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 428c2ecf20Sopenharmony_ci#include <linux/fddidevice.h> 438c2ecf20Sopenharmony_ci#include <linux/sched.h> 448c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 458c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 468c2ecf20Sopenharmony_ci#include <linux/stat.h> 478c2ecf20Sopenharmony_ci#include <linux/tc.h> 488c2ecf20Sopenharmony_ci#include <linux/timer.h> 498c2ecf20Sopenharmony_ci#include <linux/types.h> 508c2ecf20Sopenharmony_ci#include <linux/wait.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include <asm/barrier.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include "defza.h" 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define DRV_NAME "defza" 578c2ecf20Sopenharmony_ci#define DRV_VERSION "v.1.1.4" 588c2ecf20Sopenharmony_ci#define DRV_RELDATE "Oct 6 2018" 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic const char version[] = 618c2ecf20Sopenharmony_ci DRV_NAME ": " DRV_VERSION " " DRV_RELDATE " Maciej W. Rozycki\n"; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>"); 648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DEC FDDIcontroller 700 (DEFZA-xx) driver"); 658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int loopback; 688c2ecf20Sopenharmony_cimodule_param(loopback, int, 0644); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Ring Purger Multicast */ 718c2ecf20Sopenharmony_cistatic u8 hw_addr_purger[8] = { 0x09, 0x00, 0x2b, 0x02, 0x01, 0x05 }; 728c2ecf20Sopenharmony_ci/* Directed Beacon Multicast */ 738c2ecf20Sopenharmony_cistatic u8 hw_addr_beacon[8] = { 0x01, 0x80, 0xc2, 0x00, 0x01, 0x00 }; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Shorthands for MMIO accesses that we require to be strongly ordered 768c2ecf20Sopenharmony_ci * WRT preceding MMIO accesses. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define readw_o readw_relaxed 798c2ecf20Sopenharmony_ci#define readl_o readl_relaxed 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define writew_o writew_relaxed 828c2ecf20Sopenharmony_ci#define writel_o writel_relaxed 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Shorthands for MMIO accesses that we are happy with being weakly ordered 858c2ecf20Sopenharmony_ci * WRT preceding MMIO accesses. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci#define readw_u readw_relaxed 888c2ecf20Sopenharmony_ci#define readl_u readl_relaxed 898c2ecf20Sopenharmony_ci#define readq_u readq_relaxed 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define writew_u writew_relaxed 928c2ecf20Sopenharmony_ci#define writel_u writel_relaxed 938c2ecf20Sopenharmony_ci#define writeq_u writeq_relaxed 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline struct sk_buff *fza_alloc_skb_irq(struct net_device *dev, 968c2ecf20Sopenharmony_ci unsigned int length) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return __netdev_alloc_skb(dev, length, GFP_ATOMIC); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline struct sk_buff *fza_alloc_skb(struct net_device *dev, 1028c2ecf20Sopenharmony_ci unsigned int length) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return __netdev_alloc_skb(dev, length, GFP_KERNEL); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline void fza_skb_align(struct sk_buff *skb, unsigned int v) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned long x, y; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci x = (unsigned long)skb->data; 1128c2ecf20Sopenharmony_ci y = ALIGN(x, v); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci skb_reserve(skb, y - x); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic inline void fza_reads(const void __iomem *from, void *to, 1188c2ecf20Sopenharmony_ci unsigned long size) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (sizeof(unsigned long) == 8) { 1218c2ecf20Sopenharmony_ci const u64 __iomem *src = from; 1228c2ecf20Sopenharmony_ci const u32 __iomem *src_trail; 1238c2ecf20Sopenharmony_ci u64 *dst = to; 1248c2ecf20Sopenharmony_ci u32 *dst_trail; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size > 1; size -= 2) 1278c2ecf20Sopenharmony_ci *dst++ = readq_u(src++); 1288c2ecf20Sopenharmony_ci if (size) { 1298c2ecf20Sopenharmony_ci src_trail = (u32 __iomem *)src; 1308c2ecf20Sopenharmony_ci dst_trail = (u32 *)dst; 1318c2ecf20Sopenharmony_ci *dst_trail = readl_u(src_trail); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci } else { 1348c2ecf20Sopenharmony_ci const u32 __iomem *src = from; 1358c2ecf20Sopenharmony_ci u32 *dst = to; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size; size--) 1388c2ecf20Sopenharmony_ci *dst++ = readl_u(src++); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic inline void fza_writes(const void *from, void __iomem *to, 1438c2ecf20Sopenharmony_ci unsigned long size) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci if (sizeof(unsigned long) == 8) { 1468c2ecf20Sopenharmony_ci const u64 *src = from; 1478c2ecf20Sopenharmony_ci const u32 *src_trail; 1488c2ecf20Sopenharmony_ci u64 __iomem *dst = to; 1498c2ecf20Sopenharmony_ci u32 __iomem *dst_trail; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size > 1; size -= 2) 1528c2ecf20Sopenharmony_ci writeq_u(*src++, dst++); 1538c2ecf20Sopenharmony_ci if (size) { 1548c2ecf20Sopenharmony_ci src_trail = (u32 *)src; 1558c2ecf20Sopenharmony_ci dst_trail = (u32 __iomem *)dst; 1568c2ecf20Sopenharmony_ci writel_u(*src_trail, dst_trail); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci const u32 *src = from; 1608c2ecf20Sopenharmony_ci u32 __iomem *dst = to; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size; size--) 1638c2ecf20Sopenharmony_ci writel_u(*src++, dst++); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic inline void fza_moves(const void __iomem *from, void __iomem *to, 1688c2ecf20Sopenharmony_ci unsigned long size) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci if (sizeof(unsigned long) == 8) { 1718c2ecf20Sopenharmony_ci const u64 __iomem *src = from; 1728c2ecf20Sopenharmony_ci const u32 __iomem *src_trail; 1738c2ecf20Sopenharmony_ci u64 __iomem *dst = to; 1748c2ecf20Sopenharmony_ci u32 __iomem *dst_trail; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size > 1; size -= 2) 1778c2ecf20Sopenharmony_ci writeq_u(readq_u(src++), dst++); 1788c2ecf20Sopenharmony_ci if (size) { 1798c2ecf20Sopenharmony_ci src_trail = (u32 __iomem *)src; 1808c2ecf20Sopenharmony_ci dst_trail = (u32 __iomem *)dst; 1818c2ecf20Sopenharmony_ci writel_u(readl_u(src_trail), dst_trail); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } else { 1848c2ecf20Sopenharmony_ci const u32 __iomem *src = from; 1858c2ecf20Sopenharmony_ci u32 __iomem *dst = to; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size; size--) 1888c2ecf20Sopenharmony_ci writel_u(readl_u(src++), dst++); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic inline void fza_zeros(void __iomem *to, unsigned long size) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci if (sizeof(unsigned long) == 8) { 1958c2ecf20Sopenharmony_ci u64 __iomem *dst = to; 1968c2ecf20Sopenharmony_ci u32 __iomem *dst_trail; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size > 1; size -= 2) 1998c2ecf20Sopenharmony_ci writeq_u(0, dst++); 2008c2ecf20Sopenharmony_ci if (size) { 2018c2ecf20Sopenharmony_ci dst_trail = (u32 __iomem *)dst; 2028c2ecf20Sopenharmony_ci writel_u(0, dst_trail); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } else { 2058c2ecf20Sopenharmony_ci u32 __iomem *dst = to; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (size = (size + 3) / 4; size; size--) 2088c2ecf20Sopenharmony_ci writel_u(0, dst++); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic inline void fza_regs_dump(struct fza_private *fp) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci pr_debug("%s: iomem registers:\n", fp->name); 2158c2ecf20Sopenharmony_ci pr_debug(" reset: 0x%04x\n", readw_o(&fp->regs->reset)); 2168c2ecf20Sopenharmony_ci pr_debug(" interrupt event: 0x%04x\n", readw_u(&fp->regs->int_event)); 2178c2ecf20Sopenharmony_ci pr_debug(" status: 0x%04x\n", readw_u(&fp->regs->status)); 2188c2ecf20Sopenharmony_ci pr_debug(" interrupt mask: 0x%04x\n", readw_u(&fp->regs->int_mask)); 2198c2ecf20Sopenharmony_ci pr_debug(" control A: 0x%04x\n", readw_u(&fp->regs->control_a)); 2208c2ecf20Sopenharmony_ci pr_debug(" control B: 0x%04x\n", readw_u(&fp->regs->control_b)); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic inline void fza_do_reset(struct fza_private *fp) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci /* Reset the board. */ 2268c2ecf20Sopenharmony_ci writew_o(FZA_RESET_INIT, &fp->regs->reset); 2278c2ecf20Sopenharmony_ci readw_o(&fp->regs->reset); /* Synchronize. */ 2288c2ecf20Sopenharmony_ci readw_o(&fp->regs->reset); /* Read it back for a small delay. */ 2298c2ecf20Sopenharmony_ci writew_o(FZA_RESET_CLR, &fp->regs->reset); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Enable all interrupt events we handle. */ 2328c2ecf20Sopenharmony_ci writew_o(fp->int_mask, &fp->regs->int_mask); 2338c2ecf20Sopenharmony_ci readw_o(&fp->regs->int_mask); /* Synchronize. */ 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline void fza_do_shutdown(struct fza_private *fp) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci /* Disable the driver mode. */ 2398c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_B_IDLE, &fp->regs->control_b); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* And reset the board. */ 2428c2ecf20Sopenharmony_ci writew_o(FZA_RESET_INIT, &fp->regs->reset); 2438c2ecf20Sopenharmony_ci readw_o(&fp->regs->reset); /* Synchronize. */ 2448c2ecf20Sopenharmony_ci writew_o(FZA_RESET_CLR, &fp->regs->reset); 2458c2ecf20Sopenharmony_ci readw_o(&fp->regs->reset); /* Synchronize. */ 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int fza_reset(struct fza_private *fp) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci unsigned long flags; 2518c2ecf20Sopenharmony_ci uint status, state; 2528c2ecf20Sopenharmony_ci long t; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci pr_info("%s: resetting the board...\n", fp->name); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci spin_lock_irqsave(&fp->lock, flags); 2578c2ecf20Sopenharmony_ci fp->state_chg_flag = 0; 2588c2ecf20Sopenharmony_ci fza_do_reset(fp); 2598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fp->lock, flags); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* DEC says RESET needs up to 30 seconds to complete. My DEFZA-AA 2628c2ecf20Sopenharmony_ci * rev. C03 happily finishes in 9.7 seconds. :-) But we need to 2638c2ecf20Sopenharmony_ci * be on the safe side... 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag, 2668c2ecf20Sopenharmony_ci 45 * HZ); 2678c2ecf20Sopenharmony_ci status = readw_u(&fp->regs->status); 2688c2ecf20Sopenharmony_ci state = FZA_STATUS_GET_STATE(status); 2698c2ecf20Sopenharmony_ci if (fp->state_chg_flag == 0) { 2708c2ecf20Sopenharmony_ci pr_err("%s: RESET timed out!, state %x\n", fp->name, state); 2718c2ecf20Sopenharmony_ci return -EIO; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci if (state != FZA_STATE_UNINITIALIZED) { 2748c2ecf20Sopenharmony_ci pr_err("%s: RESET failed!, state %x, failure ID %x\n", 2758c2ecf20Sopenharmony_ci fp->name, state, FZA_STATUS_GET_TEST(status)); 2768c2ecf20Sopenharmony_ci return -EIO; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci pr_info("%s: OK\n", fp->name); 2798c2ecf20Sopenharmony_ci pr_debug("%s: RESET: %lums elapsed\n", fp->name, 2808c2ecf20Sopenharmony_ci (45 * HZ - t) * 1000 / HZ); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic struct fza_ring_cmd __iomem *fza_cmd_send(struct net_device *dev, 2868c2ecf20Sopenharmony_ci int command) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 2898c2ecf20Sopenharmony_ci struct fza_ring_cmd __iomem *ring = fp->ring_cmd + fp->ring_cmd_index; 2908c2ecf20Sopenharmony_ci unsigned int old_mask, new_mask; 2918c2ecf20Sopenharmony_ci union fza_cmd_buf __iomem *buf; 2928c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2938c2ecf20Sopenharmony_ci int i; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci old_mask = fp->int_mask; 2968c2ecf20Sopenharmony_ci new_mask = old_mask & ~FZA_MASK_STATE_CHG; 2978c2ecf20Sopenharmony_ci writew_u(new_mask, &fp->regs->int_mask); 2988c2ecf20Sopenharmony_ci readw_o(&fp->regs->int_mask); /* Synchronize. */ 2998c2ecf20Sopenharmony_ci fp->int_mask = new_mask; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci buf = fp->mmio + readl_u(&ring->buffer); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if ((readl_u(&ring->cmd_own) & FZA_RING_OWN_MASK) != 3048c2ecf20Sopenharmony_ci FZA_RING_OWN_HOST) { 3058c2ecf20Sopenharmony_ci pr_warn("%s: command buffer full, command: %u!\n", fp->name, 3068c2ecf20Sopenharmony_ci command); 3078c2ecf20Sopenharmony_ci return NULL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci switch (command) { 3118c2ecf20Sopenharmony_ci case FZA_RING_CMD_INIT: 3128c2ecf20Sopenharmony_ci writel_u(FZA_RING_TX_MODE, &buf->init.tx_mode); 3138c2ecf20Sopenharmony_ci writel_u(FZA_RING_RX_SIZE, &buf->init.hst_rx_size); 3148c2ecf20Sopenharmony_ci fza_zeros(&buf->init.counters, sizeof(buf->init.counters)); 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci case FZA_RING_CMD_MODCAM: 3188c2ecf20Sopenharmony_ci i = 0; 3198c2ecf20Sopenharmony_ci fza_writes(&hw_addr_purger, &buf->cam.hw_addr[i++], 3208c2ecf20Sopenharmony_ci sizeof(*buf->cam.hw_addr)); 3218c2ecf20Sopenharmony_ci fza_writes(&hw_addr_beacon, &buf->cam.hw_addr[i++], 3228c2ecf20Sopenharmony_ci sizeof(*buf->cam.hw_addr)); 3238c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 3248c2ecf20Sopenharmony_ci if (i >= FZA_CMD_CAM_SIZE) 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci fza_writes(ha->addr, &buf->cam.hw_addr[i++], 3278c2ecf20Sopenharmony_ci sizeof(*buf->cam.hw_addr)); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci while (i < FZA_CMD_CAM_SIZE) 3308c2ecf20Sopenharmony_ci fza_zeros(&buf->cam.hw_addr[i++], 3318c2ecf20Sopenharmony_ci sizeof(*buf->cam.hw_addr)); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci case FZA_RING_CMD_PARAM: 3358c2ecf20Sopenharmony_ci writel_u(loopback, &buf->param.loop_mode); 3368c2ecf20Sopenharmony_ci writel_u(fp->t_max, &buf->param.t_max); 3378c2ecf20Sopenharmony_ci writel_u(fp->t_req, &buf->param.t_req); 3388c2ecf20Sopenharmony_ci writel_u(fp->tvx, &buf->param.tvx); 3398c2ecf20Sopenharmony_ci writel_u(fp->lem_threshold, &buf->param.lem_threshold); 3408c2ecf20Sopenharmony_ci fza_writes(&fp->station_id, &buf->param.station_id, 3418c2ecf20Sopenharmony_ci sizeof(buf->param.station_id)); 3428c2ecf20Sopenharmony_ci /* Convert to milliseconds due to buggy firmware. */ 3438c2ecf20Sopenharmony_ci writel_u(fp->rtoken_timeout / 12500, 3448c2ecf20Sopenharmony_ci &buf->param.rtoken_timeout); 3458c2ecf20Sopenharmony_ci writel_u(fp->ring_purger, &buf->param.ring_purger); 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci case FZA_RING_CMD_MODPROM: 3498c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 3508c2ecf20Sopenharmony_ci writel_u(1, &buf->modprom.llc_prom); 3518c2ecf20Sopenharmony_ci writel_u(1, &buf->modprom.smt_prom); 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci writel_u(0, &buf->modprom.llc_prom); 3548c2ecf20Sopenharmony_ci writel_u(0, &buf->modprom.smt_prom); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci if (dev->flags & IFF_ALLMULTI || 3578c2ecf20Sopenharmony_ci netdev_mc_count(dev) > FZA_CMD_CAM_SIZE - 2) 3588c2ecf20Sopenharmony_ci writel_u(1, &buf->modprom.llc_multi); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci writel_u(0, &buf->modprom.llc_multi); 3618c2ecf20Sopenharmony_ci writel_u(1, &buf->modprom.llc_bcast); 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* Trigger the command. */ 3668c2ecf20Sopenharmony_ci writel_u(FZA_RING_OWN_FZA | command, &ring->cmd_own); 3678c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_CMD_POLL, &fp->regs->control_a); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci fp->ring_cmd_index = (fp->ring_cmd_index + 1) % FZA_RING_CMD_SIZE; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci fp->int_mask = old_mask; 3728c2ecf20Sopenharmony_ci writew_u(fp->int_mask, &fp->regs->int_mask); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return ring; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int fza_init_send(struct net_device *dev, 3788c2ecf20Sopenharmony_ci struct fza_cmd_init *__iomem *init) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 3818c2ecf20Sopenharmony_ci struct fza_ring_cmd __iomem *ring; 3828c2ecf20Sopenharmony_ci unsigned long flags; 3838c2ecf20Sopenharmony_ci u32 stat; 3848c2ecf20Sopenharmony_ci long t; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci spin_lock_irqsave(&fp->lock, flags); 3878c2ecf20Sopenharmony_ci fp->cmd_done_flag = 0; 3888c2ecf20Sopenharmony_ci ring = fza_cmd_send(dev, FZA_RING_CMD_INIT); 3898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fp->lock, flags); 3908c2ecf20Sopenharmony_ci if (!ring) 3918c2ecf20Sopenharmony_ci /* This should never happen in the uninitialized state, 3928c2ecf20Sopenharmony_ci * so do not try to recover and just consider it fatal. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci return -ENOBUFS; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* INIT may take quite a long time (160ms for my C03). */ 3978c2ecf20Sopenharmony_ci t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); 3988c2ecf20Sopenharmony_ci if (fp->cmd_done_flag == 0) { 3998c2ecf20Sopenharmony_ci pr_err("%s: INIT command timed out!, state %x\n", fp->name, 4008c2ecf20Sopenharmony_ci FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 4018c2ecf20Sopenharmony_ci return -EIO; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci stat = readl_u(&ring->stat); 4048c2ecf20Sopenharmony_ci if (stat != FZA_RING_STAT_SUCCESS) { 4058c2ecf20Sopenharmony_ci pr_err("%s: INIT command failed!, status %02x, state %x\n", 4068c2ecf20Sopenharmony_ci fp->name, stat, 4078c2ecf20Sopenharmony_ci FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 4088c2ecf20Sopenharmony_ci return -EIO; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci pr_debug("%s: INIT: %lums elapsed\n", fp->name, 4118c2ecf20Sopenharmony_ci (3 * HZ - t) * 1000 / HZ); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (init) 4148c2ecf20Sopenharmony_ci *init = fp->mmio + readl_u(&ring->buffer); 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void fza_rx_init(struct fza_private *fp) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci int i; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Fill the host receive descriptor ring. */ 4238c2ecf20Sopenharmony_ci for (i = 0; i < FZA_RING_RX_SIZE; i++) { 4248c2ecf20Sopenharmony_ci writel_o(0, &fp->ring_hst_rx[i].rmc); 4258c2ecf20Sopenharmony_ci writel_o((fp->rx_dma[i] + 0x1000) >> 9, 4268c2ecf20Sopenharmony_ci &fp->ring_hst_rx[i].buffer1); 4278c2ecf20Sopenharmony_ci writel_o(fp->rx_dma[i] >> 9 | FZA_RING_OWN_FZA, 4288c2ecf20Sopenharmony_ci &fp->ring_hst_rx[i].buf0_own); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void fza_set_rx_mode(struct net_device *dev) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci fza_cmd_send(dev, FZA_RING_CMD_MODCAM); 4358c2ecf20Sopenharmony_ci fza_cmd_send(dev, FZA_RING_CMD_MODPROM); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ciunion fza_buffer_txp { 4398c2ecf20Sopenharmony_ci struct fza_buffer_tx *data_ptr; 4408c2ecf20Sopenharmony_ci struct fza_buffer_tx __iomem *mmio_ptr; 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int fza_do_xmit(union fza_buffer_txp ub, int len, 4448c2ecf20Sopenharmony_ci struct net_device *dev, int smt) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 4478c2ecf20Sopenharmony_ci struct fza_buffer_tx __iomem *rmc_tx_ptr; 4488c2ecf20Sopenharmony_ci int i, first, frag_len, left_len; 4498c2ecf20Sopenharmony_ci u32 own, rmc; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 4528c2ecf20Sopenharmony_ci fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 4538c2ecf20Sopenharmony_ci FZA_TX_BUFFER_SIZE) < len) 4548c2ecf20Sopenharmony_ci return 1; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci first = fp->ring_rmc_tx_index; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci left_len = len; 4598c2ecf20Sopenharmony_ci frag_len = FZA_TX_BUFFER_SIZE; 4608c2ecf20Sopenharmony_ci /* First descriptor is relinquished last. */ 4618c2ecf20Sopenharmony_ci own = FZA_RING_TX_OWN_HOST; 4628c2ecf20Sopenharmony_ci /* First descriptor carries frame length; we don't use cut-through. */ 4638c2ecf20Sopenharmony_ci rmc = FZA_RING_TX_SOP | FZA_RING_TX_VBC | len; 4648c2ecf20Sopenharmony_ci do { 4658c2ecf20Sopenharmony_ci i = fp->ring_rmc_tx_index; 4668c2ecf20Sopenharmony_ci rmc_tx_ptr = &fp->buffer_tx[i]; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (left_len < FZA_TX_BUFFER_SIZE) 4698c2ecf20Sopenharmony_ci frag_len = left_len; 4708c2ecf20Sopenharmony_ci left_len -= frag_len; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* Length must be a multiple of 4 as only word writes are 4738c2ecf20Sopenharmony_ci * permitted! 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci frag_len = (frag_len + 3) & ~3; 4768c2ecf20Sopenharmony_ci if (smt) 4778c2ecf20Sopenharmony_ci fza_moves(ub.mmio_ptr, rmc_tx_ptr, frag_len); 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci fza_writes(ub.data_ptr, rmc_tx_ptr, frag_len); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (left_len == 0) 4828c2ecf20Sopenharmony_ci rmc |= FZA_RING_TX_EOP; /* Mark last frag. */ 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci writel_o(rmc, &fp->ring_rmc_tx[i].rmc); 4858c2ecf20Sopenharmony_ci writel_o(own, &fp->ring_rmc_tx[i].own); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci ub.data_ptr++; 4888c2ecf20Sopenharmony_ci fp->ring_rmc_tx_index = (fp->ring_rmc_tx_index + 1) % 4898c2ecf20Sopenharmony_ci fp->ring_rmc_tx_size; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Settings for intermediate frags. */ 4928c2ecf20Sopenharmony_ci own = FZA_RING_TX_OWN_RMC; 4938c2ecf20Sopenharmony_ci rmc = 0; 4948c2ecf20Sopenharmony_ci } while (left_len > 0); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 4978c2ecf20Sopenharmony_ci fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 4988c2ecf20Sopenharmony_ci FZA_TX_BUFFER_SIZE) < dev->mtu + dev->hard_header_len) { 4998c2ecf20Sopenharmony_ci netif_stop_queue(dev); 5008c2ecf20Sopenharmony_ci pr_debug("%s: queue stopped\n", fp->name); 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci writel_o(FZA_RING_TX_OWN_RMC, &fp->ring_rmc_tx[first].own); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* Go, go, go! */ 5068c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_TX_POLL, &fp->regs->control_a); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int fza_do_recv_smt(struct fza_buffer_tx *data_ptr, int len, 5128c2ecf20Sopenharmony_ci u32 rmc, struct net_device *dev) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 5158c2ecf20Sopenharmony_ci struct fza_buffer_tx __iomem *smt_rx_ptr; 5168c2ecf20Sopenharmony_ci u32 own; 5178c2ecf20Sopenharmony_ci int i; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci i = fp->ring_smt_rx_index; 5208c2ecf20Sopenharmony_ci own = readl_o(&fp->ring_smt_rx[i].own); 5218c2ecf20Sopenharmony_ci if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 5228c2ecf20Sopenharmony_ci return 1; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci smt_rx_ptr = fp->mmio + readl_u(&fp->ring_smt_rx[i].buffer); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Length must be a multiple of 4 as only word writes are permitted! */ 5278c2ecf20Sopenharmony_ci fza_writes(data_ptr, smt_rx_ptr, (len + 3) & ~3); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci writel_o(rmc, &fp->ring_smt_rx[i].rmc); 5308c2ecf20Sopenharmony_ci writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_rx[i].own); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci fp->ring_smt_rx_index = 5338c2ecf20Sopenharmony_ci (fp->ring_smt_rx_index + 1) % fp->ring_smt_rx_size; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Grab it! */ 5368c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_SMT_RX_POLL, &fp->regs->control_a); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return 0; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void fza_tx(struct net_device *dev) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 5448c2ecf20Sopenharmony_ci u32 own, rmc; 5458c2ecf20Sopenharmony_ci int i; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci while (1) { 5488c2ecf20Sopenharmony_ci i = fp->ring_rmc_txd_index; 5498c2ecf20Sopenharmony_ci if (i == fp->ring_rmc_tx_index) 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci own = readl_o(&fp->ring_rmc_tx[i].own); 5528c2ecf20Sopenharmony_ci if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rmc = readl_u(&fp->ring_rmc_tx[i].rmc); 5568c2ecf20Sopenharmony_ci /* Only process the first descriptor. */ 5578c2ecf20Sopenharmony_ci if ((rmc & FZA_RING_TX_SOP) != 0) { 5588c2ecf20Sopenharmony_ci if ((rmc & FZA_RING_TX_DCC_MASK) == 5598c2ecf20Sopenharmony_ci FZA_RING_TX_DCC_SUCCESS) { 5608c2ecf20Sopenharmony_ci int pkt_len = (rmc & FZA_RING_PBC_MASK) - 3; 5618c2ecf20Sopenharmony_ci /* Omit PRH. */ 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci fp->stats.tx_packets++; 5648c2ecf20Sopenharmony_ci fp->stats.tx_bytes += pkt_len; 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci fp->stats.tx_errors++; 5678c2ecf20Sopenharmony_ci switch (rmc & FZA_RING_TX_DCC_MASK) { 5688c2ecf20Sopenharmony_ci case FZA_RING_TX_DCC_DTP_SOP: 5698c2ecf20Sopenharmony_ci case FZA_RING_TX_DCC_DTP: 5708c2ecf20Sopenharmony_ci case FZA_RING_TX_DCC_ABORT: 5718c2ecf20Sopenharmony_ci fp->stats.tx_aborted_errors++; 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci case FZA_RING_TX_DCC_UNDRRUN: 5748c2ecf20Sopenharmony_ci fp->stats.tx_fifo_errors++; 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci case FZA_RING_TX_DCC_PARITY: 5778c2ecf20Sopenharmony_ci default: 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci fp->ring_rmc_txd_index = (fp->ring_rmc_txd_index + 1) % 5848c2ecf20Sopenharmony_ci fp->ring_rmc_tx_size; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (((((fp->ring_rmc_txd_index - 1 + fp->ring_rmc_tx_size) - 5888c2ecf20Sopenharmony_ci fp->ring_rmc_tx_index) % fp->ring_rmc_tx_size) * 5898c2ecf20Sopenharmony_ci FZA_TX_BUFFER_SIZE) >= dev->mtu + dev->hard_header_len) { 5908c2ecf20Sopenharmony_ci if (fp->queue_active) { 5918c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5928c2ecf20Sopenharmony_ci pr_debug("%s: queue woken\n", fp->name); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic inline int fza_rx_err(struct fza_private *fp, 5988c2ecf20Sopenharmony_ci const u32 rmc, const u8 fc) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci int len, min_len, max_len; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci len = rmc & FZA_RING_PBC_MASK; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (unlikely((rmc & FZA_RING_RX_BAD) != 0)) { 6058c2ecf20Sopenharmony_ci fp->stats.rx_errors++; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Check special status codes. */ 6088c2ecf20Sopenharmony_ci if ((rmc & (FZA_RING_RX_CRC | FZA_RING_RX_RRR_MASK | 6098c2ecf20Sopenharmony_ci FZA_RING_RX_DA_MASK | FZA_RING_RX_SA_MASK)) == 6108c2ecf20Sopenharmony_ci (FZA_RING_RX_CRC | FZA_RING_RX_RRR_DADDR | 6118c2ecf20Sopenharmony_ci FZA_RING_RX_DA_CAM | FZA_RING_RX_SA_ALIAS)) { 6128c2ecf20Sopenharmony_ci if (len >= 8190) 6138c2ecf20Sopenharmony_ci fp->stats.rx_length_errors++; 6148c2ecf20Sopenharmony_ci return 1; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci if ((rmc & (FZA_RING_RX_CRC | FZA_RING_RX_RRR_MASK | 6178c2ecf20Sopenharmony_ci FZA_RING_RX_DA_MASK | FZA_RING_RX_SA_MASK)) == 6188c2ecf20Sopenharmony_ci (FZA_RING_RX_CRC | FZA_RING_RX_RRR_DADDR | 6198c2ecf20Sopenharmony_ci FZA_RING_RX_DA_CAM | FZA_RING_RX_SA_CAM)) { 6208c2ecf20Sopenharmony_ci /* Halt the interface to trigger a reset. */ 6218c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_HALT, &fp->regs->control_a); 6228c2ecf20Sopenharmony_ci readw_o(&fp->regs->control_a); /* Synchronize. */ 6238c2ecf20Sopenharmony_ci return 1; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* Check the MAC status. */ 6278c2ecf20Sopenharmony_ci switch (rmc & FZA_RING_RX_RRR_MASK) { 6288c2ecf20Sopenharmony_ci case FZA_RING_RX_RRR_OK: 6298c2ecf20Sopenharmony_ci if ((rmc & FZA_RING_RX_CRC) != 0) 6308c2ecf20Sopenharmony_ci fp->stats.rx_crc_errors++; 6318c2ecf20Sopenharmony_ci else if ((rmc & FZA_RING_RX_FSC_MASK) == 0 || 6328c2ecf20Sopenharmony_ci (rmc & FZA_RING_RX_FSB_ERR) != 0) 6338c2ecf20Sopenharmony_ci fp->stats.rx_frame_errors++; 6348c2ecf20Sopenharmony_ci return 1; 6358c2ecf20Sopenharmony_ci case FZA_RING_RX_RRR_SADDR: 6368c2ecf20Sopenharmony_ci case FZA_RING_RX_RRR_DADDR: 6378c2ecf20Sopenharmony_ci case FZA_RING_RX_RRR_ABORT: 6388c2ecf20Sopenharmony_ci /* Halt the interface to trigger a reset. */ 6398c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_HALT, &fp->regs->control_a); 6408c2ecf20Sopenharmony_ci readw_o(&fp->regs->control_a); /* Synchronize. */ 6418c2ecf20Sopenharmony_ci return 1; 6428c2ecf20Sopenharmony_ci case FZA_RING_RX_RRR_LENGTH: 6438c2ecf20Sopenharmony_ci fp->stats.rx_frame_errors++; 6448c2ecf20Sopenharmony_ci return 1; 6458c2ecf20Sopenharmony_ci default: 6468c2ecf20Sopenharmony_ci return 1; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Packet received successfully; validate the length. */ 6518c2ecf20Sopenharmony_ci switch (fc & FDDI_FC_K_FORMAT_MASK) { 6528c2ecf20Sopenharmony_ci case FDDI_FC_K_FORMAT_MANAGEMENT: 6538c2ecf20Sopenharmony_ci if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_ASYNC) 6548c2ecf20Sopenharmony_ci min_len = 37; 6558c2ecf20Sopenharmony_ci else 6568c2ecf20Sopenharmony_ci min_len = 17; 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci case FDDI_FC_K_FORMAT_LLC: 6598c2ecf20Sopenharmony_ci min_len = 20; 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci default: 6628c2ecf20Sopenharmony_ci min_len = 17; 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci max_len = 4495; 6668c2ecf20Sopenharmony_ci if (len < min_len || len > max_len) { 6678c2ecf20Sopenharmony_ci fp->stats.rx_errors++; 6688c2ecf20Sopenharmony_ci fp->stats.rx_length_errors++; 6698c2ecf20Sopenharmony_ci return 1; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic void fza_rx(struct net_device *dev) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 6788c2ecf20Sopenharmony_ci struct sk_buff *skb, *newskb; 6798c2ecf20Sopenharmony_ci struct fza_fddihdr *frame; 6808c2ecf20Sopenharmony_ci dma_addr_t dma, newdma; 6818c2ecf20Sopenharmony_ci u32 own, rmc, buf; 6828c2ecf20Sopenharmony_ci int i, len; 6838c2ecf20Sopenharmony_ci u8 fc; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci while (1) { 6868c2ecf20Sopenharmony_ci i = fp->ring_hst_rx_index; 6878c2ecf20Sopenharmony_ci own = readl_o(&fp->ring_hst_rx[i].buf0_own); 6888c2ecf20Sopenharmony_ci if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci rmc = readl_u(&fp->ring_hst_rx[i].rmc); 6928c2ecf20Sopenharmony_ci skb = fp->rx_skbuff[i]; 6938c2ecf20Sopenharmony_ci dma = fp->rx_dma[i]; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci /* The RMC doesn't count the preamble and the starting 6968c2ecf20Sopenharmony_ci * delimiter. We fix it up here for a total of 3 octets. 6978c2ecf20Sopenharmony_ci */ 6988c2ecf20Sopenharmony_ci dma_rmb(); 6998c2ecf20Sopenharmony_ci len = (rmc & FZA_RING_PBC_MASK) + 3; 7008c2ecf20Sopenharmony_ci frame = (struct fza_fddihdr *)skb->data; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* We need to get at real FC. */ 7038c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(fp->bdev, 7048c2ecf20Sopenharmony_ci dma + 7058c2ecf20Sopenharmony_ci ((u8 *)&frame->hdr.fc - (u8 *)frame), 7068c2ecf20Sopenharmony_ci sizeof(frame->hdr.fc), 7078c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7088c2ecf20Sopenharmony_ci fc = frame->hdr.fc; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (fza_rx_err(fp, rmc, fc)) 7118c2ecf20Sopenharmony_ci goto err_rx; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* We have to 512-byte-align RX buffers... */ 7148c2ecf20Sopenharmony_ci newskb = fza_alloc_skb_irq(dev, FZA_RX_BUFFER_SIZE + 511); 7158c2ecf20Sopenharmony_ci if (newskb) { 7168c2ecf20Sopenharmony_ci fza_skb_align(newskb, 512); 7178c2ecf20Sopenharmony_ci newdma = dma_map_single(fp->bdev, newskb->data, 7188c2ecf20Sopenharmony_ci FZA_RX_BUFFER_SIZE, 7198c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7208c2ecf20Sopenharmony_ci if (dma_mapping_error(fp->bdev, newdma)) { 7218c2ecf20Sopenharmony_ci dev_kfree_skb_irq(newskb); 7228c2ecf20Sopenharmony_ci newskb = NULL; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci if (newskb) { 7268c2ecf20Sopenharmony_ci int pkt_len = len - 7; /* Omit P, SD and FCS. */ 7278c2ecf20Sopenharmony_ci int is_multi; 7288c2ecf20Sopenharmony_ci int rx_stat; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci dma_unmap_single(fp->bdev, dma, FZA_RX_BUFFER_SIZE, 7318c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* Queue SMT frames to the SMT receive ring. */ 7348c2ecf20Sopenharmony_ci if ((fc & (FDDI_FC_K_CLASS_MASK | 7358c2ecf20Sopenharmony_ci FDDI_FC_K_FORMAT_MASK)) == 7368c2ecf20Sopenharmony_ci (FDDI_FC_K_CLASS_ASYNC | 7378c2ecf20Sopenharmony_ci FDDI_FC_K_FORMAT_MANAGEMENT) && 7388c2ecf20Sopenharmony_ci (rmc & FZA_RING_RX_DA_MASK) != 7398c2ecf20Sopenharmony_ci FZA_RING_RX_DA_PROM) { 7408c2ecf20Sopenharmony_ci if (fza_do_recv_smt((struct fza_buffer_tx *) 7418c2ecf20Sopenharmony_ci skb->data, len, rmc, 7428c2ecf20Sopenharmony_ci dev)) { 7438c2ecf20Sopenharmony_ci writel_o(FZA_CONTROL_A_SMT_RX_OVFL, 7448c2ecf20Sopenharmony_ci &fp->regs->control_a); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci is_multi = ((frame->hdr.daddr[0] & 0x01) != 0); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci skb_reserve(skb, 3); /* Skip over P and SD. */ 7518c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); /* And cut off FCS. */ 7528c2ecf20Sopenharmony_ci skb->protocol = fddi_type_trans(skb, dev); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci rx_stat = netif_rx(skb); 7558c2ecf20Sopenharmony_ci if (rx_stat != NET_RX_DROP) { 7568c2ecf20Sopenharmony_ci fp->stats.rx_packets++; 7578c2ecf20Sopenharmony_ci fp->stats.rx_bytes += pkt_len; 7588c2ecf20Sopenharmony_ci if (is_multi) 7598c2ecf20Sopenharmony_ci fp->stats.multicast++; 7608c2ecf20Sopenharmony_ci } else { 7618c2ecf20Sopenharmony_ci fp->stats.rx_dropped++; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci skb = newskb; 7658c2ecf20Sopenharmony_ci dma = newdma; 7668c2ecf20Sopenharmony_ci fp->rx_skbuff[i] = skb; 7678c2ecf20Sopenharmony_ci fp->rx_dma[i] = dma; 7688c2ecf20Sopenharmony_ci } else { 7698c2ecf20Sopenharmony_ci fp->stats.rx_dropped++; 7708c2ecf20Sopenharmony_ci pr_notice("%s: memory squeeze, dropping packet\n", 7718c2ecf20Sopenharmony_ci fp->name); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cierr_rx: 7758c2ecf20Sopenharmony_ci writel_o(0, &fp->ring_hst_rx[i].rmc); 7768c2ecf20Sopenharmony_ci buf = (dma + 0x1000) >> 9; 7778c2ecf20Sopenharmony_ci writel_o(buf, &fp->ring_hst_rx[i].buffer1); 7788c2ecf20Sopenharmony_ci buf = dma >> 9 | FZA_RING_OWN_FZA; 7798c2ecf20Sopenharmony_ci writel_o(buf, &fp->ring_hst_rx[i].buf0_own); 7808c2ecf20Sopenharmony_ci fp->ring_hst_rx_index = 7818c2ecf20Sopenharmony_ci (fp->ring_hst_rx_index + 1) % fp->ring_hst_rx_size; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic void fza_tx_smt(struct net_device *dev) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 7888c2ecf20Sopenharmony_ci struct fza_buffer_tx __iomem *smt_tx_ptr; 7898c2ecf20Sopenharmony_ci int i, len; 7908c2ecf20Sopenharmony_ci u32 own; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci while (1) { 7938c2ecf20Sopenharmony_ci i = fp->ring_smt_tx_index; 7948c2ecf20Sopenharmony_ci own = readl_o(&fp->ring_smt_tx[i].own); 7958c2ecf20Sopenharmony_ci if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci smt_tx_ptr = fp->mmio + readl_u(&fp->ring_smt_tx[i].buffer); 7998c2ecf20Sopenharmony_ci len = readl_u(&fp->ring_smt_tx[i].rmc) & FZA_RING_PBC_MASK; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci if (!netif_queue_stopped(dev)) { 8028c2ecf20Sopenharmony_ci if (dev_nit_active(dev)) { 8038c2ecf20Sopenharmony_ci struct fza_buffer_tx *skb_data_ptr; 8048c2ecf20Sopenharmony_ci struct sk_buff *skb; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Length must be a multiple of 4 as only word 8078c2ecf20Sopenharmony_ci * reads are permitted! 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci skb = fza_alloc_skb_irq(dev, (len + 3) & ~3); 8108c2ecf20Sopenharmony_ci if (!skb) 8118c2ecf20Sopenharmony_ci goto err_no_skb; /* Drop. */ 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci skb_data_ptr = (struct fza_buffer_tx *) 8148c2ecf20Sopenharmony_ci skb->data; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci fza_reads(smt_tx_ptr, skb_data_ptr, 8178c2ecf20Sopenharmony_ci (len + 3) & ~3); 8188c2ecf20Sopenharmony_ci skb->dev = dev; 8198c2ecf20Sopenharmony_ci skb_reserve(skb, 3); /* Skip over PRH. */ 8208c2ecf20Sopenharmony_ci skb_put(skb, len - 3); 8218c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci dev_queue_xmit_nit(skb, dev); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_cierr_no_skb: 8288c2ecf20Sopenharmony_ci ; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* Queue the frame to the RMC transmit ring. */ 8328c2ecf20Sopenharmony_ci fza_do_xmit((union fza_buffer_txp) 8338c2ecf20Sopenharmony_ci { .mmio_ptr = smt_tx_ptr }, 8348c2ecf20Sopenharmony_ci len, dev, 1); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own); 8388c2ecf20Sopenharmony_ci fp->ring_smt_tx_index = 8398c2ecf20Sopenharmony_ci (fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic void fza_uns(struct net_device *dev) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 8468c2ecf20Sopenharmony_ci u32 own; 8478c2ecf20Sopenharmony_ci int i; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci while (1) { 8508c2ecf20Sopenharmony_ci i = fp->ring_uns_index; 8518c2ecf20Sopenharmony_ci own = readl_o(&fp->ring_uns[i].own); 8528c2ecf20Sopenharmony_ci if ((own & FZA_RING_OWN_MASK) == FZA_RING_OWN_FZA) 8538c2ecf20Sopenharmony_ci break; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (readl_u(&fp->ring_uns[i].id) == FZA_RING_UNS_RX_OVER) { 8568c2ecf20Sopenharmony_ci fp->stats.rx_errors++; 8578c2ecf20Sopenharmony_ci fp->stats.rx_over_errors++; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci writel_o(FZA_RING_OWN_FZA, &fp->ring_uns[i].own); 8618c2ecf20Sopenharmony_ci fp->ring_uns_index = 8628c2ecf20Sopenharmony_ci (fp->ring_uns_index + 1) % FZA_RING_UNS_SIZE; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic void fza_tx_flush(struct net_device *dev) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 8698c2ecf20Sopenharmony_ci u32 own; 8708c2ecf20Sopenharmony_ci int i; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Clean up the SMT TX ring. */ 8738c2ecf20Sopenharmony_ci i = fp->ring_smt_tx_index; 8748c2ecf20Sopenharmony_ci do { 8758c2ecf20Sopenharmony_ci writel_o(FZA_RING_OWN_FZA, &fp->ring_smt_tx[i].own); 8768c2ecf20Sopenharmony_ci fp->ring_smt_tx_index = 8778c2ecf20Sopenharmony_ci (fp->ring_smt_tx_index + 1) % fp->ring_smt_tx_size; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci } while (i != fp->ring_smt_tx_index); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* Clean up the RMC TX ring. */ 8828c2ecf20Sopenharmony_ci i = fp->ring_rmc_tx_index; 8838c2ecf20Sopenharmony_ci do { 8848c2ecf20Sopenharmony_ci own = readl_o(&fp->ring_rmc_tx[i].own); 8858c2ecf20Sopenharmony_ci if ((own & FZA_RING_OWN_MASK) == FZA_RING_TX_OWN_RMC) { 8868c2ecf20Sopenharmony_ci u32 rmc = readl_u(&fp->ring_rmc_tx[i].rmc); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci writel_u(rmc | FZA_RING_TX_DTP, 8898c2ecf20Sopenharmony_ci &fp->ring_rmc_tx[i].rmc); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci fp->ring_rmc_tx_index = 8928c2ecf20Sopenharmony_ci (fp->ring_rmc_tx_index + 1) % fp->ring_rmc_tx_size; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci } while (i != fp->ring_rmc_tx_index); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* Done. */ 8978c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_FLUSH_DONE, &fp->regs->control_a); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic irqreturn_t fza_interrupt(int irq, void *dev_id) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 9038c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 9048c2ecf20Sopenharmony_ci uint int_event; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* Get interrupt events. */ 9078c2ecf20Sopenharmony_ci int_event = readw_o(&fp->regs->int_event) & fp->int_mask; 9088c2ecf20Sopenharmony_ci if (int_event == 0) 9098c2ecf20Sopenharmony_ci return IRQ_NONE; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* Clear the events. */ 9128c2ecf20Sopenharmony_ci writew_u(int_event, &fp->regs->int_event); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci /* Now handle the events. The order matters. */ 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* Command finished interrupt. */ 9178c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_CMD_DONE) != 0) { 9188c2ecf20Sopenharmony_ci fp->irq_count_cmd_done++; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci spin_lock(&fp->lock); 9218c2ecf20Sopenharmony_ci fp->cmd_done_flag = 1; 9228c2ecf20Sopenharmony_ci wake_up(&fp->cmd_done_wait); 9238c2ecf20Sopenharmony_ci spin_unlock(&fp->lock); 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* Transmit finished interrupt. */ 9278c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_TX_DONE) != 0) { 9288c2ecf20Sopenharmony_ci fp->irq_count_tx_done++; 9298c2ecf20Sopenharmony_ci fza_tx(dev); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* Host receive interrupt. */ 9338c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_RX_POLL) != 0) { 9348c2ecf20Sopenharmony_ci fp->irq_count_rx_poll++; 9358c2ecf20Sopenharmony_ci fza_rx(dev); 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* SMT transmit interrupt. */ 9398c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_SMT_TX_POLL) != 0) { 9408c2ecf20Sopenharmony_ci fp->irq_count_smt_tx_poll++; 9418c2ecf20Sopenharmony_ci fza_tx_smt(dev); 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Transmit ring flush request. */ 9458c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_FLUSH_TX) != 0) { 9468c2ecf20Sopenharmony_ci fp->irq_count_flush_tx++; 9478c2ecf20Sopenharmony_ci fza_tx_flush(dev); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* Link status change interrupt. */ 9518c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_LINK_ST_CHG) != 0) { 9528c2ecf20Sopenharmony_ci uint status; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci fp->irq_count_link_st_chg++; 9558c2ecf20Sopenharmony_ci status = readw_u(&fp->regs->status); 9568c2ecf20Sopenharmony_ci if (FZA_STATUS_GET_LINK(status) == FZA_LINK_ON) { 9578c2ecf20Sopenharmony_ci netif_carrier_on(dev); 9588c2ecf20Sopenharmony_ci pr_info("%s: link available\n", fp->name); 9598c2ecf20Sopenharmony_ci } else { 9608c2ecf20Sopenharmony_ci netif_carrier_off(dev); 9618c2ecf20Sopenharmony_ci pr_info("%s: link unavailable\n", fp->name); 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Unsolicited event interrupt. */ 9668c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_UNS_POLL) != 0) { 9678c2ecf20Sopenharmony_ci fp->irq_count_uns_poll++; 9688c2ecf20Sopenharmony_ci fza_uns(dev); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* State change interrupt. */ 9728c2ecf20Sopenharmony_ci if ((int_event & FZA_EVENT_STATE_CHG) != 0) { 9738c2ecf20Sopenharmony_ci uint status, state; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci fp->irq_count_state_chg++; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci status = readw_u(&fp->regs->status); 9788c2ecf20Sopenharmony_ci state = FZA_STATUS_GET_STATE(status); 9798c2ecf20Sopenharmony_ci pr_debug("%s: state change: %x\n", fp->name, state); 9808c2ecf20Sopenharmony_ci switch (state) { 9818c2ecf20Sopenharmony_ci case FZA_STATE_RESET: 9828c2ecf20Sopenharmony_ci break; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci case FZA_STATE_UNINITIALIZED: 9858c2ecf20Sopenharmony_ci netif_carrier_off(dev); 9868c2ecf20Sopenharmony_ci del_timer_sync(&fp->reset_timer); 9878c2ecf20Sopenharmony_ci fp->ring_cmd_index = 0; 9888c2ecf20Sopenharmony_ci fp->ring_uns_index = 0; 9898c2ecf20Sopenharmony_ci fp->ring_rmc_tx_index = 0; 9908c2ecf20Sopenharmony_ci fp->ring_rmc_txd_index = 0; 9918c2ecf20Sopenharmony_ci fp->ring_hst_rx_index = 0; 9928c2ecf20Sopenharmony_ci fp->ring_smt_tx_index = 0; 9938c2ecf20Sopenharmony_ci fp->ring_smt_rx_index = 0; 9948c2ecf20Sopenharmony_ci if (fp->state > state) { 9958c2ecf20Sopenharmony_ci pr_info("%s: OK\n", fp->name); 9968c2ecf20Sopenharmony_ci fza_cmd_send(dev, FZA_RING_CMD_INIT); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci case FZA_STATE_INITIALIZED: 10018c2ecf20Sopenharmony_ci if (fp->state > state) { 10028c2ecf20Sopenharmony_ci fza_set_rx_mode(dev); 10038c2ecf20Sopenharmony_ci fza_cmd_send(dev, FZA_RING_CMD_PARAM); 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci break; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci case FZA_STATE_RUNNING: 10088c2ecf20Sopenharmony_ci case FZA_STATE_MAINTENANCE: 10098c2ecf20Sopenharmony_ci fp->state = state; 10108c2ecf20Sopenharmony_ci fza_rx_init(fp); 10118c2ecf20Sopenharmony_ci fp->queue_active = 1; 10128c2ecf20Sopenharmony_ci netif_wake_queue(dev); 10138c2ecf20Sopenharmony_ci pr_debug("%s: queue woken\n", fp->name); 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci case FZA_STATE_HALTED: 10178c2ecf20Sopenharmony_ci fp->queue_active = 0; 10188c2ecf20Sopenharmony_ci netif_stop_queue(dev); 10198c2ecf20Sopenharmony_ci pr_debug("%s: queue stopped\n", fp->name); 10208c2ecf20Sopenharmony_ci del_timer_sync(&fp->reset_timer); 10218c2ecf20Sopenharmony_ci pr_warn("%s: halted, reason: %x\n", fp->name, 10228c2ecf20Sopenharmony_ci FZA_STATUS_GET_HALT(status)); 10238c2ecf20Sopenharmony_ci fza_regs_dump(fp); 10248c2ecf20Sopenharmony_ci pr_info("%s: resetting the board...\n", fp->name); 10258c2ecf20Sopenharmony_ci fza_do_reset(fp); 10268c2ecf20Sopenharmony_ci fp->timer_state = 0; 10278c2ecf20Sopenharmony_ci fp->reset_timer.expires = jiffies + 45 * HZ; 10288c2ecf20Sopenharmony_ci add_timer(&fp->reset_timer); 10298c2ecf20Sopenharmony_ci break; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci default: 10328c2ecf20Sopenharmony_ci pr_warn("%s: undefined state: %x\n", fp->name, state); 10338c2ecf20Sopenharmony_ci break; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci spin_lock(&fp->lock); 10378c2ecf20Sopenharmony_ci fp->state_chg_flag = 1; 10388c2ecf20Sopenharmony_ci wake_up(&fp->state_chg_wait); 10398c2ecf20Sopenharmony_ci spin_unlock(&fp->lock); 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic void fza_reset_timer(struct timer_list *t) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct fza_private *fp = from_timer(fp, t, reset_timer); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (!fp->timer_state) { 10508c2ecf20Sopenharmony_ci pr_err("%s: RESET timed out!\n", fp->name); 10518c2ecf20Sopenharmony_ci pr_info("%s: trying harder...\n", fp->name); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* Assert the board reset. */ 10548c2ecf20Sopenharmony_ci writew_o(FZA_RESET_INIT, &fp->regs->reset); 10558c2ecf20Sopenharmony_ci readw_o(&fp->regs->reset); /* Synchronize. */ 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci fp->timer_state = 1; 10588c2ecf20Sopenharmony_ci fp->reset_timer.expires = jiffies + HZ; 10598c2ecf20Sopenharmony_ci } else { 10608c2ecf20Sopenharmony_ci /* Clear the board reset. */ 10618c2ecf20Sopenharmony_ci writew_u(FZA_RESET_CLR, &fp->regs->reset); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* Enable all interrupt events we handle. */ 10648c2ecf20Sopenharmony_ci writew_o(fp->int_mask, &fp->regs->int_mask); 10658c2ecf20Sopenharmony_ci readw_o(&fp->regs->int_mask); /* Synchronize. */ 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci fp->timer_state = 0; 10688c2ecf20Sopenharmony_ci fp->reset_timer.expires = jiffies + 45 * HZ; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci add_timer(&fp->reset_timer); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic int fza_set_mac_address(struct net_device *dev, void *addr) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistatic netdev_tx_t fza_start_xmit(struct sk_buff *skb, struct net_device *dev) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 10818c2ecf20Sopenharmony_ci unsigned int old_mask, new_mask; 10828c2ecf20Sopenharmony_ci int ret; 10838c2ecf20Sopenharmony_ci u8 fc; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci skb_push(skb, 3); /* Make room for PRH. */ 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* Decode FC to set PRH. */ 10888c2ecf20Sopenharmony_ci fc = skb->data[3]; 10898c2ecf20Sopenharmony_ci skb->data[0] = 0; 10908c2ecf20Sopenharmony_ci skb->data[1] = 0; 10918c2ecf20Sopenharmony_ci skb->data[2] = FZA_PRH2_NORMAL; 10928c2ecf20Sopenharmony_ci if ((fc & FDDI_FC_K_CLASS_MASK) == FDDI_FC_K_CLASS_SYNC) 10938c2ecf20Sopenharmony_ci skb->data[0] |= FZA_PRH0_FRAME_SYNC; 10948c2ecf20Sopenharmony_ci switch (fc & FDDI_FC_K_FORMAT_MASK) { 10958c2ecf20Sopenharmony_ci case FDDI_FC_K_FORMAT_MANAGEMENT: 10968c2ecf20Sopenharmony_ci if ((fc & FDDI_FC_K_CONTROL_MASK) == 0) { 10978c2ecf20Sopenharmony_ci /* Token. */ 10988c2ecf20Sopenharmony_ci skb->data[0] |= FZA_PRH0_TKN_TYPE_IMM; 10998c2ecf20Sopenharmony_ci skb->data[1] |= FZA_PRH1_TKN_SEND_NONE; 11008c2ecf20Sopenharmony_ci } else { 11018c2ecf20Sopenharmony_ci /* SMT or MAC. */ 11028c2ecf20Sopenharmony_ci skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 11038c2ecf20Sopenharmony_ci skb->data[1] |= FZA_PRH1_TKN_SEND_UNR; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci skb->data[1] |= FZA_PRH1_CRC_NORMAL; 11068c2ecf20Sopenharmony_ci break; 11078c2ecf20Sopenharmony_ci case FDDI_FC_K_FORMAT_LLC: 11088c2ecf20Sopenharmony_ci case FDDI_FC_K_FORMAT_FUTURE: 11098c2ecf20Sopenharmony_ci skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 11108c2ecf20Sopenharmony_ci skb->data[1] |= FZA_PRH1_CRC_NORMAL | FZA_PRH1_TKN_SEND_UNR; 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci case FDDI_FC_K_FORMAT_IMPLEMENTOR: 11138c2ecf20Sopenharmony_ci skb->data[0] |= FZA_PRH0_TKN_TYPE_UNR; 11148c2ecf20Sopenharmony_ci skb->data[1] |= FZA_PRH1_TKN_SEND_ORIG; 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* SMT transmit interrupts may sneak frames into the RMC 11198c2ecf20Sopenharmony_ci * transmit ring. We disable them while queueing a frame 11208c2ecf20Sopenharmony_ci * to maintain consistency. 11218c2ecf20Sopenharmony_ci */ 11228c2ecf20Sopenharmony_ci old_mask = fp->int_mask; 11238c2ecf20Sopenharmony_ci new_mask = old_mask & ~FZA_MASK_SMT_TX_POLL; 11248c2ecf20Sopenharmony_ci writew_u(new_mask, &fp->regs->int_mask); 11258c2ecf20Sopenharmony_ci readw_o(&fp->regs->int_mask); /* Synchronize. */ 11268c2ecf20Sopenharmony_ci fp->int_mask = new_mask; 11278c2ecf20Sopenharmony_ci ret = fza_do_xmit((union fza_buffer_txp) 11288c2ecf20Sopenharmony_ci { .data_ptr = (struct fza_buffer_tx *)skb->data }, 11298c2ecf20Sopenharmony_ci skb->len, dev, 0); 11308c2ecf20Sopenharmony_ci fp->int_mask = old_mask; 11318c2ecf20Sopenharmony_ci writew_u(fp->int_mask, &fp->regs->int_mask); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (ret) { 11348c2ecf20Sopenharmony_ci /* Probably an SMT packet filled the remaining space, 11358c2ecf20Sopenharmony_ci * so just stop the queue, but don't report it as an error. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci netif_stop_queue(dev); 11388c2ecf20Sopenharmony_ci pr_debug("%s: queue stopped\n", fp->name); 11398c2ecf20Sopenharmony_ci fp->stats.tx_dropped++; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return ret; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int fza_open(struct net_device *dev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 11508c2ecf20Sopenharmony_ci struct fza_ring_cmd __iomem *ring; 11518c2ecf20Sopenharmony_ci struct sk_buff *skb; 11528c2ecf20Sopenharmony_ci unsigned long flags; 11538c2ecf20Sopenharmony_ci dma_addr_t dma; 11548c2ecf20Sopenharmony_ci int ret, i; 11558c2ecf20Sopenharmony_ci u32 stat; 11568c2ecf20Sopenharmony_ci long t; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci for (i = 0; i < FZA_RING_RX_SIZE; i++) { 11598c2ecf20Sopenharmony_ci /* We have to 512-byte-align RX buffers... */ 11608c2ecf20Sopenharmony_ci skb = fza_alloc_skb(dev, FZA_RX_BUFFER_SIZE + 511); 11618c2ecf20Sopenharmony_ci if (skb) { 11628c2ecf20Sopenharmony_ci fza_skb_align(skb, 512); 11638c2ecf20Sopenharmony_ci dma = dma_map_single(fp->bdev, skb->data, 11648c2ecf20Sopenharmony_ci FZA_RX_BUFFER_SIZE, 11658c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 11668c2ecf20Sopenharmony_ci if (dma_mapping_error(fp->bdev, dma)) { 11678c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 11688c2ecf20Sopenharmony_ci skb = NULL; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci if (!skb) { 11728c2ecf20Sopenharmony_ci for (--i; i >= 0; i--) { 11738c2ecf20Sopenharmony_ci dma_unmap_single(fp->bdev, fp->rx_dma[i], 11748c2ecf20Sopenharmony_ci FZA_RX_BUFFER_SIZE, 11758c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 11768c2ecf20Sopenharmony_ci dev_kfree_skb(fp->rx_skbuff[i]); 11778c2ecf20Sopenharmony_ci fp->rx_dma[i] = 0; 11788c2ecf20Sopenharmony_ci fp->rx_skbuff[i] = NULL; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci return -ENOMEM; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci fp->rx_skbuff[i] = skb; 11838c2ecf20Sopenharmony_ci fp->rx_dma[i] = dma; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci ret = fza_init_send(dev, NULL); 11878c2ecf20Sopenharmony_ci if (ret != 0) 11888c2ecf20Sopenharmony_ci return ret; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Purger and Beacon multicasts need to be supplied before PARAM. */ 11918c2ecf20Sopenharmony_ci fza_set_rx_mode(dev); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci spin_lock_irqsave(&fp->lock, flags); 11948c2ecf20Sopenharmony_ci fp->cmd_done_flag = 0; 11958c2ecf20Sopenharmony_ci ring = fza_cmd_send(dev, FZA_RING_CMD_PARAM); 11968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fp->lock, flags); 11978c2ecf20Sopenharmony_ci if (!ring) 11988c2ecf20Sopenharmony_ci return -ENOBUFS; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci t = wait_event_timeout(fp->cmd_done_wait, fp->cmd_done_flag, 3 * HZ); 12018c2ecf20Sopenharmony_ci if (fp->cmd_done_flag == 0) { 12028c2ecf20Sopenharmony_ci pr_err("%s: PARAM command timed out!, state %x\n", fp->name, 12038c2ecf20Sopenharmony_ci FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 12048c2ecf20Sopenharmony_ci return -EIO; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci stat = readl_u(&ring->stat); 12078c2ecf20Sopenharmony_ci if (stat != FZA_RING_STAT_SUCCESS) { 12088c2ecf20Sopenharmony_ci pr_err("%s: PARAM command failed!, status %02x, state %x\n", 12098c2ecf20Sopenharmony_ci fp->name, stat, 12108c2ecf20Sopenharmony_ci FZA_STATUS_GET_STATE(readw_u(&fp->regs->status))); 12118c2ecf20Sopenharmony_ci return -EIO; 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci pr_debug("%s: PARAM: %lums elapsed\n", fp->name, 12148c2ecf20Sopenharmony_ci (3 * HZ - t) * 1000 / HZ); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int fza_close(struct net_device *dev) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 12228c2ecf20Sopenharmony_ci unsigned long flags; 12238c2ecf20Sopenharmony_ci uint state; 12248c2ecf20Sopenharmony_ci long t; 12258c2ecf20Sopenharmony_ci int i; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12288c2ecf20Sopenharmony_ci pr_debug("%s: queue stopped\n", fp->name); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci del_timer_sync(&fp->reset_timer); 12318c2ecf20Sopenharmony_ci spin_lock_irqsave(&fp->lock, flags); 12328c2ecf20Sopenharmony_ci fp->state = FZA_STATE_UNINITIALIZED; 12338c2ecf20Sopenharmony_ci fp->state_chg_flag = 0; 12348c2ecf20Sopenharmony_ci /* Shut the interface down. */ 12358c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_A_SHUT, &fp->regs->control_a); 12368c2ecf20Sopenharmony_ci readw_o(&fp->regs->control_a); /* Synchronize. */ 12378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fp->lock, flags); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci /* DEC says SHUT needs up to 10 seconds to complete. */ 12408c2ecf20Sopenharmony_ci t = wait_event_timeout(fp->state_chg_wait, fp->state_chg_flag, 12418c2ecf20Sopenharmony_ci 15 * HZ); 12428c2ecf20Sopenharmony_ci state = FZA_STATUS_GET_STATE(readw_o(&fp->regs->status)); 12438c2ecf20Sopenharmony_ci if (fp->state_chg_flag == 0) { 12448c2ecf20Sopenharmony_ci pr_err("%s: SHUT timed out!, state %x\n", fp->name, state); 12458c2ecf20Sopenharmony_ci return -EIO; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci if (state != FZA_STATE_UNINITIALIZED) { 12488c2ecf20Sopenharmony_ci pr_err("%s: SHUT failed!, state %x\n", fp->name, state); 12498c2ecf20Sopenharmony_ci return -EIO; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci pr_debug("%s: SHUT: %lums elapsed\n", fp->name, 12528c2ecf20Sopenharmony_ci (15 * HZ - t) * 1000 / HZ); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci for (i = 0; i < FZA_RING_RX_SIZE; i++) 12558c2ecf20Sopenharmony_ci if (fp->rx_skbuff[i]) { 12568c2ecf20Sopenharmony_ci dma_unmap_single(fp->bdev, fp->rx_dma[i], 12578c2ecf20Sopenharmony_ci FZA_RX_BUFFER_SIZE, DMA_FROM_DEVICE); 12588c2ecf20Sopenharmony_ci dev_kfree_skb(fp->rx_skbuff[i]); 12598c2ecf20Sopenharmony_ci fp->rx_dma[i] = 0; 12608c2ecf20Sopenharmony_ci fp->rx_skbuff[i] = NULL; 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic struct net_device_stats *fza_get_stats(struct net_device *dev) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci return &fp->stats; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic int fza_probe(struct device *bdev) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci static const struct net_device_ops netdev_ops = { 12768c2ecf20Sopenharmony_ci .ndo_open = fza_open, 12778c2ecf20Sopenharmony_ci .ndo_stop = fza_close, 12788c2ecf20Sopenharmony_ci .ndo_start_xmit = fza_start_xmit, 12798c2ecf20Sopenharmony_ci .ndo_set_rx_mode = fza_set_rx_mode, 12808c2ecf20Sopenharmony_ci .ndo_set_mac_address = fza_set_mac_address, 12818c2ecf20Sopenharmony_ci .ndo_get_stats = fza_get_stats, 12828c2ecf20Sopenharmony_ci }; 12838c2ecf20Sopenharmony_ci static int version_printed; 12848c2ecf20Sopenharmony_ci char rom_rev[4], fw_rev[4], rmc_rev[4]; 12858c2ecf20Sopenharmony_ci struct tc_dev *tdev = to_tc_dev(bdev); 12868c2ecf20Sopenharmony_ci struct fza_cmd_init __iomem *init; 12878c2ecf20Sopenharmony_ci resource_size_t start, len; 12888c2ecf20Sopenharmony_ci struct net_device *dev; 12898c2ecf20Sopenharmony_ci struct fza_private *fp; 12908c2ecf20Sopenharmony_ci uint smt_ver, pmd_type; 12918c2ecf20Sopenharmony_ci void __iomem *mmio; 12928c2ecf20Sopenharmony_ci uint hw_addr[2]; 12938c2ecf20Sopenharmony_ci int ret, i; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (!version_printed) { 12968c2ecf20Sopenharmony_ci pr_info("%s", version); 12978c2ecf20Sopenharmony_ci version_printed = 1; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci dev = alloc_fddidev(sizeof(*fp)); 13018c2ecf20Sopenharmony_ci if (!dev) 13028c2ecf20Sopenharmony_ci return -ENOMEM; 13038c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, bdev); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci fp = netdev_priv(dev); 13068c2ecf20Sopenharmony_ci dev_set_drvdata(bdev, dev); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci fp->bdev = bdev; 13098c2ecf20Sopenharmony_ci fp->name = dev_name(bdev); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* Request the I/O MEM resource. */ 13128c2ecf20Sopenharmony_ci start = tdev->resource.start; 13138c2ecf20Sopenharmony_ci len = tdev->resource.end - start + 1; 13148c2ecf20Sopenharmony_ci if (!request_mem_region(start, len, dev_name(bdev))) { 13158c2ecf20Sopenharmony_ci pr_err("%s: cannot reserve MMIO region\n", fp->name); 13168c2ecf20Sopenharmony_ci ret = -EBUSY; 13178c2ecf20Sopenharmony_ci goto err_out_kfree; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci /* MMIO mapping setup. */ 13218c2ecf20Sopenharmony_ci mmio = ioremap(start, len); 13228c2ecf20Sopenharmony_ci if (!mmio) { 13238c2ecf20Sopenharmony_ci pr_err("%s: cannot map MMIO\n", fp->name); 13248c2ecf20Sopenharmony_ci ret = -ENOMEM; 13258c2ecf20Sopenharmony_ci goto err_out_resource; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* Initialize the new device structure. */ 13298c2ecf20Sopenharmony_ci switch (loopback) { 13308c2ecf20Sopenharmony_ci case FZA_LOOP_NORMAL: 13318c2ecf20Sopenharmony_ci case FZA_LOOP_INTERN: 13328c2ecf20Sopenharmony_ci case FZA_LOOP_EXTERN: 13338c2ecf20Sopenharmony_ci break; 13348c2ecf20Sopenharmony_ci default: 13358c2ecf20Sopenharmony_ci loopback = FZA_LOOP_NORMAL; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci fp->mmio = mmio; 13398c2ecf20Sopenharmony_ci dev->irq = tdev->interrupt; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci pr_info("%s: DEC FDDIcontroller 700 or 700-C at 0x%08llx, irq %d\n", 13428c2ecf20Sopenharmony_ci fp->name, (long long)tdev->resource.start, dev->irq); 13438c2ecf20Sopenharmony_ci pr_debug("%s: mapped at: 0x%p\n", fp->name, mmio); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci fp->regs = mmio + FZA_REG_BASE; 13468c2ecf20Sopenharmony_ci fp->ring_cmd = mmio + FZA_RING_CMD; 13478c2ecf20Sopenharmony_ci fp->ring_uns = mmio + FZA_RING_UNS; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci init_waitqueue_head(&fp->state_chg_wait); 13508c2ecf20Sopenharmony_ci init_waitqueue_head(&fp->cmd_done_wait); 13518c2ecf20Sopenharmony_ci spin_lock_init(&fp->lock); 13528c2ecf20Sopenharmony_ci fp->int_mask = FZA_MASK_NORMAL; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci timer_setup(&fp->reset_timer, fza_reset_timer, 0); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* Sanitize the board. */ 13578c2ecf20Sopenharmony_ci fza_regs_dump(fp); 13588c2ecf20Sopenharmony_ci fza_do_shutdown(fp); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci ret = request_irq(dev->irq, fza_interrupt, IRQF_SHARED, fp->name, dev); 13618c2ecf20Sopenharmony_ci if (ret != 0) { 13628c2ecf20Sopenharmony_ci pr_err("%s: unable to get IRQ %d!\n", fp->name, dev->irq); 13638c2ecf20Sopenharmony_ci goto err_out_map; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* Enable the driver mode. */ 13678c2ecf20Sopenharmony_ci writew_o(FZA_CONTROL_B_DRIVER, &fp->regs->control_b); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci /* For some reason transmit done interrupts can trigger during 13708c2ecf20Sopenharmony_ci * reset. This avoids a division error in the handler. 13718c2ecf20Sopenharmony_ci */ 13728c2ecf20Sopenharmony_ci fp->ring_rmc_tx_size = FZA_RING_TX_SIZE; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci ret = fza_reset(fp); 13758c2ecf20Sopenharmony_ci if (ret != 0) 13768c2ecf20Sopenharmony_ci goto err_out_irq; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci ret = fza_init_send(dev, &init); 13798c2ecf20Sopenharmony_ci if (ret != 0) 13808c2ecf20Sopenharmony_ci goto err_out_irq; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci fza_reads(&init->hw_addr, &hw_addr, sizeof(hw_addr)); 13838c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, &hw_addr, FDDI_K_ALEN); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci fza_reads(&init->rom_rev, &rom_rev, sizeof(rom_rev)); 13868c2ecf20Sopenharmony_ci fza_reads(&init->fw_rev, &fw_rev, sizeof(fw_rev)); 13878c2ecf20Sopenharmony_ci fza_reads(&init->rmc_rev, &rmc_rev, sizeof(rmc_rev)); 13888c2ecf20Sopenharmony_ci for (i = 3; i >= 0 && rom_rev[i] == ' '; i--) 13898c2ecf20Sopenharmony_ci rom_rev[i] = 0; 13908c2ecf20Sopenharmony_ci for (i = 3; i >= 0 && fw_rev[i] == ' '; i--) 13918c2ecf20Sopenharmony_ci fw_rev[i] = 0; 13928c2ecf20Sopenharmony_ci for (i = 3; i >= 0 && rmc_rev[i] == ' '; i--) 13938c2ecf20Sopenharmony_ci rmc_rev[i] = 0; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci fp->ring_rmc_tx = mmio + readl_u(&init->rmc_tx); 13968c2ecf20Sopenharmony_ci fp->ring_rmc_tx_size = readl_u(&init->rmc_tx_size); 13978c2ecf20Sopenharmony_ci fp->ring_hst_rx = mmio + readl_u(&init->hst_rx); 13988c2ecf20Sopenharmony_ci fp->ring_hst_rx_size = readl_u(&init->hst_rx_size); 13998c2ecf20Sopenharmony_ci fp->ring_smt_tx = mmio + readl_u(&init->smt_tx); 14008c2ecf20Sopenharmony_ci fp->ring_smt_tx_size = readl_u(&init->smt_tx_size); 14018c2ecf20Sopenharmony_ci fp->ring_smt_rx = mmio + readl_u(&init->smt_rx); 14028c2ecf20Sopenharmony_ci fp->ring_smt_rx_size = readl_u(&init->smt_rx_size); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci fp->buffer_tx = mmio + FZA_TX_BUFFER_ADDR(readl_u(&init->rmc_tx)); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci fp->t_max = readl_u(&init->def_t_max); 14078c2ecf20Sopenharmony_ci fp->t_req = readl_u(&init->def_t_req); 14088c2ecf20Sopenharmony_ci fp->tvx = readl_u(&init->def_tvx); 14098c2ecf20Sopenharmony_ci fp->lem_threshold = readl_u(&init->lem_threshold); 14108c2ecf20Sopenharmony_ci fza_reads(&init->def_station_id, &fp->station_id, 14118c2ecf20Sopenharmony_ci sizeof(fp->station_id)); 14128c2ecf20Sopenharmony_ci fp->rtoken_timeout = readl_u(&init->rtoken_timeout); 14138c2ecf20Sopenharmony_ci fp->ring_purger = readl_u(&init->ring_purger); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci smt_ver = readl_u(&init->smt_ver); 14168c2ecf20Sopenharmony_ci pmd_type = readl_u(&init->pmd_type); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci pr_debug("%s: INIT parameters:\n", fp->name); 14198c2ecf20Sopenharmony_ci pr_debug(" tx_mode: %u\n", readl_u(&init->tx_mode)); 14208c2ecf20Sopenharmony_ci pr_debug(" hst_rx_size: %u\n", readl_u(&init->hst_rx_size)); 14218c2ecf20Sopenharmony_ci pr_debug(" rmc_rev: %.4s\n", rmc_rev); 14228c2ecf20Sopenharmony_ci pr_debug(" rom_rev: %.4s\n", rom_rev); 14238c2ecf20Sopenharmony_ci pr_debug(" fw_rev: %.4s\n", fw_rev); 14248c2ecf20Sopenharmony_ci pr_debug(" mop_type: %u\n", readl_u(&init->mop_type)); 14258c2ecf20Sopenharmony_ci pr_debug(" hst_rx: 0x%08x\n", readl_u(&init->hst_rx)); 14268c2ecf20Sopenharmony_ci pr_debug(" rmc_tx: 0x%08x\n", readl_u(&init->rmc_tx)); 14278c2ecf20Sopenharmony_ci pr_debug(" rmc_tx_size: %u\n", readl_u(&init->rmc_tx_size)); 14288c2ecf20Sopenharmony_ci pr_debug(" smt_tx: 0x%08x\n", readl_u(&init->smt_tx)); 14298c2ecf20Sopenharmony_ci pr_debug(" smt_tx_size: %u\n", readl_u(&init->smt_tx_size)); 14308c2ecf20Sopenharmony_ci pr_debug(" smt_rx: 0x%08x\n", readl_u(&init->smt_rx)); 14318c2ecf20Sopenharmony_ci pr_debug(" smt_rx_size: %u\n", readl_u(&init->smt_rx_size)); 14328c2ecf20Sopenharmony_ci /* TC systems are always LE, so don't bother swapping. */ 14338c2ecf20Sopenharmony_ci pr_debug(" hw_addr: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", 14348c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[0]) >> 0) & 0xff, 14358c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[0]) >> 8) & 0xff, 14368c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[0]) >> 16) & 0xff, 14378c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[0]) >> 24) & 0xff, 14388c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[1]) >> 0) & 0xff, 14398c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[1]) >> 8) & 0xff, 14408c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[1]) >> 16) & 0xff, 14418c2ecf20Sopenharmony_ci (readl_u(&init->hw_addr[1]) >> 24) & 0xff); 14428c2ecf20Sopenharmony_ci pr_debug(" def_t_req: %u\n", readl_u(&init->def_t_req)); 14438c2ecf20Sopenharmony_ci pr_debug(" def_tvx: %u\n", readl_u(&init->def_tvx)); 14448c2ecf20Sopenharmony_ci pr_debug(" def_t_max: %u\n", readl_u(&init->def_t_max)); 14458c2ecf20Sopenharmony_ci pr_debug(" lem_threshold: %u\n", readl_u(&init->lem_threshold)); 14468c2ecf20Sopenharmony_ci /* Don't bother swapping, see above. */ 14478c2ecf20Sopenharmony_ci pr_debug(" def_station_id: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", 14488c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[0]) >> 0) & 0xff, 14498c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[0]) >> 8) & 0xff, 14508c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[0]) >> 16) & 0xff, 14518c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[0]) >> 24) & 0xff, 14528c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[1]) >> 0) & 0xff, 14538c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[1]) >> 8) & 0xff, 14548c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[1]) >> 16) & 0xff, 14558c2ecf20Sopenharmony_ci (readl_u(&init->def_station_id[1]) >> 24) & 0xff); 14568c2ecf20Sopenharmony_ci pr_debug(" pmd_type_alt: %u\n", readl_u(&init->pmd_type_alt)); 14578c2ecf20Sopenharmony_ci pr_debug(" smt_ver: %u\n", readl_u(&init->smt_ver)); 14588c2ecf20Sopenharmony_ci pr_debug(" rtoken_timeout: %u\n", readl_u(&init->rtoken_timeout)); 14598c2ecf20Sopenharmony_ci pr_debug(" ring_purger: %u\n", readl_u(&init->ring_purger)); 14608c2ecf20Sopenharmony_ci pr_debug(" smt_ver_max: %u\n", readl_u(&init->smt_ver_max)); 14618c2ecf20Sopenharmony_ci pr_debug(" smt_ver_min: %u\n", readl_u(&init->smt_ver_min)); 14628c2ecf20Sopenharmony_ci pr_debug(" pmd_type: %u\n", readl_u(&init->pmd_type)); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci pr_info("%s: model %s, address %pMF\n", 14658c2ecf20Sopenharmony_ci fp->name, 14668c2ecf20Sopenharmony_ci pmd_type == FZA_PMD_TYPE_TW ? 14678c2ecf20Sopenharmony_ci "700-C (DEFZA-CA), ThinWire PMD selected" : 14688c2ecf20Sopenharmony_ci pmd_type == FZA_PMD_TYPE_STP ? 14698c2ecf20Sopenharmony_ci "700-C (DEFZA-CA), STP PMD selected" : 14708c2ecf20Sopenharmony_ci "700 (DEFZA-AA), MMF PMD", 14718c2ecf20Sopenharmony_ci dev->dev_addr); 14728c2ecf20Sopenharmony_ci pr_info("%s: ROM rev. %.4s, firmware rev. %.4s, RMC rev. %.4s, " 14738c2ecf20Sopenharmony_ci "SMT ver. %u\n", fp->name, rom_rev, fw_rev, rmc_rev, smt_ver); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* Now that we fetched initial parameters just shut the interface 14768c2ecf20Sopenharmony_ci * until opened. 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_ci ret = fza_close(dev); 14798c2ecf20Sopenharmony_ci if (ret != 0) 14808c2ecf20Sopenharmony_ci goto err_out_irq; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci /* The FZA-specific entries in the device structure. */ 14838c2ecf20Sopenharmony_ci dev->netdev_ops = &netdev_ops; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci ret = register_netdev(dev); 14868c2ecf20Sopenharmony_ci if (ret != 0) 14878c2ecf20Sopenharmony_ci goto err_out_irq; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci pr_info("%s: registered as %s\n", fp->name, dev->name); 14908c2ecf20Sopenharmony_ci fp->name = (const char *)dev->name; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci get_device(bdev); 14938c2ecf20Sopenharmony_ci return 0; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cierr_out_irq: 14968c2ecf20Sopenharmony_ci del_timer_sync(&fp->reset_timer); 14978c2ecf20Sopenharmony_ci fza_do_shutdown(fp); 14988c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_cierr_out_map: 15018c2ecf20Sopenharmony_ci iounmap(mmio); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cierr_out_resource: 15048c2ecf20Sopenharmony_ci release_mem_region(start, len); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_cierr_out_kfree: 15078c2ecf20Sopenharmony_ci pr_err("%s: initialization failure, aborting!\n", fp->name); 15088c2ecf20Sopenharmony_ci free_netdev(dev); 15098c2ecf20Sopenharmony_ci return ret; 15108c2ecf20Sopenharmony_ci} 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_cistatic int fza_remove(struct device *bdev) 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(bdev); 15158c2ecf20Sopenharmony_ci struct fza_private *fp = netdev_priv(dev); 15168c2ecf20Sopenharmony_ci struct tc_dev *tdev = to_tc_dev(bdev); 15178c2ecf20Sopenharmony_ci resource_size_t start, len; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci put_device(bdev); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci unregister_netdev(dev); 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci del_timer_sync(&fp->reset_timer); 15248c2ecf20Sopenharmony_ci fza_do_shutdown(fp); 15258c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci iounmap(fp->mmio); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci start = tdev->resource.start; 15308c2ecf20Sopenharmony_ci len = tdev->resource.end - start + 1; 15318c2ecf20Sopenharmony_ci release_mem_region(start, len); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci free_netdev(dev); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci return 0; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic struct tc_device_id const fza_tc_table[] = { 15398c2ecf20Sopenharmony_ci { "DEC ", "PMAF-AA " }, 15408c2ecf20Sopenharmony_ci { } 15418c2ecf20Sopenharmony_ci}; 15428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(tc, fza_tc_table); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_cistatic struct tc_driver fza_driver = { 15458c2ecf20Sopenharmony_ci .id_table = fza_tc_table, 15468c2ecf20Sopenharmony_ci .driver = { 15478c2ecf20Sopenharmony_ci .name = "defza", 15488c2ecf20Sopenharmony_ci .bus = &tc_bus_type, 15498c2ecf20Sopenharmony_ci .probe = fza_probe, 15508c2ecf20Sopenharmony_ci .remove = fza_remove, 15518c2ecf20Sopenharmony_ci }, 15528c2ecf20Sopenharmony_ci}; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_cistatic int fza_init(void) 15558c2ecf20Sopenharmony_ci{ 15568c2ecf20Sopenharmony_ci return tc_register_driver(&fza_driver); 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic void fza_exit(void) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci tc_unregister_driver(&fza_driver); 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cimodule_init(fza_init); 15658c2ecf20Sopenharmony_cimodule_exit(fza_exit); 1566