forcedeth.c 189 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/*
 * forcedeth: Ethernet driver for NVIDIA nForce media access controllers.
 *
 * Note: This driver is a cleanroom reimplementation based on reverse
 *      engineered documentation written by Carl-Daniel Hailfinger
6
 *      and Andrew de Quincey.
Linus Torvalds's avatar
Linus Torvalds committed
7
8
9
10
11
 *
 * NVIDIA, nForce and other NVIDIA marks are trademarks or registered
 * trademarks of NVIDIA Corporation in the United States and other
 * countries.
 *
12
 * Copyright (C) 2003,4,5 Manfred Spraul
Linus Torvalds's avatar
Linus Torvalds committed
13
14
15
 * Copyright (C) 2004 Andrew de Quincey (wol support)
 * Copyright (C) 2004 Carl-Daniel Hailfinger (invalid MAC handling, insane
 *		IRQ rate fixes, bigendian fixes, cleanups, verification)
16
 * Copyright (c) 2004,2005,2006,2007,2008 NVIDIA Corporation
Linus Torvalds's avatar
Linus Torvalds committed
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Known bugs:
 * We suspect that on some hardware no TX done interrupts are generated.
 * This means recovery from netif_stop_queue only happens if the hw timer
 * interrupt fires (100 times/second, configurable with NVREG_POLL_DEFAULT)
 * and the timer is active in the IRQMask, or if a rx packet arrives by chance.
 * If your hardware reliably generates tx done interrupts, then you can remove
 * DEV_NEED_TIMERIRQ from the driver_data flags.
 * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
 * superfluous timer interrupts from the nic.
 */
42
#define FORCEDETH_VERSION		"0.61"
Linus Torvalds's avatar
Linus Torvalds committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#define DRV_NAME			"forcedeth"

#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/timer.h>
#include <linux/skbuff.h>
#include <linux/mii.h>
#include <linux/random.h>
#include <linux/init.h>
59
#include <linux/if_vlan.h>
60
#include <linux/dma-mapping.h>
Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
64
65
66
67
68
69
70
71
72

#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>

#if 0
#define dprintk			printk
#else
#define dprintk(x...)		do { } while (0)
#endif

73
74
#define TX_WORK_PER_LOOP  64
#define RX_WORK_PER_LOOP  64
Linus Torvalds's avatar
Linus Torvalds committed
75
76
77
78
79

/*
 * Hardware access:
 */

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#define DEV_NEED_TIMERIRQ          0x000001  /* set the timer irq flag in the irq mask */
#define DEV_NEED_LINKTIMER         0x000002  /* poll link settings. Relies on the timer irq */
#define DEV_HAS_LARGEDESC          0x000004  /* device supports jumbo frames and needs packet format 2 */
#define DEV_HAS_HIGH_DMA           0x000008  /* device supports 64bit dma */
#define DEV_HAS_CHECKSUM           0x000010  /* device supports tx and rx checksum offloads */
#define DEV_HAS_VLAN               0x000020  /* device supports vlan tagging and striping */
#define DEV_HAS_MSI                0x000040  /* device supports MSI */
#define DEV_HAS_MSI_X              0x000080  /* device supports MSI-X */
#define DEV_HAS_POWER_CNTRL        0x000100  /* device supports power savings */
#define DEV_HAS_STATISTICS_V1      0x000200  /* device supports hw statistics version 1 */
#define DEV_HAS_STATISTICS_V2      0x000400  /* device supports hw statistics version 2 */
#define DEV_HAS_STATISTICS_V3      0x000800  /* device supports hw statistics version 3 */
#define DEV_HAS_TEST_EXTENDED      0x001000  /* device supports extended diagnostic test */
#define DEV_HAS_MGMT_UNIT          0x002000  /* device supports management unit */
#define DEV_HAS_CORRECT_MACADDR    0x004000  /* device supports correct mac address order */
#define DEV_HAS_COLLISION_FIX      0x008000  /* device supports tx collision fix */
#define DEV_HAS_PAUSEFRAME_TX_V1   0x010000  /* device supports tx pause frames version 1 */
#define DEV_HAS_PAUSEFRAME_TX_V2   0x020000  /* device supports tx pause frames version 2 */
#define DEV_HAS_PAUSEFRAME_TX_V3   0x040000  /* device supports tx pause frames version 3 */
#define DEV_NEED_TX_LIMIT          0x080000  /* device needs to limit tx */
#define DEV_HAS_GEAR_MODE          0x100000  /* device supports gear mode */
Linus Torvalds's avatar
Linus Torvalds committed
101
102
103
104

enum {
	NvRegIrqStatus = 0x000,
#define NVREG_IRQSTAT_MIIEVENT	0x040
105
#define NVREG_IRQSTAT_MASK		0x81ff
Linus Torvalds's avatar
Linus Torvalds committed
106
107
108
109
110
	NvRegIrqMask = 0x004,
#define NVREG_IRQ_RX_ERROR		0x0001
#define NVREG_IRQ_RX			0x0002
#define NVREG_IRQ_RX_NOBUF		0x0004
#define NVREG_IRQ_TX_ERR		0x0008
111
#define NVREG_IRQ_TX_OK			0x0010
Linus Torvalds's avatar
Linus Torvalds committed
112
113
#define NVREG_IRQ_TIMER			0x0020
#define NVREG_IRQ_LINK			0x0040
114
115
#define NVREG_IRQ_RX_FORCED		0x0080
#define NVREG_IRQ_TX_FORCED		0x0100
116
#define NVREG_IRQ_RECOVER_ERROR		0x8000
117
#define NVREG_IRQMASK_THROUGHPUT	0x00df
Ayaz Abdulla's avatar
Ayaz Abdulla committed
118
#define NVREG_IRQMASK_CPU		0x0060
119
120
#define NVREG_IRQ_TX_ALL		(NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED)
#define NVREG_IRQ_RX_ALL		(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_RX_FORCED)
121
#define NVREG_IRQ_OTHER			(NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RECOVER_ERROR)
122
123

#define NVREG_IRQ_UNKNOWN	(~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR| \
124
					NVREG_IRQ_TX_OK|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RX_FORCED| \
125
					NVREG_IRQ_TX_FORCED|NVREG_IRQ_RECOVER_ERROR))
Linus Torvalds's avatar
Linus Torvalds committed
126
127
128
129
130
131
132
133
134

	NvRegUnknownSetupReg6 = 0x008,
#define NVREG_UNKSETUP6_VAL		3

/*
 * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
 * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
 */
	NvRegPollingInterval = 0x00c,
Ayaz Abdulla's avatar
Ayaz Abdulla committed
135
#define NVREG_POLL_DEFAULT_THROUGHPUT	970 /* backup tx cleanup if loop max reached */
136
#define NVREG_POLL_DEFAULT_CPU	13
137
138
139
140
	NvRegMSIMap0 = 0x020,
	NvRegMSIMap1 = 0x024,
	NvRegMSIIrqMask = 0x030,
#define NVREG_MSI_VECTOR_0_ENABLED 0x01
Linus Torvalds's avatar
Linus Torvalds committed
141
	NvRegMisc1 = 0x080,
142
#define NVREG_MISC1_PAUSE_TX	0x01
Linus Torvalds's avatar
Linus Torvalds committed
143
144
145
#define NVREG_MISC1_HD		0x02
#define NVREG_MISC1_FORCE	0x3b0f3c

Ayaz Abdulla's avatar
Ayaz Abdulla committed
146
	NvRegMacReset = 0x34,
147
#define NVREG_MAC_RESET_ASSERT	0x0F3
Linus Torvalds's avatar
Linus Torvalds committed
148
149
	NvRegTransmitterControl = 0x084,
#define NVREG_XMITCTL_START	0x01
150
151
152
153
154
155
156
157
158
#define NVREG_XMITCTL_MGMT_ST	0x40000000
#define NVREG_XMITCTL_SYNC_MASK		0x000f0000
#define NVREG_XMITCTL_SYNC_NOT_READY	0x0
#define NVREG_XMITCTL_SYNC_PHY_INIT	0x00040000
#define NVREG_XMITCTL_MGMT_SEMA_MASK	0x00000f00
#define NVREG_XMITCTL_MGMT_SEMA_FREE	0x0
#define NVREG_XMITCTL_HOST_SEMA_MASK	0x0000f000
#define NVREG_XMITCTL_HOST_SEMA_ACQ	0x0000f000
#define NVREG_XMITCTL_HOST_LOADED	0x00004000
159
#define NVREG_XMITCTL_TX_PATH_EN	0x01000000
Linus Torvalds's avatar
Linus Torvalds committed
160
161
162
163
	NvRegTransmitterStatus = 0x088,
