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
epstop(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
EtherStop(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
epreset(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
EtherSend(char * pkt,int len)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
EtherReceive(char * pkt,int maxlen)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
eeprom_rdy(void)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
ep_get_e(int offset)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