1*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
2*8044SWilliam.Kucharski@Sun.COM *
3*8044SWilliam.Kucharski@Sun.COM * sundance.c -- Etherboot device driver for the Sundance ST201 "Alta".
4*8044SWilliam.Kucharski@Sun.COM * Written 2002-2002 by Timothy Legge <tlegge@rogers.com>
5*8044SWilliam.Kucharski@Sun.COM *
6*8044SWilliam.Kucharski@Sun.COM * This program is free software; you can redistribute it and/or modify
7*8044SWilliam.Kucharski@Sun.COM * it under the terms of the GNU General Public License as published by
8*8044SWilliam.Kucharski@Sun.COM * the Free Software Foundation; either version 2 of the License, or
9*8044SWilliam.Kucharski@Sun.COM * (at your option) any later version.
10*8044SWilliam.Kucharski@Sun.COM *
11*8044SWilliam.Kucharski@Sun.COM * This program is distributed in the hope that it will be useful,
12*8044SWilliam.Kucharski@Sun.COM * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*8044SWilliam.Kucharski@Sun.COM * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*8044SWilliam.Kucharski@Sun.COM * GNU General Public License for more details.
15*8044SWilliam.Kucharski@Sun.COM *
16*8044SWilliam.Kucharski@Sun.COM * You should have received a copy of the GNU General Public License
17*8044SWilliam.Kucharski@Sun.COM * along with this program; if not, write to the Free Software
18*8044SWilliam.Kucharski@Sun.COM * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*8044SWilliam.Kucharski@Sun.COM *
20*8044SWilliam.Kucharski@Sun.COM * Portions of this code based on:
21*8044SWilliam.Kucharski@Sun.COM * sundance.c: A Linux device driver for the Sundance ST201 "Alta"
22*8044SWilliam.Kucharski@Sun.COM * Written 1999-2002 by Donald Becker
23*8044SWilliam.Kucharski@Sun.COM *
24*8044SWilliam.Kucharski@Sun.COM * tulip.c: Tulip and Clone Etherboot Driver
25*8044SWilliam.Kucharski@Sun.COM * By Marty Conner
26*8044SWilliam.Kucharski@Sun.COM * Copyright (C) 2001 Entity Cyber, Inc.
27*8044SWilliam.Kucharski@Sun.COM *
28*8044SWilliam.Kucharski@Sun.COM * Linux Driver Version LK1.09a, 10-Jul-2003 (2.4.25)
29*8044SWilliam.Kucharski@Sun.COM *
30*8044SWilliam.Kucharski@Sun.COM * REVISION HISTORY:
31*8044SWilliam.Kucharski@Sun.COM * ================
32*8044SWilliam.Kucharski@Sun.COM * v1.1 01-01-2003 timlegge Initial implementation
33*8044SWilliam.Kucharski@Sun.COM * v1.7 04-10-2003 timlegge Transfers Linux Kernel (30 sec)
34*8044SWilliam.Kucharski@Sun.COM * v1.8 04-13-2003 timlegge Fix multiple transmission bug
35*8044SWilliam.Kucharski@Sun.COM * v1.9 08-19-2003 timlegge Support Multicast
36*8044SWilliam.Kucharski@Sun.COM * v1.10 01-17-2004 timlegge Initial driver output cleanup
37*8044SWilliam.Kucharski@Sun.COM * v1.11 03-21-2004 timlegge Remove unused variables
38*8044SWilliam.Kucharski@Sun.COM * v1.12 03-21-2004 timlegge Remove excess MII defines
39*8044SWilliam.Kucharski@Sun.COM * v1.13 03-24-2004 timlegge Update to Linux 2.4.25 driver
40*8044SWilliam.Kucharski@Sun.COM *
41*8044SWilliam.Kucharski@Sun.COM ****************************************************************************/
42*8044SWilliam.Kucharski@Sun.COM
43*8044SWilliam.Kucharski@Sun.COM /* to get some global routines like printf */
44*8044SWilliam.Kucharski@Sun.COM #include "etherboot.h"
45*8044SWilliam.Kucharski@Sun.COM /* to get the interface to the body of the program */
46*8044SWilliam.Kucharski@Sun.COM #include "nic.h"
47*8044SWilliam.Kucharski@Sun.COM /* to get the PCI support functions, if this is a PCI NIC */
48*8044SWilliam.Kucharski@Sun.COM #include "pci.h"
49*8044SWilliam.Kucharski@Sun.COM #include "timer.h"
50*8044SWilliam.Kucharski@Sun.COM #include "mii.h"
51*8044SWilliam.Kucharski@Sun.COM
52*8044SWilliam.Kucharski@Sun.COM #define drv_version "v1.12"
53*8044SWilliam.Kucharski@Sun.COM #define drv_date "2004-03-21"
54*8044SWilliam.Kucharski@Sun.COM
55*8044SWilliam.Kucharski@Sun.COM typedef unsigned char u8;
56*8044SWilliam.Kucharski@Sun.COM typedef signed char s8;
57*8044SWilliam.Kucharski@Sun.COM typedef unsigned short u16;
58*8044SWilliam.Kucharski@Sun.COM typedef signed short s16;
59*8044SWilliam.Kucharski@Sun.COM typedef unsigned int u32;
60*8044SWilliam.Kucharski@Sun.COM typedef signed int s32;
61*8044SWilliam.Kucharski@Sun.COM
62*8044SWilliam.Kucharski@Sun.COM #define HZ 100
63*8044SWilliam.Kucharski@Sun.COM
64*8044SWilliam.Kucharski@Sun.COM /* Condensed operations for readability. */
65*8044SWilliam.Kucharski@Sun.COM #define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
66*8044SWilliam.Kucharski@Sun.COM #define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
67*8044SWilliam.Kucharski@Sun.COM
68*8044SWilliam.Kucharski@Sun.COM /* May need to be moved to mii.h */
69*8044SWilliam.Kucharski@Sun.COM struct mii_if_info {
70*8044SWilliam.Kucharski@Sun.COM int phy_id;
71*8044SWilliam.Kucharski@Sun.COM int advertising;
72*8044SWilliam.Kucharski@Sun.COM unsigned int full_duplex:1; /* is full duplex? */
73*8044SWilliam.Kucharski@Sun.COM };
74*8044SWilliam.Kucharski@Sun.COM
75*8044SWilliam.Kucharski@Sun.COM //#define EDEBUG
76*8044SWilliam.Kucharski@Sun.COM #ifdef EDEBUG
77*8044SWilliam.Kucharski@Sun.COM #define dprintf(x) printf x
78*8044SWilliam.Kucharski@Sun.COM #else
79*8044SWilliam.Kucharski@Sun.COM #define dprintf(x)
80*8044SWilliam.Kucharski@Sun.COM #endif
81*8044SWilliam.Kucharski@Sun.COM
82*8044SWilliam.Kucharski@Sun.COM #if defined(__sun)
83*8044SWilliam.Kucharski@Sun.COM /* Hack: use grub_strcmp since strcasecmp is undefined */
84*8044SWilliam.Kucharski@Sun.COM #define strcasecmp grub_strcmp
85*8044SWilliam.Kucharski@Sun.COM #endif
86*8044SWilliam.Kucharski@Sun.COM
87*8044SWilliam.Kucharski@Sun.COM
88*8044SWilliam.Kucharski@Sun.COM /* Set the mtu */
89*8044SWilliam.Kucharski@Sun.COM static int mtu = 1514;
90*8044SWilliam.Kucharski@Sun.COM
91*8044SWilliam.Kucharski@Sun.COM /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
92*8044SWilliam.Kucharski@Sun.COM The sundance uses a 64 element hash table based on the Ethernet CRC. */
93*8044SWilliam.Kucharski@Sun.COM // static int multicast_filter_limit = 32;
94*8044SWilliam.Kucharski@Sun.COM
95*8044SWilliam.Kucharski@Sun.COM /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
96*8044SWilliam.Kucharski@Sun.COM Setting to > 1518 effectively disables this feature.
97*8044SWilliam.Kucharski@Sun.COM This chip can receive into any byte alignment buffers, so word-oriented
98*8044SWilliam.Kucharski@Sun.COM archs do not need a copy-align of the IP header. */
99*8044SWilliam.Kucharski@Sun.COM static int rx_copybreak = 0;
100*8044SWilliam.Kucharski@Sun.COM static int flowctrl = 1;
101*8044SWilliam.Kucharski@Sun.COM
102*8044SWilliam.Kucharski@Sun.COM /* Allow forcing the media type */
103*8044SWilliam.Kucharski@Sun.COM /* media[] specifies the media type the NIC operates at.
104*8044SWilliam.Kucharski@Sun.COM autosense Autosensing active media.
105*8044SWilliam.Kucharski@Sun.COM 10mbps_hd 10Mbps half duplex.
106*8044SWilliam.Kucharski@Sun.COM 10mbps_fd 10Mbps full duplex.
107*8044SWilliam.Kucharski@Sun.COM 100mbps_hd 100Mbps half duplex.
108*8044SWilliam.Kucharski@Sun.COM 100mbps_fd 100Mbps full duplex.
109*8044SWilliam.Kucharski@Sun.COM */
110*8044SWilliam.Kucharski@Sun.COM static char media[] = "autosense";
111*8044SWilliam.Kucharski@Sun.COM
112*8044SWilliam.Kucharski@Sun.COM /* Operational parameters that are set at compile time. */
113*8044SWilliam.Kucharski@Sun.COM
114*8044SWilliam.Kucharski@Sun.COM /* As Etherboot uses a Polling driver we can keep the number of rings
115*8044SWilliam.Kucharski@Sun.COM to the minimum number required. In general that is 1 transmit and 4 receive receive rings. However some cards require that
116*8044SWilliam.Kucharski@Sun.COM there be a minimum of 2 rings */
117*8044SWilliam.Kucharski@Sun.COM #define TX_RING_SIZE 2
118*8044SWilliam.Kucharski@Sun.COM #define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
119*8044SWilliam.Kucharski@Sun.COM #define RX_RING_SIZE 4
120*8044SWilliam.Kucharski@Sun.COM
121*8044SWilliam.Kucharski@Sun.COM
122*8044SWilliam.Kucharski@Sun.COM /* Operational parameters that usually are not changed. */
123*8044SWilliam.Kucharski@Sun.COM /* Time in jiffies before concluding the transmitter is hung. */
124*8044SWilliam.Kucharski@Sun.COM #define TX_TIME_OUT (4*HZ)
125*8044SWilliam.Kucharski@Sun.COM #define PKT_BUF_SZ 1536
126*8044SWilliam.Kucharski@Sun.COM
127*8044SWilliam.Kucharski@Sun.COM /* Offsets to the device registers.
128*8044SWilliam.Kucharski@Sun.COM Unlike software-only systems, device drivers interact with complex hardware.
129*8044SWilliam.Kucharski@Sun.COM It's not useful to define symbolic names for every register bit in the
130*8044SWilliam.Kucharski@Sun.COM device. The name can only partially document the semantics and make
131*8044SWilliam.Kucharski@Sun.COM the driver longer and more difficult to read.
132*8044SWilliam.Kucharski@Sun.COM In general, only the important configuration values or bits changed
133*8044SWilliam.Kucharski@Sun.COM multiple times should be defined symbolically.
134*8044SWilliam.Kucharski@Sun.COM */
135*8044SWilliam.Kucharski@Sun.COM enum alta_offsets {
136*8044SWilliam.Kucharski@Sun.COM DMACtrl = 0x00,
137*8044SWilliam.Kucharski@Sun.COM TxListPtr = 0x04,
138*8044SWilliam.Kucharski@Sun.COM TxDMABurstThresh = 0x08,
139*8044SWilliam.Kucharski@Sun.COM TxDMAUrgentThresh = 0x09,
140*8044SWilliam.Kucharski@Sun.COM TxDMAPollPeriod = 0x0a,
141*8044SWilliam.Kucharski@Sun.COM RxDMAStatus = 0x0c,
142*8044SWilliam.Kucharski@Sun.COM RxListPtr = 0x10,
143*8044SWilliam.Kucharski@Sun.COM DebugCtrl0 = 0x1a,
144*8044SWilliam.Kucharski@Sun.COM DebugCtrl1 = 0x1c,
145*8044SWilliam.Kucharski@Sun.COM RxDMABurstThresh = 0x14,
146*8044SWilliam.Kucharski@Sun.COM RxDMAUrgentThresh = 0x15,
147*8044SWilliam.Kucharski@Sun.COM RxDMAPollPeriod = 0x16,
148*8044SWilliam.Kucharski@Sun.COM LEDCtrl = 0x1a,
149*8044SWilliam.Kucharski@Sun.COM ASICCtrl = 0x30,
150*8044SWilliam.Kucharski@Sun.COM EEData = 0x34,
151*8044SWilliam.Kucharski@Sun.COM EECtrl = 0x36,
152*8044SWilliam.Kucharski@Sun.COM TxStartThresh = 0x3c,
153*8044SWilliam.Kucharski@Sun.COM RxEarlyThresh = 0x3e,
154*8044SWilliam.Kucharski@Sun.COM FlashAddr = 0x40,
155*8044SWilliam.Kucharski@Sun.COM FlashData = 0x44,
156*8044SWilliam.Kucharski@Sun.COM TxStatus = 0x46,
157*8044SWilliam.Kucharski@Sun.COM TxFrameId = 0x47,
158*8044SWilliam.Kucharski@Sun.COM DownCounter = 0x18,
159*8044SWilliam.Kucharski@Sun.COM IntrClear = 0x4a,
160*8044SWilliam.Kucharski@Sun.COM IntrEnable = 0x4c,
161*8044SWilliam.Kucharski@Sun.COM IntrStatus = 0x4e,
162*8044SWilliam.Kucharski@Sun.COM MACCtrl0 = 0x50,
163*8044SWilliam.Kucharski@Sun.COM MACCtrl1 = 0x52,
164*8044SWilliam.Kucharski@Sun.COM StationAddr = 0x54,
165*8044SWilliam.Kucharski@Sun.COM MaxFrameSize = 0x5A,
166*8044SWilliam.Kucharski@Sun.COM RxMode = 0x5c,
167*8044SWilliam.Kucharski@Sun.COM MIICtrl = 0x5e,
168*8044SWilliam.Kucharski@Sun.COM MulticastFilter0 = 0x60,
169*8044SWilliam.Kucharski@Sun.COM MulticastFilter1 = 0x64,
170*8044SWilliam.Kucharski@Sun.COM RxOctetsLow = 0x68,
171*8044SWilliam.Kucharski@Sun.COM RxOctetsHigh = 0x6a,
172*8044SWilliam.Kucharski@Sun.COM TxOctetsLow = 0x6c,
173*8044SWilliam.Kucharski@Sun.COM TxOctetsHigh = 0x6e,
174*8044SWilliam.Kucharski@Sun.COM TxFramesOK = 0x70,
175*8044SWilliam.Kucharski@Sun.COM RxFramesOK = 0x72,
176*8044SWilliam.Kucharski@Sun.COM StatsCarrierError = 0x74,
177*8044SWilliam.Kucharski@Sun.COM StatsLateColl = 0x75,
178*8044SWilliam.Kucharski@Sun.COM StatsMultiColl = 0x76,
179*8044SWilliam.Kucharski@Sun.COM StatsOneColl = 0x77,
180*8044SWilliam.Kucharski@Sun.COM StatsTxDefer = 0x78,
181*8044SWilliam.Kucharski@Sun.COM RxMissed = 0x79,
182*8044SWilliam.Kucharski@Sun.COM StatsTxXSDefer = 0x7a,
183*8044SWilliam.Kucharski@Sun.COM StatsTxAbort = 0x7b,
184*8044SWilliam.Kucharski@Sun.COM StatsBcastTx = 0x7c,
185*8044SWilliam.Kucharski@Sun.COM StatsBcastRx = 0x7d,
186*8044SWilliam.Kucharski@Sun.COM StatsMcastTx = 0x7e,
187*8044SWilliam.Kucharski@Sun.COM StatsMcastRx = 0x7f,
188*8044SWilliam.Kucharski@Sun.COM /* Aliased and bogus values! */
189*8044SWilliam.Kucharski@Sun.COM RxStatus = 0x0c,
190*8044SWilliam.Kucharski@Sun.COM };
191*8044SWilliam.Kucharski@Sun.COM enum ASICCtrl_HiWord_bit {
192*8044SWilliam.Kucharski@Sun.COM GlobalReset = 0x0001,
193*8044SWilliam.Kucharski@Sun.COM RxReset = 0x0002,
194*8044SWilliam.Kucharski@Sun.COM TxReset = 0x0004,
195*8044SWilliam.Kucharski@Sun.COM DMAReset = 0x0008,
196*8044SWilliam.Kucharski@Sun.COM FIFOReset = 0x0010,
197*8044SWilliam.Kucharski@Sun.COM NetworkReset = 0x0020,
198*8044SWilliam.Kucharski@Sun.COM HostReset = 0x0040,
199*8044SWilliam.Kucharski@Sun.COM ResetBusy = 0x0400,
200*8044SWilliam.Kucharski@Sun.COM };
201*8044SWilliam.Kucharski@Sun.COM
202*8044SWilliam.Kucharski@Sun.COM /* Bits in the interrupt status/mask registers. */
203*8044SWilliam.Kucharski@Sun.COM enum intr_status_bits {
204*8044SWilliam.Kucharski@Sun.COM IntrSummary = 0x0001, IntrPCIErr = 0x0002, IntrMACCtrl = 0x0008,
205*8044SWilliam.Kucharski@Sun.COM IntrTxDone = 0x0004, IntrRxDone = 0x0010, IntrRxStart = 0x0020,
206*8044SWilliam.Kucharski@Sun.COM IntrDrvRqst = 0x0040,
207*8044SWilliam.Kucharski@Sun.COM StatsMax = 0x0080, LinkChange = 0x0100,
208*8044SWilliam.Kucharski@Sun.COM IntrTxDMADone = 0x0200, IntrRxDMADone = 0x0400,
209*8044SWilliam.Kucharski@Sun.COM };
210*8044SWilliam.Kucharski@Sun.COM
211*8044SWilliam.Kucharski@Sun.COM /* Bits in the RxMode register. */
212*8044SWilliam.Kucharski@Sun.COM enum rx_mode_bits {
213*8044SWilliam.Kucharski@Sun.COM AcceptAllIPMulti = 0x20, AcceptMultiHash = 0x10, AcceptAll = 0x08,
214*8044SWilliam.Kucharski@Sun.COM AcceptBroadcast = 0x04, AcceptMulticast = 0x02, AcceptMyPhys =
215*8044SWilliam.Kucharski@Sun.COM 0x01,
216*8044SWilliam.Kucharski@Sun.COM };
217*8044SWilliam.Kucharski@Sun.COM /* Bits in MACCtrl. */
218*8044SWilliam.Kucharski@Sun.COM enum mac_ctrl0_bits {
219*8044SWilliam.Kucharski@Sun.COM EnbFullDuplex = 0x20, EnbRcvLargeFrame = 0x40,
220*8044SWilliam.Kucharski@Sun.COM EnbFlowCtrl = 0x100, EnbPassRxCRC = 0x200,
221*8044SWilliam.Kucharski@Sun.COM };
222*8044SWilliam.Kucharski@Sun.COM enum mac_ctrl1_bits {
223*8044SWilliam.Kucharski@Sun.COM StatsEnable = 0x0020, StatsDisable = 0x0040, StatsEnabled = 0x0080,
224*8044SWilliam.Kucharski@Sun.COM TxEnable = 0x0100, TxDisable = 0x0200, TxEnabled = 0x0400,
225*8044SWilliam.Kucharski@Sun.COM RxEnable = 0x0800, RxDisable = 0x1000, RxEnabled = 0x2000,
226*8044SWilliam.Kucharski@Sun.COM };
227*8044SWilliam.Kucharski@Sun.COM
228*8044SWilliam.Kucharski@Sun.COM /* The Rx and Tx buffer descriptors.
229*8044SWilliam.Kucharski@Sun.COM Using only 32 bit fields simplifies software endian correction.
230*8044SWilliam.Kucharski@Sun.COM This structure must be aligned, and should avoid spanning cache lines.
231*8044SWilliam.Kucharski@Sun.COM */
232*8044SWilliam.Kucharski@Sun.COM struct netdev_desc {
233*8044SWilliam.Kucharski@Sun.COM u32 next_desc;
234*8044SWilliam.Kucharski@Sun.COM u32 status;
235*8044SWilliam.Kucharski@Sun.COM u32 addr;
236*8044SWilliam.Kucharski@Sun.COM u32 length;
237*8044SWilliam.Kucharski@Sun.COM };
238*8044SWilliam.Kucharski@Sun.COM
239*8044SWilliam.Kucharski@Sun.COM /* Bits in netdev_desc.status */
240*8044SWilliam.Kucharski@Sun.COM enum desc_status_bits {
241*8044SWilliam.Kucharski@Sun.COM DescOwn = 0x8000,
242*8044SWilliam.Kucharski@Sun.COM DescEndPacket = 0x4000,
243*8044SWilliam.Kucharski@Sun.COM DescEndRing = 0x2000,
244*8044SWilliam.Kucharski@Sun.COM LastFrag = 0x80000000,
245*8044SWilliam.Kucharski@Sun.COM DescIntrOnTx = 0x8000,
246*8044SWilliam.Kucharski@Sun.COM DescIntrOnDMADone = 0x80000000,
247*8044SWilliam.Kucharski@Sun.COM DisableAlign = 0x00000001,
248*8044SWilliam.Kucharski@Sun.COM };
249*8044SWilliam.Kucharski@Sun.COM
250*8044SWilliam.Kucharski@Sun.COM /**********************************************
251*8044SWilliam.Kucharski@Sun.COM * Descriptor Ring and Buffer defination
252*8044SWilliam.Kucharski@Sun.COM ***********************************************/
253*8044SWilliam.Kucharski@Sun.COM /* Define the TX Descriptor */
254*8044SWilliam.Kucharski@Sun.COM static struct netdev_desc tx_ring[TX_RING_SIZE];
255*8044SWilliam.Kucharski@Sun.COM
256*8044SWilliam.Kucharski@Sun.COM /* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor.
257*8044SWilliam.Kucharski@Sun.COM All descriptors point to a part of this buffer */
258*8044SWilliam.Kucharski@Sun.COM static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
259*8044SWilliam.Kucharski@Sun.COM
260*8044SWilliam.Kucharski@Sun.COM /* Define the RX Descriptor */
261*8044SWilliam.Kucharski@Sun.COM static struct netdev_desc rx_ring[RX_RING_SIZE];
262*8044SWilliam.Kucharski@Sun.COM
263*8044SWilliam.Kucharski@Sun.COM /* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor.
264*8044SWilliam.Kucharski@Sun.COM All descriptors point to a part of this buffer */
265*8044SWilliam.Kucharski@Sun.COM static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
266*8044SWilliam.Kucharski@Sun.COM
267*8044SWilliam.Kucharski@Sun.COM /* FIXME: Move BASE to the private structure */
268*8044SWilliam.Kucharski@Sun.COM static u32 BASE;
269*8044SWilliam.Kucharski@Sun.COM #define EEPROM_SIZE 128
270*8044SWilliam.Kucharski@Sun.COM
271*8044SWilliam.Kucharski@Sun.COM enum pci_id_flags_bits {
272*8044SWilliam.Kucharski@Sun.COM PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
273*8044SWilliam.Kucharski@Sun.COM PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
274*8044SWilliam.Kucharski@Sun.COM 2 << 4, PCI_ADDR3 = 3 << 4,
275*8044SWilliam.Kucharski@Sun.COM };
276*8044SWilliam.Kucharski@Sun.COM
277*8044SWilliam.Kucharski@Sun.COM enum chip_capability_flags { CanHaveMII = 1, KendinPktDropBug = 2, };
278*8044SWilliam.Kucharski@Sun.COM #define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
279*8044SWilliam.Kucharski@Sun.COM
280*8044SWilliam.Kucharski@Sun.COM #define MII_CNT 4
281*8044SWilliam.Kucharski@Sun.COM struct sundance_private {
282*8044SWilliam.Kucharski@Sun.COM const char *nic_name;
283*8044SWilliam.Kucharski@Sun.COM /* Frequently used values */
284*8044SWilliam.Kucharski@Sun.COM
285*8044SWilliam.Kucharski@Sun.COM unsigned int cur_rx; /* Producer/consumer ring indicies */
286*8044SWilliam.Kucharski@Sun.COM unsigned int mtu;
287*8044SWilliam.Kucharski@Sun.COM
288*8044SWilliam.Kucharski@Sun.COM /* These values keep track of the tranceiver/media in use */
289*8044SWilliam.Kucharski@Sun.COM unsigned int flowctrl:1;
290*8044SWilliam.Kucharski@Sun.COM unsigned int an_enable:1;
291*8044SWilliam.Kucharski@Sun.COM
292*8044SWilliam.Kucharski@Sun.COM unsigned int speed;
293*8044SWilliam.Kucharski@Sun.COM
294*8044SWilliam.Kucharski@Sun.COM /* MII tranceiver section */
295*8044SWilliam.Kucharski@Sun.COM struct mii_if_info mii_if;
296*8044SWilliam.Kucharski@Sun.COM int mii_preamble_required;
297*8044SWilliam.Kucharski@Sun.COM unsigned char phys[MII_CNT];
298*8044SWilliam.Kucharski@Sun.COM unsigned char pci_rev_id;
299*8044SWilliam.Kucharski@Sun.COM } sdx;
300*8044SWilliam.Kucharski@Sun.COM
301*8044SWilliam.Kucharski@Sun.COM static struct sundance_private *sdc;
302*8044SWilliam.Kucharski@Sun.COM
303*8044SWilliam.Kucharski@Sun.COM /* Station Address location within the EEPROM */
304*8044SWilliam.Kucharski@Sun.COM #define EEPROM_SA_OFFSET 0x10
305*8044SWilliam.Kucharski@Sun.COM #define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \
306*8044SWilliam.Kucharski@Sun.COM IntrDrvRqst | IntrTxDone | StatsMax | \
307*8044SWilliam.Kucharski@Sun.COM LinkChange)
308*8044SWilliam.Kucharski@Sun.COM
309*8044SWilliam.Kucharski@Sun.COM static int eeprom_read(long ioaddr, int location);
310*8044SWilliam.Kucharski@Sun.COM static int mdio_read(struct nic *nic, int phy_id, unsigned int location);
311*8044SWilliam.Kucharski@Sun.COM static void mdio_write(struct nic *nic, int phy_id, unsigned int location,
312*8044SWilliam.Kucharski@Sun.COM int value);
313*8044SWilliam.Kucharski@Sun.COM static void set_rx_mode(struct nic *nic);
314*8044SWilliam.Kucharski@Sun.COM
check_duplex(struct nic * nic)315*8044SWilliam.Kucharski@Sun.COM static void check_duplex(struct nic *nic)
316*8044SWilliam.Kucharski@Sun.COM {
317*8044SWilliam.Kucharski@Sun.COM int mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
318*8044SWilliam.Kucharski@Sun.COM int negotiated = mii_lpa & sdc->mii_if.advertising;
319*8044SWilliam.Kucharski@Sun.COM int duplex;
320*8044SWilliam.Kucharski@Sun.COM
321*8044SWilliam.Kucharski@Sun.COM /* Force media */
322*8044SWilliam.Kucharski@Sun.COM if (!sdc->an_enable || mii_lpa == 0xffff) {
323*8044SWilliam.Kucharski@Sun.COM if (sdc->mii_if.full_duplex)
324*8044SWilliam.Kucharski@Sun.COM outw(inw(BASE + MACCtrl0) | EnbFullDuplex,
325*8044SWilliam.Kucharski@Sun.COM BASE + MACCtrl0);
326*8044SWilliam.Kucharski@Sun.COM return;
327*8044SWilliam.Kucharski@Sun.COM }
328*8044SWilliam.Kucharski@Sun.COM
329*8044SWilliam.Kucharski@Sun.COM /* Autonegotiation */
330*8044SWilliam.Kucharski@Sun.COM duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
331*8044SWilliam.Kucharski@Sun.COM if (sdc->mii_if.full_duplex != duplex) {
332*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex = duplex;
333*8044SWilliam.Kucharski@Sun.COM dprintf(("%s: Setting %s-duplex based on MII #%d "
334*8044SWilliam.Kucharski@Sun.COM "negotiated capability %4.4x.\n", sdc->nic_name,
335*8044SWilliam.Kucharski@Sun.COM duplex ? "full" : "half", sdc->phys[0],
336*8044SWilliam.Kucharski@Sun.COM negotiated));
337*8044SWilliam.Kucharski@Sun.COM outw(inw(BASE + MACCtrl0) | duplex ? 0x20 : 0,
338*8044SWilliam.Kucharski@Sun.COM BASE + MACCtrl0);
339*8044SWilliam.Kucharski@Sun.COM }
340*8044SWilliam.Kucharski@Sun.COM }
341*8044SWilliam.Kucharski@Sun.COM
342*8044SWilliam.Kucharski@Sun.COM
343*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
344*8044SWilliam.Kucharski@Sun.COM * init_ring - setup the tx and rx descriptors
345*8044SWilliam.Kucharski@Sun.COM *************************************************************************/
init_ring(struct nic * nic __unused)346*8044SWilliam.Kucharski@Sun.COM static void init_ring(struct nic *nic __unused)
347*8044SWilliam.Kucharski@Sun.COM {
348*8044SWilliam.Kucharski@Sun.COM int i;
349*8044SWilliam.Kucharski@Sun.COM
350*8044SWilliam.Kucharski@Sun.COM sdc->cur_rx = 0;
351*8044SWilliam.Kucharski@Sun.COM
352*8044SWilliam.Kucharski@Sun.COM /* Initialize all the Rx descriptors */
353*8044SWilliam.Kucharski@Sun.COM for (i = 0; i < RX_RING_SIZE; i++) {
354*8044SWilliam.Kucharski@Sun.COM rx_ring[i].next_desc = virt_to_le32desc(&rx_ring[i + 1]);
355*8044SWilliam.Kucharski@Sun.COM rx_ring[i].status = 0;
356*8044SWilliam.Kucharski@Sun.COM rx_ring[i].length = 0;
357*8044SWilliam.Kucharski@Sun.COM rx_ring[i].addr = 0;
358*8044SWilliam.Kucharski@Sun.COM }
359*8044SWilliam.Kucharski@Sun.COM
360*8044SWilliam.Kucharski@Sun.COM /* Mark the last entry as wrapping the ring */
361*8044SWilliam.Kucharski@Sun.COM rx_ring[i - 1].next_desc = virt_to_le32desc(&rx_ring[0]);
362*8044SWilliam.Kucharski@Sun.COM
363*8044SWilliam.Kucharski@Sun.COM for (i = 0; i < RX_RING_SIZE; i++) {
364*8044SWilliam.Kucharski@Sun.COM rx_ring[i].addr = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
365*8044SWilliam.Kucharski@Sun.COM rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
366*8044SWilliam.Kucharski@Sun.COM }
367*8044SWilliam.Kucharski@Sun.COM
368*8044SWilliam.Kucharski@Sun.COM /* We only use one transmit buffer, but two
369*8044SWilliam.Kucharski@Sun.COM * descriptors so transmit engines have somewhere
370*8044SWilliam.Kucharski@Sun.COM * to point should they feel the need */
371*8044SWilliam.Kucharski@Sun.COM tx_ring[0].status = 0x00000000;
372*8044SWilliam.Kucharski@Sun.COM tx_ring[0].addr = virt_to_bus(&txb[0]);
373*8044SWilliam.Kucharski@Sun.COM tx_ring[0].next_desc = 0; /* virt_to_bus(&tx_ring[1]); */
374*8044SWilliam.Kucharski@Sun.COM
375*8044SWilliam.Kucharski@Sun.COM /* This descriptor is never used */
376*8044SWilliam.Kucharski@Sun.COM tx_ring[1].status = 0x00000000;
377*8044SWilliam.Kucharski@Sun.COM tx_ring[1].addr = 0; /*virt_to_bus(&txb[0]); */
378*8044SWilliam.Kucharski@Sun.COM tx_ring[1].next_desc = 0;
379*8044SWilliam.Kucharski@Sun.COM
380*8044SWilliam.Kucharski@Sun.COM /* Mark the last entry as wrapping the ring,
381*8044SWilliam.Kucharski@Sun.COM * though this should never happen */
382*8044SWilliam.Kucharski@Sun.COM tx_ring[1].length = cpu_to_le32(LastFrag | PKT_BUF_SZ);
383*8044SWilliam.Kucharski@Sun.COM }
384*8044SWilliam.Kucharski@Sun.COM
385*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
386*8044SWilliam.Kucharski@Sun.COM * RESET - Reset Adapter
387*8044SWilliam.Kucharski@Sun.COM * ***********************************************************************/
sundance_reset(struct nic * nic)388*8044SWilliam.Kucharski@Sun.COM static void sundance_reset(struct nic *nic)
389*8044SWilliam.Kucharski@Sun.COM {
390*8044SWilliam.Kucharski@Sun.COM int i;
391*8044SWilliam.Kucharski@Sun.COM
392*8044SWilliam.Kucharski@Sun.COM init_ring(nic);
393*8044SWilliam.Kucharski@Sun.COM
394*8044SWilliam.Kucharski@Sun.COM outl(virt_to_le32desc(&rx_ring[0]), BASE + RxListPtr);
395*8044SWilliam.Kucharski@Sun.COM /* The Tx List Pointer is written as packets are queued */
396*8044SWilliam.Kucharski@Sun.COM
397*8044SWilliam.Kucharski@Sun.COM /* Initialize other registers. */
398*8044SWilliam.Kucharski@Sun.COM /* __set_mac_addr(dev); */
399*8044SWilliam.Kucharski@Sun.COM {
400*8044SWilliam.Kucharski@Sun.COM u16 addr16;
401*8044SWilliam.Kucharski@Sun.COM
402*8044SWilliam.Kucharski@Sun.COM addr16 = (nic->node_addr[0] | (nic->node_addr[1] << 8));
403*8044SWilliam.Kucharski@Sun.COM outw(addr16, BASE + StationAddr);
404*8044SWilliam.Kucharski@Sun.COM addr16 = (nic->node_addr[2] | (nic->node_addr[3] << 8));
405*8044SWilliam.Kucharski@Sun.COM outw(addr16, BASE + StationAddr + 2);
406*8044SWilliam.Kucharski@Sun.COM addr16 = (nic->node_addr[4] | (nic->node_addr[5] << 8));
407*8044SWilliam.Kucharski@Sun.COM outw(addr16, BASE + StationAddr + 4);
408*8044SWilliam.Kucharski@Sun.COM }
409*8044SWilliam.Kucharski@Sun.COM
410*8044SWilliam.Kucharski@Sun.COM outw(sdc->mtu + 14, BASE + MaxFrameSize);
411*8044SWilliam.Kucharski@Sun.COM if (sdc->mtu > 2047) /* this will never happen with default options */
412*8044SWilliam.Kucharski@Sun.COM outl(inl(BASE + ASICCtrl) | 0x0c, BASE + ASICCtrl);
413*8044SWilliam.Kucharski@Sun.COM
414*8044SWilliam.Kucharski@Sun.COM set_rx_mode(nic);
415*8044SWilliam.Kucharski@Sun.COM
416*8044SWilliam.Kucharski@Sun.COM outw(0, BASE + DownCounter);
417*8044SWilliam.Kucharski@Sun.COM /* Set the chip to poll every N*30nsec */
418*8044SWilliam.Kucharski@Sun.COM outb(100, BASE + RxDMAPollPeriod);
419*8044SWilliam.Kucharski@Sun.COM
420*8044SWilliam.Kucharski@Sun.COM /* Fix DFE-580TX packet drop issue */
421*8044SWilliam.Kucharski@Sun.COM if (sdc->pci_rev_id >= 0x14)
422*8044SWilliam.Kucharski@Sun.COM writeb(0x01, BASE + DebugCtrl1);
423*8044SWilliam.Kucharski@Sun.COM
424*8044SWilliam.Kucharski@Sun.COM outw(RxEnable | TxEnable, BASE + MACCtrl1);
425*8044SWilliam.Kucharski@Sun.COM
426*8044SWilliam.Kucharski@Sun.COM /* Construct a perfect filter frame with the mac address as first match
427*8044SWilliam.Kucharski@Sun.COM * and broadcast for all others */
428*8044SWilliam.Kucharski@Sun.COM for (i = 0; i < 192; i++)
429*8044SWilliam.Kucharski@Sun.COM txb[i] = 0xFF;
430*8044SWilliam.Kucharski@Sun.COM
431*8044SWilliam.Kucharski@Sun.COM txb[0] = nic->node_addr[0];
432*8044SWilliam.Kucharski@Sun.COM txb[1] = nic->node_addr[1];
433*8044SWilliam.Kucharski@Sun.COM txb[2] = nic->node_addr[2];
434*8044SWilliam.Kucharski@Sun.COM txb[3] = nic->node_addr[3];
435*8044SWilliam.Kucharski@Sun.COM txb[4] = nic->node_addr[4];
436*8044SWilliam.Kucharski@Sun.COM txb[5] = nic->node_addr[5];
437*8044SWilliam.Kucharski@Sun.COM
438*8044SWilliam.Kucharski@Sun.COM dprintf(("%s: Done sundance_reset, status: Rx %hX Tx %hX "
439*8044SWilliam.Kucharski@Sun.COM "MAC Control %hX, %hX %hX\n",
440*8044SWilliam.Kucharski@Sun.COM sdc->nic_name, (int) inl(BASE + RxStatus),
441*8044SWilliam.Kucharski@Sun.COM (int) inw(BASE + TxStatus), (int) inl(BASE + MACCtrl0),
442*8044SWilliam.Kucharski@Sun.COM (int) inw(BASE + MACCtrl1), (int) inw(BASE + MACCtrl0)));
443*8044SWilliam.Kucharski@Sun.COM }
444*8044SWilliam.Kucharski@Sun.COM
445*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
446*8044SWilliam.Kucharski@Sun.COM IRQ - Wait for a frame
447*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
sundance_irq(struct nic * nic,irq_action_t action)448*8044SWilliam.Kucharski@Sun.COM void sundance_irq ( struct nic *nic, irq_action_t action ) {
449*8044SWilliam.Kucharski@Sun.COM unsigned int intr_status;
450*8044SWilliam.Kucharski@Sun.COM
451*8044SWilliam.Kucharski@Sun.COM switch ( action ) {
452*8044SWilliam.Kucharski@Sun.COM case DISABLE :
453*8044SWilliam.Kucharski@Sun.COM case ENABLE :
454*8044SWilliam.Kucharski@Sun.COM intr_status = inw(nic->ioaddr + IntrStatus);
455*8044SWilliam.Kucharski@Sun.COM intr_status = intr_status & ~DEFAULT_INTR;
456*8044SWilliam.Kucharski@Sun.COM if ( action == ENABLE )
457*8044SWilliam.Kucharski@Sun.COM intr_status = intr_status | DEFAULT_INTR;
458*8044SWilliam.Kucharski@Sun.COM outw(intr_status, nic->ioaddr + IntrEnable);
459*8044SWilliam.Kucharski@Sun.COM break;
460*8044SWilliam.Kucharski@Sun.COM case FORCE :
461*8044SWilliam.Kucharski@Sun.COM outw(0x0200, BASE + ASICCtrl);
462*8044SWilliam.Kucharski@Sun.COM break;
463*8044SWilliam.Kucharski@Sun.COM }
464*8044SWilliam.Kucharski@Sun.COM }
465*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
466*8044SWilliam.Kucharski@Sun.COM POLL - Wait for a frame
467*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
sundance_poll(struct nic * nic,int retreive)468*8044SWilliam.Kucharski@Sun.COM static int sundance_poll(struct nic *nic, int retreive)
469*8044SWilliam.Kucharski@Sun.COM {
470*8044SWilliam.Kucharski@Sun.COM /* return true if there's an ethernet packet ready to read */
471*8044SWilliam.Kucharski@Sun.COM /* nic->packet should contain data on return */
472*8044SWilliam.Kucharski@Sun.COM /* nic->packetlen should contain length of data */
473*8044SWilliam.Kucharski@Sun.COM int entry = sdc->cur_rx % RX_RING_SIZE;
474*8044SWilliam.Kucharski@Sun.COM u32 frame_status = le32_to_cpu(rx_ring[entry].status);
475*8044SWilliam.Kucharski@Sun.COM int intr_status;
476*8044SWilliam.Kucharski@Sun.COM int pkt_len = 0;
477*8044SWilliam.Kucharski@Sun.COM
478*8044SWilliam.Kucharski@Sun.COM if (!(frame_status & DescOwn))
479*8044SWilliam.Kucharski@Sun.COM return 0;
480*8044SWilliam.Kucharski@Sun.COM
481*8044SWilliam.Kucharski@Sun.COM /* There is a packet ready */
482*8044SWilliam.Kucharski@Sun.COM if(!retreive)
483*8044SWilliam.Kucharski@Sun.COM return 1;
484*8044SWilliam.Kucharski@Sun.COM
485*8044SWilliam.Kucharski@Sun.COM intr_status = inw(nic->ioaddr + IntrStatus);
486*8044SWilliam.Kucharski@Sun.COM outw(intr_status, nic->ioaddr + IntrStatus);
487*8044SWilliam.Kucharski@Sun.COM
488*8044SWilliam.Kucharski@Sun.COM pkt_len = frame_status & 0x1fff;
489*8044SWilliam.Kucharski@Sun.COM
490*8044SWilliam.Kucharski@Sun.COM if (frame_status & 0x001f4000) {
491*8044SWilliam.Kucharski@Sun.COM dprintf(("Polling frame_status error\n")); /* Do we really care about this */
492*8044SWilliam.Kucharski@Sun.COM } else {
493*8044SWilliam.Kucharski@Sun.COM if (pkt_len < rx_copybreak) {
494*8044SWilliam.Kucharski@Sun.COM /* FIXME: What should happen Will this ever occur */
495*8044SWilliam.Kucharski@Sun.COM printf("Poll Error: pkt_len < rx_copybreak");
496*8044SWilliam.Kucharski@Sun.COM } else {
497*8044SWilliam.Kucharski@Sun.COM nic->packetlen = pkt_len;
498*8044SWilliam.Kucharski@Sun.COM memcpy(nic->packet, rxb +
499*8044SWilliam.Kucharski@Sun.COM (sdc->cur_rx * PKT_BUF_SZ), nic->packetlen);
500*8044SWilliam.Kucharski@Sun.COM
501*8044SWilliam.Kucharski@Sun.COM }
502*8044SWilliam.Kucharski@Sun.COM }
503*8044SWilliam.Kucharski@Sun.COM rx_ring[entry].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
504*8044SWilliam.Kucharski@Sun.COM rx_ring[entry].status = 0;
505*8044SWilliam.Kucharski@Sun.COM entry++;
506*8044SWilliam.Kucharski@Sun.COM sdc->cur_rx = entry % RX_RING_SIZE;
507*8044SWilliam.Kucharski@Sun.COM outw(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone),
508*8044SWilliam.Kucharski@Sun.COM nic->ioaddr + IntrStatus);
509*8044SWilliam.Kucharski@Sun.COM return 1;
510*8044SWilliam.Kucharski@Sun.COM }
511*8044SWilliam.Kucharski@Sun.COM
512*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
513*8044SWilliam.Kucharski@Sun.COM TRANSMIT - Transmit a frame
514*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
sundance_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)515*8044SWilliam.Kucharski@Sun.COM static void sundance_transmit(struct nic *nic, const char *d, /* Destination */
516*8044SWilliam.Kucharski@Sun.COM unsigned int t, /* Type */
517*8044SWilliam.Kucharski@Sun.COM unsigned int s, /* size */
518*8044SWilliam.Kucharski@Sun.COM const char *p)
519*8044SWilliam.Kucharski@Sun.COM { /* Packet */
520*8044SWilliam.Kucharski@Sun.COM u16 nstype;
521*8044SWilliam.Kucharski@Sun.COM u32 to;
522*8044SWilliam.Kucharski@Sun.COM
523*8044SWilliam.Kucharski@Sun.COM /* Disable the Tx */
524*8044SWilliam.Kucharski@Sun.COM outw(TxDisable, BASE + MACCtrl1);
525*8044SWilliam.Kucharski@Sun.COM
526*8044SWilliam.Kucharski@Sun.COM memcpy(txb, d, ETH_ALEN);
527*8044SWilliam.Kucharski@Sun.COM memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
528*8044SWilliam.Kucharski@Sun.COM nstype = htons((u16) t);
529*8044SWilliam.Kucharski@Sun.COM memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
530*8044SWilliam.Kucharski@Sun.COM memcpy(txb + ETH_HLEN, p, s);
531*8044SWilliam.Kucharski@Sun.COM
532*8044SWilliam.Kucharski@Sun.COM s += ETH_HLEN;
533*8044SWilliam.Kucharski@Sun.COM s &= 0x0FFF;
534*8044SWilliam.Kucharski@Sun.COM while (s < ETH_ZLEN)
535*8044SWilliam.Kucharski@Sun.COM txb[s++] = '\0';
536*8044SWilliam.Kucharski@Sun.COM
537*8044SWilliam.Kucharski@Sun.COM /* Setup the transmit descriptor */
538*8044SWilliam.Kucharski@Sun.COM tx_ring[0].length = cpu_to_le32(s | LastFrag);
539*8044SWilliam.Kucharski@Sun.COM tx_ring[0].status = cpu_to_le32(0x00000001);
540*8044SWilliam.Kucharski@Sun.COM
541*8044SWilliam.Kucharski@Sun.COM /* Point to transmit descriptor */
542*8044SWilliam.Kucharski@Sun.COM outl(virt_to_le32desc(&tx_ring[0]), BASE + TxListPtr);
543*8044SWilliam.Kucharski@Sun.COM
544*8044SWilliam.Kucharski@Sun.COM /* Enable Tx */
545*8044SWilliam.Kucharski@Sun.COM outw(TxEnable, BASE + MACCtrl1);
546*8044SWilliam.Kucharski@Sun.COM /* Trigger an immediate send */
547*8044SWilliam.Kucharski@Sun.COM outw(0, BASE + TxStatus);
548*8044SWilliam.Kucharski@Sun.COM
549*8044SWilliam.Kucharski@Sun.COM to = currticks() + TX_TIME_OUT;
550*8044SWilliam.Kucharski@Sun.COM while (!(tx_ring[0].status & 0x00010000) && (currticks() < to)); /* wait */
551*8044SWilliam.Kucharski@Sun.COM
552*8044SWilliam.Kucharski@Sun.COM if (currticks() >= to) {
553*8044SWilliam.Kucharski@Sun.COM printf("TX Time Out");
554*8044SWilliam.Kucharski@Sun.COM }
555*8044SWilliam.Kucharski@Sun.COM /* Disable Tx */
556*8044SWilliam.Kucharski@Sun.COM outw(TxDisable, BASE + MACCtrl1);
557*8044SWilliam.Kucharski@Sun.COM
558*8044SWilliam.Kucharski@Sun.COM }
559*8044SWilliam.Kucharski@Sun.COM
560*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
561*8044SWilliam.Kucharski@Sun.COM DISABLE - Turn off ethernet interface
562*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
sundance_disable(struct dev * dev __unused)563*8044SWilliam.Kucharski@Sun.COM static void sundance_disable(struct dev *dev __unused)
564*8044SWilliam.Kucharski@Sun.COM {
565*8044SWilliam.Kucharski@Sun.COM /* put the card in its initial state */
566*8044SWilliam.Kucharski@Sun.COM /* This function serves 3 purposes.
567*8044SWilliam.Kucharski@Sun.COM * This disables DMA and interrupts so we don't receive
568*8044SWilliam.Kucharski@Sun.COM * unexpected packets or interrupts from the card after
569*8044SWilliam.Kucharski@Sun.COM * etherboot has finished.
570*8044SWilliam.Kucharski@Sun.COM * This frees resources so etherboot may use
571*8044SWilliam.Kucharski@Sun.COM * this driver on another interface
572*8044SWilliam.Kucharski@Sun.COM * This allows etherboot to reinitialize the interface
573*8044SWilliam.Kucharski@Sun.COM * if something is something goes wrong.
574*8044SWilliam.Kucharski@Sun.COM */
575*8044SWilliam.Kucharski@Sun.COM outw(0x0000, BASE + IntrEnable);
576*8044SWilliam.Kucharski@Sun.COM /* Stop the Chipchips Tx and Rx Status */
577*8044SWilliam.Kucharski@Sun.COM outw(TxDisable | RxDisable | StatsDisable, BASE + MACCtrl1);
578*8044SWilliam.Kucharski@Sun.COM }
579*8044SWilliam.Kucharski@Sun.COM
580*8044SWilliam.Kucharski@Sun.COM
581*8044SWilliam.Kucharski@Sun.COM
582*8044SWilliam.Kucharski@Sun.COM /**************************************************************************
583*8044SWilliam.Kucharski@Sun.COM PROBE - Look for an adapter, this routine's visible to the outside
584*8044SWilliam.Kucharski@Sun.COM ***************************************************************************/
sundance_probe(struct dev * dev,struct pci_device * pci)585*8044SWilliam.Kucharski@Sun.COM static int sundance_probe(struct dev *dev, struct pci_device *pci)
586*8044SWilliam.Kucharski@Sun.COM {
587*8044SWilliam.Kucharski@Sun.COM struct nic *nic = (struct nic *) dev;
588*8044SWilliam.Kucharski@Sun.COM u8 ee_data[EEPROM_SIZE];
589*8044SWilliam.Kucharski@Sun.COM u16 mii_ctl;
590*8044SWilliam.Kucharski@Sun.COM int i;
591*8044SWilliam.Kucharski@Sun.COM int speed;
592*8044SWilliam.Kucharski@Sun.COM
593*8044SWilliam.Kucharski@Sun.COM if (pci->ioaddr == 0)
594*8044SWilliam.Kucharski@Sun.COM return 0;
595*8044SWilliam.Kucharski@Sun.COM
596*8044SWilliam.Kucharski@Sun.COM /* BASE is used throughout to address the card */
597*8044SWilliam.Kucharski@Sun.COM BASE = pci->ioaddr;
598*8044SWilliam.Kucharski@Sun.COM printf(" sundance.c: Found %s Vendor=0x%hX Device=0x%hX\n",
599*8044SWilliam.Kucharski@Sun.COM pci->name, pci->vendor, pci->dev_id);
600*8044SWilliam.Kucharski@Sun.COM
601*8044SWilliam.Kucharski@Sun.COM /* Get the MAC Address by reading the EEPROM */
602*8044SWilliam.Kucharski@Sun.COM for (i = 0; i < 3; i++) {
603*8044SWilliam.Kucharski@Sun.COM ((u16 *) ee_data)[i] =
604*8044SWilliam.Kucharski@Sun.COM le16_to_cpu(eeprom_read(BASE, i + EEPROM_SA_OFFSET));
605*8044SWilliam.Kucharski@Sun.COM }
606*8044SWilliam.Kucharski@Sun.COM /* Update the nic structure with the MAC Address */
607*8044SWilliam.Kucharski@Sun.COM for (i = 0; i < ETH_ALEN; i++) {
608*8044SWilliam.Kucharski@Sun.COM nic->node_addr[i] = ee_data[i];
609*8044SWilliam.Kucharski@Sun.COM }
610*8044SWilliam.Kucharski@Sun.COM
611*8044SWilliam.Kucharski@Sun.COM /* Set the card as PCI Bus Master */
612*8044SWilliam.Kucharski@Sun.COM adjust_pci_device(pci);
613*8044SWilliam.Kucharski@Sun.COM
614*8044SWilliam.Kucharski@Sun.COM // sdc->mii_if.dev = pci;
615*8044SWilliam.Kucharski@Sun.COM // sdc->mii_if.phy_id_mask = 0x1f;
616*8044SWilliam.Kucharski@Sun.COM // sdc->mii_if.reg_num_mask = 0x1f;
617*8044SWilliam.Kucharski@Sun.COM
618*8044SWilliam.Kucharski@Sun.COM /* point to private storage */
619*8044SWilliam.Kucharski@Sun.COM sdc = &sdx;
620*8044SWilliam.Kucharski@Sun.COM
621*8044SWilliam.Kucharski@Sun.COM sdc->nic_name = pci->name;
622*8044SWilliam.Kucharski@Sun.COM sdc->mtu = mtu;
623*8044SWilliam.Kucharski@Sun.COM
624*8044SWilliam.Kucharski@Sun.COM pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id);
625*8044SWilliam.Kucharski@Sun.COM dprintf(("Device revision id: %hx\n", sdc->pci_rev_id));
626*8044SWilliam.Kucharski@Sun.COM /* Print out some hardware info */
627*8044SWilliam.Kucharski@Sun.COM printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr, BASE);
628*8044SWilliam.Kucharski@Sun.COM sdc->mii_preamble_required = 0;
629*8044SWilliam.Kucharski@Sun.COM if (1) {
630*8044SWilliam.Kucharski@Sun.COM int phy, phy_idx = 0;
631*8044SWilliam.Kucharski@Sun.COM sdc->phys[0] = 1; /* Default Setting */
632*8044SWilliam.Kucharski@Sun.COM sdc->mii_preamble_required++;
633*8044SWilliam.Kucharski@Sun.COM for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
634*8044SWilliam.Kucharski@Sun.COM int mii_status = mdio_read(nic, phy, MII_BMSR);
635*8044SWilliam.Kucharski@Sun.COM if (mii_status != 0xffff && mii_status != 0x0000) {
636*8044SWilliam.Kucharski@Sun.COM sdc->phys[phy_idx++] = phy;
637*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.advertising =
638*8044SWilliam.Kucharski@Sun.COM mdio_read(nic, phy, MII_ADVERTISE);
639*8044SWilliam.Kucharski@Sun.COM if ((mii_status & 0x0040) == 0)
640*8044SWilliam.Kucharski@Sun.COM sdc->mii_preamble_required++;
641*8044SWilliam.Kucharski@Sun.COM dprintf
642*8044SWilliam.Kucharski@Sun.COM (("%s: MII PHY found at address %d, status " "%hX advertising %hX\n", sdc->nic_name, phy, mii_status, sdc->mii_if.advertising));
643*8044SWilliam.Kucharski@Sun.COM }
644*8044SWilliam.Kucharski@Sun.COM }
645*8044SWilliam.Kucharski@Sun.COM sdc->mii_preamble_required--;
646*8044SWilliam.Kucharski@Sun.COM if (phy_idx == 0)
647*8044SWilliam.Kucharski@Sun.COM printf("%s: No MII transceiver found!\n",
648*8044SWilliam.Kucharski@Sun.COM sdc->nic_name);
649*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.phy_id = sdc->phys[0];
650*8044SWilliam.Kucharski@Sun.COM }
651*8044SWilliam.Kucharski@Sun.COM
652*8044SWilliam.Kucharski@Sun.COM /* Parse override configuration */
653*8044SWilliam.Kucharski@Sun.COM sdc->an_enable = 1;
654*8044SWilliam.Kucharski@Sun.COM if (strcasecmp(media, "autosense") != 0) {
655*8044SWilliam.Kucharski@Sun.COM sdc->an_enable = 0;
656*8044SWilliam.Kucharski@Sun.COM if (strcasecmp(media, "100mbps_fd") == 0 ||
657*8044SWilliam.Kucharski@Sun.COM strcasecmp(media, "4") == 0) {
658*8044SWilliam.Kucharski@Sun.COM sdc->speed = 100;
659*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex = 1;
660*8044SWilliam.Kucharski@Sun.COM } else if (strcasecmp(media, "100mbps_hd") == 0
661*8044SWilliam.Kucharski@Sun.COM || strcasecmp(media, "3") == 0) {
662*8044SWilliam.Kucharski@Sun.COM sdc->speed = 100;
663*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex = 0;
664*8044SWilliam.Kucharski@Sun.COM } else if (strcasecmp(media, "10mbps_fd") == 0 ||
665*8044SWilliam.Kucharski@Sun.COM strcasecmp(media, "2") == 0) {
666*8044SWilliam.Kucharski@Sun.COM sdc->speed = 10;
667*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex = 1;
668*8044SWilliam.Kucharski@Sun.COM } else if (strcasecmp(media, "10mbps_hd") == 0 ||
669*8044SWilliam.Kucharski@Sun.COM strcasecmp(media, "1") == 0) {
670*8044SWilliam.Kucharski@Sun.COM sdc->speed = 10;
671*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex = 0;
672*8044SWilliam.Kucharski@Sun.COM } else {
673*8044SWilliam.Kucharski@Sun.COM sdc->an_enable = 1;
674*8044SWilliam.Kucharski@Sun.COM }
675*8044SWilliam.Kucharski@Sun.COM }
676*8044SWilliam.Kucharski@Sun.COM if (flowctrl == 1)
677*8044SWilliam.Kucharski@Sun.COM sdc->flowctrl = 1;
678*8044SWilliam.Kucharski@Sun.COM
679*8044SWilliam.Kucharski@Sun.COM /* Fibre PHY? */
680*8044SWilliam.Kucharski@Sun.COM if (inl(BASE + ASICCtrl) & 0x80) {
681*8044SWilliam.Kucharski@Sun.COM /* Default 100Mbps Full */
682*8044SWilliam.Kucharski@Sun.COM if (sdc->an_enable) {
683*8044SWilliam.Kucharski@Sun.COM sdc->speed = 100;
684*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex = 1;
685*8044SWilliam.Kucharski@Sun.COM sdc->an_enable = 0;
686*8044SWilliam.Kucharski@Sun.COM }
687*8044SWilliam.Kucharski@Sun.COM }
688*8044SWilliam.Kucharski@Sun.COM
689*8044SWilliam.Kucharski@Sun.COM /* The Linux driver uses flow control and resets the link here. This means the
690*8044SWilliam.Kucharski@Sun.COM mii section from above would need to be re done I believe. Since it serves
691*8044SWilliam.Kucharski@Sun.COM no real purpose leave it out. */
692*8044SWilliam.Kucharski@Sun.COM
693*8044SWilliam.Kucharski@Sun.COM /* Force media type */
694*8044SWilliam.Kucharski@Sun.COM if (!sdc->an_enable) {
695*8044SWilliam.Kucharski@Sun.COM mii_ctl = 0;
696*8044SWilliam.Kucharski@Sun.COM mii_ctl |= (sdc->speed == 100) ? BMCR_SPEED100 : 0;
697*8044SWilliam.Kucharski@Sun.COM mii_ctl |= (sdc->mii_if.full_duplex) ? BMCR_FULLDPLX : 0;
698*8044SWilliam.Kucharski@Sun.COM mdio_write(nic, sdc->phys[0], MII_BMCR, mii_ctl);
699*8044SWilliam.Kucharski@Sun.COM printf("Override speed=%d, %s duplex\n",
700*8044SWilliam.Kucharski@Sun.COM sdc->speed,
701*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex ? "Full" : "Half");
702*8044SWilliam.Kucharski@Sun.COM }
703*8044SWilliam.Kucharski@Sun.COM
704*8044SWilliam.Kucharski@Sun.COM /* Reset the chip to erase previous misconfiguration */
705*8044SWilliam.Kucharski@Sun.COM dprintf(("ASIC Control is %x.\n", inl(BASE + ASICCtrl)));
706*8044SWilliam.Kucharski@Sun.COM outw(0x007f, BASE + ASICCtrl + 2);
707*8044SWilliam.Kucharski@Sun.COM dprintf(("ASIC Control is now %x.\n", inl(BASE + ASICCtrl)));
708*8044SWilliam.Kucharski@Sun.COM
709*8044SWilliam.Kucharski@Sun.COM sundance_reset(nic);
710*8044SWilliam.Kucharski@Sun.COM if (sdc->an_enable) {
711*8044SWilliam.Kucharski@Sun.COM u16 mii_advertise, mii_lpa;
712*8044SWilliam.Kucharski@Sun.COM mii_advertise =
713*8044SWilliam.Kucharski@Sun.COM mdio_read(nic, sdc->phys[0], MII_ADVERTISE);
714*8044SWilliam.Kucharski@Sun.COM mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
715*8044SWilliam.Kucharski@Sun.COM mii_advertise &= mii_lpa;
716*8044SWilliam.Kucharski@Sun.COM if (mii_advertise & ADVERTISE_100FULL)
717*8044SWilliam.Kucharski@Sun.COM sdc->speed = 100;
718*8044SWilliam.Kucharski@Sun.COM else if (mii_advertise & ADVERTISE_100HALF)
719*8044SWilliam.Kucharski@Sun.COM sdc->speed = 100;
720*8044SWilliam.Kucharski@Sun.COM else if (mii_advertise & ADVERTISE_10FULL)
721*8044SWilliam.Kucharski@Sun.COM sdc->speed = 10;
722*8044SWilliam.Kucharski@Sun.COM else if (mii_advertise & ADVERTISE_10HALF)
723*8044SWilliam.Kucharski@Sun.COM sdc->speed = 10;
724*8044SWilliam.Kucharski@Sun.COM } else {
725*8044SWilliam.Kucharski@Sun.COM mii_ctl = mdio_read(nic, sdc->phys[0], MII_BMCR);
726*8044SWilliam.Kucharski@Sun.COM speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
727*8044SWilliam.Kucharski@Sun.COM sdc->speed = speed;
728*8044SWilliam.Kucharski@Sun.COM printf("%s: Link changed: %dMbps ,", sdc->nic_name, speed);
729*8044SWilliam.Kucharski@Sun.COM printf("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
730*8044SWilliam.Kucharski@Sun.COM "full" : "half");
731*8044SWilliam.Kucharski@Sun.COM }
732*8044SWilliam.Kucharski@Sun.COM check_duplex(nic);
733*8044SWilliam.Kucharski@Sun.COM if (sdc->flowctrl && sdc->mii_if.full_duplex) {
734*8044SWilliam.Kucharski@Sun.COM outw(inw(BASE + MulticastFilter1 + 2) | 0x0200,
735*8044SWilliam.Kucharski@Sun.COM BASE + MulticastFilter1 + 2);
736*8044SWilliam.Kucharski@Sun.COM outw(inw(BASE + MACCtrl0) | EnbFlowCtrl, BASE + MACCtrl0);
737*8044SWilliam.Kucharski@Sun.COM }
738*8044SWilliam.Kucharski@Sun.COM printf("%dMbps, %s-Duplex\n", sdc->speed,
739*8044SWilliam.Kucharski@Sun.COM sdc->mii_if.full_duplex ? "Full" : "Half");
740*8044SWilliam.Kucharski@Sun.COM
741*8044SWilliam.Kucharski@Sun.COM /* point to NIC specific routines */
742*8044SWilliam.Kucharski@Sun.COM dev->disable = sundance_disable;
743*8044SWilliam.Kucharski@Sun.COM nic->poll = sundance_poll;
744*8044SWilliam.Kucharski@Sun.COM nic->transmit = sundance_transmit;
745*8044SWilliam.Kucharski@Sun.COM nic->irqno = pci->irq;
746*8044SWilliam.Kucharski@Sun.COM nic->irq = sundance_irq;
747*8044SWilliam.Kucharski@Sun.COM nic->ioaddr = BASE;
748*8044SWilliam.Kucharski@Sun.COM
749*8044SWilliam.Kucharski@Sun.COM return 1;
750*8044SWilliam.Kucharski@Sun.COM }
751*8044SWilliam.Kucharski@Sun.COM
752*8044SWilliam.Kucharski@Sun.COM
753*8044SWilliam.Kucharski@Sun.COM /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
eeprom_read(long ioaddr,int location)754*8044SWilliam.Kucharski@Sun.COM static int eeprom_read(long ioaddr, int location)
755*8044SWilliam.Kucharski@Sun.COM {
756*8044SWilliam.Kucharski@Sun.COM int boguscnt = 10000; /* Typical 1900 ticks */
757*8044SWilliam.Kucharski@Sun.COM outw(0x0200 | (location & 0xff), ioaddr + EECtrl);
758*8044SWilliam.Kucharski@Sun.COM do {
759*8044SWilliam.Kucharski@Sun.COM if (!(inw(ioaddr + EECtrl) & 0x8000)) {
760*8044SWilliam.Kucharski@Sun.COM return inw(ioaddr + EEData);
761*8044SWilliam.Kucharski@Sun.COM }
762*8044SWilliam.Kucharski@Sun.COM }
763*8044SWilliam.Kucharski@Sun.COM while (--boguscnt > 0);
764*8044SWilliam.Kucharski@Sun.COM return 0;
765*8044SWilliam.Kucharski@Sun.COM }
766*8044SWilliam.Kucharski@Sun.COM
767*8044SWilliam.Kucharski@Sun.COM /* MII transceiver control section.
768*8044SWilliam.Kucharski@Sun.COM Read and write the MII registers using software-generated serial
769*8044SWilliam.Kucharski@Sun.COM MDIO protocol. See the MII specifications or DP83840A data sheet
770*8044SWilliam.Kucharski@Sun.COM for details.
771*8044SWilliam.Kucharski@Sun.COM
772*8044SWilliam.Kucharski@Sun.COM The maximum data clock rate is 2.5 Mhz.
773*8044SWilliam.Kucharski@Sun.COM The timing is decoupled from the processor clock by flushing the write
774*8044SWilliam.Kucharski@Sun.COM from the CPU write buffer with a following read, and using PCI
775*8044SWilliam.Kucharski@Sun.COM transaction time. */
776*8044SWilliam.Kucharski@Sun.COM
777*8044SWilliam.Kucharski@Sun.COM #define mdio_in(mdio_addr) inb(mdio_addr)
778*8044SWilliam.Kucharski@Sun.COM #define mdio_out(value, mdio_addr) outb(value, mdio_addr)
779*8044SWilliam.Kucharski@Sun.COM #define mdio_delay(mdio_addr) inb(mdio_addr)
780*8044SWilliam.Kucharski@Sun.COM
781*8044SWilliam.Kucharski@Sun.COM enum mii_reg_bits {
782*8044SWilliam.Kucharski@Sun.COM MDIO_ShiftClk = 0x0001, MDIO_Data = 0x0002, MDIO_EnbOutput =
783*8044SWilliam.Kucharski@Sun.COM 0x0004,
784*8044SWilliam.Kucharski@Sun.COM };
785*8044SWilliam.Kucharski@Sun.COM #define MDIO_EnbIn (0)
786*8044SWilliam.Kucharski@Sun.COM #define MDIO_WRITE0 (MDIO_EnbOutput)
787*8044SWilliam.Kucharski@Sun.COM #define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)
788*8044SWilliam.Kucharski@Sun.COM
789*8044SWilliam.Kucharski@Sun.COM /* Generate the preamble required for initial synchronization and
790*8044SWilliam.Kucharski@Sun.COM a few older transceivers. */
mdio_sync(long mdio_addr)791*8044SWilliam.Kucharski@Sun.COM static void mdio_sync(long mdio_addr)
792*8044SWilliam.Kucharski@Sun.COM {
793*8044SWilliam.Kucharski@Sun.COM int bits = 32;
794*8044SWilliam.Kucharski@Sun.COM
795*8044SWilliam.Kucharski@Sun.COM /* Establish sync by sending at least 32 logic ones. */
796*8044SWilliam.Kucharski@Sun.COM while (--bits >= 0) {
797*8044SWilliam.Kucharski@Sun.COM mdio_out(MDIO_WRITE1, mdio_addr);
798*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
799*8044SWilliam.Kucharski@Sun.COM mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
800*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
801*8044SWilliam.Kucharski@Sun.COM }
802*8044SWilliam.Kucharski@Sun.COM }
803*8044SWilliam.Kucharski@Sun.COM
804*8044SWilliam.Kucharski@Sun.COM static int
mdio_read(struct nic * nic __unused,int phy_id,unsigned int location)805*8044SWilliam.Kucharski@Sun.COM mdio_read(struct nic *nic __unused, int phy_id, unsigned int location)
806*8044SWilliam.Kucharski@Sun.COM {
807*8044SWilliam.Kucharski@Sun.COM long mdio_addr = BASE + MIICtrl;
808*8044SWilliam.Kucharski@Sun.COM int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
809*8044SWilliam.Kucharski@Sun.COM int i, retval = 0;
810*8044SWilliam.Kucharski@Sun.COM
811*8044SWilliam.Kucharski@Sun.COM if (sdc->mii_preamble_required)
812*8044SWilliam.Kucharski@Sun.COM mdio_sync(mdio_addr);
813*8044SWilliam.Kucharski@Sun.COM
814*8044SWilliam.Kucharski@Sun.COM /* Shift the read command bits out. */
815*8044SWilliam.Kucharski@Sun.COM for (i = 15; i >= 0; i--) {
816*8044SWilliam.Kucharski@Sun.COM int dataval =
817*8044SWilliam.Kucharski@Sun.COM (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
818*8044SWilliam.Kucharski@Sun.COM
819*8044SWilliam.Kucharski@Sun.COM mdio_out(dataval, mdio_addr);
820*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
821*8044SWilliam.Kucharski@Sun.COM mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
822*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
823*8044SWilliam.Kucharski@Sun.COM }
824*8044SWilliam.Kucharski@Sun.COM /* Read the two transition, 16 data, and wire-idle bits. */
825*8044SWilliam.Kucharski@Sun.COM for (i = 19; i > 0; i--) {
826*8044SWilliam.Kucharski@Sun.COM mdio_out(MDIO_EnbIn, mdio_addr);
827*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
828*8044SWilliam.Kucharski@Sun.COM retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data)
829*8044SWilliam.Kucharski@Sun.COM ? 1 : 0);
830*8044SWilliam.Kucharski@Sun.COM mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
831*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
832*8044SWilliam.Kucharski@Sun.COM }
833*8044SWilliam.Kucharski@Sun.COM return (retval >> 1) & 0xffff;
834*8044SWilliam.Kucharski@Sun.COM }
835*8044SWilliam.Kucharski@Sun.COM
836*8044SWilliam.Kucharski@Sun.COM static void
mdio_write(struct nic * nic __unused,int phy_id,unsigned int location,int value)837*8044SWilliam.Kucharski@Sun.COM mdio_write(struct nic *nic __unused, int phy_id,
838*8044SWilliam.Kucharski@Sun.COM unsigned int location, int value)
839*8044SWilliam.Kucharski@Sun.COM {
840*8044SWilliam.Kucharski@Sun.COM long mdio_addr = BASE + MIICtrl;
841*8044SWilliam.Kucharski@Sun.COM int mii_cmd =
842*8044SWilliam.Kucharski@Sun.COM (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
843*8044SWilliam.Kucharski@Sun.COM int i;
844*8044SWilliam.Kucharski@Sun.COM
845*8044SWilliam.Kucharski@Sun.COM if (sdc->mii_preamble_required)
846*8044SWilliam.Kucharski@Sun.COM mdio_sync(mdio_addr);
847*8044SWilliam.Kucharski@Sun.COM
848*8044SWilliam.Kucharski@Sun.COM /* Shift the command bits out. */
849*8044SWilliam.Kucharski@Sun.COM for (i = 31; i >= 0; i--) {
850*8044SWilliam.Kucharski@Sun.COM int dataval =
851*8044SWilliam.Kucharski@Sun.COM (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
852*8044SWilliam.Kucharski@Sun.COM mdio_out(dataval, mdio_addr);
853*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
854*8044SWilliam.Kucharski@Sun.COM mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
855*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
856*8044SWilliam.Kucharski@Sun.COM }
857*8044SWilliam.Kucharski@Sun.COM /* Clear out extra bits. */
858*8044SWilliam.Kucharski@Sun.COM for (i = 2; i > 0; i--) {
859*8044SWilliam.Kucharski@Sun.COM mdio_out(MDIO_EnbIn, mdio_addr);
860*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
861*8044SWilliam.Kucharski@Sun.COM mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
862*8044SWilliam.Kucharski@Sun.COM mdio_delay(mdio_addr);
863*8044SWilliam.Kucharski@Sun.COM }
864*8044SWilliam.Kucharski@Sun.COM return;
865*8044SWilliam.Kucharski@Sun.COM }
866*8044SWilliam.Kucharski@Sun.COM
set_rx_mode(struct nic * nic __unused)867*8044SWilliam.Kucharski@Sun.COM static void set_rx_mode(struct nic *nic __unused)
868*8044SWilliam.Kucharski@Sun.COM {
869*8044SWilliam.Kucharski@Sun.COM int i;
870*8044SWilliam.Kucharski@Sun.COM u16 mc_filter[4]; /* Multicast hash filter */
871*8044SWilliam.Kucharski@Sun.COM u32 rx_mode;
872*8044SWilliam.Kucharski@Sun.COM
873*8044SWilliam.Kucharski@Sun.COM memset(mc_filter, 0xff, sizeof(mc_filter));
874*8044SWilliam.Kucharski@Sun.COM rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
875*8044SWilliam.Kucharski@Sun.COM
876*8044SWilliam.Kucharski@Sun.COM if (sdc->mii_if.full_duplex && sdc->flowctrl)
877*8044SWilliam.Kucharski@Sun.COM mc_filter[3] |= 0x0200;
878*8044SWilliam.Kucharski@Sun.COM for (i = 0; i < 4; i++)
879*8044SWilliam.Kucharski@Sun.COM outw(mc_filter[i], BASE + MulticastFilter0 + i * 2);
880*8044SWilliam.Kucharski@Sun.COM outb(rx_mode, BASE + RxMode);
881*8044SWilliam.Kucharski@Sun.COM return;
882*8044SWilliam.Kucharski@Sun.COM }
883*8044SWilliam.Kucharski@Sun.COM
884*8044SWilliam.Kucharski@Sun.COM static struct pci_id sundance_nics[] = {
885*8044SWilliam.Kucharski@Sun.COM PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"),
886*8044SWilliam.Kucharski@Sun.COM PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"),
887*8044SWilliam.Kucharski@Sun.COM };
888*8044SWilliam.Kucharski@Sun.COM
889*8044SWilliam.Kucharski@Sun.COM struct pci_driver sundance_driver = {
890*8044SWilliam.Kucharski@Sun.COM .type = NIC_DRIVER,
891*8044SWilliam.Kucharski@Sun.COM .name = "SUNDANCE/PCI",
892*8044SWilliam.Kucharski@Sun.COM .probe = sundance_probe,
893*8044SWilliam.Kucharski@Sun.COM .ids = sundance_nics,
894*8044SWilliam.Kucharski@Sun.COM .id_count = sizeof(sundance_nics) / sizeof(sundance_nics[0]),
895*8044SWilliam.Kucharski@Sun.COM .class = 0,
896*8044SWilliam.Kucharski@Sun.COM };
897