18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci#define VERSION "0.23" 38c2ecf20Sopenharmony_ci/* ns83820.c by Benjamin LaHaise with contributions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Questions/comments/discussion to linux-ns83820@kvack.org. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * $Revision: 1.34.2.23 $ 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright 2001 Benjamin LaHaise. 108c2ecf20Sopenharmony_ci * Copyright 2001, 2002 Red Hat. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Mmmm, chocolate vanilla mocha... 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * ChangeLog 158c2ecf20Sopenharmony_ci * ========= 168c2ecf20Sopenharmony_ci * 20010414 0.1 - created 178c2ecf20Sopenharmony_ci * 20010622 0.2 - basic rx and tx. 188c2ecf20Sopenharmony_ci * 20010711 0.3 - added duplex and link state detection support. 198c2ecf20Sopenharmony_ci * 20010713 0.4 - zero copy, no hangs. 208c2ecf20Sopenharmony_ci * 0.5 - 64 bit dma support (davem will hate me for this) 218c2ecf20Sopenharmony_ci * - disable jumbo frames to avoid tx hangs 228c2ecf20Sopenharmony_ci * - work around tx deadlocks on my 1.02 card via 238c2ecf20Sopenharmony_ci * fiddling with TXCFG 248c2ecf20Sopenharmony_ci * 20010810 0.6 - use pci dma api for ringbuffers, work on ia64 258c2ecf20Sopenharmony_ci * 20010816 0.7 - misc cleanups 268c2ecf20Sopenharmony_ci * 20010826 0.8 - fix critical zero copy bugs 278c2ecf20Sopenharmony_ci * 0.9 - internal experiment 288c2ecf20Sopenharmony_ci * 20010827 0.10 - fix ia64 unaligned access. 298c2ecf20Sopenharmony_ci * 20010906 0.11 - accept all packets with checksum errors as 308c2ecf20Sopenharmony_ci * otherwise fragments get lost 318c2ecf20Sopenharmony_ci * - fix >> 32 bugs 328c2ecf20Sopenharmony_ci * 0.12 - add statistics counters 338c2ecf20Sopenharmony_ci * - add allmulti/promisc support 348c2ecf20Sopenharmony_ci * 20011009 0.13 - hotplug support, other smaller pci api cleanups 358c2ecf20Sopenharmony_ci * 20011204 0.13a - optical transceiver support added 368c2ecf20Sopenharmony_ci * by Michael Clark <michael@metaparadigm.com> 378c2ecf20Sopenharmony_ci * 20011205 0.13b - call register_netdev earlier in initialization 388c2ecf20Sopenharmony_ci * suppress duplicate link status messages 398c2ecf20Sopenharmony_ci * 20011117 0.14 - ethtool GDRVINFO, GLINK support from jgarzik 408c2ecf20Sopenharmony_ci * 20011204 0.15 get ppc (big endian) working 418c2ecf20Sopenharmony_ci * 20011218 0.16 various cleanups 428c2ecf20Sopenharmony_ci * 20020310 0.17 speedups 438c2ecf20Sopenharmony_ci * 20020610 0.18 - actually use the pci dma api for highmem 448c2ecf20Sopenharmony_ci * - remove pci latency register fiddling 458c2ecf20Sopenharmony_ci * 0.19 - better bist support 468c2ecf20Sopenharmony_ci * - add ihr and reset_phy parameters 478c2ecf20Sopenharmony_ci * - gmii bus probing 488c2ecf20Sopenharmony_ci * - fix missed txok introduced during performance 498c2ecf20Sopenharmony_ci * tuning 508c2ecf20Sopenharmony_ci * 0.20 - fix stupid RFEN thinko. i am such a smurf. 518c2ecf20Sopenharmony_ci * 20040828 0.21 - add hardware vlan accleration 528c2ecf20Sopenharmony_ci * by Neil Horman <nhorman@redhat.com> 538c2ecf20Sopenharmony_ci * 20050406 0.22 - improved DAC ifdefs from Andi Kleen 548c2ecf20Sopenharmony_ci * - removal of dead code from Adrian Bunk 558c2ecf20Sopenharmony_ci * - fix half duplex collision behaviour 568c2ecf20Sopenharmony_ci * Driver Overview 578c2ecf20Sopenharmony_ci * =============== 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * This driver was originally written for the National Semiconductor 608c2ecf20Sopenharmony_ci * 83820 chip, a 10/100/1000 Mbps 64 bit PCI ethernet NIC. Hopefully 618c2ecf20Sopenharmony_ci * this code will turn out to be a) clean, b) correct, and c) fast. 628c2ecf20Sopenharmony_ci * With that in mind, I'm aiming to split the code up as much as 638c2ecf20Sopenharmony_ci * reasonably possible. At present there are X major sections that 648c2ecf20Sopenharmony_ci * break down into a) packet receive, b) packet transmit, c) link 658c2ecf20Sopenharmony_ci * management, d) initialization and configuration. Where possible, 668c2ecf20Sopenharmony_ci * these code paths are designed to run in parallel. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * This driver has been tested and found to work with the following 698c2ecf20Sopenharmony_ci * cards (in no particular order): 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Cameo SOHO-GA2000T SOHO-GA2500T 728c2ecf20Sopenharmony_ci * D-Link DGE-500T 738c2ecf20Sopenharmony_ci * PureData PDP8023Z-TG 748c2ecf20Sopenharmony_ci * SMC SMC9452TX SMC9462TX 758c2ecf20Sopenharmony_ci * Netgear GA621 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * Special thanks to SMC for providing hardware to test this driver on. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Reports of success or failure would be greatly appreciated. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci//#define dprintk printk 828c2ecf20Sopenharmony_ci#define dprintk(x...) do { } while (0) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#include <linux/module.h> 858c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 868c2ecf20Sopenharmony_ci#include <linux/types.h> 878c2ecf20Sopenharmony_ci#include <linux/pci.h> 888c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 898c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 908c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 918c2ecf20Sopenharmony_ci#include <linux/delay.h> 928c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 938c2ecf20Sopenharmony_ci#include <linux/init.h> 948c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 958c2ecf20Sopenharmony_ci#include <linux/ip.h> /* for iph */ 968c2ecf20Sopenharmony_ci#include <linux/in.h> /* for IPPROTO_... */ 978c2ecf20Sopenharmony_ci#include <linux/compiler.h> 988c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 998c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 1008c2ecf20Sopenharmony_ci#include <linux/sched.h> 1018c2ecf20Sopenharmony_ci#include <linux/timer.h> 1028c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 1038c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 1048c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 1058c2ecf20Sopenharmony_ci#include <linux/slab.h> 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#include <asm/io.h> 1088c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define DRV_NAME "ns83820" 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Global parameters. See module_param near the bottom. */ 1138c2ecf20Sopenharmony_cistatic int ihr = 2; 1148c2ecf20Sopenharmony_cistatic int reset_phy = 0; 1158c2ecf20Sopenharmony_cistatic int lnksts = 0; /* CFG_LNKSTS bit polarity */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* Dprintk is used for more interesting debug events */ 1188c2ecf20Sopenharmony_ci#undef Dprintk 1198c2ecf20Sopenharmony_ci#define Dprintk dprintk 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* tunables */ 1228c2ecf20Sopenharmony_ci#define RX_BUF_SIZE 1500 /* 8192 */ 1238c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VLAN_8021Q) 1248c2ecf20Sopenharmony_ci#define NS83820_VLAN_ACCEL_SUPPORT 1258c2ecf20Sopenharmony_ci#endif 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* Must not exceed ~65000. */ 1288c2ecf20Sopenharmony_ci#define NR_RX_DESC 64 1298c2ecf20Sopenharmony_ci#define NR_TX_DESC 128 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* not tunable */ 1328c2ecf20Sopenharmony_ci#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14) /* rx/tx mac addr + type */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define MIN_TX_DESC_FREE 8 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* register defines */ 1378c2ecf20Sopenharmony_ci#define CFGCS 0x04 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define CR_TXE 0x00000001 1408c2ecf20Sopenharmony_ci#define CR_TXD 0x00000002 1418c2ecf20Sopenharmony_ci/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE 1428c2ecf20Sopenharmony_ci * The Receive engine skips one descriptor and moves 1438c2ecf20Sopenharmony_ci * onto the next one!! */ 1448c2ecf20Sopenharmony_ci#define CR_RXE 0x00000004 1458c2ecf20Sopenharmony_ci#define CR_RXD 0x00000008 1468c2ecf20Sopenharmony_ci#define CR_TXR 0x00000010 1478c2ecf20Sopenharmony_ci#define CR_RXR 0x00000020 1488c2ecf20Sopenharmony_ci#define CR_SWI 0x00000080 1498c2ecf20Sopenharmony_ci#define CR_RST 0x00000100 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci#define PTSCR_EEBIST_FAIL 0x00000001 1528c2ecf20Sopenharmony_ci#define PTSCR_EEBIST_EN 0x00000002 1538c2ecf20Sopenharmony_ci#define PTSCR_EELOAD_EN 0x00000004 1548c2ecf20Sopenharmony_ci#define PTSCR_RBIST_FAIL 0x000001b8 1558c2ecf20Sopenharmony_ci#define PTSCR_RBIST_DONE 0x00000200 1568c2ecf20Sopenharmony_ci#define PTSCR_RBIST_EN 0x00000400 1578c2ecf20Sopenharmony_ci#define PTSCR_RBIST_RST 0x00002000 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define MEAR_EEDI 0x00000001 1608c2ecf20Sopenharmony_ci#define MEAR_EEDO 0x00000002 1618c2ecf20Sopenharmony_ci#define MEAR_EECLK 0x00000004 1628c2ecf20Sopenharmony_ci#define MEAR_EESEL 0x00000008 1638c2ecf20Sopenharmony_ci#define MEAR_MDIO 0x00000010 1648c2ecf20Sopenharmony_ci#define MEAR_MDDIR 0x00000020 1658c2ecf20Sopenharmony_ci#define MEAR_MDC 0x00000040 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define ISR_TXDESC3 0x40000000 1688c2ecf20Sopenharmony_ci#define ISR_TXDESC2 0x20000000 1698c2ecf20Sopenharmony_ci#define ISR_TXDESC1 0x10000000 1708c2ecf20Sopenharmony_ci#define ISR_TXDESC0 0x08000000 1718c2ecf20Sopenharmony_ci#define ISR_RXDESC3 0x04000000 1728c2ecf20Sopenharmony_ci#define ISR_RXDESC2 0x02000000 1738c2ecf20Sopenharmony_ci#define ISR_RXDESC1 0x01000000 1748c2ecf20Sopenharmony_ci#define ISR_RXDESC0 0x00800000 1758c2ecf20Sopenharmony_ci#define ISR_TXRCMP 0x00400000 1768c2ecf20Sopenharmony_ci#define ISR_RXRCMP 0x00200000 1778c2ecf20Sopenharmony_ci#define ISR_DPERR 0x00100000 1788c2ecf20Sopenharmony_ci#define ISR_SSERR 0x00080000 1798c2ecf20Sopenharmony_ci#define ISR_RMABT 0x00040000 1808c2ecf20Sopenharmony_ci#define ISR_RTABT 0x00020000 1818c2ecf20Sopenharmony_ci#define ISR_RXSOVR 0x00010000 1828c2ecf20Sopenharmony_ci#define ISR_HIBINT 0x00008000 1838c2ecf20Sopenharmony_ci#define ISR_PHY 0x00004000 1848c2ecf20Sopenharmony_ci#define ISR_PME 0x00002000 1858c2ecf20Sopenharmony_ci#define ISR_SWI 0x00001000 1868c2ecf20Sopenharmony_ci#define ISR_MIB 0x00000800 1878c2ecf20Sopenharmony_ci#define ISR_TXURN 0x00000400 1888c2ecf20Sopenharmony_ci#define ISR_TXIDLE 0x00000200 1898c2ecf20Sopenharmony_ci#define ISR_TXERR 0x00000100 1908c2ecf20Sopenharmony_ci#define ISR_TXDESC 0x00000080 1918c2ecf20Sopenharmony_ci#define ISR_TXOK 0x00000040 1928c2ecf20Sopenharmony_ci#define ISR_RXORN 0x00000020 1938c2ecf20Sopenharmony_ci#define ISR_RXIDLE 0x00000010 1948c2ecf20Sopenharmony_ci#define ISR_RXEARLY 0x00000008 1958c2ecf20Sopenharmony_ci#define ISR_RXERR 0x00000004 1968c2ecf20Sopenharmony_ci#define ISR_RXDESC 0x00000002 1978c2ecf20Sopenharmony_ci#define ISR_RXOK 0x00000001 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#define TXCFG_CSI 0x80000000 2008c2ecf20Sopenharmony_ci#define TXCFG_HBI 0x40000000 2018c2ecf20Sopenharmony_ci#define TXCFG_MLB 0x20000000 2028c2ecf20Sopenharmony_ci#define TXCFG_ATP 0x10000000 2038c2ecf20Sopenharmony_ci#define TXCFG_ECRETRY 0x00800000 2048c2ecf20Sopenharmony_ci#define TXCFG_BRST_DIS 0x00080000 2058c2ecf20Sopenharmony_ci#define TXCFG_MXDMA1024 0x00000000 2068c2ecf20Sopenharmony_ci#define TXCFG_MXDMA512 0x00700000 2078c2ecf20Sopenharmony_ci#define TXCFG_MXDMA256 0x00600000 2088c2ecf20Sopenharmony_ci#define TXCFG_MXDMA128 0x00500000 2098c2ecf20Sopenharmony_ci#define TXCFG_MXDMA64 0x00400000 2108c2ecf20Sopenharmony_ci#define TXCFG_MXDMA32 0x00300000 2118c2ecf20Sopenharmony_ci#define TXCFG_MXDMA16 0x00200000 2128c2ecf20Sopenharmony_ci#define TXCFG_MXDMA8 0x00100000 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#define CFG_LNKSTS 0x80000000 2158c2ecf20Sopenharmony_ci#define CFG_SPDSTS 0x60000000 2168c2ecf20Sopenharmony_ci#define CFG_SPDSTS1 0x40000000 2178c2ecf20Sopenharmony_ci#define CFG_SPDSTS0 0x20000000 2188c2ecf20Sopenharmony_ci#define CFG_DUPSTS 0x10000000 2198c2ecf20Sopenharmony_ci#define CFG_TBI_EN 0x01000000 2208c2ecf20Sopenharmony_ci#define CFG_MODE_1000 0x00400000 2218c2ecf20Sopenharmony_ci/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy. 2228c2ecf20Sopenharmony_ci * Read the Phy response and then configure the MAC accordingly */ 2238c2ecf20Sopenharmony_ci#define CFG_AUTO_1000 0x00200000 2248c2ecf20Sopenharmony_ci#define CFG_PINT_CTL 0x001c0000 2258c2ecf20Sopenharmony_ci#define CFG_PINT_DUPSTS 0x00100000 2268c2ecf20Sopenharmony_ci#define CFG_PINT_LNKSTS 0x00080000 2278c2ecf20Sopenharmony_ci#define CFG_PINT_SPDSTS 0x00040000 2288c2ecf20Sopenharmony_ci#define CFG_TMRTEST 0x00020000 2298c2ecf20Sopenharmony_ci#define CFG_MRM_DIS 0x00010000 2308c2ecf20Sopenharmony_ci#define CFG_MWI_DIS 0x00008000 2318c2ecf20Sopenharmony_ci#define CFG_T64ADDR 0x00004000 2328c2ecf20Sopenharmony_ci#define CFG_PCI64_DET 0x00002000 2338c2ecf20Sopenharmony_ci#define CFG_DATA64_EN 0x00001000 2348c2ecf20Sopenharmony_ci#define CFG_M64ADDR 0x00000800 2358c2ecf20Sopenharmony_ci#define CFG_PHY_RST 0x00000400 2368c2ecf20Sopenharmony_ci#define CFG_PHY_DIS 0x00000200 2378c2ecf20Sopenharmony_ci#define CFG_EXTSTS_EN 0x00000100 2388c2ecf20Sopenharmony_ci#define CFG_REQALG 0x00000080 2398c2ecf20Sopenharmony_ci#define CFG_SB 0x00000040 2408c2ecf20Sopenharmony_ci#define CFG_POW 0x00000020 2418c2ecf20Sopenharmony_ci#define CFG_EXD 0x00000010 2428c2ecf20Sopenharmony_ci#define CFG_PESEL 0x00000008 2438c2ecf20Sopenharmony_ci#define CFG_BROM_DIS 0x00000004 2448c2ecf20Sopenharmony_ci#define CFG_EXT_125 0x00000002 2458c2ecf20Sopenharmony_ci#define CFG_BEM 0x00000001 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define EXTSTS_UDPPKT 0x00200000 2488c2ecf20Sopenharmony_ci#define EXTSTS_TCPPKT 0x00080000 2498c2ecf20Sopenharmony_ci#define EXTSTS_IPPKT 0x00020000 2508c2ecf20Sopenharmony_ci#define EXTSTS_VPKT 0x00010000 2518c2ecf20Sopenharmony_ci#define EXTSTS_VTG_MASK 0x0000ffff 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0)) 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci#define MIBC_MIBS 0x00000008 2568c2ecf20Sopenharmony_ci#define MIBC_ACLR 0x00000004 2578c2ecf20Sopenharmony_ci#define MIBC_FRZ 0x00000002 2588c2ecf20Sopenharmony_ci#define MIBC_WRN 0x00000001 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#define PCR_PSEN (1 << 31) 2618c2ecf20Sopenharmony_ci#define PCR_PS_MCAST (1 << 30) 2628c2ecf20Sopenharmony_ci#define PCR_PS_DA (1 << 29) 2638c2ecf20Sopenharmony_ci#define PCR_STHI_8 (3 << 23) 2648c2ecf20Sopenharmony_ci#define PCR_STLO_4 (1 << 23) 2658c2ecf20Sopenharmony_ci#define PCR_FFHI_8K (3 << 21) 2668c2ecf20Sopenharmony_ci#define PCR_FFLO_4K (1 << 21) 2678c2ecf20Sopenharmony_ci#define PCR_PAUSE_CNT 0xFFFE 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci#define RXCFG_AEP 0x80000000 2708c2ecf20Sopenharmony_ci#define RXCFG_ARP 0x40000000 2718c2ecf20Sopenharmony_ci#define RXCFG_STRIPCRC 0x20000000 2728c2ecf20Sopenharmony_ci#define RXCFG_RX_FD 0x10000000 2738c2ecf20Sopenharmony_ci#define RXCFG_ALP 0x08000000 2748c2ecf20Sopenharmony_ci#define RXCFG_AIRL 0x04000000 2758c2ecf20Sopenharmony_ci#define RXCFG_MXDMA512 0x00700000 2768c2ecf20Sopenharmony_ci#define RXCFG_DRTH 0x0000003e 2778c2ecf20Sopenharmony_ci#define RXCFG_DRTH0 0x00000002 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci#define RFCR_RFEN 0x80000000 2808c2ecf20Sopenharmony_ci#define RFCR_AAB 0x40000000 2818c2ecf20Sopenharmony_ci#define RFCR_AAM 0x20000000 2828c2ecf20Sopenharmony_ci#define RFCR_AAU 0x10000000 2838c2ecf20Sopenharmony_ci#define RFCR_APM 0x08000000 2848c2ecf20Sopenharmony_ci#define RFCR_APAT 0x07800000 2858c2ecf20Sopenharmony_ci#define RFCR_APAT3 0x04000000 2868c2ecf20Sopenharmony_ci#define RFCR_APAT2 0x02000000 2878c2ecf20Sopenharmony_ci#define RFCR_APAT1 0x01000000 2888c2ecf20Sopenharmony_ci#define RFCR_APAT0 0x00800000 2898c2ecf20Sopenharmony_ci#define RFCR_AARP 0x00400000 2908c2ecf20Sopenharmony_ci#define RFCR_MHEN 0x00200000 2918c2ecf20Sopenharmony_ci#define RFCR_UHEN 0x00100000 2928c2ecf20Sopenharmony_ci#define RFCR_ULM 0x00080000 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#define VRCR_RUDPE 0x00000080 2958c2ecf20Sopenharmony_ci#define VRCR_RTCPE 0x00000040 2968c2ecf20Sopenharmony_ci#define VRCR_RIPE 0x00000020 2978c2ecf20Sopenharmony_ci#define VRCR_IPEN 0x00000010 2988c2ecf20Sopenharmony_ci#define VRCR_DUTF 0x00000008 2998c2ecf20Sopenharmony_ci#define VRCR_DVTF 0x00000004 3008c2ecf20Sopenharmony_ci#define VRCR_VTREN 0x00000002 3018c2ecf20Sopenharmony_ci#define VRCR_VTDEN 0x00000001 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci#define VTCR_PPCHK 0x00000008 3048c2ecf20Sopenharmony_ci#define VTCR_GCHK 0x00000004 3058c2ecf20Sopenharmony_ci#define VTCR_VPPTI 0x00000002 3068c2ecf20Sopenharmony_ci#define VTCR_VGTI 0x00000001 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#define CR 0x00 3098c2ecf20Sopenharmony_ci#define CFG 0x04 3108c2ecf20Sopenharmony_ci#define MEAR 0x08 3118c2ecf20Sopenharmony_ci#define PTSCR 0x0c 3128c2ecf20Sopenharmony_ci#define ISR 0x10 3138c2ecf20Sopenharmony_ci#define IMR 0x14 3148c2ecf20Sopenharmony_ci#define IER 0x18 3158c2ecf20Sopenharmony_ci#define IHR 0x1c 3168c2ecf20Sopenharmony_ci#define TXDP 0x20 3178c2ecf20Sopenharmony_ci#define TXDP_HI 0x24 3188c2ecf20Sopenharmony_ci#define TXCFG 0x28 3198c2ecf20Sopenharmony_ci#define GPIOR 0x2c 3208c2ecf20Sopenharmony_ci#define RXDP 0x30 3218c2ecf20Sopenharmony_ci#define RXDP_HI 0x34 3228c2ecf20Sopenharmony_ci#define RXCFG 0x38 3238c2ecf20Sopenharmony_ci#define PQCR 0x3c 3248c2ecf20Sopenharmony_ci#define WCSR 0x40 3258c2ecf20Sopenharmony_ci#define PCR 0x44 3268c2ecf20Sopenharmony_ci#define RFCR 0x48 3278c2ecf20Sopenharmony_ci#define RFDR 0x4c 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci#define SRR 0x58 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci#define VRCR 0xbc 3328c2ecf20Sopenharmony_ci#define VTCR 0xc0 3338c2ecf20Sopenharmony_ci#define VDR 0xc4 3348c2ecf20Sopenharmony_ci#define CCSR 0xcc 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci#define TBICR 0xe0 3378c2ecf20Sopenharmony_ci#define TBISR 0xe4 3388c2ecf20Sopenharmony_ci#define TANAR 0xe8 3398c2ecf20Sopenharmony_ci#define TANLPAR 0xec 3408c2ecf20Sopenharmony_ci#define TANER 0xf0 3418c2ecf20Sopenharmony_ci#define TESR 0xf4 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci#define TBICR_MR_AN_ENABLE 0x00001000 3448c2ecf20Sopenharmony_ci#define TBICR_MR_RESTART_AN 0x00000200 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci#define TBISR_MR_LINK_STATUS 0x00000020 3478c2ecf20Sopenharmony_ci#define TBISR_MR_AN_COMPLETE 0x00000004 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci#define TANAR_PS2 0x00000100 3508c2ecf20Sopenharmony_ci#define TANAR_PS1 0x00000080 3518c2ecf20Sopenharmony_ci#define TANAR_HALF_DUP 0x00000040 3528c2ecf20Sopenharmony_ci#define TANAR_FULL_DUP 0x00000020 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci#define GPIOR_GP5_OE 0x00000200 3558c2ecf20Sopenharmony_ci#define GPIOR_GP4_OE 0x00000100 3568c2ecf20Sopenharmony_ci#define GPIOR_GP3_OE 0x00000080 3578c2ecf20Sopenharmony_ci#define GPIOR_GP2_OE 0x00000040 3588c2ecf20Sopenharmony_ci#define GPIOR_GP1_OE 0x00000020 3598c2ecf20Sopenharmony_ci#define GPIOR_GP3_OUT 0x00000004 3608c2ecf20Sopenharmony_ci#define GPIOR_GP1_OUT 0x00000001 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define LINK_AUTONEGOTIATE 0x01 3638c2ecf20Sopenharmony_ci#define LINK_DOWN 0x02 3648c2ecf20Sopenharmony_ci#define LINK_UP 0x04 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci#define HW_ADDR_LEN sizeof(dma_addr_t) 3678c2ecf20Sopenharmony_ci#define desc_addr_set(desc, addr) \ 3688c2ecf20Sopenharmony_ci do { \ 3698c2ecf20Sopenharmony_ci ((desc)[0] = cpu_to_le32(addr)); \ 3708c2ecf20Sopenharmony_ci if (HW_ADDR_LEN == 8) \ 3718c2ecf20Sopenharmony_ci (desc)[1] = cpu_to_le32(((u64)addr) >> 32); \ 3728c2ecf20Sopenharmony_ci } while(0) 3738c2ecf20Sopenharmony_ci#define desc_addr_get(desc) \ 3748c2ecf20Sopenharmony_ci (le32_to_cpu((desc)[0]) | \ 3758c2ecf20Sopenharmony_ci (HW_ADDR_LEN == 8 ? ((dma_addr_t)le32_to_cpu((desc)[1]))<<32 : 0)) 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci#define DESC_LINK 0 3788c2ecf20Sopenharmony_ci#define DESC_BUFPTR (DESC_LINK + HW_ADDR_LEN/4) 3798c2ecf20Sopenharmony_ci#define DESC_CMDSTS (DESC_BUFPTR + HW_ADDR_LEN/4) 3808c2ecf20Sopenharmony_ci#define DESC_EXTSTS (DESC_CMDSTS + 4/4) 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci#define CMDSTS_OWN 0x80000000 3838c2ecf20Sopenharmony_ci#define CMDSTS_MORE 0x40000000 3848c2ecf20Sopenharmony_ci#define CMDSTS_INTR 0x20000000 3858c2ecf20Sopenharmony_ci#define CMDSTS_ERR 0x10000000 3868c2ecf20Sopenharmony_ci#define CMDSTS_OK 0x08000000 3878c2ecf20Sopenharmony_ci#define CMDSTS_RUNT 0x00200000 3888c2ecf20Sopenharmony_ci#define CMDSTS_LEN_MASK 0x0000ffff 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci#define CMDSTS_DEST_MASK 0x01800000 3918c2ecf20Sopenharmony_ci#define CMDSTS_DEST_SELF 0x00800000 3928c2ecf20Sopenharmony_ci#define CMDSTS_DEST_MULTI 0x01000000 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci#define DESC_SIZE 8 /* Should be cache line sized */ 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistruct rx_info { 3978c2ecf20Sopenharmony_ci spinlock_t lock; 3988c2ecf20Sopenharmony_ci int up; 3998c2ecf20Sopenharmony_ci unsigned long idle; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci struct sk_buff *skbs[NR_RX_DESC]; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci __le32 *next_rx_desc; 4048c2ecf20Sopenharmony_ci u16 next_rx, next_empty; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci __le32 *descs; 4078c2ecf20Sopenharmony_ci dma_addr_t phy_descs; 4088c2ecf20Sopenharmony_ci}; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistruct ns83820 { 4128c2ecf20Sopenharmony_ci u8 __iomem *base; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 4158c2ecf20Sopenharmony_ci struct net_device *ndev; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci struct rx_info rx_info; 4188c2ecf20Sopenharmony_ci struct tasklet_struct rx_tasklet; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci unsigned ihr; 4218c2ecf20Sopenharmony_ci struct work_struct tq_refill; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* protects everything below. irqsave when using. */ 4248c2ecf20Sopenharmony_ci spinlock_t misc_lock; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci u32 CFG_cache; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci u32 MEAR_cache; 4298c2ecf20Sopenharmony_ci u32 IMR_cache; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci unsigned linkstate; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci spinlock_t tx_lock; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci u16 tx_done_idx; 4368c2ecf20Sopenharmony_ci u16 tx_idx; 4378c2ecf20Sopenharmony_ci volatile u16 tx_free_idx; /* idx of free desc chain */ 4388c2ecf20Sopenharmony_ci u16 tx_intr_idx; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci atomic_t nr_tx_skbs; 4418c2ecf20Sopenharmony_ci struct sk_buff *tx_skbs[NR_TX_DESC]; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci char pad[16] __attribute__((aligned(16))); 4448c2ecf20Sopenharmony_ci __le32 *tx_descs; 4458c2ecf20Sopenharmony_ci dma_addr_t tx_phy_descs; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci struct timer_list tx_watchdog; 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic inline struct ns83820 *PRIV(struct net_device *dev) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci return netdev_priv(dev); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci#define __kick_rx(dev) writel(CR_RXE, dev->base + CR) 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic inline void kick_rx(struct net_device *ndev) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 4608c2ecf20Sopenharmony_ci dprintk("kick_rx: maybe kicking\n"); 4618c2ecf20Sopenharmony_ci if (test_and_clear_bit(0, &dev->rx_info.idle)) { 4628c2ecf20Sopenharmony_ci dprintk("actually kicking\n"); 4638c2ecf20Sopenharmony_ci writel(dev->rx_info.phy_descs + 4648c2ecf20Sopenharmony_ci (4 * DESC_SIZE * dev->rx_info.next_rx), 4658c2ecf20Sopenharmony_ci dev->base + RXDP); 4668c2ecf20Sopenharmony_ci if (dev->rx_info.next_rx == dev->rx_info.next_empty) 4678c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: uh-oh: next_rx == next_empty???\n", 4688c2ecf20Sopenharmony_ci ndev->name); 4698c2ecf20Sopenharmony_ci __kick_rx(dev); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci//free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC 4748c2ecf20Sopenharmony_ci#define start_tx_okay(dev) \ 4758c2ecf20Sopenharmony_ci (((NR_TX_DESC-2 + dev->tx_done_idx - dev->tx_free_idx) % NR_TX_DESC) > MIN_TX_DESC_FREE) 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/* Packet Receiver 4788c2ecf20Sopenharmony_ci * 4798c2ecf20Sopenharmony_ci * The hardware supports linked lists of receive descriptors for 4808c2ecf20Sopenharmony_ci * which ownership is transferred back and forth by means of an 4818c2ecf20Sopenharmony_ci * ownership bit. While the hardware does support the use of a 4828c2ecf20Sopenharmony_ci * ring for receive descriptors, we only make use of a chain in 4838c2ecf20Sopenharmony_ci * an attempt to reduce bus traffic under heavy load scenarios. 4848c2ecf20Sopenharmony_ci * This will also make bugs a bit more obvious. The current code 4858c2ecf20Sopenharmony_ci * only makes use of a single rx chain; I hope to implement 4868c2ecf20Sopenharmony_ci * priority based rx for version 1.0. Goal: even under overload 4878c2ecf20Sopenharmony_ci * conditions, still route realtime traffic with as low jitter as 4888c2ecf20Sopenharmony_ci * possible. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_cistatic inline void build_rx_desc(struct ns83820 *dev, __le32 *desc, dma_addr_t link, dma_addr_t buf, u32 cmdsts, u32 extsts) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci desc_addr_set(desc + DESC_LINK, link); 4938c2ecf20Sopenharmony_ci desc_addr_set(desc + DESC_BUFPTR, buf); 4948c2ecf20Sopenharmony_ci desc[DESC_EXTSTS] = cpu_to_le32(extsts); 4958c2ecf20Sopenharmony_ci mb(); 4968c2ecf20Sopenharmony_ci desc[DESC_CMDSTS] = cpu_to_le32(cmdsts); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci#define nr_rx_empty(dev) ((NR_RX_DESC-2 + dev->rx_info.next_rx - dev->rx_info.next_empty) % NR_RX_DESC) 5008c2ecf20Sopenharmony_cistatic inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci unsigned next_empty; 5038c2ecf20Sopenharmony_ci u32 cmdsts; 5048c2ecf20Sopenharmony_ci __le32 *sg; 5058c2ecf20Sopenharmony_ci dma_addr_t buf; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci next_empty = dev->rx_info.next_empty; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* don't overrun last rx marker */ 5108c2ecf20Sopenharmony_ci if (unlikely(nr_rx_empty(dev) <= 2)) { 5118c2ecf20Sopenharmony_ci kfree_skb(skb); 5128c2ecf20Sopenharmony_ci return 1; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci#if 0 5168c2ecf20Sopenharmony_ci dprintk("next_empty[%d] nr_used[%d] next_rx[%d]\n", 5178c2ecf20Sopenharmony_ci dev->rx_info.next_empty, 5188c2ecf20Sopenharmony_ci dev->rx_info.nr_used, 5198c2ecf20Sopenharmony_ci dev->rx_info.next_rx 5208c2ecf20Sopenharmony_ci ); 5218c2ecf20Sopenharmony_ci#endif 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci sg = dev->rx_info.descs + (next_empty * DESC_SIZE); 5248c2ecf20Sopenharmony_ci BUG_ON(NULL != dev->rx_info.skbs[next_empty]); 5258c2ecf20Sopenharmony_ci dev->rx_info.skbs[next_empty] = skb; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC; 5288c2ecf20Sopenharmony_ci cmdsts = REAL_RX_BUF_SIZE | CMDSTS_INTR; 5298c2ecf20Sopenharmony_ci buf = dma_map_single(&dev->pci_dev->dev, skb->data, REAL_RX_BUF_SIZE, 5308c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5318c2ecf20Sopenharmony_ci build_rx_desc(dev, sg, 0, buf, cmdsts, 0); 5328c2ecf20Sopenharmony_ci /* update link of previous rx */ 5338c2ecf20Sopenharmony_ci if (likely(next_empty != dev->rx_info.next_rx)) 5348c2ecf20Sopenharmony_ci dev->rx_info.descs[((NR_RX_DESC + next_empty - 1) % NR_RX_DESC) * DESC_SIZE] = cpu_to_le32(dev->rx_info.phy_descs + (next_empty * DESC_SIZE * 4)); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic inline int rx_refill(struct net_device *ndev, gfp_t gfp) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 5428c2ecf20Sopenharmony_ci unsigned i; 5438c2ecf20Sopenharmony_ci unsigned long flags = 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (unlikely(nr_rx_empty(dev) <= 2)) 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci dprintk("rx_refill(%p)\n", ndev); 5498c2ecf20Sopenharmony_ci if (gfp == GFP_ATOMIC) 5508c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->rx_info.lock, flags); 5518c2ecf20Sopenharmony_ci for (i=0; i<NR_RX_DESC; i++) { 5528c2ecf20Sopenharmony_ci struct sk_buff *skb; 5538c2ecf20Sopenharmony_ci long res; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* extra 16 bytes for alignment */ 5568c2ecf20Sopenharmony_ci skb = __netdev_alloc_skb(ndev, REAL_RX_BUF_SIZE+16, gfp); 5578c2ecf20Sopenharmony_ci if (unlikely(!skb)) 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci skb_reserve(skb, skb->data - PTR_ALIGN(skb->data, 16)); 5618c2ecf20Sopenharmony_ci if (gfp != GFP_ATOMIC) 5628c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->rx_info.lock, flags); 5638c2ecf20Sopenharmony_ci res = ns83820_add_rx_skb(dev, skb); 5648c2ecf20Sopenharmony_ci if (gfp != GFP_ATOMIC) 5658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->rx_info.lock, flags); 5668c2ecf20Sopenharmony_ci if (res) { 5678c2ecf20Sopenharmony_ci i = 1; 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci if (gfp == GFP_ATOMIC) 5728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->rx_info.lock, flags); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return i ? 0 : -ENOMEM; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void rx_refill_atomic(struct net_device *ndev) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci rx_refill(ndev, GFP_ATOMIC); 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/* REFILL */ 5838c2ecf20Sopenharmony_cistatic inline void queue_refill(struct work_struct *work) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct ns83820 *dev = container_of(work, struct ns83820, tq_refill); 5868c2ecf20Sopenharmony_ci struct net_device *ndev = dev->ndev; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci rx_refill(ndev, GFP_KERNEL); 5898c2ecf20Sopenharmony_ci if (dev->rx_info.up) 5908c2ecf20Sopenharmony_ci kick_rx(ndev); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic inline void clear_rx_desc(struct ns83820 *dev, unsigned i) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci build_rx_desc(dev, dev->rx_info.descs + (DESC_SIZE * i), 0, 0, CMDSTS_OWN, 0); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void phy_intr(struct net_device *ndev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 6018c2ecf20Sopenharmony_ci static const char *speeds[] = { "10", "100", "1000", "1000(?)", "1000F" }; 6028c2ecf20Sopenharmony_ci u32 cfg, new_cfg; 6038c2ecf20Sopenharmony_ci u32 tanar, tanlpar; 6048c2ecf20Sopenharmony_ci int speed, fullduplex, newlinkstate; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (dev->CFG_cache & CFG_TBI_EN) { 6098c2ecf20Sopenharmony_ci u32 __maybe_unused tbisr; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* we have an optical transceiver */ 6128c2ecf20Sopenharmony_ci tbisr = readl(dev->base + TBISR); 6138c2ecf20Sopenharmony_ci tanar = readl(dev->base + TANAR); 6148c2ecf20Sopenharmony_ci tanlpar = readl(dev->base + TANLPAR); 6158c2ecf20Sopenharmony_ci dprintk("phy_intr: tbisr=%08x, tanar=%08x, tanlpar=%08x\n", 6168c2ecf20Sopenharmony_ci tbisr, tanar, tanlpar); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if ( (fullduplex = (tanlpar & TANAR_FULL_DUP) && 6198c2ecf20Sopenharmony_ci (tanar & TANAR_FULL_DUP)) ) { 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* both of us are full duplex */ 6228c2ecf20Sopenharmony_ci writel(readl(dev->base + TXCFG) 6238c2ecf20Sopenharmony_ci | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, 6248c2ecf20Sopenharmony_ci dev->base + TXCFG); 6258c2ecf20Sopenharmony_ci writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, 6268c2ecf20Sopenharmony_ci dev->base + RXCFG); 6278c2ecf20Sopenharmony_ci /* Light up full duplex LED */ 6288c2ecf20Sopenharmony_ci writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT, 6298c2ecf20Sopenharmony_ci dev->base + GPIOR); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci } else if (((tanlpar & TANAR_HALF_DUP) && 6328c2ecf20Sopenharmony_ci (tanar & TANAR_HALF_DUP)) || 6338c2ecf20Sopenharmony_ci ((tanlpar & TANAR_FULL_DUP) && 6348c2ecf20Sopenharmony_ci (tanar & TANAR_HALF_DUP)) || 6358c2ecf20Sopenharmony_ci ((tanlpar & TANAR_HALF_DUP) && 6368c2ecf20Sopenharmony_ci (tanar & TANAR_FULL_DUP))) { 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* one or both of us are half duplex */ 6398c2ecf20Sopenharmony_ci writel((readl(dev->base + TXCFG) 6408c2ecf20Sopenharmony_ci & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP, 6418c2ecf20Sopenharmony_ci dev->base + TXCFG); 6428c2ecf20Sopenharmony_ci writel(readl(dev->base + RXCFG) & ~RXCFG_RX_FD, 6438c2ecf20Sopenharmony_ci dev->base + RXCFG); 6448c2ecf20Sopenharmony_ci /* Turn off full duplex LED */ 6458c2ecf20Sopenharmony_ci writel(readl(dev->base + GPIOR) & ~GPIOR_GP1_OUT, 6468c2ecf20Sopenharmony_ci dev->base + GPIOR); 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci speed = 4; /* 1000F */ 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci } else { 6528c2ecf20Sopenharmony_ci /* we have a copper transceiver */ 6538c2ecf20Sopenharmony_ci new_cfg = dev->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (cfg & CFG_SPDSTS1) 6568c2ecf20Sopenharmony_ci new_cfg |= CFG_MODE_1000; 6578c2ecf20Sopenharmony_ci else 6588c2ecf20Sopenharmony_ci new_cfg &= ~CFG_MODE_1000; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci speed = ((cfg / CFG_SPDSTS0) & 3); 6618c2ecf20Sopenharmony_ci fullduplex = (cfg & CFG_DUPSTS); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (fullduplex) { 6648c2ecf20Sopenharmony_ci new_cfg |= CFG_SB; 6658c2ecf20Sopenharmony_ci writel(readl(dev->base + TXCFG) 6668c2ecf20Sopenharmony_ci | TXCFG_CSI | TXCFG_HBI, 6678c2ecf20Sopenharmony_ci dev->base + TXCFG); 6688c2ecf20Sopenharmony_ci writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, 6698c2ecf20Sopenharmony_ci dev->base + RXCFG); 6708c2ecf20Sopenharmony_ci } else { 6718c2ecf20Sopenharmony_ci writel(readl(dev->base + TXCFG) 6728c2ecf20Sopenharmony_ci & ~(TXCFG_CSI | TXCFG_HBI), 6738c2ecf20Sopenharmony_ci dev->base + TXCFG); 6748c2ecf20Sopenharmony_ci writel(readl(dev->base + RXCFG) & ~(RXCFG_RX_FD), 6758c2ecf20Sopenharmony_ci dev->base + RXCFG); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if ((cfg & CFG_LNKSTS) && 6798c2ecf20Sopenharmony_ci ((new_cfg ^ dev->CFG_cache) != 0)) { 6808c2ecf20Sopenharmony_ci writel(new_cfg, dev->base + CFG); 6818c2ecf20Sopenharmony_ci dev->CFG_cache = new_cfg; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci dev->CFG_cache &= ~CFG_SPDSTS; 6858c2ecf20Sopenharmony_ci dev->CFG_cache |= cfg & CFG_SPDSTS; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (newlinkstate & LINK_UP && 6918c2ecf20Sopenharmony_ci dev->linkstate != newlinkstate) { 6928c2ecf20Sopenharmony_ci netif_start_queue(ndev); 6938c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 6948c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: link now %s mbps, %s duplex and up.\n", 6958c2ecf20Sopenharmony_ci ndev->name, 6968c2ecf20Sopenharmony_ci speeds[speed], 6978c2ecf20Sopenharmony_ci fullduplex ? "full" : "half"); 6988c2ecf20Sopenharmony_ci } else if (newlinkstate & LINK_DOWN && 6998c2ecf20Sopenharmony_ci dev->linkstate != newlinkstate) { 7008c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 7018c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: link now down.\n", ndev->name); 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci dev->linkstate = newlinkstate; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int ns83820_setup_rx(struct net_device *ndev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 7108c2ecf20Sopenharmony_ci unsigned i; 7118c2ecf20Sopenharmony_ci int ret; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci dprintk("ns83820_setup_rx(%p)\n", ndev); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci dev->rx_info.idle = 1; 7168c2ecf20Sopenharmony_ci dev->rx_info.next_rx = 0; 7178c2ecf20Sopenharmony_ci dev->rx_info.next_rx_desc = dev->rx_info.descs; 7188c2ecf20Sopenharmony_ci dev->rx_info.next_empty = 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci for (i=0; i<NR_RX_DESC; i++) 7218c2ecf20Sopenharmony_ci clear_rx_desc(dev, i); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci writel(0, dev->base + RXDP_HI); 7248c2ecf20Sopenharmony_ci writel(dev->rx_info.phy_descs, dev->base + RXDP); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci ret = rx_refill(ndev, GFP_KERNEL); 7278c2ecf20Sopenharmony_ci if (!ret) { 7288c2ecf20Sopenharmony_ci dprintk("starting receiver\n"); 7298c2ecf20Sopenharmony_ci /* prevent the interrupt handler from stomping on us */ 7308c2ecf20Sopenharmony_ci spin_lock_irq(&dev->rx_info.lock); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci writel(0x0001, dev->base + CCSR); 7338c2ecf20Sopenharmony_ci writel(0, dev->base + RFCR); 7348c2ecf20Sopenharmony_ci writel(0x7fc00000, dev->base + RFCR); 7358c2ecf20Sopenharmony_ci writel(0xffc00000, dev->base + RFCR); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci dev->rx_info.up = 1; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci phy_intr(ndev); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Okay, let it rip */ 7428c2ecf20Sopenharmony_ci spin_lock(&dev->misc_lock); 7438c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_PHY; 7448c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_RXRCMP; 7458c2ecf20Sopenharmony_ci //dev->IMR_cache |= ISR_RXERR; 7468c2ecf20Sopenharmony_ci //dev->IMR_cache |= ISR_RXOK; 7478c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_RXORN; 7488c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_RXSOVR; 7498c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_RXDESC; 7508c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_RXIDLE; 7518c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_TXDESC; 7528c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_TXIDLE; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci writel(dev->IMR_cache, dev->base + IMR); 7558c2ecf20Sopenharmony_ci writel(1, dev->base + IER); 7568c2ecf20Sopenharmony_ci spin_unlock(&dev->misc_lock); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci kick_rx(ndev); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->rx_info.lock); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci return ret; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic void ns83820_cleanup_rx(struct ns83820 *dev) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci unsigned i; 7688c2ecf20Sopenharmony_ci unsigned long flags; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci dprintk("ns83820_cleanup_rx(%p)\n", dev); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* disable receive interrupts */ 7738c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->misc_lock, flags); 7748c2ecf20Sopenharmony_ci dev->IMR_cache &= ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | ISR_RXIDLE); 7758c2ecf20Sopenharmony_ci writel(dev->IMR_cache, dev->base + IMR); 7768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->misc_lock, flags); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* synchronize with the interrupt handler and kill it */ 7798c2ecf20Sopenharmony_ci dev->rx_info.up = 0; 7808c2ecf20Sopenharmony_ci synchronize_irq(dev->pci_dev->irq); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* touch the pci bus... */ 7838c2ecf20Sopenharmony_ci readl(dev->base + IMR); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* assumes the transmitter is already disabled and reset */ 7868c2ecf20Sopenharmony_ci writel(0, dev->base + RXDP_HI); 7878c2ecf20Sopenharmony_ci writel(0, dev->base + RXDP); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (i=0; i<NR_RX_DESC; i++) { 7908c2ecf20Sopenharmony_ci struct sk_buff *skb = dev->rx_info.skbs[i]; 7918c2ecf20Sopenharmony_ci dev->rx_info.skbs[i] = NULL; 7928c2ecf20Sopenharmony_ci clear_rx_desc(dev, i); 7938c2ecf20Sopenharmony_ci kfree_skb(skb); 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic void ns83820_rx_kick(struct net_device *ndev) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 8008c2ecf20Sopenharmony_ci /*if (nr_rx_empty(dev) >= NR_RX_DESC/4)*/ { 8018c2ecf20Sopenharmony_ci if (dev->rx_info.up) { 8028c2ecf20Sopenharmony_ci rx_refill_atomic(ndev); 8038c2ecf20Sopenharmony_ci kick_rx(ndev); 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4) 8088c2ecf20Sopenharmony_ci schedule_work(&dev->tq_refill); 8098c2ecf20Sopenharmony_ci else 8108c2ecf20Sopenharmony_ci kick_rx(ndev); 8118c2ecf20Sopenharmony_ci if (dev->rx_info.idle) 8128c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: BAD\n", ndev->name); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/* rx_irq 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_cistatic void rx_irq(struct net_device *ndev) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 8218c2ecf20Sopenharmony_ci struct rx_info *info = &dev->rx_info; 8228c2ecf20Sopenharmony_ci unsigned next_rx; 8238c2ecf20Sopenharmony_ci int rx_rc, len; 8248c2ecf20Sopenharmony_ci u32 cmdsts; 8258c2ecf20Sopenharmony_ci __le32 *desc; 8268c2ecf20Sopenharmony_ci unsigned long flags; 8278c2ecf20Sopenharmony_ci int nr = 0; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci dprintk("rx_irq(%p)\n", ndev); 8308c2ecf20Sopenharmony_ci dprintk("rxdp: %08x, descs: %08lx next_rx[%d]: %p next_empty[%d]: %p\n", 8318c2ecf20Sopenharmony_ci readl(dev->base + RXDP), 8328c2ecf20Sopenharmony_ci (long)(dev->rx_info.phy_descs), 8338c2ecf20Sopenharmony_ci (int)dev->rx_info.next_rx, 8348c2ecf20Sopenharmony_ci (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_rx)), 8358c2ecf20Sopenharmony_ci (int)dev->rx_info.next_empty, 8368c2ecf20Sopenharmony_ci (dev->rx_info.descs + (DESC_SIZE * dev->rx_info.next_empty)) 8378c2ecf20Sopenharmony_ci ); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->lock, flags); 8408c2ecf20Sopenharmony_ci if (!info->up) 8418c2ecf20Sopenharmony_ci goto out; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci dprintk("walking descs\n"); 8448c2ecf20Sopenharmony_ci next_rx = info->next_rx; 8458c2ecf20Sopenharmony_ci desc = info->next_rx_desc; 8468c2ecf20Sopenharmony_ci while ((CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) && 8478c2ecf20Sopenharmony_ci (cmdsts != CMDSTS_OWN)) { 8488c2ecf20Sopenharmony_ci struct sk_buff *skb; 8498c2ecf20Sopenharmony_ci u32 extsts = le32_to_cpu(desc[DESC_EXTSTS]); 8508c2ecf20Sopenharmony_ci dma_addr_t bufptr = desc_addr_get(desc + DESC_BUFPTR); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci dprintk("cmdsts: %08x\n", cmdsts); 8538c2ecf20Sopenharmony_ci dprintk("link: %08x\n", cpu_to_le32(desc[DESC_LINK])); 8548c2ecf20Sopenharmony_ci dprintk("extsts: %08x\n", extsts); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci skb = info->skbs[next_rx]; 8578c2ecf20Sopenharmony_ci info->skbs[next_rx] = NULL; 8588c2ecf20Sopenharmony_ci info->next_rx = (next_rx + 1) % NR_RX_DESC; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci mb(); 8618c2ecf20Sopenharmony_ci clear_rx_desc(dev, next_rx); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci dma_unmap_single(&dev->pci_dev->dev, bufptr, RX_BUF_SIZE, 8648c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 8658c2ecf20Sopenharmony_ci len = cmdsts & CMDSTS_LEN_MASK; 8668c2ecf20Sopenharmony_ci#ifdef NS83820_VLAN_ACCEL_SUPPORT 8678c2ecf20Sopenharmony_ci /* NH: As was mentioned below, this chip is kinda 8688c2ecf20Sopenharmony_ci * brain dead about vlan tag stripping. Frames 8698c2ecf20Sopenharmony_ci * that are 64 bytes with a vlan header appended 8708c2ecf20Sopenharmony_ci * like arp frames, or pings, are flagged as Runts 8718c2ecf20Sopenharmony_ci * when the tag is stripped and hardware. This 8728c2ecf20Sopenharmony_ci * also means that the OK bit in the descriptor 8738c2ecf20Sopenharmony_ci * is cleared when the frame comes in so we have 8748c2ecf20Sopenharmony_ci * to do a specific length check here to make sure 8758c2ecf20Sopenharmony_ci * the frame would have been ok, had we not stripped 8768c2ecf20Sopenharmony_ci * the tag. 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci if (likely((CMDSTS_OK & cmdsts) || 8798c2ecf20Sopenharmony_ci ((cmdsts & CMDSTS_RUNT) && len >= 56))) { 8808c2ecf20Sopenharmony_ci#else 8818c2ecf20Sopenharmony_ci if (likely(CMDSTS_OK & cmdsts)) { 8828c2ecf20Sopenharmony_ci#endif 8838c2ecf20Sopenharmony_ci skb_put(skb, len); 8848c2ecf20Sopenharmony_ci if (unlikely(!skb)) 8858c2ecf20Sopenharmony_ci goto netdev_mangle_me_harder_failed; 8868c2ecf20Sopenharmony_ci if (cmdsts & CMDSTS_DEST_MULTI) 8878c2ecf20Sopenharmony_ci ndev->stats.multicast++; 8888c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 8898c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += len; 8908c2ecf20Sopenharmony_ci if ((extsts & 0x002a0000) && !(extsts & 0x00540000)) { 8918c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 8928c2ecf20Sopenharmony_ci } else { 8938c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 8968c2ecf20Sopenharmony_ci#ifdef NS83820_VLAN_ACCEL_SUPPORT 8978c2ecf20Sopenharmony_ci if(extsts & EXTSTS_VPKT) { 8988c2ecf20Sopenharmony_ci unsigned short tag; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci tag = ntohs(extsts & EXTSTS_VTG_MASK); 9018c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_IPV6), tag); 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci#endif 9048c2ecf20Sopenharmony_ci rx_rc = netif_rx(skb); 9058c2ecf20Sopenharmony_ci if (NET_RX_DROP == rx_rc) { 9068c2ecf20Sopenharmony_cinetdev_mangle_me_harder_failed: 9078c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci } else { 9108c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci nr++; 9148c2ecf20Sopenharmony_ci next_rx = info->next_rx; 9158c2ecf20Sopenharmony_ci desc = info->descs + (DESC_SIZE * next_rx); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci info->next_rx = next_rx; 9188c2ecf20Sopenharmony_ci info->next_rx_desc = info->descs + (DESC_SIZE * next_rx); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ciout: 9218c2ecf20Sopenharmony_ci if (0 && !nr) { 9228c2ecf20Sopenharmony_ci Dprintk("dazed: cmdsts_f: %08x\n", cmdsts); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->lock, flags); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic void rx_action(struct tasklet_struct *t) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct ns83820 *dev = from_tasklet(dev, t, rx_tasklet); 9318c2ecf20Sopenharmony_ci struct net_device *ndev = dev->ndev; 9328c2ecf20Sopenharmony_ci rx_irq(ndev); 9338c2ecf20Sopenharmony_ci writel(ihr, dev->base + IHR); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci spin_lock_irq(&dev->misc_lock); 9368c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_RXDESC; 9378c2ecf20Sopenharmony_ci writel(dev->IMR_cache, dev->base + IMR); 9388c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->misc_lock); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci rx_irq(ndev); 9418c2ecf20Sopenharmony_ci ns83820_rx_kick(ndev); 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci/* Packet Transmit code 9458c2ecf20Sopenharmony_ci */ 9468c2ecf20Sopenharmony_cistatic inline void kick_tx(struct ns83820 *dev) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci dprintk("kick_tx(%p): tx_idx=%d free_idx=%d\n", 9498c2ecf20Sopenharmony_ci dev, dev->tx_idx, dev->tx_free_idx); 9508c2ecf20Sopenharmony_ci writel(CR_TXE, dev->base + CR); 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* No spinlock needed on the transmit irq path as the interrupt handler is 9548c2ecf20Sopenharmony_ci * serialized. 9558c2ecf20Sopenharmony_ci */ 9568c2ecf20Sopenharmony_cistatic void do_tx_done(struct net_device *ndev) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 9598c2ecf20Sopenharmony_ci u32 cmdsts, tx_done_idx; 9608c2ecf20Sopenharmony_ci __le32 *desc; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci dprintk("do_tx_done(%p)\n", ndev); 9638c2ecf20Sopenharmony_ci tx_done_idx = dev->tx_done_idx; 9648c2ecf20Sopenharmony_ci desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", 9678c2ecf20Sopenharmony_ci tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); 9688c2ecf20Sopenharmony_ci while ((tx_done_idx != dev->tx_free_idx) && 9698c2ecf20Sopenharmony_ci !(CMDSTS_OWN & (cmdsts = le32_to_cpu(desc[DESC_CMDSTS]))) ) { 9708c2ecf20Sopenharmony_ci struct sk_buff *skb; 9718c2ecf20Sopenharmony_ci unsigned len; 9728c2ecf20Sopenharmony_ci dma_addr_t addr; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (cmdsts & CMDSTS_ERR) 9758c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 9768c2ecf20Sopenharmony_ci if (cmdsts & CMDSTS_OK) 9778c2ecf20Sopenharmony_ci ndev->stats.tx_packets++; 9788c2ecf20Sopenharmony_ci if (cmdsts & CMDSTS_OK) 9798c2ecf20Sopenharmony_ci ndev->stats.tx_bytes += cmdsts & 0xffff; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci dprintk("tx_done_idx=%d free_idx=%d cmdsts=%08x\n", 9828c2ecf20Sopenharmony_ci tx_done_idx, dev->tx_free_idx, cmdsts); 9838c2ecf20Sopenharmony_ci skb = dev->tx_skbs[tx_done_idx]; 9848c2ecf20Sopenharmony_ci dev->tx_skbs[tx_done_idx] = NULL; 9858c2ecf20Sopenharmony_ci dprintk("done(%p)\n", skb); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci len = cmdsts & CMDSTS_LEN_MASK; 9888c2ecf20Sopenharmony_ci addr = desc_addr_get(desc + DESC_BUFPTR); 9898c2ecf20Sopenharmony_ci if (skb) { 9908c2ecf20Sopenharmony_ci dma_unmap_single(&dev->pci_dev->dev, addr, len, 9918c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9928c2ecf20Sopenharmony_ci dev_consume_skb_irq(skb); 9938c2ecf20Sopenharmony_ci atomic_dec(&dev->nr_tx_skbs); 9948c2ecf20Sopenharmony_ci } else 9958c2ecf20Sopenharmony_ci dma_unmap_page(&dev->pci_dev->dev, addr, len, 9968c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci tx_done_idx = (tx_done_idx + 1) % NR_TX_DESC; 9998c2ecf20Sopenharmony_ci dev->tx_done_idx = tx_done_idx; 10008c2ecf20Sopenharmony_ci desc[DESC_CMDSTS] = cpu_to_le32(0); 10018c2ecf20Sopenharmony_ci mb(); 10028c2ecf20Sopenharmony_ci desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* Allow network stack to resume queueing packets after we've 10068c2ecf20Sopenharmony_ci * finished transmitting at least 1/4 of the packets in the queue. 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci if (netif_queue_stopped(ndev) && start_tx_okay(dev)) { 10098c2ecf20Sopenharmony_ci dprintk("start_queue(%p)\n", ndev); 10108c2ecf20Sopenharmony_ci netif_start_queue(ndev); 10118c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic void ns83820_cleanup_tx(struct ns83820 *dev) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci unsigned i; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci for (i=0; i<NR_TX_DESC; i++) { 10208c2ecf20Sopenharmony_ci struct sk_buff *skb = dev->tx_skbs[i]; 10218c2ecf20Sopenharmony_ci dev->tx_skbs[i] = NULL; 10228c2ecf20Sopenharmony_ci if (skb) { 10238c2ecf20Sopenharmony_ci __le32 *desc = dev->tx_descs + (i * DESC_SIZE); 10248c2ecf20Sopenharmony_ci dma_unmap_single(&dev->pci_dev->dev, 10258c2ecf20Sopenharmony_ci desc_addr_get(desc + DESC_BUFPTR), 10268c2ecf20Sopenharmony_ci le32_to_cpu(desc[DESC_CMDSTS]) & CMDSTS_LEN_MASK, 10278c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 10288c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 10298c2ecf20Sopenharmony_ci atomic_dec(&dev->nr_tx_skbs); 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci memset(dev->tx_descs, 0, NR_TX_DESC * DESC_SIZE * 4); 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci/* transmit routine. This code relies on the network layer serializing 10378c2ecf20Sopenharmony_ci * its calls in, but will run happily in parallel with the interrupt 10388c2ecf20Sopenharmony_ci * handler. This code currently has provisions for fragmenting tx buffers 10398c2ecf20Sopenharmony_ci * while trying to track down a bug in either the zero copy code or 10408c2ecf20Sopenharmony_ci * the tx fifo (hence the MAX_FRAG_LEN). 10418c2ecf20Sopenharmony_ci */ 10428c2ecf20Sopenharmony_cistatic netdev_tx_t ns83820_hard_start_xmit(struct sk_buff *skb, 10438c2ecf20Sopenharmony_ci struct net_device *ndev) 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 10468c2ecf20Sopenharmony_ci u32 free_idx, cmdsts, extsts; 10478c2ecf20Sopenharmony_ci int nr_free, nr_frags; 10488c2ecf20Sopenharmony_ci unsigned tx_done_idx, last_idx; 10498c2ecf20Sopenharmony_ci dma_addr_t buf; 10508c2ecf20Sopenharmony_ci unsigned len; 10518c2ecf20Sopenharmony_ci skb_frag_t *frag; 10528c2ecf20Sopenharmony_ci int stopped = 0; 10538c2ecf20Sopenharmony_ci int do_intr = 0; 10548c2ecf20Sopenharmony_ci volatile __le32 *first_desc; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci dprintk("ns83820_hard_start_xmit\n"); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci nr_frags = skb_shinfo(skb)->nr_frags; 10598c2ecf20Sopenharmony_ciagain: 10608c2ecf20Sopenharmony_ci if (unlikely(dev->CFG_cache & CFG_LNKSTS)) { 10618c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 10628c2ecf20Sopenharmony_ci if (unlikely(dev->CFG_cache & CFG_LNKSTS)) 10638c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 10648c2ecf20Sopenharmony_ci netif_start_queue(ndev); 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci last_idx = free_idx = dev->tx_free_idx; 10688c2ecf20Sopenharmony_ci tx_done_idx = dev->tx_done_idx; 10698c2ecf20Sopenharmony_ci nr_free = (tx_done_idx + NR_TX_DESC-2 - free_idx) % NR_TX_DESC; 10708c2ecf20Sopenharmony_ci nr_free -= 1; 10718c2ecf20Sopenharmony_ci if (nr_free <= nr_frags) { 10728c2ecf20Sopenharmony_ci dprintk("stop_queue - not enough(%p)\n", ndev); 10738c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* Check again: we may have raced with a tx done irq */ 10768c2ecf20Sopenharmony_ci if (dev->tx_done_idx != tx_done_idx) { 10778c2ecf20Sopenharmony_ci dprintk("restart queue(%p)\n", ndev); 10788c2ecf20Sopenharmony_ci netif_start_queue(ndev); 10798c2ecf20Sopenharmony_ci goto again; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (free_idx == dev->tx_intr_idx) { 10858c2ecf20Sopenharmony_ci do_intr = 1; 10868c2ecf20Sopenharmony_ci dev->tx_intr_idx = (dev->tx_intr_idx + NR_TX_DESC/4) % NR_TX_DESC; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci nr_free -= nr_frags; 10908c2ecf20Sopenharmony_ci if (nr_free < MIN_TX_DESC_FREE) { 10918c2ecf20Sopenharmony_ci dprintk("stop_queue - last entry(%p)\n", ndev); 10928c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 10938c2ecf20Sopenharmony_ci stopped = 1; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci frag = skb_shinfo(skb)->frags; 10978c2ecf20Sopenharmony_ci if (!nr_frags) 10988c2ecf20Sopenharmony_ci frag = NULL; 10998c2ecf20Sopenharmony_ci extsts = 0; 11008c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 11018c2ecf20Sopenharmony_ci extsts |= EXTSTS_IPPKT; 11028c2ecf20Sopenharmony_ci if (IPPROTO_TCP == ip_hdr(skb)->protocol) 11038c2ecf20Sopenharmony_ci extsts |= EXTSTS_TCPPKT; 11048c2ecf20Sopenharmony_ci else if (IPPROTO_UDP == ip_hdr(skb)->protocol) 11058c2ecf20Sopenharmony_ci extsts |= EXTSTS_UDPPKT; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci#ifdef NS83820_VLAN_ACCEL_SUPPORT 11098c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 11108c2ecf20Sopenharmony_ci /* fetch the vlan tag info out of the 11118c2ecf20Sopenharmony_ci * ancillary data if the vlan code 11128c2ecf20Sopenharmony_ci * is using hw vlan acceleration 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci short tag = skb_vlan_tag_get(skb); 11158c2ecf20Sopenharmony_ci extsts |= (EXTSTS_VPKT | htons(tag)); 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci#endif 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci len = skb->len; 11208c2ecf20Sopenharmony_ci if (nr_frags) 11218c2ecf20Sopenharmony_ci len -= skb->data_len; 11228c2ecf20Sopenharmony_ci buf = dma_map_single(&dev->pci_dev->dev, skb->data, len, 11238c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci first_desc = dev->tx_descs + (free_idx * DESC_SIZE); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci for (;;) { 11288c2ecf20Sopenharmony_ci volatile __le32 *desc = dev->tx_descs + (free_idx * DESC_SIZE); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len, 11318c2ecf20Sopenharmony_ci (unsigned long long)buf); 11328c2ecf20Sopenharmony_ci last_idx = free_idx; 11338c2ecf20Sopenharmony_ci free_idx = (free_idx + 1) % NR_TX_DESC; 11348c2ecf20Sopenharmony_ci desc[DESC_LINK] = cpu_to_le32(dev->tx_phy_descs + (free_idx * DESC_SIZE * 4)); 11358c2ecf20Sopenharmony_ci desc_addr_set(desc + DESC_BUFPTR, buf); 11368c2ecf20Sopenharmony_ci desc[DESC_EXTSTS] = cpu_to_le32(extsts); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci cmdsts = ((nr_frags) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0); 11398c2ecf20Sopenharmony_ci cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN; 11408c2ecf20Sopenharmony_ci cmdsts |= len; 11418c2ecf20Sopenharmony_ci desc[DESC_CMDSTS] = cpu_to_le32(cmdsts); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci if (!nr_frags) 11448c2ecf20Sopenharmony_ci break; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci buf = skb_frag_dma_map(&dev->pci_dev->dev, frag, 0, 11478c2ecf20Sopenharmony_ci skb_frag_size(frag), DMA_TO_DEVICE); 11488c2ecf20Sopenharmony_ci dprintk("frag: buf=%08Lx page=%08lx offset=%08lx\n", 11498c2ecf20Sopenharmony_ci (long long)buf, (long) page_to_pfn(frag->page), 11508c2ecf20Sopenharmony_ci frag->page_offset); 11518c2ecf20Sopenharmony_ci len = skb_frag_size(frag); 11528c2ecf20Sopenharmony_ci frag++; 11538c2ecf20Sopenharmony_ci nr_frags--; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci dprintk("done pkt\n"); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci spin_lock_irq(&dev->tx_lock); 11588c2ecf20Sopenharmony_ci dev->tx_skbs[last_idx] = skb; 11598c2ecf20Sopenharmony_ci first_desc[DESC_CMDSTS] |= cpu_to_le32(CMDSTS_OWN); 11608c2ecf20Sopenharmony_ci dev->tx_free_idx = free_idx; 11618c2ecf20Sopenharmony_ci atomic_inc(&dev->nr_tx_skbs); 11628c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->tx_lock); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci kick_tx(dev); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Check again: we may have raced with a tx done irq */ 11678c2ecf20Sopenharmony_ci if (stopped && (dev->tx_done_idx != tx_done_idx) && start_tx_okay(dev)) 11688c2ecf20Sopenharmony_ci netif_start_queue(ndev); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic void ns83820_update_stats(struct ns83820 *dev) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci struct net_device *ndev = dev->ndev; 11768c2ecf20Sopenharmony_ci u8 __iomem *base = dev->base; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* the DP83820 will freeze counters, so we need to read all of them */ 11798c2ecf20Sopenharmony_ci ndev->stats.rx_errors += readl(base + 0x60) & 0xffff; 11808c2ecf20Sopenharmony_ci ndev->stats.rx_crc_errors += readl(base + 0x64) & 0xffff; 11818c2ecf20Sopenharmony_ci ndev->stats.rx_missed_errors += readl(base + 0x68) & 0xffff; 11828c2ecf20Sopenharmony_ci ndev->stats.rx_frame_errors += readl(base + 0x6c) & 0xffff; 11838c2ecf20Sopenharmony_ci /*ndev->stats.rx_symbol_errors +=*/ readl(base + 0x70); 11848c2ecf20Sopenharmony_ci ndev->stats.rx_length_errors += readl(base + 0x74) & 0xffff; 11858c2ecf20Sopenharmony_ci ndev->stats.rx_length_errors += readl(base + 0x78) & 0xffff; 11868c2ecf20Sopenharmony_ci /*ndev->stats.rx_badopcode_errors += */ readl(base + 0x7c); 11878c2ecf20Sopenharmony_ci /*ndev->stats.rx_pause_count += */ readl(base + 0x80); 11888c2ecf20Sopenharmony_ci /*ndev->stats.tx_pause_count += */ readl(base + 0x84); 11898c2ecf20Sopenharmony_ci ndev->stats.tx_carrier_errors += readl(base + 0x88) & 0xff; 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic struct net_device_stats *ns83820_get_stats(struct net_device *ndev) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* somewhat overkill */ 11978c2ecf20Sopenharmony_ci spin_lock_irq(&dev->misc_lock); 11988c2ecf20Sopenharmony_ci ns83820_update_stats(dev); 11998c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->misc_lock); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return &ndev->stats; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci/* Let ethtool retrieve info */ 12058c2ecf20Sopenharmony_cistatic int ns83820_get_link_ksettings(struct net_device *ndev, 12068c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 12098c2ecf20Sopenharmony_ci u32 cfg, tbicr; 12108c2ecf20Sopenharmony_ci int fullduplex = 0; 12118c2ecf20Sopenharmony_ci u32 supported; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* 12148c2ecf20Sopenharmony_ci * Here's the list of available ethtool commands from other drivers: 12158c2ecf20Sopenharmony_ci * cmd->advertising = 12168c2ecf20Sopenharmony_ci * ethtool_cmd_speed_set(cmd, ...) 12178c2ecf20Sopenharmony_ci * cmd->duplex = 12188c2ecf20Sopenharmony_ci * cmd->port = 0; 12198c2ecf20Sopenharmony_ci * cmd->phy_address = 12208c2ecf20Sopenharmony_ci * cmd->transceiver = 0; 12218c2ecf20Sopenharmony_ci * cmd->autoneg = 12228c2ecf20Sopenharmony_ci * cmd->maxtxpkt = 0; 12238c2ecf20Sopenharmony_ci * cmd->maxrxpkt = 0; 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* read current configuration */ 12278c2ecf20Sopenharmony_ci cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; 12288c2ecf20Sopenharmony_ci readl(dev->base + TANAR); 12298c2ecf20Sopenharmony_ci tbicr = readl(dev->base + TBICR); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci supported = SUPPORTED_Autoneg; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (dev->CFG_cache & CFG_TBI_EN) { 12368c2ecf20Sopenharmony_ci /* we have optical interface */ 12378c2ecf20Sopenharmony_ci supported |= SUPPORTED_1000baseT_Half | 12388c2ecf20Sopenharmony_ci SUPPORTED_1000baseT_Full | 12398c2ecf20Sopenharmony_ci SUPPORTED_FIBRE; 12408c2ecf20Sopenharmony_ci cmd->base.port = PORT_FIBRE; 12418c2ecf20Sopenharmony_ci } else { 12428c2ecf20Sopenharmony_ci /* we have copper */ 12438c2ecf20Sopenharmony_ci supported |= SUPPORTED_10baseT_Half | 12448c2ecf20Sopenharmony_ci SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | 12458c2ecf20Sopenharmony_ci SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Half | 12468c2ecf20Sopenharmony_ci SUPPORTED_1000baseT_Full | 12478c2ecf20Sopenharmony_ci SUPPORTED_MII; 12488c2ecf20Sopenharmony_ci cmd->base.port = PORT_MII; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 12528c2ecf20Sopenharmony_ci supported); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci cmd->base.duplex = fullduplex ? DUPLEX_FULL : DUPLEX_HALF; 12558c2ecf20Sopenharmony_ci switch (cfg / CFG_SPDSTS0 & 3) { 12568c2ecf20Sopenharmony_ci case 2: 12578c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_1000; 12588c2ecf20Sopenharmony_ci break; 12598c2ecf20Sopenharmony_ci case 1: 12608c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_100; 12618c2ecf20Sopenharmony_ci break; 12628c2ecf20Sopenharmony_ci default: 12638c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10; 12648c2ecf20Sopenharmony_ci break; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci cmd->base.autoneg = (tbicr & TBICR_MR_AN_ENABLE) 12678c2ecf20Sopenharmony_ci ? AUTONEG_ENABLE : AUTONEG_DISABLE; 12688c2ecf20Sopenharmony_ci return 0; 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci/* Let ethool change settings*/ 12728c2ecf20Sopenharmony_cistatic int ns83820_set_link_ksettings(struct net_device *ndev, 12738c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 12768c2ecf20Sopenharmony_ci u32 cfg, tanar; 12778c2ecf20Sopenharmony_ci int have_optical = 0; 12788c2ecf20Sopenharmony_ci int fullduplex = 0; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* read current configuration */ 12818c2ecf20Sopenharmony_ci cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; 12828c2ecf20Sopenharmony_ci tanar = readl(dev->base + TANAR); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (dev->CFG_cache & CFG_TBI_EN) { 12858c2ecf20Sopenharmony_ci /* we have optical */ 12868c2ecf20Sopenharmony_ci have_optical = 1; 12878c2ecf20Sopenharmony_ci fullduplex = (tanar & TANAR_FULL_DUP); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci } else { 12908c2ecf20Sopenharmony_ci /* we have copper */ 12918c2ecf20Sopenharmony_ci fullduplex = cfg & CFG_DUPSTS; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci spin_lock_irq(&dev->misc_lock); 12958c2ecf20Sopenharmony_ci spin_lock(&dev->tx_lock); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* Set duplex */ 12988c2ecf20Sopenharmony_ci if (cmd->base.duplex != fullduplex) { 12998c2ecf20Sopenharmony_ci if (have_optical) { 13008c2ecf20Sopenharmony_ci /*set full duplex*/ 13018c2ecf20Sopenharmony_ci if (cmd->base.duplex == DUPLEX_FULL) { 13028c2ecf20Sopenharmony_ci /* force full duplex */ 13038c2ecf20Sopenharmony_ci writel(readl(dev->base + TXCFG) 13048c2ecf20Sopenharmony_ci | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, 13058c2ecf20Sopenharmony_ci dev->base + TXCFG); 13068c2ecf20Sopenharmony_ci writel(readl(dev->base + RXCFG) | RXCFG_RX_FD, 13078c2ecf20Sopenharmony_ci dev->base + RXCFG); 13088c2ecf20Sopenharmony_ci /* Light up full duplex LED */ 13098c2ecf20Sopenharmony_ci writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT, 13108c2ecf20Sopenharmony_ci dev->base + GPIOR); 13118c2ecf20Sopenharmony_ci } else { 13128c2ecf20Sopenharmony_ci /*TODO: set half duplex */ 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci } else { 13168c2ecf20Sopenharmony_ci /*we have copper*/ 13178c2ecf20Sopenharmony_ci /* TODO: Set duplex for copper cards */ 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Duplex set via ethtool\n", 13208c2ecf20Sopenharmony_ci ndev->name); 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* Set autonegotiation */ 13248c2ecf20Sopenharmony_ci if (1) { 13258c2ecf20Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_ENABLE) { 13268c2ecf20Sopenharmony_ci /* restart auto negotiation */ 13278c2ecf20Sopenharmony_ci writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, 13288c2ecf20Sopenharmony_ci dev->base + TBICR); 13298c2ecf20Sopenharmony_ci writel(TBICR_MR_AN_ENABLE, dev->base + TBICR); 13308c2ecf20Sopenharmony_ci dev->linkstate = LINK_AUTONEGOTIATE; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: autoneg enabled via ethtool\n", 13338c2ecf20Sopenharmony_ci ndev->name); 13348c2ecf20Sopenharmony_ci } else { 13358c2ecf20Sopenharmony_ci /* disable auto negotiation */ 13368c2ecf20Sopenharmony_ci writel(0x00000000, dev->base + TBICR); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: autoneg %s via ethtool\n", ndev->name, 13408c2ecf20Sopenharmony_ci cmd->base.autoneg ? "ENABLED" : "DISABLED"); 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci phy_intr(ndev); 13448c2ecf20Sopenharmony_ci spin_unlock(&dev->tx_lock); 13458c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->misc_lock); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci return 0; 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci/* end ethtool get/set support -df */ 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 13548c2ecf20Sopenharmony_ci strlcpy(info->driver, "ns83820", sizeof(info->driver)); 13558c2ecf20Sopenharmony_ci strlcpy(info->version, VERSION, sizeof(info->version)); 13568c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(dev->pci_dev), sizeof(info->bus_info)); 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic u32 ns83820_get_link(struct net_device *ndev) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 13628c2ecf20Sopenharmony_ci u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; 13638c2ecf20Sopenharmony_ci return cfg & CFG_LNKSTS ? 1 : 0; 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic const struct ethtool_ops ops = { 13678c2ecf20Sopenharmony_ci .get_drvinfo = ns83820_get_drvinfo, 13688c2ecf20Sopenharmony_ci .get_link = ns83820_get_link, 13698c2ecf20Sopenharmony_ci .get_link_ksettings = ns83820_get_link_ksettings, 13708c2ecf20Sopenharmony_ci .set_link_ksettings = ns83820_set_link_ksettings, 13718c2ecf20Sopenharmony_ci}; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_cistatic inline void ns83820_disable_interrupts(struct ns83820 *dev) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci writel(0, dev->base + IMR); 13768c2ecf20Sopenharmony_ci writel(0, dev->base + IER); 13778c2ecf20Sopenharmony_ci readl(dev->base + IER); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci/* this function is called in irq context from the ISR */ 13818c2ecf20Sopenharmony_cistatic void ns83820_mib_isr(struct ns83820 *dev) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci unsigned long flags; 13848c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->misc_lock, flags); 13858c2ecf20Sopenharmony_ci ns83820_update_stats(dev); 13868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->misc_lock, flags); 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic void ns83820_do_isr(struct net_device *ndev, u32 isr); 13908c2ecf20Sopenharmony_cistatic irqreturn_t ns83820_irq(int foo, void *data) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci struct net_device *ndev = data; 13938c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 13948c2ecf20Sopenharmony_ci u32 isr; 13958c2ecf20Sopenharmony_ci dprintk("ns83820_irq(%p)\n", ndev); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci dev->ihr = 0; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci isr = readl(dev->base + ISR); 14008c2ecf20Sopenharmony_ci dprintk("irq: %08x\n", isr); 14018c2ecf20Sopenharmony_ci ns83820_do_isr(ndev, isr); 14028c2ecf20Sopenharmony_ci return IRQ_HANDLED; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic void ns83820_do_isr(struct net_device *ndev, u32 isr) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 14088c2ecf20Sopenharmony_ci unsigned long flags; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci#ifdef DEBUG 14118c2ecf20Sopenharmony_ci if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC)) 14128c2ecf20Sopenharmony_ci Dprintk("odd isr? 0x%08x\n", isr); 14138c2ecf20Sopenharmony_ci#endif 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (ISR_RXIDLE & isr) { 14168c2ecf20Sopenharmony_ci dev->rx_info.idle = 1; 14178c2ecf20Sopenharmony_ci Dprintk("oh dear, we are idle\n"); 14188c2ecf20Sopenharmony_ci ns83820_rx_kick(ndev); 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if ((ISR_RXDESC | ISR_RXOK) & isr) { 14228c2ecf20Sopenharmony_ci prefetch(dev->rx_info.next_rx_desc); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->misc_lock, flags); 14258c2ecf20Sopenharmony_ci dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK); 14268c2ecf20Sopenharmony_ci writel(dev->IMR_cache, dev->base + IMR); 14278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->misc_lock, flags); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci tasklet_schedule(&dev->rx_tasklet); 14308c2ecf20Sopenharmony_ci //rx_irq(ndev); 14318c2ecf20Sopenharmony_ci //writel(4, dev->base + IHR); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if ((ISR_RXIDLE | ISR_RXORN | ISR_RXDESC | ISR_RXOK | ISR_RXERR) & isr) 14358c2ecf20Sopenharmony_ci ns83820_rx_kick(ndev); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (unlikely(ISR_RXSOVR & isr)) { 14388c2ecf20Sopenharmony_ci //printk("overrun: rxsovr\n"); 14398c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci if (unlikely(ISR_RXORN & isr)) { 14438c2ecf20Sopenharmony_ci //printk("overrun: rxorn\n"); 14448c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if ((ISR_RXRCMP & isr) && dev->rx_info.up) 14488c2ecf20Sopenharmony_ci writel(CR_RXE, dev->base + CR); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (ISR_TXIDLE & isr) { 14518c2ecf20Sopenharmony_ci u32 txdp; 14528c2ecf20Sopenharmony_ci txdp = readl(dev->base + TXDP); 14538c2ecf20Sopenharmony_ci dprintk("txdp: %08x\n", txdp); 14548c2ecf20Sopenharmony_ci txdp -= dev->tx_phy_descs; 14558c2ecf20Sopenharmony_ci dev->tx_idx = txdp / (DESC_SIZE * 4); 14568c2ecf20Sopenharmony_ci if (dev->tx_idx >= NR_TX_DESC) { 14578c2ecf20Sopenharmony_ci printk(KERN_ALERT "%s: BUG -- txdp out of range\n", ndev->name); 14588c2ecf20Sopenharmony_ci dev->tx_idx = 0; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci /* The may have been a race between a pci originated read 14618c2ecf20Sopenharmony_ci * and the descriptor update from the cpu. Just in case, 14628c2ecf20Sopenharmony_ci * kick the transmitter if the hardware thinks it is on a 14638c2ecf20Sopenharmony_ci * different descriptor than we are. 14648c2ecf20Sopenharmony_ci */ 14658c2ecf20Sopenharmony_ci if (dev->tx_idx != dev->tx_free_idx) 14668c2ecf20Sopenharmony_ci kick_tx(dev); 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* Defer tx ring processing until more than a minimum amount of 14708c2ecf20Sopenharmony_ci * work has accumulated 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ci if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) { 14738c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->tx_lock, flags); 14748c2ecf20Sopenharmony_ci do_tx_done(ndev); 14758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->tx_lock, flags); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci /* Disable TxOk if there are no outstanding tx packets. 14788c2ecf20Sopenharmony_ci */ 14798c2ecf20Sopenharmony_ci if ((dev->tx_done_idx == dev->tx_free_idx) && 14808c2ecf20Sopenharmony_ci (dev->IMR_cache & ISR_TXOK)) { 14818c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->misc_lock, flags); 14828c2ecf20Sopenharmony_ci dev->IMR_cache &= ~ISR_TXOK; 14838c2ecf20Sopenharmony_ci writel(dev->IMR_cache, dev->base + IMR); 14848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->misc_lock, flags); 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* The TxIdle interrupt can come in before the transmit has 14898c2ecf20Sopenharmony_ci * completed. Normally we reap packets off of the combination 14908c2ecf20Sopenharmony_ci * of TxDesc and TxIdle and leave TxOk disabled (since it 14918c2ecf20Sopenharmony_ci * occurs on every packet), but when no further irqs of this 14928c2ecf20Sopenharmony_ci * nature are expected, we must enable TxOk. 14938c2ecf20Sopenharmony_ci */ 14948c2ecf20Sopenharmony_ci if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) { 14958c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->misc_lock, flags); 14968c2ecf20Sopenharmony_ci dev->IMR_cache |= ISR_TXOK; 14978c2ecf20Sopenharmony_ci writel(dev->IMR_cache, dev->base + IMR); 14988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->misc_lock, flags); 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci /* MIB interrupt: one of the statistics counters is about to overflow */ 15028c2ecf20Sopenharmony_ci if (unlikely(ISR_MIB & isr)) 15038c2ecf20Sopenharmony_ci ns83820_mib_isr(dev); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci /* PHY: Link up/down/negotiation state change */ 15068c2ecf20Sopenharmony_ci if (unlikely(ISR_PHY & isr)) 15078c2ecf20Sopenharmony_ci phy_intr(ndev); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci#if 0 /* Still working on the interrupt mitigation strategy */ 15108c2ecf20Sopenharmony_ci if (dev->ihr) 15118c2ecf20Sopenharmony_ci writel(dev->ihr, dev->base + IHR); 15128c2ecf20Sopenharmony_ci#endif 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cistatic void ns83820_do_reset(struct ns83820 *dev, u32 which) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci Dprintk("resetting chip...\n"); 15188c2ecf20Sopenharmony_ci writel(which, dev->base + CR); 15198c2ecf20Sopenharmony_ci do { 15208c2ecf20Sopenharmony_ci schedule(); 15218c2ecf20Sopenharmony_ci } while (readl(dev->base + CR) & which); 15228c2ecf20Sopenharmony_ci Dprintk("okay!\n"); 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_cistatic int ns83820_stop(struct net_device *ndev) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci /* FIXME: protect against interrupt handler? */ 15308c2ecf20Sopenharmony_ci del_timer_sync(&dev->tx_watchdog); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci ns83820_disable_interrupts(dev); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci dev->rx_info.up = 0; 15358c2ecf20Sopenharmony_ci synchronize_irq(dev->pci_dev->irq); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci ns83820_do_reset(dev, CR_RST); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci synchronize_irq(dev->pci_dev->irq); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci spin_lock_irq(&dev->misc_lock); 15428c2ecf20Sopenharmony_ci dev->IMR_cache &= ~(ISR_TXURN | ISR_TXIDLE | ISR_TXERR | ISR_TXDESC | ISR_TXOK); 15438c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->misc_lock); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci ns83820_cleanup_rx(dev); 15468c2ecf20Sopenharmony_ci ns83820_cleanup_tx(dev); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci return 0; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic void ns83820_tx_timeout(struct net_device *ndev, unsigned int txqueue) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 15548c2ecf20Sopenharmony_ci u32 tx_done_idx; 15558c2ecf20Sopenharmony_ci __le32 *desc; 15568c2ecf20Sopenharmony_ci unsigned long flags; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->tx_lock, flags); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci tx_done_idx = dev->tx_done_idx; 15618c2ecf20Sopenharmony_ci desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: tx_timeout: tx_done_idx=%d free_idx=%d cmdsts=%08x\n", 15648c2ecf20Sopenharmony_ci ndev->name, 15658c2ecf20Sopenharmony_ci tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci#if defined(DEBUG) 15688c2ecf20Sopenharmony_ci { 15698c2ecf20Sopenharmony_ci u32 isr; 15708c2ecf20Sopenharmony_ci isr = readl(dev->base + ISR); 15718c2ecf20Sopenharmony_ci printk("irq: %08x imr: %08x\n", isr, dev->IMR_cache); 15728c2ecf20Sopenharmony_ci ns83820_do_isr(ndev, isr); 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci#endif 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci do_tx_done(ndev); 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci tx_done_idx = dev->tx_done_idx; 15798c2ecf20Sopenharmony_ci desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: after: tx_done_idx=%d free_idx=%d cmdsts=%08x\n", 15828c2ecf20Sopenharmony_ci ndev->name, 15838c2ecf20Sopenharmony_ci tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->tx_lock, flags); 15868c2ecf20Sopenharmony_ci} 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_cistatic void ns83820_tx_watch(struct timer_list *t) 15898c2ecf20Sopenharmony_ci{ 15908c2ecf20Sopenharmony_ci struct ns83820 *dev = from_timer(dev, t, tx_watchdog); 15918c2ecf20Sopenharmony_ci struct net_device *ndev = dev->ndev; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci#if defined(DEBUG) 15948c2ecf20Sopenharmony_ci printk("ns83820_tx_watch: %u %u %d\n", 15958c2ecf20Sopenharmony_ci dev->tx_done_idx, dev->tx_free_idx, atomic_read(&dev->nr_tx_skbs) 15968c2ecf20Sopenharmony_ci ); 15978c2ecf20Sopenharmony_ci#endif 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci if (time_after(jiffies, dev_trans_start(ndev) + 1*HZ) && 16008c2ecf20Sopenharmony_ci dev->tx_done_idx != dev->tx_free_idx) { 16018c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: ns83820_tx_watch: %u %u %d\n", 16028c2ecf20Sopenharmony_ci ndev->name, 16038c2ecf20Sopenharmony_ci dev->tx_done_idx, dev->tx_free_idx, 16048c2ecf20Sopenharmony_ci atomic_read(&dev->nr_tx_skbs)); 16058c2ecf20Sopenharmony_ci ns83820_tx_timeout(ndev, UINT_MAX); 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci mod_timer(&dev->tx_watchdog, jiffies + 2*HZ); 16098c2ecf20Sopenharmony_ci} 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic int ns83820_open(struct net_device *ndev) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 16148c2ecf20Sopenharmony_ci unsigned i; 16158c2ecf20Sopenharmony_ci u32 desc; 16168c2ecf20Sopenharmony_ci int ret; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci dprintk("ns83820_open\n"); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci writel(0, dev->base + PQCR); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci ret = ns83820_setup_rx(ndev); 16238c2ecf20Sopenharmony_ci if (ret) 16248c2ecf20Sopenharmony_ci goto failed; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci memset(dev->tx_descs, 0, 4 * NR_TX_DESC * DESC_SIZE); 16278c2ecf20Sopenharmony_ci for (i=0; i<NR_TX_DESC; i++) { 16288c2ecf20Sopenharmony_ci dev->tx_descs[(i * DESC_SIZE) + DESC_LINK] 16298c2ecf20Sopenharmony_ci = cpu_to_le32( 16308c2ecf20Sopenharmony_ci dev->tx_phy_descs 16318c2ecf20Sopenharmony_ci + ((i+1) % NR_TX_DESC) * DESC_SIZE * 4); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci dev->tx_idx = 0; 16358c2ecf20Sopenharmony_ci dev->tx_done_idx = 0; 16368c2ecf20Sopenharmony_ci desc = dev->tx_phy_descs; 16378c2ecf20Sopenharmony_ci writel(0, dev->base + TXDP_HI); 16388c2ecf20Sopenharmony_ci writel(desc, dev->base + TXDP); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci timer_setup(&dev->tx_watchdog, ns83820_tx_watch, 0); 16418c2ecf20Sopenharmony_ci mod_timer(&dev->tx_watchdog, jiffies + 2*HZ); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci netif_start_queue(ndev); /* FIXME: wait for phy to come up */ 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci return 0; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cifailed: 16488c2ecf20Sopenharmony_ci ns83820_stop(ndev); 16498c2ecf20Sopenharmony_ci return ret; 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic void ns83820_getmac(struct ns83820 *dev, u8 *mac) 16538c2ecf20Sopenharmony_ci{ 16548c2ecf20Sopenharmony_ci unsigned i; 16558c2ecf20Sopenharmony_ci for (i=0; i<3; i++) { 16568c2ecf20Sopenharmony_ci u32 data; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* Read from the perfect match memory: this is loaded by 16598c2ecf20Sopenharmony_ci * the chip from the EEPROM via the EELOAD self test. 16608c2ecf20Sopenharmony_ci */ 16618c2ecf20Sopenharmony_ci writel(i*2, dev->base + RFCR); 16628c2ecf20Sopenharmony_ci data = readl(dev->base + RFDR); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci *mac++ = data; 16658c2ecf20Sopenharmony_ci *mac++ = data >> 8; 16668c2ecf20Sopenharmony_ci } 16678c2ecf20Sopenharmony_ci} 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic void ns83820_set_multicast(struct net_device *ndev) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 16728c2ecf20Sopenharmony_ci u8 __iomem *rfcr = dev->base + RFCR; 16738c2ecf20Sopenharmony_ci u32 and_mask = 0xffffffff; 16748c2ecf20Sopenharmony_ci u32 or_mask = 0; 16758c2ecf20Sopenharmony_ci u32 val; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci if (ndev->flags & IFF_PROMISC) 16788c2ecf20Sopenharmony_ci or_mask |= RFCR_AAU | RFCR_AAM; 16798c2ecf20Sopenharmony_ci else 16808c2ecf20Sopenharmony_ci and_mask &= ~(RFCR_AAU | RFCR_AAM); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (ndev->flags & IFF_ALLMULTI || netdev_mc_count(ndev)) 16838c2ecf20Sopenharmony_ci or_mask |= RFCR_AAM; 16848c2ecf20Sopenharmony_ci else 16858c2ecf20Sopenharmony_ci and_mask &= ~RFCR_AAM; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci spin_lock_irq(&dev->misc_lock); 16888c2ecf20Sopenharmony_ci val = (readl(rfcr) & and_mask) | or_mask; 16898c2ecf20Sopenharmony_ci /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */ 16908c2ecf20Sopenharmony_ci writel(val & ~RFCR_RFEN, rfcr); 16918c2ecf20Sopenharmony_ci writel(val, rfcr); 16928c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->misc_lock); 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_cistatic void ns83820_run_bist(struct net_device *ndev, const char *name, u32 enable, u32 done, u32 fail) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 16988c2ecf20Sopenharmony_ci int timed_out = 0; 16998c2ecf20Sopenharmony_ci unsigned long start; 17008c2ecf20Sopenharmony_ci u32 status; 17018c2ecf20Sopenharmony_ci int loops = 0; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci dprintk("%s: start %s\n", ndev->name, name); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci start = jiffies; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci writel(enable, dev->base + PTSCR); 17088c2ecf20Sopenharmony_ci for (;;) { 17098c2ecf20Sopenharmony_ci loops++; 17108c2ecf20Sopenharmony_ci status = readl(dev->base + PTSCR); 17118c2ecf20Sopenharmony_ci if (!(status & enable)) 17128c2ecf20Sopenharmony_ci break; 17138c2ecf20Sopenharmony_ci if (status & done) 17148c2ecf20Sopenharmony_ci break; 17158c2ecf20Sopenharmony_ci if (status & fail) 17168c2ecf20Sopenharmony_ci break; 17178c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, start + HZ)) { 17188c2ecf20Sopenharmony_ci timed_out = 1; 17198c2ecf20Sopenharmony_ci break; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci if (status & fail) 17258c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %s failed! (0x%08x & 0x%08x)\n", 17268c2ecf20Sopenharmony_ci ndev->name, name, status, fail); 17278c2ecf20Sopenharmony_ci else if (timed_out) 17288c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: run_bist %s timed out! (%08x)\n", 17298c2ecf20Sopenharmony_ci ndev->name, name, status); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci dprintk("%s: done %s in %d loops\n", ndev->name, name, loops); 17328c2ecf20Sopenharmony_ci} 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci#ifdef PHY_CODE_IS_FINISHED 17358c2ecf20Sopenharmony_cistatic void ns83820_mii_write_bit(struct ns83820 *dev, int bit) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci /* drive MDC low */ 17388c2ecf20Sopenharmony_ci dev->MEAR_cache &= ~MEAR_MDC; 17398c2ecf20Sopenharmony_ci writel(dev->MEAR_cache, dev->base + MEAR); 17408c2ecf20Sopenharmony_ci readl(dev->base + MEAR); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci /* enable output, set bit */ 17438c2ecf20Sopenharmony_ci dev->MEAR_cache |= MEAR_MDDIR; 17448c2ecf20Sopenharmony_ci if (bit) 17458c2ecf20Sopenharmony_ci dev->MEAR_cache |= MEAR_MDIO; 17468c2ecf20Sopenharmony_ci else 17478c2ecf20Sopenharmony_ci dev->MEAR_cache &= ~MEAR_MDIO; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci /* set the output bit */ 17508c2ecf20Sopenharmony_ci writel(dev->MEAR_cache, dev->base + MEAR); 17518c2ecf20Sopenharmony_ci readl(dev->base + MEAR); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci /* Wait. Max clock rate is 2.5MHz, this way we come in under 1MHz */ 17548c2ecf20Sopenharmony_ci udelay(1); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci /* drive MDC high causing the data bit to be latched */ 17578c2ecf20Sopenharmony_ci dev->MEAR_cache |= MEAR_MDC; 17588c2ecf20Sopenharmony_ci writel(dev->MEAR_cache, dev->base + MEAR); 17598c2ecf20Sopenharmony_ci readl(dev->base + MEAR); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci /* Wait again... */ 17628c2ecf20Sopenharmony_ci udelay(1); 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_cistatic int ns83820_mii_read_bit(struct ns83820 *dev) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci int bit; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci /* drive MDC low, disable output */ 17708c2ecf20Sopenharmony_ci dev->MEAR_cache &= ~MEAR_MDC; 17718c2ecf20Sopenharmony_ci dev->MEAR_cache &= ~MEAR_MDDIR; 17728c2ecf20Sopenharmony_ci writel(dev->MEAR_cache, dev->base + MEAR); 17738c2ecf20Sopenharmony_ci readl(dev->base + MEAR); 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci /* Wait. Max clock rate is 2.5MHz, this way we come in under 1MHz */ 17768c2ecf20Sopenharmony_ci udelay(1); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* drive MDC high causing the data bit to be latched */ 17798c2ecf20Sopenharmony_ci bit = (readl(dev->base + MEAR) & MEAR_MDIO) ? 1 : 0; 17808c2ecf20Sopenharmony_ci dev->MEAR_cache |= MEAR_MDC; 17818c2ecf20Sopenharmony_ci writel(dev->MEAR_cache, dev->base + MEAR); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci /* Wait again... */ 17848c2ecf20Sopenharmony_ci udelay(1); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci return bit; 17878c2ecf20Sopenharmony_ci} 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_cistatic unsigned ns83820_mii_read_reg(struct ns83820 *dev, unsigned phy, unsigned reg) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci unsigned data = 0; 17928c2ecf20Sopenharmony_ci int i; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci /* read some garbage so that we eventually sync up */ 17958c2ecf20Sopenharmony_ci for (i=0; i<64; i++) 17968c2ecf20Sopenharmony_ci ns83820_mii_read_bit(dev); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 0); /* start */ 17998c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 1); 18008c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 1); /* opcode read */ 18018c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 0); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci /* write out the phy address: 5 bits, msb first */ 18048c2ecf20Sopenharmony_ci for (i=0; i<5; i++) 18058c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, phy & (0x10 >> i)); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci /* write out the register address, 5 bits, msb first */ 18088c2ecf20Sopenharmony_ci for (i=0; i<5; i++) 18098c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, reg & (0x10 >> i)); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci ns83820_mii_read_bit(dev); /* turn around cycles */ 18128c2ecf20Sopenharmony_ci ns83820_mii_read_bit(dev); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci /* read in the register data, 16 bits msb first */ 18158c2ecf20Sopenharmony_ci for (i=0; i<16; i++) { 18168c2ecf20Sopenharmony_ci data <<= 1; 18178c2ecf20Sopenharmony_ci data |= ns83820_mii_read_bit(dev); 18188c2ecf20Sopenharmony_ci } 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci return data; 18218c2ecf20Sopenharmony_ci} 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_cistatic unsigned ns83820_mii_write_reg(struct ns83820 *dev, unsigned phy, unsigned reg, unsigned data) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci int i; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci /* read some garbage so that we eventually sync up */ 18288c2ecf20Sopenharmony_ci for (i=0; i<64; i++) 18298c2ecf20Sopenharmony_ci ns83820_mii_read_bit(dev); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 0); /* start */ 18328c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 1); 18338c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 0); /* opcode read */ 18348c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, 1); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci /* write out the phy address: 5 bits, msb first */ 18378c2ecf20Sopenharmony_ci for (i=0; i<5; i++) 18388c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, phy & (0x10 >> i)); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci /* write out the register address, 5 bits, msb first */ 18418c2ecf20Sopenharmony_ci for (i=0; i<5; i++) 18428c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, reg & (0x10 >> i)); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci ns83820_mii_read_bit(dev); /* turn around cycles */ 18458c2ecf20Sopenharmony_ci ns83820_mii_read_bit(dev); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci /* read in the register data, 16 bits msb first */ 18488c2ecf20Sopenharmony_ci for (i=0; i<16; i++) 18498c2ecf20Sopenharmony_ci ns83820_mii_write_bit(dev, (data >> (15 - i)) & 1); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci return data; 18528c2ecf20Sopenharmony_ci} 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_cistatic void ns83820_probe_phy(struct net_device *ndev) 18558c2ecf20Sopenharmony_ci{ 18568c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); 18578c2ecf20Sopenharmony_ci int j; 18588c2ecf20Sopenharmony_ci unsigned a, b; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci for (j = 0; j < 0x16; j += 4) { 18618c2ecf20Sopenharmony_ci dprintk("%s: [0x%02x] %04x %04x %04x %04x\n", 18628c2ecf20Sopenharmony_ci ndev->name, j, 18638c2ecf20Sopenharmony_ci ns83820_mii_read_reg(dev, 1, 0 + j), 18648c2ecf20Sopenharmony_ci ns83820_mii_read_reg(dev, 1, 1 + j), 18658c2ecf20Sopenharmony_ci ns83820_mii_read_reg(dev, 1, 2 + j), 18668c2ecf20Sopenharmony_ci ns83820_mii_read_reg(dev, 1, 3 + j) 18678c2ecf20Sopenharmony_ci ); 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci /* read firmware version: memory addr is 0x8402 and 0x8403 */ 18718c2ecf20Sopenharmony_ci ns83820_mii_write_reg(dev, 1, 0x16, 0x000d); 18728c2ecf20Sopenharmony_ci ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e); 18738c2ecf20Sopenharmony_ci a = ns83820_mii_read_reg(dev, 1, 0x1d); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci ns83820_mii_write_reg(dev, 1, 0x16, 0x000d); 18768c2ecf20Sopenharmony_ci ns83820_mii_write_reg(dev, 1, 0x1e, 0x810e); 18778c2ecf20Sopenharmony_ci b = ns83820_mii_read_reg(dev, 1, 0x1d); 18788c2ecf20Sopenharmony_ci dprintk("version: 0x%04x 0x%04x\n", a, b); 18798c2ecf20Sopenharmony_ci} 18808c2ecf20Sopenharmony_ci#endif 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 18838c2ecf20Sopenharmony_ci .ndo_open = ns83820_open, 18848c2ecf20Sopenharmony_ci .ndo_stop = ns83820_stop, 18858c2ecf20Sopenharmony_ci .ndo_start_xmit = ns83820_hard_start_xmit, 18868c2ecf20Sopenharmony_ci .ndo_get_stats = ns83820_get_stats, 18878c2ecf20Sopenharmony_ci .ndo_set_rx_mode = ns83820_set_multicast, 18888c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 18898c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 18908c2ecf20Sopenharmony_ci .ndo_tx_timeout = ns83820_tx_timeout, 18918c2ecf20Sopenharmony_ci}; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_cistatic int ns83820_init_one(struct pci_dev *pci_dev, 18948c2ecf20Sopenharmony_ci const struct pci_device_id *id) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci struct net_device *ndev; 18978c2ecf20Sopenharmony_ci struct ns83820 *dev; 18988c2ecf20Sopenharmony_ci long addr; 18998c2ecf20Sopenharmony_ci int err; 19008c2ecf20Sopenharmony_ci int using_dac = 0; 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci /* See if we can set the dma mask early on; failure is fatal. */ 19038c2ecf20Sopenharmony_ci if (sizeof(dma_addr_t) == 8 && 19048c2ecf20Sopenharmony_ci !dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64))) { 19058c2ecf20Sopenharmony_ci using_dac = 1; 19068c2ecf20Sopenharmony_ci } else if (!dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { 19078c2ecf20Sopenharmony_ci using_dac = 0; 19088c2ecf20Sopenharmony_ci } else { 19098c2ecf20Sopenharmony_ci dev_warn(&pci_dev->dev, "dma_set_mask failed!\n"); 19108c2ecf20Sopenharmony_ci return -ENODEV; 19118c2ecf20Sopenharmony_ci } 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct ns83820)); 19148c2ecf20Sopenharmony_ci err = -ENOMEM; 19158c2ecf20Sopenharmony_ci if (!ndev) 19168c2ecf20Sopenharmony_ci goto out; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci dev = PRIV(ndev); 19198c2ecf20Sopenharmony_ci dev->ndev = ndev; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci spin_lock_init(&dev->rx_info.lock); 19228c2ecf20Sopenharmony_ci spin_lock_init(&dev->tx_lock); 19238c2ecf20Sopenharmony_ci spin_lock_init(&dev->misc_lock); 19248c2ecf20Sopenharmony_ci dev->pci_dev = pci_dev; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, &pci_dev->dev); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci INIT_WORK(&dev->tq_refill, queue_refill); 19298c2ecf20Sopenharmony_ci tasklet_setup(&dev->rx_tasklet, rx_action); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci err = pci_enable_device(pci_dev); 19328c2ecf20Sopenharmony_ci if (err) { 19338c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "pci_enable_dev failed: %d\n", err); 19348c2ecf20Sopenharmony_ci goto out_free; 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci pci_set_master(pci_dev); 19388c2ecf20Sopenharmony_ci addr = pci_resource_start(pci_dev, 1); 19398c2ecf20Sopenharmony_ci dev->base = ioremap(addr, PAGE_SIZE); 19408c2ecf20Sopenharmony_ci dev->tx_descs = dma_alloc_coherent(&pci_dev->dev, 19418c2ecf20Sopenharmony_ci 4 * DESC_SIZE * NR_TX_DESC, 19428c2ecf20Sopenharmony_ci &dev->tx_phy_descs, GFP_KERNEL); 19438c2ecf20Sopenharmony_ci dev->rx_info.descs = dma_alloc_coherent(&pci_dev->dev, 19448c2ecf20Sopenharmony_ci 4 * DESC_SIZE * NR_RX_DESC, 19458c2ecf20Sopenharmony_ci &dev->rx_info.phy_descs, GFP_KERNEL); 19468c2ecf20Sopenharmony_ci err = -ENOMEM; 19478c2ecf20Sopenharmony_ci if (!dev->base || !dev->tx_descs || !dev->rx_info.descs) 19488c2ecf20Sopenharmony_ci goto out_disable; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci dprintk("%p: %08lx %p: %08lx\n", 19518c2ecf20Sopenharmony_ci dev->tx_descs, (long)dev->tx_phy_descs, 19528c2ecf20Sopenharmony_ci dev->rx_info.descs, (long)dev->rx_info.phy_descs); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci ns83820_disable_interrupts(dev); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci dev->IMR_cache = 0; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED, 19598c2ecf20Sopenharmony_ci DRV_NAME, ndev); 19608c2ecf20Sopenharmony_ci if (err) { 19618c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "unable to register irq %d, err %d\n", 19628c2ecf20Sopenharmony_ci pci_dev->irq, err); 19638c2ecf20Sopenharmony_ci goto out_disable; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci /* 19678c2ecf20Sopenharmony_ci * FIXME: we are holding rtnl_lock() over obscenely long area only 19688c2ecf20Sopenharmony_ci * because some of the setup code uses dev->name. It's Wrong(tm) - 19698c2ecf20Sopenharmony_ci * we should be using driver-specific names for all that stuff. 19708c2ecf20Sopenharmony_ci * For now that will do, but we really need to come back and kill 19718c2ecf20Sopenharmony_ci * most of the dev_alloc_name() users later. 19728c2ecf20Sopenharmony_ci */ 19738c2ecf20Sopenharmony_ci rtnl_lock(); 19748c2ecf20Sopenharmony_ci err = dev_alloc_name(ndev, ndev->name); 19758c2ecf20Sopenharmony_ci if (err < 0) { 19768c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "unable to get netdev name: %d\n", err); 19778c2ecf20Sopenharmony_ci goto out_free_irq; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci printk("%s: ns83820.c: 0x22c: %08x, subsystem: %04x:%04x\n", 19818c2ecf20Sopenharmony_ci ndev->name, le32_to_cpu(readl(dev->base + 0x22c)), 19828c2ecf20Sopenharmony_ci pci_dev->subsystem_vendor, pci_dev->subsystem_device); 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci ndev->netdev_ops = &netdev_ops; 19858c2ecf20Sopenharmony_ci ndev->ethtool_ops = &ops; 19868c2ecf20Sopenharmony_ci ndev->watchdog_timeo = 5 * HZ; 19878c2ecf20Sopenharmony_ci pci_set_drvdata(pci_dev, ndev); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci ns83820_do_reset(dev, CR_RST); 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci /* Must reset the ram bist before running it */ 19928c2ecf20Sopenharmony_ci writel(PTSCR_RBIST_RST, dev->base + PTSCR); 19938c2ecf20Sopenharmony_ci ns83820_run_bist(ndev, "sram bist", PTSCR_RBIST_EN, 19948c2ecf20Sopenharmony_ci PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL); 19958c2ecf20Sopenharmony_ci ns83820_run_bist(ndev, "eeprom bist", PTSCR_EEBIST_EN, 0, 19968c2ecf20Sopenharmony_ci PTSCR_EEBIST_FAIL); 19978c2ecf20Sopenharmony_ci ns83820_run_bist(ndev, "eeprom load", PTSCR_EELOAD_EN, 0, 0); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci /* I love config registers */ 20008c2ecf20Sopenharmony_ci dev->CFG_cache = readl(dev->base + CFG); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci if ((dev->CFG_cache & CFG_PCI64_DET)) { 20038c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: detected 64 bit PCI data bus.\n", 20048c2ecf20Sopenharmony_ci ndev->name); 20058c2ecf20Sopenharmony_ci /*dev->CFG_cache |= CFG_DATA64_EN;*/ 20068c2ecf20Sopenharmony_ci if (!(dev->CFG_cache & CFG_DATA64_EN)) 20078c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: EEPROM did not enable 64 bit bus. Disabled.\n", 20088c2ecf20Sopenharmony_ci ndev->name); 20098c2ecf20Sopenharmony_ci } else 20108c2ecf20Sopenharmony_ci dev->CFG_cache &= ~(CFG_DATA64_EN); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci dev->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS | 20138c2ecf20Sopenharmony_ci CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 | 20148c2ecf20Sopenharmony_ci CFG_M64ADDR); 20158c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS | 20168c2ecf20Sopenharmony_ci CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL; 20178c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_REQALG; 20188c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_POW; 20198c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_TMRTEST; 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* When compiled with 64 bit addressing, we must always enable 20228c2ecf20Sopenharmony_ci * the 64 bit descriptor format. 20238c2ecf20Sopenharmony_ci */ 20248c2ecf20Sopenharmony_ci if (sizeof(dma_addr_t) == 8) 20258c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_M64ADDR; 20268c2ecf20Sopenharmony_ci if (using_dac) 20278c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_T64ADDR; 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci /* Big endian mode does not seem to do what the docs suggest */ 20308c2ecf20Sopenharmony_ci dev->CFG_cache &= ~CFG_BEM; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci /* setup optical transceiver if we have one */ 20338c2ecf20Sopenharmony_ci if (dev->CFG_cache & CFG_TBI_EN) { 20348c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: enabling optical transceiver\n", 20358c2ecf20Sopenharmony_ci ndev->name); 20368c2ecf20Sopenharmony_ci writel(readl(dev->base + GPIOR) | 0x3e8, dev->base + GPIOR); 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci /* setup auto negotiation feature advertisement */ 20398c2ecf20Sopenharmony_ci writel(readl(dev->base + TANAR) 20408c2ecf20Sopenharmony_ci | TANAR_HALF_DUP | TANAR_FULL_DUP, 20418c2ecf20Sopenharmony_ci dev->base + TANAR); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* start auto negotiation */ 20448c2ecf20Sopenharmony_ci writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, 20458c2ecf20Sopenharmony_ci dev->base + TBICR); 20468c2ecf20Sopenharmony_ci writel(TBICR_MR_AN_ENABLE, dev->base + TBICR); 20478c2ecf20Sopenharmony_ci dev->linkstate = LINK_AUTONEGOTIATE; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci dev->CFG_cache |= CFG_MODE_1000; 20508c2ecf20Sopenharmony_ci } 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci writel(dev->CFG_cache, dev->base + CFG); 20538c2ecf20Sopenharmony_ci dprintk("CFG: %08x\n", dev->CFG_cache); 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci if (reset_phy) { 20568c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: resetting phy\n", ndev->name); 20578c2ecf20Sopenharmony_ci writel(dev->CFG_cache | CFG_PHY_RST, dev->base + CFG); 20588c2ecf20Sopenharmony_ci msleep(10); 20598c2ecf20Sopenharmony_ci writel(dev->CFG_cache, dev->base + CFG); 20608c2ecf20Sopenharmony_ci } 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci#if 0 /* Huh? This sets the PCI latency register. Should be done via 20638c2ecf20Sopenharmony_ci * the PCI layer. FIXME. 20648c2ecf20Sopenharmony_ci */ 20658c2ecf20Sopenharmony_ci if (readl(dev->base + SRR)) 20668c2ecf20Sopenharmony_ci writel(readl(dev->base+0x20c) | 0xfe00, dev->base + 0x20c); 20678c2ecf20Sopenharmony_ci#endif 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci /* Note! The DMA burst size interacts with packet 20708c2ecf20Sopenharmony_ci * transmission, such that the largest packet that 20718c2ecf20Sopenharmony_ci * can be transmitted is 8192 - FLTH - burst size. 20728c2ecf20Sopenharmony_ci * If only the transmit fifo was larger... 20738c2ecf20Sopenharmony_ci */ 20748c2ecf20Sopenharmony_ci /* Ramit : 1024 DMA is not a good idea, it ends up banging 20758c2ecf20Sopenharmony_ci * some DELL and COMPAQ SMP systems */ 20768c2ecf20Sopenharmony_ci writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512 20778c2ecf20Sopenharmony_ci | ((1600 / 32) * 0x100), 20788c2ecf20Sopenharmony_ci dev->base + TXCFG); 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci /* Flush the interrupt holdoff timer */ 20818c2ecf20Sopenharmony_ci writel(0x000, dev->base + IHR); 20828c2ecf20Sopenharmony_ci writel(0x100, dev->base + IHR); 20838c2ecf20Sopenharmony_ci writel(0x000, dev->base + IHR); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci /* Set Rx to full duplex, don't accept runt, errored, long or length 20868c2ecf20Sopenharmony_ci * range errored packets. Use 512 byte DMA. 20878c2ecf20Sopenharmony_ci */ 20888c2ecf20Sopenharmony_ci /* Ramit : 1024 DMA is not a good idea, it ends up banging 20898c2ecf20Sopenharmony_ci * some DELL and COMPAQ SMP systems 20908c2ecf20Sopenharmony_ci * Turn on ALP, only we are accpeting Jumbo Packets */ 20918c2ecf20Sopenharmony_ci writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD 20928c2ecf20Sopenharmony_ci | RXCFG_STRIPCRC 20938c2ecf20Sopenharmony_ci //| RXCFG_ALP 20948c2ecf20Sopenharmony_ci | (RXCFG_MXDMA512) | 0, dev->base + RXCFG); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci /* Disable priority queueing */ 20978c2ecf20Sopenharmony_ci writel(0, dev->base + PQCR); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci /* Enable IP checksum validation and detetion of VLAN headers. 21008c2ecf20Sopenharmony_ci * Note: do not set the reject options as at least the 0x102 21018c2ecf20Sopenharmony_ci * revision of the chip does not properly accept IP fragments 21028c2ecf20Sopenharmony_ci * at least for UDP. 21038c2ecf20Sopenharmony_ci */ 21048c2ecf20Sopenharmony_ci /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since 21058c2ecf20Sopenharmony_ci * the MAC it calculates the packetsize AFTER stripping the VLAN 21068c2ecf20Sopenharmony_ci * header, and if a VLAN Tagged packet of 64 bytes is received (like 21078c2ecf20Sopenharmony_ci * a ping with a VLAN header) then the card, strips the 4 byte VLAN 21088c2ecf20Sopenharmony_ci * tag and then checks the packet size, so if RXCFG_ARP is not enabled, 21098c2ecf20Sopenharmony_ci * it discrards it!. These guys...... 21108c2ecf20Sopenharmony_ci * also turn on tag stripping if hardware acceleration is enabled 21118c2ecf20Sopenharmony_ci */ 21128c2ecf20Sopenharmony_ci#ifdef NS83820_VLAN_ACCEL_SUPPORT 21138c2ecf20Sopenharmony_ci#define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN|VRCR_VTREN) 21148c2ecf20Sopenharmony_ci#else 21158c2ecf20Sopenharmony_ci#define VRCR_INIT_VALUE (VRCR_IPEN|VRCR_VTDEN) 21168c2ecf20Sopenharmony_ci#endif 21178c2ecf20Sopenharmony_ci writel(VRCR_INIT_VALUE, dev->base + VRCR); 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci /* Enable per-packet TCP/UDP/IP checksumming 21208c2ecf20Sopenharmony_ci * and per packet vlan tag insertion if 21218c2ecf20Sopenharmony_ci * vlan hardware acceleration is enabled 21228c2ecf20Sopenharmony_ci */ 21238c2ecf20Sopenharmony_ci#ifdef NS83820_VLAN_ACCEL_SUPPORT 21248c2ecf20Sopenharmony_ci#define VTCR_INIT_VALUE (VTCR_PPCHK|VTCR_VPPTI) 21258c2ecf20Sopenharmony_ci#else 21268c2ecf20Sopenharmony_ci#define VTCR_INIT_VALUE VTCR_PPCHK 21278c2ecf20Sopenharmony_ci#endif 21288c2ecf20Sopenharmony_ci writel(VTCR_INIT_VALUE, dev->base + VTCR); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci /* Ramit : Enable async and sync pause frames */ 21318c2ecf20Sopenharmony_ci /* writel(0, dev->base + PCR); */ 21328c2ecf20Sopenharmony_ci writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K | 21338c2ecf20Sopenharmony_ci PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT), 21348c2ecf20Sopenharmony_ci dev->base + PCR); 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci /* Disable Wake On Lan */ 21378c2ecf20Sopenharmony_ci writel(0, dev->base + WCSR); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci ns83820_getmac(dev, ndev->dev_addr); 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci /* Yes, we support dumb IP checksum on transmit */ 21428c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_SG; 21438c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_IP_CSUM; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci ndev->min_mtu = 0; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci#ifdef NS83820_VLAN_ACCEL_SUPPORT 21488c2ecf20Sopenharmony_ci /* We also support hardware vlan acceleration */ 21498c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 21508c2ecf20Sopenharmony_ci#endif 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci if (using_dac) { 21538c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: using 64 bit addressing.\n", 21548c2ecf20Sopenharmony_ci ndev->name); 21558c2ecf20Sopenharmony_ci ndev->features |= NETIF_F_HIGHDMA; 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: ns83820 v" VERSION ": DP83820 v%u.%u: %pM io=0x%08lx irq=%d f=%s\n", 21598c2ecf20Sopenharmony_ci ndev->name, 21608c2ecf20Sopenharmony_ci (unsigned)readl(dev->base + SRR) >> 8, 21618c2ecf20Sopenharmony_ci (unsigned)readl(dev->base + SRR) & 0xff, 21628c2ecf20Sopenharmony_ci ndev->dev_addr, addr, pci_dev->irq, 21638c2ecf20Sopenharmony_ci (ndev->features & NETIF_F_HIGHDMA) ? "h,sg" : "sg" 21648c2ecf20Sopenharmony_ci ); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci#ifdef PHY_CODE_IS_FINISHED 21678c2ecf20Sopenharmony_ci ns83820_probe_phy(ndev); 21688c2ecf20Sopenharmony_ci#endif 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci err = register_netdevice(ndev); 21718c2ecf20Sopenharmony_ci if (err) { 21728c2ecf20Sopenharmony_ci printk(KERN_INFO "ns83820: unable to register netdev: %d\n", err); 21738c2ecf20Sopenharmony_ci goto out_cleanup; 21748c2ecf20Sopenharmony_ci } 21758c2ecf20Sopenharmony_ci rtnl_unlock(); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci return 0; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ciout_cleanup: 21808c2ecf20Sopenharmony_ci ns83820_disable_interrupts(dev); /* paranoia */ 21818c2ecf20Sopenharmony_ciout_free_irq: 21828c2ecf20Sopenharmony_ci rtnl_unlock(); 21838c2ecf20Sopenharmony_ci free_irq(pci_dev->irq, ndev); 21848c2ecf20Sopenharmony_ciout_disable: 21858c2ecf20Sopenharmony_ci if (dev->base) 21868c2ecf20Sopenharmony_ci iounmap(dev->base); 21878c2ecf20Sopenharmony_ci dma_free_coherent(&pci_dev->dev, 4 * DESC_SIZE * NR_TX_DESC, 21888c2ecf20Sopenharmony_ci dev->tx_descs, dev->tx_phy_descs); 21898c2ecf20Sopenharmony_ci dma_free_coherent(&pci_dev->dev, 4 * DESC_SIZE * NR_RX_DESC, 21908c2ecf20Sopenharmony_ci dev->rx_info.descs, dev->rx_info.phy_descs); 21918c2ecf20Sopenharmony_ci pci_disable_device(pci_dev); 21928c2ecf20Sopenharmony_ciout_free: 21938c2ecf20Sopenharmony_ci free_netdev(ndev); 21948c2ecf20Sopenharmony_ciout: 21958c2ecf20Sopenharmony_ci return err; 21968c2ecf20Sopenharmony_ci} 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_cistatic void ns83820_remove_one(struct pci_dev *pci_dev) 21998c2ecf20Sopenharmony_ci{ 22008c2ecf20Sopenharmony_ci struct net_device *ndev = pci_get_drvdata(pci_dev); 22018c2ecf20Sopenharmony_ci struct ns83820 *dev = PRIV(ndev); /* ok even if NULL */ 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (!ndev) /* paranoia */ 22048c2ecf20Sopenharmony_ci return; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci ns83820_disable_interrupts(dev); /* paranoia */ 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci unregister_netdev(ndev); 22098c2ecf20Sopenharmony_ci free_irq(dev->pci_dev->irq, ndev); 22108c2ecf20Sopenharmony_ci iounmap(dev->base); 22118c2ecf20Sopenharmony_ci dma_free_coherent(&dev->pci_dev->dev, 4 * DESC_SIZE * NR_TX_DESC, 22128c2ecf20Sopenharmony_ci dev->tx_descs, dev->tx_phy_descs); 22138c2ecf20Sopenharmony_ci dma_free_coherent(&dev->pci_dev->dev, 4 * DESC_SIZE * NR_RX_DESC, 22148c2ecf20Sopenharmony_ci dev->rx_info.descs, dev->rx_info.phy_descs); 22158c2ecf20Sopenharmony_ci pci_disable_device(dev->pci_dev); 22168c2ecf20Sopenharmony_ci free_netdev(ndev); 22178c2ecf20Sopenharmony_ci} 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_cistatic const struct pci_device_id ns83820_pci_tbl[] = { 22208c2ecf20Sopenharmony_ci { 0x100b, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, .driver_data = 0, }, 22218c2ecf20Sopenharmony_ci { 0, }, 22228c2ecf20Sopenharmony_ci}; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_cistatic struct pci_driver driver = { 22258c2ecf20Sopenharmony_ci .name = "ns83820", 22268c2ecf20Sopenharmony_ci .id_table = ns83820_pci_tbl, 22278c2ecf20Sopenharmony_ci .probe = ns83820_init_one, 22288c2ecf20Sopenharmony_ci .remove = ns83820_remove_one, 22298c2ecf20Sopenharmony_ci#if 0 /* FIXME: implement */ 22308c2ecf20Sopenharmony_ci .suspend = , 22318c2ecf20Sopenharmony_ci .resume = , 22328c2ecf20Sopenharmony_ci#endif 22338c2ecf20Sopenharmony_ci}; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_cistatic int __init ns83820_init(void) 22378c2ecf20Sopenharmony_ci{ 22388c2ecf20Sopenharmony_ci printk(KERN_INFO "ns83820.c: National Semiconductor DP83820 10/100/1000 driver.\n"); 22398c2ecf20Sopenharmony_ci return pci_register_driver(&driver); 22408c2ecf20Sopenharmony_ci} 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_cistatic void __exit ns83820_exit(void) 22438c2ecf20Sopenharmony_ci{ 22448c2ecf20Sopenharmony_ci pci_unregister_driver(&driver); 22458c2ecf20Sopenharmony_ci} 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin LaHaise <bcrl@kvack.org>"); 22488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("National Semiconductor DP83820 10/100/1000 driver"); 22498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ns83820_pci_tbl); 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_cimodule_param(lnksts, int, 0); 22548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(lnksts, "Polarity of LNKSTS bit"); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_cimodule_param(ihr, int, 0); 22578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ihr, "Time in 100 us increments to delay interrupts (range 0-127)"); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_cimodule_param(reset_phy, int, 0); 22608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset_phy, "Set to 1 to reset the PHY on startup"); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_cimodule_init(ns83820_init); 22638c2ecf20Sopenharmony_cimodule_exit(ns83820_exit); 2264