xref: /netbsd/sys/arch/i386/stand/lib/netif/elink3.c (revision 6550d01e)
1 /*	$NetBSD: elink3.c,v 1.4 2008/12/14 18:46:33 christos 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
44 epstop(void)
45 {
46 
47 	/* stop card */
48 	outw(BASE + EP_COMMAND, RX_DISABLE);
49 	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
50 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
51 
52 	outw(BASE + EP_COMMAND, TX_DISABLE);
53 	outw(BASE + EP_COMMAND, STOP_TRANSCEIVER);
54 
55 	outw(BASE + EP_COMMAND, RX_RESET);
56 	outw(BASE + EP_COMMAND, TX_RESET);
57 
58 	outw(BASE + EP_COMMAND, C_INTR_LATCH);
59 	outw(BASE + EP_COMMAND, SET_RD_0_MASK);
60 	outw(BASE + EP_COMMAND, SET_INTR_MASK);
61 	outw(BASE + EP_COMMAND, SET_RX_FILTER);
62 }
63 
64 void
65 EtherStop(void)
66 {
67 
68 	epstop();
69 	outw(BASE + EP_COMMAND, GLOBAL_RESET);
70 	delay(100000);
71 }
72 
73 /**************************************************************************
74 ETH_RESET - Reset adapter
75 ***************************************************************************/
76 void
77 epreset(void)
78 {
79 	int i;
80 
81 	/***********************************************************
82 			Reset 3Com 509 card
83 	*************************************************************/
84 
85 	epstop();
86 
87 	/*
88 	 * initialize card
89 	*/
90 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
91 		continue;
92 
93 	GO_WINDOW(0);
94 
95 	/* Disable the card */
96 	outw(BASE + EP_W0_CONFIG_CTRL, 0);
97 
98 	/* Configure IRQ to none */
99 	outw(BASE + EP_W0_RESOURCE_CFG, SET_IRQ(0));
100 
101 	/* Enable the card */
102 	outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
103 
104 	GO_WINDOW(2);
105 
106 	/* Reload the ether_addr. */
107 	for (i = 0; i < 6; i++)
108 		outb(BASE + EP_W2_ADDR_0 + i, eth_myaddr[i]);
109 
110 	outw(BASE + EP_COMMAND, RX_RESET);
111 	outw(BASE + EP_COMMAND, TX_RESET);
112 
113 	/* Window 1 is operating window */
114 	GO_WINDOW(1);
115 	for (i = 0; i < 31; i++)
116 		inb(BASE + EP_W1_TX_STATUS);
117 
118 	/* get rid of stray intr's */
119 	outw(BASE + EP_COMMAND, ACK_INTR | 0xff);
120 
121 	outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
122 
123 	outw(BASE + EP_COMMAND, SET_INTR_MASK);
124 
125 	outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
126 	    FIL_BRDCST);
127 
128 	/* configure BNC */
129 	if (ether_medium == ETHERMEDIUM_BNC) {
130 		outw(BASE + EP_COMMAND, START_TRANSCEIVER);
131 		delay(1000);
132 	}
133 	/* configure UTP */
134 	if (ether_medium == ETHERMEDIUM_UTP) {
135 		GO_WINDOW(4);
136 		outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP);
137 		GO_WINDOW(1);
138 	}
139 
140 	/* start tranciever and receiver */
141 	outw(BASE + EP_COMMAND, RX_ENABLE);
142 	outw(BASE + EP_COMMAND, TX_ENABLE);
143 
144 	/* set early threshold for minimal packet length */
145 	outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | 64);
146 
147 	outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16);
148 }
149 
150 /**************************************************************************
151 ETH_TRANSMIT - Transmit a frame
152 ***************************************************************************/
153 static const char padmap[] = {
154 	0, 3, 2, 1};
155 
156 int
157 EtherSend(char *pkt, int len)
158 {
159 	int pad;
160 	int status;
161 
162 #ifdef EDEBUG
163 	printf("{l=%d}", len);
164 #endif
165 
166 	pad = padmap[len & 3];
167 
168 	/*
169 	* The 3c509 automatically pads short packets to minimum ethernet length,
170 	* but we drop packets that are too large. Perhaps we should truncate
171 	* them instead?
172 	*/
173 	if (len + pad > ETHER_MAX_LEN) {
174 		return -1;
175 	}
176 
177 	/* drop acknowledgements */
178 	while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
179 		if (status & (TXS_UNDERRUN | TXS_MAX_COLLISION |
180 			TXS_STATUS_OVERFLOW)) {
181 			outw(BASE + EP_COMMAND, TX_RESET);
182 			outw(BASE + EP_COMMAND, TX_ENABLE);
183 		}
184 
185 		outb(BASE + EP_W1_TX_STATUS, 0x0);
186 	}
187 
188 	while (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) {
189 		/* no room in FIFO */
190 		continue;
191 	}
192 
193 	outw(BASE + EP_W1_TX_PIO_WR_1, len);
194 	outw(BASE + EP_W1_TX_PIO_WR_1, 0x0);	/* Second dword meaningless */
195 
196 	/* write packet */
197 	outsw(BASE + EP_W1_TX_PIO_WR_1, pkt, len / 2);
198 	if (len & 1)
199 		outb(BASE + EP_W1_TX_PIO_WR_1, *(pkt + len - 1));
200 
201 	while (pad--)
202 		outb(BASE + EP_W1_TX_PIO_WR_1, 0);	/* Padding */
203 
204 	/* timeout after sending */
205 	delay(1000);
206 	return len;
207 }
208 
209 /**************************************************************************
210 ETH_POLL - Wait for a frame
211 ***************************************************************************/
212 int
213 EtherReceive(char *pkt, int maxlen)
214 {
215 	/* common variables */
216 	int len;
217 	/* variables for 3C509 */
218 	short status, cst;
219 	register short rx_fifo;
220 
221 	cst = inw(BASE + EP_STATUS);
222 
223 #ifdef EDEBUG
224 	if (cst & 0x1FFF)
225 		printf("-%x-",cst);
226 #endif
227 
228 	if ((cst & (S_RX_COMPLETE|S_RX_EARLY)) == 0) {
229 		/* acknowledge  everything */
230 		outw(BASE + EP_COMMAND, ACK_INTR| (cst & S_5_INTS));
231 		outw(BASE + EP_COMMAND, C_INTR_LATCH);
232 
233 		return 0;
234 	}
235 
236 	status = inw(BASE + EP_W1_RX_STATUS);
237 #ifdef EDEBUG
238 	printf("*%x*",status);
239 #endif
240 
241 	if (status & ERR_RX) {
242 		outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
243 		return 0;
244 	}
245 
246 	rx_fifo = status & RX_BYTES_MASK;
247 	if (rx_fifo == 0)
248 		return 0;
249 
250 	if (rx_fifo > maxlen)
251 		goto zulang;
252 
253 	/* read packet */
254 #ifdef EDEBUG
255 	printf("[l=%d",rx_fifo);
256 #endif
257 	insw(BASE + EP_W1_RX_PIO_RD_1, pkt, rx_fifo / 2);
258 	if (rx_fifo & 1)
259 		pkt[rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1);
260 	len = rx_fifo;
261 
262 	for (;;) {
263 		status = inw(BASE + EP_W1_RX_STATUS);
264 #ifdef EDEBUG
265 		printf("*%x*",status);
266 #endif
267 		rx_fifo = status & RX_BYTES_MASK;
268 
269 		if (rx_fifo > 0) {
270 			if ((len + rx_fifo) > maxlen)
271 				goto zulang;
272 
273 			insw(BASE + EP_W1_RX_PIO_RD_1, pkt + len, rx_fifo / 2);
274 			if (rx_fifo & 1)
275 				pkt[len + rx_fifo-1] = inb(BASE + EP_W1_RX_PIO_RD_1);
276 			len += rx_fifo;
277 #ifdef EDEBUG
278 			printf("+%d",rx_fifo);
279 #endif
280 		}
281 
282 		if ((status & RX_INCOMPLETE) == 0) {
283 #ifdef EDEBUG
284 			printf("=%d",len);
285 #endif
286 			break;
287 		}
288 
289 		delay(1000);
290 	}
291 
292 	/* acknowledge reception of packet */
293 	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
294 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
295 		continue;
296 
297 	return len;
298 
299  zulang:
300 	outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK);
301 	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
302 		continue;
303 	return 0;
304 }
305 
306 /*************************************************************************
307 	3Com 509 - specific routines
308 **************************************************************************/
309 
310 static int
311 eeprom_rdy(void)
312 {
313 	int i;
314 
315 	for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
316 	if (i >= MAX_EEPROMBUSY) {
317 		printf("3c509: eeprom failed to come ready.\r\n");
318 		return 0;
319 	}
320 	return 1;
321 }
322 
323 /*
324  * get_e: gets a 16 bits word from the EEPROM. we must have set the window
325  * before
326  */
327 int
328 ep_get_e(int offset)
329 {
330 	if (!eeprom_rdy())
331 		return 0xffff;
332 	outw(IS_BASE + EP_W0_EEPROM_COMMAND, EEPROM_CMD_RD | offset);
333 	if (!eeprom_rdy())
334 		return 0xffff;
335 	return inw(IS_BASE + EP_W0_EEPROM_DATA);
336 }
337