source: bootcd/isolinux/syslinux-6.03/gpxe/src/drivers/net/3c5x9.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: 10.6 KB
Line 
1/**************************************************************************
2ETHERBOOT -  BOOTP/TFTP Bootstrap Program
3
4Author: Martin Renters.
5  Date: Mar 22 1995
6
7 This code is based heavily on David Greenman's if_ed.c driver and
8  Andres Vega Garcia's if_ep.c driver.
9
10 Copyright (C) 1993-1994, David Greenman, Martin Renters.
11 Copyright (C) 1993-1995, Andres Vega Garcia.
12 Copyright (C) 1995, Serge Babkin.
13  This software may be used, modified, copied, distributed, and sold, in
14  both source and binary form provided that the above copyright and these
15  terms are retained. Under no circumstances are the authors responsible for
16  the proper functioning of this software, nor do the authors assume any
17  responsibility for damages incurred with its use.
18
193c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
20
21$Id$
22
23***************************************************************************/
24
25FILE_LICENCE ( BSD2 );
26
27/* #define EDEBUG */
28
29#include <gpxe/ethernet.h>
30#include "etherboot.h"
31#include "nic.h"
32#include <gpxe/isa.h>
33#include "3c509.h"
34
35static enum { none, bnc, utp } connector = none;        /* for 3C509 */
36
37/**************************************************************************
38ETH_RESET - Reset adapter
39***************************************************************************/
40void t5x9_disable ( struct nic *nic ) {
41        /* stop card */
42        outw(RX_DISABLE, nic->ioaddr + EP_COMMAND);
43        outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
44        while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
45                ;
46        outw(TX_DISABLE, nic->ioaddr + EP_COMMAND);
47        outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
48        udelay(1000);
49        outw(RX_RESET, nic->ioaddr + EP_COMMAND);
50        outw(TX_RESET, nic->ioaddr + EP_COMMAND);
51        outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
52        outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND);
53        outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
54        outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND);
55
56        /*
57         * wait for reset to complete
58         */
59        while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
60                ;
61
62        GO_WINDOW(nic->ioaddr,0);
63
64        /* Disable the card */
65        outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL);
66
67        /* Configure IRQ to none */
68        outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG);
69}
70
71static void t509_enable ( struct nic *nic ) {
72        int i;
73
74        /* Enable the card */
75        GO_WINDOW(nic->ioaddr,0);
76        outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL);
77
78        GO_WINDOW(nic->ioaddr,2);
79
80        /* Reload the ether_addr. */
81        for (i = 0; i < ETH_ALEN; i++)
82                outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i);
83
84        outw(RX_RESET, nic->ioaddr + EP_COMMAND);
85        outw(TX_RESET, nic->ioaddr + EP_COMMAND);
86
87        /* Window 1 is operating window */
88        GO_WINDOW(nic->ioaddr,1);
89        for (i = 0; i < 31; i++)
90                inb(nic->ioaddr + EP_W1_TX_STATUS);
91
92        /* get rid of stray intr's */
93        outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND);
94
95        outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND);
96
97        outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
98
99        outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST,
100             nic->ioaddr + EP_COMMAND);
101
102        /* configure BNC */
103        if (connector == bnc) {
104                outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
105                udelay(1000);
106        }
107        /* configure UTP */
108        else if (connector == utp) {
109                GO_WINDOW(nic->ioaddr,4);
110                outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE);
111                sleep(2);       /* Give time for media to negotiate */
112                GO_WINDOW(nic->ioaddr,1);
113        }
114
115        /* start transceiver and receiver */
116        outw(RX_ENABLE, nic->ioaddr + EP_COMMAND);
117        outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
118
119        /* set early threshold for minimal packet length */
120        outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND);
121        outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND);
122}
123
124static void t509_reset ( struct nic *nic ) {
125        t5x9_disable ( nic );
126        t509_enable ( nic );
127}   
128
129/**************************************************************************
130ETH_TRANSMIT - Transmit a frame
131***************************************************************************/
132static char padmap[] = {
133        0, 3, 2, 1};
134
135static void t509_transmit(
136struct nic *nic,
137const char *d,                  /* Destination */
138unsigned int t,                 /* Type */
139unsigned int s,                 /* size */
140const char *p)                  /* Packet */
141{
142        register unsigned int len;
143        int pad;
144        int status;
145
146#ifdef  EDEBUG
147        printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
148#endif
149
150        /* swap bytes of type */
151        t= htons(t);
152
153        len=s+ETH_HLEN; /* actual length of packet */
154        pad = padmap[len & 3];
155
156        /*
157        * The 3c509 automatically pads short packets to minimum ethernet length,
158        * but we drop packets that are too large. Perhaps we should truncate
159        * them instead?
160        */
161        if (len + pad > ETH_FRAME_LEN) {
162                return;
163        }
164
165        /* drop acknowledgements */
166        while ((status=inb(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
167                if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
168                        outw(TX_RESET, nic->ioaddr + EP_COMMAND);
169                        outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
170                }
171                outb(0x0, nic->ioaddr + EP_W1_TX_STATUS);
172        }
173
174        while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
175                ; /* no room in FIFO */
176
177        outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1);
178        outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1);     /* Second dword meaningless */
179
180        /* write packet */
181        outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
182        outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
183        outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1);
184        outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2);
185        if (s & 1)
186                outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1);
187
188        while (pad--)
189                outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1);       /* Padding */
190
191        /* wait for Tx complete */
192        while((inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
193                ;
194}
195
196/**************************************************************************
197ETH_POLL - Wait for a frame
198***************************************************************************/
199static int t509_poll(struct nic *nic, int retrieve)
200{
201        /* common variables */
202        /* variables for 3C509 */
203        short status, cst;
204        register short rx_fifo;
205
206        cst=inw(nic->ioaddr + EP_STATUS);
207
208#ifdef  EDEBUG
209        if(cst & 0x1FFF)
210                printf("-%hX-",cst);
211#endif
212
213        if( (cst & S_RX_COMPLETE)==0 ) {
214                /* acknowledge  everything */
215                outw(ACK_INTR| (cst & S_5_INTS), nic->ioaddr + EP_COMMAND);
216                outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
217
218                return 0;
219        }
220
221        status = inw(nic->ioaddr + EP_W1_RX_STATUS);
222#ifdef  EDEBUG
223        printf("*%hX*",status);
224#endif
225
226        if (status & ERR_RX) {
227                outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
228                return 0;
229        }
230
231        rx_fifo = status & RX_BYTES_MASK;
232        if (rx_fifo==0)
233                return 0;
234
235        if ( ! retrieve ) return 1;
236
237                /* read packet */
238#ifdef  EDEBUG
239        printf("[l=%d",rx_fifo);
240#endif
241        insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
242        if(rx_fifo & 1)
243                nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
244        nic->packetlen=rx_fifo;
245
246        while(1) {
247                status = inw(nic->ioaddr + EP_W1_RX_STATUS);
248#ifdef  EDEBUG
249                printf("*%hX*",status);
250#endif
251                rx_fifo = status & RX_BYTES_MASK;
252                if(rx_fifo>0) {
253                        insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
254                        if(rx_fifo & 1)
255                                nic->packet[nic->packetlen+rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
256                        nic->packetlen+=rx_fifo;
257#ifdef  EDEBUG
258                        printf("+%d",rx_fifo);
259#endif
260                }
261                if(( status & RX_INCOMPLETE )==0) {
262#ifdef  EDEBUG
263                        printf("=%d",nic->packetlen);
264#endif
265                        break;
266                }
267                udelay(1000);   /* if incomplete wait 1 ms */
268        }
269        /* acknowledge reception of packet */
270        outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
271        while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
272                ;
273#ifdef  EDEBUG
274{
275        unsigned short type = 0;        /* used by EDEBUG */
276        type = (nic->packet[12]<<8) | nic->packet[13];
277        if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
278            nic->packet[5] == 0xFF*ETH_ALEN)
279                printf(",t=%hX,b]",type);
280        else
281                printf(",t=%hX]",type);
282}
283#endif
284        return (1);
285}
286
287/**************************************************************************
288ETH_IRQ - interrupt handling
289***************************************************************************/
290static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
291{
292  switch ( action ) {
293  case DISABLE :
294    break;
295  case ENABLE :
296    break;
297  case FORCE :
298    break;
299  }
300}
301
302/*************************************************************************
303        3Com 509 - specific routines
304**************************************************************************/
305
306static int eeprom_rdy ( uint16_t ioaddr ) {
307        int i;
308
309        for (i = 0; is_eeprom_busy(ioaddr) && i < MAX_EEPROMBUSY; i++);
310        if (i >= MAX_EEPROMBUSY) {
311                /* printf("3c509: eeprom failed to come ready.\n"); */
312                /* memory in EPROM is tight */
313                /* printf("3c509: eeprom busy.\n"); */
314                return (0);
315        }
316        return (1);
317}
318
319/*
320 * get_e: gets a 16 bits word from the EEPROM.
321 */
322static int get_e ( uint16_t ioaddr, int offset ) {
323        GO_WINDOW(ioaddr,0);
324        if (!eeprom_rdy(ioaddr))
325                return (0xffff);
326        outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND);
327        if (!eeprom_rdy(ioaddr))
328                return (0xffff);
329        return (inw(ioaddr + EP_W0_EEPROM_DATA));
330}
331
332static struct nic_operations t509_operations = {
333        .connect        = dummy_connect,
334        .poll           = t509_poll,
335        .transmit       = t509_transmit,
336        .irq            = t509_irq,
337};
338
339/**************************************************************************
340ETH_PROBE - Look for an adapter
341***************************************************************************/
342int t5x9_probe ( struct nic *nic,
343                 uint16_t prod_id_check, uint16_t prod_id_mask ) {
344        uint16_t prod_id;
345        int i,j;
346        unsigned short *p;
347       
348        /* Check product ID */
349        prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID );
350        if ( ( prod_id & prod_id_mask ) != prod_id_check ) {
351                printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n",
352                         prod_id, prod_id_mask, prod_id_check );
353                return 0;
354        }
355
356        /* test for presence of connectors */
357        GO_WINDOW(nic->ioaddr,0);
358        i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL);
359        j = (inw(nic->ioaddr + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
360
361        switch(j) {
362        case 0:
363                if (i & IS_UTP) {
364                        printf("10baseT\n");
365                        connector = utp;
366                } else {
367                        printf("10baseT not present\n");
368                        return 0;
369                }
370                break;
371        case 1:
372                if (i & IS_AUI) {
373                        printf("10base5\n");
374                } else {
375                        printf("10base5 not present\n");
376                        return 0;
377                }
378                break;
379        case 3:
380                if (i & IS_BNC) {
381                        printf("10base2\n");
382                        connector = bnc;
383                } else {
384                        printf("10base2 not present\n");
385                        return 0;
386                }
387                break;
388        default:
389                printf("unknown connector\n");
390                return 0;
391        }
392
393        /*
394        * Read the station address from the eeprom
395        */
396        p = (unsigned short *) nic->node_addr;
397        for (i = 0; i < ETH_ALEN / 2; i++) {
398                p[i] = htons(get_e(nic->ioaddr,i));
399                GO_WINDOW(nic->ioaddr,2);
400                outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2));
401        }
402
403        DBG ( "Ethernet Address: %s\n", eth_ntoa ( nic->node_addr ) );
404
405        t509_reset(nic);
406
407        nic->nic_op = &t509_operations;
408        return 1;
409
410}
411
412/*
413 * Local variables:
414 *  c-basic-offset: 8
415 * End:
416 */
Note: See TracBrowser for help on using the repository browser.