xref: /minix3/minix/drivers/net/dpeth/3c509.c (revision f7df02e7476731c31f12548e38bcadbaf0233f6a)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc **  File:	3c509.c		Jun. 01, 2000
3433d6423SLionel Sambuc **
4433d6423SLionel Sambuc **  Author:	Giovanni Falzoni <gfalzoni@inwind.it>
5433d6423SLionel Sambuc **
6433d6423SLionel Sambuc **  This file contains specific implementation of the ethernet
7433d6423SLionel Sambuc **  device driver for 3Com Etherlink III (3c509) boards.
8433d6423SLionel Sambuc **  NOTE: The board has to be setup to disable PnP and to assign
9433d6423SLionel Sambuc **	  I/O base and IRQ.  The driver is for ISA bus only
10433d6423SLionel Sambuc */
11433d6423SLionel Sambuc 
12433d6423SLionel Sambuc #include <minix/drivers.h>
1391c4db25SDavid van Moolenbroek #include <minix/netdriver.h>
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc #include "dp.h"
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc #if (ENABLE_3C509 == 1)
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc #include "3c509.h"
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc static const char *const IfNamesMsg[] = {
22433d6423SLionel Sambuc 	"10BaseT", "AUI", "unknown", "BNC",
23433d6423SLionel Sambuc };
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc /*
2691c4db25SDavid van Moolenbroek **  Name:	el3_update_stats
27433d6423SLionel Sambuc **  Function:	Reads statistic counters from board
28433d6423SLionel Sambuc **  		and updates local counters.
29433d6423SLionel Sambuc */
el3_update_stats(dpeth_t * dep)30433d6423SLionel Sambuc static void el3_update_stats(dpeth_t * dep)
31433d6423SLionel Sambuc {
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc   /* Disables statistics while reading and switches to the correct window */
34433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
35433d6423SLionel Sambuc   SetWindow(WNO_Statistics);
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc   /* Reads everything, adding values to the local counters */
38*f7df02e7SDavid van Moolenbroek   netdriver_stat_oerror(inb_el3(dep, REG_TxCarrierLost));	/* Reg. 00 */
39*f7df02e7SDavid van Moolenbroek   netdriver_stat_oerror(inb_el3(dep, REG_TxNoCD));		/* Reg. 01 */
40*f7df02e7SDavid van Moolenbroek   netdriver_stat_coll(inb_el3(dep, REG_TxMultColl));		/* Reg. 02 */
41*f7df02e7SDavid van Moolenbroek   netdriver_stat_coll(inb_el3(dep, REG_TxSingleColl));		/* Reg. 03 */
42*f7df02e7SDavid van Moolenbroek   netdriver_stat_coll(inb_el3(dep, REG_TxLate));		/* Reg. 04 */
43*f7df02e7SDavid van Moolenbroek   netdriver_stat_ierror(inb_el3(dep, REG_RxDiscarded));		/* Reg. 05 */
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc   /* Goes back to operating window and enables statistics */
46433d6423SLionel Sambuc   SetWindow(WNO_Operating);
47433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
48433d6423SLionel Sambuc }
49433d6423SLionel Sambuc 
50433d6423SLionel Sambuc /*
5191c4db25SDavid van Moolenbroek **  Name:	el3_getstats
52433d6423SLionel Sambuc **  Function:	Reads statistics counters from board.
53433d6423SLionel Sambuc */
el3_getstats(dpeth_t * dep)54433d6423SLionel Sambuc static void el3_getstats(dpeth_t * dep)
55433d6423SLionel Sambuc {
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc   el3_update_stats(dep);
58433d6423SLionel Sambuc }
59433d6423SLionel Sambuc 
60433d6423SLionel Sambuc /*
6191c4db25SDavid van Moolenbroek **  Name:	el3_dodump
62433d6423SLionel Sambuc **  Function:	Dumps counter on screen (support for console display).
63433d6423SLionel Sambuc */
el3_dodump(dpeth_t * dep)64433d6423SLionel Sambuc static void el3_dodump(dpeth_t * dep)
65433d6423SLionel Sambuc {
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc   el3_getstats(dep);
68433d6423SLionel Sambuc }
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc /*
7191c4db25SDavid van Moolenbroek **  Name:	el3_rx_mode
72433d6423SLionel Sambuc **  Function:	Initializes receiver mode
73433d6423SLionel Sambuc */
el3_rx_mode(dpeth_t * dep)74433d6423SLionel Sambuc static void el3_rx_mode(dpeth_t * dep)
75433d6423SLionel Sambuc {
76433d6423SLionel Sambuc 
77433d6423SLionel Sambuc   dep->de_recv_mode = FilterIndividual;
78433d6423SLionel Sambuc   if (dep->de_flags & DEF_BROAD) dep->de_recv_mode |= FilterBroadcast;
79433d6423SLionel Sambuc   if (dep->de_flags & DEF_MULTI) dep->de_recv_mode |= FilterMulticast;
80433d6423SLionel Sambuc   if (dep->de_flags & DEF_PROMISC) dep->de_recv_mode |= FilterPromiscuous;
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_RxReset);
83433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter | dep->de_recv_mode);
84433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
85433d6423SLionel Sambuc }
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc /*
8891c4db25SDavid van Moolenbroek **  Name:	el3_reset
89433d6423SLionel Sambuc **  Function:	Reset function specific for Etherlink hardware.
90433d6423SLionel Sambuc */
el3_reset(dpeth_t * UNUSED (dep))91433d6423SLionel Sambuc static void el3_reset(dpeth_t * UNUSED(dep))
92433d6423SLionel Sambuc {
93433d6423SLionel Sambuc 
94433d6423SLionel Sambuc }
95433d6423SLionel Sambuc 
96433d6423SLionel Sambuc /*
9791c4db25SDavid van Moolenbroek **  Name:	el3_recv
98433d6423SLionel Sambuc **  Function:	Receive function.  Called from interrupt handler or
99433d6423SLionel Sambuc **  		from main to unload recv. buffer (packet to client)
100433d6423SLionel Sambuc */
el3_recv(dpeth_t * dep,struct netdriver_data * data,size_t max)10191c4db25SDavid van Moolenbroek static ssize_t el3_recv(dpeth_t *dep, struct netdriver_data *data, size_t max)
102433d6423SLionel Sambuc {
103433d6423SLionel Sambuc   buff_t *rxptr;
10491c4db25SDavid van Moolenbroek   size_t size;
105433d6423SLionel Sambuc 
10691c4db25SDavid van Moolenbroek   if ((rxptr = dep->de_recvq_head) == NULL)
10791c4db25SDavid van Moolenbroek 	return SUSPEND;
108433d6423SLionel Sambuc 
10991c4db25SDavid van Moolenbroek   /* Remove buffer from queue */
110433d6423SLionel Sambuc   if (dep->de_recvq_tail == dep->de_recvq_head)
111433d6423SLionel Sambuc 	dep->de_recvq_head = dep->de_recvq_tail = NULL;
112433d6423SLionel Sambuc   else
113433d6423SLionel Sambuc 	dep->de_recvq_head = rxptr->next;
114433d6423SLionel Sambuc 
115433d6423SLionel Sambuc   /* Copy buffer to user area and free it */
116*f7df02e7SDavid van Moolenbroek   size = MIN((size_t)rxptr->size, max);
117433d6423SLionel Sambuc 
11891c4db25SDavid van Moolenbroek   netdriver_copyout(data, 0, rxptr->buffer, size);
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc   /* Return buffer to the idle pool */
121433d6423SLionel Sambuc   free_buff(dep, rxptr);
12291c4db25SDavid van Moolenbroek 
12391c4db25SDavid van Moolenbroek   return size;
124433d6423SLionel Sambuc }
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc /*
12791c4db25SDavid van Moolenbroek **  Name:	el3_rx_complete
128433d6423SLionel Sambuc **  Function:	Upon receiving a packet, provides status checks
129433d6423SLionel Sambuc **  		and if packet is OK copies it to local buffer.
130433d6423SLionel Sambuc */
el3_rx_complete(dpeth_t * dep)131433d6423SLionel Sambuc static void el3_rx_complete(dpeth_t * dep)
132433d6423SLionel Sambuc {
133433d6423SLionel Sambuc   short int RxStatus;
134433d6423SLionel Sambuc   int pktsize;
135433d6423SLionel Sambuc   buff_t *rxptr;
136433d6423SLionel Sambuc 
137433d6423SLionel Sambuc   RxStatus = inw_el3(dep, REG_RxStatus);
138433d6423SLionel Sambuc   pktsize = RxStatus & RXS_Length;	/* Mask off packet length */
139433d6423SLionel Sambuc 
140433d6423SLionel Sambuc   if (RxStatus & RXS_Error) {
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 	/* First checks for receiving errors */
143433d6423SLionel Sambuc 	RxStatus &= RXS_ErrType;
144433d6423SLionel Sambuc 	switch (RxStatus) {	/* Bad packet (see error type) */
145433d6423SLionel Sambuc 	    case RXS_Dribble:
146433d6423SLionel Sambuc 	    case RXS_Oversize:
147*f7df02e7SDavid van Moolenbroek 	    case RXS_Runt:
148*f7df02e7SDavid van Moolenbroek 	    case RXS_Overrun:
149*f7df02e7SDavid van Moolenbroek 	    case RXS_Framing:
150*f7df02e7SDavid van Moolenbroek 	    case RXS_CRC:
151*f7df02e7SDavid van Moolenbroek 		netdriver_stat_ierror(1);
152433d6423SLionel Sambuc 	}
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc   } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
155433d6423SLionel Sambuc 	/* Memory not available. Drop packet */
156*f7df02e7SDavid van Moolenbroek 	netdriver_stat_ierror(1);
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc   } else {
159433d6423SLionel Sambuc 	/* Good packet.  Read it from FIFO */
16091c4db25SDavid van Moolenbroek 	insb(dep->de_data_port, rxptr->buffer, pktsize);
161433d6423SLionel Sambuc 	rxptr->next = NULL;
162433d6423SLionel Sambuc 	rxptr->size = pktsize;
163433d6423SLionel Sambuc 
16491c4db25SDavid van Moolenbroek 	/* Queue packet to receive queue */
165433d6423SLionel Sambuc 	if (dep->de_recvq_head == NULL)
166433d6423SLionel Sambuc 		dep->de_recvq_head = rxptr;
167433d6423SLionel Sambuc 	else
168433d6423SLionel Sambuc 		dep->de_recvq_tail->next = rxptr;
169433d6423SLionel Sambuc 	dep->de_recvq_tail = rxptr;
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc 	/* Reply to pending Receive requests, if any */
17291c4db25SDavid van Moolenbroek 	netdriver_recv();
173433d6423SLionel Sambuc   }
174433d6423SLionel Sambuc 
175433d6423SLionel Sambuc   /* Discard top packet from queue */
176433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_RxDiscard);
177433d6423SLionel Sambuc }
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc /*
18091c4db25SDavid van Moolenbroek **  Name:	el3_send
181433d6423SLionel Sambuc **  Function:	Send function.  Called from main to transit a packet or
182433d6423SLionel Sambuc **  		from interrupt handler when Tx FIFO gets available.
183433d6423SLionel Sambuc */
el3_send(dpeth_t * dep,struct netdriver_data * data,size_t size)18491c4db25SDavid van Moolenbroek static int el3_send(dpeth_t *dep, struct netdriver_data *data, size_t size)
185433d6423SLionel Sambuc {
186433d6423SLionel Sambuc   clock_t now;
187433d6423SLionel Sambuc   int ix;
188433d6423SLionel Sambuc   short int TxStatus;
18991c4db25SDavid van Moolenbroek   size_t padding;
190433d6423SLionel Sambuc 
191d91f738bSDavid van Moolenbroek   now = getticks();
192433d6423SLionel Sambuc   if ((dep->de_flags & DEF_XMIT_BUSY) &&
193433d6423SLionel Sambuc       (now - dep->de_xmit_start) > 4) {
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	DEBUG(printf("3c509:  Transmitter timed out. Resetting ....\n");)
196*f7df02e7SDavid van Moolenbroek 	netdriver_stat_oerror(1);
197*f7df02e7SDavid van Moolenbroek 	/* Resets and restarts the transmitter */
198433d6423SLionel Sambuc 	outw_el3(dep, REG_CmdStatus, CMD_TxReset);
199433d6423SLionel Sambuc 	outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
200433d6423SLionel Sambuc 	dep->de_flags &= NOT(DEF_XMIT_BUSY);
201433d6423SLionel Sambuc   }
20291c4db25SDavid van Moolenbroek   if (dep->de_flags & DEF_XMIT_BUSY)
20391c4db25SDavid van Moolenbroek 	return SUSPEND;
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc   /* Writes Transmitter preamble 1st Word (packet len, no ints) */
20691c4db25SDavid van Moolenbroek   outw_el3(dep, REG_TxFIFO, size);
207433d6423SLionel Sambuc   /* Writes Transmitter preamble 2nd Word (all zero) */
208433d6423SLionel Sambuc   outw_el3(dep, REG_TxFIFO, 0);
209433d6423SLionel Sambuc   /* Writes packet */
21091c4db25SDavid van Moolenbroek   netdriver_portoutb(data, 0, dep->de_data_port, size);
21191c4db25SDavid van Moolenbroek   padding = size;
21291c4db25SDavid van Moolenbroek   while ((padding++ % sizeof(long)) != 0) outb(dep->de_data_port, 0x00);
213433d6423SLionel Sambuc 
214d91f738bSDavid van Moolenbroek   dep->de_xmit_start = getticks();
21591c4db25SDavid van Moolenbroek   dep->de_flags |= DEF_XMIT_BUSY;
216*f7df02e7SDavid van Moolenbroek   if (inw_el3(dep, REG_TxFree) > NDEV_ETH_PACKET_MAX) {
217433d6423SLionel Sambuc 	/* Tx has enough room for a packet of maximum size */
21891c4db25SDavid van Moolenbroek 	dep->de_flags &= NOT(DEF_XMIT_BUSY);
219433d6423SLionel Sambuc   } else {
220433d6423SLionel Sambuc 	/* Interrupt driver when enough room is available */
221*f7df02e7SDavid van Moolenbroek 	outw_el3(dep, REG_CmdStatus, CMD_SetTxAvailable | NDEV_ETH_PACKET_MAX);
222433d6423SLionel Sambuc   }
223433d6423SLionel Sambuc 
224433d6423SLionel Sambuc   /* Pops Tx status stack */
225433d6423SLionel Sambuc   for (ix = 4; --ix && (TxStatus = inb_el3(dep, REG_TxStatus)) > 0;) {
226*f7df02e7SDavid van Moolenbroek 	if (TxStatus & 0x38)
227*f7df02e7SDavid van Moolenbroek 		netdriver_stat_oerror(1);
228433d6423SLionel Sambuc 	if (TxStatus & 0x30)
229433d6423SLionel Sambuc 		outw_el3(dep, REG_CmdStatus, CMD_TxReset);
230433d6423SLionel Sambuc 	if (TxStatus & 0x3C)
231433d6423SLionel Sambuc 		outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
232433d6423SLionel Sambuc 	outb_el3(dep, REG_TxStatus, 0);
233433d6423SLionel Sambuc   }
23491c4db25SDavid van Moolenbroek 
23591c4db25SDavid van Moolenbroek   return OK;
236433d6423SLionel Sambuc }
237433d6423SLionel Sambuc 
238433d6423SLionel Sambuc /*
23991c4db25SDavid van Moolenbroek **  Name:	el3_close
240433d6423SLionel Sambuc **  Function:	Stops board and makes it ready to shut down.
241433d6423SLionel Sambuc */
el3_close(dpeth_t * dep)242433d6423SLionel Sambuc static void el3_close(dpeth_t * dep)
243433d6423SLionel Sambuc {
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc   /* Disables statistics, Receiver and Transmitter */
246433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
247433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_RxDisable);
248433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_TxDisable);
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc   if (dep->de_if_port == BNC_XCVR) {
251433d6423SLionel Sambuc 	outw_el3(dep, REG_CmdStatus, CMD_StopIntXcvr);
25291c4db25SDavid van Moolenbroek 	/* micro_delay(5000); */
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc   } else if (dep->de_if_port == TP_XCVR) {
255433d6423SLionel Sambuc 	SetWindow(WNO_Diagnostics);
256433d6423SLionel Sambuc 	outw_el3(dep, REG_MediaStatus, inw_el3(dep, REG_MediaStatus) &
257433d6423SLionel Sambuc 		 NOT((MediaLBeatEnable | MediaJabberEnable)));
25891c4db25SDavid van Moolenbroek 	/* micro_delay(5000); */
259433d6423SLionel Sambuc   }
260*f7df02e7SDavid van Moolenbroek   DEBUG(printf("%s: stopping Etherlink ... \n", netdriver_name()));
261433d6423SLionel Sambuc   /* Issues a global reset
262433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_GlobalReset); */
263433d6423SLionel Sambuc   sys_irqdisable(&dep->de_hook);	/* Disable interrupt */
264433d6423SLionel Sambuc }
265433d6423SLionel Sambuc 
266433d6423SLionel Sambuc /*
26791c4db25SDavid van Moolenbroek **  Name:	el3_interrupt
268433d6423SLionel Sambuc **  Function:	Interrupt handler.  Acknwledges transmit interrupts
269433d6423SLionel Sambuc **  		or unloads receive buffer to memory queue.
270433d6423SLionel Sambuc */
el3_interrupt(dpeth_t * dep)271433d6423SLionel Sambuc static void el3_interrupt(dpeth_t * dep)
272433d6423SLionel Sambuc {
273433d6423SLionel Sambuc   int loop;
274433d6423SLionel Sambuc   unsigned short isr;
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc   for (loop = 5; loop > 0 && ((isr = inw_el3(dep, REG_CmdStatus)) &
277433d6423SLionel Sambuc          (INT_Latch | INT_RxComplete | INT_UpdateStats)); loop -= 1) {
278433d6423SLionel Sambuc 
279433d6423SLionel Sambuc 	if (isr & INT_RxComplete)	/* Got a new packet */
280433d6423SLionel Sambuc 		el3_rx_complete(dep);
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc 	if (isr & INT_TxAvailable) {	/* Tx has room for big packets */
283433d6423SLionel Sambuc 		DEBUG(printf("3c509: got Tx interrupt, Status=0x%04x\n", isr);)
284433d6423SLionel Sambuc 		dep->de_flags &= NOT(DEF_XMIT_BUSY);
285433d6423SLionel Sambuc 		outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_TxAvailable);
28691c4db25SDavid van Moolenbroek 		netdriver_send();
287433d6423SLionel Sambuc 	}
288433d6423SLionel Sambuc 	if (isr & (INT_AdapterFail | INT_RxEarly | INT_UpdateStats)) {
289433d6423SLionel Sambuc 
290433d6423SLionel Sambuc 		if (isr & INT_UpdateStats)	/* Empties statistics */
291433d6423SLionel Sambuc 			el3_getstats(dep);
292433d6423SLionel Sambuc 
293433d6423SLionel Sambuc 		if (isr & INT_RxEarly)	/* Not really used. Do nothing */
294433d6423SLionel Sambuc 			outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_RxEarly));
295433d6423SLionel Sambuc 
296433d6423SLionel Sambuc 		if (isr & INT_AdapterFail) {
297433d6423SLionel Sambuc 			/* Adapter error. Reset and re-enable receiver */
298433d6423SLionel Sambuc 			DEBUG(printf("3c509: got Rx fail interrupt, Status=0x%04x\n", isr);)
299433d6423SLionel Sambuc 			el3_rx_mode(dep);
300433d6423SLionel Sambuc 			outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_AdapterFail);
301433d6423SLionel Sambuc 		}
302433d6423SLionel Sambuc 	}
303433d6423SLionel Sambuc 
304433d6423SLionel Sambuc 	/* Acknowledge interrupt */
305433d6423SLionel Sambuc 	outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_Latch | INT_Requested));
306433d6423SLionel Sambuc   }
307433d6423SLionel Sambuc }
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc /*
31091c4db25SDavid van Moolenbroek **  Name:	el3_read_eeprom
311433d6423SLionel Sambuc **  Function:	Reads the EEPROM at specified address
312433d6423SLionel Sambuc */
el3_read_eeprom(port_t port,unsigned address)313433d6423SLionel Sambuc static unsigned el3_read_eeprom(port_t port, unsigned address)
314433d6423SLionel Sambuc {
315433d6423SLionel Sambuc   unsigned int result;
316433d6423SLionel Sambuc   int bit;
317433d6423SLionel Sambuc 
318433d6423SLionel Sambuc   address |= EL3_READ_EEPROM;
319433d6423SLionel Sambuc   outb(port, address);
32091c4db25SDavid van Moolenbroek   micro_delay(5000);		/* Allows EEPROM reads */
321433d6423SLionel Sambuc   for (result = 0, bit = 16; bit > 0; bit -= 1) {
322433d6423SLionel Sambuc 	result = (result << 1) | (inb(port) & 0x0001);
323433d6423SLionel Sambuc   }
324433d6423SLionel Sambuc   return result;
325433d6423SLionel Sambuc }
326433d6423SLionel Sambuc 
327433d6423SLionel Sambuc /*
32891c4db25SDavid van Moolenbroek **  Name:	el3_read_StationAddress
329433d6423SLionel Sambuc **  Function:	Reads station address from board
330433d6423SLionel Sambuc */
el3_read_StationAddress(dpeth_t * dep)331433d6423SLionel Sambuc static void el3_read_StationAddress(dpeth_t * dep)
332433d6423SLionel Sambuc {
333433d6423SLionel Sambuc   unsigned int ix, rc;
334433d6423SLionel Sambuc 
335433d6423SLionel Sambuc   for (ix = EE_3COM_NODE_ADDR; ix < SA_ADDR_LEN+EE_3COM_NODE_ADDR;) {
336433d6423SLionel Sambuc 	/* Accesses with word No. */
337433d6423SLionel Sambuc 	rc = el3_read_eeprom(dep->de_id_port, ix / 2);
338433d6423SLionel Sambuc 	/* Swaps bytes of word */
339*f7df02e7SDavid van Moolenbroek 	dep->de_address.na_addr[ix++] = (rc >> 8) & 0xFF;
340*f7df02e7SDavid van Moolenbroek 	dep->de_address.na_addr[ix++] = rc & 0xFF;
341433d6423SLionel Sambuc   }
342433d6423SLionel Sambuc }
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc /*
34591c4db25SDavid van Moolenbroek **  Name:	el3_open
346433d6423SLionel Sambuc **  Function:	Initalizes board hardware and driver data structures.
347433d6423SLionel Sambuc */
el3_open(dpeth_t * dep)348433d6423SLionel Sambuc static void el3_open(dpeth_t * dep)
349433d6423SLionel Sambuc {
350433d6423SLionel Sambuc   unsigned int AddrCfgReg, ResCfgReg;
351433d6423SLionel Sambuc   unsigned int ix;
352433d6423SLionel Sambuc 
353433d6423SLionel Sambuc   el3_read_StationAddress(dep);	/* Get ethernet address */
354433d6423SLionel Sambuc 
355433d6423SLionel Sambuc   /* Get address and resource configurations */
356433d6423SLionel Sambuc   AddrCfgReg = el3_read_eeprom(dep->de_id_port, EE_ADDR_CFG);
357433d6423SLionel Sambuc   ResCfgReg = el3_read_eeprom(dep->de_id_port, EE_RESOURCE_CFG);
358433d6423SLionel Sambuc   outb(dep->de_id_port, EL3_ACTIVATE);	/* Activate the board */
359433d6423SLionel Sambuc 
360433d6423SLionel Sambuc   /* Gets xcvr configuration */
361433d6423SLionel Sambuc   dep->de_if_port = AddrCfgReg & EL3_CONFIG_XCVR_MASK;
362433d6423SLionel Sambuc 
363433d6423SLionel Sambuc   AddrCfgReg = ((AddrCfgReg & EL3_CONFIG_IOBASE_MASK) << 4) + EL3_IO_BASE_ADDR;
364433d6423SLionel Sambuc   if (AddrCfgReg != dep->de_base_port)
365433d6423SLionel Sambuc 	panic("Bad I/O port for Etherlink board");
366433d6423SLionel Sambuc 
367433d6423SLionel Sambuc   ResCfgReg >>= 12;
368433d6423SLionel Sambuc   dep->de_irq &= NOT(DEI_DEFAULT);	/* Strips the default flag */
369433d6423SLionel Sambuc   if (ResCfgReg != dep->de_irq) panic("Bad IRQ for Etherlink board");
370433d6423SLionel Sambuc 
371433d6423SLionel Sambuc   SetWindow(WNO_Setup);
372433d6423SLionel Sambuc 
373433d6423SLionel Sambuc   /* Reset transmitter and receiver */
374433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_TxReset);
375433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_RxReset);
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc   /* Enable the adapter */
378433d6423SLionel Sambuc   outb_el3(dep, REG_CfgControl, EL3_EnableAdapter);
379433d6423SLionel Sambuc   /* Disable Status bits */
380433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab + 0x00);
381433d6423SLionel Sambuc 
382433d6423SLionel Sambuc   /* Set "my own" address */
383433d6423SLionel Sambuc   SetWindow(WNO_StationAddress);
384433d6423SLionel Sambuc   for (ix = 0; ix < 6; ix += 1)
385*f7df02e7SDavid van Moolenbroek 	outb_el3(dep, REG_SA0_1 + ix, dep->de_address.na_addr[ix]);
386433d6423SLionel Sambuc 
387433d6423SLionel Sambuc   /* Start Transceivers as required */
388433d6423SLionel Sambuc   if (dep->de_if_port == BNC_XCVR) {
389433d6423SLionel Sambuc 	/* Start internal transceiver for Coaxial cable */
390433d6423SLionel Sambuc 	outw_el3(dep, REG_CmdStatus, CMD_StartIntXcvr);
39191c4db25SDavid van Moolenbroek 	micro_delay(5000);
392433d6423SLionel Sambuc 
393433d6423SLionel Sambuc   } else if (dep->de_if_port == TP_XCVR) {
394433d6423SLionel Sambuc 	/* Start internal transceiver for Twisted pair cable */
395433d6423SLionel Sambuc 	SetWindow(WNO_Diagnostics);
396433d6423SLionel Sambuc 	outw_el3(dep, REG_MediaStatus,
397433d6423SLionel Sambuc 		 inw_el3(dep, REG_MediaStatus) | (MediaLBeatEnable | MediaJabberEnable));
398433d6423SLionel Sambuc   }
399433d6423SLionel Sambuc 
400433d6423SLionel Sambuc   /* Switch to the statistic window, and clear counts (by reading) */
401433d6423SLionel Sambuc   SetWindow(WNO_Statistics);
402433d6423SLionel Sambuc   for (ix = REG_TxCarrierLost; ix <= REG_TxDefer; ix += 1) inb_el3(dep, ix);
403433d6423SLionel Sambuc   inw_el3(dep, REG_RxBytes);
404433d6423SLionel Sambuc   inw_el3(dep, REG_TxBytes);
405433d6423SLionel Sambuc 
406433d6423SLionel Sambuc   /* Switch to operating window for normal use */
407433d6423SLionel Sambuc   SetWindow(WNO_Operating);
408433d6423SLionel Sambuc 
409433d6423SLionel Sambuc   /* Receive individual address & broadcast. (Mofified later by rx_mode) */
410433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter |
411433d6423SLionel Sambuc 	 (FilterIndividual | FilterBroadcast));
412433d6423SLionel Sambuc 
413433d6423SLionel Sambuc   /* Turn on statistics */
414433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
415433d6423SLionel Sambuc 
416433d6423SLionel Sambuc   /* Enable transmitter and receiver */
417433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
418433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
419433d6423SLionel Sambuc 
420433d6423SLionel Sambuc   /* Enable all the status bits */
421433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab | 0xFF);
422433d6423SLionel Sambuc 
423433d6423SLionel Sambuc   /* Acknowledge all interrupts to clear adapter. Enable interrupts */
424433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | 0xFF);
425433d6423SLionel Sambuc   outw_el3(dep, REG_CmdStatus, CMD_SetIntMask |
426433d6423SLionel Sambuc     (INT_Latch | INT_TxAvailable | INT_RxComplete | INT_UpdateStats));
427433d6423SLionel Sambuc 
428433d6423SLionel Sambuc   /* Ready to operate, sets the environment for eth_task */
429433d6423SLionel Sambuc   dep->de_data_port = dep->de_base_port;
430433d6423SLionel Sambuc   /* Allocates Rx/Tx buffers */
431433d6423SLionel Sambuc   init_buff(dep, NULL);
432433d6423SLionel Sambuc 
433433d6423SLionel Sambuc   /* Device specific functions */
434433d6423SLionel Sambuc   dep->de_recvf = el3_recv;
435433d6423SLionel Sambuc   dep->de_sendf = el3_send;
436433d6423SLionel Sambuc   dep->de_flagsf = el3_rx_mode;
437433d6423SLionel Sambuc   dep->de_resetf = el3_reset;
438433d6423SLionel Sambuc   dep->de_getstatsf = el3_getstats;
439433d6423SLionel Sambuc   dep->de_dumpstatsf = el3_dodump;
440433d6423SLionel Sambuc   dep->de_interruptf = el3_interrupt;
441433d6423SLionel Sambuc 
442433d6423SLionel Sambuc   printf("%s: Etherlink III (%s) at %X:%d, %s port - ",
443*f7df02e7SDavid van Moolenbroek          netdriver_name(), "3c509", dep->de_base_port, dep->de_irq,
444433d6423SLionel Sambuc          IfNamesMsg[dep->de_if_port >> 14]);
445433d6423SLionel Sambuc   for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
446*f7df02e7SDavid van Moolenbroek 	printf("%02X%c", dep->de_address.na_addr[ix],
447433d6423SLionel Sambuc 	       ix < SA_ADDR_LEN - 1 ? ':' : '\n');
448433d6423SLionel Sambuc }
449433d6423SLionel Sambuc 
450433d6423SLionel Sambuc /*
45191c4db25SDavid van Moolenbroek **  Name:	int el3_checksum
452433d6423SLionel Sambuc **  Function:	Reads EEPROM and computes checksum.
453433d6423SLionel Sambuc */
el3_checksum(port_t port)454433d6423SLionel Sambuc static unsigned short el3_checksum(port_t port)
455433d6423SLionel Sambuc {
456433d6423SLionel Sambuc   unsigned short rc, checksum, address;
457433d6423SLionel Sambuc   unsigned char lo, hi;
458433d6423SLionel Sambuc 
459433d6423SLionel Sambuc   for (checksum = address = 0; address < 15; address += 1) {
460433d6423SLionel Sambuc 	rc = el3_read_eeprom(port, address);
461433d6423SLionel Sambuc 	lo = rc & 0xFF;
462433d6423SLionel Sambuc 	hi = (rc >> 8) & 0xFF;
463433d6423SLionel Sambuc 	if ((address == EE_PROD_ID && (rc & EE_PROD_ID_MASK) != EL3_PRODUCT_ID) ||
464433d6423SLionel Sambuc 	    (address == EE_3COM_CODE && rc != EL3_3COM_CODE))
465433d6423SLionel Sambuc 		return address;
466433d6423SLionel Sambuc 	if (address == EE_ADDR_CFG ||
467433d6423SLionel Sambuc 	    address == EE_RESOURCE_CFG ||
468433d6423SLionel Sambuc 	    address == EE_SW_CONFIG_INFO) {
469433d6423SLionel Sambuc 		lo ^= hi;
470433d6423SLionel Sambuc 		hi = 0;
471433d6423SLionel Sambuc 	} else {
472433d6423SLionel Sambuc 		hi ^= lo;
473433d6423SLionel Sambuc 		lo = 0;
474433d6423SLionel Sambuc 	}
475433d6423SLionel Sambuc 	rc = ((unsigned) hi << 8) + lo;
476433d6423SLionel Sambuc 	checksum ^= rc;
477433d6423SLionel Sambuc   }
478433d6423SLionel Sambuc   rc = el3_read_eeprom(port, address);
479433d6423SLionel Sambuc   return(checksum ^= rc);	/* If OK checksum is 0 */
480433d6423SLionel Sambuc }
481433d6423SLionel Sambuc 
482433d6423SLionel Sambuc /*
48391c4db25SDavid van Moolenbroek **  Name:	el3_write_id
484433d6423SLionel Sambuc **  Function:	Writes the ID sequence to the board.
485433d6423SLionel Sambuc */
el3_write_id(port_t port)486433d6423SLionel Sambuc static void el3_write_id(port_t port)
487433d6423SLionel Sambuc {
488433d6423SLionel Sambuc   int ix, pattern;
489433d6423SLionel Sambuc 
490433d6423SLionel Sambuc   outb(port, 0);		/* Selects the ID port */
491433d6423SLionel Sambuc   outb(port, 0);		/* Resets hardware pattern generator */
492433d6423SLionel Sambuc   for (pattern = ix = 0x00FF; ix > 0; ix -= 1) {
493433d6423SLionel Sambuc 	outb(port, pattern);
494433d6423SLionel Sambuc 	pattern <<= 1;
495433d6423SLionel Sambuc 	pattern = (pattern & 0x0100) ? pattern ^ 0xCF : pattern;
496433d6423SLionel Sambuc   }
497433d6423SLionel Sambuc }
498433d6423SLionel Sambuc 
499433d6423SLionel Sambuc /*
50091c4db25SDavid van Moolenbroek **  Name:	el3_probe
501433d6423SLionel Sambuc **  Function:	Checks for presence of the board.
502433d6423SLionel Sambuc */
el3_probe(dpeth_t * dep)503433d6423SLionel Sambuc int el3_probe(dpeth_t * dep)
504433d6423SLionel Sambuc {
505433d6423SLionel Sambuc   port_t id_port;
506433d6423SLionel Sambuc 
507433d6423SLionel Sambuc   /* Don't ask me what is this for !! */
508433d6423SLionel Sambuc   outb(0x0279, 0x02);	/* Select PnP config control register. */
509433d6423SLionel Sambuc   outb(0x0A79, 0x02);	/* Return to WaitForKey state. */
510433d6423SLionel Sambuc   /* Tests I/O ports in the 0x1xF range for a valid ID port */
511433d6423SLionel Sambuc   for (id_port = 0x110; id_port < 0x200; id_port += 0x10) {
512433d6423SLionel Sambuc 	outb(id_port, 0x00);
513433d6423SLionel Sambuc 	outb(id_port, 0xFF);
514433d6423SLionel Sambuc 	if (inb(id_port) & 0x01) break;
515433d6423SLionel Sambuc   }
516433d6423SLionel Sambuc   if (id_port == 0x200) return 0;	/* No board responding */
517433d6423SLionel Sambuc 
518433d6423SLionel Sambuc   el3_write_id(id_port);
519433d6423SLionel Sambuc   outb(id_port, EL3_ID_GLOBAL_RESET);	/* Reset the board */
52091c4db25SDavid van Moolenbroek   micro_delay(5000);		/* Technical reference says 162 micro sec. */
521433d6423SLionel Sambuc   el3_write_id(id_port);
522433d6423SLionel Sambuc   outb(id_port, EL3_SET_TAG_REGISTER);
52391c4db25SDavid van Moolenbroek   micro_delay(5000);
524433d6423SLionel Sambuc 
525433d6423SLionel Sambuc   dep->de_id_port = id_port;	/* Stores ID port No. */
526433d6423SLionel Sambuc   dep->de_ramsize =		/* RAM size is meaningless */
527433d6423SLionel Sambuc 	dep->de_offset_page = 0;
528433d6423SLionel Sambuc   dep->de_linmem = 0L;		/* Access is via I/O port  */
529433d6423SLionel Sambuc 
530433d6423SLionel Sambuc   /* Device specific functions */
531433d6423SLionel Sambuc   dep->de_initf = el3_open;
532433d6423SLionel Sambuc   dep->de_stopf = el3_close;
533433d6423SLionel Sambuc 
534433d6423SLionel Sambuc   return(el3_checksum(id_port) == 0);	/* Etherlink board found/not found */
535433d6423SLionel Sambuc }
536433d6423SLionel Sambuc 
537433d6423SLionel Sambuc #endif				/* ENABLE_3C509 */
538433d6423SLionel Sambuc 
539433d6423SLionel Sambuc /** 3c509.c **/
540