1 | /*
|
---|
2 | ** File: 3c509.c Jun. 01, 2000
|
---|
3 | **
|
---|
4 | ** Author: Giovanni Falzoni <gfalzoni@inwind.it>
|
---|
5 | **
|
---|
6 | ** This file contains specific implementation of the ethernet
|
---|
7 | ** device driver for 3Com Etherlink III (3c509) boards.
|
---|
8 | ** NOTE: The board has to be setup to disable PnP and to assign
|
---|
9 | ** I/O base and IRQ. The driver is for ISA bus only
|
---|
10 | **
|
---|
11 | ** $Id: 3c509.c,v 1.3 2005/08/05 19:08:43 beng Exp $
|
---|
12 | */
|
---|
13 |
|
---|
14 | #include "drivers.h"
|
---|
15 | #include <minix/com.h>
|
---|
16 | #include <net/hton.h>
|
---|
17 | #include <net/gen/ether.h>
|
---|
18 | #include <net/gen/eth_io.h>
|
---|
19 |
|
---|
20 | #include "dp.h"
|
---|
21 |
|
---|
22 | #if (ENABLE_3C509 == 1)
|
---|
23 |
|
---|
24 | #include "3c509.h"
|
---|
25 |
|
---|
26 | static const char *const IfNamesMsg[] = {
|
---|
27 | "10BaseT", "AUI", "unknown", "BNC",
|
---|
28 | };
|
---|
29 |
|
---|
30 | /*
|
---|
31 | ** Name: void el3_update_stats(dpeth_t *dep)
|
---|
32 | ** Function: Reads statistic counters from board
|
---|
33 | ** and updates local counters.
|
---|
34 | */
|
---|
35 | static void el3_update_stats(dpeth_t * dep)
|
---|
36 | {
|
---|
37 |
|
---|
38 | /* Disables statistics while reading and switches to the correct window */
|
---|
39 | outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
|
---|
40 | SetWindow(WNO_Statistics);
|
---|
41 |
|
---|
42 | /* Reads everything, adding values to the local counters */
|
---|
43 | dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxCarrierLost); /* Reg. 00 */
|
---|
44 | dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxNoCD); /* Reg. 01 */
|
---|
45 | dep->de_stat.ets_collision += inb_el3(dep, REG_TxMultColl); /* Reg. 02 */
|
---|
46 | dep->de_stat.ets_collision += inb_el3(dep, REG_TxSingleColl); /* Reg. 03 */
|
---|
47 | dep->de_stat.ets_collision += inb_el3(dep, REG_TxLate); /* Reg. 04 */
|
---|
48 | dep->de_stat.ets_recvErr += inb_el3(dep, REG_RxDiscarded); /* Reg. 05 */
|
---|
49 | dep->de_stat.ets_packetT += inb_el3(dep, REG_TxFrames); /* Reg. 06 */
|
---|
50 | dep->de_stat.ets_packetR += inb_el3(dep, REG_RxFrames); /* Reg. 07 */
|
---|
51 | dep->de_stat.ets_transDef += inb_el3(dep, REG_TxDefer); /* Reg. 08 */
|
---|
52 | dep->bytes_Rx += (unsigned) inw_el3(dep, REG_RxBytes); /* Reg. 10 */
|
---|
53 | dep->bytes_Tx += (unsigned) inw_el3(dep, REG_TxBytes); /* Reg. 12 */
|
---|
54 |
|
---|
55 | /* Goes back to operating window and enables statistics */
|
---|
56 | SetWindow(WNO_Operating);
|
---|
57 | outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
|
---|
58 |
|
---|
59 | return;
|
---|
60 | }
|
---|
61 |
|
---|
62 | /*
|
---|
63 | ** Name: void el3_getstats(dpeth_t *dep)
|
---|
64 | ** Function: Reads statistics counters from board.
|
---|
65 | */
|
---|
66 | static void el3_getstats(dpeth_t * dep)
|
---|
67 | {
|
---|
68 |
|
---|
69 | lock();
|
---|
70 | el3_update_stats(dep);
|
---|
71 | unlock();
|
---|
72 | return;
|
---|
73 | }
|
---|
74 |
|
---|
75 | /*
|
---|
76 | ** Name: void el3_dodump(dpeth_t *dep)
|
---|
77 | ** Function: Dumps counter on screen (support for console display).
|
---|
78 | */
|
---|
79 | static void el3_dodump(dpeth_t * dep)
|
---|
80 | {
|
---|
81 |
|
---|
82 | el3_getstats(dep);
|
---|
83 | return;
|
---|
84 | }
|
---|
85 |
|
---|
86 | /*
|
---|
87 | ** Name: void el3_rx_mode(dpeth_t *dep)
|
---|
88 | ** Function: Initializes receiver mode
|
---|
89 | */
|
---|
90 | static void el3_rx_mode(dpeth_t * dep)
|
---|
91 | {
|
---|
92 |
|
---|
93 | dep->de_recv_mode = FilterIndividual;
|
---|
94 | if (dep->de_flags & DEF_BROAD) dep->de_recv_mode |= FilterBroadcast;
|
---|
95 | if (dep->de_flags & DEF_MULTI) dep->de_recv_mode |= FilterMulticast;
|
---|
96 | if (dep->de_flags & DEF_PROMISC) dep->de_recv_mode |= FilterPromiscuous;
|
---|
97 |
|
---|
98 | outw_el3(dep, REG_CmdStatus, CMD_RxReset);
|
---|
99 | outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter | dep->de_recv_mode);
|
---|
100 | outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
|
---|
101 |
|
---|
102 | return;
|
---|
103 | }
|
---|
104 |
|
---|
105 | /*
|
---|
106 | ** Name: void el3_reset(dpeth_t *dep)
|
---|
107 | ** Function: Reset function specific for Etherlink hardware.
|
---|
108 | */
|
---|
109 | static void el3_reset(dpeth_t * dep)
|
---|
110 | {
|
---|
111 |
|
---|
112 | return; /* Done */
|
---|
113 | }
|
---|
114 |
|
---|
115 | /*
|
---|
116 | ** Name: void el3_write_fifo(dpeth_t * dep, int pktsize);
|
---|
117 | ** Function: Writes a packet from user area to board.
|
---|
118 | ** Remark: Writing a word/dword at a time may result faster
|
---|
119 | ** but is a lot more complicated. Let's go simpler way.
|
---|
120 | */
|
---|
121 | static void el3_write_fifo(dpeth_t * dep, int pktsize)
|
---|
122 | {
|
---|
123 | phys_bytes phys_user;
|
---|
124 | int bytes, ix = 0;
|
---|
125 | iovec_dat_t *iovp = &dep->de_write_iovec;
|
---|
126 | int padding = pktsize;
|
---|
127 |
|
---|
128 | do { /* Writes chuncks of packet from user buffers */
|
---|
129 |
|
---|
130 | bytes = iovp->iod_iovec[ix].iov_size; /* Size of buffer */
|
---|
131 | if (bytes > pktsize) bytes = pktsize;
|
---|
132 | /* Writes from user buffer to Tx FIFO */
|
---|
133 | outsb(dep->de_data_port, iovp->iod_proc_nr,
|
---|
134 | (void*)(iovp->iod_iovec[ix].iov_addr), bytes);
|
---|
135 | if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
|
---|
136 | dp_next_iovec(iovp);
|
---|
137 | ix = 0;
|
---|
138 | }
|
---|
139 | /* Till packet done */
|
---|
140 | } while ((pktsize -= bytes) > 0);
|
---|
141 | while ((padding++ % sizeof(long)) != 0) outb(dep->de_data_port, 0x00);
|
---|
142 | return;
|
---|
143 | }
|
---|
144 |
|
---|
145 | /*
|
---|
146 | ** Name: void el3_recv(dpeth_t *dep, int fromint, int size)
|
---|
147 | ** Function: Receive function. Called from interrupt handler or
|
---|
148 | ** from main to unload recv. buffer (packet to client)
|
---|
149 | */
|
---|
150 | static void el3_recv(dpeth_t *dep, int fromint, int size)
|
---|
151 | {
|
---|
152 | buff_t *rxptr;
|
---|
153 |
|
---|
154 | while ((dep->de_flags & DEF_READING) && (rxptr = dep->de_recvq_head)) {
|
---|
155 |
|
---|
156 | lock(); /* Remove buffer from queue */
|
---|
157 | if (dep->de_recvq_tail == dep->de_recvq_head)
|
---|
158 | dep->de_recvq_head = dep->de_recvq_tail = NULL;
|
---|
159 | else
|
---|
160 | dep->de_recvq_head = rxptr->next;
|
---|
161 | unlock();
|
---|
162 |
|
---|
163 | /* Copy buffer to user area and free it */
|
---|
164 | mem2user(dep, rxptr);
|
---|
165 |
|
---|
166 | dep->de_read_s = rxptr->size;
|
---|
167 | dep->de_flags |= DEF_ACK_RECV;
|
---|
168 | dep->de_flags &= NOT(DEF_READING);
|
---|
169 |
|
---|
170 | /* Return buffer to the idle pool */
|
---|
171 | free_buff(dep, rxptr);
|
---|
172 | }
|
---|
173 | return;
|
---|
174 | }
|
---|
175 |
|
---|
176 | /*
|
---|
177 | ** Name: void el3_rx_complete(dpeth_t * dep);
|
---|
178 | ** Function: Upon receiving a packet, provides status checks
|
---|
179 | ** and if packet is OK copies it to local buffer.
|
---|
180 | */
|
---|
181 | static void el3_rx_complete(dpeth_t * dep)
|
---|
182 | {
|
---|
183 | short int RxStatus;
|
---|
184 | int pktsize;
|
---|
185 | buff_t *rxptr;
|
---|
186 |
|
---|
187 | RxStatus = inw_el3(dep, REG_RxStatus);
|
---|
188 | pktsize = RxStatus & RXS_Length; /* Mask off packet length */
|
---|
189 |
|
---|
190 | if (RxStatus & RXS_Error) {
|
---|
191 |
|
---|
192 | /* First checks for receiving errors */
|
---|
193 | RxStatus &= RXS_ErrType;
|
---|
194 | switch (RxStatus) { /* Bad packet (see error type) */
|
---|
195 | case RXS_Dribble:
|
---|
196 | case RXS_Oversize:
|
---|
197 | case RXS_Runt: dep->de_stat.ets_recvErr += 1; break;
|
---|
198 | case RXS_Overrun: dep->de_stat.ets_OVW += 1; break;
|
---|
199 | case RXS_Framing: dep->de_stat.ets_frameAll += 1; break;
|
---|
200 | case RXS_CRC: dep->de_stat.ets_CRCerr += 1; break;
|
---|
201 | }
|
---|
202 |
|
---|
203 | } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
|
---|
204 | /* Memory not available. Drop packet */
|
---|
205 | dep->de_stat.ets_fifoOver += 1;
|
---|
206 |
|
---|
207 | } else {
|
---|
208 | /* Good packet. Read it from FIFO */
|
---|
209 | insb(dep->de_data_port, SELF, rxptr->buffer, pktsize);
|
---|
210 | rxptr->next = NULL;
|
---|
211 | rxptr->size = pktsize;
|
---|
212 |
|
---|
213 | lock(); /* Queue packet to receive queue */
|
---|
214 | if (dep->de_recvq_head == NULL)
|
---|
215 | dep->de_recvq_head = rxptr;
|
---|
216 | else
|
---|
217 | dep->de_recvq_tail->next = rxptr;
|
---|
218 | dep->de_recvq_tail = rxptr;
|
---|
219 | unlock();
|
---|
220 |
|
---|
221 | /* Reply to pending Receive requests, if any */
|
---|
222 | el3_recv(dep, TRUE, pktsize);
|
---|
223 | }
|
---|
224 |
|
---|
225 | /* Discard top packet from queue */
|
---|
226 | outw_el3(dep, REG_CmdStatus, CMD_RxDiscard);
|
---|
227 |
|
---|
228 | return;
|
---|
229 | }
|
---|
230 |
|
---|
231 | /*
|
---|
232 | ** Name: void el3_send(dpeth_t *dep, int count)
|
---|
233 | ** Function: Send function. Called from main to transit a packet or
|
---|
234 | ** from interrupt handler when Tx FIFO gets available.
|
---|
235 | */
|
---|
236 | static void el3_send(dpeth_t * dep, int from_int, int count)
|
---|
237 | {
|
---|
238 | clock_t now;
|
---|
239 | int ix;
|
---|
240 | short int TxStatus;
|
---|
241 |
|
---|
242 | getuptime(&now);
|
---|
243 | if ((dep->de_flags & DEF_XMIT_BUSY) &&
|
---|
244 | (now - dep->de_xmit_start) > 4) {
|
---|
245 |
|
---|
246 | DEBUG(printf("3c509: Transmitter timed out. Resetting ....\n");)
|
---|
247 | dep->de_stat.ets_sendErr += 1;
|
---|
248 | /* Resets and restars the transmitter */
|
---|
249 | outw_el3(dep, REG_CmdStatus, CMD_TxReset);
|
---|
250 | outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
|
---|
251 | dep->de_flags &= NOT(DEF_XMIT_BUSY);
|
---|
252 | }
|
---|
253 | if (!(dep->de_flags & DEF_XMIT_BUSY)) {
|
---|
254 |
|
---|
255 | /* Writes Transmitter preamble 1st Word (packet len, no ints) */
|
---|
256 | outw_el3(dep, REG_TxFIFO, count);
|
---|
257 | /* Writes Transmitter preamble 2nd Word (all zero) */
|
---|
258 | outw_el3(dep, REG_TxFIFO, 0);
|
---|
259 | /* Writes packet */
|
---|
260 | el3_write_fifo(dep, count);
|
---|
261 |
|
---|
262 | getuptime(&dep->de_xmit_start);
|
---|
263 | dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND);
|
---|
264 | if (inw_el3(dep, REG_TxFree) > ETH_MAX_PACK_SIZE) {
|
---|
265 | /* Tx has enough room for a packet of maximum size */
|
---|
266 | dep->de_flags &= NOT(DEF_XMIT_BUSY | DEF_SENDING);
|
---|
267 | } else {
|
---|
268 | /* Interrupt driver when enough room is available */
|
---|
269 | outw_el3(dep, REG_CmdStatus, CMD_SetTxAvailable | ETH_MAX_PACK_SIZE);
|
---|
270 | dep->de_flags &= NOT(DEF_SENDING);
|
---|
271 | }
|
---|
272 |
|
---|
273 | /* Pops Tx status stack */
|
---|
274 | for (ix = 4; --ix && (TxStatus = inb_el3(dep, REG_TxStatus)) > 0;) {
|
---|
275 | if (TxStatus & 0x38) dep->de_stat.ets_sendErr += 1;
|
---|
276 | if (TxStatus & 0x30)
|
---|
277 | outw_el3(dep, REG_CmdStatus, CMD_TxReset);
|
---|
278 | if (TxStatus & 0x3C)
|
---|
279 | outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
|
---|
280 | outb_el3(dep, REG_TxStatus, 0);
|
---|
281 | }
|
---|
282 | }
|
---|
283 | return;
|
---|
284 | }
|
---|
285 |
|
---|
286 | /*
|
---|
287 | ** Name: void el3_close(dpeth_t *dep)
|
---|
288 | ** Function: Stops board and makes it ready to shut down.
|
---|
289 | */
|
---|
290 | static void el3_close(dpeth_t * dep)
|
---|
291 | {
|
---|
292 |
|
---|
293 | /* Disables statistics, Receiver and Transmitter */
|
---|
294 | outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
|
---|
295 | outw_el3(dep, REG_CmdStatus, CMD_RxDisable);
|
---|
296 | outw_el3(dep, REG_CmdStatus, CMD_TxDisable);
|
---|
297 |
|
---|
298 | if (dep->de_if_port == BNC_XCVR) {
|
---|
299 | outw_el3(dep, REG_CmdStatus, CMD_StopIntXcvr);
|
---|
300 | /* milli_delay(5); */
|
---|
301 |
|
---|
302 | } else if (dep->de_if_port == TP_XCVR) {
|
---|
303 | SetWindow(WNO_Diagnostics);
|
---|
304 | outw_el3(dep, REG_MediaStatus, inw_el3(dep, REG_MediaStatus) &
|
---|
305 | NOT((MediaLBeatEnable | MediaJabberEnable)));
|
---|
306 | /* milli_delay(5); */
|
---|
307 | }
|
---|
308 | DEBUG(printf("%s: stopping Etherlink ... \n", dep->de_name));
|
---|
309 | /* Issues a global reset
|
---|
310 | outw_el3(dep, REG_CmdStatus, CMD_GlobalReset); */
|
---|
311 | sys_irqdisable(&dep->de_hook); /* Disable interrupt */
|
---|
312 |
|
---|
313 | return;
|
---|
314 | }
|
---|
315 |
|
---|
316 | /*
|
---|
317 | ** Name: void el3_interrupt(dpeth_t *dep)
|
---|
318 | ** Function: Interrupt handler. Acknwledges transmit interrupts
|
---|
319 | ** or unloads receive buffer to memory queue.
|
---|
320 | */
|
---|
321 | static void el3_interrupt(dpeth_t * dep)
|
---|
322 | {
|
---|
323 | int loop;
|
---|
324 | unsigned short isr;
|
---|
325 |
|
---|
326 | for (loop = 5; loop > 0 && ((isr = inw_el3(dep, REG_CmdStatus)) &
|
---|
327 | (INT_Latch | INT_RxComplete | INT_UpdateStats)); loop -= 1) {
|
---|
328 |
|
---|
329 | if (isr & INT_RxComplete) /* Got a new packet */
|
---|
330 | el3_rx_complete(dep);
|
---|
331 |
|
---|
332 | if (isr & INT_TxAvailable) { /* Tx has room for big packets */
|
---|
333 | DEBUG(printf("3c509: got Tx interrupt, Status=0x%04x\n", isr);)
|
---|
334 | dep->de_flags &= NOT(DEF_XMIT_BUSY);
|
---|
335 | outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_TxAvailable);
|
---|
336 | if (dep->de_flags & DEF_SENDING) /* Send pending */
|
---|
337 | el3_send(dep, TRUE, dep->de_send_s);
|
---|
338 | }
|
---|
339 | if (isr & (INT_AdapterFail | INT_RxEarly | INT_UpdateStats)) {
|
---|
340 |
|
---|
341 | if (isr & INT_UpdateStats) /* Empties statistics */
|
---|
342 | el3_getstats(dep);
|
---|
343 |
|
---|
344 | if (isr & INT_RxEarly) /* Not really used. Do nothing */
|
---|
345 | outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_RxEarly));
|
---|
346 |
|
---|
347 | if (isr & INT_AdapterFail) {
|
---|
348 | /* Adapter error. Reset and re-enable receiver */
|
---|
349 | DEBUG(printf("3c509: got Rx fail interrupt, Status=0x%04x\n", isr);)
|
---|
350 | el3_rx_mode(dep);
|
---|
351 | outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_AdapterFail);
|
---|
352 | }
|
---|
353 | }
|
---|
354 |
|
---|
355 | /* Acknowledge interrupt */
|
---|
356 | outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_Latch | INT_Requested));
|
---|
357 | }
|
---|
358 | return;
|
---|
359 | }
|
---|
360 |
|
---|
361 | /*
|
---|
362 | ** Name: unsigned el3_read_eeprom(port_t port, unsigned address);
|
---|
363 | ** Function: Reads the EEPROM at specified address
|
---|
364 | */
|
---|
365 | static unsigned el3_read_eeprom(port_t port, unsigned address)
|
---|
366 | {
|
---|
367 | unsigned int result;
|
---|
368 | int bit;
|
---|
369 |
|
---|
370 | address |= EL3_READ_EEPROM;
|
---|
371 | outb(port, address);
|
---|
372 | milli_delay(5); /* Allows EEPROM reads */
|
---|
373 | for (result = 0, bit = 16; bit > 0; bit -= 1) {
|
---|
374 | result = (result << 1) | (inb(port) & 0x0001);
|
---|
375 | }
|
---|
376 | return result;
|
---|
377 | }
|
---|
378 |
|
---|
379 | /*
|
---|
380 | ** Name: void el3_read_StationAddress(dpeth_t *dep)
|
---|
381 | ** Function: Reads station address from board
|
---|
382 | */
|
---|
383 | static void el3_read_StationAddress(dpeth_t * dep)
|
---|
384 | {
|
---|
385 | unsigned int ix, rc;
|
---|
386 |
|
---|
387 | for (ix = EE_3COM_NODE_ADDR; ix < SA_ADDR_LEN;) {
|
---|
388 | /* Accesses with word No. */
|
---|
389 | rc = el3_read_eeprom(dep->de_id_port, ix / 2);
|
---|
390 | /* Swaps bytes of word */
|
---|
391 | dep->de_address.ea_addr[ix++] = (rc >> 8) & 0xFF;
|
---|
392 | dep->de_address.ea_addr[ix++] = rc & 0xFF;
|
---|
393 | }
|
---|
394 | return;
|
---|
395 | }
|
---|
396 |
|
---|
397 | /*
|
---|
398 | ** Name: void el3_open(dpeth_t *dep)
|
---|
399 | ** Function: Initalizes board hardware and driver data structures.
|
---|
400 | */
|
---|
401 | static void el3_open(dpeth_t * dep)
|
---|
402 | {
|
---|
403 | unsigned int AddrCfgReg, ResCfgReg;
|
---|
404 | unsigned int ix;
|
---|
405 |
|
---|
406 | el3_read_StationAddress(dep); /* Get ethernet address */
|
---|
407 |
|
---|
408 | /* Get address and resource configurations */
|
---|
409 | AddrCfgReg = el3_read_eeprom(dep->de_id_port, EE_ADDR_CFG);
|
---|
410 | ResCfgReg = el3_read_eeprom(dep->de_id_port, EE_RESOURCE_CFG);
|
---|
411 | outb(dep->de_id_port, EL3_ACTIVATE); /* Activate the board */
|
---|
412 |
|
---|
413 | /* Gets xcvr configuration */
|
---|
414 | dep->de_if_port = AddrCfgReg & EL3_CONFIG_XCVR_MASK;
|
---|
415 |
|
---|
416 | AddrCfgReg = ((AddrCfgReg & EL3_CONFIG_IOBASE_MASK) << 4) + EL3_IO_BASE_ADDR;
|
---|
417 | if (AddrCfgReg != dep->de_base_port)
|
---|
418 | panic(dep->de_name, "Bad I/O port for Etherlink board", NO_NUM);
|
---|
419 |
|
---|
420 | ResCfgReg >>= 12;
|
---|
421 | dep->de_irq &= NOT(DEI_DEFAULT); /* Strips the default flag */
|
---|
422 | if (ResCfgReg != dep->de_irq) panic(dep->de_name, "Bad IRQ for Etherlink board", NO_NUM);
|
---|
423 |
|
---|
424 | SetWindow(WNO_Setup);
|
---|
425 |
|
---|
426 | /* Reset transmitter and receiver */
|
---|
427 | outw_el3(dep, REG_CmdStatus, CMD_TxReset);
|
---|
428 | outw_el3(dep, REG_CmdStatus, CMD_RxReset);
|
---|
429 |
|
---|
430 | /* Enable the adapter */
|
---|
431 | outb_el3(dep, REG_CfgControl, EL3_EnableAdapter);
|
---|
432 | /* Disable Status bits */
|
---|
433 | outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab + 0x00);
|
---|
434 |
|
---|
435 | /* Set "my own" address */
|
---|
436 | SetWindow(WNO_StationAddress);
|
---|
437 | for (ix = 0; ix < 6; ix += 1)
|
---|
438 | outb_el3(dep, REG_SA0_1 + ix, dep->de_address.ea_addr[ix]);
|
---|
439 |
|
---|
440 | /* Start Transceivers as required */
|
---|
441 | if (dep->de_if_port == BNC_XCVR) {
|
---|
442 | /* Start internal transceiver for Coaxial cable */
|
---|
443 | outw_el3(dep, REG_CmdStatus, CMD_StartIntXcvr);
|
---|
444 | milli_delay(5);
|
---|
445 |
|
---|
446 | } else if (dep->de_if_port == TP_XCVR) {
|
---|
447 | /* Start internal transceiver for Twisted pair cable */
|
---|
448 | SetWindow(WNO_Diagnostics);
|
---|
449 | outw_el3(dep, REG_MediaStatus,
|
---|
450 | inw_el3(dep, REG_MediaStatus) | (MediaLBeatEnable | MediaJabberEnable));
|
---|
451 | }
|
---|
452 |
|
---|
453 | /* Switch to the statistic window, and clear counts (by reading) */
|
---|
454 | SetWindow(WNO_Statistics);
|
---|
455 | for (ix = REG_TxCarrierLost; ix <= REG_TxDefer; ix += 1) inb_el3(dep, ix);
|
---|
456 | inw_el3(dep, REG_RxBytes);
|
---|
457 | inw_el3(dep, REG_TxBytes);
|
---|
458 |
|
---|
459 | /* Switch to operating window for normal use */
|
---|
460 | SetWindow(WNO_Operating);
|
---|
461 |
|
---|
462 | /* Receive individual address & broadcast. (Mofified later by rx_mode) */
|
---|
463 | outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter |
|
---|
464 | (FilterIndividual | FilterBroadcast));
|
---|
465 |
|
---|
466 | /* Turn on statistics */
|
---|
467 | outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
|
---|
468 |
|
---|
469 | /* Enable transmitter and receiver */
|
---|
470 | outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
|
---|
471 | outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
|
---|
472 |
|
---|
473 | /* Enable all the status bits */
|
---|
474 | outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab | 0xFF);
|
---|
475 |
|
---|
476 | /* Acknowledge all interrupts to clear adapter. Enable interrupts */
|
---|
477 | outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | 0xFF);
|
---|
478 | outw_el3(dep, REG_CmdStatus, CMD_SetIntMask |
|
---|
479 | (INT_Latch | INT_TxAvailable | INT_RxComplete | INT_UpdateStats));
|
---|
480 |
|
---|
481 | /* Ready to operate, sets the environment for eth_task */
|
---|
482 | dep->de_data_port = dep->de_base_port;
|
---|
483 | /* Allocates Rx/Tx buffers */
|
---|
484 | init_buff(dep, NULL);
|
---|
485 |
|
---|
486 | /* Device specific functions */
|
---|
487 | dep->de_recvf = el3_recv;
|
---|
488 | dep->de_sendf = el3_send;
|
---|
489 | dep->de_flagsf = el3_rx_mode;
|
---|
490 | dep->de_resetf = el3_reset;
|
---|
491 | dep->de_getstatsf = el3_getstats;
|
---|
492 | dep->de_dumpstatsf = el3_dodump;
|
---|
493 | dep->de_interruptf = el3_interrupt;
|
---|
494 |
|
---|
495 | printf("%s: Etherlink III (%s) at %X:%d, %s port - ",
|
---|
496 | dep->de_name, "3c509", dep->de_base_port, dep->de_irq,
|
---|
497 | IfNamesMsg[dep->de_if_port >> 14]);
|
---|
498 | for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
|
---|
499 | printf("%02X%c", dep->de_address.ea_addr[ix],
|
---|
500 | ix < SA_ADDR_LEN - 1 ? ':' : '\n');
|
---|
501 |
|
---|
502 | return; /* Done */
|
---|
503 | }
|
---|
504 |
|
---|
505 | /*
|
---|
506 | ** Name: unsigned int el3_checksum(port_t port);
|
---|
507 | ** Function: Reads EEPROM and computes checksum.
|
---|
508 | */
|
---|
509 | static unsigned short el3_checksum(port_t port)
|
---|
510 | {
|
---|
511 | unsigned short rc, checksum, address;
|
---|
512 | unsigned char lo, hi;
|
---|
513 |
|
---|
514 | for (checksum = address = 0; address < 15; address += 1) {
|
---|
515 | rc = el3_read_eeprom(port, address);
|
---|
516 | lo = rc & 0xFF;
|
---|
517 | hi = (rc >> 8) & 0xFF;
|
---|
518 | if ((address == EE_PROD_ID && (rc & EE_PROD_ID_MASK) != EL3_PRODUCT_ID) ||
|
---|
519 | (address == EE_3COM_CODE && rc != EL3_3COM_CODE))
|
---|
520 | return address;
|
---|
521 | if (address == EE_ADDR_CFG ||
|
---|
522 | address == EE_RESOURCE_CFG ||
|
---|
523 | address == EE_SW_CONFIG_INFO) {
|
---|
524 | lo ^= hi;
|
---|
525 | hi = 0;
|
---|
526 | } else {
|
---|
527 | hi ^= lo;
|
---|
528 | lo = 0;
|
---|
529 | }
|
---|
530 | rc = ((unsigned) hi << 8) + lo;
|
---|
531 | checksum ^= rc;
|
---|
532 | }
|
---|
533 | rc = el3_read_eeprom(port, address);
|
---|
534 | return(checksum ^= rc); /* If OK checksum is 0 */
|
---|
535 | }
|
---|
536 |
|
---|
537 | /*
|
---|
538 | ** Name: void el3_write_id(port_t port);
|
---|
539 | ** Function: Writes the ID sequence to the board.
|
---|
540 | */
|
---|
541 | static void el3_write_id(port_t port)
|
---|
542 | {
|
---|
543 | int ix, pattern;
|
---|
544 |
|
---|
545 | outb(port, 0); /* Selects the ID port */
|
---|
546 | outb(port, 0); /* Resets hardware pattern generator */
|
---|
547 | for (pattern = ix = 0x00FF; ix > 0; ix -= 1) {
|
---|
548 | outb(port, pattern);
|
---|
549 | pattern <<= 1;
|
---|
550 | pattern = (pattern & 0x0100) ? pattern ^ 0xCF : pattern;
|
---|
551 | }
|
---|
552 | return;
|
---|
553 | }
|
---|
554 |
|
---|
555 | /*
|
---|
556 | ** Name: int el3_probe(dpeth_t *dep)
|
---|
557 | ** Function: Checks for presence of the board.
|
---|
558 | */
|
---|
559 | PUBLIC int el3_probe(dpeth_t * dep)
|
---|
560 | {
|
---|
561 | port_t id_port;
|
---|
562 |
|
---|
563 | /* Don't ask me what is this for !! */
|
---|
564 | outb(0x0279, 0x02); /* Select PnP config control register. */
|
---|
565 | outb(0x0A79, 0x02); /* Return to WaitForKey state. */
|
---|
566 | /* Tests I/O ports in the 0x1xF range for a valid ID port */
|
---|
567 | for (id_port = 0x110; id_port < 0x200; id_port += 0x10) {
|
---|
568 | outb(id_port, 0x00);
|
---|
569 | outb(id_port, 0xFF);
|
---|
570 | if (inb(id_port) & 0x01) break;
|
---|
571 | }
|
---|
572 | if (id_port == 0x200) return 0; /* No board responding */
|
---|
573 |
|
---|
574 | el3_write_id(id_port);
|
---|
575 | outb(id_port, EL3_ID_GLOBAL_RESET); /* Reset the board */
|
---|
576 | milli_delay(5); /* Technical reference says 162 micro sec. */
|
---|
577 | el3_write_id(id_port);
|
---|
578 | outb(id_port, EL3_SET_TAG_REGISTER);
|
---|
579 | milli_delay(5);
|
---|
580 |
|
---|
581 | dep->de_id_port = id_port; /* Stores ID port No. */
|
---|
582 | dep->de_ramsize = /* RAM size is meaningless */
|
---|
583 | dep->de_offset_page = 0;
|
---|
584 | dep->de_linmem = 0L; /* Access is via I/O port */
|
---|
585 |
|
---|
586 | /* Device specific functions */
|
---|
587 | dep->de_initf = el3_open;
|
---|
588 | dep->de_stopf = el3_close;
|
---|
589 |
|
---|
590 | return(el3_checksum(id_port) == 0); /* Etherlink board found/not found */
|
---|
591 | }
|
---|
592 |
|
---|
593 | #endif /* ENABLE_3C509 */
|
---|
594 |
|
---|
595 | /** 3c509.c **/
|
---|