aboutsummaryrefslogtreecommitdiffstats
path: root/gpxe/src/drivers/net/forcedeth.c
blob: a30f137895e0c95a24bc1fbf6600a03394bf419b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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
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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
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
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
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
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
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
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
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
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
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
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
1377
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
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
/**************************************************************************
*    forcedeth.c -- Etherboot device driver for the NVIDIA nForce 
*			media access controllers.
*
* Note: This driver is based on the Linux driver that was based on
*      a cleanroom reimplementation which was based on reverse
*      engineered documentation written by Carl-Daniel Hailfinger
*      and Andrew de Quincey. It's neither supported nor endorsed
*      by NVIDIA Corp. Use at your own risk.
*
*    Written 2004 by Timothy Legge <tlegge@rogers.com>
*
*    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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*    Portions of this code based on:
*		forcedeth: Ethernet driver for NVIDIA nForce media access controllers:
*
*	(C) 2003 Manfred Spraul
*		See Linux Driver for full information
*	
*	Linux Driver Version 0.30, 25 Sep 2004
*	Linux Kernel 2.6.10
* 
* 
*    REVISION HISTORY:
*    ================
*    v1.0	01-31-2004	timlegge	Initial port of Linux driver
*    v1.1	02-03-2004	timlegge	Large Clean up, first release 
*    v1.2	05-14-2005	timlegge	Add Linux 0.22 to .030 features
*
*    Indent Options: indent -kr -i8
***************************************************************************/

/* to get some global routines like printf */
#include "etherboot.h"
/* to get the interface to the body of the program */
#include "nic.h"
/* to get the PCI support functions, if this is a PCI NIC */
#include <gpxe/pci.h>
/* Include timer support functions */
#include <gpxe/ethernet.h>
#include "mii.h"

#define drv_version "v1.2"
#define drv_date "05-14-2005"

//#define TFTM_DEBUG
#ifdef TFTM_DEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif

#define ETH_DATA_LEN   1500

/* Condensed operations for readability. */
#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))

static unsigned long BASE;
/* NIC specific static variables go here */
#define PCI_DEVICE_ID_NVIDIA_NVENET_1           0x01c3
#define PCI_DEVICE_ID_NVIDIA_NVENET_2           0x0066
#define PCI_DEVICE_ID_NVIDIA_NVENET_4           0x0086
#define PCI_DEVICE_ID_NVIDIA_NVENET_5           0x008c
#define PCI_DEVICE_ID_NVIDIA_NVENET_3           0x00d6
#define PCI_DEVICE_ID_NVIDIA_NVENET_7           0x00df
#define PCI_DEVICE_ID_NVIDIA_NVENET_6           0x00e6
#define PCI_DEVICE_ID_NVIDIA_NVENET_8           0x0056
#define PCI_DEVICE_ID_NVIDIA_NVENET_9           0x0057
#define PCI_DEVICE_ID_NVIDIA_NVENET_10          0x0037
#define PCI_DEVICE_ID_NVIDIA_NVENET_11          0x0038
#define PCI_DEVICE_ID_NVIDIA_NVENET_15          0x0373


/*
 * Hardware access:
 */

#define DEV_NEED_LASTPACKET1	0x0001	/* set LASTPACKET1 in tx flags */
#define DEV_IRQMASK_1		0x0002	/* use NVREG_IRQMASK_WANTED_1 for irq mask */
#define DEV_IRQMASK_2		0x0004	/* use NVREG_IRQMASK_WANTED_2 for irq mask */
#define DEV_NEED_TIMERIRQ	0x0008	/* set the timer irq flag in the irq mask */
#define DEV_NEED_LINKTIMER	0x0010	/* poll link settings. Relies on the timer irq */

enum {
	NvRegIrqStatus = 0x000,
#define NVREG_IRQSTAT_MIIEVENT	0040
#define NVREG_IRQSTAT_MASK		0x1ff
	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
#define NVREG_IRQ_TX2			0x0010
#define NVREG_IRQ_TIMER			0x0020
#define NVREG_IRQ_LINK			0x0040
#define NVREG_IRQ_TX1			0x0100
#define NVREG_IRQMASK_WANTED_1		0x005f
#define NVREG_IRQMASK_WANTED_2		0x0147
#define NVREG_IRQ_UNKNOWN		(~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))

	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,
#define NVREG_POLL_DEFAULT	970
	NvRegMisc1 = 0x080,
#define NVREG_MISC1_HD		0x02
#define NVREG_MISC1_FORCE	0x3b0f3c

	NvRegTransmitterControl = 0x084,
#define NVREG_XMITCTL_START	0x01
	NvRegTransmitterStatus = 0x088,
#define NVREG_XMITSTAT_BUSY	0x01

	NvRegPacketFilterFlags = 0x8c,
#define NVREG_PFF_ALWAYS	0x7F0008
#define NVREG_PFF_PROMISC	0x80
#define NVREG_PFF_MYADDR	0x20

