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