1*1b8adde7SWilliam Kucharski /*
2*1b8adde7SWilliam Kucharski * 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
3*1b8adde7SWilliam Kucharski * by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
4*1b8adde7SWilliam Kucharski * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
5*1b8adde7SWilliam Kucharski *
6*1b8adde7SWilliam Kucharski * This program Copyright (C) 1999 LightSys Technology Services, Inc.
7*1b8adde7SWilliam Kucharski * Portions Copyright (C) 1999 Steve Smith
8*1b8adde7SWilliam Kucharski *
9*1b8adde7SWilliam Kucharski * This program may be re-distributed in source or binary form, modified,
10*1b8adde7SWilliam Kucharski * sold, or copied for any purpose, provided that the above copyright message
11*1b8adde7SWilliam Kucharski * and this text are included with all source copies or derivative works, and
12*1b8adde7SWilliam Kucharski * provided that the above copyright message and this text are included in the
13*1b8adde7SWilliam Kucharski * documentation of any binary-only distributions. This program is distributed
14*1b8adde7SWilliam Kucharski * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
15*1b8adde7SWilliam Kucharski * PURPOSE or MERCHANTABILITY. Please read the associated documentation
16*1b8adde7SWilliam Kucharski * "3c90x.txt" before compiling and using this driver.
17*1b8adde7SWilliam Kucharski *
18*1b8adde7SWilliam Kucharski * --------
19*1b8adde7SWilliam Kucharski *
20*1b8adde7SWilliam Kucharski * Program written with the assistance of the 3com documentation for
21*1b8adde7SWilliam Kucharski * the 3c905B-TX card, as well as with some assistance from the 3c59x
22*1b8adde7SWilliam Kucharski * driver Donald Becker wrote for the Linux kernel, and with some assistance
23*1b8adde7SWilliam Kucharski * from the remainder of the Etherboot distribution.
24*1b8adde7SWilliam Kucharski *
25*1b8adde7SWilliam Kucharski * REVISION HISTORY:
26*1b8adde7SWilliam Kucharski *
27*1b8adde7SWilliam Kucharski * v0.10 1-26-1998 GRB Initial implementation.
28*1b8adde7SWilliam Kucharski * v0.90 1-27-1998 GRB System works.
29*1b8adde7SWilliam Kucharski * v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
30*1b8adde7SWilliam Kucharski * v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
31*1b8adde7SWilliam Kucharski * Re-wrote poll and transmit for
32*1b8adde7SWilliam Kucharski * better error recovery and heavy
33*1b8adde7SWilliam Kucharski * network traffic operation
34*1b8adde7SWilliam Kucharski * v2.01 5-26-2003 NN Fixed driver alignment issue which
35*1b8adde7SWilliam Kucharski * caused system lockups if driver structures
36*1b8adde7SWilliam Kucharski * not 8-byte aligned.
37*1b8adde7SWilliam Kucharski *
38*1b8adde7SWilliam Kucharski */
39*1b8adde7SWilliam Kucharski
40*1b8adde7SWilliam Kucharski #include "etherboot.h"
41*1b8adde7SWilliam Kucharski #include "nic.h"
42*1b8adde7SWilliam Kucharski #include "pci.h"
43*1b8adde7SWilliam Kucharski #include "timer.h"
44*1b8adde7SWilliam Kucharski
45*1b8adde7SWilliam Kucharski #define XCVR_MAGIC (0x5A00)
46*1b8adde7SWilliam Kucharski /** any single transmission fails after 16 collisions or other errors
47*1b8adde7SWilliam Kucharski ** this is the number of times to retry the transmission -- this should
48*1b8adde7SWilliam Kucharski ** be plenty
49*1b8adde7SWilliam Kucharski **/
50*1b8adde7SWilliam Kucharski #define XMIT_RETRIES 250
51*1b8adde7SWilliam Kucharski
52*1b8adde7SWilliam Kucharski /*** Register definitions for the 3c905 ***/
53*1b8adde7SWilliam Kucharski enum Registers
54*1b8adde7SWilliam Kucharski {
55*1b8adde7SWilliam Kucharski regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/
56*1b8adde7SWilliam Kucharski regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/
57*1b8adde7SWilliam Kucharski regDnMaxBurst_w = 0x78, /** 905B Revision Only **/
58*1b8adde7SWilliam Kucharski regDebugControl_w = 0x74, /** 905B Revision Only **/
59*1b8adde7SWilliam Kucharski regDebugData_l = 0x70, /** 905B Revision Only **/
60*1b8adde7SWilliam Kucharski regRealTimeCnt_l = 0x40, /** Universal **/
61*1b8adde7SWilliam Kucharski regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/
62*1b8adde7SWilliam Kucharski regUpPoll_b = 0x3d, /** 905B Revision Only **/
63*1b8adde7SWilliam Kucharski regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/
64*1b8adde7SWilliam Kucharski regUpListPtr_l = 0x38, /** Universal **/
65*1b8adde7SWilliam Kucharski regCountdown_w = 0x36, /** Universal **/
66*1b8adde7SWilliam Kucharski regFreeTimer_w = 0x34, /** Universal **/
67*1b8adde7SWilliam Kucharski regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/
68*1b8adde7SWilliam Kucharski regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/
69*1b8adde7SWilliam Kucharski regDnPoll_b = 0x2d, /** 905B Revision Only **/
70*1b8adde7SWilliam Kucharski regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/
71*1b8adde7SWilliam Kucharski regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/
72*1b8adde7SWilliam Kucharski regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/
73*1b8adde7SWilliam Kucharski regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/
74*1b8adde7SWilliam Kucharski /** **/
75*1b8adde7SWilliam Kucharski regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/
76*1b8adde7SWilliam Kucharski regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/
77*1b8adde7SWilliam Kucharski regTimer_b = 0x1a, /** Universal **/
78*1b8adde7SWilliam Kucharski regTxPktId_b = 0x18, /** 905B Revision Only **/
79*1b8adde7SWilliam Kucharski regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/
80*1b8adde7SWilliam Kucharski };
81*1b8adde7SWilliam Kucharski
82*1b8adde7SWilliam Kucharski /** following are windowed registers **/
83*1b8adde7SWilliam Kucharski enum Registers7
84*1b8adde7SWilliam Kucharski {
85*1b8adde7SWilliam Kucharski regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/
86*1b8adde7SWilliam Kucharski regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/
87*1b8adde7SWilliam Kucharski regVlanMask_7_w = 0x00, /** 905B Revision Only **/
88*1b8adde7SWilliam Kucharski };
89*1b8adde7SWilliam Kucharski
90*1b8adde7SWilliam Kucharski enum Registers6
91*1b8adde7SWilliam Kucharski {
92*1b8adde7SWilliam Kucharski regBytesXmittedOk_6_w = 0x0c, /** Universal **/
93*1b8adde7SWilliam Kucharski regBytesRcvdOk_6_w = 0x0a, /** Universal **/
94*1b8adde7SWilliam Kucharski regUpperFramesOk_6_b = 0x09, /** Universal **/
95*1b8adde7SWilliam Kucharski regFramesDeferred_6_b = 0x08, /** Universal **/
96*1b8adde7SWilliam Kucharski regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/
97*1b8adde7SWilliam Kucharski regFramesXmittedOk_6_b = 0x06, /** Universal **/
98*1b8adde7SWilliam Kucharski regRxOverruns_6_b = 0x05, /** Universal **/
99*1b8adde7SWilliam Kucharski regLateCollisions_6_b = 0x04, /** Universal **/
100*1b8adde7SWilliam Kucharski regSingleCollisions_6_b = 0x03, /** Universal **/
101*1b8adde7SWilliam Kucharski regMultipleCollisions_6_b = 0x02, /** Universal **/
102*1b8adde7SWilliam Kucharski regSqeErrors_6_b = 0x01, /** Universal **/
103*1b8adde7SWilliam Kucharski regCarrierLost_6_b = 0x00, /** Universal **/
104*1b8adde7SWilliam Kucharski };
105*1b8adde7SWilliam Kucharski
106*1b8adde7SWilliam Kucharski enum Registers5
107*1b8adde7SWilliam Kucharski {
108*1b8adde7SWilliam Kucharski regIndicationEnable_5_w = 0x0c, /** Universal **/
109*1b8adde7SWilliam Kucharski regInterruptEnable_5_w = 0x0a, /** Universal **/
110*1b8adde7SWilliam Kucharski regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/
111*1b8adde7SWilliam Kucharski regRxFilter_5_b = 0x08, /** Universal **/
112*1b8adde7SWilliam Kucharski regRxEarlyThresh_5_w = 0x06, /** Universal **/
113*1b8adde7SWilliam Kucharski regTxStartThresh_5_w = 0x00, /** Universal **/
114*1b8adde7SWilliam Kucharski };
115*1b8adde7SWilliam Kucharski
116*1b8adde7SWilliam Kucharski enum Registers4
117*1b8adde7SWilliam Kucharski {
118*1b8adde7SWilliam Kucharski regUpperBytesOk_4_b = 0x0d, /** Universal **/
119*1b8adde7SWilliam Kucharski regBadSSD_4_b = 0x0c, /** Universal **/
120*1b8adde7SWilliam Kucharski regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/
121*1b8adde7SWilliam Kucharski regPhysicalMgmt_4_w = 0x08, /** Universal **/
122*1b8adde7SWilliam Kucharski regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/
123*1b8adde7SWilliam Kucharski regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/
124*1b8adde7SWilliam Kucharski regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/
125*1b8adde7SWilliam Kucharski };
126*1b8adde7SWilliam Kucharski
127*1b8adde7SWilliam Kucharski enum Registers3
128*1b8adde7SWilliam Kucharski {
129*1b8adde7SWilliam Kucharski regTxFree_3_w = 0x0c, /** Universal **/
130*1b8adde7SWilliam Kucharski regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/
131*1b8adde7SWilliam Kucharski regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/
132*1b8adde7SWilliam Kucharski /** Reset Options on Non-B Revision **/
133*1b8adde7SWilliam Kucharski regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/
134*1b8adde7SWilliam Kucharski regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/
135*1b8adde7SWilliam Kucharski regInternalConfig_3_l = 0x00, /** Universal, different bit **/
136*1b8adde7SWilliam Kucharski /** definitions, pg 59 **/
137*1b8adde7SWilliam Kucharski };
138*1b8adde7SWilliam Kucharski
139*1b8adde7SWilliam Kucharski enum Registers2
140*1b8adde7SWilliam Kucharski {
141*1b8adde7SWilliam Kucharski regResetOptions_2_w = 0x0c, /** 905B Revision Only **/
142*1b8adde7SWilliam Kucharski regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/
143*1b8adde7SWilliam Kucharski regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/
144*1b8adde7SWilliam Kucharski };
145*1b8adde7SWilliam Kucharski
146*1b8adde7SWilliam Kucharski enum Registers1
147*1b8adde7SWilliam Kucharski {
148*1b8adde7SWilliam Kucharski regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/
149*1b8adde7SWilliam Kucharski };
150*1b8adde7SWilliam Kucharski
151*1b8adde7SWilliam Kucharski enum Registers0
152*1b8adde7SWilliam Kucharski {
153*1b8adde7SWilliam Kucharski regEepromData_0_w = 0x0c, /** Universal **/
154*1b8adde7SWilliam Kucharski regEepromCommand_0_w = 0x0a, /** Universal **/
155*1b8adde7SWilliam Kucharski regBiosRomData_0_b = 0x08, /** 905B Revision Only **/
156*1b8adde7SWilliam Kucharski regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/
157*1b8adde7SWilliam Kucharski };
158*1b8adde7SWilliam Kucharski
159*1b8adde7SWilliam Kucharski
160*1b8adde7SWilliam Kucharski /*** The names for the eight register windows ***/
161*1b8adde7SWilliam Kucharski enum Windows
162*1b8adde7SWilliam Kucharski {
163*1b8adde7SWilliam Kucharski winPowerVlan7 = 0x07,
164*1b8adde7SWilliam Kucharski winStatistics6 = 0x06,
165*1b8adde7SWilliam Kucharski winTxRxControl5 = 0x05,
166*1b8adde7SWilliam Kucharski winDiagnostics4 = 0x04,
167*1b8adde7SWilliam Kucharski winTxRxOptions3 = 0x03,
168*1b8adde7SWilliam Kucharski winAddressing2 = 0x02,
169*1b8adde7SWilliam Kucharski winUnused1 = 0x01,
170*1b8adde7SWilliam Kucharski winEepromBios0 = 0x00,
171*1b8adde7SWilliam Kucharski };
172*1b8adde7SWilliam Kucharski
173*1b8adde7SWilliam Kucharski
174*1b8adde7SWilliam Kucharski /*** Command definitions for the 3c90X ***/
175*1b8adde7SWilliam Kucharski enum Commands
176*1b8adde7SWilliam Kucharski {
177*1b8adde7SWilliam Kucharski cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/
178*1b8adde7SWilliam Kucharski cmdSelectRegisterWindow = 0x01, /** Universal **/
179*1b8adde7SWilliam Kucharski cmdEnableDcConverter = 0x02, /** **/
180*1b8adde7SWilliam Kucharski cmdRxDisable = 0x03, /** **/
181*1b8adde7SWilliam Kucharski cmdRxEnable = 0x04, /** Universal **/
182*1b8adde7SWilliam Kucharski cmdRxReset = 0x05, /** Universal **/
183*1b8adde7SWilliam Kucharski cmdStallCtl = 0x06, /** Universal **/
184*1b8adde7SWilliam Kucharski cmdTxEnable = 0x09, /** Universal **/
185*1b8adde7SWilliam Kucharski cmdTxDisable = 0x0A, /** **/
186*1b8adde7SWilliam Kucharski cmdTxReset = 0x0B, /** Universal **/
187*1b8adde7SWilliam Kucharski cmdRequestInterrupt = 0x0C, /** **/
188*1b8adde7SWilliam Kucharski cmdAcknowledgeInterrupt = 0x0D, /** Universal **/
189*1b8adde7SWilliam Kucharski cmdSetInterruptEnable = 0x0E, /** Universal **/
190*1b8adde7SWilliam Kucharski cmdSetIndicationEnable = 0x0F, /** Universal **/
191*1b8adde7SWilliam Kucharski cmdSetRxFilter = 0x10, /** Universal **/
192*1b8adde7SWilliam Kucharski cmdSetRxEarlyThresh = 0x11, /** **/
193*1b8adde7SWilliam Kucharski cmdSetTxStartThresh = 0x13, /** **/
194*1b8adde7SWilliam Kucharski cmdStatisticsEnable = 0x15, /** **/
195*1b8adde7SWilliam Kucharski cmdStatisticsDisable = 0x16, /** **/
196*1b8adde7SWilliam Kucharski cmdDisableDcConverter = 0x17, /** **/
197*1b8adde7SWilliam Kucharski cmdSetTxReclaimThresh = 0x18, /** **/
198*1b8adde7SWilliam Kucharski cmdSetHashFilterBit = 0x19, /** **/
199*1b8adde7SWilliam Kucharski };
200*1b8adde7SWilliam Kucharski
201*1b8adde7SWilliam Kucharski
202*1b8adde7SWilliam Kucharski /*** Values for int status register bitmask **/
203*1b8adde7SWilliam Kucharski #define INT_INTERRUPTLATCH (1<<0)
204*1b8adde7SWilliam Kucharski #define INT_HOSTERROR (1<<1)
205*1b8adde7SWilliam Kucharski #define INT_TXCOMPLETE (1<<2)
206*1b8adde7SWilliam Kucharski #define INT_RXCOMPLETE (1<<4)
207*1b8adde7SWilliam Kucharski #define INT_RXEARLY (1<<5)
208*1b8adde7SWilliam Kucharski #define INT_INTREQUESTED (1<<6)
209*1b8adde7SWilliam Kucharski #define INT_UPDATESTATS (1<<7)
210*1b8adde7SWilliam Kucharski #define INT_LINKEVENT (1<<8)
211*1b8adde7SWilliam Kucharski #define INT_DNCOMPLETE (1<<9)
212*1b8adde7SWilliam Kucharski #define INT_UPCOMPLETE (1<<10)
213*1b8adde7SWilliam Kucharski #define INT_CMDINPROGRESS (1<<12)
214*1b8adde7SWilliam Kucharski #define INT_WINDOWNUMBER (7<<13)
215*1b8adde7SWilliam Kucharski
216*1b8adde7SWilliam Kucharski
217*1b8adde7SWilliam Kucharski /*** TX descriptor ***/
218*1b8adde7SWilliam Kucharski typedef struct
219*1b8adde7SWilliam Kucharski {
220*1b8adde7SWilliam Kucharski unsigned int DnNextPtr;
221*1b8adde7SWilliam Kucharski unsigned int FrameStartHeader;
222*1b8adde7SWilliam Kucharski unsigned int HdrAddr;
223*1b8adde7SWilliam Kucharski unsigned int HdrLength;
224*1b8adde7SWilliam Kucharski unsigned int DataAddr;
225*1b8adde7SWilliam Kucharski unsigned int DataLength;
226*1b8adde7SWilliam Kucharski }
227*1b8adde7SWilliam Kucharski TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
228*1b8adde7SWilliam Kucharski
229*1b8adde7SWilliam Kucharski /*** RX descriptor ***/
230*1b8adde7SWilliam Kucharski typedef struct
231*1b8adde7SWilliam Kucharski {
232*1b8adde7SWilliam Kucharski unsigned int UpNextPtr;
233*1b8adde7SWilliam Kucharski unsigned int UpPktStatus;
234*1b8adde7SWilliam Kucharski unsigned int DataAddr;
235*1b8adde7SWilliam Kucharski unsigned int DataLength;
236*1b8adde7SWilliam Kucharski }
237*1b8adde7SWilliam Kucharski RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
238*1b8adde7SWilliam Kucharski
239*1b8adde7SWilliam Kucharski /*** Global variables ***/
240*1b8adde7SWilliam Kucharski static struct
241*1b8adde7SWilliam Kucharski {
242*1b8adde7SWilliam Kucharski unsigned char isBrev;
243*1b8adde7SWilliam Kucharski unsigned char CurrentWindow;
244*1b8adde7SWilliam Kucharski unsigned int IOAddr;
245*1b8adde7SWilliam Kucharski unsigned char HWAddr[ETH_ALEN];
246*1b8adde7SWilliam Kucharski TXD TransmitDPD;
247*1b8adde7SWilliam Kucharski RXD ReceiveUPD;
248*1b8adde7SWilliam Kucharski }
249*1b8adde7SWilliam Kucharski INF_3C90X;
250*1b8adde7SWilliam Kucharski
251*1b8adde7SWilliam Kucharski
252*1b8adde7SWilliam Kucharski /*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
253*1b8adde7SWilliam Kucharski ***/
254*1b8adde7SWilliam Kucharski static int
a3c90x_internal_IssueCommand(int ioaddr,int cmd,int param)255*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
256*1b8adde7SWilliam Kucharski {
257*1b8adde7SWilliam Kucharski unsigned int val;
258*1b8adde7SWilliam Kucharski
259*1b8adde7SWilliam Kucharski /** Build the cmd. **/
260*1b8adde7SWilliam Kucharski val = cmd;
261*1b8adde7SWilliam Kucharski val <<= 11;
262*1b8adde7SWilliam Kucharski val |= param;
263*1b8adde7SWilliam Kucharski
264*1b8adde7SWilliam Kucharski /** Send the cmd to the cmd register **/
265*1b8adde7SWilliam Kucharski outw(val, ioaddr + regCommandIntStatus_w);
266*1b8adde7SWilliam Kucharski
267*1b8adde7SWilliam Kucharski /** Wait for the cmd to complete, if necessary **/
268*1b8adde7SWilliam Kucharski while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
269*1b8adde7SWilliam Kucharski
270*1b8adde7SWilliam Kucharski return 0;
271*1b8adde7SWilliam Kucharski }
272*1b8adde7SWilliam Kucharski
273*1b8adde7SWilliam Kucharski
274*1b8adde7SWilliam Kucharski /*** a3c90x_internal_SetWindow: selects a register window set.
275*1b8adde7SWilliam Kucharski ***/
276*1b8adde7SWilliam Kucharski static int
a3c90x_internal_SetWindow(int ioaddr,int window)277*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(int ioaddr, int window)
278*1b8adde7SWilliam Kucharski {
279*1b8adde7SWilliam Kucharski
280*1b8adde7SWilliam Kucharski /** Window already as set? **/
281*1b8adde7SWilliam Kucharski if (INF_3C90X.CurrentWindow == window) return 0;
282*1b8adde7SWilliam Kucharski
283*1b8adde7SWilliam Kucharski /** Issue the window command. **/
284*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
285*1b8adde7SWilliam Kucharski INF_3C90X.CurrentWindow = window;
286*1b8adde7SWilliam Kucharski
287*1b8adde7SWilliam Kucharski return 0;
288*1b8adde7SWilliam Kucharski }
289*1b8adde7SWilliam Kucharski
290*1b8adde7SWilliam Kucharski
291*1b8adde7SWilliam Kucharski /*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
292*1b8adde7SWilliam Kucharski ***/
293*1b8adde7SWilliam Kucharski static unsigned short
a3c90x_internal_ReadEeprom(int ioaddr,int address)294*1b8adde7SWilliam Kucharski a3c90x_internal_ReadEeprom(int ioaddr, int address)
295*1b8adde7SWilliam Kucharski {
296*1b8adde7SWilliam Kucharski unsigned short val;
297*1b8adde7SWilliam Kucharski
298*1b8adde7SWilliam Kucharski /** Select correct window **/
299*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
300*1b8adde7SWilliam Kucharski
301*1b8adde7SWilliam Kucharski /** Make sure the eeprom isn't busy **/
302*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
303*1b8adde7SWilliam Kucharski
304*1b8adde7SWilliam Kucharski /** Read the value. **/
305*1b8adde7SWilliam Kucharski outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
306*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
307*1b8adde7SWilliam Kucharski val = inw(ioaddr + regEepromData_0_w);
308*1b8adde7SWilliam Kucharski
309*1b8adde7SWilliam Kucharski return val;
310*1b8adde7SWilliam Kucharski }
311*1b8adde7SWilliam Kucharski
312*1b8adde7SWilliam Kucharski
313*1b8adde7SWilliam Kucharski #if 0
314*1b8adde7SWilliam Kucharski /*** a3c90x_internal_WriteEepromWord - write a physical word of
315*1b8adde7SWilliam Kucharski *** data to the onboard serial eeprom (not the BIOS prom, but the
316*1b8adde7SWilliam Kucharski *** nvram in the card that stores, among other things, the MAC
317*1b8adde7SWilliam Kucharski *** address).
318*1b8adde7SWilliam Kucharski ***/
319*1b8adde7SWilliam Kucharski static int
320*1b8adde7SWilliam Kucharski a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value)
321*1b8adde7SWilliam Kucharski {
322*1b8adde7SWilliam Kucharski /** Select register window **/
323*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(ioaddr, winEepromBios0);
324*1b8adde7SWilliam Kucharski
325*1b8adde7SWilliam Kucharski /** Verify Eeprom not busy **/
326*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
327*1b8adde7SWilliam Kucharski
328*1b8adde7SWilliam Kucharski /** Issue WriteEnable, and wait for completion. **/
329*1b8adde7SWilliam Kucharski outw(0x30, ioaddr + regEepromCommand_0_w);
330*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
331*1b8adde7SWilliam Kucharski
332*1b8adde7SWilliam Kucharski /** Issue EraseRegister, and wait for completion. **/
333*1b8adde7SWilliam Kucharski outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w);
334*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
335*1b8adde7SWilliam Kucharski
336*1b8adde7SWilliam Kucharski /** Send the new data to the eeprom, and wait for completion. **/
337*1b8adde7SWilliam Kucharski outw(value, ioaddr + regEepromData_0_w);
338*1b8adde7SWilliam Kucharski outw(0x30, ioaddr + regEepromCommand_0_w);
339*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
340*1b8adde7SWilliam Kucharski
341*1b8adde7SWilliam Kucharski /** Burn the new data into the eeprom, and wait for completion. **/
342*1b8adde7SWilliam Kucharski outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w);
343*1b8adde7SWilliam Kucharski while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
344*1b8adde7SWilliam Kucharski
345*1b8adde7SWilliam Kucharski return 0;
346*1b8adde7SWilliam Kucharski }
347*1b8adde7SWilliam Kucharski #endif
348*1b8adde7SWilliam Kucharski
349*1b8adde7SWilliam Kucharski #if 0
350*1b8adde7SWilliam Kucharski /*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
351*1b8adde7SWilliam Kucharski *** and re-compute the eeprom checksum.
352*1b8adde7SWilliam Kucharski ***/
353*1b8adde7SWilliam Kucharski static int
354*1b8adde7SWilliam Kucharski a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
355*1b8adde7SWilliam Kucharski {
356*1b8adde7SWilliam Kucharski int cksum = 0,v;
357*1b8adde7SWilliam Kucharski int i;
358*1b8adde7SWilliam Kucharski int maxAddress, cksumAddress;
359*1b8adde7SWilliam Kucharski
360*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
361*1b8adde7SWilliam Kucharski {
362*1b8adde7SWilliam Kucharski maxAddress=0x1f;
363*1b8adde7SWilliam Kucharski cksumAddress=0x20;
364*1b8adde7SWilliam Kucharski }
365*1b8adde7SWilliam Kucharski else
366*1b8adde7SWilliam Kucharski {
367*1b8adde7SWilliam Kucharski maxAddress=0x16;
368*1b8adde7SWilliam Kucharski cksumAddress=0x17;
369*1b8adde7SWilliam Kucharski }
370*1b8adde7SWilliam Kucharski
371*1b8adde7SWilliam Kucharski /** Write the value. **/
372*1b8adde7SWilliam Kucharski if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
373*1b8adde7SWilliam Kucharski return -1;
374*1b8adde7SWilliam Kucharski
375*1b8adde7SWilliam Kucharski /** Recompute the checksum. **/
376*1b8adde7SWilliam Kucharski for(i=0;i<=maxAddress;i++)
377*1b8adde7SWilliam Kucharski {
378*1b8adde7SWilliam Kucharski v = a3c90x_internal_ReadEeprom(ioaddr, i);
379*1b8adde7SWilliam Kucharski cksum ^= (v & 0xFF);
380*1b8adde7SWilliam Kucharski cksum ^= ((v>>8) & 0xFF);
381*1b8adde7SWilliam Kucharski }
382*1b8adde7SWilliam Kucharski /** Write the checksum to the location in the eeprom **/
383*1b8adde7SWilliam Kucharski if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
384*1b8adde7SWilliam Kucharski return -1;
385*1b8adde7SWilliam Kucharski
386*1b8adde7SWilliam Kucharski return 0;
387*1b8adde7SWilliam Kucharski }
388*1b8adde7SWilliam Kucharski #endif
389*1b8adde7SWilliam Kucharski
390*1b8adde7SWilliam Kucharski /*** a3c90x_reset: exported function that resets the card to its default
391*1b8adde7SWilliam Kucharski *** state. This is so the Linux driver can re-set the card up the way
392*1b8adde7SWilliam Kucharski *** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
393*1b8adde7SWilliam Kucharski *** not alter the selected transceiver that we used to download the boot
394*1b8adde7SWilliam Kucharski *** image.
395*1b8adde7SWilliam Kucharski ***/
a3c90x_reset(void)396*1b8adde7SWilliam Kucharski static void a3c90x_reset(void)
397*1b8adde7SWilliam Kucharski {
398*1b8adde7SWilliam Kucharski #ifdef CFG_3C90X_PRESERVE_XCVR
399*1b8adde7SWilliam Kucharski int cfg;
400*1b8adde7SWilliam Kucharski /** Read the current InternalConfig value. **/
401*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
402*1b8adde7SWilliam Kucharski cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
403*1b8adde7SWilliam Kucharski #endif
404*1b8adde7SWilliam Kucharski
405*1b8adde7SWilliam Kucharski /** Send the reset command to the card **/
406*1b8adde7SWilliam Kucharski printf("Issuing RESET:\n");
407*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
408*1b8adde7SWilliam Kucharski
409*1b8adde7SWilliam Kucharski /** wait for reset command to complete **/
410*1b8adde7SWilliam Kucharski while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
411*1b8adde7SWilliam Kucharski
412*1b8adde7SWilliam Kucharski /** global reset command resets station mask, non-B revision cards
413*1b8adde7SWilliam Kucharski ** require explicit reset of values
414*1b8adde7SWilliam Kucharski **/
415*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
416*1b8adde7SWilliam Kucharski outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
417*1b8adde7SWilliam Kucharski outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
418*1b8adde7SWilliam Kucharski outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
419*1b8adde7SWilliam Kucharski
420*1b8adde7SWilliam Kucharski #ifdef CFG_3C90X_PRESERVE_XCVR
421*1b8adde7SWilliam Kucharski /** Re-set the original InternalConfig value from before reset **/
422*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
423*1b8adde7SWilliam Kucharski outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
424*1b8adde7SWilliam Kucharski
425*1b8adde7SWilliam Kucharski /** enable DC converter for 10-Base-T **/
426*1b8adde7SWilliam Kucharski if ((cfg&0x0300) == 0x0300)
427*1b8adde7SWilliam Kucharski {
428*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
429*1b8adde7SWilliam Kucharski }
430*1b8adde7SWilliam Kucharski #endif
431*1b8adde7SWilliam Kucharski
432*1b8adde7SWilliam Kucharski /** Issue transmit reset, wait for command completion **/
433*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
434*1b8adde7SWilliam Kucharski while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
435*1b8adde7SWilliam Kucharski ;
436*1b8adde7SWilliam Kucharski if (! INF_3C90X.isBrev)
437*1b8adde7SWilliam Kucharski outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
438*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
439*1b8adde7SWilliam Kucharski
440*1b8adde7SWilliam Kucharski /**
441*1b8adde7SWilliam Kucharski ** reset of the receiver on B-revision cards re-negotiates the link
442*1b8adde7SWilliam Kucharski ** takes several seconds (a computer eternity)
443*1b8adde7SWilliam Kucharski **/
444*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
445*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
446*1b8adde7SWilliam Kucharski else
447*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
448*1b8adde7SWilliam Kucharski while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
449*1b8adde7SWilliam Kucharski ;
450*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
451*1b8adde7SWilliam Kucharski
452*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
453*1b8adde7SWilliam Kucharski cmdSetInterruptEnable, 0);
454*1b8adde7SWilliam Kucharski /** enable rxComplete and txComplete **/
455*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
456*1b8adde7SWilliam Kucharski cmdSetIndicationEnable, 0x0014);
457*1b8adde7SWilliam Kucharski /** acknowledge any pending status flags **/
458*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
459*1b8adde7SWilliam Kucharski cmdAcknowledgeInterrupt, 0x661);
460*1b8adde7SWilliam Kucharski
461*1b8adde7SWilliam Kucharski return;
462*1b8adde7SWilliam Kucharski }
463*1b8adde7SWilliam Kucharski
464*1b8adde7SWilliam Kucharski
465*1b8adde7SWilliam Kucharski
466*1b8adde7SWilliam Kucharski /*** a3c90x_transmit: exported function that transmits a packet. Does not
467*1b8adde7SWilliam Kucharski *** return any particular status. Parameters are:
468*1b8adde7SWilliam Kucharski *** d[6] - destination address, ethernet;
469*1b8adde7SWilliam Kucharski *** t - protocol type (ARP, IP, etc);
470*1b8adde7SWilliam Kucharski *** s - size of the non-header part of the packet that needs transmitted;
471*1b8adde7SWilliam Kucharski *** p - the pointer to the packet data itself.
472*1b8adde7SWilliam Kucharski ***/
473*1b8adde7SWilliam Kucharski static void
a3c90x_transmit(struct nic * nic __unused,const char * d,unsigned int t,unsigned int s,const char * p)474*1b8adde7SWilliam Kucharski a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t,
475*1b8adde7SWilliam Kucharski unsigned int s, const char *p)
476*1b8adde7SWilliam Kucharski {
477*1b8adde7SWilliam Kucharski
478*1b8adde7SWilliam Kucharski struct eth_hdr
479*1b8adde7SWilliam Kucharski {
480*1b8adde7SWilliam Kucharski unsigned char dst_addr[ETH_ALEN];
481*1b8adde7SWilliam Kucharski unsigned char src_addr[ETH_ALEN];
482*1b8adde7SWilliam Kucharski unsigned short type;
483*1b8adde7SWilliam Kucharski } hdr;
484*1b8adde7SWilliam Kucharski
485*1b8adde7SWilliam Kucharski unsigned char status;
486*1b8adde7SWilliam Kucharski unsigned i, retries;
487*1b8adde7SWilliam Kucharski
488*1b8adde7SWilliam Kucharski for (retries=0; retries < XMIT_RETRIES ; retries++)
489*1b8adde7SWilliam Kucharski {
490*1b8adde7SWilliam Kucharski /** Stall the download engine **/
491*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
492*1b8adde7SWilliam Kucharski
493*1b8adde7SWilliam Kucharski /** Make sure the card is not waiting on us **/
494*1b8adde7SWilliam Kucharski inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
495*1b8adde7SWilliam Kucharski inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
496*1b8adde7SWilliam Kucharski
497*1b8adde7SWilliam Kucharski while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
498*1b8adde7SWilliam Kucharski INT_CMDINPROGRESS)
499*1b8adde7SWilliam Kucharski ;
500*1b8adde7SWilliam Kucharski
501*1b8adde7SWilliam Kucharski /** Set the ethernet packet type **/
502*1b8adde7SWilliam Kucharski hdr.type = htons(t);
503*1b8adde7SWilliam Kucharski
504*1b8adde7SWilliam Kucharski /** Copy the destination address **/
505*1b8adde7SWilliam Kucharski memcpy(hdr.dst_addr, d, ETH_ALEN);
506*1b8adde7SWilliam Kucharski
507*1b8adde7SWilliam Kucharski /** Copy our MAC address **/
508*1b8adde7SWilliam Kucharski memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN);
509*1b8adde7SWilliam Kucharski
510*1b8adde7SWilliam Kucharski /** Setup the DPD (download descriptor) **/
511*1b8adde7SWilliam Kucharski INF_3C90X.TransmitDPD.DnNextPtr = 0;
512*1b8adde7SWilliam Kucharski /** set notification for transmission completion (bit 15) **/
513*1b8adde7SWilliam Kucharski INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
514*1b8adde7SWilliam Kucharski INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
515*1b8adde7SWilliam Kucharski INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
516*1b8adde7SWilliam Kucharski INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
517*1b8adde7SWilliam Kucharski INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
518*1b8adde7SWilliam Kucharski
519*1b8adde7SWilliam Kucharski /** Send the packet **/
520*1b8adde7SWilliam Kucharski outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
521*1b8adde7SWilliam Kucharski INF_3C90X.IOAddr + regDnListPtr_l);
522*1b8adde7SWilliam Kucharski
523*1b8adde7SWilliam Kucharski /** End Stall and Wait for upload to complete. **/
524*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
525*1b8adde7SWilliam Kucharski while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
526*1b8adde7SWilliam Kucharski ;
527*1b8adde7SWilliam Kucharski
528*1b8adde7SWilliam Kucharski /** Wait for NIC Transmit to Complete **/
529*1b8adde7SWilliam Kucharski load_timer2(10*TICKS_PER_MS); /* Give it 10 ms */
530*1b8adde7SWilliam Kucharski while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
531*1b8adde7SWilliam Kucharski timer2_running())
532*1b8adde7SWilliam Kucharski ;
533*1b8adde7SWilliam Kucharski
534*1b8adde7SWilliam Kucharski if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004))
535*1b8adde7SWilliam Kucharski {
536*1b8adde7SWilliam Kucharski printf("3C90X: Tx Timeout\n");
537*1b8adde7SWilliam Kucharski continue;
538*1b8adde7SWilliam Kucharski }
539*1b8adde7SWilliam Kucharski
540*1b8adde7SWilliam Kucharski status = inb(INF_3C90X.IOAddr + regTxStatus_b);
541*1b8adde7SWilliam Kucharski
542*1b8adde7SWilliam Kucharski /** acknowledge transmit interrupt by writing status **/
543*1b8adde7SWilliam Kucharski outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
544*1b8adde7SWilliam Kucharski
545*1b8adde7SWilliam Kucharski /** successful completion (sans "interrupt Requested" bit) **/
546*1b8adde7SWilliam Kucharski if ((status & 0xbf) == 0x80)
547*1b8adde7SWilliam Kucharski return;
548*1b8adde7SWilliam Kucharski
549*1b8adde7SWilliam Kucharski printf("3C90X: Status (%hhX)\n", status);
550*1b8adde7SWilliam Kucharski /** check error codes **/
551*1b8adde7SWilliam Kucharski if (status & 0x02)
552*1b8adde7SWilliam Kucharski {
553*1b8adde7SWilliam Kucharski printf("3C90X: Tx Reclaim Error (%hhX)\n", status);
554*1b8adde7SWilliam Kucharski a3c90x_reset();
555*1b8adde7SWilliam Kucharski }
556*1b8adde7SWilliam Kucharski else if (status & 0x04)
557*1b8adde7SWilliam Kucharski {
558*1b8adde7SWilliam Kucharski printf("3C90X: Tx Status Overflow (%hhX)\n", status);
559*1b8adde7SWilliam Kucharski for (i=0; i<32; i++)
560*1b8adde7SWilliam Kucharski outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
561*1b8adde7SWilliam Kucharski /** must re-enable after max collisions before re-issuing tx **/
562*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
563*1b8adde7SWilliam Kucharski }
564*1b8adde7SWilliam Kucharski else if (status & 0x08)
565*1b8adde7SWilliam Kucharski {
566*1b8adde7SWilliam Kucharski printf("3C90X: Tx Max Collisions (%hhX)\n", status);
567*1b8adde7SWilliam Kucharski /** must re-enable after max collisions before re-issuing tx **/
568*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
569*1b8adde7SWilliam Kucharski }
570*1b8adde7SWilliam Kucharski else if (status & 0x10)
571*1b8adde7SWilliam Kucharski {
572*1b8adde7SWilliam Kucharski printf("3C90X: Tx Underrun (%hhX)\n", status);
573*1b8adde7SWilliam Kucharski a3c90x_reset();
574*1b8adde7SWilliam Kucharski }
575*1b8adde7SWilliam Kucharski else if (status & 0x20)
576*1b8adde7SWilliam Kucharski {
577*1b8adde7SWilliam Kucharski printf("3C90X: Tx Jabber (%hhX)\n", status);
578*1b8adde7SWilliam Kucharski a3c90x_reset();
579*1b8adde7SWilliam Kucharski }
580*1b8adde7SWilliam Kucharski else if ((status & 0x80) != 0x80)
581*1b8adde7SWilliam Kucharski {
582*1b8adde7SWilliam Kucharski printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
583*1b8adde7SWilliam Kucharski status);
584*1b8adde7SWilliam Kucharski a3c90x_reset();
585*1b8adde7SWilliam Kucharski }
586*1b8adde7SWilliam Kucharski }
587*1b8adde7SWilliam Kucharski
588*1b8adde7SWilliam Kucharski /** failed after RETRY attempts **/
589*1b8adde7SWilliam Kucharski printf("Failed to send after %d retries\n", retries);
590*1b8adde7SWilliam Kucharski return;
591*1b8adde7SWilliam Kucharski
592*1b8adde7SWilliam Kucharski }
593*1b8adde7SWilliam Kucharski
594*1b8adde7SWilliam Kucharski
595*1b8adde7SWilliam Kucharski
596*1b8adde7SWilliam Kucharski /*** a3c90x_poll: exported routine that waits for a certain length of time
597*1b8adde7SWilliam Kucharski *** for a packet, and if it sees none, returns 0. This routine should
598*1b8adde7SWilliam Kucharski *** copy the packet to nic->packet if it gets a packet and set the size
599*1b8adde7SWilliam Kucharski *** in nic->packetlen. Return 1 if a packet was found.
600*1b8adde7SWilliam Kucharski ***/
601*1b8adde7SWilliam Kucharski static int
a3c90x_poll(struct nic * nic,int retrieve)602*1b8adde7SWilliam Kucharski a3c90x_poll(struct nic *nic, int retrieve)
603*1b8adde7SWilliam Kucharski {
604*1b8adde7SWilliam Kucharski int i, errcode;
605*1b8adde7SWilliam Kucharski
606*1b8adde7SWilliam Kucharski if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
607*1b8adde7SWilliam Kucharski {
608*1b8adde7SWilliam Kucharski return 0;
609*1b8adde7SWilliam Kucharski }
610*1b8adde7SWilliam Kucharski
611*1b8adde7SWilliam Kucharski if ( ! retrieve ) return 1;
612*1b8adde7SWilliam Kucharski
613*1b8adde7SWilliam Kucharski /** we don't need to acknowledge rxComplete -- the upload engine
614*1b8adde7SWilliam Kucharski ** does it for us.
615*1b8adde7SWilliam Kucharski **/
616*1b8adde7SWilliam Kucharski
617*1b8adde7SWilliam Kucharski /** Build the up-load descriptor **/
618*1b8adde7SWilliam Kucharski INF_3C90X.ReceiveUPD.UpNextPtr = 0;
619*1b8adde7SWilliam Kucharski INF_3C90X.ReceiveUPD.UpPktStatus = 0;
620*1b8adde7SWilliam Kucharski INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
621*1b8adde7SWilliam Kucharski INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31);
622*1b8adde7SWilliam Kucharski
623*1b8adde7SWilliam Kucharski /** Submit the upload descriptor to the NIC **/
624*1b8adde7SWilliam Kucharski outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)),
625*1b8adde7SWilliam Kucharski INF_3C90X.IOAddr + regUpListPtr_l);
626*1b8adde7SWilliam Kucharski
627*1b8adde7SWilliam Kucharski /** Wait for upload completion (upComplete(15) or upError (14)) **/
628*1b8adde7SWilliam Kucharski for(i=0;i<40000;i++);
629*1b8adde7SWilliam Kucharski while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0)
630*1b8adde7SWilliam Kucharski for(i=0;i<40000;i++);
631*1b8adde7SWilliam Kucharski
632*1b8adde7SWilliam Kucharski /** Check for Error (else we have good packet) **/
633*1b8adde7SWilliam Kucharski if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14))
634*1b8adde7SWilliam Kucharski {
635*1b8adde7SWilliam Kucharski errcode = INF_3C90X.ReceiveUPD.UpPktStatus;
636*1b8adde7SWilliam Kucharski if (errcode & (1<<16))
637*1b8adde7SWilliam Kucharski printf("3C90X: Rx Overrun (%hX)\n",errcode>>16);
638*1b8adde7SWilliam Kucharski else if (errcode & (1<<17))
639*1b8adde7SWilliam Kucharski printf("3C90X: Runt Frame (%hX)\n",errcode>>16);
640*1b8adde7SWilliam Kucharski else if (errcode & (1<<18))
641*1b8adde7SWilliam Kucharski printf("3C90X: Alignment Error (%hX)\n",errcode>>16);
642*1b8adde7SWilliam Kucharski else if (errcode & (1<<19))
643*1b8adde7SWilliam Kucharski printf("3C90X: CRC Error (%hX)\n",errcode>>16);
644*1b8adde7SWilliam Kucharski else if (errcode & (1<<20))
645*1b8adde7SWilliam Kucharski printf("3C90X: Oversized Frame (%hX)\n",errcode>>16);
646*1b8adde7SWilliam Kucharski else
647*1b8adde7SWilliam Kucharski printf("3C90X: Packet error (%hX)\n",errcode>>16);
648*1b8adde7SWilliam Kucharski return 0;
649*1b8adde7SWilliam Kucharski }
650*1b8adde7SWilliam Kucharski
651*1b8adde7SWilliam Kucharski /** Ok, got packet. Set length in nic->packetlen. **/
652*1b8adde7SWilliam Kucharski nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF);
653*1b8adde7SWilliam Kucharski
654*1b8adde7SWilliam Kucharski return 1;
655*1b8adde7SWilliam Kucharski }
656*1b8adde7SWilliam Kucharski
657*1b8adde7SWilliam Kucharski
658*1b8adde7SWilliam Kucharski
659*1b8adde7SWilliam Kucharski /*** a3c90x_disable: exported routine to disable the card. What's this for?
660*1b8adde7SWilliam Kucharski *** the eepro100.c driver didn't have one, so I just left this one empty too.
661*1b8adde7SWilliam Kucharski *** Ideas anyone?
662*1b8adde7SWilliam Kucharski *** Must turn off receiver at least so stray packets will not corrupt memory
663*1b8adde7SWilliam Kucharski *** [Ken]
664*1b8adde7SWilliam Kucharski ***/
665*1b8adde7SWilliam Kucharski static void
a3c90x_disable(struct dev * dev __unused)666*1b8adde7SWilliam Kucharski a3c90x_disable(struct dev *dev __unused)
667*1b8adde7SWilliam Kucharski {
668*1b8adde7SWilliam Kucharski /* reset and disable merge */
669*1b8adde7SWilliam Kucharski a3c90x_reset();
670*1b8adde7SWilliam Kucharski /* Disable the receiver and transmitter. */
671*1b8adde7SWilliam Kucharski outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
672*1b8adde7SWilliam Kucharski outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
673*1b8adde7SWilliam Kucharski }
674*1b8adde7SWilliam Kucharski
a3c90x_irq(struct nic * nic __unused,irq_action_t action __unused)675*1b8adde7SWilliam Kucharski static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused)
676*1b8adde7SWilliam Kucharski {
677*1b8adde7SWilliam Kucharski switch ( action ) {
678*1b8adde7SWilliam Kucharski case DISABLE :
679*1b8adde7SWilliam Kucharski break;
680*1b8adde7SWilliam Kucharski case ENABLE :
681*1b8adde7SWilliam Kucharski break;
682*1b8adde7SWilliam Kucharski case FORCE :
683*1b8adde7SWilliam Kucharski break;
684*1b8adde7SWilliam Kucharski }
685*1b8adde7SWilliam Kucharski }
686*1b8adde7SWilliam Kucharski
687*1b8adde7SWilliam Kucharski /*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
688*1b8adde7SWilliam Kucharski *** initialization. If this routine is called, the pci functions did find the
689*1b8adde7SWilliam Kucharski *** card. We just have to init it here.
690*1b8adde7SWilliam Kucharski ***/
a3c90x_probe(struct dev * dev,struct pci_device * pci)691*1b8adde7SWilliam Kucharski static int a3c90x_probe(struct dev *dev, struct pci_device *pci)
692*1b8adde7SWilliam Kucharski {
693*1b8adde7SWilliam Kucharski struct nic *nic = (struct nic *)dev;
694*1b8adde7SWilliam Kucharski int i, c;
695*1b8adde7SWilliam Kucharski unsigned short eeprom[0x21];
696*1b8adde7SWilliam Kucharski unsigned int cfg;
697*1b8adde7SWilliam Kucharski unsigned int mopt;
698*1b8adde7SWilliam Kucharski unsigned int mstat;
699*1b8adde7SWilliam Kucharski unsigned short linktype;
700*1b8adde7SWilliam Kucharski #define HWADDR_OFFSET 10
701*1b8adde7SWilliam Kucharski
702*1b8adde7SWilliam Kucharski if (pci->ioaddr == 0)
703*1b8adde7SWilliam Kucharski return 0;
704*1b8adde7SWilliam Kucharski
705*1b8adde7SWilliam Kucharski adjust_pci_device(pci);
706*1b8adde7SWilliam Kucharski
707*1b8adde7SWilliam Kucharski nic->ioaddr = pci->ioaddr & ~3;
708*1b8adde7SWilliam Kucharski nic->irqno = 0;
709*1b8adde7SWilliam Kucharski
710*1b8adde7SWilliam Kucharski INF_3C90X.IOAddr = pci->ioaddr & ~3;
711*1b8adde7SWilliam Kucharski INF_3C90X.CurrentWindow = 255;
712*1b8adde7SWilliam Kucharski switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
713*1b8adde7SWilliam Kucharski {
714*1b8adde7SWilliam Kucharski case 0x9000: /** 10 Base TPO **/
715*1b8adde7SWilliam Kucharski case 0x9001: /** 10/100 T4 **/
716*1b8adde7SWilliam Kucharski case 0x9050: /** 10/100 TPO **/
717*1b8adde7SWilliam Kucharski case 0x9051: /** 10 Base Combo **/
718*1b8adde7SWilliam Kucharski INF_3C90X.isBrev = 0;
719*1b8adde7SWilliam Kucharski break;
720*1b8adde7SWilliam Kucharski
721*1b8adde7SWilliam Kucharski case 0x9004: /** 10 Base TPO **/
722*1b8adde7SWilliam Kucharski case 0x9005: /** 10 Base Combo **/
723*1b8adde7SWilliam Kucharski case 0x9006: /** 10 Base TPO and Base2 **/
724*1b8adde7SWilliam Kucharski case 0x900A: /** 10 Base FL **/
725*1b8adde7SWilliam Kucharski case 0x9055: /** 10/100 TPO **/
726*1b8adde7SWilliam Kucharski case 0x9056: /** 10/100 T4 **/
727*1b8adde7SWilliam Kucharski case 0x905A: /** 10 Base FX **/
728*1b8adde7SWilliam Kucharski default:
729*1b8adde7SWilliam Kucharski INF_3C90X.isBrev = 1;
730*1b8adde7SWilliam Kucharski break;
731*1b8adde7SWilliam Kucharski }
732*1b8adde7SWilliam Kucharski
733*1b8adde7SWilliam Kucharski /** Load the EEPROM contents **/
734*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
735*1b8adde7SWilliam Kucharski {
736*1b8adde7SWilliam Kucharski for(i=0;i<=0x20;i++)
737*1b8adde7SWilliam Kucharski {
738*1b8adde7SWilliam Kucharski eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
739*1b8adde7SWilliam Kucharski }
740*1b8adde7SWilliam Kucharski
741*1b8adde7SWilliam Kucharski #ifdef CFG_3C90X_BOOTROM_FIX
742*1b8adde7SWilliam Kucharski /** Set xcvrSelect in InternalConfig in eeprom. **/
743*1b8adde7SWilliam Kucharski /* only necessary for 3c905b revision cards with boot PROM bug!!! */
744*1b8adde7SWilliam Kucharski a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
745*1b8adde7SWilliam Kucharski #endif
746*1b8adde7SWilliam Kucharski
747*1b8adde7SWilliam Kucharski #ifdef CFG_3C90X_XCVR
748*1b8adde7SWilliam Kucharski if (CFG_3C90X_XCVR == 255)
749*1b8adde7SWilliam Kucharski {
750*1b8adde7SWilliam Kucharski /** Clear the LanWorks register **/
751*1b8adde7SWilliam Kucharski a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
752*1b8adde7SWilliam Kucharski }
753*1b8adde7SWilliam Kucharski else
754*1b8adde7SWilliam Kucharski {
755*1b8adde7SWilliam Kucharski /** Set the selected permanent-xcvrSelect in the
756*1b8adde7SWilliam Kucharski ** LanWorks register
757*1b8adde7SWilliam Kucharski **/
758*1b8adde7SWilliam Kucharski a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
759*1b8adde7SWilliam Kucharski XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
760*1b8adde7SWilliam Kucharski }
761*1b8adde7SWilliam Kucharski #endif
762*1b8adde7SWilliam Kucharski }
763*1b8adde7SWilliam Kucharski else
764*1b8adde7SWilliam Kucharski {
765*1b8adde7SWilliam Kucharski for(i=0;i<=0x17;i++)
766*1b8adde7SWilliam Kucharski {
767*1b8adde7SWilliam Kucharski eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
768*1b8adde7SWilliam Kucharski }
769*1b8adde7SWilliam Kucharski }
770*1b8adde7SWilliam Kucharski
771*1b8adde7SWilliam Kucharski /** Print identification message **/
772*1b8adde7SWilliam Kucharski printf("\n\n3C90X Driver 2.00 "
773*1b8adde7SWilliam Kucharski "Copyright 1999 LightSys Technology Services, Inc.\n"
774*1b8adde7SWilliam Kucharski "Portions Copyright 1999 Steve Smith\n");
775*1b8adde7SWilliam Kucharski printf("Provided with ABSOLUTELY NO WARRANTY.\n");
776*1b8adde7SWilliam Kucharski #ifdef CFG_3C90X_BOOTROM_FIX
777*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
778*1b8adde7SWilliam Kucharski {
779*1b8adde7SWilliam Kucharski printf("NOTE: 3c905b bootrom fix enabled; has side "
780*1b8adde7SWilliam Kucharski "effects. See 3c90x.txt for info.\n");
781*1b8adde7SWilliam Kucharski }
782*1b8adde7SWilliam Kucharski #endif
783*1b8adde7SWilliam Kucharski printf("-------------------------------------------------------"
784*1b8adde7SWilliam Kucharski "------------------------\n");
785*1b8adde7SWilliam Kucharski
786*1b8adde7SWilliam Kucharski /** Retrieve the Hardware address and print it on the screen. **/
787*1b8adde7SWilliam Kucharski INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8;
788*1b8adde7SWilliam Kucharski INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF;
789*1b8adde7SWilliam Kucharski INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8;
790*1b8adde7SWilliam Kucharski INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF;
791*1b8adde7SWilliam Kucharski INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8;
792*1b8adde7SWilliam Kucharski INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF;
793*1b8adde7SWilliam Kucharski printf("MAC Address = %!\n", INF_3C90X.HWAddr);
794*1b8adde7SWilliam Kucharski
795*1b8adde7SWilliam Kucharski /* Test if the link is good, if not continue */
796*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4);
797*1b8adde7SWilliam Kucharski mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w);
798*1b8adde7SWilliam Kucharski if((mstat & (1<<11)) == 0) {
799*1b8adde7SWilliam Kucharski printf("Valid link not established\n");
800*1b8adde7SWilliam Kucharski return 0;
801*1b8adde7SWilliam Kucharski }
802*1b8adde7SWilliam Kucharski
803*1b8adde7SWilliam Kucharski /** Program the MAC address into the station address registers **/
804*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
805*1b8adde7SWilliam Kucharski outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
806*1b8adde7SWilliam Kucharski outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
807*1b8adde7SWilliam Kucharski outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
808*1b8adde7SWilliam Kucharski outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
809*1b8adde7SWilliam Kucharski outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
810*1b8adde7SWilliam Kucharski outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
811*1b8adde7SWilliam Kucharski
812*1b8adde7SWilliam Kucharski /** Fill in our entry in the etherboot arp table **/
813*1b8adde7SWilliam Kucharski for(i=0;i<ETH_ALEN;i++)
814*1b8adde7SWilliam Kucharski nic->node_addr[i] = (eeprom[HWADDR_OFFSET + i/2] >> (8*((i&1)^1))) & 0xff;
815*1b8adde7SWilliam Kucharski
816*1b8adde7SWilliam Kucharski /** Read the media options register, print a message and set default
817*1b8adde7SWilliam Kucharski ** xcvr.
818*1b8adde7SWilliam Kucharski **
819*1b8adde7SWilliam Kucharski ** Uses Media Option command on B revision, Reset Option on non-B
820*1b8adde7SWilliam Kucharski ** revision cards -- same register address
821*1b8adde7SWilliam Kucharski **/
822*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
823*1b8adde7SWilliam Kucharski mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
824*1b8adde7SWilliam Kucharski
825*1b8adde7SWilliam Kucharski /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
826*1b8adde7SWilliam Kucharski if (! INF_3C90X.isBrev)
827*1b8adde7SWilliam Kucharski {
828*1b8adde7SWilliam Kucharski mopt &= 0x7F;
829*1b8adde7SWilliam Kucharski }
830*1b8adde7SWilliam Kucharski
831*1b8adde7SWilliam Kucharski printf("Connectors present: ");
832*1b8adde7SWilliam Kucharski c = 0;
833*1b8adde7SWilliam Kucharski linktype = 0x0008;
834*1b8adde7SWilliam Kucharski if (mopt & 0x01)
835*1b8adde7SWilliam Kucharski {
836*1b8adde7SWilliam Kucharski printf("%s100Base-T4",(c++)?", ":"");
837*1b8adde7SWilliam Kucharski linktype = 0x0006;
838*1b8adde7SWilliam Kucharski }
839*1b8adde7SWilliam Kucharski if (mopt & 0x04)
840*1b8adde7SWilliam Kucharski {
841*1b8adde7SWilliam Kucharski printf("%s100Base-FX",(c++)?", ":"");
842*1b8adde7SWilliam Kucharski linktype = 0x0005;
843*1b8adde7SWilliam Kucharski }
844*1b8adde7SWilliam Kucharski if (mopt & 0x10)
845*1b8adde7SWilliam Kucharski {
846*1b8adde7SWilliam Kucharski printf("%s10Base-2",(c++)?", ":"");
847*1b8adde7SWilliam Kucharski linktype = 0x0003;
848*1b8adde7SWilliam Kucharski }
849*1b8adde7SWilliam Kucharski if (mopt & 0x20)
850*1b8adde7SWilliam Kucharski {
851*1b8adde7SWilliam Kucharski printf("%sAUI",(c++)?", ":"");
852*1b8adde7SWilliam Kucharski linktype = 0x0001;
853*1b8adde7SWilliam Kucharski }
854*1b8adde7SWilliam Kucharski if (mopt & 0x40)
855*1b8adde7SWilliam Kucharski {
856*1b8adde7SWilliam Kucharski printf("%sMII",(c++)?", ":"");
857*1b8adde7SWilliam Kucharski linktype = 0x0006;
858*1b8adde7SWilliam Kucharski }
859*1b8adde7SWilliam Kucharski if ((mopt & 0xA) == 0xA)
860*1b8adde7SWilliam Kucharski {
861*1b8adde7SWilliam Kucharski printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
862*1b8adde7SWilliam Kucharski linktype = 0x0008;
863*1b8adde7SWilliam Kucharski }
864*1b8adde7SWilliam Kucharski else if ((mopt & 0xA) == 0x2)
865*1b8adde7SWilliam Kucharski {
866*1b8adde7SWilliam Kucharski printf("%s100Base-TX",(c++)?", ":"");
867*1b8adde7SWilliam Kucharski linktype = 0x0008;
868*1b8adde7SWilliam Kucharski }
869*1b8adde7SWilliam Kucharski else if ((mopt & 0xA) == 0x8)
870*1b8adde7SWilliam Kucharski {
871*1b8adde7SWilliam Kucharski printf("%s10Base-T",(c++)?", ":"");
872*1b8adde7SWilliam Kucharski linktype = 0x0008;
873*1b8adde7SWilliam Kucharski }
874*1b8adde7SWilliam Kucharski printf(".\n");
875*1b8adde7SWilliam Kucharski
876*1b8adde7SWilliam Kucharski /** Determine transceiver type to use, depending on value stored in
877*1b8adde7SWilliam Kucharski ** eeprom 0x16
878*1b8adde7SWilliam Kucharski **/
879*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
880*1b8adde7SWilliam Kucharski {
881*1b8adde7SWilliam Kucharski if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
882*1b8adde7SWilliam Kucharski {
883*1b8adde7SWilliam Kucharski /** User-defined **/
884*1b8adde7SWilliam Kucharski linktype = eeprom[0x16] & 0x000F;
885*1b8adde7SWilliam Kucharski }
886*1b8adde7SWilliam Kucharski }
887*1b8adde7SWilliam Kucharski else
888*1b8adde7SWilliam Kucharski {
889*1b8adde7SWilliam Kucharski #ifdef CFG_3C90X_XCVR
890*1b8adde7SWilliam Kucharski if (CFG_3C90X_XCVR != 255)
891*1b8adde7SWilliam Kucharski linktype = CFG_3C90X_XCVR;
892*1b8adde7SWilliam Kucharski #endif /* CFG_3C90X_XCVR */
893*1b8adde7SWilliam Kucharski
894*1b8adde7SWilliam Kucharski /** I don't know what MII MAC only mode is!!! **/
895*1b8adde7SWilliam Kucharski if (linktype == 0x0009)
896*1b8adde7SWilliam Kucharski {
897*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
898*1b8adde7SWilliam Kucharski printf("WARNING: MII External MAC Mode only supported on B-revision "
899*1b8adde7SWilliam Kucharski "cards!!!!\nFalling Back to MII Mode\n");
900*1b8adde7SWilliam Kucharski linktype = 0x0006;
901*1b8adde7SWilliam Kucharski }
902*1b8adde7SWilliam Kucharski }
903*1b8adde7SWilliam Kucharski
904*1b8adde7SWilliam Kucharski /** enable DC converter for 10-Base-T **/
905*1b8adde7SWilliam Kucharski if (linktype == 0x0003)
906*1b8adde7SWilliam Kucharski {
907*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
908*1b8adde7SWilliam Kucharski }
909*1b8adde7SWilliam Kucharski
910*1b8adde7SWilliam Kucharski /** Set the link to the type we just determined. **/
911*1b8adde7SWilliam Kucharski a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
912*1b8adde7SWilliam Kucharski cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
913*1b8adde7SWilliam Kucharski cfg &= ~(0xF<<20);
914*1b8adde7SWilliam Kucharski cfg |= (linktype<<20);
915*1b8adde7SWilliam Kucharski outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
916*1b8adde7SWilliam Kucharski
917*1b8adde7SWilliam Kucharski /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
918*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
919*1b8adde7SWilliam Kucharski while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
920*1b8adde7SWilliam Kucharski ;
921*1b8adde7SWilliam Kucharski
922*1b8adde7SWilliam Kucharski if (!INF_3C90X.isBrev)
923*1b8adde7SWilliam Kucharski outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
924*1b8adde7SWilliam Kucharski
925*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
926*1b8adde7SWilliam Kucharski
927*1b8adde7SWilliam Kucharski /**
928*1b8adde7SWilliam Kucharski ** reset of the receiver on B-revision cards re-negotiates the link
929*1b8adde7SWilliam Kucharski ** takes several seconds (a computer eternity)
930*1b8adde7SWilliam Kucharski **/
931*1b8adde7SWilliam Kucharski if (INF_3C90X.isBrev)
932*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
933*1b8adde7SWilliam Kucharski else
934*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
935*1b8adde7SWilliam Kucharski while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
936*1b8adde7SWilliam Kucharski ;
937*1b8adde7SWilliam Kucharski
938*1b8adde7SWilliam Kucharski /** Set the RX filter = receive only individual pkts & multicast & bcast. **/
939*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x02 + 0x04);
940*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
941*1b8adde7SWilliam Kucharski
942*1b8adde7SWilliam Kucharski
943*1b8adde7SWilliam Kucharski /**
944*1b8adde7SWilliam Kucharski ** set Indication and Interrupt flags , acknowledge any IRQ's
945*1b8adde7SWilliam Kucharski **/
946*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
947*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
948*1b8adde7SWilliam Kucharski cmdSetIndicationEnable, 0x0014);
949*1b8adde7SWilliam Kucharski a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
950*1b8adde7SWilliam Kucharski cmdAcknowledgeInterrupt, 0x661);
951*1b8adde7SWilliam Kucharski
952*1b8adde7SWilliam Kucharski /** Set our exported functions **/
953*1b8adde7SWilliam Kucharski dev->disable = a3c90x_disable;
954*1b8adde7SWilliam Kucharski nic->poll = a3c90x_poll;
955*1b8adde7SWilliam Kucharski nic->transmit = a3c90x_transmit;
956*1b8adde7SWilliam Kucharski nic->irq = a3c90x_irq;
957*1b8adde7SWilliam Kucharski
958*1b8adde7SWilliam Kucharski return 1;
959*1b8adde7SWilliam Kucharski }
960*1b8adde7SWilliam Kucharski
961*1b8adde7SWilliam Kucharski
962*1b8adde7SWilliam Kucharski static struct pci_id a3c90x_nics[] = {
963*1b8adde7SWilliam Kucharski /* Original 90x revisions: */
964*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO"), /* 10 Base TPO */
965*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo"), /* 10/100 T4 */
966*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX"), /* 100 Base TX / 10/100 TPO */
967*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4"), /* 100 Base T4 / 10 Base Combo */
968*1b8adde7SWilliam Kucharski /* Newer 90xB revisions: */
969*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
970*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo"), /* 10 Base Combo */
971*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
972*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL"), /* 10 Base FL */
973*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"), /* 10/100 TPO */
974*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4"), /* 10/100 T4 */
975*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058"), /* Cyclone 10/100/BNC */
976*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL"), /* 100 Base FX / 10 Base FX */
977*1b8adde7SWilliam Kucharski /* Newer 90xC revision: */
978*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM"), /* 10/100 TPO (3C905C-TXM) */
979*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"),
980*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone"), /* Cyclone */
981*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805"), /* Dual Port Server Cyclone */
982*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX"), /* Hurricane */
983*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado"),
984*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A"),
985*1b8adde7SWilliam Kucharski PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B"),
986*1b8adde7SWilliam Kucharski };
987*1b8adde7SWilliam Kucharski
988*1b8adde7SWilliam Kucharski struct pci_driver a3c90x_driver = {
989*1b8adde7SWilliam Kucharski .type = NIC_DRIVER,
990*1b8adde7SWilliam Kucharski .name = "3C90X",
991*1b8adde7SWilliam Kucharski .probe = a3c90x_probe,
992*1b8adde7SWilliam Kucharski .ids = a3c90x_nics,
993*1b8adde7SWilliam Kucharski .id_count = sizeof(a3c90x_nics)/sizeof(a3c90x_nics[0]),
994*1b8adde7SWilliam Kucharski .class = 0,
995*1b8adde7SWilliam Kucharski };
996