	NvRegOffloadConfig = 0x90,
#define NVREG_OFFLOAD_HOMEPHY	0x601
#define NVREG_OFFLOAD_NORMAL	RX_NIC_BUFSIZE
	NvRegReceiverControl = 0x094,
#define NVREG_RCVCTL_START	0x01
	NvRegReceiverStatus = 0x98,
#define NVREG_RCVSTAT_BUSY	0x01

	NvRegRandomSeed = 0x9c,
#define NVREG_RNDSEED_MASK	0x00ff
#define NVREG_RNDSEED_FORCE	0x7f00
#define NVREG_RNDSEED_FORCE2	0x2d00
#define NVREG_RNDSEED_FORCE3	0x7400

	NvRegUnknownSetupReg1 = 0xA0,
#define NVREG_UNKSETUP1_VAL	0x16070f
	NvRegUnknownSetupReg2 = 0xA4,
#define NVREG_UNKSETUP2_VAL	0x16
	NvRegMacAddrA = 0xA8,
	NvRegMacAddrB = 0xAC,
	NvRegMulticastAddrA = 0xB0,
#define NVREG_MCASTADDRA_FORCE	0x01
	NvRegMulticastAddrB = 0xB4,
	NvRegMulticastMaskA = 0xB8,
	NvRegMulticastMaskB = 0xBC,

	NvRegPhyInterface = 0xC0,
#define PHY_RGMII		0x10000000

	NvRegTxRingPhysAddr = 0x100,
	NvRegRxRingPhysAddr = 0x104,
	NvRegRingSizes = 0x108,
#define NVREG_RINGSZ_TXSHIFT 0
#define NVREG_RINGSZ_RXSHIFT 16
	NvRegUnknownTransmitterReg = 0x10c,
	NvRegLinkSpeed = 0x110,
#define NVREG_LINKSPEED_FORCE 0x10000
#define NVREG_LINKSPEED_10	1000
#define NVREG_LINKSPEED_100	100
#define NVREG_LINKSPEED_1000	50
	NvRegUnknownSetupReg5 = 0x130,
#define NVREG_UNKSETUP5_BIT31	(1<<31)
	NvRegUnknownSetupReg3 = 0x13c,
#define NVREG_UNKSETUP3_VAL1	0x200010
	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
	NvRegMIIStatus = 0x180,
#define NVREG_MIISTAT_ERROR		0x0001
#define NVREG_MIISTAT_LINKCHANGE	0x0008
#define NVREG_MIISTAT_MASK		0x000f
#define NVREG_MIISTAT_MASK2		0x000f
	NvRegUnknownSetupReg4 = 0x184,
#define NVREG_UNKSETUP4_VAL	8

	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,
	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
};

#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)
#define NV_TX_LASTPACKET1	(1<<24)
#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)
#define NV_TX2_LASTPACKET1	(1<<23)
#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)

#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)

#define NV_RX2_CHECKSUMMASK	(0x1C000000)
#define NV_RX2_CHECKSUMOK1	(0x10000000)
#define NV_RX2_CHECKSUMOK2	(0x14000000)
#define NV_RX2_CHECKSUMOK3	(0x18000000)
#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)

/* Miscelaneous hardware related defines: */
#define NV_PCI_REGSZ		0x270

/* 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

#define NV_WAKEUPPATTERNS	5
#define NV_WAKEUPMASKENTRIES	4

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

#define RX_RING		4
#define TX_RING		2

/* 
 * If your nic mysteriously hangs then try to reduce the limits
 * to 1/0: It might be required to set NV_TX_LASTPACKET in the
 * last valid ring entry. But this would be impossible to
 * implement - probably a disassembly error.
 */
#define TX_LIMIT_STOP	63
#define TX_LIMIT_START	62

/* rx/tx mac addr + type + vlan + align + slack*/
#define RX_NIC_BUFSIZE		(ETH_DATA_LEN + 64)
/* even more slack */
#define RX_ALLOC_BUFSIZE	(ETH_DATA_LEN + 128)

#define OOM_REFILL	(1+HZ/20)
#define POLL_WAIT	(1+HZ/100)
#define LINK_TIMEOUT	(3*HZ)

/* 
 * desc_ver values:
 * This field has two purposes:
 * - Newer nics uses a different ring layout. The layout is selected by
 *   comparing np->desc_ver with DESC_VER_xy.
 * - It contains bits that are forced on when writing to NvRegTxRxControl.
 */
#define DESC_VER_1	0x0
#define DESC_VER_2	(0x02100|NVREG_TXRXCTL_RXCHECK)

/* PHY defines */
#define PHY_OUI_MARVELL	0x5043
#define PHY_OUI_CICADA	0x03f1
#define PHYID1_OUI_MASK	0x03ff
#define PHYID1_OUI_SHFT	6
#define PHYID2_OUI_MASK	0xfc00
#define PHYID2_OUI_SHFT	10
#define PHY_INIT1	0x0f000
#define PHY_INIT2	0x0e00
#define PHY_INIT3	0x01000
#define PHY_INIT4	0x0200
#define PHY_INIT5	0x0004
#define PHY_INIT6	0x02000
#define PHY_GIGABIT	0x0100