#define NVREG_XMITSTAT_BUSY	0x01

	NvRegPacketFilterFlags = 0x8c,
164
165
#define NVREG_PFF_PAUSE_RX	0x08
#define NVREG_PFF_ALWAYS	0x7F0000
Linus Torvalds's avatar
Linus Torvalds committed
166
167
#define NVREG_PFF_PROMISC	0x80
#define NVREG_PFF_MYADDR	0x20
168
#define NVREG_PFF_LOOPBACK	0x10
Linus Torvalds's avatar
Linus Torvalds committed
169
170
171
172
173
174

	NvRegOffloadConfig = 0x90,
#define NVREG_OFFLOAD_HOMEPHY	0x601
#define NVREG_OFFLOAD_NORMAL	RX_NIC_BUFSIZE
	NvRegReceiverControl = 0x094,
#define NVREG_RCVCTL_START	0x01
175
#define NVREG_RCVCTL_RX_PATH_EN	0x01000000
Linus Torvalds's avatar
Linus Torvalds committed
176
177
178
	NvRegReceiverStatus = 0x98,
#define NVREG_RCVSTAT_BUSY	0x01

179
180
181
182
183
184
185
	NvRegSlotTime = 0x9c,
#define NVREG_SLOTTIME_LEGBF_ENABLED	0x80000000
#define NVREG_SLOTTIME_10_100_FULL	0x00007f00
#define NVREG_SLOTTIME_1000_FULL 	0x0003ff00
#define NVREG_SLOTTIME_HALF		0x0000ff00
#define NVREG_SLOTTIME_DEFAULT	 	0x00007f00
#define NVREG_SLOTTIME_MASK		0x000000ff
Linus Torvalds's avatar
Linus Torvalds committed
186

187
	NvRegTxDeferral = 0xA0,
Ayaz Abdulla's avatar
Ayaz Abdulla committed
188
189
190
191
192
193
#define NVREG_TX_DEFERRAL_DEFAULT		0x15050f
#define NVREG_TX_DEFERRAL_RGMII_10_100		0x16070f
#define NVREG_TX_DEFERRAL_RGMII_1000		0x14050f
#define NVREG_TX_DEFERRAL_RGMII_STRETCH_10	0x16190f
#define NVREG_TX_DEFERRAL_RGMII_STRETCH_100	0x16300f
#define NVREG_TX_DEFERRAL_MII_STRETCH		0x152000
194
195
	NvRegRxDeferral = 0xA4,
#define NVREG_RX_DEFERRAL_DEFAULT	0x16
Linus Torvalds's avatar
Linus Torvalds committed
196
197
198
199
200
201
	NvRegMacAddrA = 0xA8,
	NvRegMacAddrB = 0xAC,
	NvRegMulticastAddrA = 0xB0,
#define NVREG_MCASTADDRA_FORCE	0x01
	NvRegMulticastAddrB = 0xB4,
	NvRegMulticastMaskA = 0xB8,
Ayaz Abdulla's avatar
Ayaz Abdulla committed
202
#define NVREG_MCASTMASKA_NONE		0xffffffff
Linus Torvalds's avatar
Linus Torvalds committed
203
	NvRegMulticastMaskB = 0xBC,
Ayaz Abdulla's avatar
Ayaz Abdulla committed
204
#define NVREG_MCASTMASKB_NONE		0xffff
Linus Torvalds's avatar
Linus Torvalds committed
205
206
207

	NvRegPhyInterface = 0xC0,
#define PHY_RGMII		0x10000000
208
209
210
211
212
	NvRegBackOffControl = 0xC4,
#define NVREG_BKOFFCTRL_DEFAULT			0x70000000
#define NVREG_BKOFFCTRL_SEED_MASK		0x000003ff
#define NVREG_BKOFFCTRL_SELECT			24
#define NVREG_BKOFFCTRL_GEAR			12
Linus Torvalds's avatar
Linus Torvalds committed
213
214
215
216
217
218

	NvRegTxRingPhysAddr = 0x100,
	NvRegRxRingPhysAddr = 0x104,
	NvRegRingSizes = 0x108,
#define NVREG_RINGSZ_TXSHIFT 0
#define NVREG_RINGSZ_RXSHIFT 16
219
220
	NvRegTransmitPoll = 0x10c,
#define NVREG_TRANSMITPOLL_MAC_ADDR_REV	0x00008000
Linus Torvalds's avatar
Linus Torvalds committed
221
222
223
224
225
226
227
228
	NvRegLinkSpeed = 0x110,
#define NVREG_LINKSPEED_FORCE 0x10000
#define NVREG_LINKSPEED_10	1000
#define NVREG_LINKSPEED_100	100
#define NVREG_LINKSPEED_1000	50
#define NVREG_LINKSPEED_MASK	(0xFFF)
	NvRegUnknownSetupReg5 = 0x130,
#define NVREG_UNKSETUP5_BIT31	(1<<31)
229
230
231
232
	NvRegTxWatermark = 0x13c,
#define NVREG_TX_WM_DESC1_DEFAULT	0x0200010
#define NVREG_TX_WM_DESC2_3_DEFAULT	0x1e08000
#define NVREG_TX_WM_DESC2_3_1000	0xfe08000
Linus Torvalds's avatar
Linus Torvalds committed
233
234
235
236
237
238
239
	NvRegTxRxControl = 0x144,
#define NVREG_TXRXCTL_KICK	0x0001
#define NVREG_TXRXCTL_BIT1	0x0002
#define NVREG_TXRXCTL_BIT2	0x0004
#define NVREG_TXRXCTL_IDLE	0x0008
#define NVREG_TXRXCTL_RESET	0x0010
#define NVREG_TXRXCTL_RXCHECK	0x0400
240
#define NVREG_TXRXCTL_DESC_1	0
Ayaz Abdulla's avatar
Ayaz Abdulla committed
241
242
#define NVREG_TXRXCTL_DESC_2	0x002100
#define NVREG_TXRXCTL_DESC_3	0xc02200
243
244
#define NVREG_TXRXCTL_VLANSTRIP 0x00040
#define NVREG_TXRXCTL_VLANINS	0x00080
245
246
	NvRegTxRingPhysAddrHigh = 0x148,
	NvRegRxRingPhysAddrHigh = 0x14C,
247
	NvRegTxPauseFrame = 0x170,
Ayaz Abdulla's avatar
Ayaz Abdulla committed
248
249
250
251
#define NVREG_TX_PAUSEFRAME_DISABLE	0x0fff0080
#define NVREG_TX_PAUSEFRAME_ENABLE_V1	0x01800010
#define NVREG_TX_PAUSEFRAME_ENABLE_V2	0x056003f0
#define NVREG_TX_PAUSEFRAME_ENABLE_V3	0x09f00880
Ayaz Abdulla's avatar
Ayaz Abdulla committed
252
253
	NvRegTxPauseFrameLimit = 0x174,
#define NVREG_TX_PAUSEFRAMELIMIT_ENABLE	0x00010000
Linus Torvalds's avatar
Linus Torvalds committed
254
255
256
	NvRegMIIStatus = 0x180,
#define NVREG_MIISTAT_ERROR		0x0001
#define NVREG_MIISTAT_LINKCHANGE	0x0008
Ayaz Abdulla's avatar
Ayaz Abdulla committed
257
258
#define NVREG_MIISTAT_MASK_RW		0x0007
#define NVREG_MIISTAT_MASK_ALL		0x000f
259
260
	NvRegMIIMask = 0x184,
#define NVREG_MII_LINKCHANGE		0x0008
Linus Torvalds's avatar
Linus Torvalds committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

	NvRegAdapterControl = 0x188,
#define NVREG_ADAPTCTL_START	0x02
#define NVREG_ADAPTCTL_LINKUP	0x04
#define NVREG_ADAPTCTL_PHYVALID	0x40000
#define NVREG_ADAPTCTL_RUNNING	0x100000
#define NVREG_ADAPTCTL_PHYSHIFT	24
	NvRegMIISpeed = 0x18c,
#define NVREG_MIISPEED_BIT8	(1<<8)
#define NVREG_MIIDELAY	5
	NvRegMIIControl = 0x190,
#define NVREG_MIICTL_INUSE	0x08000
#define NVREG_MIICTL_WRITE	0x00400
#define NVREG_MIICTL_ADDRSHIFT	5
	NvRegMIIData = 0x194,
