1 /* atlantic.c: Setup program for AT/LANTIC DP83905 ethercards. */
3 This is a setup and diagnostic program for ISA NE2000 Ethernet adapters.
4 It has specific support for configuring the National Semiconductor
5 AT/LANTIC DP83905 used in on ISA Ethernet adapters such as the NE2000plus.
6 It also works several work-alike chips from other vendors.
8 This program must be compiled with '-O'.
9 If you have unresolved references to e.g. inw(), then you haven't used -O.
10 The suggested compile command is at the bottom of this file.
12 Copyright 1994-2000 by Donald Becker.
13 This software may be used and distributed according to the terms of
14 the GNU General Public License (GPL), incorporated herein by reference.
15 Contact the author for use under other terms.
17 The author may be reached as becker@scyld.com, or C/O
18 Scyld Computing Corporation
19 410 Severn Ave., Suite 210
22 Support and updates are available at
23 http://www.scyld.com/diag/index.html
25 Common-sense licensing statement: Using any portion of this program in
26 your own program means that you must give credit to the original author
27 and release the resulting code under the GPL. You must include the
28 text of the license with any redistribution. See
29 http://www.gnu.org/copyleft/gpl.txt
32 static char vcid[] = "$Id$";
35 static char *version_msg =
36 "atlantic.c v1.01 7/23/00 Donald Becker http://www.scyld.com/diag/index.html\n";
37 static char *usage_msg =
38 "Usage: atlantic [-afhNsvVwW] [-p <IOport>] [-F <xcvr-type>] [-P <newIOport>]
39 -p <I/O base address> Use the card at this I/O address (default 0x300).
40 EEPROM configuration commands take effect at the next reset
41 -F 10baseT, 10base2, AUI, 10baset Set the specified transceiver type.
42 -Q 3, 4, 5, 9, 10, 11, 12, 15 Set the IRQ line.
43 -P 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360 New I/O base.
44 -N or -W Set to NE2000 or WD8013 clone mode (mutually exclusive)
47 #if ! defined(__OPTIMIZE__)
48 #warning You must compile this program with the correct options!
49 #error You must compile this driver with "-O".
51 #include <unistd.h> /* Hey, we're all POSIX here, right? */
58 #if defined(__linux__) && __GNU_LIBRARY__ == 1
59 #include <asm/io.h> /* Newer libraries use <sys/io.h> instead. */
64 /* Two functions that some GLIBC header file sets omit. grrr... */
65 extern int ioperm(unsigned long __base, unsigned long extent, int on_or_off);
66 extern int iopl(int level);
68 struct option longopts[] = {
69 /* { name has_arg *flag val } */
70 {"base-address", 1, 0, 'p'}, /* Base I/O port address. */
71 {"help", 0, 0, 'h'}, /* Give help */
72 {"interface", 0, 0, 'F'}, /* Set the transceiver type (built-in, AUI) */
73 {"NE2000", 1, 0, 'N'}, /* Set to NE2000 mode */
74 {"new_ioport", 1, 0, 'P'}, /* Set a I/O location for the card. */
75 {"irq", 1, 0, 'Q'}, /* Set a new IRQ (interrupt) line. */
76 {"wd8013", 1, 0, 'W'}, /* Set to WD8013 mode */
77 {"verbose", 0, 0, 'v'}, /* Verbose mode */
78 {"version", 0, 0, 'V'}, /* Display version number */
79 {"write-EEPROM", 1, 0, 'w'},/* Write th EEPROMS with the specified vals */
83 /* Three configuration registers are read and may be set from the EEPROM.
84 They are named registers A, B and C
86 RegA NE/WD FREAD INT2 INT1 INT0 IOAD2 IOAD1 IOAD0
87 RegB EELOAD BTPR BUSE CHRDY IO16 GDLINK XCVR1 XCVR0
88 RegC SoftEn ClkSel IntMde COMP BtPR3 BtPR2 BtPR1 BtPROM0
90 /* I/O base settings, IOAD2-0, in register A. */
91 static int io_base[8] = {
92 0x300, 0x278, 0x240, 0x280, 0x2C0, 0x320, 0x340, 0x360};
93 /* The IRQ line settings, INT2-0, in register A, and a reverse map. */
94 static int index2irq[8] = {3, 4, 5, 9, 10, 11, 12, 15};
95 static int irq2index[16] = {-1, -1, 3, 0, 1, 2, -1, -1,
96 -1, 3, 4, 5, 6, -1, -1, 7};
97 /* The transceiver settings, XCVR1-0, in register B.
98 10baseT-LRT is Low Receive Threshold -- "turning the volume up" for long
101 static char *xcvr_name[4] = {"10baseT", "Thinnet", "AUI", "10baseT (LRT)"};
105 static void show_config(int regA, int regB);
106 static void write_EEPROM(int dp8390_base, int regA, int regB, int regC);
110 I should say something here... uuhhhhmmm....
114 main(int argc, char *argv[])
117 int port_base = 0x300; /* Base address of the board. */
118 int ioaddr = 0x300; /* Base address of the 8390 registers. */
119 int new_ioport = -1, new_irq = -1, new_mode = 0;
120 int errflag = 0, verbose = 0, wd_mode = 0, write_eeprom = 0;
121 int show_version = 0, opt_a = 0;
122 int new_io_idx = -1, xcvr = -1; /* I/O port, Transceiver type to set. */
123 int regA, regB, old_regA, old_regB;
126 while ((option = getopt(argc, argv, "afF:hi:Q:m:Np:P:vVwW")) != -1)
128 case 'a': opt_a++; break;
129 case 'f': opt_f++; break;
131 if (strncmp(optarg, "10base", 6) == 0) {
133 case 'T': xcvr = 0; break;
134 case '2': xcvr = 1; break;
135 case '5': xcvr = 2; break;
138 } else if (strcmp(optarg, "AUI") == 0)
140 else if (optarg[0] >= '0' && optarg[0] <= '3'
142 xcvr = optarg[0] - '0';
144 fprintf(stderr, "Invalid interface specified: it must be"
145 " 0..3, '10base{T,2,5}' or 'AUI'.\n");
149 case 'h': printf(usage_msg); return 0;
150 case 'N': wd_mode = 0; new_mode = 2; break;
153 new_irq = atoi(optarg);
154 if (new_irq < 3 || new_irq > 15 || new_irq == 6 || new_irq == 8) {
155 fprintf(stderr, "Invalid new IRQ %#x. Valid values: "
156 "3-5,7,9-15.\n", new_irq);
160 case 'p': port_base = strtol(optarg, NULL, 16); break;
161 case 'P': new_ioport = strtol(optarg, NULL, 16); break;
162 case 'v': verbose++; break;
163 case 'V': show_version++; break;
164 case 'w': write_eeprom++; break;
165 case 'W': wd_mode++; new_mode = 1; break;
170 fprintf(stderr, usage_msg);
174 if (verbose || show_version)
177 /* Turn on access to the I/O ports. */
178 if (ioperm(port_base, 32, 1) < 0) {
179 perror("atlantic: io-perm");
180 fprintf(stderr, " (You must run this program with 'root' permissions.)\n");
184 /* Check the specified new I/O port value. */
185 if (new_irq >= 0 && (new_irq >= 16 || irq2index[new_irq] < 0)) {
186 fprintf(stderr, "The new IRQ line, %d, is invalid.\n",
190 if (new_ioport >= 0) {
192 for (i = 0; i < 8; i++)
193 if (new_ioport == io_base[i])
196 fprintf(stderr, "The new base I/O address, %#x, is invalid.\n",
203 if ( ! opt_f && inb(port_base) == 0xff) {
204 printf("No AT/LANTIC chip found at I/O %#x.\n"
205 " Use '-f' to override if you are certain the chip is at this"
206 " I/O location.\n", port_base);
210 /* First find if this card is set to WD or NE mode by locating the 8390
213 int saved_cmd = inb(port_base);
215 outb(0x20, port_base);
216 inb(port_base + 13); /* Clear a counter register */
217 cntr = inb(port_base + 13);
219 printf(" The NE2K 8390 cmd register was %2.2x, cntr %d.\n",
222 ioaddr = port_base + 16;
225 outb(saved_cmd, port_base);
229 int saved_0 = inb(port_base);
232 printf("8390 registers at %#x (command register was %2.2x)\n",
234 for (window = 0; window < 4; window++) {
235 printf(" Window %d:", window);
236 outb((window<<6) | 0x20, ioaddr);
237 for(i = 0; i < 16; i++) {
238 if (window == 0 && i == 6)
241 printf(" %2.2x", inb(ioaddr + i));
245 if (ioaddr != port_base) { /* WD emulation. */
246 printf("WD8013 compatible registers: ");
247 for(i = 0; i < 16; i++) {
248 printf(" %2.2x", inb(port_base + i));
255 printf("Reading the configuration from the AT/LANTIC at %#3x...\n",
257 outb(0x21, ioaddr); /* Select Window 0 */
258 old_regA = regA = inb(ioaddr + 0x0A);
259 old_regB = regB = inb(ioaddr + 0x0B);
261 printf("The current configuration is\n");
262 show_config(regA, regB);
264 /* This should never be triggered. We see the card after all!*/
266 printf(" Your card is set to be configured at boot-time.\n"
267 " Remaining with this software configuration is a bad idea"
268 " when using\n modern systems. The card will likely be"
269 " unintentionally activated at\n unexpected I/O and IRQ"
273 regA = (regA & ~0x80) | (new_mode == 1 ? 0x80 : 0);
275 regA = (regA & ~0x38) | (irq2index[new_irq]<<3);
277 regA = (regA & ~0x07) | new_io_idx;
279 regB = (regB & 0x5C) | (xcvr & 0x3);
281 if (regA != old_regA || regB != old_regB || write_eeprom) {
282 printf("The proposed new configuration is\n");
283 show_config(regA, regB);
285 printf(" Use '-w' to set this as the current configuration "
286 "(valid until next reset).\n"
287 " Use '-w -w' to write the settings to the EEPROM.\n"
288 " Use '-w -w -w' to do both.\n");
291 /* We must write the EEPROM and register B before possibly moving the
293 if (write_eeprom > 1) {
294 printf("Writing the new configuration to the EEPROM...\n");
295 write_EEPROM(ioaddr, regA, regB, 0x30);
296 printf("Wrote the EEPROM at %#3x with 0x%2.2x 0x%2.2x 0x%2.2x.\n",
297 port_base, regA, regB, 0x30);
300 if (write_eeprom & 1) {
301 if (regB != old_regB) {
302 printf(" Setting register B to %#02x.\n", regB);
304 outb(regB, ioaddr + 0x0B);
305 regB = inb(ioaddr + 0x0B);
306 printf(" Register B is now %#02x: Interface %s.\n", regB,
307 xcvr_name[regB & 0x03]);
309 if (regA != old_regA) {
310 printf(" Setting register A to %#02x (%s mode I/O %#x IRQ %d).\n",
311 regA, regA & 0x80 ? "WD8013" : "NE2000",
312 io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07] );
314 outb(regA, ioaddr + 0x0A);
315 if (port_base == io_base[regA&7]) {
316 ioaddr = port_base + (regA & 0x80 ? 16 : 0);
317 printf(" Register A at base %x is now %#02x.\n",
318 ioaddr, inb(ioaddr + 0x0A));
326 static void show_config(int regA, int regB)
328 printf(" Register A 0x%2.2x: I/O base @ %#x, IRQ %d,"
329 " %s mode, %s ISA read.\n",
330 regA, io_base[regA & 0x07], index2irq[(regA >> 3) & 0x07],
331 regA & 0x80 ? "WD8013" : "NE2000",
332 regA & 0x40 ? "fast" : "normal" );
333 printf(" Register B 0x%2.2x: Interface %s.\n", regB,
334 xcvr_name[regB & 0x03]);
335 printf(" Boot PROM writes are %sabled, CHRDY after %s%s.\n",
336 regB & 0x40 ? "en" : "dis",
337 regB & 0x10 ? "BALE" : "IORD/IOWR",
338 regB & 0x08 ? ", IO16 after IORD/IOWR" : "");
340 printf(" Bus error detected.\n");
343 /* Write the A, B, and C configuration registers into the EEPROM at
345 Ideally this would be done with interrupts disabled, but we do not have
346 that capability with user level code.
348 static void write_EEPROM(int dp8390_base, int regA, int regB, int regC)
350 int ioregb = dp8390_base + 0x0B;
351 int currB = inb(ioregb) & (~0x04) ;
353 outb(currB | 0x80, ioregb);
363 * compile-command: "gcc -Wall -Wstrict-prototypes -O6 -o atlantic atlantic.c"