#define PHY_TIMEOUT	0x1
#define PHY_ERROR	0x2

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

/* FIXME: MII defines that should be added to <linux/mii.h> */
#define MII_1000BT_CR	0x09
#define MII_1000BT_SR	0x0a
#define ADVERTISE_1000FULL	0x0200
#define ADVERTISE_1000HALF	0x0100
#define LPA_1000FULL	0x0800
#define LPA_1000HALF	0x0400

/* Big endian: should work, but is untested */
struct ring_desc {
	u32 PacketBuffer;
	u32 FlagLen;
};


/* Define the TX and RX Descriptor and Buffers */
struct {
	struct ring_desc tx_ring[TX_RING];
	unsigned char txb[TX_RING * RX_NIC_BUFSIZE];
	struct ring_desc rx_ring[RX_RING];
	unsigned char rxb[RX_RING * RX_NIC_BUFSIZE];
} forcedeth_bufs __shared;
#define tx_ring forcedeth_bufs.tx_ring
#define rx_ring forcedeth_bufs.rx_ring
#define txb forcedeth_bufs.txb
#define rxb forcedeth_bufs.rxb

/* Private Storage for the NIC */
static struct forcedeth_private {
	/* General data:
	 * Locking: spin_lock(&np->lock); */
	int in_shutdown;
	u32 linkspeed;
	int duplex;
	int phyaddr;
	int wolenabled;
	unsigned int phy_oui;
	u16 gigabit;

	/* General data: RO fields */
	u8 *ring_addr;
	u32 orig_mac[2];
	u32 irqmask;
	u32 desc_ver;
	/* rx specific fields.
	 * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
	 */
	unsigned int cur_rx, refill_rx;

	/*
	 * tx specific fields.
	 */
	unsigned int next_tx, nic_tx;
	u32 tx_flags;
} npx;

static struct forcedeth_private *np;

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

static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
{
	return le32_to_cpu(prd->FlagLen)
	    & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
}

static int reg_delay(int offset, u32 mask,
		     u32 target, int delay, int delaymax, const char *msg)
{
	u8 *base = (u8 *) BASE;

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

#define MII_READ	(-1)
#define MII_PHYSID1         0x02	/* PHYS ID 1                   */
#define MII_PHYSID2         0x03	/* PHYS ID 2                   */
#define MII_BMCR            0x00	/* Basic mode control register */
#define MII_BMSR            0x01	/* Basic mode status register  */
#define MII_ADVERTISE       0x04	/* Advertisement control reg   */
#define MII_LPA             0x05	/* Link partner ability reg    */

#define BMSR_ANEGCOMPLETE       0x0020	/* Auto-negotiation complete   */

/* Link partner ability register. */
#define LPA_SLCT                0x001f	/* Same as advertise selector  */
#define LPA_10HALF              0x0020	/* Can do 10mbps half-duplex   */
#define LPA_10FULL              0x0040	/* Can do 10mbps full-duplex   */
#define LPA_100HALF             0x0080	/* Can do 100mbps half-duplex  */
#define LPA_100FULL             0x0100	/* Can do 100mbps full-duplex  */
#define LPA_100BASE4            0x0200	/* Can do 100mbps 4k packets   */
#define LPA_RESV                0x1c00	/* Unused...                   */
#define LPA_RFAULT              0x2000	/* Link partner faulted        */
#define LPA_LPACK               0x4000	/* Link partner acked us       */
#define LPA_NPAGE               0x8000	/* Next page bit               */

/* mii_rw: read/write a register on the PHY.
 *
 * Caller must guarantee serialization
 */
static int mii_rw(struct nic *nic __unused, int addr, int miireg,
		  int value)
{
	u8 *base = (u8 *) BASE;
	u32 reg;
	int retval;

	writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);

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

static int phy_reset(struct nic *nic)
{

	u32 miicontrol;
	unsigned int tries = 0;

	miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
	miicontrol |= BMCR_RESET;
	if (mii_rw(nic, np->phyaddr, MII_BMCR, miicontrol)) {
		return -1;
	}

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

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

static int phy_init(struct nic *nic)
{
	u8 *base = (u8 *) BASE;
	u32 phyinterface, phy_reserved, mii_status, mii_control,
	    mii_control_1000, reg;

	/* set advertise register */
	reg = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
	reg |=
	    (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
	     ADVERTISE_100FULL | 0x800 | 0x400);
	if (mii_rw(nic, np->phyaddr, MII_ADVERTISE, reg)) {
		printf("phy write to advertise failed.\n");
		return PHY_ERROR;
	}

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

	/* see if gigabit phy */
	mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);

	if (mii_status & PHY_GIGABIT) {
		np->gigabit = PHY_GIGABIT;
		mii_control_1000 =
		    mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
		mii_control_1000 &= ~ADVERTISE_1000HALF;
		if (phyinterface & PHY_RGMII)
			mii_control_1000 |= ADVERTISE_1000FULL;
		else
			mii_control_1000 &= ~ADVERTISE_1000FULL;

		if (mii_rw
		    (nic, np->phyaddr, MII_1000BT_CR, mii_control_1000)) {
			printf("phy init failed.\n");
			return PHY_ERROR;
		}
	} else
		np->gigabit = 0;

	/* reset the phy */
	if (phy_reset(nic)) {
		printf("phy reset failed\n");
		return PHY_ERROR;
	}

	/* phy vendor specific configuration */
	if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII)) {
		phy_reserved =
		    mii_rw(nic, np->phyaddr, MII_RESV1, MII_READ);
		phy_reserved &= ~(PHY_INIT1 | PHY_INIT2);
		phy_reserved |= (PHY_INIT3 | PHY_INIT4);
		if (mii_rw(nic, np->phyaddr, MII_RESV1, phy_reserved)) {
			printf("phy init failed.\n");
			return PHY_ERROR;
		}
		phy_reserved =
		    mii_rw(nic, np->phyaddr, MII_NCONFIG, MII_READ);
		phy_reserved |= PHY_INIT5;
		if (mii_rw(nic, np->phyaddr, MII_NCONFIG, phy_reserved)) {
			printf("phy init failed.\n");
			return PHY_ERROR;
		}
	}
	if (np->phy_oui == PHY_OUI_CICADA) {
		phy_reserved =
		    mii_rw(nic, np->phyaddr, MII_SREVISION, MII_READ);
		phy_reserved |= PHY_INIT6;
		if (mii_rw(nic, np->phyaddr, MII_SREVISION, phy_reserved)) {
			printf("phy init failed.\n");
			return PHY_ERROR;
		}
	}

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

	return 0;
}

