xref: /netbsd/sys/arch/i386/stand/lib/netif/elink3.c (revision bf9ec67e)
1 /*	$NetBSD: elink3.c,v 1.3 1998/02/16 11:26:36 drochner Exp $	*/
2 
3 /* stripped down from freebsd:sys/i386/netboot/3c509.c */
4 
5 /**************************************************************************
6 NETBOOT -  BOOTP/TFTP Bootstrap Program
7 
8 Author: Martin Renters.
9   Date: Mar 22 1995
10 
11  This code is based heavily on David Greenman's if_ed.c driver and
12   Andres Vega Garcia's if_ep.c driver.
13 
14  Copyright (C) 1993-1994, David Greenman, Martin Renters.
15  Copyright (C) 1993-1995, Andres Vega Garcia.
16  Copyright (C) 1995, Serge Babkin.
17   This software may be used, modified, copied, distributed, and sold, in
18   both source and binary form provided that the above copyright and these
19   terms are retained. Under no circumstances are the authors responsible for
20   the proper functioning of this software, nor do the authors assume any
21   responsibility for damages incurred with its use.
22 
23 3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
24 
25 3c509.c,v 1.2 1995/05/30 07:58:52 rgrimes Exp
26 
27 ***************************************************************************/
28 
29 #include <sys/types.h>
30 #include <machine/pio.h>
31 
32 #include <lib/libsa/stand.h>
33 
34 #include <libi386.h>
35 
36 #include "etherdrv.h"
37 #include "3c509.h"
38 
39 extern unsigned short eth_base;
40 
41 extern u_char eth_myaddr[6];
42 
43 void epstop()
44 {
45 	/* stop card */
46 	outw(BASE + EP_COMMAND, RX_DISABLE);
47 	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
48 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
49 
50 	outw(BASE + EP_COMMAND, TX_DISABLE);
51 	outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
52 
53 	outw(BASE + EP_COMMAND, RX_RESET);
54 	outw(BASE + EP_COMMAND, TX_RESET);
55 
56 	outw(BASE + EP_COMMAND, C_INTR_LATCH);
57 	outw(BASE + EP_COMMAND, SET_RD_0_MASK);
58 	outw(BASE + EP_COMMAND, SET_INTR_MASK);
59 	outw(BASE + EP_COMMAND, SET_RX_FILTER);
60 }
61 
62 void EtherStop()
63 {
64 	epstop();
65 	outw(BASE + EP_COMMAND, GLOBAL_RESET);
66 	delay(100000);
67 }
68 
69 /**************************************************************************
70 ETH_RESET - Reset adapter
71 ***************************************************************************/
72 void epreset()
73 {
74 	int i;
75 
76 	/***********************************************************
77 			Reset 3Com 509 card
78 	*************************************************************/
79 
80 	epstop();
81 
82 	/*
83 	 * initialize card
84 	*/
85 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
86 
87 	GO_WINDOW(0);
88 
89 	/* Disable the card */
90 	outw(BASE + EP_W0_CONFIG_CTRL, 0);
91 
92 	/* Configure IRQ to none */
93 	outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(0));
94 
95 	/* Enable the card */
96 	outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
97 
98 	GO_WINDOW(2);
99 
100 	/* Reload the ether_addr. */
101 	for (i = 0; i < 6; i++)
102 		outb(BASE + EP_W2_ADDR_0 + i, eth_myaddr[i]);
103 
104 	outw(BASE + EP_COMMAND, RX_RESET);
105 	outw(BASE + EP_COMMAND, TX_RESET);
106 
107 	/* Window 1 is operating window */
108 	GO_WINDOW(1);
109 	for (i = 0; i < 31; i++)
110 		inb(BASE + EP_W1_TX_STATUS);
111 
112 	/* get rid of stray intr's */
113 	outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
114 
115 	outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
116 
117 	outw(BASE + EP_COMMAND, SET_INTR_MASK);
118 
119 	outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
120 	    FIL_BRDCST);
121 
122 	/* configure BNC */
123 	if(ether_medium == ETHERMEDIUM_BNC) {
124 		outw(BASE + EP_COMMAND, START_TRANSCEIVER);
125 		delay(1000);
126 		}
127 	/* configure UTP */
128 	if(ether_medium == ETHERMEDIUM_UTP) {
129 		GO_WINDOW(4);
130 		outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
131 		GO_WINDOW(1);
132 		}
133 
134 	/* start tranciever and receiver */
135 	outw(BASE + EP_COMMAND, RX_ENABLE);
136 	outw(BASE + EP_COMMAND, TX_ENABLE);
137 
138 	/* set early threshold for minimal packet length */
139 	outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 64);
140 
141 	outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16);
142 }
143 
144 /**************************************************************************
145 ETH_TRANSMIT - Transmit a frame
146 ***************************************************************************/
147 static const char padmap[] = {
148 	0, 3, 2, 1};
149 
150 int EtherSend(pkt, len)
151     char *pkt;
152     int len;
153 {
154 	int pad;
155 	int status;
156 
157 #ifdef EDEBUG
158 	printf("{l=%d}", len);
159 #endif
160 
161 	pad = padmap[len & 3];
162 
163 	/*
164 	* The 3c509 automatically pads short packets to minimum ethernet length,
165 	* but we drop packets that are too large. Perhaps we should truncate
166 	* them instead?
167 	*/
168 	if (len + pad > ETHER_MAX_LEN) {
169 		return -1;
170 	}
171 
172 	/* drop acknowledgements */
173 	while(( status=inb(BASE + EP_W1_TX_STATUS) )& TXS_COMPLETE ) {
174 		if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
175 			outw(BASE + EP_COMMAND, TX_RESET);
176 			outw(BASE + EP_COMMAND, TX_ENABLE);
177 		}
178 
179 		outb(BASE + EP_W1_TX_STATUS, 0x0);
180 	}
181 
182 	while (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
183 		/* no room in FIFO */
184 	}
185 
186 	outw(BASE + EP_W1_TX_PIO_WR_1, len);
187 	outw(BASE + EP_W1_TX_PIO_WR_1, 0x0);	/* Second dword meaningless */
188 
189 	/* write packet */
190 	outsw(BASE + EP_W1_TX_PIO_WR_1, pkt, len / 2);
191 	if (len & 1)
192 		outb(BASE + EP_W1_TX_PIO_WR_1, *(pkt + len - 1));
193 
194 	while (pad--)
195 		outb(BASE + EP_W1_TX_PIO_WR_1, 0);	/* Padding */
196 
197 	/* timeout after sending */
198 	delay(1000);
199 	return(len);
200 }
201 
202 /**************************************************************************
203 ETH_POLL - Wait for a frame
204 ***************************************************************************/
205 int EtherReceive(pkt, maxlen)
206 char *pkt;
207 int maxlen;
208 {
209 	/* common variables */
210 	int len;
211 	/* variables for 3C509 */
212 	short status, cst;
213 	register short rx_fifo;
214 
215 	cst=inw(BASE + EP_STATUS);
216 
217 #ifdef EDEBUG
218 	if(cst & 0x1FFF)
219 		printf("-%x-",cst);
220 #endif
221 
222 	if( (cst & (S_RX_COMPLETE|S_RX_EARLY) )==0 ) {
223 		/* acknowledge  everything */
224 		outw(BASE + EP_COMMAND, ACK_INTR| (cst & S_5_INTS));
225 		outw(BASE + EP_COMMAND, C_INTR_LATCH);
226 
227 		return 0;
228 	}
229 
230 	status = inw(BASE + EP_W1_RX_STATUS);
231 #ifdef EDEBUG
232 	printf("*%x*",status);
233 #endif
234 
235 	if (status & ERR_RX) {
236 		outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
237 		return 0;
238 	}
239 
240 	rx_fifo = status & RX_BYTES_MASK;
241 	if (rx_fifo==0)
242 		return 0;
243 
244 	if (rx_fifo > maxlen) goto zulang;
245 
246 		/* read packet */
247 #ifdef EDEBUG
248 	printf("[l=%d",rx_fifo);
249 #endif
250 	insw(BASE + EP_W1_RX_PIO_RD_1, pkt, rx_fifo / 2);
251 	if(rx_fifo & 1)
252 		pkt[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
253 	len=rx_fifo;
254 
255 	while(1) {
256 		status = inw(BASE + EP_W1_RX_STATUS);
257 #ifdef EDEBUG
258 		printf("*%x*",status);
259 #endif
260 		rx_fifo = status & RX_BYTES_MASK;
261 
262 		if(rx_fifo>0) {
263 		  if((len + rx_fifo) > maxlen) goto zulang;
264 
265 			insw(BASE + EP_W1_RX_PIO_RD_1, pkt+len, rx_fifo / 2);
266 			if(rx_fifo & 1)
267 				pkt[len+rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
268 			len+=rx_fifo;
269 #ifdef EDEBUG
270 			printf("+%d",rx_fifo);
271 #endif
272 		}
273 
274 		if(( status & RX_INCOMPLETE )==0) {
275 #ifdef EDEBUG
276 			printf("=%d",len);
277 #endif
278 			break;
279 		}
280 
281 		delay(1000);
282 	}
283 
284 	/* acknowledge reception of packet */
285 	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
286 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
287 
288 	return len;
289 
290 zulang:
291 	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
292 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
293 	return 0;
294 }
295 
296 /*************************************************************************
297 	3Com 509 - specific routines
298 **************************************************************************/
299 
300 static int
301 eeprom_rdy()
302 {
303 	int i;
304 
305 	for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
306 	if (i >= MAX_EEPROMBUSY) {
307 		printf("3c509: eeprom failed to come ready.\r\n");
308 		return (0);
309 	}
310 	return (1);
311 }
312 
313 /*
314  * get_e: gets a 16 bits word from the EEPROM. we must have set the window
315  * before
316  */
317 int
318 ep_get_e(offset)
319 int offset;
320 {
321 	if (!eeprom_rdy())
322 		return (0xffff);
323 	outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset);
324 	if (!eeprom_rdy())
325 		return (0xffff);
326 	return (inw(IS_BASE + EP_W0_EEPROM_DATA));
327 }
328