276
277
278
	NvRegTxUnicast = 0x1a0,
	NvRegTxMulticast = 0x1a4,
	NvRegTxBroadcast = 0x1a8,
Linus Torvalds's avatar
Linus Torvalds committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
	NvRegWakeUpFlags = 0x200,
#define NVREG_WAKEUPFLAGS_VAL		0x7770
#define NVREG_WAKEUPFLAGS_BUSYSHIFT	24
#define NVREG_WAKEUPFLAGS_ENABLESHIFT	16
#define NVREG_WAKEUPFLAGS_D3SHIFT	12
#define NVREG_WAKEUPFLAGS_D2SHIFT	8
#define NVREG_WAKEUPFLAGS_D1SHIFT	4
#define NVREG_WAKEUPFLAGS_D0SHIFT	0
#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT		0x01
#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT	0x02
#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE	0x04
#define NVREG_WAKEUPFLAGS_ENABLE	0x1111

	NvRegPatternCRC = 0x204,
	NvRegPatternMask = 0x208,
	NvRegPowerCap = 0x268,
#define NVREG_POWERCAP_D3SUPP	(1<<30)
#define NVREG_POWERCAP_D2SUPP	(1<<26)
#define NVREG_POWERCAP_D1SUPP	(1<<25)
	NvRegPowerState = 0x26c,
#define NVREG_POWERSTATE_POWEREDUP	0x8000
#define NVREG_POWERSTATE_VALID		0x0100
#define NVREG_POWERSTATE_MASK		0x0003
#define NVREG_POWERSTATE_D0		0x0000
#define NVREG_POWERSTATE_D1		0x0001
#define NVREG_POWERSTATE_D2		0x0002
#define NVREG_POWERSTATE_D3		0x0003
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
	NvRegTxCnt = 0x280,
	NvRegTxZeroReXmt = 0x284,
	NvRegTxOneReXmt = 0x288,
	NvRegTxManyReXmt = 0x28c,
	NvRegTxLateCol = 0x290,
	NvRegTxUnderflow = 0x294,
	NvRegTxLossCarrier = 0x298,
	NvRegTxExcessDef = 0x29c,
	NvRegTxRetryErr = 0x2a0,
	NvRegRxFrameErr = 0x2a4,
	NvRegRxExtraByte = 0x2a8,
	NvRegRxLateCol = 0x2ac,
	NvRegRxRunt = 0x2b0,
	NvRegRxFrameTooLong = 0x2b4,
	NvRegRxOverflow = 0x2b8,
	NvRegRxFCSErr = 0x2bc,
	NvRegRxFrameAlignErr = 0x2c0,
	NvRegRxLenErr = 0x2c4,
	NvRegRxUnicast = 0x2c8,
	NvRegRxMulticast = 0x2cc,
	NvRegRxBroadcast = 0x2d0,
	NvRegTxDef = 0x2d4,
	NvRegTxFrame = 0x2d8,
	NvRegRxCnt = 0x2dc,
	NvRegTxPause = 0x2e0,
	NvRegRxPause = 0x2e4,
	NvRegRxDropFrame = 0x2e8,
333
334
	NvRegVlanControl = 0x300,
#define NVREG_VLANCONTROL_ENABLE	0x2000
335
336
337
	NvRegMSIXMap0 = 0x3e0,
	NvRegMSIXMap1 = 0x3e4,
	NvRegMSIXIrqStatus = 0x3f0,
338
339

	NvRegPowerState2 = 0x600,
340
#define NVREG_POWERSTATE2_POWERUP_MASK		0x0F15
341
#define NVREG_POWERSTATE2_POWERUP_REV_A3	0x0001
342
#define NVREG_POWERSTATE2_PHY_RESET		0x0004
Linus Torvalds's avatar
Linus Torvalds committed
343
344
345
346
};

/* Big endian: should work, but is untested */
struct ring_desc {
347
348
	__le32 buf;
	__le32 flaglen;
Linus Torvalds's avatar
Linus Torvalds committed
349
350
};

351
struct ring_desc_ex {
352
353
354
355
	__le32 bufhigh;
	__le32 buflow;
	__le32 txvlan;
	__le32 flaglen;
356
357
};

358
union ring_type {
359
360
	struct ring_desc* orig;
	struct ring_desc_ex* ex;
361
};
362

Linus Torvalds's avatar
Linus Torvalds committed
363
364
365
366
367
368
369
#define FLAG_MASK_V1 0xffff0000
#define FLAG_MASK_V2 0xffffc000
#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)

#define NV_TX_LASTPACKET	(1<<16)
#define NV_TX_RETRYERROR	(1<<19)
370
#define NV_TX_RETRYCOUNT_MASK	(0xF<<20)
371
#define NV_TX_FORCED_INTERRUPT	(1<<24)
Linus Torvalds's avatar
Linus Torvalds committed
372
373
374
375
376
377
378
379
380
#define NV_TX_DEFERRED		(1<<26)
#define NV_TX_CARRIERLOST	(1<<27)
#define NV_TX_LATECOLLISION	(1<<28)
#define NV_TX_UNDERFLOW		(1<<29)
#define NV_TX_ERROR		(1<<30)
#define NV_TX_VALID		(1<<31)

#define NV_TX2_LASTPACKET	(1<<29)
#define NV_TX2_RETRYERROR	(1<<18)
381
#define NV_TX2_RETRYCOUNT_MASK	(0xF<<19)
382
#define NV_TX2_FORCED_INTERRUPT	(1<<30)
Linus Torvalds's avatar
Linus Torvalds committed
383
384
385
386
387
388
389
#define NV_TX2_DEFERRED		(1<<25)
#define NV_TX2_CARRIERLOST	(1<<26)
#define NV_TX2_LATECOLLISION	(1<<27)
#define NV_TX2_UNDERFLOW	(1<<28)
/* error and valid are the same for both */
#define NV_TX2_ERROR		(1<<30)
#define NV_TX2_VALID		(1<<31)
390
391
#define NV_TX2_TSO		(1<<28)
#define NV_TX2_TSO_SHIFT	14
392
393
#define NV_TX2_TSO_MAX_SHIFT	14
#define NV_TX2_TSO_MAX_SIZE	(1<<NV_TX2_TSO_MAX_SHIFT)
394
395
#define NV_TX2_CHECKSUM_L3	(1<<27)
#define NV_TX2_CHECKSUM_L4	(1<<26)
Linus Torvalds's avatar
Linus Torvalds committed
396

397
398
#define NV_TX3_VLAN_TAG_PRESENT (1<<18)

Linus Torvalds's avatar
Linus Torvalds committed
399
400
401
402
403
404
405
406
407
408
409
410
#define NV_RX_DESCRIPTORVALID	(1<<16)
#define NV_RX_MISSEDFRAME	(1<<17)
#define NV_RX_SUBSTRACT1	(1<<18)
#define NV_RX_ERROR1		(1<<23)
#define NV_RX_ERROR2		(1<<24)
#define NV_RX_ERROR3		(1<<25)
#define NV_RX_ERROR4		(1<<26)
#define NV_RX_CRCERR		(1<<27)
#define NV_RX_OVERFLOW		(1<<28)
#define NV_RX_FRAMINGERR	(1<<29)
#define NV_RX_ERROR		(1<<30)
#define NV_RX_AVAIL		(1<<31)
Ayaz Abdulla's avatar
Ayaz Abdulla committed
411
#define NV_RX_ERROR_MASK	(NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3|NV_RX_ERROR4|NV_RX_CRCERR|NV_RX_OVERFLOW|NV_RX_FRAMINGERR)
Linus Torvalds's avatar
Linus Torvalds committed
412
413

#define NV_RX2_CHECKSUMMASK	(0x1C000000)
Ayaz Abdulla's avatar
Ayaz Abdulla committed
414
415
416
#define NV_RX2_CHECKSUM_IP	(0x10000000)
#define NV_RX2_CHECKSUM_IP_TCP	(0x14000000)
#define NV_RX2_CHECKSUM_IP_UDP	(0x18000000)
Linus Torvalds's avatar
Linus Torvalds committed
417
418
419
420
421
422
423
424
425
426
427
428
#define NV_RX2_DESCRIPTORVALID	(1<<29)
#define NV_RX2_SUBSTRACT1	(1<<25)
#define NV_RX2_ERROR1		(1<<18)
#define NV_RX2_ERROR2		(1<<19)
#define NV_RX2_ERROR3		(1<<20)
#define NV_RX2_ERROR4		(1<<21)
#define NV_RX2_CRCERR		(1<<22)
#define NV_RX2_OVERFLOW		(1<<23)
#define NV_RX2_FRAMINGERR	(1<<24)
/* error and avail are the same for both */
#define NV_RX2_ERROR		(1<<30)
#define NV_RX2_AVAIL		(1<<31)
Ayaz Abdulla's avatar
Ayaz Abdulla committed
429
#define NV_RX2_ERROR_MASK	(NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3|NV_RX2_ERROR4|NV_RX2_CRCERR|NV_RX2_OVERFLOW|NV_RX2_FRAMINGERR)
Linus Torvalds's avatar
Linus Torvalds committed
430