static void start_rx(struct nic *nic __unused)
{
	u8 *base = (u8 *) BASE;

	dprintf(("start_rx\n"));
	/* Already running? Stop it. */
	if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
		writel(0, base + NvRegReceiverControl);
		pci_push(base);
	}
	writel(np->linkspeed, base + NvRegLinkSpeed);
	pci_push(base);
	writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
	pci_push(base);
}

static void stop_rx(void)
{
	u8 *base = (u8 *) BASE;

	dprintf(("stop_rx\n"));
	writel(0, base + NvRegReceiverControl);
	reg_delay(NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
		  NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
		  "stop_rx: ReceiverStatus remained busy");

	udelay(NV_RXSTOP_DELAY2);
	writel(0, base + NvRegLinkSpeed);
}

static void start_tx(struct nic *nic __unused)
{
	u8 *base = (u8 *) BASE;

	dprintf(("start_tx\n"));
	writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
	pci_push(base);
}

static void stop_tx(void)
{
	u8 *base = (u8 *) BASE;

	dprintf(("stop_tx\n"));
	writel(0, base + NvRegTransmitterControl);
	reg_delay(NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
		  NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
		  "stop_tx: TransmitterStatus remained busy");

	udelay(NV_TXSTOP_DELAY2);
	writel(0, base + NvRegUnknownTransmitterReg);
}


static void txrx_reset(struct nic *nic __unused)
{
	u8 *base = (u8 *) BASE;

	dprintf(("txrx_reset\n"));
	writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver,
	       base + NvRegTxRxControl);

	pci_push(base);
	udelay(NV_TXRX_RESET_DELAY);
	writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
	pci_push(base);
}

/*
 * alloc_rx: fill rx ring entries.
 * Return 1 if the allocations for the skbs failed and the
 * rx engine is without Available descriptors
 */
static int alloc_rx(struct nic *nic __unused)
{
	unsigned int refill_rx = np->refill_rx;
	int i;
	//while (np->cur_rx != refill_rx) {
	for (i = 0; i < RX_RING; i++) {
		//int nr = refill_rx % RX_RING;
		rx_ring[i].PacketBuffer =
		    virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
		wmb();
		rx_ring[i].FlagLen =
		    cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL);
		/*      printf("alloc_rx: Packet  %d marked as Available\n",
		   refill_rx); */
		refill_rx++;
	}
	np->refill_rx = refill_rx;
	if (np->cur_rx - refill_rx == RX_RING)
		return 1;
	return 0;
}

static int update_linkspeed(struct nic *nic)
{
	int adv, lpa;
	u32 newls;
	int newdup = np->duplex;
	u32 mii_status;
	int retval = 0; 
	u32 control_1000, status_1000, phyreg;
	u8 *base = (u8 *) BASE;
	int i;

	/* BMSR_LSTATUS is latched, read it twice:
	 * we want the current value.
	 */
	mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
	mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);

#if 1
	//yhlu
	for(i=0;i<30;i++) {
		mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
		if((mii_status & BMSR_LSTATUS) && (mii_status & BMSR_ANEGCOMPLETE)) break;
		mdelay(100);
	}
