source: bootcd/isolinux/syslinux-6.03/gpxe/src/drivers/net/3c595.c @ dd1be7c

Last change on this file since dd1be7c was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/*
2* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
3*
4* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
5* All rights reserved.
6* Mar. 14, 2000
7*
8*  This software may be used, modified, copied, distributed, and sold, in
9*  both source and binary form provided that the above copyright and these
10*  terms are retained. Under no circumstances are the authors responsible for
11*  the proper functioning of this software, nor do the authors assume any
12*  responsibility for damages incurred with its use.
13*
14* This code is based on Martin Renters' etherboot-4.4.3 3c509.c and
15* Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
16*
17*  Copyright (C) 1993-1994, David Greenman, Martin Renters.
18*  Copyright (C) 1993-1995, Andres Vega Garcia.
19*  Copyright (C) 1995, Serge Babkin.
20*
21*  Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
22*
23* timlegge      08-24-2003      Add Multicast Support
24*/
25
26FILE_LICENCE ( BSD2 );
27
28/* #define EDEBUG */
29
30#include "etherboot.h"
31#include "nic.h"
32#include <gpxe/pci.h>
33#include <gpxe/ethernet.h>
34#include "3c595.h"
35
36static struct nic_operations t595_operations;
37
38static unsigned short   eth_nic_base;
39static unsigned short   vx_connector, vx_connectors;
40
41static struct connector_entry {
42  int bit;
43  char *name;
44} conn_tab[VX_CONNECTORS] = {
45#define CONNECTOR_UTP   0
46  { 0x08, "utp"},
47#define CONNECTOR_AUI   1
48  { 0x20, "aui"},
49/* dummy */
50  { 0, "???"},
51#define CONNECTOR_BNC   3
52  { 0x10, "bnc"},
53#define CONNECTOR_TX    4
54  { 0x02, "tx"},
55#define CONNECTOR_FX    5
56  { 0x04, "fx"},
57#define CONNECTOR_MII   6
58  { 0x40, "mii"},
59  { 0, "???"}
60};
61
62static void vxgetlink(void);
63static void vxsetlink(void);
64
65/**************************************************************************
66ETH_RESET - Reset adapter
67***************************************************************************/
68static void t595_reset(struct nic *nic)
69{
70        int i;
71
72        /***********************************************************
73                        Reset 3Com 595 card
74        *************************************************************/
75
76        /* stop card */
77        outw(RX_DISABLE, BASE + VX_COMMAND);
78        outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
79        VX_BUSY_WAIT;
80        outw(TX_DISABLE, BASE + VX_COMMAND);
81        outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
82        udelay(8000);
83        outw(RX_RESET, BASE + VX_COMMAND);
84        VX_BUSY_WAIT;
85        outw(TX_RESET, BASE + VX_COMMAND);
86        VX_BUSY_WAIT;
87        outw(C_INTR_LATCH, BASE + VX_COMMAND);
88        outw(SET_RD_0_MASK, BASE + VX_COMMAND);
89        outw(SET_INTR_MASK, BASE + VX_COMMAND);
90        outw(SET_RX_FILTER, BASE + VX_COMMAND);
91
92        /*
93        * initialize card
94        */
95        VX_BUSY_WAIT;
96
97        GO_WINDOW(0);
98
99        /* Disable the card */
100/*      outw(0, BASE + VX_W0_CONFIG_CTRL); */
101
102        /* Configure IRQ to none */
103/*      outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
104
105        /* Enable the card */
106/*      outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
107
108        GO_WINDOW(2);
109
110        /* Reload the ether_addr. */
111        for (i = 0; i < ETH_ALEN; i++)
112                outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
113
114        outw(RX_RESET, BASE + VX_COMMAND);
115        VX_BUSY_WAIT;
116        outw(TX_RESET, BASE + VX_COMMAND);
117        VX_BUSY_WAIT;
118
119        /* Window 1 is operating window */
120        GO_WINDOW(1);
121        for (i = 0; i < 31; i++)
122                inb(BASE + VX_W1_TX_STATUS);
123
124        outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
125                S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
126        outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
127                S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
128
129/*
130 * Attempt to get rid of any stray interrupts that occured during
131 * configuration.  On the i386 this isn't possible because one may
132 * already be queued.  However, a single stray interrupt is
133 * unimportant.
134 */
135
136        outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
137
138        outw(SET_RX_FILTER | FIL_INDIVIDUAL |
139            FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND);
140
141        vxsetlink();
142/*{
143        int i,j;
144        i = CONNECTOR_TX;
145        GO_WINDOW(3);
146        j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
147        outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
148        GO_WINDOW(4);
149        outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
150        GO_WINDOW(1);
151}*/
152
153        /* start tranciever and receiver */
154        outw(RX_ENABLE, BASE + VX_COMMAND);
155        outw(TX_ENABLE, BASE + VX_COMMAND);
156
157}
158
159/**************************************************************************
160ETH_TRANSMIT - Transmit a frame
161***************************************************************************/
162static char padmap[] = {
163        0, 3, 2, 1};
164
165static void t595_transmit(
166struct nic *nic,
167const char *d,                  /* Destination */
168unsigned int t,                 /* Type */
169unsigned int s,                 /* size */
170const char *p)                  /* Packet */
171{
172        register int len;
173        int pad;
174        int status;
175
176#ifdef EDEBUG
177        printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
178#endif
179
180        /* swap bytes of type */
181        t= htons(t);
182
183        len=s+ETH_HLEN; /* actual length of packet */
184        pad = padmap[len & 3];
185
186        /*
187        * The 3c595 automatically pads short packets to minimum ethernet length,
188        * but we drop packets that are too large. Perhaps we should truncate
189        * them instead?
190        */
191        if (len + pad > ETH_FRAME_LEN) {
192                return;
193        }
194
195        /* drop acknowledgements */
196        while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
197                if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
198                        outw(TX_RESET, BASE + VX_COMMAND);
199                        outw(TX_ENABLE, BASE + VX_COMMAND);
200                }
201
202                outb(0x0, BASE + VX_W1_TX_STATUS);
203        }
204
205        while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
206                /* no room in FIFO */
207        }
208
209        outw(len, BASE + VX_W1_TX_PIO_WR_1);
210        outw(0x0, BASE + VX_W1_TX_PIO_WR_1);    /* Second dword meaningless */
211
212        /* write packet */
213        outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
214        outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
215        outw(t, BASE + VX_W1_TX_PIO_WR_1);
216        outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
217        if (s & 1)
218                outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
219
220        while (pad--)
221                outb(0, BASE + VX_W1_TX_PIO_WR_1);      /* Padding */
222
223        /* wait for Tx complete */
224        while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
225                ;
226}
227
228/**************************************************************************
229ETH_POLL - Wait for a frame
230***************************************************************************/
231static int t595_poll(struct nic *nic, int retrieve)
232{
233        /* common variables */
234        /* variables for 3C595 */
235        short status, cst;
236        register short rx_fifo;
237
238        cst=inw(BASE + VX_STATUS);
239
240#ifdef EDEBUG
241        if(cst & 0x1FFF)
242                printf("-%hX-",cst);
243#endif
244
245        if( (cst & S_RX_COMPLETE)==0 ) {
246                /* acknowledge  everything */
247                outw(ACK_INTR | cst, BASE + VX_COMMAND);
248                outw(C_INTR_LATCH, BASE + VX_COMMAND);
249
250                return 0;
251        }
252
253        status = inw(BASE + VX_W1_RX_STATUS);
254#ifdef EDEBUG
255        printf("*%hX*",status);
256#endif
257
258        if (status & ERR_RX) {
259                outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
260                return 0;
261        }
262
263        rx_fifo = status & RX_BYTES_MASK;
264        if (rx_fifo==0)
265                return 0;
266
267        if ( ! retrieve ) return 1;
268
269                /* read packet */
270#ifdef EDEBUG
271        printf("[l=%d",rx_fifo);
272#endif
273        insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
274        if(rx_fifo & 1)
275                nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
276        nic->packetlen=rx_fifo;
277
278        while(1) {
279                status = inw(BASE + VX_W1_RX_STATUS);
280#ifdef EDEBUG
281                printf("*%hX*",status);
282#endif
283                rx_fifo = status & RX_BYTES_MASK;
284
285                if(rx_fifo>0) {
286                        insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
287                        if(rx_fifo & 1)
288                                nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
289                        nic->packetlen+=rx_fifo;
290#ifdef EDEBUG
291                        printf("+%d",rx_fifo);
292#endif
293                }
294                if(( status & RX_INCOMPLETE )==0) {
295#ifdef EDEBUG
296                        printf("=%d",nic->packetlen);
297#endif
298                        break;
299                }
300                udelay(1000);
301        }
302
303        /* acknowledge reception of packet */
304        outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
305        while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
306#ifdef EDEBUG
307{
308        unsigned short type = 0;        /* used by EDEBUG */
309        type = (nic->packet[12]<<8) | nic->packet[13];
310        if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
311            nic->packet[5] == 0xFF*ETH_ALEN)
312                printf(",t=%hX,b]",type);
313        else
314                printf(",t=%hX]",type);
315}
316#endif
317        return 1;
318}
319
320
321/*************************************************************************
322        3Com 595 - specific routines
323**************************************************************************/
324
325static int
326eeprom_rdy()
327{
328        int i;
329
330        for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
331                udelay(1000);
332        if (i >= MAX_EEPROMBUSY) {
333                /* printf("3c595: eeprom failed to come ready.\n"); */
334                printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
335                return (0);
336        }
337        return (1);
338}
339
340/*
341 * get_e: gets a 16 bits word from the EEPROM. we must have set the window
342 * before
343 */
344static int
345get_e(offset)
346int offset;
347{
348        if (!eeprom_rdy())
349                return (0xffff);
350        outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
351        if (!eeprom_rdy())
352                return (0xffff);
353        return (inw(BASE + VX_W0_EEPROM_DATA));
354}
355
356static void           
357vxgetlink(void)
358{
359    int n, k;
360
361    GO_WINDOW(3);
362    vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
363    for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
364      if (vx_connectors & conn_tab[k].bit) {
365        if (n > 0) {
366          printf("/");
367        }
368        printf("%s", conn_tab[k].name );
369        n++;
370      }
371    }
372    if (vx_connectors == 0) {
373        printf("no connectors!");
374        return;
375    }
376    GO_WINDOW(3);
377    vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
378                        & INTERNAL_CONNECTOR_MASK)
379                        >> INTERNAL_CONNECTOR_BITS;
380    if (vx_connector & 0x10) {
381        vx_connector &= 0x0f;
382        printf("[*%s*]", conn_tab[vx_connector].name);
383        printf(": disable 'auto select' with DOS util!");
384    } else {
385        printf("[*%s*]", conn_tab[vx_connector].name);
386    }
387}
388
389static void           
390vxsetlink(void)
391{       
392    int i, j;
393    char *reason, *warning;
394    static char prev_conn = -1;
395
396    if (prev_conn == -1) {
397        prev_conn = vx_connector;
398    }
399
400    i = vx_connector;       /* default in EEPROM */
401    reason = "default";
402    warning = 0;
403
404    if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
405        warning = "strange connector type in EEPROM.";
406        reason = "forced";
407        i = CONNECTOR_UTP;
408    }
409
410        if (warning != 0) {
411            printf("warning: %s\n", warning);
412        }
413        printf("selected %s. (%s)\n", conn_tab[i].name, reason);
414
415    /* Set the selected connector. */
416    GO_WINDOW(3);
417    j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
418    outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
419
420    /* First, disable all. */
421    outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
422    udelay(8000);
423    GO_WINDOW(4);
424    outw(0, BASE + VX_W4_MEDIA_TYPE);
425
426    /* Second, enable the selected one. */
427    switch(i) {
428      case CONNECTOR_UTP:
429        GO_WINDOW(4);
430        outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
431        break;
432      case CONNECTOR_BNC:
433        outw(START_TRANSCEIVER,BASE + VX_COMMAND);
434        udelay(8000);
435        break;
436      case CONNECTOR_TX:
437      case CONNECTOR_FX:
438        GO_WINDOW(4);
439        outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
440        break;
441      default:  /* AUI and MII fall here */
442        break;
443    }
444    GO_WINDOW(1);
445}
446
447static void t595_disable ( struct nic *nic ) {
448
449        t595_reset(nic);
450
451        outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
452        udelay(8000);
453        GO_WINDOW(4);
454        outw(0, BASE + VX_W4_MEDIA_TYPE);
455        GO_WINDOW(1);
456}
457
458static void t595_irq(struct nic *nic __unused, irq_action_t action __unused)
459{
460  switch ( action ) {
461  case DISABLE :
462    break;
463  case ENABLE :
464    break;
465  case FORCE :
466    break;
467  }
468}
469
470/**************************************************************************
471ETH_PROBE - Look for an adapter
472***************************************************************************/
473static int t595_probe ( struct nic *nic, struct pci_device *pci ) {
474
475        int i;
476        unsigned short *p;
477
478        if (pci->ioaddr == 0)
479                return 0;
480        eth_nic_base = pci->ioaddr;
481
482        nic->irqno  = 0;
483        nic->ioaddr = pci->ioaddr;
484
485        GO_WINDOW(0);
486        outw(GLOBAL_RESET, BASE + VX_COMMAND);
487        VX_BUSY_WAIT;
488
489        vxgetlink();
490
491/*
492        printf("\nEEPROM:");
493        for (i = 0; i < (EEPROMSIZE/2); i++) {
494          printf("%hX:", get_e(i));
495        }
496        printf("\n");
497*/
498        /*
499        * Read the station address from the eeprom
500        */
501        p = (unsigned short *) nic->node_addr;
502        for (i = 0; i < 3; i++) {
503                GO_WINDOW(0);
504                p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
505                GO_WINDOW(2);
506                outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
507        }
508
509        DBG ( "Ethernet address: %s\n", eth_ntoa (nic->node_addr) );
510
511        t595_reset(nic);
512        nic->nic_op     = &t595_operations;
513        return 1;
514
515}
516
517static struct nic_operations t595_operations = {
518        .connect        = dummy_connect,
519        .poll           = t595_poll,
520        .transmit       = t595_transmit,
521        .irq            = t595_irq,
522
523};
524
525static struct pci_device_id t595_nics[] = {
526PCI_ROM(0x10b7, 0x5900, "3c590",           "3Com590", 0),               /* Vortex 10Mbps */
527PCI_ROM(0x10b7, 0x5950, "3c595",           "3Com595", 0),               /* Vortex 100baseTx */
528PCI_ROM(0x10b7, 0x5951, "3c595-1",         "3Com595", 0),               /* Vortex 100baseT4 */
529PCI_ROM(0x10b7, 0x5952, "3c595-2",         "3Com595", 0),               /* Vortex 100base-MII */
530PCI_ROM(0x10b7, 0x9000, "3c900-tpo",       "3Com900-TPO", 0),   /* 10 Base TPO */
531PCI_ROM(0x10b7, 0x9001, "3c900-t4",        "3Com900-Combo", 0), /* 10/100 T4 */
532PCI_ROM(0x10b7, 0x9004, "3c900b-tpo",      "3Com900B-TPO", 0),  /* 10 Base TPO */
533PCI_ROM(0x10b7, 0x9005, "3c900b-combo",    "3Com900B-Combo", 0),        /* 10 Base Combo */
534PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2",     "3Com900B-2/T", 0),  /* 10 Base TP and Base2 */
535PCI_ROM(0x10b7, 0x900a, "3c900b-fl",       "3Com900B-FL", 0),   /* 10 Base F */
536PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone", 0),       /* Cyclone */
537PCI_ROM(0x10b7, 0x9805, "3c9805-1",        "3Com9805", 0),              /* Dual Port Server Cyclone */
538PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1",  "3CSOHO100-TX", 0),  /* Hurricane */
539PCI_ROM(0x10b7, 0x4500, "3c450-1",         "3Com450 HomePNA Tornado", 0),
540};
541
542PCI_DRIVER ( t595_driver, t595_nics, PCI_NO_CLASS );
543
544DRIVER ( "3C595", nic_driver, pci_driver, t595_driver,
545         t595_probe, t595_disable );
546
547/*
548 * Local variables:
549 *  c-basic-offset: 8
550 *  c-indent-level: 8
551 *  tab-width: 8
552 * End:
553 */
Note: See TracBrowser for help on using the repository browser.