431
432
433
#define NV_RX3_VLAN_TAG_PRESENT (1<<16)
#define NV_RX3_VLAN_TAG_MASK	(0x0000FFFF)

Linus Torvalds's avatar
Linus Torvalds committed
434
/* Miscelaneous hardware related defines: */
435
#define NV_PCI_REGSZ_VER1      	0x270
Ayaz Abdulla's avatar
Ayaz Abdulla committed
436
437
#define NV_PCI_REGSZ_VER2      	0x2d4
#define NV_PCI_REGSZ_VER3      	0x604
438
#define NV_PCI_REGSZ_MAX       	0x604
Linus Torvalds's avatar
Linus Torvalds committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454

/* various timeout delays: all in usec */
#define NV_TXRX_RESET_DELAY	4
#define NV_TXSTOP_DELAY1	10
#define NV_TXSTOP_DELAY1MAX	500000
#define NV_TXSTOP_DELAY2	100
#define NV_RXSTOP_DELAY1	10
#define NV_RXSTOP_DELAY1MAX	500000
#define NV_RXSTOP_DELAY2	100
#define NV_SETUP5_DELAY		5
#define NV_SETUP5_DELAYMAX	50000
#define NV_POWERUP_DELAY	5
#define NV_POWERUP_DELAYMAX	5000
#define NV_MIIBUSY_DELAY	50
#define NV_MIIPHY_DELAY	10
#define NV_MIIPHY_DELAYMAX	10000
455
#define NV_MAC_RESET_DELAY	64
Linus Torvalds's avatar
Linus Torvalds committed
456
457
458
459
460
461
462

#define NV_WAKEUPPATTERNS	5
#define NV_WAKEUPMASKENTRIES	4

/* General driver defaults */
#define NV_WATCHDOG_TIMEO	(5*HZ)

463
464
465
466
467
468
#define RX_RING_DEFAULT		128
#define TX_RING_DEFAULT		256
#define RX_RING_MIN		128
#define TX_RING_MIN		64
#define RING_MAX_DESC_VER_1	1024
#define RING_MAX_DESC_VER_2_3	16384
Linus Torvalds's avatar
Linus Torvalds committed
469
470

/* rx/tx mac addr + type + vlan + align + slack*/
471
472
473
474
475
476
477
#define NV_RX_HEADERS		(64)
/* even more slack. */
#define NV_RX_ALLOC_PAD		(64)

/* maximum mtu size */
#define NV_PKTLIMIT_1	ETH_DATA_LEN	/* hard limit not known */
#define NV_PKTLIMIT_2	9100	/* Actual limit according to NVidia: 9202 */
Linus Torvalds's avatar
Linus Torvalds committed
478
479
480
481

#define OOM_REFILL	(1+HZ/20)
#define POLL_WAIT	(1+HZ/100)
#define LINK_TIMEOUT	(3*HZ)
482
#define STATS_INTERVAL	(10*HZ)
Linus Torvalds's avatar
Linus Torvalds committed
483

484
/*
Linus Torvalds's avatar
Linus Torvalds committed
485
 * desc_ver values:
486
487
488
489
 * The nic supports three different descriptor types:
 * - DESC_VER_1: Original
 * - DESC_VER_2: support for jumbo frames.
 * - DESC_VER_3: 64-bit format.
Linus Torvalds's avatar
Linus Torvalds committed
490
 */
491
492
493
#define DESC_VER_1	1
#define DESC_VER_2	2
#define DESC_VER_3	3
Linus Torvalds's avatar
Linus Torvalds committed
494
495

/* PHY defines */
496
497
498
499
500
#define PHY_OUI_MARVELL		0x5043
#define PHY_OUI_CICADA		0x03f1
#define PHY_OUI_VITESSE		0x01c1
#define PHY_OUI_REALTEK		0x0732
#define PHY_OUI_REALTEK2	0x0020
Linus Torvalds's avatar
Linus Torvalds committed
501
502
503
504
#define PHYID1_OUI_MASK	0x03ff
#define PHYID1_OUI_SHFT	6
#define PHYID2_OUI_MASK	0xfc00
#define PHYID2_OUI_SHFT	10
505
#define PHYID2_MODEL_MASK		0x03f0
506
507
508
509
510
511
#define PHY_MODEL_REALTEK_8211		0x0110
#define PHY_REV_MASK			0x0001
#define PHY_REV_REALTEK_8211B		0x0000
#define PHY_REV_REALTEK_8211C		0x0001
#define PHY_MODEL_REALTEK_8201		0x0200
#define PHY_MODEL_MARVELL_E3016		0x0220
512
#define PHY_MARVELL_E3016_INITMASK	0x0300
Ayaz Abdulla's avatar
Ayaz Abdulla committed
513
514
515
516
517
518
#define PHY_CICADA_INIT1	0x0f000
#define PHY_CICADA_INIT2	0x0e00
#define PHY_CICADA_INIT3	0x01000
#define PHY_CICADA_INIT4	0x0200
#define PHY_CICADA_INIT5	0x0004
#define PHY_CICADA_INIT6	0x02000
Ayaz Abdulla's avatar
Ayaz Abdulla committed
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
#define PHY_VITESSE_INIT_REG1	0x1f
#define PHY_VITESSE_INIT_REG2	0x10
#define PHY_VITESSE_INIT_REG3	0x11
#define PHY_VITESSE_INIT_REG4	0x12
#define PHY_VITESSE_INIT_MSK1	0xc
#define PHY_VITESSE_INIT_MSK2	0x0180
#define PHY_VITESSE_INIT1	0x52b5
#define PHY_VITESSE_INIT2	0xaf8a
#define PHY_VITESSE_INIT3	0x8
#define PHY_VITESSE_INIT4	0x8f8a
#define PHY_VITESSE_INIT5	0xaf86
#define PHY_VITESSE_INIT6	0x8f86
#define PHY_VITESSE_INIT7	0xaf82
#define PHY_VITESSE_INIT8	0x0100
#define PHY_VITESSE_INIT9	0x8f82
#define PHY_VITESSE_INIT10	0x0
Ayaz Abdulla's avatar
Ayaz Abdulla committed
535
536
537
#define PHY_REALTEK_INIT_REG1	0x1f
#define PHY_REALTEK_INIT_REG2	0x19
#define PHY_REALTEK_INIT_REG3	0x13
538
539
540
#define PHY_REALTEK_INIT_REG4	0x14
#define PHY_REALTEK_INIT_REG5	0x18
#define PHY_REALTEK_INIT_REG6	0x11
541
#define PHY_REALTEK_INIT_REG7	0x01
Ayaz Abdulla's avatar
Ayaz Abdulla committed
542
543
544
545
#define PHY_REALTEK_INIT1	0x0000
#define PHY_REALTEK_INIT2	0x8e00
#define PHY_REALTEK_INIT3	0x0001
#define PHY_REALTEK_INIT4	0xad17
546
547
548
549
#define PHY_REALTEK_INIT5	0xfb54
#define PHY_REALTEK_INIT6	0xf5c7
#define PHY_REALTEK_INIT7	0x1000
#define PHY_REALTEK_INIT8	0x0003
550
551
552
#define PHY_REALTEK_INIT9	0x0008
#define PHY_REALTEK_INIT10	0x0005
#define PHY_REALTEK_INIT11	0x0200
553
#define PHY_REALTEK_INIT_MSK1	0x0003
Ayaz Abdulla's avatar
Ayaz Abdulla committed
554

Linus Torvalds's avatar
Linus Torvalds committed
555
556
557
558
559
560
561
562
563
#define PHY_GIGABIT	0x0100

#define PHY_TIMEOUT	0x1
#define PHY_ERROR	0x2

#define PHY_100	0x1
#define PHY_1000	0x2
#define PHY_HALF	0x100