#endif

	if (!(mii_status & BMSR_LSTATUS)) {
		printf
		    ("no link detected by phy - falling back to 10HD.\n");
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
		newdup = 0;
		retval = 0;
		goto set_speed;
	}

	/* check auto negotiation is complete */
	if (!(mii_status & BMSR_ANEGCOMPLETE)) {
		/* still in autonegotiation - configure nic for 10 MBit HD and wait. */
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
		newdup = 0;
		retval = 0;
		printf("autoneg not completed - falling back to 10HD.\n");
		goto set_speed;
	}

	retval = 1;
	if (np->gigabit == PHY_GIGABIT) {
		control_1000 =
		    mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ);
		status_1000 =
		    mii_rw(nic, np->phyaddr, MII_1000BT_SR, MII_READ);

		if ((control_1000 & ADVERTISE_1000FULL) &&
		    (status_1000 & LPA_1000FULL)) {
			printf
			    ("update_linkspeed: GBit ethernet detected.\n");
			newls =
			    NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
			newdup = 1;
			goto set_speed;
		}
	}

	adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
	lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ);
	dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n",
		 adv, lpa));

	/* FIXME: handle parallel detection properly, handle gigabit ethernet */
	lpa = lpa & adv;
	if (lpa & LPA_100FULL) {
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
		newdup = 1;
	} else if (lpa & LPA_100HALF) {
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
		newdup = 0;
	} else if (lpa & LPA_10FULL) {
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
		newdup = 1;
	} else if (lpa & LPA_10HALF) {
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
		newdup = 0;
	} else {
		printf("bad ability %hX - falling back to 10HD.\n", lpa);
		newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
		newdup = 0;
	}

      set_speed:
	if (np->duplex == newdup && np->linkspeed == newls)
		return retval;

	dprintf(("changing link setting from %d/%s to %d/%s.\n",
	       np->linkspeed, np->duplex ? "Full-Duplex": "Half-Duplex", newls, newdup ? "Full-Duplex": "Half-Duplex"));

	np->duplex = newdup;
	np->linkspeed = newls;

	if (np->gigabit == PHY_GIGABIT) {
		phyreg = readl(base + NvRegRandomSeed);
		phyreg &= ~(0x3FF00);
		if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
			phyreg |= NVREG_RNDSEED_FORCE3;
		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
			phyreg |= NVREG_RNDSEED_FORCE2;
		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
			phyreg |= NVREG_RNDSEED_FORCE;
		writel(phyreg, base + NvRegRandomSeed);
	}

	phyreg = readl(base + NvRegPhyInterface);
	phyreg &= ~(PHY_HALF | PHY_100 | PHY_1000);
	if (np->duplex == 0)
		phyreg |= PHY_HALF;
	if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
		phyreg |= PHY_100;
	else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
		phyreg |= PHY_1000;
	writel(phyreg, base + NvRegPhyInterface);

	writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
	       base + NvRegMisc1);
	pci_push(base);
	writel(np->linkspeed, base + NvRegLinkSpeed);
	pci_push(base);

	return retval;
}

#if 0 /* Not used */
static void nv_linkchange(struct nic *nic)
{
	if (update_linkspeed(nic)) {
//                if (netif_carrier_ok(nic)) {
		stop_rx();
//=                } else {
		//                      netif_carrier_on(dev);
		//                    printk(KERN_INFO "%s: link up.\n", dev->name);
		//          }
		start_rx(nic);
	} else {
		//        if (netif_carrier_ok(dev)) {
		//              netif_carrier_off(dev);
		//            printk(KERN_INFO "%s: link down.\n", dev->name);
		stop_rx();
		//  }
	}
}
#endif

static int init_ring(struct nic *nic)
{
	int i;

	np->next_tx = np->nic_tx = 0;
	for (i = 0; i < TX_RING; i++)
		tx_ring[i].FlagLen = 0;

	np->cur_rx = 0;
	np->refill_rx = 0;
	for (i = 0; i < RX_RING; i++)
		rx_ring[i].FlagLen = 0;
	return alloc_rx(nic);
}

static void set_multicast(struct nic *nic)
{

	u8 *base = (u8 *) BASE;
	u32 addr[2];
	u32 mask[2];
	u32 pff;
	u32 alwaysOff[2];
	u32 alwaysOn[2];

	memset(addr, 0, sizeof(addr));
	memset(mask, 0, sizeof(mask));

	pff = NVREG_PFF_MYADDR;

	alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;

	addr[0] = alwaysOn[0];
	addr[1] = alwaysOn[1];
	mask[0] = alwaysOn[0] | alwaysOff[0];
	mask[1] = alwaysOn[1] | alwaysOff[1];

	addr[0] |= NVREG_MCASTADDRA_FORCE;
	pff |= NVREG_PFF_ALWAYS;
	stop_rx();
	writel(addr[0], base + NvRegMulticastAddrA);
	writel(addr[1], base + NvRegMulticastAddrB);
	writel(mask[0], base + NvRegMulticastMaskA);
	writel(mask[1], base + NvRegMulticastMaskB);
	writel(pff, base + NvRegPacketFilterFlags);
	start_rx(nic);
}

