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 FILE_LICENCE ( BSD2 );
26 
27 /* #define EDEBUG */
28 
29 #include <ipxe/ethernet.h>
30 #include "etherboot.h"
31 #include "nic.h"
32 #include <ipxe/isa.h>
33 #include "3c509.h"
34 
35 static enum { none, bnc, utp } connector = none;	/* for 3C509 */
36 
37 /**************************************************************************
38 ETH_RESET - Reset adapter
39 ***************************************************************************/
t5x9_disable(struct nic * nic)40 void 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 
t509_enable(struct nic * nic)71 static 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 		mdelay(2000);	/* 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 
t509_reset(struct nic * nic)124 static void t509_reset ( struct nic *nic ) {
125 	t5x9_disable ( nic );
126 	t509_enable ( nic );
127 }
128 
129 /**************************************************************************
130 ETH_TRANSMIT - Transmit a frame
131 ***************************************************************************/
132 static char padmap[] = {
133 	0, 3, 2, 1};
134 
t509_transmit(struct nic * nic,const char * d,unsigned int t,unsigned int s,const char * p)135 static void t509_transmit(
136 struct nic *nic,
137 const char *d,			/* Destination */
138 unsigned int t,			/* Type */
139 unsigned int s,			/* size */
140 const 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 /**************************************************************************
197 ETH_POLL - Wait for a frame
198 ***************************************************************************/
t509_poll(struct nic * nic,int retrieve)199 static 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 /**************************************************************************
288 ETH_IRQ - interrupt handling
289 ***************************************************************************/
t509_irq(struct nic * nic __unused,irq_action_t action __unused)290 static 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 
eeprom_rdy(uint16_t ioaddr)306 static 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  */
get_e(uint16_t ioaddr,int offset)322 static 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 
332 static struct nic_operations t509_operations = {
333 	.connect	= dummy_connect,
334 	.poll		= t509_poll,
335 	.transmit	= t509_transmit,
336 	.irq		= t509_irq,
337 };
338 
339 /**************************************************************************
340 ETH_PROBE - Look for an adapter
341 ***************************************************************************/
t5x9_probe(struct nic * nic,uint16_t prod_id_check,uint16_t prod_id_mask)342 int 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  */
417