564
565
566
567
#define NV_PAUSEFRAME_RX_CAPABLE 0x0001
#define NV_PAUSEFRAME_TX_CAPABLE 0x0002
#define NV_PAUSEFRAME_RX_ENABLE  0x0004
#define NV_PAUSEFRAME_TX_ENABLE  0x0008
568
569
570
#define NV_PAUSEFRAME_RX_REQ     0x0010
#define NV_PAUSEFRAME_TX_REQ     0x0020
#define NV_PAUSEFRAME_AUTONEG    0x0040
Linus Torvalds's avatar
Linus Torvalds committed
571

572
573
574
575
576
577
578
579
580
581
582
583
/* MSI/MSI-X defines */
#define NV_MSI_X_MAX_VECTORS  8
#define NV_MSI_X_VECTORS_MASK 0x000f
#define NV_MSI_CAPABLE        0x0010
#define NV_MSI_X_CAPABLE      0x0020
#define NV_MSI_ENABLED        0x0040
#define NV_MSI_X_ENABLED      0x0080

#define NV_MSI_X_VECTOR_ALL   0x0
#define NV_MSI_X_VECTOR_RX    0x0
#define NV_MSI_X_VECTOR_TX    0x1
#define NV_MSI_X_VECTOR_OTHER 0x2
Linus Torvalds's avatar
Linus Torvalds committed
584

Ayaz Abdulla's avatar
Ayaz Abdulla committed
585
586
587
#define NV_RESTART_TX         0x1
#define NV_RESTART_RX         0x2

Ayaz Abdulla's avatar
Ayaz Abdulla committed
588
589
#define NV_TX_LIMIT_COUNT     16

590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
/* statistics */
struct nv_ethtool_str {
	char name[ETH_GSTRING_LEN];
};

static const struct nv_ethtool_str nv_estats_str[] = {
	{ "tx_bytes" },
	{ "tx_zero_rexmt" },
	{ "tx_one_rexmt" },
	{ "tx_many_rexmt" },
	{ "tx_late_collision" },
	{ "tx_fifo_errors" },
	{ "tx_carrier_errors" },
	{ "tx_excess_deferral" },
	{ "tx_retry_error" },
	{ "rx_frame_error" },
	{ "rx_extra_byte" },
	{ "rx_late_collision" },
	{ "rx_runt" },
	{ "rx_frame_too_long" },
	{ "rx_over_errors" },
	{ "rx_crc_errors" },
	{ "rx_frame_align_error" },
	{ "rx_length_error" },
	{ "rx_unicast" },
	{ "rx_multicast" },
	{ "rx_broadcast" },
Ayaz Abdulla's avatar
Ayaz Abdulla committed
617
618
619
620
621
622
623
	{ "rx_packets" },
	{ "rx_errors_total" },
	{ "tx_errors_total" },

	/* version 2 stats */
	{ "tx_deferral" },
	{ "tx_packets" },
624
	{ "rx_bytes" },
Ayaz Abdulla's avatar
Ayaz Abdulla committed
625
	{ "tx_pause" },
626
	{ "rx_pause" },
627
628
629
630
631
632
	{ "rx_drop_frame" },

	/* version 3 stats */
	{ "tx_unicast" },
	{ "tx_multicast" },
	{ "tx_broadcast" }
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
};

struct nv_ethtool_stats {
	u64 tx_bytes;
	u64 tx_zero_rexmt;
	u64 tx_one_rexmt;
	u64 tx_many_rexmt;
	u64 tx_late_collision;
	u64 tx_fifo_errors;
	u64 tx_carrier_errors;
	u64 tx_excess_deferral;
	u64 tx_retry_error;
	u64 rx_frame_error;
	u64 rx_extra_byte;
	u64 rx_late_collision;
	u64 rx_runt;
	u64 rx_frame_too_long;
	u64 rx_over_errors;
	u64 rx_crc_errors;
	u64 rx_frame_align_error;
	u64 rx_length_error;
	u64 rx_unicast;
	u64 rx_multicast;
	u64 rx_broadcast;
Ayaz Abdulla's avatar
Ayaz Abdulla committed
657
658
659
660
661
662
663
	u64 rx_packets;
	u64 rx_errors_total;
	u64 tx_errors_total;

	/* version 2 stats */
	u64 tx_deferral;
	u64 tx_packets;
664
	u64 rx_bytes;
Ayaz Abdulla's avatar
Ayaz Abdulla committed
665
	u64 tx_pause;
666
667
	u64 rx_pause;
	u64 rx_drop_frame;
668
669
670
671
672

	/* version 3 stats */
	u64 tx_unicast;
	u64 tx_multicast;
	u64 tx_broadcast;
673
674
};

675
676
#define NV_DEV_STATISTICS_V3_COUNT (sizeof(struct nv_ethtool_stats)/sizeof(u64))
#define NV_DEV_STATISTICS_V2_COUNT (NV_DEV_STATISTICS_V3_COUNT - 3)
Ayaz Abdulla's avatar
Ayaz Abdulla committed
677
678
#define NV_DEV_STATISTICS_V1_COUNT (NV_DEV_STATISTICS_V2_COUNT - 6)

679
680
681
682
683
684
685
686
687
688
689
690
/* diagnostics */
#define NV_TEST_COUNT_BASE 3
#define NV_TEST_COUNT_EXTENDED 4

static const struct nv_ethtool_str nv_etests_str[] = {
	{ "link      (online/offline)" },
	{ "register  (offline)       " },
	{ "interrupt (offline)       " },
	{ "loopback  (offline)       " }
};

struct register_test {
Al Viro's avatar
Al Viro committed
691
692
	__u32 reg;
	__u32 mask;
693
694
695
696
697
698
699
};

static const struct register_test nv_registers_test[] = {
	{ NvRegUnknownSetupReg6, 0x01 },
	{ NvRegMisc1, 0x03c },
	{ NvRegOffloadConfig, 0x03ff },
	{ NvRegMulticastAddrA, 0xffffffff },
700
	{ NvRegTxWatermark, 0x0ff },
701
702
703
704
	{ NvRegWakeUpFlags, 0x07777 },
	{ 0,0 }
};

Ayaz Abdulla's avatar
Ayaz Abdulla committed
705
706
707
708
struct nv_skb_map {
	struct sk_buff *skb;
	dma_addr_t dma;
	unsigned int dma_len;
Ayaz Abdulla's avatar
Ayaz Abdulla committed
709
710
	struct ring_desc_ex *first_tx_desc;
	struct nv_skb_map *next_tx_ctx;
Ayaz Abdulla's avatar
Ayaz Abdulla committed
711
712
};

Linus Torvalds's avatar
Linus Torvalds committed
713
714
715
716
717
718
/*
 * SMP locking:
 * All hardware access under dev->priv->lock, except the performance
 * critical parts:
 * - rx is (pseudo-) lockless: it relies on the single-threading provided
 *	by the arch code for interrupts.
Herbert Xu's avatar
Herbert Xu committed
719
 * - tx setup is lockless: it relies on netif_tx_lock. Actual submission
Linus Torvalds's avatar
Linus Torvalds committed
720
 *	needs dev->priv->lock :-(
Herbert Xu's avatar
Herbert Xu committed
721
 * - set_multicast_list: preparation lockless, relies on netif_tx_lock.
Linus Torvalds's avatar
Linus Torvalds committed
722
723
724
725
726
727
 */

/* in dev: base, irq */
struct fe_priv {
	spinlock_t lock;

728
729
730
	struct net_device *dev;
	struct napi_struct napi;

Linus Torvalds's avatar
Linus Torvalds committed
731
732
	/* General data:
	 * Locking: spin_lock(&np->lock); */
733
	struct nv_ethtool_stats estats;
Linus Torvalds's avatar
Linus Torvalds committed
734
735
736
737
738
739
740
741
	int in_shutdown;
	u32 linkspeed;
	int duplex;
	int autoneg;
	int fixed_mode;
	int phyaddr;
	int wolenabled;
	unsigned int phy_oui;
742
	unsigned int phy_model;
743
	unsigned int phy_rev;
Linus Torvalds's avatar
Linus Torvalds committed
744
	u16 gigabit;
745
	int intr_test;
746
	int recover_error;
Linus Torvalds's avatar
Linus Torvalds committed
747
748
749
750
751
752
753

