~smartboyhw/wubi/bug-1080090-new

« back to all changes in this revision

Viewing changes to src/grub4dos/netboot/.svn/text-base/3c90x.c.svn-base

  • Committer: Howard Chan
  • Date: 2012-11-20 10:16:05 UTC
  • Revision ID: smartboyhw@gmail.com-20121120101605-qfmjfsdynpzg9an9
Added images

Show diffs side-by-side

added added

removed removed

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