/**************************************************************************
RESET - Reset the NIC to prepare for use
***************************************************************************/
static int forcedeth_reset(struct nic *nic)
{
	u8 *base = (u8 *) BASE;
	int ret, oom, i;
	ret = 0;
	dprintf(("forcedeth: open\n"));

	/* 1) erase previous misconfiguration */
	/* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */
	writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
	writel(0, base + NvRegMulticastAddrB);
	writel(0, base + NvRegMulticastMaskA);
	writel(0, base + NvRegMulticastMaskB);
	writel(0, base + NvRegPacketFilterFlags);

	writel(0, base + NvRegTransmitterControl);
	writel(0, base + NvRegReceiverControl);

	writel(0, base + NvRegAdapterControl);

	/* 2) initialize descriptor rings */
	oom = init_ring(nic);

	writel(0, base + NvRegLinkSpeed);
	writel(0, base + NvRegUnknownTransmitterReg);
	txrx_reset(nic);
	writel(0, base + NvRegUnknownSetupReg6);

	np->in_shutdown = 0;

	/* 3) set mac address */
	{
		u32 mac[2];

		mac[0] =
		    (nic->node_addr[0] << 0) + (nic->node_addr[1] << 8) +
		    (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24);
		mac[1] =
		    (nic->node_addr[4] << 0) + (nic->node_addr[5] << 8);

		writel(mac[0], base + NvRegMacAddrA);
		writel(mac[1], base + NvRegMacAddrB);
	}

	/* 4) give hw rings */
	writel((u32) virt_to_le32desc(&rx_ring[0]),
	       base + NvRegRxRingPhysAddr);
	writel((u32) virt_to_le32desc(&tx_ring[0]),
	       base + NvRegTxRingPhysAddr);

	writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
	       ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
	       base + NvRegRingSizes);

	/* 5) continue setup */
	np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
	np->duplex = 0;
	writel(np->linkspeed, base + NvRegLinkSpeed);
	writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
	writel(np->desc_ver, base + NvRegTxRxControl);
	pci_push(base);
	writel(NVREG_TXRXCTL_BIT1 | np->desc_ver, base + NvRegTxRxControl);
	reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
		  NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY,
		  NV_SETUP5_DELAYMAX,
		  "open: SetupReg5, Bit 31 remained off\n");

	writel(0, base + NvRegUnknownSetupReg4);
//       writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
	writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
#if 0
	printf("%d-Mbs Link, %s-Duplex\n",
	       np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
	       np->duplex ? "Full" : "Half");
#endif

	/* 6) continue setup */
	writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
	writel(readl(base + NvRegTransmitterStatus),
	       base + NvRegTransmitterStatus);
	writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
	writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig);

	writel(readl(base + NvRegReceiverStatus),
	       base + NvRegReceiverStatus);

	/* Get a random number */
	i = random();
	writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK),
	       base + NvRegRandomSeed);
	writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
	writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
	writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
	writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
	writel((np->
		phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
	       NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
	       base + NvRegAdapterControl);
	writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
	writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4);
	writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags);

	i = readl(base + NvRegPowerState);
	if ((i & NVREG_POWERSTATE_POWEREDUP) == 0)
		writel(NVREG_POWERSTATE_POWEREDUP | i,
		       base + NvRegPowerState);

	pci_push(base);
	udelay(10);
	writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID,
	       base + NvRegPowerState);

	writel(0, base + NvRegIrqMask);
	pci_push(base);
	writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
	writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
	pci_push(base);
/*
	writel(np->irqmask, base + NvRegIrqMask);
*/
	writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
	writel(0, base + NvRegMulticastAddrB);
	writel(0, base + NvRegMulticastMaskA);
	writel(0, base + NvRegMulticastMaskB);
	writel(NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR,
	       base + NvRegPacketFilterFlags);

	set_multicast(nic);
	/* One manual link speed update: Interrupts are enabled, future link
	 * speed changes cause interrupts and are handled by nv_link_irq().
	 */
	{
		u32 miistat;
		miistat = readl(base + NvRegMIIStatus);
		writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
		dprintf(("startup: got 0x%hX.\n", miistat));
	}
	ret = update_linkspeed(nic);

	//start_rx(nic);
	start_tx(nic);

	if (ret) {
		//Start Connection netif_carrier_on(dev);
	} else {
		printf("no link during initialization.\n");
	}

	return ret;
}