	/* General data: RO fields */
	dma_addr_t ring_addr;
	struct pci_dev *pci_dev;
	u32 orig_mac[2];
	u32 irqmask;
	u32 desc_ver;
754
	u32 txrxctl_bits;
755
	u32 vlanctl_bits;
756
	u32 driver_data;
757
	u32 device_id;
758
	u32 register_size;
759
	int rx_csum;
760
	u32 mac_in_use;
Linus Torvalds's avatar
Linus Torvalds committed
761
762
763
764
765
766

	void __iomem *base;

	/* rx specific fields.
	 * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
	 */
Ayaz Abdulla's avatar
Ayaz Abdulla committed
767
768
769
770
771
	union ring_type get_rx, put_rx, first_rx, last_rx;
	struct nv_skb_map *get_rx_ctx, *put_rx_ctx;
	struct nv_skb_map *first_rx_ctx, *last_rx_ctx;
	struct nv_skb_map *rx_skb;

772
	union ring_type rx_ring;
Linus Torvalds's avatar
Linus Torvalds committed
773
	unsigned int rx_buf_sz;
774
	unsigned int pkt_limit;
Linus Torvalds's avatar
Linus Torvalds committed
775
776
	struct timer_list oom_kick;
	struct timer_list nic_poll;
777
	struct timer_list stats_poll;
778
	u32 nic_poll_irq;
779
	int rx_ring_size;
Linus Torvalds's avatar
Linus Torvalds committed
780
781
782
783
784
785
786
787
788

	/* media detection workaround.
	 * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
	 */
	int need_linktimer;
	unsigned long link_timeout;
	/*
	 * tx specific fields.
	 */
Ayaz Abdulla's avatar
Ayaz Abdulla committed
789
790
791
792
793
	union ring_type get_tx, put_tx, first_tx, last_tx;
	struct nv_skb_map *get_tx_ctx, *put_tx_ctx;
	struct nv_skb_map *first_tx_ctx, *last_tx_ctx;
	struct nv_skb_map *tx_skb;

794
	union ring_type tx_ring;
Linus Torvalds's avatar
Linus Torvalds committed
795
	u32 tx_flags;
796
	int tx_ring_size;
Ayaz Abdulla's avatar
Ayaz Abdulla committed
797
798
799
800
	int tx_limit;
	u32 tx_pkts_in_progress;
	struct nv_skb_map *tx_change_owner;
	struct nv_skb_map *tx_end_flip;
Ayaz Abdulla's avatar
Ayaz Abdulla committed
801
	int tx_stop;
802
803
804

	/* vlan fields */
	struct vlan_group *vlangrp;
805
806
807
808

	/* msi/msi-x fields */
	u32 msi_flags;
	struct msix_entry msi_x_entry[NV_MSI_X_MAX_VECTORS];
809
810
811

	/* flow control */
	u32 pause_flags;
812
813
814

	/* power saved state */
	u32 saved_config_space[NV_PCI_REGSZ_MAX/4];
Linus Torvalds's avatar
Linus Torvalds committed
815
816
817
818
819
820
};

/*
 * Maximum number of loops until we assume that a bit in the irq mask
 * is stuck. Overridable with module param.
 */
821
static int max_interrupt_work = 15;
Linus Torvalds's avatar
Linus Torvalds committed
822

823
824
/*
 * Optimization can be either throuput mode or cpu mode
825
 *
826
827
828
 * Throughput Mode: Every tx and rx packet will generate an interrupt.
 * CPU Mode: Interrupts are controlled by a timer.
 */
829
830
831
832
enum {
	NV_OPTIMIZATION_MODE_THROUGHPUT,
	NV_OPTIMIZATION_MODE_CPU
};
833
834
835
836
837
838
839
840
841
842
843
static int optimization_mode = NV_OPTIMIZATION_MODE_THROUGHPUT;

/*
 * Poll interval for timer irq
 *
 * This interval determines how frequent an interrupt is generated.
 * The is value is determined by [(time_in_micro_secs * 100) / (2^10)]
 * Min = 0, and Max = 65535
 */
static int poll_interval = -1;

844
/*
845
 * MSI interrupts
846
 */
847
848
849
850
851
enum {
	NV_MSI_INT_DISABLED,
	NV_MSI_INT_ENABLED
};
static int msi = NV_MSI_INT_ENABLED;
852
853

/*
854
 * MSIX interrupts
855
 */
856
857
858
859
enum {
	NV_MSIX_INT_DISABLED,
	NV_MSIX_INT_ENABLED
};
Ayaz Abdulla's avatar
Ayaz Abdulla committed
860
static int msix = NV_MSIX_INT_DISABLED;
861
862
863
864
865
866
867
868
869

/*
 * DMA 64bit
 */
enum {
	NV_DMA_64BIT_DISABLED,
	NV_DMA_64BIT_ENABLED
};
static int dma_64bit = NV_DMA_64BIT_ENABLED;
870

871
872
873
874
875
876
877
878
879
880
/*
 * Crossover Detection
 * Realtek 8201 phy + some OEM boards do not work properly.
 */
enum {
	NV_CROSSOVER_DETECTION_DISABLED,
	NV_CROSSOVER_DETECTION_ENABLED
};
static int phy_cross = NV_CROSSOVER_DETECTION_DISABLED;

Linus Torvalds's avatar
Linus Torvalds committed
881
882
883
884
885
886
887
static inline struct fe_priv *get_nvpriv(struct net_device *dev)
{
	return netdev_priv(dev);
}

static inline u8 __iomem *get_hwbase(struct net_device *dev)
{
888
	return ((struct fe_priv *)netdev_priv(dev))->base;
Linus Torvalds's avatar
Linus Torvalds committed
889
890
891
892
893
894
895
896
897
898
}

static inline void pci_push(u8 __iomem *base)
{
	/* force out pending posted writes */
	readl(base);
}

static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
{
899
	return le32_to_cpu(prd->flaglen)
Linus Torvalds's avatar
Linus Torvalds committed
900
901
902
		& ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
}

903
904
static inline u32 nv_descr_getlength_ex(struct ring_desc_ex *prd, u32 v)
{
905
	return le32_to_cpu(prd->flaglen) & LEN_MASK_V2;
906
907
}

908
909
910
911
912
913
914
static bool nv_optimized(struct fe_priv *np)
{
	if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
		return false;
	return true;
}

Linus Torvalds's avatar
Linus Torvalds committed
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
static int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
				int delay, int delaymax, const char *msg)
{
	u8 __iomem *base = get_hwbase(dev);

	pci_push(base);
	do {
		udelay(delay);
		delaymax -= delay;
		if (delaymax < 0) {
			if (msg)
				printk(msg);
			return 1;
		}
	} while ((readl(base + offset) & mask) != target);
	return 0;
}

933
934
935
#define NV_SETUP_RX_RING 0x01
#define NV_SETUP_TX_RING 0x02

Al Viro's avatar
Al Viro committed
936
937
938
939
940
941
942
943
944
945
static inline u32 dma_low(dma_addr_t addr)
{
	return addr;
}

static inline u32 dma_high(dma_addr_t addr)
{
	return addr>>31>>1;	/* 0 if 32bit, shift down by 32 if 64bit */
}

