Initial revision
[people/sha0/gpxe.git] / src / drivers / net / smc9000.c
1 #ifdef ALLMULTI
2 #error multicast support is not yet implemented
3 #endif
4  /*------------------------------------------------------------------------
5  * smc9000.c
6  * This is a Etherboot driver for SMC's 9000 series of Ethernet cards.
7  *
8  * Copyright (C) 1998 Daniel Engström <daniel.engstrom@riksnett.no>
9  * Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman
10  * Copyright (C) 1996 by Erik Stahlman <eric@vt.edu>
11  *
12  * This software may be used and distributed according to the terms
13  * of the GNU Public License, incorporated herein by reference.
14  *
15  * "Features" of the SMC chip:
16  *   4608 byte packet memory. ( for the 91C92/4.  Others have more )
17  *   EEPROM for configuration
18  *   AUI/TP selection
19  *
20  * Authors
21  *      Erik Stahlman                           <erik@vt.edu>
22  *      Daniel Engström                         <daniel.engstrom@riksnett.no>
23  *
24  * History
25  * 98-09-25              Daniel Engström Etherboot driver crated from Eric's
26  *                                       Linux driver.
27  *
28  *---------------------------------------------------------------------------*/
29 #define LINUX_OUT_MACROS 1
30 #define SMC9000_VERBOSE  1
31 #define SMC9000_DEBUG    0
32
33 #include "etherboot.h"
34 #include "nic.h"
35 #include "isa.h"
36 #include "smc9000.h"
37
38 # define _outb outb
39 # define _outw outw
40
41 static const char       smc9000_version[] = "Version 0.99 98-09-30";
42 static unsigned int     smc9000_base=0;
43 static const char       *interfaces[ 2 ] = { "TP", "AUI" };
44 static const char       *chip_ids[ 15 ] =  {
45    NULL, NULL, NULL,
46    /* 3 */ "SMC91C90/91C92",
47    /* 4 */ "SMC91C94",
48    /* 5 */ "SMC91C95",
49    NULL,
50    /* 7 */ "SMC91C100",
51    /* 8 */ "SMC91C100FD",
52    NULL, NULL, NULL,
53    NULL, NULL, NULL
54 };
55 static const char      smc91c96_id[] = "SMC91C96";
56
57 /*
58  * Function: smc_reset( int ioaddr )
59  * Purpose:
60  *      This sets the SMC91xx chip to its normal state, hopefully from whatever
61  *      mess that any other DOS driver has put it in.
62  *
63  * Maybe I should reset more registers to defaults in here?  SOFTRESET  should
64  * do that for me.
65  *
66  * Method:
67  *      1.  send a SOFT RESET
68  *      2.  wait for it to finish
69  *      3.  reset the memory management unit
70  *      4.  clear all interrupts
71  *
72 */
73 static void smc_reset(int ioaddr)
74 {
75    /* This resets the registers mostly to defaults, but doesn't
76     * affect EEPROM.  That seems unnecessary */
77    SMC_SELECT_BANK(ioaddr, 0);
78    _outw( RCR_SOFTRESET, ioaddr + RCR );
79
80    /* this should pause enough for the chip to be happy */
81    SMC_DELAY(ioaddr);
82
83    /* Set the transmit and receive configuration registers to
84     * default values */
85    _outw(RCR_CLEAR, ioaddr + RCR);
86    _outw(TCR_CLEAR, ioaddr + TCR);
87
88    /* Reset the MMU */
89    SMC_SELECT_BANK(ioaddr, 2);
90    _outw( MC_RESET, ioaddr + MMU_CMD );
91
92    /* Note:  It doesn't seem that waiting for the MMU busy is needed here,
93     * but this is a place where future chipsets _COULD_ break.  Be wary
94     * of issuing another MMU command right after this */
95    _outb(0, ioaddr + INT_MASK);
96 }
97
98
99 /*----------------------------------------------------------------------
100  * Function: smc_probe( int ioaddr )
101  *
102  * Purpose:
103  *      Tests to see if a given ioaddr points to an SMC9xxx chip.
104  *      Returns a 0 on success
105  *
106  * Algorithm:
107  *      (1) see if the high byte of BANK_SELECT is 0x33
108  *      (2) compare the ioaddr with the base register's address
109  *      (3) see if I recognize the chip ID in the appropriate register
110  *
111  * ---------------------------------------------------------------------
112  */
113 static int smc_probe( int ioaddr )
114 {
115    word bank;
116    word revision_register;
117    word base_address_register;
118
119    /* First, see if the high byte is 0x33 */
120    bank = inw(ioaddr + BANK_SELECT);
121    if ((bank & 0xFF00) != 0x3300) {
122       return -1;
123    }
124    /* The above MIGHT indicate a device, but I need to write to further
125     *   test this.  */
126    _outw(0x0, ioaddr + BANK_SELECT);
127    bank = inw(ioaddr + BANK_SELECT);
128    if ((bank & 0xFF00) != 0x3300) {
129       return -1;
130    }
131
132    /* well, we've already written once, so hopefully another time won't
133     *  hurt.  This time, I need to switch the bank register to bank 1,
134     *  so I can access the base address register */
135    SMC_SELECT_BANK(ioaddr, 1);
136    base_address_register = inw(ioaddr + BASE);
137
138    if (ioaddr != (base_address_register >> 3 & 0x3E0))  {
139 #ifdef  SMC9000_VERBOSE
140       printf("SMC9000: IOADDR %hX doesn't match configuration (%hX)."
141              "Probably not a SMC chip\n",
142              ioaddr, base_address_register >> 3 & 0x3E0);
143 #endif
144       /* well, the base address register didn't match.  Must not have
145        * been a SMC chip after all. */
146       return -1;
147    }
148
149
150    /* check if the revision register is something that I recognize.
151     * These might need to be added to later, as future revisions
152     * could be added.  */
153    SMC_SELECT_BANK(ioaddr, 3);
154    revision_register  = inw(ioaddr + REVISION);
155    if (!chip_ids[(revision_register >> 4) & 0xF]) {
156       /* I don't recognize this chip, so... */
157 #ifdef  SMC9000_VERBOSE
158       printf("SMC9000: IO %hX: Unrecognized revision register:"
159              " %hX, Contact author.\n", ioaddr, revision_register);
160 #endif
161       return -1;
162    }
163
164    /* at this point I'll assume that the chip is an SMC9xxx.
165     * It might be prudent to check a listing of MAC addresses
166     * against the hardware address, or do some other tests. */
167    return 0;
168 }
169
170
171 /**************************************************************************
172  * ETH_TRANSMIT - Transmit a frame
173  ***************************************************************************/
174 static void smc9000_transmit(
175         struct nic *nic,
176         const char *d,                  /* Destination */
177         unsigned int t,                 /* Type */
178         unsigned int s,                 /* size */
179         const char *p)                  /* Packet */
180 {
181    word length; /* real, length incl. header */
182    word numPages;
183    unsigned long time_out;
184    byte packet_no;
185    word status;
186    int i;
187
188    /* We dont pad here since we can have the hardware doing it for us */
189    length = (s + ETH_HLEN + 1)&~1;
190
191    /* convert to MMU pages */
192    numPages = length / 256;
193
194    if (numPages > 7 ) {
195 #ifdef  SMC9000_VERBOSE
196       printf("SMC9000: Far too big packet error. \n");
197 #endif
198       return;
199    }
200
201    /* dont try more than, say 30 times */
202    for (i=0;i<30;i++) {
203       /* now, try to allocate the memory */
204       SMC_SELECT_BANK(smc9000_base, 2);
205       _outw(MC_ALLOC | numPages, smc9000_base + MMU_CMD);
206
207       status = 0;
208       /* wait for the memory allocation to finnish */
209       for (time_out = currticks() + 5*TICKS_PER_SEC; currticks() < time_out; ) {
210          status = inb(smc9000_base + INTERRUPT);
211          if ( status & IM_ALLOC_INT ) {
212             /* acknowledge the interrupt */
213             _outb(IM_ALLOC_INT, smc9000_base + INTERRUPT);
214             break;
215          }
216       }
217
218       if ((status & IM_ALLOC_INT) != 0 ) {
219          /* We've got the memory */
220          break;
221       } else {
222          printf("SMC9000: Memory allocation timed out, resetting MMU.\n");
223          _outw(MC_RESET, smc9000_base + MMU_CMD);
224       }
225    }
226
227    /* If I get here, I _know_ there is a packet slot waiting for me */
228    packet_no = inb(smc9000_base + PNR_ARR + 1);
229    if (packet_no & 0x80) {
230       /* or isn't there?  BAD CHIP! */
231       printf("SMC9000: Memory allocation failed. \n");
232       return;
233    }
234
235    /* we have a packet address, so tell the card to use it */
236    _outb(packet_no, smc9000_base + PNR_ARR);
237
238    /* point to the beginning of the packet */
239    _outw(PTR_AUTOINC, smc9000_base + POINTER);
240
241 #if     SMC9000_DEBUG > 2
242    printf("Trying to xmit packet of length %hX\n", length );
243 #endif
244
245    /* send the packet length ( +6 for status, length and ctl byte )
246     * and the status word ( set to zeros ) */
247    _outw(0, smc9000_base + DATA_1 );
248
249    /* send the packet length ( +6 for status words, length, and ctl) */
250    _outb((length+6) & 0xFF,  smc9000_base + DATA_1);
251    _outb((length+6) >> 8 ,   smc9000_base + DATA_1);
252
253    /* Write the contents of the packet */
254
255    /* The ethernet header first... */
256    outsw(smc9000_base + DATA_1, d, ETH_ALEN >> 1);
257    outsw(smc9000_base + DATA_1, nic->node_addr, ETH_ALEN >> 1);
258    _outw(htons(t), smc9000_base + DATA_1);
259
260    /* ... the data ... */
261    outsw(smc9000_base + DATA_1 , p, s >> 1);
262
263    /* ... and the last byte, if there is one.   */
264    if ((s & 1) == 0) {
265       _outw(0, smc9000_base + DATA_1);
266    } else {
267       _outb(p[s-1], smc9000_base + DATA_1);
268       _outb(0x20, smc9000_base + DATA_1);
269    }
270
271    /* and let the chipset deal with it */
272    _outw(MC_ENQUEUE , smc9000_base + MMU_CMD);
273
274    status = 0; time_out = currticks() + 5*TICKS_PER_SEC;
275    do {
276       status = inb(smc9000_base + INTERRUPT);
277
278       if ((status & IM_TX_INT ) != 0) {
279          word tx_status;
280
281          /* ack interrupt */
282          _outb(IM_TX_INT, smc9000_base + INTERRUPT);
283
284          packet_no = inw(smc9000_base + FIFO_PORTS);
285          packet_no &= 0x7F;
286
287          /* select this as the packet to read from */
288          _outb( packet_no, smc9000_base + PNR_ARR );
289
290          /* read the first word from this packet */
291          _outw( PTR_AUTOINC | PTR_READ, smc9000_base + POINTER );
292
293          tx_status = inw( smc9000_base + DATA_1 );
294
295          if (0 == (tx_status & TS_SUCCESS)) {
296 #ifdef  SMC9000_VERBOSE
297             printf("SMC9000: TX FAIL STATUS: %hX \n", tx_status);
298 #endif
299             /* re-enable transmit */
300             SMC_SELECT_BANK(smc9000_base, 0);
301             _outw(inw(smc9000_base + TCR ) | TCR_ENABLE, smc9000_base + TCR );
302          }
303
304          /* kill the packet */
305          SMC_SELECT_BANK(smc9000_base, 2);
306          _outw(MC_FREEPKT, smc9000_base + MMU_CMD);
307
308          return;
309       }
310    }while(currticks() < time_out);
311
312    printf("SMC9000: Waring TX timed out, resetting board\n");
313    smc_reset(smc9000_base);
314    return;
315 }
316
317 /**************************************************************************
318  * ETH_POLL - Wait for a frame
319  ***************************************************************************/
320 static int smc9000_poll(struct nic *nic, int retrieve)
321 {
322    if(!smc9000_base)
323      return 0;
324
325    SMC_SELECT_BANK(smc9000_base, 2);
326    if (inw(smc9000_base + FIFO_PORTS) & FP_RXEMPTY)
327      return 0;
328    
329    if ( ! retrieve ) return 1;
330
331    /*  start reading from the start of the packet */
332    _outw(PTR_READ | PTR_RCV | PTR_AUTOINC, smc9000_base + POINTER);
333
334    /* First read the status and check that we're ok */
335    if (!(inw(smc9000_base + DATA_1) & RS_ERRORS)) {
336       /* Next: read the packet length and mask off the top bits */
337       nic->packetlen = (inw(smc9000_base + DATA_1) & 0x07ff);
338
339       /* the packet length includes the 3 extra words */
340       nic->packetlen -= 6;
341 #if     SMC9000_DEBUG > 2
342       printf(" Reading %d words (and %d byte(s))\n",
343                (nic->packetlen >> 1), nic->packetlen & 1);
344 #endif
345       /* read the packet (and the last "extra" word) */
346       insw(smc9000_base + DATA_1, nic->packet, (nic->packetlen+2) >> 1);
347       /* is there an odd last byte ? */
348       if (nic->packet[nic->packetlen+1] & 0x20)
349          nic->packetlen++;
350
351       /*  error or good, tell the card to get rid of this packet */
352       _outw(MC_RELEASE, smc9000_base + MMU_CMD);
353       return 1;
354    }
355
356    printf("SMC9000: RX error\n");
357    /*  error or good, tell the card to get rid of this packet */
358    _outw(MC_RELEASE, smc9000_base + MMU_CMD);
359    return 0;
360 }
361
362 static void smc9000_disable(struct dev *dev __unused)
363 {
364    if(!smc9000_base)
365      return;
366
367    smc_reset(smc9000_base);
368
369    /* no more interrupts for me */
370    SMC_SELECT_BANK(smc9000_base, 2);
371    _outb( 0, smc9000_base + INT_MASK);
372
373    /* and tell the card to stay away from that nasty outside world */
374    SMC_SELECT_BANK(smc9000_base, 0);
375    _outb( RCR_CLEAR, smc9000_base + RCR );
376    _outb( TCR_CLEAR, smc9000_base + TCR );
377 }
378
379 static void smc9000_irq(struct nic *nic __unused, irq_action_t action __unused)
380 {
381   switch ( action ) {
382   case DISABLE :
383     break;
384   case ENABLE :
385     break;
386   case FORCE :
387     break;
388   }
389 }
390
391 /**************************************************************************
392  * ETH_PROBE - Look for an adapter
393  ***************************************************************************/
394
395 static int smc9000_probe(struct dev *dev, unsigned short *probe_addrs)
396 {
397    struct nic *nic = (struct nic *)dev;
398    unsigned short   revision;
399    int              memory;
400    int              media;
401    const char *     version_string;
402    const char *     if_string;
403    int              i;
404
405    /*
406     * the SMC9000 can be at any of the following port addresses.  To change,
407     * for a slightly different card, you can add it to the array.  Keep in
408     * mind that the array must end in zero.
409     */
410    static unsigned short portlist[] = {
411 #ifdef  SMC9000_SCAN
412       SMC9000_SCAN,
413 #else
414       0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
415       0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0,
416 #endif
417       0 };
418
419    /* if no addresses supplied, fall back on defaults */
420    if (probe_addrs == 0 || probe_addrs[0] == 0)
421      probe_addrs = portlist;
422
423    /* check every ethernet address */
424    for (i = 0; probe_addrs[i]; i++) {
425       /* check this specific address */
426       if (smc_probe(probe_addrs[i]) == 0)
427         smc9000_base = probe_addrs[i];
428    }
429
430    /* couldn't find anything */
431    if(0 == smc9000_base)
432      goto out;
433
434    nic->irqno  = 0;
435    nic->ioaddr = smc9000_base;
436
437    /*
438     * Get the MAC address ( bank 1, regs 4 - 9 )
439     */
440    SMC_SELECT_BANK(smc9000_base, 1);
441    for ( i = 0; i < 6; i += 2 ) {
442       word address;
443
444       address = inw(smc9000_base + ADDR0 + i);
445       nic->node_addr[i+1] = address >> 8;
446       nic->node_addr[i] = address & 0xFF;
447    }
448
449
450    /* get the memory information */
451    SMC_SELECT_BANK(smc9000_base, 0);
452    memory = ( inw(smc9000_base + MCR) >> 9 )  & 0x7;  /* multiplier */
453    memory *= 256 * (inw(smc9000_base + MIR) & 0xFF);
454
455    /*
456     * Now, I want to find out more about the chip.  This is sort of
457     * redundant, but it's cleaner to have it in both, rather than having
458     * one VERY long probe procedure.
459     */
460    SMC_SELECT_BANK(smc9000_base, 3);
461    revision  = inw(smc9000_base + REVISION);
462    version_string = chip_ids[(revision >> 4) & 0xF];
463
464    if (((revision & 0xF0) >> 4 == CHIP_9196) &&
465        ((revision & 0x0F) >= REV_9196)) {
466       /* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but
467        * a revision starting at 6 */
468       version_string = smc91c96_id;
469    }
470
471    if ( !version_string ) {
472       /* I shouldn't get here because this call was done before.... */
473       goto out;
474    }
475
476    /* is it using AUI or 10BaseT ? */
477    SMC_SELECT_BANK(smc9000_base, 1);
478    if (inw(smc9000_base + CONFIG) & CFG_AUI_SELECT)
479      media = 2;
480    else
481      media = 1;
482
483    if_string = interfaces[media - 1];
484
485    /* now, reset the chip, and put it into a known state */
486    smc_reset(smc9000_base);
487
488    printf("SMC9000 %s\n", smc9000_version);
489 #ifdef  SMC9000_VERBOSE
490    printf("Copyright (C) 1998 Daniel Engstr\x94m\n");
491    printf("Copyright (C) 1996 Eric Stahlman\n");
492 #endif
493
494    printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n",
495           version_string, revision & 0xF,
496           smc9000_base, if_string, memory );
497    /*
498     * Print the Ethernet address
499     */
500    printf("Ethernet MAC address: %!\n", nic->node_addr);
501
502    SMC_SELECT_BANK(smc9000_base, 0);
503
504    /* see the header file for options in TCR/RCR NORMAL*/
505    _outw(TCR_NORMAL, smc9000_base + TCR);
506    _outw(RCR_NORMAL, smc9000_base + RCR);
507
508    /* Select which interface to use */
509    SMC_SELECT_BANK(smc9000_base, 1);
510    if ( media == 1 ) {
511       _outw( inw( smc9000_base + CONFIG ) & ~CFG_AUI_SELECT,
512            smc9000_base + CONFIG );
513    }
514    else if ( media == 2 ) {
515       _outw( inw( smc9000_base + CONFIG ) | CFG_AUI_SELECT,
516            smc9000_base + CONFIG );
517    }
518
519    dev->disable  = smc9000_disable;
520    nic->poll     = smc9000_poll;
521    nic->transmit = smc9000_transmit;
522    nic->irq      = smc9000_irq;
523
524    /* Based on PnP ISA map */
525    dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
526    dev->devid.device_id = htons(0x8228);
527
528    return 1;
529
530 out:
531 #ifdef  SMC9000_VERBOSE
532    /* printf("No SMC9000 adapters found\n"); */
533 #endif
534    smc9000_base = 0;
535
536    return (0);
537 }
538
539 static struct isa_driver smc9000_driver __isa_driver = {
540         .type    = NIC_DRIVER,
541         .name    = "SMC9000",
542         .probe   = smc9000_probe,
543         .ioaddrs = 0,
544 };