/* 
 * extern void hex_dump(const char *data, const unsigned int len);
*/
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
static int forcedeth_poll(struct nic *nic, int retrieve)
{
	/* return true if there's an ethernet packet ready to read */
	/* nic->packet should contain data on return */
	/* nic->packetlen should contain length of data */

	int len;
	int i;
	u32 Flags;

	i = np->cur_rx % RX_RING;

	Flags = le32_to_cpu(rx_ring[i].FlagLen);
	len = nv_descr_getlength(&rx_ring[i], np->desc_ver);

	if (Flags & NV_RX_AVAIL)
		return 0;	/* still owned by hardware, */

	if (np->desc_ver == DESC_VER_1) {
		if (!(Flags & NV_RX_DESCRIPTORVALID))
			return 0;
	} else {
		if (!(Flags & NV_RX2_DESCRIPTORVALID))
			return 0;
	}

	if (!retrieve)
		return 1;

	/* got a valid packet - forward it to the network core */
	nic->packetlen = len;
	memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen);
/*
 * 	hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
*/
	wmb();
	np->cur_rx++;
	alloc_rx(nic);
	return 1;
}


/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void forcedeth_transmit(struct nic *nic, const char *d,	/* Destination */
			       unsigned int t,	/* Type */
			       unsigned int s,	/* size */
			       const char *p)
{				/* Packet */
	/* send the packet to destination */
	u8 *ptxb;
	u16 nstype;
	u8 *base = (u8 *) BASE;
	int nr = np->next_tx % TX_RING;

	/* point to the current txb incase multiple tx_rings are used */
	ptxb = txb + (nr * RX_NIC_BUFSIZE);
	//np->tx_skbuff[nr] = ptxb;

	/* copy the packet to ring buffer */
	memcpy(ptxb, d, ETH_ALEN);	/* dst */
	memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);	/* src */
	nstype = htons((u16) t);	/* type */
	memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);	/* type */
	memcpy(ptxb + ETH_HLEN, p, s);

	s += ETH_HLEN;
	while (s < ETH_ZLEN)	/* pad to min length */
		ptxb[s++] = '\0';

	tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);

	wmb();
	tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags);

	writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl);
	pci_push(base);
	np->next_tx++;
}

/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
static void forcedeth_disable ( struct nic *nic __unused ) {
	/* put the card in its initial state */
	/* This function serves 3 purposes.
	 * This disables DMA and interrupts so we don't receive
	 *  unexpected packets or interrupts from the card after
	 *  etherboot has finished. 
	 * This frees resources so etherboot may use
	 *  this driver on another interface
	 * This allows etherboot to reinitialize the interface
	 *  if something is something goes wrong.
	 */
	u8 *base = (u8 *) BASE;
	np->in_shutdown = 1;
	stop_tx();
	stop_rx();

	/* disable interrupts on the nic or we will lock up */
	writel(0, base + NvRegIrqMask);
	pci_push(base);
	dprintf(("Irqmask is zero again\n"));

	/* specia op:o write back the misordered MAC address - otherwise
	 * the next probe_nic would see a wrong address.
	 */
	writel(np->orig_mac[0], base + NvRegMacAddrA);
	writel(np->orig_mac[1], base + NvRegMacAddrB);
}

/**************************************************************************
IRQ - Enable, Disable, or Force interrupts
***************************************************************************/
static void forcedeth_irq(struct nic *nic __unused,
			  irq_action_t action __unused)
{
	switch (action) {
	case DISABLE:
		break;
	case ENABLE:
		break;
	case FORCE:
		break;
	}
}

static struct nic_operations forcedeth_operations = {
	.connect	= dummy_connect,
	.poll		= forcedeth_poll,
	.transmit	= forcedeth_transmit,
	.irq		= forcedeth_irq,

};

/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
***************************************************************************/
#define IORESOURCE_MEM 0x00000200
#define board_found 1
#define valid_link 0
static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {

	unsigned long addr;
	int sz;
	u8 *base;
	int i;

	if (pci->ioaddr == 0)
		return 0;

	printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n",
	       pci->driver_name, pci->vendor, pci->device);

        nic->ioaddr = pci->ioaddr;
        nic->irqno = 0;

	/* point to private storage */
	np = &npx;

	adjust_pci_device(pci);

	addr = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
	sz = pci_bar_size(pci, PCI_BASE_ADDRESS_0);

	/* BASE is used throughout to address the card */
	BASE = (unsigned long) ioremap(addr, sz);
	if (!BASE)
		return 0;

	/* handle different descriptor versions */
	if (pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
	    pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
	    pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_3)
		np->desc_ver = DESC_VER_1;
	else
		np->desc_ver = DESC_VER_2;

	//rx_ring[0] = rx_ring;
	//tx_ring[0] = tx_ring; 

	/* read the mac address */
	base = (u8 *) BASE;
	np->orig_mac[0] = readl(base + NvRegMacAddrA);
	np->orig_mac[1] = readl(base + NvRegMacAddrB);

	nic->node_addr[0] = (np->orig_mac[1] >> 8) & 0xff;
	nic->node_addr[1] = (np->orig_mac[1] >> 0) & 0xff;
	nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
	nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
	nic->node_addr[4] = (np->orig_mac[0] >> 8) & 0xff;
	nic->node_addr[5] = (np->orig_mac[0] >> 0) & 0xff;