946
947
948
949
950
static void setup_hw_rings(struct net_device *dev, int rxtx_flags)
{
	struct fe_priv *np = get_nvpriv(dev);
	u8 __iomem *base = get_hwbase(dev);

951
	if (!nv_optimized(np)) {
952
		if (rxtx_flags & NV_SETUP_RX_RING) {
Al Viro's avatar
Al Viro committed
953
			writel(dma_low(np->ring_addr), base + NvRegRxRingPhysAddr);
954
955
		}
		if (rxtx_flags & NV_SETUP_TX_RING) {
Al Viro's avatar
Al Viro committed
956
			writel(dma_low(np->ring_addr + np->rx_ring_size*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
957
958
959
		}
	} else {
		if (rxtx_flags & NV_SETUP_RX_RING) {
Al Viro's avatar
Al Viro committed
960
961
			writel(dma_low(np->ring_addr), base + NvRegRxRingPhysAddr);
			writel(dma_high(np->ring_addr), base + NvRegRxRingPhysAddrHigh);
962
963
		}
		if (rxtx_flags & NV_SETUP_TX_RING) {
Al Viro's avatar
Al Viro committed
964
965
			writel(dma_low(np->ring_addr + np->rx_ring_size*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddr);
			writel(dma_high(np->ring_addr + np->rx_ring_size*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddrHigh);
966
967
968
969
		}
	}
}

970
971
972
973
static void free_rings(struct net_device *dev)
{
	struct fe_priv *np = get_nvpriv(dev);

974
	if (!nv_optimized(np)) {
975
		if (np->rx_ring.orig)
976
977
978
979
980
981
982
			pci_free_consistent(np->pci_dev, sizeof(struct ring_desc) * (np->rx_ring_size + np->tx_ring_size),
					    np->rx_ring.orig, np->ring_addr);
	} else {
		if (np->rx_ring.ex)
			pci_free_consistent(np->pci_dev, sizeof(struct ring_desc_ex) * (np->rx_ring_size + np->tx_ring_size),
					    np->rx_ring.ex, np->ring_addr);
	}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
983
984
985
986
	if (np->rx_skb)
		kfree(np->rx_skb);
	if (np->tx_skb)
		kfree(np->tx_skb);
987
988
}

989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
static int using_multi_irqs(struct net_device *dev)
{
	struct fe_priv *np = get_nvpriv(dev);

	if (!(np->msi_flags & NV_MSI_X_ENABLED) ||
	    ((np->msi_flags & NV_MSI_X_ENABLED) &&
	     ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1)))
		return 0;
	else
		return 1;
}

static void nv_enable_irq(struct net_device *dev)
{
	struct fe_priv *np = get_nvpriv(dev);

	if (!using_multi_irqs(dev)) {
		if (np->msi_flags & NV_MSI_X_ENABLED)
			enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
		else
Manfred Spraul's avatar
Manfred Spraul committed
1009
			enable_irq(np->pci_dev->irq);
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
	} else {
		enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
		enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
		enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
	}
}

static void nv_disable_irq(struct net_device *dev)
{
	struct fe_priv *np = get_nvpriv(dev);

	if (!using_multi_irqs(dev)) {
		if (np->msi_flags & NV_MSI_X_ENABLED)
			disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
		else
Manfred Spraul's avatar
Manfred Spraul committed
1025
			disable_irq(np->pci_dev->irq);
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
	} else {
		disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
		disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
		disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
	}
}

/* In MSIX mode, a write to irqmask behaves as XOR */
static void nv_enable_hw_interrupts(struct net_device *dev, u32 mask)
{
	u8 __iomem *base = get_hwbase(dev);

	writel(mask, base + NvRegIrqMask);
}

static void nv_disable_hw_interrupts(struct net_device *dev, u32 mask)
{
	struct fe_priv *np = get_nvpriv(dev);
	u8 __iomem *base = get_hwbase(dev);

	if (np->msi_flags & NV_MSI_X_ENABLED) {
		writel(mask, base + NvRegIrqMask);
	} else {
		if (np->msi_flags & NV_MSI_ENABLED)
			writel(0, base + NvRegMSIIrqMask);
		writel(0, base + NvRegIrqMask);
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
#define MII_READ	(-1)
/* mii_rw: read/write a register on the PHY.
 *
 * Caller must guarantee serialization
 */
static int mii_rw(struct net_device *dev, int addr, int miireg, int value)
{
	u8 __iomem *base = get_hwbase(dev);
	u32 reg;
	int retval;

Ayaz Abdulla's avatar
Ayaz Abdulla committed
1066
	writel(NVREG_MIISTAT_MASK_RW, base + NvRegMIIStatus);
Linus Torvalds's avatar
Linus Torvalds committed
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103

	reg = readl(base + NvRegMIIControl);
	if (reg & NVREG_MIICTL_INUSE) {
		writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
		udelay(NV_MIIBUSY_DELAY);
	}

	reg = (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
	if (value != MII_READ) {
		writel(value, base + NvRegMIIData);
		reg |= NVREG_MIICTL_WRITE;
	}
	writel(reg, base + NvRegMIIControl);

	if (reg_delay(dev, NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
			NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL)) {
		dprintk(KERN_DEBUG "%s: mii_rw of reg %d at PHY %d timed out.\n",
				dev->name, miireg, addr);
		retval = -1;
	} else if (value != MII_READ) {
		/* it was a write operation - fewer failures are detectable */
		dprintk(KERN_DEBUG "%s: mii_rw wrote 0x%x to reg %d at PHY %d\n",
				dev->name, value, miireg, addr);
		retval = 0;
	} else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) {
		dprintk(KERN_DEBUG "%s: mii_rw of reg %d at PHY %d failed.\n",
				dev->name, miireg, addr);
		retval = -1;
	} else {
		retval = readl(base + NvRegMIIData);
		dprintk(KERN_DEBUG "%s: mii_rw read from reg %d at PHY %d: 0x%x.\n",
				dev->name, miireg, addr, retval);
	}

	return retval;
}

1104
static int phy_reset(struct net_device *dev, u32 bmcr_setup)
Linus Torvalds's avatar
Linus Torvalds committed
1105
{
1106
	struct fe_priv *np = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1107
1108
1109
	u32 miicontrol;
	unsigned int tries = 0;

1110
	miicontrol = BMCR_RESET | bmcr_setup;
Linus Torvalds's avatar
Linus Torvalds committed
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
	if (mii_rw(dev, np->phyaddr, MII_BMCR, miicontrol)) {
		return -1;
	}

	/* wait for 500ms */
	msleep(500);

	/* must wait till reset is deasserted */
	while (miicontrol & BMCR_RESET) {
		msleep(10);
		miicontrol = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
		/* FIXME: 100 tries seem excessive */
		if (tries++ > 100)
			return -1;
	}
	return 0;
}

static int phy_init(struct net_device *dev)
{
	struct fe_priv *np = get_nvpriv(dev);
	u8 __iomem *base = get_hwbase(dev);
	u32 phyinterface, phy_reserved, mii_status, mii_control, mii_control_1000,reg;

1135
1136
1137
1138
1139
1140
1141
1142
1143
	/* phy errata for E3016 phy */
	if (np->phy_model == PHY_MODEL_MARVELL_E3016) {
		reg = mii_rw(dev, np->phyaddr, MII_NCONFIG, MII_READ);
		reg &= ~PHY_MARVELL_E3016_INITMASK;
		if (mii_rw(dev, np->phyaddr, MII_NCONFIG, reg)) {
			printk(KERN_INFO "%s: phy write to errata reg failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
	}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1144
	if (np->phy_oui == PHY_OUI_REALTEK) {
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
		if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
		    np->phy_rev == PHY_REV_REALTEK_8211B) {
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1175
		}
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
		if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
		    np->phy_rev == PHY_REV_REALTEK_8211C) {
			u32 powerstate = readl(base + NvRegPowerState2);

			/* need to perform hw phy reset */
			powerstate |= NVREG_POWERSTATE2_PHY_RESET;
			writel(powerstate, base + NvRegPowerState2);
			msleep(25);

			powerstate &= ~NVREG_POWERSTATE2_PHY_RESET;
			writel(powerstate, base + NvRegPowerState2);
			msleep(25);

			reg = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
			reg |= PHY_REALTEK_INIT9;
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, reg)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			reg = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG7, MII_READ);
			if (!(reg & PHY_REALTEK_INIT11)) {
				reg |= PHY_REALTEK_INIT11;
				if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG7, reg)) {
					printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
					return PHY_ERROR;
				}
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
		}
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
		if (np->phy_model == PHY_MODEL_REALTEK_8201) {
			if (np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_34 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_35 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_36 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_37 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_38 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_39) {
				phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
				phy_reserved |= PHY_REALTEK_INIT7;
				if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, phy_reserved)) {
					printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
					return PHY_ERROR;
				}
			}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1228
1229
		}
	}
1230

Linus Torvalds's avatar
Linus Torvalds committed
1231
1232
	/* set advertise register */
	reg = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
1233
	reg |= (ADVERTISE_10HALF|ADVERTISE_10FULL|ADVERTISE_100HALF|ADVERTISE_100FULL|ADVERTISE_PAUSE_ASYM|ADVERTISE_PAUSE_CAP);
Linus Torvalds's avatar
Linus Torvalds committed
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
	if (mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg)) {
		printk(KERN_INFO "%s: phy write to advertise failed.\n", pci_name(np->pci_dev));
		return PHY_ERROR;
	}

	/* get phy interface type */
	phyinterface = readl(base + NvRegPhyInterface);

	/* see if gigabit phy */
	mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
	if (mii_status & PHY_GIGABIT) {
		np->gigabit = PHY_GIGABIT;
1246
		mii_control_1000 = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
Linus Torvalds's avatar
Linus Torvalds committed
1247
1248
1249
1250
1251
1252
		mii_control_1000 &= ~ADVERTISE_1000HALF;
		if (phyinterface & PHY_RGMII)
			mii_control_1000 |= ADVERTISE_1000FULL;
		else
			mii_control_1000 &= ~ADVERTISE_1000FULL;

1253
		if (mii_rw(dev, np->phyaddr, MII_CTRL1000, mii_control_1000)) {
Linus Torvalds's avatar
Linus Torvalds committed
1254
1255
1256
1257
1258
1259
1260
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
	}
	else
		np->gigabit = 0;

1261
1262
1263
	mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
	mii_control |= BMCR_ANENABLE;

1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
	if (np->phy_oui == PHY_OUI_REALTEK &&
	    np->phy_model == PHY_MODEL_REALTEK_8211 &&
	    np->phy_rev == PHY_REV_REALTEK_8211C) {
		/* start autoneg since we already performed hw reset above */
		mii_control |= BMCR_ANRESTART;
		if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
			printk(KERN_INFO "%s: phy init failed\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
	} else {
		/* reset the phy
		 * (certain phys need bmcr to be setup with reset)
		 */
		if (phy_reset(dev, mii_control)) {
			printk(KERN_INFO "%s: phy reset failed\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
Linus Torvalds's avatar
Linus Torvalds committed
1281
1282
1283
1284
1285
	}

	/* phy vendor specific configuration */
	if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII) ) {
		phy_reserved = mii_rw(dev, np->phyaddr, MII_RESV1, MII_READ);
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1286
1287
		phy_reserved &= ~(PHY_CICADA_INIT1 | PHY_CICADA_INIT2);
		phy_reserved |= (PHY_CICADA_INIT3 | PHY_CICADA_INIT4);
Linus Torvalds's avatar
Linus Torvalds committed
1288
1289
1290
1291
1292
		if (mii_rw(dev, np->phyaddr, MII_RESV1, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, MII_NCONFIG, MII_READ);
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1293
		phy_reserved |= PHY_CICADA_INIT5;
Linus Torvalds's avatar
Linus Torvalds committed
1294
1295
1296
1297
1298
1299
1300
		if (mii_rw(dev, np->phyaddr, MII_NCONFIG, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
	}
	if (np->phy_oui == PHY_OUI_CICADA) {
		phy_reserved = mii_rw(dev, np->phyaddr, MII_SREVISION, MII_READ);
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1301
		phy_reserved |= PHY_CICADA_INIT6;
Linus Torvalds's avatar
Linus Torvalds committed
1302
1303
1304
1305
1306
		if (mii_rw(dev, np->phyaddr, MII_SREVISION, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
	}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
	if (np->phy_oui == PHY_OUI_VITESSE) {
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT1)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT2)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, MII_READ);
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, MII_READ);
		phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
		phy_reserved |= PHY_VITESSE_INIT3;
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT4)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT5)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, MII_READ);
		phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
		phy_reserved |= PHY_VITESSE_INIT3;
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, MII_READ);
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT6)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT7)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, MII_READ);
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		phy_reserved = mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, MII_READ);
		phy_reserved &= ~PHY_VITESSE_INIT_MSK2;
		phy_reserved |= PHY_VITESSE_INIT8;
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, phy_reserved)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT9)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
		if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT10)) {
			printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
			return PHY_ERROR;
		}
	}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1377
	if (np->phy_oui == PHY_OUI_REALTEK) {
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
		if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
		    np->phy_rev == PHY_REV_REALTEK_8211B) {
			/* reset could have cleared these out, set them back */
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
			if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
			}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1409
		}
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
		if (np->phy_model == PHY_MODEL_REALTEK_8201) {
			if (np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_34 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_35 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_36 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_37 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_38 ||
			    np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_39) {
				phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
				phy_reserved |= PHY_REALTEK_INIT7;
				if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, phy_reserved)) {
					printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
					return PHY_ERROR;
				}
			}
			if (phy_cross == NV_CROSSOVER_DETECTION_DISABLED) {
				if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) {
					printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
					return PHY_ERROR;
				}
				phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, MII_READ);
				phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
				phy_reserved |= PHY_REALTEK_INIT3;
				if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, phy_reserved)) {
					printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
					return PHY_ERROR;
				}
				if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
					printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
					return PHY_ERROR;
				}
			}
