xref: /titanic_50/usr/src/grub/grub-0.97/netboot/3c90x.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
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