#ifdef LINUX
	if (!is_valid_ether_addr(dev->dev_addr)) {
		/*
		 * Bad mac address. At least one bios sets the mac address
		 * to 01:23:45:67:89:ab
		 */
		printk(KERN_ERR
		       "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n",
		       pci_name(pci_dev), dev->dev_addr[0],
		       dev->dev_addr[1], dev->dev_addr[2],
		       dev->dev_addr[3], dev->dev_addr[4],
		       dev->dev_addr[5]);
		printk(KERN_ERR
		       "Please complain to your hardware vendor. Switching to a random MAC.\n");
		dev->dev_addr[0] = 0x00;
		dev->dev_addr[1] = 0x00;
		dev->dev_addr[2] = 0x6c;
		get_random_bytes(&dev->dev_addr[3], 3);
	}
#endif

	DBG ( "%s: MAC Address %s\n", pci->driver_name, eth_ntoa ( nic->node_addr ) );

 	/* disable WOL */
	writel(0, base + NvRegWakeUpFlags);
 	np->wolenabled = 0;
	
 	if (np->desc_ver == DESC_VER_1) {
 		np->tx_flags = NV_TX_LASTPACKET | NV_TX_VALID;
 	} else {
 		np->tx_flags = NV_TX2_LASTPACKET | NV_TX2_VALID;
 	}

  	switch (pci->device) {
  	case 0x01C3:		// nforce
 		// DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
 		np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER;
		//              np->need_linktimer = 1;
		//              np->link_timeout = jiffies + LINK_TIMEOUT;
  		break;
 	case 0x0066:
 		/* Fall Through */
 	case 0x00D6:
 		// DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER
  		np->irqmask = NVREG_IRQMASK_WANTED_2;
  		np->irqmask |= NVREG_IRQ_TIMER;
		//              np->need_linktimer = 1;
		//              np->link_timeout = jiffies + LINK_TIMEOUT;
 		if (np->desc_ver == DESC_VER_1)
 			np->tx_flags |= NV_TX_LASTPACKET1;
 		else
 			np->tx_flags |= NV_TX2_LASTPACKET1;
  		break;
	case 0x0373:
		/* Fall Through */
 	case 0x0086:
 		/* Fall Through */
 	case 0x008c:
 		/* Fall Through */
 	case 0x00e6:
 		/* Fall Through */
 	case 0x00df:
		/* Fall Through */
 	case 0x0056:
 		/* Fall Through */
 	case 0x0057:
 		/* Fall Through */
 	case 0x0037:
 		/* Fall Through */
 	case 0x0038:
 		//DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
  		np->irqmask = NVREG_IRQMASK_WANTED_2;
  		np->irqmask |= NVREG_IRQ_TIMER;
		//              np->need_linktimer = 1;
		//              np->link_timeout = jiffies + LINK_TIMEOUT;
 		if (np->desc_ver == DESC_VER_1)
 			np->tx_flags |= NV_TX_LASTPACKET1;
 		else
 			np->tx_flags |= NV_TX2_LASTPACKET1;
 		break;
 	default:
 		printf
			("Your card was undefined in this driver.  Review driver_data in Linux driver and send a patch\n");
 	}
	
 	/* find a suitable phy */
 	for (i = 1; i < 32; i++) {
 		int id1, id2;
 		id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
 		if (id1 < 0 || id1 == 0xffff)
 			continue;
 		id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
 		if (id2 < 0 || id2 == 0xffff)
 			continue;
 		id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
 		id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
 		dprintf
			(("%s: open: Found PHY %hX:%hX at address %d.\n",
			  pci->driver_name, id1, id2, i));
 		np->phyaddr = i;
 		np->phy_oui = id1 | id2;
 		break;
 	}
 	if (i == 32) {
 		/* PHY in isolate mode? No phy attached and user wants to
 		 * test loopback? Very odd, but can be correct.
 		 */
 		printf
			("%s: open: Could not find a valid PHY.\n", pci->driver_name);
 	}
	
 	if (i != 32) {
 		/* reset it */
 		phy_init(nic);
 	}
	
	dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
		 pci->driver_name, pci->vendor, pci->dev_id, pci->driver_name));
	if(!forcedeth_reset(nic)) return 0; // no valid link

	/* point to NIC specific routines */
	nic->nic_op	= &forcedeth_operations;
	return 1;
}

static struct pci_device_id forcedeth_nics[] = {
PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller"),
PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller"),
PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller"),
PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller"),
PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller"),
PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller"),
PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller"),
PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"),
PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"),
PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"),
PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"),
PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller")
};

PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );

DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver,
	 forcedeth_probe, forcedeth_disable );

/*
 * Local variables:
 *  c-basic-offset: 8
 *  c-indent-level: 8
 *  tab-width: 8
 * End:
 */