Ayaz Abdulla's avatar
Ayaz Abdulla committed
1443
1444
1445
		}
	}

1446
1447
	/* some phys clear out pause advertisment on reset, set it back */
	mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg);
Linus Torvalds's avatar
Linus Torvalds committed
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460

	/* restart auto negotiation */
	mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
	mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
	if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
		return PHY_ERROR;
	}

	return 0;
}

static void nv_start_rx(struct net_device *dev)
{
1461
	struct fe_priv *np = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1462
	u8 __iomem *base = get_hwbase(dev);
1463
	u32 rx_ctrl = readl(base + NvRegReceiverControl);
Linus Torvalds's avatar
Linus Torvalds committed
1464
1465
1466

	dprintk(KERN_DEBUG "%s: nv_start_rx\n", dev->name);
	/* Already running? Stop it. */
1467
1468
1469
	if ((readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) && !np->mac_in_use) {
		rx_ctrl &= ~NVREG_RCVCTL_START;
		writel(rx_ctrl, base + NvRegReceiverControl);
Linus Torvalds's avatar
Linus Torvalds committed
1470
1471
1472
1473
		pci_push(base);
	}
	writel(np->linkspeed, base + NvRegLinkSpeed);
	pci_push(base);
1474
1475
1476
1477
        rx_ctrl |= NVREG_RCVCTL_START;
        if (np->mac_in_use)
		rx_ctrl &= ~NVREG_RCVCTL_RX_PATH_EN;
	writel(rx_ctrl, base + NvRegReceiverControl);
Linus Torvalds's avatar
Linus Torvalds committed
1478
1479
1480
1481
1482
1483
1484
	dprintk(KERN_DEBUG "%s: nv_start_rx to duplex %d, speed 0x%08x.\n",
				dev->name, np->duplex, np->linkspeed);
	pci_push(base);
}

static void nv_stop_rx(struct net_device *dev)
{
1485
	struct fe_priv *np = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1486
	u8 __iomem *base = get_hwbase(dev);
1487
	u32 rx_ctrl = readl(base + NvRegReceiverControl);
Linus Torvalds's avatar
Linus Torvalds committed
1488
1489

	dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
1490
1491
1492
1493
1494
	if (!np->mac_in_use)
		rx_ctrl &= ~NVREG_RCVCTL_START;
	else
		rx_ctrl |= NVREG_RCVCTL_RX_PATH_EN;
	writel(rx_ctrl, base + NvRegReceiverControl);
Linus Torvalds's avatar
Linus Torvalds committed
1495
1496
1497
1498
1499
	reg_delay(dev, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
			NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
			KERN_INFO "nv_stop_rx: ReceiverStatus remained busy");

	udelay(NV_RXSTOP_DELAY2);
1500
1501
	if (!np->mac_in_use)
		writel(0, base + NvRegLinkSpeed);
Linus Torvalds's avatar
Linus Torvalds committed
1502
1503
1504
1505
}

static void nv_start_tx(struct net_device *dev)
{
1506
	struct fe_priv *np = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1507
	u8 __iomem *base = get_hwbase(dev);
1508
	u32 tx_ctrl = readl(base + NvRegTransmitterControl);
Linus Torvalds's avatar
Linus Torvalds committed
1509
1510

	dprintk(KERN_DEBUG "%s: nv_start_tx\n", dev->name);
1511
1512
1513
1514
	tx_ctrl |= NVREG_XMITCTL_START;
	if (np->mac_in_use)
		tx_ctrl &= ~NVREG_XMITCTL_TX_PATH_EN;
	writel(tx_ctrl, base + NvRegTransmitterControl);
Linus Torvalds's avatar
Linus Torvalds committed
1515
1516
1517
1518
1519
	pci_push(base);
}

static void nv_stop_tx(struct net_device *dev)
{
1520
	struct fe_priv *np = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1521
	u8 __iomem *base = get_hwbase(dev);
1522
	u32 tx_ctrl = readl(base + NvRegTransmitterControl);
Linus Torvalds's avatar
Linus Torvalds committed
1523
1524

	dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
1525
1526
1527
1528
1529
	if (!np->mac_in_use)
		tx_ctrl &= ~NVREG_XMITCTL_START;
	else
		tx_ctrl |= NVREG_XMITCTL_TX_PATH_EN;
	writel(tx_ctrl, base + NvRegTransmitterControl);
Linus Torvalds's avatar
Linus Torvalds committed