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